zoukankan      html  css  js  c++  java
  • 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():返回泛型参数的类型


  • 相关阅读:
    [翻译]汇编器和加载器简史
    一定要制作一个计划
    基本排序算法(冒泡,快排,插入,希尔,选择,归并)
    用yunio网盘搭建git私有仓库
    git 学习笔记1
    select函数
    axel源码学习(1)——重要流程细节
    axel源码学习(0)——程序逻辑
    Unix网络编程(3)——C/S模型几种情况
    【转】程序员技术练级攻略
  • 原文地址:https://www.cnblogs.com/hust_wsh/p/5101933.html
Copyright © 2011-2022 走看看