一、反射简介:
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
反射库(feflection library)提供了一个非常丰富且精心设计的工具集。以便编写能够动态操纵Java代码的程序。这项功能被大量应用于JavaBean中。
1.1反射机制提供的功能:
1、在运行时分析类的能力,判断任意一个对象所属的类。
2、在运行时构造任意一个类的对象。
3、在运行时判断任意一个类所具有的成员变量和方法。
4、在运行时调用任意一个对象的成员变量和方法。
5、生成动态代理。
1.2 反射相关的主要API:
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法
1.3 Class类
对照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
Class本身也是一个类
Class 对象只能由系统建立对象
一个类在 JVM 中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个 Class 实例所生成
通过Class可以完整地得到一个类中的完整结构
Class类的常用方法
方法名 | 功能说明 |
static Class forName(String name) | 返回指定类名 name 的 Class 对象 |
Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class [] getInterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Class getSuperclass() | 返回表示此Class所表示的实体的超类的Class |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
Method getMethod(String name,Class … paramTypes) | 返回一个Method对象,此对象的形参类型为paramType |
二、创建class实例(重要)
2.1 过程:源文件通过编译(javac.exe)以后,得到一个或多个.class文件。.class.class文件经过运行(java.exe)这步,
就需要进行类的加载(通过JVM的类的加载器),记载到内存中的缓存。每一个放入缓存中的.class文件就是一个Class的实例!
2.2 Class的一个对象,对应着一个运行时类。相当于一个运行时类本身充当了Class的一个实例。
2.3 java.lang.Class是反射的源头。接下来涉及到反射的类都在java.lang.reflect子包下。如:Field Method Constructor Type Package..
当通过Class的实例调用getMethods() --->Method , getConstructors() ---->Constructor
2.4实例化Class的方法(四种):
2.4.1调用运行时类的.class属性
Class clazz1 = Person.class;
System.out.println(clazz1);
Class clazz2 = Creator.class;
System.out.println(clazz2);
2.4.2通过运行时类的对象,调用其getClass()方法
Person p = new Person();
Class clazz3 = p.getClass();
System.out.println(clazz3);
2.4.3调用Class的静态方法forName(String className)。此方法报ClassNotFoundException
String className = "com.xiaomifeng1010.java.Person";
Class clazz4 = Class.forName(className);
System.out.println(clazz4);
2.4.4其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);
三、反射的应用
3.1 创建Class实例之后,应用一:可以创建对应的运行时类的对象(重点)
package com.xiaomifeng1010.review.Animal
public class Animal {
private String name;
public int age;
static String desc = "我是一个动物";
public Animal() {
super();
System.out.println("!!!");
}
private Animal(String name, int age) {
super();
this.name = name;
this.age = age;
}
public static void info(){
System.out.println("动物");
}
public void show(String desc){
System.out.println("我是一个:" + desc);
}
private int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static String getDesc() {
return desc;
}
public static void setDesc(String desc) {
Animal.desc = desc;
}
@Override
public String toString() {
return "Animal [name=" + name + ", age=" + age + "]";
}
}
//获取运行时类的对象:方法一
@Test
public void test1() throws Exception{
Class clazz = Class.forName("com.xiaomifeng1010.review.Animal");
Object obj = clazz.newInstance();
Animal a = (Animal)obj;
System.out.println(a);
}
//调用指定的构造器创建运行时类的对象
@Test
public void test2() throws Exception{
Class clazz = Animal.class;
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
cons.setAccessible(true);
Animal a = (Animal)cons.newInstance("xiaomifeng",18);
System.out.println(a);
}
注意:一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。比如上边的int不是类,但int.class是一个Class类型的对象。
3.2 应用二:获取对应的运行时类的完整的类的结构:属性、方法、构造器、包、父类、接口、泛型、注解、异常、内部类。
如:Method[] m1 = clazz.getMethods() :获取到对应的运行时类中声明的权限为public的方法(包含其父类中的声明的public)
Method[] m2 = clazz.getDeclaredMethods():获取到对应的运行时类中声明的所有的方法(①任何权限修饰符修饰的都能获取②不含父类中的)
3.3 应用三:调用对应的运行时类中指定的结构(某个指定的属性、方法、构造器)(重点)
//调用指定属性
@Test
public void test3() throws Exception{
Class clazz = Class.forName("com.xiaomifeng1010.review.Animal");
Object obj=clazz.newInstance();
Animal a=(Anmal)obj;
//调用非public的属性
Field f1=clazz.getDeclaredField("name");
f1.setAccessible(true);
f1.set(a,"albert");
//调用public的属性
Field f2 = clazz.getField("age");
f2.set(a, 9);
System.out.println(f2.get(a));
System.out.println(a);
//调用static的属性
Field f3 = clazz.getDeclaredField("desc");
System.out.println(f3.get(null));
}
//调用指定的方法
@Test
public void test4() throws Exception{
Class clazz = Class.forName("com.xiaomifeng1010.review.Animal");
Object obj = clazz.newInstance();
Animal a = (Animal)obj;
//调用非public的方法
Method m1 = clazz.getDeclaredMethod("getAge");
m1.setAccessible(true);
int age = (Integer)m1.invoke(a);
System.out.println(age);
//调用public的方法
Method m2 = clazz.getMethod("show", String.class);
Object returnVal = m2.invoke(a,"二哈");
System.out.println(returnVal);
//调用static的方法
Method m3 = clazz.getDeclaredMethod("info");
m3.setAccessible(true);
// m3.invoke(Animal.class);
m3.invoke(null);
}
Class对象功能:
* 获取功能:
1. 获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)
2. 获取构造方法们
//获取所有public修饰的构造方法
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(类<?>... parameterTypes)
//获取所有构造方法。
* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
*setAccessible(true):暴力反射
3. 获取成员方法们:
//获取到对应的运行时类中声明的权限为public的方法(包含其父类中的声明的public)
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
//获取到对应的运行时类中声明的所有的方法(①任何权限修饰符修饰的都能获取②不含父类中的)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
4. 获取全类名
* String getName()
*setAccessible(true):暴力反射
总结:
获取属性,方法,构造方法时,带有declared的获取方式,是获取所有的属性,方法,构造方法,而没有带declared的获取方式,只获取public修饰的属性,方法,构造方法。
四、动态代理
4.1 体会反射的动态性
代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上。
静态代理:要求被代理类和代理类同时实现相应的一套接口;通过代理类的对象调用重写接口的方法时,实际上执行的是被代理类的同样的方法的调用。
静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。 最好可以通过一个代理类完成全部的代理功能.
动态代理:在程序运行时,根据被代理类及其实现的接口,动态的创建一个代理类。当调用代理类的实现的抽象方法时,就发起对被代理类同样方法的调用。
动态代理使用场合: 调试 远程方法调用
涉及到的技术点:①提供一个实现了InvocationHandler接口实现类,并重写其invoke()方法
②Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),h);
//注:obj:被代理类对象 ; h:实现了InvocationHandler接口的实现类的对象
4.2动态代理步骤:
1、创建一个实现接口 实现了InvocationHandler接口实现类,并重写其invoke()方法
2、创建被代理的类以及接口
3、通过proxy的静态方法
//newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理
RealSubject target = new RealSubject();
// Create a proxy to wrap the original implementation
DebugProxy proxy = new DebugProxy(target);
// Get a reference to the proxy through the Subject interface
Subject sub = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class[] { Subject.class }, proxy);
4、通过Subject代理调用RealSubject实现类的方法
String info = sub.say(“Albert", 18);
System.out.println(info);
4.3动态代理与AOP(Aspect Orient Programming)
前面介绍的proxy和InvocationHandler,很难看出这种动态代理模式的优势,下面介绍一种更实用的动态代理机制。
public interface Dog{
void info();
void run();
}
public class HuntingDog implements Dog{
public void info(){
System.out.println("我是一只猎狗");
}
public void run(){
System.out.println("我奔跑迅速");
}
}
public class DogUtil{
public void method1(){
System.out.println("=====模拟通用方法一=====");
}
public void method2(){
System.out.println("=====模拟通用方法二=====");
}
}
public class MyInvocationHandler implements InvocationHandler{
// 需要被代理的对象
private Object target;
public void setTarget(Object target){
this.target = target;}
// 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Exception{
DogUtil du = new DogUtil();
// 执行DogUtil对象中的method1。
du.method1();
// 以target作为主调来执行method方法
Object result = method.invoke(target , args);
// 执行DogUtil对象中的method2。
du.method2();
return result;}}
public class MyProxyFactory{
// 为指定target生成动态代理对象
public static Object getProxy(Object target)
throws Exception{
// 创建一个MyInvokationHandler对象
MyInvokationHandler handler =
new MyInvokationHandler();
// 为MyInvokationHandler设置target对象
handler.setTarget(target);
// 创建、并返回一个动态代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader()
, target.getClass().getInterfaces() , handler);
}
}
public class Test{
public static void main(String[] args)
throws Exception{
// 创建一个原始的HuntingDog对象,作为target
Dog target = new HuntingDog();
// 以指定的target来创建动态代理
Dog dog = (Dog)MyProxyFactory.getProxy(target);
dog.info();
dog.run();
}
}
使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理 。
这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理
面试题:
描述一下JVM加载class文件的原理机制?
答:JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader
是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。