一、反射(reflection)
反射: 加载完类之后,在堆中会产生一个 Class 类 对象(一个类只有一个 Class 类对象),这个类对象包含了该类的所有信息。
反射相关的主要类(反射中, 万物皆对象):
java.lang.Class
代表一个类
Class 对象表示某个类加载后再堆中的对象
java.lang.reflect.Method
代表类的方法
Method 对象表示某个类的方法
java.lang.reflect.Field
代表类的成员变量
Field 对象表示某个类的成员变量
java.lang.refect.Constructor
代表类的构造方法
Constructor 对象表示构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class CatDemo1 { public String name = "波斯喵" ; public CatDemo1 () { } public CatDemo1 (String name) { this .name = name; } public void hi () { System.out.println("招财猫~" ); } public void food () { System.out.println("猫吃鱼,狗吃肉~" ); } }
1 2 3 4 // classCat.properties className =com.reflection.CatDemo1 method :food
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class Demo1 { public static void main (String[] args) throws Exception { Properties properties = new Properties (); properties.load(new FileInputStream ("src/classCat.properties" )); String className = properties.getProperty("className" ); System.out.println("className = " + className); String method = properties.getProperty("method" ); System.out.println("method = " + method); Class<?> cls = Class.forName(className); Object o = cls.newInstance(); Method clsMethod = cls.getMethod(method); clsMethod.invoke(o); Field name = cls.getField("name" ); System.out.println(name.get(o)); Constructor constructors = cls.getConstructor(); System.out.println(constructors); Constructor constructors1 = cls.getConstructor(String.class); System.out.println(constructors1); } }
1. Class 类
Class 也是类,也继承了 Object 类;
Class 类对象不是 new 出来的,而是系统创建的;
对于某个类的 Class 类对象,在内存中只有一份,因此类只加载一次;
每个类的实例都会记得自己是由那个 Class 实例做生成;
通过 Class 对象可以完整地得到一个类的完成结构,通过一系列 API 操作;
Class 对象时存在堆中;
类的字节码二进制数据,是放在方法区的,有的地方称为元数据(包括方法代码,变量名,方法名等)
反射优化:
Method 和 Field、Constructor 对象都有 setAccessible() 方法;
setAccessible 作用是启动和禁用访问安全检查的开关
true : 表示反射的对象在使用时取消访问检查, 提高反射效率;
false : 表示反射对象执行访问检查;
class 类的常用方法:
方法名
功能说明
static Class forName(String name)
返回指定类名 name 的 class 对象
Object newInstance()
调用缺省构造函数, 返回该 Class 对象的一个实例
getName()
返回此 Class 对象所表示的实体(类、接口等)名称
Class getSuperClass()
返回当前 Class 对象的父类的 Class 对象
Class [] getInterfaces()
获取当前 Class 对象的接口
ClassLoader getClassLoader()
返回该类的类加载器
Class getSuperclass()
返回表示此 Class 所表示的实体的 超类的 Class
constructor[] getConstructors()
返回一个包含某些 Constructor 对象的数组
Field[] getDeclaredFields()
返回 Field 对象的一个数组
Method getMethod
返回一个 Method 对象, 此对象的形参类型为 paramType
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class ClassDemo2 { public static void main (String[] args) throws Exception { String pathAll = "com.reflection.Car" ; Class<?> cls = Class.forName(pathAll); System.out.println(cls); System.out.println(cls.getClass()); System.out.println(cls.getPackage().getName()); System.out.println(cls.getName()); Car car = (Car) cls.getConstructor().newInstance(); System.out.println(car); Field brand = cls.getField("brand" ); System.out.println(brand.get(car)); brand.set(car, "大奔" ); System.out.println(brand.get(car)); Field[] fields = cls.getFields(); for (int i = 0 ; i < fields.length; i++) { System.out.println(fields[i].get(car)); } } }
获取 Class 类对象的方法:
已知全类名, 且该类在类路径下, 可通过 Class 类的静态方法 forName() 获取;
如 Class cls = Class.forName(“com.reflection.Car”);
主要应用于: 读取配置文件, 读取类全路径, 加载类
已知具体的类, 通过类的 Class 获取, 该方法最 安全可靠, 性能最高
Class cls = Car.class;
主要应用于: 参数传递, 如通过反射得到对应构造器对象;
已知某个类的实例, 调用该实例的 getClass() 方法获取 class 对象
Class cls = 对象.getClass()
主要应用于 : 通过创建好的对象, 获取 class 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class GetClass1 { public static void main (String[] args) throws Exception { String classPath = "com.reflection.Car" ; Class<?> cls1 = Class.forName(classPath); System.out.println(cls1); Class<Car> cls2 = Car.class; System.out.println(cls2); Car car = new Car (); Class<? extends Car > cls3 = car.getClass(); System.out.println(cls3); ClassLoader classLoader = car.getClass().getClassLoader(); Class<?> cls4 = classLoader.loadClass(classPath); System.out.println(cls4); } }
2. 类加载
静态加载 : 编译时加载相关的类, 如果没有则报错, 依赖性太强
动态加载 : 运行时加载需要的类, 如果运行时不用该类, 则不报错, 降低依赖性;
类加载的时机:
当创建对象时( new) - 静态加载;
当子类被加载时 - 静态加载;
调用类中的静态成员时 - 静态加载;
通过反射 - 动态加载;
类加载的三个阶段:
加载阶段 : 将字节码文件从不同的数据源 (可能是 class 文件、也可能是 jar 包、甚至网络) 转换为 二进制字节流加载到内存中 , 并生成一个代表该类的 java.long.Class 对象
连接阶段 :
验证 : 为了确保 Class 文件的 字节流中包含的信息符合当前 JVM 的要求 , 且不危害 JVM 自身安全;
包括: 文件格式验证、元数据验证、字节码验证 和 符号引用验证
可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施, 缩短 JVM 类加载的时间;
准备 : 对静态变量 分配内存并初始化 (对应数据类的默认初始值, 如 0, null, false 等), 这些变量的初始值都会在方法区中进行分配;
public int n1 = 10; : 是实例属性, 在准备阶段不会分配内存
public static int n2 = 20;: 是静态变量, 会分配内存, 初始值是 0, 而不是 20;
public static final int n3 = 30;: 是常量, 一旦不知就不可变;
解析 : JVM 将常量池内的符号引用替换为直接引用的过程;
初始化阶段 : 真正开始执行类中定义的 Java 程序代码, 此阶段是执行 <clinit>() 方法的过程;
<clinit>() : 是由编译器按语句在源文件中出现的顺序, 依次自动收集类中的所有 静态变量 的赋值动作和静态代码块 中的语句, 并进行合并
JVM 会保证一个类 <clinit>() 方法在多线程环境中被正确的加锁、同步, 如果多线程同时去初始化一个类, 那么只会有一个线程去执行这个类 <clinit>() 方法, 其他线程都会阻塞等待, 直到活动线程执行完毕;
3. 获取类的结构信息 1. 类对象
getName
获取全类名
getSimpleName
获取简单类名
getFields
获取所有 public 修饰的属性, 包括本类以及父类的
getDeclaredFields
获取本类中所有属性
getMethods
获取所有 public 修饰的方法, 包括 本类以及父类的
getDeclaredMethods
获取本类中的所有方法
getConstructors
获取所有 public 修饰的本类构造器
getDeclaredConstructors
获取本类中所有的构造器
getPackage
以 package 形式返回 包信息
getSuperClass
以 class 形式返回 父类信息
getInterfaces
以 class[] 形式返回 接口信息
getAnnotations
以 Annotation[] 形式返回注释信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 public class ReflectionDemo1 { @Test public void api_01 () throws Exception { Class<?> cls = Class.forName("com.reflection.Person" ); System.out.println(cls.getName()); System.out.println(cls.getSimpleName()); Field[] fields = cls.getFields(); for (Field field : fields) { System.out.println("本类以及父类的public属性" +field.getName()); } Field[] declaredFields = cls.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("获取本类中所有属性" + declaredField.getName()); } Method[] methods = cls.getMethods(); for (Method method : methods) { System.out.println("本类以及父类的public方法" +method.getName()); } Method[] declaredMethods = cls.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("获取本类中的所有方法" + declaredMethod.getName()); } Constructor<?>[] constructors = cls.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("本类以及父类的public构造器" + constructor.getName()); } Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("获取本类中所有的构造器" +declaredConstructor); } Package aPackage = cls.getPackage(); System.out.println(aPackage); Class<?> superclass = cls.getSuperclass(); System.out.println(superclass); Class<?>[] interfaces = cls.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println(anInterface.getName()); } Annotation[] annotations = cls.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } } } class A { public String hobby; } class Person extends A { public String name; protected int age; String job; private double sal; public void m1 () {} protected void m2 () {} void m3 () {} private void m4 () {} }
2. 属性对象
命令
说明
getModifiers
以 int 形式返回修饰符, 默认修饰符 为 0 [ public 为 1, private 为 2, protected 为 4, static 为 8, final 为 16] 如: protected static int age ; 返回 int 值为 12
getType
以 Class 形式返回类型
getName
返回属性名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class ReflectionDemo1 { @Test public void api_01 () throws Exception { Class<?> cls = Class.forName("com.reflection.Person" ); Field[] fields1 = cls.getDeclaredFields(); for (Field field : fields1) { System.out.println("int 形式返回修饰符: " + field.getName() +"=" +field.getModifiers() + " " + field.getType()); } } } class A { public String hobby; } class Person extends A { public String name; protected static int age; String job; private double sal; public void m1 () {} protected void m2 () {} void m3 () {} private void m4 () {} } int 形式返回修饰符: name=1 class java .lang.String nameint 形式返回修饰符: age=12 int ageint 形式返回修饰符: job=0 class java .lang.String jobint 形式返回修饰符: sal=2 double sal
3. 方法对象
命令
说明
getModifiers
以 int 形式返回修饰符, 默认修饰符 为 0[ public 为 1, private 为 2, protected 为 4, static 为 8, final 为 16] 如: protected static int age ; 返回 int 值为 12
getReturnType
以 Class 形式获取, 返回类型
getName
返回方法名
getParameterTypes
以 Class[] 返回参数类型数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class ReflectionDemo1 { @Test public void api_01 () throws Exception { Class<?> cls = Class.forName("com.reflection.Person" ); Method[] methods = cls.getDeclaredMethods(); for (Method method : methods) { System.out.println("本类中所有的方法: " + method.getName() + " 修饰符值=" + method.getModifiers() +" " + method.getReturnType()); Class<?>[] types = method.getParameterTypes(); for (Class<?> type : types) { System.out.println("形参数据类型" +type.getName()); } } } } class A { public String hobby; } class Person extends A { public String name; protected static int age; String job; private double sal; public String m1 (String name, int age, String job) { return "" ; } protected void m2 () {} void m3 () {} private void m4 () {} } 本类中所有的方法: m3 修饰符值=0 void 本类中所有的方法: m2 修饰符值=4 void 本类中所有的方法: m1 修饰符值=1 class java .lang.String 形参数据类型java.lang.String 形参数据类型int 形参数据类型java.lang.String 本类中所有的方法: m4 修饰符值=2 void
4. 构造器对象
命令
说明
getModifiers
以 int 形式返回修饰符
getName
返回构造器(全类名)
getParameterTypes
以 Class[] 返回参数类型数组
4. 通过反射创建对象 Class 类 相关方法:
命令
说明
newInstance
调用类中的无参构造器, 获取对应类的对象
getConstructor(Class…)
根据参数列表, 获取对应的 public 构造器对象
getDecalaredConstructor(Class…)
根据参数列表, 获取对应的所有构造器对象
Constructor 类相关方法:
命令
说明
setAccessible
爆破, 使反射可以访问 private 构造器
newInstance(object…)
调用构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class Demo2 { public static void main (String[] args) throws Exception { Class<?> userClass = Class.forName("com.reflection.User" ); User user = (User) userClass.getConstructor().newInstance(); System.out.println(user); Constructor<?> constructor = userClass.getConstructor(String.class); Object o = constructor.newInstance("宝塔镇河妖" ); System.out.println(o); Constructor<?> dc = userClass.getDeclaredConstructor(int .class, String.class); dc.setAccessible(true ); Object o1 = dc.newInstance(200 , "小妖怪" ); System.out.println(o1); } } class User { private int age = 10 ; private String name = "天王盖地虎" ; public User () {} public User (String name) { this .name = name; } private User (int age, String name) { this .age = age; this .name = name; } @Override public String toString () { return "User{" + "age=" + age + ", name='" + name + '\'' + '}' ; } }
5. 通过反射访问属性
根据属性名获取 Field 对象
Field f = class 对象.getDeclaredField(属性名);
爆破 : f.setAccessible(true);
访问:
f.set(o, 值); // o 表示对象
syso(f.get(o));
如果是静态属性, 则 set 和 get 中的参数 o, 可以写成 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class Demo3 { public static void main (String[] args) throws Exception { Class<?> cla = Class.forName("com.reflection.Student" ); Object o = cla.getConstructor().newInstance(); Field age = cla.getField("age" ); age.set(o, 88 ); System.out.println(o); System.out.println(age.get(o)); Field name = cla.getDeclaredField("name" ); name.setAccessible(true ); name.set(null , "李四" ); System.out.println(o); System.out.println(name.get(null )); } } class Student { public int age; private static String name; public Student () {} @Override public String toString () { return "Student{" + "age=" + age + ",name=" + name +'}' ; } }
6. 通过反射访问方法
根据方法名 和 参数列表获取 Method 方法对象;
Method m = 类对象.getDeclared Method(方法名, XX.class);
获取对象: Object o = 类对象.newInstance();
爆破 : m.setAccessible(true);
访问: Object returnValue = m.invoke(o.实参列表);
在反射中, 如果有返回值, 统一返回 Object 类型, 但是运行类型和定义类型一致
如果是静态方法, 则 invoke 的参数 o, 可以写成 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Demo4 { public static void main (String[] args) throws Exception{ Class<?> cls = Class.forName("com.reflection.Boss" ); Object o = cls.getConstructor().newInstance(); Method hi = cls.getMethod("hi" , String.class); hi.invoke(o, "王五" ); Method say = cls.getDeclaredMethod("say" , int .class, String.class, char .class); say.setAccessible(true ); Object o1 = say.invoke(null , 30 , "赵六" , '叼' ); System.out.println(o1); } } class Boss { public int age; public static String name; public Boss () { } private static String say (int n, String s, char c) { return n+" " +s+" " +c; } public void hi (String s) { System.out.println("hi " + s); } }