Java读书笔记(6)-类的加载机制与反射

ch18 类加载机制与反射

  1. 类的加载,连接和初始化

    系统可能在第一次使用某个类时加载该类,也可能采用预加载机制来加载某个类

    • JVM和类

      • 一个Java程序就是一个Java虚拟机进程

      • 两个JVM之间数据独立,所以一个类的静态属性并不会跨虚拟机进程共享

    • 类的加载

      • 加载->连接->初始化

      • 类加载是指将类的class文件读入内存,并为之创建一个java.lang.Class对象,当程序中使用任何类时,系统都将为之建立一个java.lang.Class对象

      • 系统中的所有类实际上也是实例,它们都是java.lang.Class的实例

      • 类的加载由类加载器完成,类加载器通常由JVM提供,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器

      • 类的来源:

        1. 本地文件系统class

        2. JAR

        3. 网络

        4. 动态编译Java文件

    • 类的连接

      • 验证->准备->解析

    • 类的初始化

      • 在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对静态Field进行初始化

      • Java初始化静态Field:声明时指定初始值或使用静态初始化块

      • JVM初始化一个类的步骤:

        1. 如未加载和连接,则加载并连接该类

        2. 如其直接父类未初始化,则先初始化其直接父类

        3. 如有初始化语句,则依次执行这些初始化语句

      当执行步骤2时,系统对直接父类的初始化步骤也遵循此步骤13,依次迭代直到java.lang.Object类。当程序主动

      使用任何一个类时,系统会保证该类以及所有父类(包括直接父类和间接父类)都被初始化

    • 类初始化的时机

      • Java程序首次通过下面6种方式使用某个类或者接口时,系统就会初始化该类或接口:

        1. 创建类的实例 (new,反射,反序列化)

        2. 调用某个类的静态方法

        3. 访问(读写)某个类或接口的静态Field

        4. 使用反射方式强制创建某个类或接口对应的java.lang.Class对象,如Class.forName(“Person”)

        5. 初始化某个类的子类

        6. 直接使用java.exe运行某个主类

      • final型静态Field在编译时就能定下来,故不会触发类初始化行为

      • 当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。

      • 使用ClassforName()静态方法才会导致强制初始化该类

  1. 类加载器

    负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象

    • 类加载器简介

      • JVM中,一个类用其全限定类名和其类加载器作为其唯一标识

      • JVM启动时,会形成由3个类加载器组成的初始类加载器层次结构

        1. Bootstrap ClassLoader:根类加载器(引导类加载器,负责加载Java的核心类)

        2. Extension ClassLoader:扩展类加载器

        3. System ClassLoader:系统类加载器(JVM启动时加载来自java命令的-classpath选项,java.class.path

        4. 系统属性或者CLASSPATH环境变量所指定的JAR包和类路径。程序都可以通过ClassLoader的静态方法getSystemClassLoader()获取系统类加载器)

    • 类加载机制

      • 类型:全盘负责,父类委托,缓存机制

      • 层级关系:用户类加载器->系统类加载器->扩展类加载器->根类加载器

      • getParent()方法

      • 根类加载器不是由Java实现的

      • 系统类加载器是AppClassLoader的实例,扩展类加载器是ExtClassLoader的实例,这两个类都是URLClassLoader的实例

      • 类加载class8个步骤

    • 创建并使用自定义的类加载器

      • JVM中除了根类加载器之外的所有类加载器都是ClassLoader子类的实例

      • 通过扩展ClassLoader子类,重写包含的方法可以实现自定义类加载器

      • 两个关键方法:

        1. loadClass(String name,boolean resolve)

        2. findClass(String name)

      • 推荐重写findClass()方法,而不是loadClass()方法

      • loadClass()执行步骤:

        1. findLoadedClass(String)来检查是否已经加载类,如果已经加载则返回;

        2. 在父类加载器上调用loadClass()方法。如果父类加载器为null,则使用根类加载器来加载;

        3. 调用findClass(String)方法查找类。

      • 核心方法Class defineClass(String name,byte[] b,int off,int len),该方法负责将指定类的字节码文件(class文件)读入字节数组byte[] b内,并把它转换为Class对象

    • URLClassLoader

      • 两个构造器创建ClassLoader对象

      • 通过loadClass()方法可以加载指定类

      • 应用:从文件系统加载MySQL驱动,并使用该驱动来获取数据库连接、

  2. 通过反射查看类信息

    • 获取Class对象

      • 三种方式:

        1. Class.forName(String),传入包含完整包名的全限定类名称

        2. 调用某个类的class属性来获取该类对应的Class对象

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

    • Class中获取信息

      • 构造器

      • Field

      • 方法

      • Annotation

  3. 使用反射生成并操作对象

    • 创建对象

      • 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,要求该Class对象的对应类有默认构造器

      • 使用Class对象获取指定的Constructor对象,再调用Constuctor对象的newInstance()方法来创建该Class对象对应的实例。通过这种方式可以选择使用指定的构造器来创建实例

      • 在很多JavaEE框架中都需要根据配置文件信息来创建Java对象,从配置文件读取的只是某个类的字符串类名,程序需要根据该字符串来创建对应的实例,就必须使用反射

    • 调用方法

      • geMethods()方法或者getMethod()方法获取全部方法或者指定方法——分别返回Method对象数组或者Method对象

      • 获得Method对象后,就可通过该Methodinvoke(Object obj,Object...args)调用相应的方法,obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参

      • 权限问题:setAccessible(boolean flag),实现通过反射来调用private方法,private构造器和访问private属性

    • 访问属性值

      • 通过Class对象的getFields()getField()方法可以获取该类所包括的全部Field或指定Field

    • 操作数组

      • java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过Array来动态创建数组,操作数组元素等

      • Object arr=Array.newInstance(String.class,10)

  4. 使用反射生成JDK动态代理

    • 使用ProxyInvocationHandler创建动态代理

      • Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态的生成实现类。就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。

      • 系统生成的每个代理对象都有一个与之关联的InvocationHandler对象,定义一个InvocationHandler实现类需要重写invoke()方法——调用代理对象的所有方法时都会被替换成调用该invoke()方法,Object invoke(Object proxy,Method method,Object[] args)

    • 动态代理和AOP

      • JDK动态代理只能为接口创建动态代理

      • 采用动态代理可以非常灵活的实现解耦,通常都是为指定的目标对象生成动态代理

      • 这种动态代理在AOP(Aspect Orient Programming)中被称为AOP代理,AOP代理包含目标对象的全部方法,可以替代目标对象。但是二者存在差异:AOP代理里的方法可以在执行目标方法前后插入一些通用处理

  5. 反射和泛型

    • 泛型和Class

      • 使用Class<T>泛型可以避免强制类型转换

      • 对象工厂

    • 使用反射来获取泛型信息

      • 通过指定类对应的Class对象,可以获得类中的所有Field及其类型:

        Class<?> a=f.getType()//普通类型

      • 获取泛型类型:Type gType=f.getGenericType()

      • ParameterizedType对象

        1. getRawType():返回没有泛型信息的原始类型

        2. getActualTypeArguments():返回泛型参数的类型


原文地址:https://www.cnblogs.com/hust_wsh/p/5101933.html