八、反射
1、反射的概念
- Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性及方法
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构
2、关于Class类
- Object类中定义了public final Class getClass()方法,此方法被所有的子类继承
- 一个Class对象包含了某个特定的结构
- Class本身也是一个类
- Class对象只能由系统建立对象
- 一个加载的类在jvm中只会有一个Class实例
- 一个Class对象就是对应加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所构成
- 通过Class可以完整的得到一个类中所有被加载的结构
- Class是Reflection的根源,针对你任何想要动态加载、运行的类,唯有先获得响应的Class对象
- Class类的常用方法
- static Class forName(String name) : 返回指定类名 name 的 Class对象
- Object newInstance() 调用缺省的构造函数,返回该Class对象的一个实例
- getName() 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
- Class getSuperClass() 返回当前Class对象的父类的Class对象
- Class[] getInterfaces() 获取当前Class对象的接口
- ClassLoader getClassLoader() 返回该类的类加载器
- Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
- Field[] getDeclaredFields() 返回Field对象的一个数组
- Method getMethod(String naem,Class … paramTypes) 返回一个Method对象,此对象的形参类型为paramTypes
3、获取Class实例的四种方法
-
已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高 (.class)
-
已知某个类的实例,调用该实例的getClass()方法
-
已知一个类的全类名,可通过Class类的静态方法forName(String path),可能会抛出ClassNotFoundException
-
通过获取类的加载器,在通过加载器loadClass(“类的全类名”)获取Class实例
/** * 获取Class实例的四种方式 * 四种方式获取的Class实例都是一样的,证明同一个类在只有一个Class实例 */ @Test public void test1() throws ClassNotFoundException { //方式一:根据类的class属性获取 Class<Person> clazz1 = Person.class; System.out.println(clazz1);//class day01.domain.Person //方式二:根据对象的getClass()方法获取 Person p1 = new Person(); Class<? extends Person> clazz2 = p1.getClass(); System.out.println(clazz2);//class day01.domain.Person //方式三:根据类的全类名使用Class的静态方法forName("全类名")方法获取(会报类找不到异常) Class<?> clazz3 = Class.forName("day01.domain.Person"); System.out.println(clazz3);//class day01.domain.Person //方式四:根据类的加载器ClassLoader获取 Person p2 = new Person(); ClassLoader loader1 = p2.getClass().getClassLoader(); Class<?> clazz4 = loader1.loadClass("day01.domain.Person"); System.out.println(clazz4);//class day01.domain.Person ClassLoader loader2 = Person.class.getClassLoader(); Class<?> clazz5 = loader2.loadClass("day01.domain.Person"); System.out.println(clazz5);//class day01.domain.Person }
4、类的加载过程
- 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时 数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与
- 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程
- 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存 都将在方法区中进行分配
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
- 初始化:JVM负责对类 进行初始化
5、关于类的加载器(了解)
- 类加载器作用是用来把类(class)装载进内存的
- JVM规范定义了三种类型的类加载器
- 引导类加载器:用C++编写的,是JVM自带的类 加载器,负责Java平台核心库,用来装载核心类 库。该加载器无法直接获取
- 扩展类加载器:负责jre/lib/ext目录下的jar包或 – D java.ext.dirs 指定目录下的jar包装入工作库
- 系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工 作 ,是最常用的加载器
6、使用类的加载器来读取配置文件
@Test
public void test2() throws IOException {
//读取配置文件方式一:使用I/O流
Properties prop1 = new Properties();
FileInputStream fis =
new FileInputStream("src\\main\\resources\\jdbc.properties");
prop1.load(fis);
String name1 = prop1.getProperty("name");
String password1 = prop1.getProperty("password");
System.out.println("name = " + name1 + ",password = " + password1);
//读取配置文件方式二:使用类的加载器
ClassLoader loader = ClassTest.class.getClassLoader();
InputStream ras = loader.getResourceAsStream("jdbc.properties");
Properties prop2 = new Properties();
prop2.load(ras);
String name2 = prop2.getProperty("name");
String password2 = prop2.getProperty("password");
System.out.println("name = " + name2 + ",password = " + password2);
}
7、通过反射创建运行时类的对象
-
方式一:调用Class对象的newInstance()方法创建对象
-
方式二:通过获取构造器,调用构造器中的newInstance()方法创建对象
/** * 通过反射创建运行时类的对象 */ @Test public void test3() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { /* 方式一:调用Class对象的newInstance() 方法 注意点:newInstance() 方法实际上还是通过调用运行时类的空参构造器 所以要对运行时类有以下要求: 1.运行时类必须提供空参构造器 2.空参的构造器访问权限要足够,能访问的到,通常为public 补充:javabean要求提供一个public的空参构造器的原因 1.便于通过反射,创建运行时类的对象 2.便于子类继承此运行时类对象时,默认调用super(),保证父类有此构造器 */ Class<Person> clazz1 = Person.class; Person p1 = clazz1.newInstance(); System.out.println(p1);//Person{name='null', age=0} /* 方式二:通过获取构造器来创建运行时类对象 通过Class对象可以获取到运行时类的构造器, 获取的构造器通过调用newInstance(Object ... args)方法创建运行时类的对象 注意:这里面的newInstance() 方法和Class中的方法并不一样, 是Constructor中的newInstance()方法 */ Class<Person> clazz2 = Person.class; Constructor<Person> constructor = clazz2.getConstructor(); Person p2 = constructor.newInstance(); System.out.println(p2); }
8、获取运行时类的完整结构
- Field、Method、Constructor、Superclass、Interface、Annotation
- 获取实现的全部接口
- public Class<?>[] getInterfaces()
- 获取所继承的父类
- public Class<? Super T> getSuperclass()
- 获取属性(Field)
- public Field[] getFields() : 获取当前运行时类及其父类中声明为public权限的属性
- public Field[] getDeclaredFields() : 获取当前运行时类中声明的属性(不包含父类中声明的属性)
- Field中的常用方法:
- public int getModifiers() 以整数形式返回此Field的修饰符
- public Class<?> getType() 返回Field的属性类型
- public String getName() 返回Field的名称
- 获取方法(Method)
- public Method[] getMethods() : 获取当前运行时类及其父类中声明为public权限的方法
- public Method[] getDeclaredMethods() : 获取当前运行时类中声明的方法(不包含父类中声明的方法)
- Method中的常用方法:
- public Class<?> getReturnType() 取得全部的返回值类类型
- public Class<?>[] getParameterTypes() 取得全部的参数
- public int getModifiers() 取得权限修饰符
- public Class<?>[] getExceptionTypes() 获取异常信息
- public Annotation[] getAnnotations() 获取全部的注解
- public int getModifiers() 取得权限修饰符
- 获取构造器(Constructor)
- public Constructor[] getConstructords() : 获取当前运行时类及其父类中声明为public权限的构造器
- public Constructor[] getDeclaredConstructors() : 获取当前运行时类中声明的构造器(不包含父类中声明的构造器)
- Constructor中的常用方法:
- public Class<?>[] getParameterTypes() 取得全部的参数
- public int getModifiers() 取得权限修饰符
- public int getModifiers() 取得权限修饰符
- public Class<?> newInstance(Object … args) 创建一个运行时类的对象
- 获取注解(Annotation)
- public Annotation getAnnotation(Class annotationClass) 获取注解
- public Annotation[] getAnnotations() 获取全部生命的注解
- 获取泛型(GenericType)
- 获取所在的包(Package)
9、调用运行时类的指定结构
-
操作指定运行时类的属性(Field):
/** * 操作指定运行时类的属性Field */ @Test public void test5() throws Exception { Class<Person> clazz = Person.class; Person person = clazz.newInstance(); //1.getDeclaredField() 方法获取属性 Field name = clazz.getDeclaredField("name"); //2.设置属性 name.setAccessible(true);//设置之前必须要开启权限 name.set(person,"张三"); //3.得到属性 System.out.println(name.get(person)); }
-
操作指定运行时类的方法(Method):
/** * 操作指定运行时类的方法Method * 当操作静态方法时,传递 类.class,null都可以 */ @Test public void test6() throws Exception { Class<Person> clazz = Person.class; Person person = clazz.newInstance(); //1.getDeclaredMethod()获取方法 Method showMethod = clazz.getDeclaredMethod("show", String.class); //2.设置是可以访问的 showMethod.setAccessible(true); //invoke() 方法的返回值就是要执行的方法的返回值 String nation = (String) showMethod.invoke(person, "中国"); System.out.println(nation); }
-
操作指定运行时类的构造器(Constructor):
/** * 操作运行时类指定构造器 */ @Test public void test7() throws Exception { Class<Person> clazz = Person.class; Constructor<Person> constructor = clazz.getDeclaredConstructor(); Person person = constructor.newInstance(); System.out.println(person); }
10、动态代理
-
原理:使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上
-
动态代理优点:抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中 处理,这样,我们可以更加灵活和统一的处理众多的方法
-
静态代理的例子:
public class ProxyTest { public static void main(String[] args) { //被代理类对象 ClothFactory nike = new NikeFactory(); //代理类对象 ClothFactory proxyClothFactory = new ProxyClothFactory(nike); //代理类执行方法 proxyClothFactory.product(); } } //抽象接口 interface ClothFactory{ void product(); } //被代理类 class NikeFactory implements ClothFactory{ @Override public void product() { System.out.println("Nike工厂生产衣服"); } } //代理类 class ProxyClothFactory implements ClothFactory{ private ClothFactory factory; public ProxyClothFactory(ClothFactory factory){ this.factory = factory; } @Override public void product() { System.out.println("代理类做一些准备工作"); factory.product(); System.out.println("代理类做一些收尾工作"); } }
-
动态代理:
public class ProxyTest { public static void main(String[] args) { //创建被代理类对象 SuperMan man = new SuperMan(); //根据被代理类创建代理类对象 Human proxy = (Human) ProxyFactory.getProxy(man); String belief = proxy.belief(); System.out.println(belief); proxy.eat("甜甜圈"); } } //抽象接口 interface Human { String belief(); void eat(String food); } //被代理类 class SuperMan implements Human{ @Override public String belief() { System.out.println("I believe I can fly"); return "SuperMan"; } @Override public void eat(String food) { System.out.println("超人爱吃" + food); } } //代理工厂 class ProxyFactory{ //调用此方法返回一个代理类对象 public static Object getProxy(Object obj){//obj:被代理对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.setObj(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handler); } } class MyInvocationHandler implements InvocationHandler{ private Object obj;//用来保存被代理对象 public void setObj(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object returnVal = method.invoke(obj, args); return returnVal; } }