反射和注解
反射
常用方法1
@Test
public void t1() {
String str = "";
System.out.println(str instanceof String);
System.out.println(str.getClass() == String.class); //直接判断是否为这个类型
// 如果需要判断是否为子类或是接口/抽象类的实现,我们可以使用asSubClass()方法:
Integer i = 10;
i.getClass().asSubclass(Number.class); //当Integer不是Number的子类时,会产生异常
// 通过getSuperclass()方法,我们可以获取到父类的Class对象:
System.out.println(i.getClass().getSuperclass());
// 通过getGenericSuperclass()获取父类的原始类型的Type:
Type type = i.getClass().getGenericSuperclass();
System.out.println(type);
System.out.println(type instanceof Class);
}
针对public 反射
// 反射的方法
@Test
public void t2() throws Exception {
// 通过Class.forName()方法获取Class对象
Class<?> clazz = Class.forName("ref.Stu");
// 使用getConstructor()方法来获取类的构造方法
Constructor<?> constructor = clazz.getConstructor(String.class);
Object instance = constructor.newInstance("what's up");
// 通过调用getMethod()方法,我们可以获取到类中所有声明为public的方法
Method method = clazz.getMethod("test", String.class); //通过方法名和形参类型获取类中的方法
// 我们可以通过Method对象的invoke()方法(返回值就是方法的返回值,因为这里是void,返回值为null)来调用已经获取到的方法,注意传参
method.invoke(instance, "what's up"); //通过Method对象的invoke方法来调用方法
// 通过getField()方法来获取一个类定义的指定字段:
Field field = clazz.getField("i");
field.set(instance, 10); //通过Field对象的set方法来设置属性值
}
反射的目标类
class Stu {
public int i;
public Stu(String str) {
}
public void test(String str) {
System.out.println("萨日朗" + str);
}
}
针对private 反射
@Test
public void t3() throws Exception {
// 处理私有构造器,私有方法
Class<?> clazz2 = Class.forName("ref.Stu2");
// 使用getDeclaredConstructor()方法可以找到类中的非public构造方法
Constructor<?> declaredConstructor = clazz2.getDeclaredConstructor(String.class);
// 但是在使用之前,我们需要先修改访问权限
declaredConstructor.setAccessible(true);
Object instance1 = declaredConstructor.newInstance("what's up");
// 使用getDeclaredMethod()方法可以找到类中的非public方法
Method declaredMethod = clazz2.getDeclaredMethod("test", String.class);
// Method method = clazz.getDeclaredMethod("test", String[].class); //如果方法的形参是数组,我们需要使用数组的Class对象
// 同样,我们需要先修改访问权限
declaredMethod.setAccessible(true);
// 调用invoke()方法
declaredMethod.invoke(instance1, "what's up");
// 使用getDeclaredField()方法可以找到类中的非public字段
Field field1 = clazz2.getDeclaredField("i");
// 同样,我们需要先修改访问权限
field1.setAccessible(true);
// 调用set()方法
field1.set(instance1, 10);
}
反射的目标类
class Stu2 {
private int i;
private Stu2(String str) {
}
private void test(String str) {
System.out.println("萨日朗" + str);
}
}
针对注解
无论是方法、类、还是字段,都可以使用getAnnotations()方法(还有几个同名的)来快速获取我们标记的注解。
// 反射获取注解
@Test
public void t4() throws Exception {
Class<?> clazz = Class.forName("ref.Stu");
Method method = clazz.getMethod("test", String.class);
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
Annotation annotation = method.getAnnotation(Test.class);
System.out.println(annotation);
}
注解
常见注解
- JDK预设了以下注解,作用于代码:
- @Override - 检查(仅仅是检查,不保留到运行时)该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告(仅仅编译器阶段,不保留到运行时)
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
元注解
元注解是作用于注解上的注解,用于我们编写自定义的注解:
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
- @Documented - 标记这些注解是否包含在用户文档中。
- @Target - 标记这个注解应该是哪种 Java 成员。
- @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
自定义注解
//自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Test2 {
String value() default "都看到这里了,给个三连吧!";
// String[] value();也可以是数组
}