关于Java中的反射和动态代理

java的编译期和运行期

参考博文:

https://blog.csdn.net/zl1zl2zl3/article/details/107425987

首先我觉得应该了解一下java中的编译期和运行期的概念。

一张图片:
在这里插入图片描述

  1. 一个.java文件完成运行需要经过哪些步骤?

(1)Java源码–>java字节码

(2)加载java字节码并运行

  1. 编译期间做些什么?

编译器的作用:源代码(原始语言)–> 目标语言

(1)解析与填充符号表:通过词法语法分析,填充符号表。

(2)注解处理器:当我们处理注解时,如果修改了语法树的话,会重新执行分析以及符号填充过程,把注解也填充进来,直到处理完所有的注解。

(3)语义分析:编译器获得语法树之后,无法确定源程序是符合逻辑的。

(4)解语法糖:虚拟机不支持java中的语法糖,他们在编译阶段就被还原成了简单的基础语法结构。

(5)字节码生成:主要工作就是把语法树和符号表加工成字节码文件。

  1. 运行期间做些什么?

java运行期间主要是处理编译器产生的字节码,包括加载与执行。

  1. java程序是如何运行的?

    首先需要把源代码(高级语言)编译成虚拟机可执行的语言(字节码),其次,需要把字节码解释运行后编译成操作系统级别的机器语言,用于执行函数调用。

    另外,java语言是平台独立的,但是虚拟机不是。每个操作系统都要下载对应的虚拟机,主要是由于它最终调用的函数库以及线程模型不同。

什么是反射?

反射是一种间接操纵目标对象机制。前提是java程序在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法。

反射就是把java类中的各种成分映射成相应的java类,然后进行调用。

反射原理是什么?

在JVM(java虚拟机,通过jvm,获得字节码,java语言在不同平台运行时不需要重新编译。)运行时才会动态的加载类,但是我们有时候在编译期不知道运行的对象是谁,编译成.class文件之后。通过反射,运行中的java程序也可以获取类的信息,并且可以操作类或对象的内部属性,它是在运行时根据需要才加载。

java反射就是当程序运行时,在内存中获得.class对象之后,反向获取该class对象的类的属性和方法等各种信息

从面向对象的角度来看,我们平时用到的“类”,“构造器”,“属性”,“方法”其实也是一个“类”,它们在jdk中分别对应Class、Constructor、Field、Method类。其中Class相当于“类的类”,可称为“元类”,从这个角度看,我们平时自定义的“类”可以理解为Class的一个对象。

反射有哪些好处?

反射允许静态语言在运行时检查、修改程序的结构和行为。在静态语言中,使用一个变量,必须知道它的类型。换句话说就是,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射来解决。

“无反射,不框架;无代理,不框架”。反射是框架设计的灵魂。

反射广泛应用于那些需要在运行时检测或修改程序行为的程序中。

当我们想要输入一个对象和类,并想调用它的属性和方法时,一按点号,编译器就会自动列出它的属性和方法。这里用到的就是反射。

反射怎么用?

功能

  1. 运行时判断任意一个对象所属的类。

  2. 在运行时构造任意一个类的对象。

  3. 在运行时判断任意一个类所具有的成员变量和方法。

  4. 在运行时调用任意一个对象的方法。

  5. 生成动态代理

    获取Class的三种方法:

    Student stu1 = new Student();
    

    (1)Object --> getClass

    Class stuClass = stu1.getClass();
    

    (2)任何数据类型(包括基本的数据类型)都有一个”静态“的class属性。

    Class stuClass2 = Student.class
    

    (3)通过class类的静态方法:forName(String className)

    Class stuClass3 = Class.forName("Stu.Student")//此路径是真实地路径
    

    注意: 在运行期间,一个类只有一个Class对象产生。

    通过反射获取构造方法并使用:

    Class clazz = Class.forName("Stu.Student");
    Constructor[] conArray = clazz.getConstructors();
    //所有公有地构造方法。
    
    conArray = clazz.getDeclaredConstructors();
    //所有的构造方法。
    
    Constructor con = clazz.getConstructor(null);
    //获取公有并且无参的构造方法。
    
    con = clazz.getDeclaredConstructor(char.class);
    //获取私有的构造方法,并调用。
    

    获取成员变量并调用:

    Field[] fieldArray = stuClass.getFields();
    //获取所有公有地字段
    
fieldArray = stuClass.getDeclaredFields();
//获取所有的字段
Field f = stuClass.getField("name")
    //获取公有字段并调用
f = stuClass.getDeclaredField("phoneNum")
    //获取私有字段并调用

获取成员方法并调用:

Method[] methodArray = stuClass.getMethods();
//获取所有公有地方法
methodArray = stuClass.getDeclaredMethods();
//获取所有的方法,包括私有的
Method m = stuClass.getMethod("show1",String.class);
//获取公有的show1()方法
m = stuClass.getDeclaredMethod("show4",int.class);
//获取私有的show4()方法

反射main方法:

Class clazz = Class.forName("fanshe.main.Student");
Method methodMain = clazz.getMethod("main",String[].class);
methodMain.invoke(null,(object)new String[]{"a","b","c"})

反射方法的其他使用之—通过反射运行配置文件内容

public static String getValue(String key) throws IOException{
    Properties pro = new Properties();
    FileReader in = new FileReader("pro.txt");
    pro.load(in);
    in.close();
    return pro.getProperty(key);
}//此方法接收一个key,在配置文件中获取相应的value

反射方法的其他使用之—通过反射越过泛型检查:

泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的。

静态代理和动态代理的区别

在这里插入图片描述

使用代理的目的是:对目标对象(的方法)进行功能增强。

既然是对目标对象的方法进行增强,代理对象的方法中一定会调用目标对象的方法。而且一般会在目标对象的方法调用前后(或者其他时机)做一些其他的处理以达到增强的效果。

什么是静态代理?

代理类和实现类实现同一个接口,代理类里面有一个实现类的引用,客户调用代理类的方法时,代理类就调用实现类的方法。称为静态,是因为代理类和实现类是写死的,就是在编译阶段就确定了。

静态方法可以在代理类调用实现类的方法前后加上一些代码;在构造方法中也可以通过网络加载器加载到别的机器上的类,这样本地调用时,感觉不到远程机器,就像调用本地代码一样。

静态代理的缺点

如果有很多类需要代理,就要写很多的代理类,就比较麻烦。

动态代理的本质是什么?

动态代理的本质就是用户提供类名、方法名、参数、代理类执行方法,返回结果。用类加载器可以将类加载到虚拟机,用Class clazz表示,有这个对象,就可以执行它的方法。(这就是反射)

动态代理的使用?

Proxy实现类是由Proxy的static方法在执行时刻生成的,这个类和被代理类有相同的接口,并且它的构造函数的参数是InvocationHandler。因为这个类是在运行时刻生成的,可以根据传入不同的参数,生成不同的代理类,所以是动态代理。

动态代理类不是由程序员写的,而是根据传入的参数,由Proxy类在运行时生成的,所以可以传入不同的参数,这样就可以在运行时产生不同的代理类,所以是动态的。

Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[]{Foo.class}, handler);
动态代理的内涵?

java动态代理类位于Java.lang.reflect包下,一般涉及到以下的两个类:

(1)Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method,Object[] args)。在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2)Proxy:该类即为动态代理类,主要包含以下内容:Protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

Static Class getProxyClass(ClassLoader loader,Class[] interfaces):获得一个代理类,其中loader是类装载器,interface是真实类所拥有的全部接口的数组。

Static Object newProxyInstance(ClassLoader loader,Class[] interface, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)。

参考博文

https://blog.csdn.net/yangwu007/article/details/109116421

https://blog.csdn.net/ju_362204801/article/details/90578678

https://fxnfk.blog.csdn.net/article/details/78905997

https://blog.csdn.net/he90227/article/details/39155613

https://blog.csdn.net/sinat_38259539/article/details/71799078

https://blog.csdn.net/he90227/article/details/39155613

https://cloud.tencent.com/developer/article/1561114

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值