JavaSE7️⃣注解 & 反射
1、注解
1.1、Annotation
JDK 1.5 引入
-
作用:
- 对程序做出解释(该作用类似 comment 注释)
- 可以被程序读取(如编译器)。
-
格式:
@注解名 (参数)
@Override @SuppressWarnings(value = "unchecked")
-
使用范围:
- 在 package、class、method、field 等的上方使用。
- 可通过反射机制访问。
1.2、内置注解
- @Override:重写超类方法。
- @Deprecated:方法不建议使用。
- @SuppressWarnings:抑制编译时的警告信息。
1.3、元注解
元注解 (meta-annotation):负责注解其它的注解
Java 的 4 个元注解:
- @Documented:生成 Java Doc 文档
- @Retention:保留级别(有效期)
- SOURCE:源码
- CLASS:类
- RUNTIME:运行时
- @Target:可用范围
- TYPE:类类型,如 Interface、Class
- FILED:成员变量
- METHOD
- PARAMETER
- CONSTRUCTOR
- LOCAL_VARIABLE
- ANNOTATION_TYPE
- PACKAGE
- 1.8 引入
- TYPE_PARAMETER
- TYPE_USE
- @Inherited:子类可以继承父类中的该注解
1.4、自定义注解
1.4.1、@interface
使用 @interface 自定义注解
该注解会自动实现接口
java.lang.annotation.Annotaion
@元注解
public @interface 注解名{
参数类型 参数名();
// 如:int count() default 0;
}
-
定义参数:在注解内部定义的是参数,而不是方法。
- 仅支持基本类型。
- 可用 default 声明默认值,如 0 或空串。
-
value:若注解只有一个参数且名为 value,使用注解时可用字面量形式。
@注解(value = "xxx") @注解("xxx")
1.4.2、case
case 1
-
源码有效。
-
方法级别可用。
-
两个参数,且带有默认值。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyAnnotation1 { int id() default 0; String name() default ""; }
case 2
-
生成 Java Doc 文档。
-
运行时有效。
-
类级别、方法级别可用。
-
单个参数,且名为 value。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface MyAnnotation2 { String value(); }
2、反射
2.1、Reflection
Java 是静态语言,反射机制使 Java 具有动态性。
2.1、功能特点
-
作用:在运行时
- 实例化对象。
- 访问成员变量和方法。
- 获取泛型信息,处理注解。
- 生成动态代理。
-
特点:
-
优点:动态创建对象和编译,体现出很大的灵活性。
-
缺点:影响性能。反射是一种解释操作(告诉 JVM 要做什么),比直接执行相应操作的效率低。
-
2.2、API
使用 Reflect API 获取类的内部信息,操作对象的内部属性和方法。
-
java.lang.Class:类的元数据
- 在类加载阶段被载入方法区。
- 采用 C++ 的 instanceKlass 描述。
-
java.lang.refect
- Constructor:构造方法
- Field:成员变量
- Method:方法
2.2、Class
Class(反射对象)
类被加载之后,在堆内存的方法区中产生一个 Class 实例。
-
Class 对象:
- Class 本身是一个类。
- Class 中包含类的所有结构:成员变量、方法、构造器、父类、实现的接口、注解等。
- Class 是反射机制的根源,只有获取到 Class 实例才能动态加载和运行对应的类。
-
Class 实例:
- 每个类的 Class 实例唯一。
- 每个类的对象实例,都可以获取自己的 Class 实例。
2.2.1、常用方法
含义 | |
---|---|
forName(全限类名) | 根据全限类名获取 Class 实例 |
newInstance() | 创建该 Class 实例的对象 |
getSuperClass() | 获取父类 Class 对象 |
getInterfaces() | 获取实现的接口 |
getClassLoader() | 获取类加载器 |
getName() | 获取类名 |
getConstructors() | 获取构造器 |
getDeclaredFields() | 获取成员变量 |
getMethod() | 获取方法 |
Hint:getClass() 在 Object 类中定义,所有类都可以调用。
2.2.2、实例获取方式
根据不同已知条件,获取 Class 实例。
获取方式 | |
---|---|
类名 | 类名.class |
对象实例 | 实例名.getClass() |
全类名 | Class.forName(全限类名) |
子类 Class 对象 | 子类.getSuperclass() |
类加载器 | - |
e.g.
以 Person 类为例,其中 Student 是 Person 的子类
@Test
public void testGetInstance() throws ClassNotFoundException {
Person p = new Person();
// 1、已知类名
Class<Person> class1 = Person.class;
// 2、已知类的实例
Class<? extends Person> class2 = p.getClass();
// 3、已知全限类名
Class<?> class3 = Class.forName("indi.jaywee.Person");
// 4、已知子类Class:假设Student.class是已知的
Class<? super Student> class4 = Student.class.getSuperclass();
}
2.2.3、常见 Class 实例
- 类 (class):Class 本身、外部类、内部类、局部内部类、匿名内部类
- 接口 (interface)
- 数组:一维、二维、...
- 枚举 (enum)
- 注解 (annotation)
- 基本数据类型 (primitive type)
- void
e.g.
// 类
Class<Class> class1 = Class.class;
Class<Person> class11 = Person.class;
// 接口
Class<Comparable> class2 = Comparable.class;
// 数组
Class<int[]> class3 = int[].class;
Class<int[][]> class33 = int[][].class;
// 枚举
Class<ElementType> class4 = ElementType.class;
// 注解
Class<Override> class5 = Override.class;
// 基本数据类型
Class<Double> class6 = Double.class;
// void
Class<Void> class7 = void.class;
2.3、类加载机制
2.4、获取类的结构
反射机制可在运行时获取类的结构。
@Test
public void test() throws NoSuchFieldException, NoSuchMethodException {
Class<Person> personClass = Person.class;
/* 类名
getName():全限类名
getSimpleName():简单类名
*/
String name = personClass.getName();
String simpleName = personClass.getSimpleName();
/* 成员变量
getFields():所有public权限的成员变量
getDeclaredFields():所有任意权限的成员变量
getField():指定public权限的成员变量
getDeclaredField():指定任意权限的成员变量
*/
Field[] fields = personClass.getFields();
Field[] declaredFields = personClass.getDeclaredFields();
// Field field1 = personClass.getField("name");
Field declaredField = personClass.getDeclaredField("name");
/* 类的方法
getMethods():所有public权限的方法:包括父类
getDeclaredMethods():所有任意权限的方法:不包括父类
getMethod():指定public权限的方法:包括父类(需要传入参数类型)
getDeclaredMethod():指定任意权限的方法:包括父类(需要传入参数类型)
*/
Method[] methods = personClass.getMethods();
Method[] declaredMethods = personClass.getDeclaredMethods();
Method setName = personClass.getMethod("setName", String.class);
Method setAge = personClass.getDeclaredMethod("setAge", int.class);
/* 构造器:同上
getConstructors():所有public权限的构造器:包括父类
getDeclaredConstructors():所有任意权限的构造器:不包括父类
getConstructor():指定public权限的构造器:包括父类(需要传入参数类型)
getDeclaredConstructor():指定任意权限的构造器:包括父类(需要传入参数类型)
*/
Constructor<?>[] constructors = personClass.getConstructors();
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
Constructor<Person> constructor = personClass.getConstructor(null);
// Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);
}
Constructor-创建实例
知识点:通过反射创建实例,会调用构造方法。
通过Class对象的 newInstance() 方法,可以创建类的实例对象。
- 该方式默认使用类的无参构造器;
- 要求类必须有无参构造器,且访问权限足够。
通过构造器的 newInstance() 方法,也可以创建类的实例化对象
- 获取本类的指定形参的构造器:getDeclaredConstructor (Class<?>... parameterTypes);
- 实例化对象:调用构造器的 newInstance (Object ... initargs) 方法,传递参数。
e.g.
@Test
public void testGetInstance() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> personClass = Person.class;
// 无参构造
Person person1 = (Person) personClass.newInstance();
// 无参构造
Constructor<?> constructor = personClass.getDeclaredConstructor(null);
Person person2 = (Person) constructor.newInstance(null);
// 有参构造
Constructor<?> constructor1 = personClass.getDeclaredConstructor(String.class, int.class);
Person person3 = (Person) constructor1.newInstance("Jaywee", 7);
System.out.println(person1);
System.out.println(person2);
System.out.println(person3);
}
Method-调用方法
- 获取本类的指定形参的方法:通过 Class对象的 getDeclaredMethod (Class<?>... parameterTypes) 方法获取;
- 调用方法:调用方法的 invoke (Object obj, Object... args) 方法
- obj:被调用方法所属的对象;
- args:方法参数;
- 返回值:对应原方法的返回值,若原方法无返回值则为null;
- 注意
- 原方法为静态方法,形参 Object obj 可为 null;
- 原方法形参列表为空, Object[] args 可为 null;
- 原方法权限为 private,需要先调用 setAccessible (true) 方法开启访问权限,才可访问 invoke() 方法。
e.g.
分别以 getter 和 setter 为例
@Test
public void testInvokeMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<Person> personClass = Person.class;
// 创建实例
Person person = personClass.newInstance();
// 获取并调用getter
Method getAge = personClass.getDeclaredMethod("getAge", null);
int age = (int) getAge.invoke(person);
// 获取并调用setter
Method setName = personClass.getDeclaredMethod("setName", String.class);
setName.invoke(person, "Jaywee");
}
Field-获取成员变量
- 获取本类的指定成员变量:通过 Class对象的 getDeclaredField (String name) 方法获取;
- 调用成员变量的相关方法:
- set (Object obj, Object value):相当于 obj.setXxx (value)
- get (Object obj):相当于 obj.getXxx()
- 注意:如果成员变量的权限为 private,需要先调用 setAccessible (true) 方法开启访问权限,才可访问私有变量的方法。
e.g.
@Test
public void testGetField() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
Class<Person> personClass = Person.class;
// 创建实例
Person person = personClass.newInstance();
// 获取name属性
Field name = personClass.getDeclaredField("name");
name.setAccessible(true);
// 相当于调用person.setName("Jaywee")
name.set(person,"Jaywee");
// 相当于调用person.getName()
Object o = name.get(person);
System.out.println(o);
}
setAccessible()
Constructor、Method、Field对象都有 setAccessible() 方法
public void setAccessible(boolean flag)
- 作用:启动或禁用访问安全检查;
- true:允许访问,即禁用安全检查
- 使得私有成员得以访问;
- 提高反射效率,如果有使用到反射的代码需要被频繁使用,需设置 true;
- false:禁止访问,即开启安全检查(默认值)