• java反射和动态代理基础


    类加载

    • 类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象
    • 当一个变量被修饰成static final,并在编译器就能确定其值,也就是常量,那么通过类名.变量名访问该变量时不会加载该类(也就是访问常量并不会初始化这个类)

    类初始化时机

    类从加载到虚拟机内存开始,到卸载出内存为止,真个生命周期包括:加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using)和卸载(Unloading)七个阶段。其中,验证、准备、解析三个部分统称为连接(Linking)

     

    1. new对象、调用类的静态方法或者静态变量时
    2. 虚拟机启动时,加载用户指定的main方法的所在类
    3. 通过反射调用某个类
    4. 初始化一个类时先初始化它的父类
    5. 初始化某个类的子类

    中心思想:初始化时机为该类首次主动使用
    以下情况不会初始化

    1、通过子类类名调用父类静态代码,不会触发子类的初始化。
    2、通过数组来创建对象不会触发此类的初始化。
    3、通过调用静态常量不会触发初始化。

    类加载器

    1. Bootstrap ClassLoader,被称为引导(也称为原始或根)类加载器。它负责加载 Java的核心类。在Sun的JVM中,当执行java.exe的命令时使用-Xbootclasspath选项或使用-D选项指定sun.boot.class.path系统属性值可以指定加载附加的类。根类加载器非常特殊,它并不是java.lang.ClassLoader的子类,而是由JVM自身实现的。
    2. Extension ClassLoader,被称为扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或由java.ext.dirs系统属性指定的目录)中的JAR的类包。
    3. System ClassLoaser,被称为系统(也称为应用)类加载器,它负责在JVM启动时,加载来自命令java中的-classpath选项或java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。

    反射

    概念

    官方解释:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    简单理解:反射就是直接通过字节码文件class,去使用它的变量,方法,和构造

    使用步骤

    1. 获得class对象
    2. 从class中获取信息

    获取class对象

    共有三种方式:

    1. 使用Class类的forName(类的全名),该方法需要传入参数,参数为某个类的全名(包名+类名)

    Class.forName("com.test.User");

    1. 调用某个类的class属性获取其对应的Class对象

    User.class;

    1. 调用某个对象的getClass()方法

    User.getClass();

    从Class中获取信息

    获取对应类中的构造器,类型为Constructor
    1. 获得所有public修饰的构造方法

    public Constructor[] getConstructors();

    1. 获得所有构造方法,包括私有

    public Constructor[] getDeclaredConstructors();

    1. 获取指定的public修饰构造方法,参数是该构造方法里参数的类型的class对象

    public Constructor getConstructor(Class<?>... parameterTypes);

    例如:

    enter description here

     
    1. 获取指定构造方法,包括私有

    public Constructor getDeclaredConstructor(Class<?>... parameterTypes);

    使用:拿到构造方法后,用Constructor对象调用newInstance()方法就可以调用该构造方法创建对象。例如

    enter description here

     

    获得私有构造方法也可以使用,在newInstance操作前设置一下构造方法的访问权限:

    constructor.setAccessible(true);

    获取方法,类型为Method

    1. 获取指定的public方法

    public Method getMethod(String methodName ,Class<?>… parameterTypes) //第一个参数为方法名,后面参数为形参的类型的class
    2. 获取所有public方法

    public Method[] getMethods()
    3. 获取指定的方法,包括私有

    public Method getDeclaredMethods(String name ,Class<?>… parameterTypes)
    4. 获取所有方法,包括私有

    public Method[] getDeclaredMethods()

    使用:invoke(对象名,实参)

    enter description here

     

    获取变量,类型为Field

    1. 获取指定的public变量

    public Field getField(String name)

    1. 获取所有public变量

    public Field[] getFields()

    1. 获取指定的变量,包括私有

    public getDeclaredField(String name)

    1. 获取所有变量,包括私有

    public Field[] getDeclaredFields()

    使用:set(对象,值):设置属性;get(对象):获取属性值

    enter description here

     

    结合配置文件的使用

    enter description here

     

    操作数组

    可以通过java.lang.reflect下的Array类来操作数组

    • static Object new Instance(Class<?> componentType,int... length):创建一个具有指定的元素类型、指定维度的数组
    • static xxx get(Object array,int index)
    • static void set(Object array,in index,xxx value)
    enter description here

     

    动态代理

    • 通过Proxy创建动态代理对象

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) //三个参数分别为目标对象的类加载器,目标对象所有接口,实现了InvocationHandler接口的类

    • InvocationHandler接口,重写invoke方法
    enter description here

     
    enter description here

     

    作用:
    1. 解决多个方法中调用了一个通用功能的问题
    2. 解耦

    举例说明
    在开发过程中,有些功能代码是可以共用的。比如有个学生类,里面有个写作业方法,写作业包含三个步骤,打开作业本,写,合上;老师类里有个批作业方法,包含三个步骤:打开作业本,批改,合上;学生类里还有个抄作业方法,包含三个步骤:打开作业本,抄作业,合上(什么鬼啊,我么都是好学生,抄什么作业啊)。
    好的不要在意细节,以上例子只是想来说明一下代理的作用。对于以上的三个方法,打开和合上作业本就属于相同的代码块,于是初学者开发时为了节省时间会选择直接复制粘贴,像下面这样。

    enter description here

     

    有一天,你发现需要修改红色部分的代码块,而你之前将此部分的代码块复制了好多遍,这时你发现,哇,维护起来好不方便哦。于是你想到了封装,可以把红色部分代码封装起来,在其他代码块里直接调用啊。


     

    此时虽然解决了代码复用问题,但是却又提高了代码之间的耦合性。(开发的标准是要尽量做到高内聚,低耦合)现在的代码是这个样子:

    enter description here

     

    而且调用方法的这句话也是一直在重复出现很麻烦哎
    现在就让我们用动态代理来改进,所有的代理,就是帮你去做事啊,我们只需要写自己的专有方法即可,执行相同部分的代码这一步操作可以交给代理来做。
    第一步,创建Student接口,包含写作业抄作业两个基本方法(jdk提供的动态代理只能做接口代理,所有要定义接口)
    enter description here

     

    第二步,编写学生接口的实现类StudentImpl
    enter description here

     

    第三步,实现InvocationHandler接口
    enter description here

     

    第四步,编写代理工厂
    enter description here

     

    第五步,测试
    enter description here

     

    调用代理对象的方法时,其实走的就是重写的invoke方法,invoke方法里我们去调用了共用的方法,调用对象本身方法时就用到了反射
  • 相关阅读:
    sql: 生日三个月内有效
    asp 日期操作
    Csharp:操作存儲過程輸出參數,和返回值
    sql: 生日赠品中的相关算法
    asp and javascript: sql server export data to csv and to xls
    Csharp:user WebControl Read Adobe PDF Files In Your Web Browser
    Csharp: Send Email
    数据结构(七)排序---冒泡排序
    数据结构(七)排序---基本概念和分类
    数据结构(六)查找---散列表(哈希表)查找
  • 原文地址:https://www.cnblogs.com/duibd/p/12060964.html
Copyright © 2020-2023  润新知