文章目录
Java反射机制概述
- 反射可以调用
任意一个
对象的成员变量和方法,包括私有的
。
反射相关的主要API
理解Class类并获取Class的实例
-
加载到内存中的类,叫运行时类------>即,Class类的实例【由于Class类是
描述类
的类,所以Class类的实例也是类(具体是,运行时类)
;一般类是描述对象的,一般类的实例是对象】
顾名思义,类加载到内存中,相当于运行了,所以叫运行时类,比如Person类,加载到内存中的Person叫运行时类。此时Preson其实就是一个Class实例,只不过为了区分Person是Class实例(运行时类)还是原本的Person类,用Person.class的方式表示Class实例 -
一个类对应一个.class文件
理解Class类
- Class 类是
描述类的
类。此类是Java反射的源头
,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。【如下图】
Class类特点
Class类的常用方法
获取Class类的实例(即获取运行时类)【重点】
- 注:这里
对象和实例
是同一个概念,它们的区别参考 - 由于Class类是
描述类
的类,所以Class类的实例是类(具体是,运行时类)
。如下:
//clazz是运行时类Person(Class类的实例)的引用;
Class clazz = Person.class;
//通过newInstance()创建运行时类Person的对象;由于clazz是运行时类Person的引用,所以obj是Person对象的引用。
Object obj = clazz.newInstance();
- 方式3运用最多
类的加载与ClassLoader的理解
ClassLoader(类加载器)加载配置文件
-
Properties(集合中的):用来读取配置文件
-
类加载器的一个
主要方法
:getResourceAsStream(String str)
:获取类路径
下的指定文件的输入流
。
InputStream in = null;
in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
System.out.println(in);
- 文件位置
创建运行时类的对象【重点】
有
无参构造器情况下,创建运行时类对象:【用的多】
//1、创建Class类的实例(对象);
//clazz是运行时类Person(Class类的实例)的引用;
Class clazz = Person.class;
//2、通过newInstance()创建运行时类Person的对象;由于clazz是运行时类Person的引用,所以obj是Person对象的引用。
Object obj = clazz.newInstance();
//由于知道clazz是运行时类Person的引用,所以也可做强转
Person obj = (Person)clazz.newInstance();
没有
无参构造器情况下,创建运行时类对象:【用的少】
反射获取运行时类的完整结构【了解】
反射获取运行时类的属性结构及内部结构
反射获取运行时类的方法结构
。。。。。。
调用运行时类的指定结构【重点】
调用指定属性、方法、构造器的通用两步
1、//创建Class类的实例(这里是Person.class),即,Class类对应的运行时类
Class clazz = Person.class;
2、//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//JAVA9之后不推荐使用newInstance()方法,推荐下面方法
//Object p = clazz.getConstructor().newInstance();
- 下面例子用到的Person类如下
public class Person {
public static int id = 0;
private String name;
public Person(){}
private Person(String name){
this.name = name;
}
private String show(String nation){
System.out.println("我的国籍是: " + nation);
return nation;
}
public static void showDwsc(String who){
System.out.println(who + "是一个可爱的人");
}
public void run(){}
}
调用指定属性
- getField)()返回的是指定的
public
属性,getDeclaredField()返回的是指定的所有权限
的属性,所以以后都用getDeclaredField()即可
- 在需要获取的属性是私有的情况下,需setAccessible (true)方法使其可访问,而需要获取的属性不是私有的情况下,则不需要etAccessible (true)方法,所以可不管什么情况都设置
setAccessible (true)方法
即可。
-
例子,调用指定属性:
-
创建运行时类的对象,原因是调用非静态属性需要创建对象,调用静态属性不需要创建对象(静态属性随着类的加载而加载),
所以不管什么情况直接创建运行时类的对象
,用运行时类的对象调用属性即可。 -
对于静态属性的调用方法:
Field id = clazz.getDeclaredField("id");
name.setAccessible(true);
id.set(Person.class , 10);
或
id.set(clazz , 10);
或
id.set(null , 10);//可以加null,是因为id是通过clazz.getDeclaredField("id")得到的,所以已经知道id是属于哪个运行时类的了
调用指定方法
-
invoke:调用
-
反射中,invoke调用返回相应函数的返回值:
-
普通调用:
对象.方法名(参数)
反射调用:方法名.invoke(运行时类的对象 , 参数)
由上看出,两种调用的 对象和方法名 在点“.”
两边的顺序颠倒,这也体现了反射这个名字的含义。 -
对于静态方法的调用:
-
创建运行时类的对象,原因是调用非静态方法需要创建对象,调用静态方法不需要创建对象(静态方法随着类的加载而加载),
所以不管什么情况直接创建运行时类的对象
,用运行时类的对象调用方法即可。
Class clazz = Person.class;
Person p = (Person) clazz.newInstance();
1、
Method showDwsc = clazz.getDeclaredMethod("showDwsc" , String.class);
2、
showDwsc.setAccessible(true);
3、
showDwsc.invoke(p , "我");//通用方法,掉静态非静态方法都行。
showDwsc.invoke(Person.class, "我");
showDwsc.invoke(clazz, "我");
showDwsc.invoke(null, "我");
Object returnVal = showDwsc.invoke(clazz, "我");
System.out.println(returnVal);//因为showDwsc()返回值是void,所以输出null
输出
我是一个可爱的人
我是一个可爱的人
我是一个可爱的人
我是一个可爱的人
我是一个可爱的人
null
调用指定构造器(了解)
动态代理(体现了反射的动态性)
概述
在静态代理中,一个真实对象(被代理对象)对应一个代理对象,这样真实对象多,代理对象也很多,又由于代理对象去代理真实对象的实现方法很通用,所有我们可以将代理对象的实现方法抽象出来,用一个类来表示代理对象的实现方方式,这个类在运行期间
,根据加载到内存当中的真实对象,动态的
创建对应的代理类。由于代理类和真实对象实现同一个接口,在运行期间看加载的真实对象实现了哪个接口,动态
创建的对应的代理类也实现哪些接口。
- 要想在运行期间创建代理对象,就得通过
反射
来做。
之所以要通过反射来做:比如动态代理,真实对象在编译时确定,代理对象需要在运行时动态创建,而代理对象需要根据真实对象进行动态创建,又因为此时真实对象已处于运行时状态(即,运行时类),所以需要通过反射来获取真实对象的个属性、方法等。
Java动态代理相关API
代码实现例子
package com.tong.pojo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//抽象接口
interface Human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements Human{
public String getBelief() {
return "I believe I can fly!";
}
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}
class ProxyFactory{
//调用此方法,根据加载到内存中的被代理类,返回一个代理类的对象。-----------【解决问题一】
// 你得告诉我此时创建的代理类,是代理哪个被代理类的,所以要传进来obj
public static Object getProxyInstance(Object obj){//obj:被代理类的对象
//⑤===============================================================================================================
MyInvocationHandler handler = new MyInvocationHandler();
//handler.bind()作用是使用被代理类的对象赋值,又由于参数obj即为被代理类的对象,所以直接将obj传入即可
handler.bind(obj);
//①===============================================================================================================
//创建代理类的对象
//第一个参数:类加载器:代理类是由哪个类加载器加载的,代理类和被代理类是由同一个类加载器加载的,所以获得被代理类的类加载器等同于获得代理类的类加载器
//第二个参数:代理类和被代理类共同实现的接口
//第三个参数:【解决的是问题二】
//第三个参数:InvocationHandler(调用处理器):是一个接口,又因为这里作为参数,所以要传入的是它的一个实现类,我们要在下面写一个它的实现类
//前两个参数动态创建代理类,最后一个参数实现,通过代理类的对象调用方法a(被代理类要执行的方法a)时,就会自动调用handler的invoke()方法。
return Proxy.newProxyInstance(obj.getClass().getClassLoader() , obj.getClass().getInterfaces() , handler);
}
}
class MyInvocationHandler implements InvocationHandler{
//④===============================================================================================================
private Object obj;//需要使用被代理类的对象赋值,用下面的bind赋值(也可用构造器)
public void bind(Object obj){
this.obj = obj;
}
//②===============================================================================================================
//当我们通过代理类的对象调用方法a(被代理类要执行的方法a)时,就会自动调用如下的方法:invoke(),所以将被代理类要执行的方法a的功能声明在invoke()中;
//为什么会自动调用invoke()方法呢?是因为:由于InvocationHandler的实例对象handler传给了代理类Proxy.newProxyInstance(...,handler),
// 所以通过getProxyInstance()返回的代理类调用方法a时,会通过传入的handler参数自动的调用invoke()方法,这些已被封装好。
//Object proxy:代理类的对象,也就是上面getProxyInstance()返回的对象
//Method method:代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法。
//Object[] args:同名方法的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//③===============================================================================================================
//obj:被代理的对象
Object returnVal = method.invoke(obj, args);
return returnVal;
}
}
public class ProxyTest {
public static void main(String[] args) {
//创建一个被代理类对象
SuperMan superMan = new SuperMan();
//创建一个代理类对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
//当通过代理类调用方法时,会自动的调用被代理类中同名的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("水果捞");
}
}
结果