反射

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("水果捞");
    }
}

在这里插入图片描述

结果
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值