一、反射机制 反射机制允许程序在执行期借助Reflection API取得任何类的内部信息(比如成员变量、构造器、成员方法等)并能操作对象的属性和方法,反射在设计模式和框架底层都会用到。
加载完类之后,在堆中就会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。
1.1 Java反射机制可以完成
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时得到任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的成员变量和方法
生成动态代理
1.2 反射主要的类 java.lang.Class 代表一个类,Class对象表示某个类加载后在堆中的对象
java.lang.reflect.Method 代表类的方法,Method对象表示某个类的方法
java.lang.reflect.Field 代表类的成员变量,Field对象表示某个类的成员变量
java.lang.reflect.Constructor 代表类的构造方法,Constructor对象表示某个类的构造器
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 public static void main (String[] args) throws Exception { Class cls=Class.forName("类的全限定名" ); Object o=cls.newInstance(); System.out.println(o.getClass()); Method method1=cls.getMethod("方法名" ); System.out.println("=======" ); method1.invoke(o); Field nameField=cls.getField("属性名" ); System.out.println(nameField.get(o)); Constructor constructor=cls.getConstructor(); System.out.println(constructor); Constructor constructor1=cls.getConstructor(String.class); System.out.println(constructor1); }
1.3 反射优点和缺点 优点:可以动态的创建和使用对象,使用领灵活,没有反射机制,框架技术失去底层支撑
缺点:使用反射基本是解释执行,对执行速度有影响
1.4 反射调用优化 Method、Field、Constructor对象都有setAccessible()方法,其作用是启动和禁用访问安全检查的开关,参数为true表示反射的对象在使用时取消访问检查,提高反射效率。参数值为false表示反射的对象执行访问检查。
二、Class类
如图,Class也是类,继承Object类
Class类对象不是new出来的,而是系统创建的
对于某个类的Class类对象,在内存中只有一份,类只加载一次
每个类的实例都会记得自己由哪个Class实例所生成的
通过Class的一系列API可以完整的得到一个类的完整结构
Class对象存放在堆中
类的字节码二进制数据,放在方法区的,有的地方称为类的元数据(包含方法代码、变量名、方法名、访问权限等)
2.1 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 public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException { String classFullPath = "base.reflection.pojo.Cat" ; Class cls = Class.forName(classFullPath); System.out.println(cls); System.out.println(cls.getClass()); System.out.println(cls.getPackage().getName()); System.out.println(cls.getName()); Cat cat = (Cat) cls.newInstance(); System.out.println(cat); Field age = cls.getField("age" ); System.out.println(age.get(cat)); age.set(cat, 20 ); Field[] fields = cls.getFields(); for (Field field : fields) { System.out.println(field); } }
2.2 获取Class类对象的方法
已知一个类的全类名,而且类在类路径下,可通过Class的静态方法forName()获取。应用场景:多用于配置文件、读取类的全路径,加载类
1 Class cls = Class.forName("类的全路径" );
已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高。应用场景:多用于参数传递
已知某个类的实例,调用该实例的getClass()方法获取Class对象。应用场景:通过创建好的对象,获取Class对象
1 Class cls3=cat.getClass();
通过类加载器创建
1 2 ClassLoader cl=对象.getClass().getClassLoader(); Class cls4=cl.loadClass("类的全类名" );
基本数据类型(八种)
基本数据类型对应的包装类,可以通过.TYPE得到Class对象(获取的是基本数据类型的Class对象)
相同的基本类型和包装类.TYPE获取的Class类对象是同一个
2.3 具有Class对象的类型
外部类、成员内部类、静态内部类、局部内部类、匿名内部类
interface:接口
数组
enum:枚举
annotation:注解
基本数据类型
void
三、类加载 反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
静态加载:编译时加载相关的类,如果不存在则报错,依赖性太强
动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性
类的加载时机
当创建对象时(静态加载)
当子类被加载时,父类也加载(静态加载)
调用类中的静态成员时(静态加载)
通过反射(动态加载)
类的加载过程
java源码编译为字节码文件进行类的加载(类的加载分为三个阶段,如图),类加载后内存布局情况:类的字节码(二进制数据)存放于方法区,类的Class对象存放于堆区。
3.1 加载阶段 JVMZ在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象
3.2 连接阶段 验证
目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
包括:文件格式验证、元数据验证、字节码验证和符号引用验证
可以使用-Xverify:none参数关闭大部分的类验证措施,缩短虚拟机加载的时间
准备 JVM会在该阶段对静态变量,分配内存并默认初始化。这些变量所使用的内存都将在方法去中进行分配
解析 虚拟机将常量池内的符号引用替换为直接引用的过程
3.3 初始化
到初始化阶段才真正开始执行类中定义的Java程序代码,此阶段是执行()方法的过程
()方法由编译器按语句在源文件中出现的顺序,依次自动收集类中所有的静态变量的赋值动作和静态代码块中的语句,并进行合并。
虚拟机会保证一个类的()方法在多线程环境中被正确的加锁,同步。如果多个线程同时去初始化一个类,那么只有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。
四、反射相关方法的使用 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 public class Car { public Integer id; private String address; public Car () { } public Car (Integer id) { this .id = id; } private Car (Integer id, String address) { this .id = id; this .address = address; } public void start (Integer id) { System.out.println("编号为:" +id+"的汽车开始运行" ); } private void restart (String address) { System.out.println("原产地为:" +address+"的汽车重启" ); } @Override public String toString () { return "Car{" + "id=" + id + ", address='" + address + '\'' + '}' ; } }
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 public static void main (String[] args) throws Exception { Class cls=Class.forName("base.reflection.pojo.Car" ); Object o=cls.newInstance(); System.out.println(o); System.out.println(Integer.class); Constructor constructor=cls.getConstructor(Integer.class); Object o1=constructor.newInstance(126 ); System.out.println(o1); Constructor constructor1=cls.getDeclaredConstructor(Integer.class,String.class); constructor1.setAccessible(true ); Object o2=constructor1.newInstance(100 ,"新西兰" ); System.out.println(o2); Field fieldId=cls.getField("id" ); fieldId.set(o,123 ); Field fieldAddress=cls.getDeclaredField("address" ); fieldAddress.setAccessible(true ); fieldAddress.set(o,"冰岛" ); System.out.println(o); Method methodStart=cls.getMethod("start" ,Integer.class); methodStart.invoke(o,6 ); Method methodRestart=cls.getDeclaredMethod("restart" ,String.class); methodRestart.setAccessible(true ); methodRestart.invoke(o,"英国" ); }
视频资料 :【韩顺平讲Java】Java反射专题