Java反射&动态代理

目录

语言基础

编译型

解释型

混合型

反射

获取class字节码文件对象

获取构造方法(创建对象)

获取成员变量(赋值,获取值)

获取成员方法(运行)

动态代理

动态代理三要素:

真正干活的对象

代理对象

利用代理调用方法

代理类

主程序

ps:接口类


语言基础

先说结论Java是一门跨平台,混合型的语言。因为所有程序的运行都要分为这么三个步骤

1.程序员编写代码脚本。像c语言写完后会有个helloworld.c文件,python有个hello.py文件,java会有一个hello.java文件 。

2对编写好的语言进行编译或者说翻译变成二进制内容,因为计算机只认识0-1。

3.将二进制内容进行执行运行。

在这里针对第二步的不同将语言进行了分类分别是编译型,解释性和混合型,java就是混合型。

编译型

典型的就是c语言了,会先对 .c 文件进行一次编译,然后生成一个obj文件然后运行,因为编译不同操作系统,不同设备硬件可能存在差异,所以.c 文件需要每次都编译一次。

解释型

典型的就是python了,它写完代码后不会再生成多余的文件,而是直接按行就解释了,所以说python的代码就直接是源码了。

混合型

顾名思义就是既有编译又有解释,最典型的就是java了,在写完一个.java文件后需要先编译生成一个.class的二进制字节码文件,然后再按行对这个class字节码文件进行按行的解释,并且解释的平台是jvm,因此而具有很好的跨平台性。这里要再次强调一下java 是会在编译成class文件后还会逐行进行解释运行

反射

反射就是程序在运行的过程中,能知道类的属性和方法,能调用对象的属性和方法。并且利用反射 创建的对象可以无视修饰符调用类里面的内容

反射都是要从class字节码文件中获取的内容。

  • 如何获取class字节码文件的对象

  • 利用反射如何获取构造方法(创建对象)

  • 利用反射如何获取成员变量(赋值,获取值)

  • 利用反射如何获取成员方法(运行)

获取class字节码文件对象

一共有如下3种方法

  • Class这个类里面的静态方法forName("全类名")(最常用)

    • Class clazz1 = Class.forName("com.itheima.reflectdemo.Student");
  • 通过具体类的class属性获取

    • Class clazz2 = Student.class;
  • 通过对象获取字节码文件对象的getClass()方法

    • Class clazz3 = s.getClass();

获取构造方法(创建对象)

假设clazz是Student这个类的 字节码文件对象

1.获取空参的构造方法

Constructor con = clazz.getConstructor();

2.利用空参构造方法创建对象

Student stu = (Student) con.newInstance();

获取成员变量(赋值,获取值)

假设clazz是Student这个类的 字节码文件对象,s是具体的Student类的一个具体的对象

获取类属性--name

Field field = clazz.getDeclaredField("name");

临时修饰name的访问权限

field.setAccessible(true);

设置(修改)name的值

field.set(s,"wangwu");

表示我要获取这个对象的name的值,获取name的值,查看验证

String result = (String)field.get(s);

获取成员方法(运行)

假设clazz是Student这个类的 字节码文件对象,s是具体的Student类的一个具体的对象

获取一个指定的方法

Method eatMethod = clazz.getMethod("eat",String.class);

运行

String result = (String) eatMethod.invoke(s, "重庆小面");

动态代理

动态代理三要素:

真正干活的对象

代理对象

利用代理调用方法

动态代理简单的理解就是通过反射,去运行某个类的某个的具体对象的方法。因此动态代理可以,无侵入式的给方法增强功能 ,另外代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。java专门为这种动态代理提供了一个类newProxyInstance。

代理类

1.先自定义一个代理类名字是ProxyUtil

2.定义名创建需要被代理的类对象,也就是代理经纪人这里是Star star = (Star)Proxy.newProxyInstance(xxx)

2.1继续补充newProxyInstance里面的参数,第一个参数是ProxyUtil.class.getClassLoader(),是固定的,是一个类加载器,可以理解为准备开始逐行解释class文件了

2.2第二个参数要指定接口,new Class\[\]{Star.class},这些接口用于指定生成的代理要实现哪些方法这里是"唱歌","跳舞"方法

2.3第三个参数也是最麻烦的new InvocationHandler() {xxx}进行真正的代理运行要实现的方法要新建一个InvocationHandler类对象(接口)然后再对象的方法里面用反射实现调用

2.3.1这里是具体的方法实现{public Object invoke(Object proxy, Method method, Object\[\] args)} ,返回Object,这里invoke不能用其他的名字,因为这个是抽象方法必须要实现。然后这里的第一个参数是就是被代理的对象,第二个是被代理对象要调用的方法,第三个是方法的传入值。

java 复制代码
//类
public class ProxyUtil {

        
    public static Star createProxy(BigStar bigStar){


    Star star = (Star) Proxy.newProxyInstance(
        
        ProxyUtil.class.getClassLoader(),
        new Class[]{Star.class},
        new InvocationHandler(){
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
                    if("sing".equals(method.getName())){
                            System.out.println("准备话筒,收钱");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地,收钱");
                        }
                        //去找大明星开始唱歌或者跳舞
                        //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigStar,args);
                
                }
            }

        );
return star;

}



}
主程序

1.创建对象BigStar bigStar = new BigStar("鸡哥");

2.创建代理对象Star proxy = ProxyUtil.createProxy(bigStar);

3.调用代理

String result = proxy.sing("只因你太美");

System.out.println(result);

ps:接口类
java 复制代码
public interface Star {
    //我们可以把所有想要被代理的方法定义在接口当中
    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();
}