反射
概述
java反射机制是在程序运行状态 中,对于任意一个类,都能够知道这个类的所有的属性和方法。对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称之为java的反射机制。
要想解剖一个类,必须先要获取这个类的字节码文件对象。解剖这个类,使用的就是Class类中的方法。字节码文件对象就对应到Class类型的对象
要掌握的单词:
- Class 类
- Constructor 构造方法
- Method 方法
- Field 属性
- Instance 实例
- invoke 执行
获取Class对象
“ 通过类字面量:
Class<?> clazz = String.class;
通过对象实例:
String str = "Hello";
Class<?> clazz = str.getClass();
通过 Class.forName() 方法:
Class<?> clazz = Class.forName("java.lang.String");
创建对象
可以使用反射动态创建对象:
Class<?> clazz = Class.forName("java.lang.String");
Object obj = clazz.getDeclaredConstructor().newInstance();
import java.util.ArrayList;
import java.util.List;
public class AddUserTest {
public static void main(String[] args) throws ClassNotFoundException {
//获取Class对象
//方式一
//接口名/类名.class
Class
<String> stringClass = String.class;
System.out.println(stringClass);
Class
<List> lsitclass = List.class;
System.out.println(lsitclass);
Class<int[]> aclass = int[].class;
System.out.println(aclass);
Class
<Integer> integerClass = Integer.class;
System.out.println(integerClass);
Class
<Double> doubleClass = Double.class;
System.out.println(doubleClass);
//对象名.getClass();
//方式二
List
<String> list = new ArrayList<String>();
System.out.println(list.getClass());
//方式三
//字符串必须是全路径名
Class<?> aClass1 = Class.forName("java.util.Date");
System.out.println(aClass1);
Class<?> aClass2 = Class.forName("cn.javasm.Testdemo");
System.out.println(aClass2);
}
}
访问字段
可以通过反射访问和修改类的字段:
Class<?> clazz = Person.class;
Field field = clazz.getDeclaredField("name");
// 如果字段是私有的,需要设置为可访问
field.setAccessible(true);
// 获取字段值
Object value = field.get(personInstance);
// 设置字段值
field.set(personInstance, "New Name");
import java.lang.reflect.Field;
public class TestDemo3 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
// 获取字符串的hash属性
// 获取字节码文件对象
Class
<String> stringClass = String.class;
// 获取serialVersionUID属性
// stringClass.getDeclaredField("serialVersionUID");
// stringClass.getDeclaredFields();
Field field = stringClass.getDeclaredField("hash");
System.out.println(field);
//暴力破解
String str = "abc";
field.setAccessible(true);
field.set("str", 10);
Object obj = field.get(str);
System.out.println(obj);
//获取所有的属性
Field[] declaredFields = stringClass.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f);
}
//获取属性的修饰符
int modifiers = field.getModifiers();
System.out.println(modifiers);
Class
<Person> personClass = Person.class;
Field name = personClass.getDeclaredField("name");
System.out.println(name);
System.out.println(name.getModifiers());
}
}
修饰符对应返回值
;
method.invoke(personInstance);
Method methodWithArgs = clazz.getMethod("greet", String.class);
methodWithArgs.invoke(personInstance, "World");
获取公共方法 & 获取非公开的方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestDemo4 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// method1();
// method2();
}
private static void method2() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
//获取字节码文件对象
Class
<String> staringClass = String.class;
//获取公共方法
Method method = staringClass.getMethod("substring", int.class, int.class);
String str = "abcd";
// 执行方法
// 参数一 要执行的对象
// 参数二 方法的实参
// 返回值就是方法的返回值
Object invoke = method.invoke(str, 1, 3);
System.out.println(invoke);
}
private static void method1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
//获取字节码文件对象
Class
<Person> personClass = Person.class;
//获取非公开的方法
Method method = personClass.getDeclaredMethod("eat");
Person person = new Person("cyz", 22);
//暴力破解
method.setAccessible(true);
//执行方法
Object invoke = method.invoke(person);
System.out.println(invoke);
}
}
获取枚举类
public class TestDemo5 {
public static void main(String[] args) {
// 获取字节码文件对象
Class
<Level> levelClass = Level.class;
// 获取枚举Level中的所有的枚举常量
Level[] enumConstants = levelClass.getEnumConstants();
for (Level enumConstant : enumConstants) {
System.out.println(enumConstant);
}
}
enum Level {
A, B, C, D, E
}
}
如果不是枚举 返回null
public static void method2() {
Class
<String> stringClass = String.class;
String[] enumConstants1 = stringClass.getEnumConstants();
System.out.println(enumConstants1);
}
获取接口和父类
可以使用反射获取类实现的接口和父类:
Class<?> clazz = Person.class;
// 获取所有接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> i : interfaces) {
System.out.println("Interface: " + i.getName());
}
// 获取父类
Class<?> superClass = clazz.getSuperclass();
System.out.println("Superclass: " + superClass.getName());
获取所有接口
public class TestDemo6 {
public static void main(String[] args) {
Class
<String> stringClass = String.class;
Class<?>[] interfaces = stringClass.getInterfaces();
for (Class<?> i : interfaces) {
System.out.println(i);
}
}
}
// 获取父类 全路径类名 类名 包名
public class TestDemo6 {
public static void main(String[] args) {
Class
<String> stringClass = String.class;
//获取父类
Class<? super String> superclass = stringClass.getSuperclass();
System.out.println(superclass);
//获取全路径类名
String name = superclass.getName();
System.out.println(name);
//获取简单类名
String simpleName = superclass.getSimpleName();
System.out.println(simpleName);
//获取包名
System.out.println(stringClass.getPackageName());
}
}
获取构造方法
可以使用反射获取和调用构造函数:
Class<?> clazz = Person.class;
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("John", 30);
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class TestDemo2 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取字符串字节码文件对象
Class
<String> stringClass = String.class;
// 获取构造方法
//public String(byte ascii[], int hibyte, int offset, int count)
Constructor
<String> constructor = stringClass.getConstructor(byte[].class, int.class, int.class);
// 创建对象
String string = constructor.newInstance(new byte[]{97, 98, 99}, 0, 3);
System.out.println(string);
//获取所有公共的构造方法
Constructor<?>[] constructors = stringClass.getConstructors();
for (Constructor<?> constructor1 : constructors) {
System.out.println(constructor1);
}
//获取指定的构造方法
Constructor
<String> declaredConstructor = stringClass.getDeclaredConstructor(byte[].class, int.class, int.class);
System.out.println(declaredConstructor);
//暴力破解
declaredConstructor.setAccessible(true);
String s1 = declaredConstructor.newInstance(new byte[]{97, 98, 99});
System.out.println(s1);
//获取所有构造方法
Constructor<?>[] declaredConstructors = stringClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor1 : declaredConstructors) {
System.out.println(declaredConstructors);
}
}
}
反射劣势
1 反射打破了封装原则
2 可以跳过泛型检查
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
List
<String> list = new ArrayList<>();
// 获取字节码文件对象
Class<? extends List> aClass = list.getClass();
// 获取add方法
Method method = aClass.getMethod("add", Object.class);
// 执行方法
method.invoke(list, 12);
System.out.println(list);
}
Method和Field相关方法
判断方法
@FirstAnno("abc")
public class TestDemo10 {
int j = 20;
public static void main(String[] args) {
int i = 10;
}
}
// 自定义注解
@interface FirstAnno {
// 在注解中可以定义属性,默认被public static final修饰
public static final int i = 10;
// 在注解中定义变量 使用()
// 注解中的属性使用public修饰
// String str();
//
// double d();
// 设置默认值
char c() default '国';
// 注解中的属性类型只能是基本数据类型/String/Class/注解/枚举以及它们的一维数组形式
// Object obj();
// 如果数组中只有一个元素, {} 可以省略
// int[] arr();
// 如果传递值时只有一个值,并且名称是value,那么可以省略value =
String value();
}
注解
自定义注解
@FirstAnno("abc")
public class TestDemo10 {
int j = 20;
public static void main(String[] args) {
int i = 10;
}
}
// 自定义注解
@interface FirstAnno {
// 在注解中可以定义属性,默认被public static final修饰
public static final int i = 10;
// 在注解中定义变量 使用()
// 注解中的属性使用public修饰
// String str();
//
// double d();
// 设置默认值
char c() default '国';
// 注解中的属性类型只能是基本数据类型/String/Class/注解/枚举以及它们的一维数组形式
// Object obj();
// 如果数组中只有一个元素, {} 可以省略
// int[] arr();
// 如果传递值时只有一个值,并且名称是value,那么可以省略value =
String value();
}
元注解
对注解进行注解的那些注解
@Target({ElementType.METHOD, ElementType.TYPE}) // 限定注解的使用范围
@Retention(RetentionPolicy.RUNTIME) //限定注解的存活范围
@Inherited // 表示这个注解也要作用在子类上
@Documented // 表示这个注解会生成到java文档中
10.3 反射注解
public class TestDemo11 {
public static void main(String[] args) throws NoSuchMethodException {
// 获取字节码文件对象
Class
<Teacher> teacherClass = Teacher.class;
Method method = teacherClass.getMethod("teach");
// 判断类或者方法或者属性上面是否有指定的注解isAnnotationPresent
if (teacherClass.isAnnotationPresent(Anno.class)) {
// 获取所有注解
// teacherClass.getAnnotations();
Anno annotation = teacherClass.getAnnotation(Anno.class);
// 获取注解的值
String value = annotation.value();
if ("初级".equals(value)) {
System.out.println("认真负责");
} else if ("中级".equals(value)) {
System.out.println("三尺讲台,两袖清风");
} else if ("特级".equals(value)) {
System.out.println("桃李满天下");
} else {
System.out.println("辅导员...");
}
} else {
System.out.println("野鸡老师......");
}
}
}
@Anno("特级")
class Teacher {
public void teach() {
System.out.println("老师要教书育人");
}
}
@Retention(RetentionPolicy.RUNTIME)// 保留到运行阶段
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Anno {
String value();
}