注解与反射
# 注解
# 理解注解
**注解(Annotation)**是从 JDK5.0 开始引入的一个新技术
注解的作用
- 注解并不是程序本身,但却可以对程序作出解释,解释的内容也称之为元数据
- 注解可以被其它程序读取
注解的书写格式
以
@注释名
的格式在代码中存在注解可以在哪里使用
可以附加在
package
、class
、method
、field
等上面,相当于给它们添加了额外的辅助信息。我们可以通过反射机制编程实现对这些元数据的访问JAVA 中的注解类似于 TypeScript 中的装饰器
# 内置注解
常见的内置注解有下面三种
@Override
定义在 java.lang.Override 中,此注解只能用于修饰方法。表示要重写超类的一个方法
@Deprecated
定义在 java.lang.Deprecated 中,此注解可以用于修饰属性、方法、类。表示不鼓励程序员使用被修饰的元素
@SuppressWarnings
定义在 java.lang.SuppressWarnings 中,用于抑制编译时的警告信息
与前两个注解不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性地使用即可,比如:
@SuppressWarnings("all")
@SuppressWarnings("unchecked")
@SuppressWarnings(value={"unchecked", "deprecated"})
# 元注解
元注解的作用就是负责注解其它注解(也称为注解的注解),Java 中定义了4个标准的 meta-annotation 类型,它们被用来提供对其它 annotation 类型的说明
这些类型和它们所支持的类可以在 java.lang.annotation 包中找到
@Target
用于描述注解的使用范围
@Retention
表示需要在什么级别使用保存该注解信息,用于描述注解的生命周期
@Document
说明该注解将包含在 javadoc 中
@Inherited
说明子类可以继承该父类中的注解
定义一个注解:[public] @interface 注解名
// 使用元注解
//
@Target(value = ElementType.METHOD)
@interface MyAnnotation{
}
# 自定义注解
自定义注解格式:[public] @interface 注解名{定义内容}
使用 @interface 自定义注解时,自动继承了 java.lang.annotation.Annotation 接口
当注解的 定义内容
中有 value
字段,在使用注解时传递的参数可以不写 value
, 直接写参数值即可,例子如下
public class Demo {
@MyAnnotation2("hello")
@MyAnnotation(name = "lkj")
public void test(){
System.out.println("使用了自定义注解");
}
}
/**
* 定义一个注解 MyAnnotation,该注解只能在方法上使用
* 该注解需要传递一个 name 参数,如果没有传递,默认为 ""
*/
@Target(ElementType.METHOD)
@interface MyAnnotation{
String name() default "";
}
/**
* 定义一个注解 MyAnnotation2,该注解只能在方法上使用
* 该注解需要传递一个 value 参数
*/
@Target(ElementType.METHOD)
@interface MyAnnotation2{
String value();
}
# 反射
# 理解反射
反射是 java 被视为动态语言的关键,反射机制允许程序在执行期间借助 Reflection API 取得任何类的内部消息,并能直接操作任意对象的内部属性和方法
加载完类后,在堆内存的方法区中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象,并且 Class 对象不是我们 new 出来的,是类加载器(ClassLoader)加载出来的),这个对象就包含了完整的类的结构信息,因此我们可以通过这个对象看到类的结构。换言之,这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以,我们也形象的称之为反射
反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。
正常方式
- 引入需要的包类名称
- 通过 new 实例化
- 取得实例化对象
反射方式
- 取得实例化对象
- 调用 getClass() 方法
- 取得完整的包类名称
# 优缺点
# 优点
可以实现动态创建对象和编译,体现出很大的灵活性
# 缺点
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉 JVM ,我们希望做什么并且满足我们的要求。这类操作总是慢于直接执行相同的操作。
# 反射相关类
# java.lang.Class
# java.lang.reflect.Field
# java.lang.reflect.Method
# java.lang.reflect.Constructor
# 获取Class类对象
前提:已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFountException 异常。
实例:Class cls = Class.forName("java.lang.Cat");
应用场景:多用于配置文件,读取类全路径,加载类
前提:若已知具体的类,通过类的 class 属性获取,该方法最为安全可靠,程序性能最高。
实例:Class cls = Cat.class;
应用场景:多用于参数传递,比如通过反射,得到对应构造器对象
前提:已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象。
实例:Class cls = 对象.getClass();
应用场景:通过创建好的对象,获取 Class 对象
其它方式:通过类加载器(4种)
ClassLoader cl = 对象.getClass().getClassLoader();
Cl.loadeClass(类的全路径);
基本数据类型的 Class 类对象
Class cl = 基本数据类型.class;
包装数据类型的 Class 类对象
Class cl = 包装数据类型.TYPE;
Class<?> aClass = Class.forName("basicNode.TreeNode");
System.out.println("第一种方式获取 Class 对象:" + aClass);
Class<TreeNode> treeNodeClass = TreeNode.class;
System.out.println("第二种方式获取 Class 对象:" + treeNodeClass);
TreeNode treeNode = new TreeNode(12);
Class<? extends TreeNode> aClass1 = treeNode.getClass();
System.out.println("第三种方式获取 Class 对象:" + aClass1);
ClassLoader classLoader = aClass1.getClassLoader();
Class<?> aClass2 = classLoader.loadClass("basicNode.TreeNode");
System.out.println("第四种方式获取 Class 对象:" + aClass2);
Class<Integer> integerClass = int.class;
System.out.println("基本数据类型的 Class 对象:" + integerClass);
Class<Boolean> type = Boolean.TYPE;
System.out.println("包装数据类型的 Class 对象:" + type);
# 哪些类型有Class对象
- 类
- 接口(interface)
- 数组
- 枚举(enum)
- 注解(annotation)
- 基本数据类型
- void
Class<String> stringClass = String.class;// 普通类
Class<Serializable> serializableClass = Serializable.class;// 接口
Class<Integer[]> aClass = Integer[].class;// 数组
Class<Deprecated> deprecatedClass = Deprecated.class;// 注解
Class<Thread.State> stateClass = Thread.State.class;// 枚举
Class<Void> voidClass = void.class;// void
Class<Class> classClass = Class.class;// Class
# 类加载
反射机制是 java 实现动态语言的关键,也就是通过反射来实现类的动态加载
- 静态加载:编译时加载相关的类,如果没有,则报错,依赖性太强
- 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。反射就是采用了这种加载方式
类加载的时机
- 当通过 new 的方式创建对象时。属于静态加载
- 当子类被加载时,父类也被加载。属于静态加载
- 调用类中的静态成员时。属于静态加载
- 通过反射。属于动态加载