JavaSE — 类加载机制和反射

一、类的加载、连接和初始化
1、JVM和类

当程序启动时,会先启动一个Java虚拟机进程,程序的运行都是在Java虚拟机进程里的

  • JVM进程被终止的情况:

    1)、程序运行到最后正常结束

    2)、程序运行到使用System.exit()或者Runtime.getRuntime().exit()代码处结束

    3)、程序运行遇到了未捕获的异常或者报错了

    4)、程序所在平台强制结束了JVM进程

注意:其中SystemRuntime分别为调程序运行平台方法和运行环境方法

2、类的加载

当程序使用某个类时,如果类还未被加载到内存中,系统就会对类进行加载、连接、初始化三个步骤

注意:类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。

  • 其中,类加载是指将类的class文件读入内存,并为之创建一个java.lang.Class对象
3、类的连接

当类被加载之后,系统为之生成了一个Class对象,接着就是连接阶段,负责将类的二进制文件合并到JRE中,分为3个步骤:

  • 验证:用于检验被加载的类是否具有正确的内部结构,并和其他类协调一致
  • 准备:负责为类变量分配内存,并设置初始值
  • 解析:将类的二进制数据中对常量池的符号引用替换成直接引用
4、类的初始化
  • 初始化阶段,就是虚拟机对类进行变量的初始化

  • 对类变量指定初始值有两种方式:

    1)、声明变量时指定初始值

    2)、使用静态初始化块为类变量指定初始值

  • 注意:JVM最先初始化的总是java.lang.Object类(因为如果类有父类,会先初始化父类,而Object类是所有类的父类)

5、类初始化的时机

程序首次通过以下6种方式使用类或接口时,系统会初始化该类或者接口

  • 创建类的实例(包括new一个对象,通过反射创建,通过反序列化创建)

  • 访问某个类或者接口的静态变量(注意:当该静态变量在编译时就确定了,实际上后面的访问,并没有,而是直接使用常量)

  • 调用某个类的静态方法

  • 使用反射强制创建某个类或者接口对应的java.lang.Class对象,例如

    Class.forName("Car");
    

    如果系统还没初始化Car类,这个时候也会被初始化,并返回Car类对应的java.lang.Class对象

  • 初始化某个类的子类(初始化子类时,会先初始化父类)

  • 直接使用java.exe命令运行某个主类,运行时会先初始化该主类

二、类加载器

类加载器负责将.class文件(可能在磁盘或者网络上)加载到内存中,并生成相应的java.lang.Class对象

1、类加载机制

JVM的类加载机制主要是以下3种

  • 全盘负责:当一个类加载器加载某个Class时,该Class所依赖的和引用的其他Class也由该加载器加载(好人帮到底,送佛送到西)
  • 父类委托:尝试先让父类加载器加载,没有再依次返回来加载
  • 缓存机制:会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器会先从缓存区搜索该Class,缓存区没有,才会读取对应的二进制数据,加载(这也是为什么代码改了,需要重新启动JVM才能生效)

双亲委派机制:试图先让父类加载器加载,一直向上直到根类加载器,如果没有,在向下依次看有无,有则加载

好处有两点

​ 1、避免重复加载,父类加载器加载了,子类就不用加载了

​ 2、安全,防止Java核心API被篡改(因为优先有父类加载器加载,自定义加载器最后才有机会执行)

注意:区分加载过没,是由类的全限定名+类加载器作为唯一标识

三、反射

程序在运行时获得对象和类的真实信息

1、获得Class对象

类被加载后,系统会为每个类生成一个对应的Class对象,通过该对象,就可以访问到JVM中的这个类

在Java程序中,获得Class对象有3中方式(推荐第一种)

  • 调用某个类的class属性:例如 Car.class获得Car类对应的 Class对象
  • 使用Class类的的forName(String s)静态方法:需要传入类的全限定类名(完整包名)
  • 调用对象的getClass()方法

通过调用类的class属性获得Class对象的好处

  • 代码安全,在编译阶段就可以确定需要的Class对象是否存在
  • 程序性能更好,无需调用方法
2、Class对象的使用

通过过Class对象可以得到大量的Method方法、Constructor构造器、Field属性等对象,这些对象分别代表该类所包括的方法、构造器和属性等,程序还可以通过这些对象来执行实际的功能,例如调用方法、创建实例

// 根据类名获取对应的Class对象
Class<?> cla = Car.class;

// 可以通过Class对象获得类的的各种信息
// 获得Car类的所有public方法
Method[] methods = testClass.getMethods();
for(Method m:methods){
	System.out.println(m.toString());
}
3、 使用反射获得操作对象

程序可以通过Method 对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建实例,能通过Field对象直接访问并修改对象的属性值

  • 通过Class对象获取指定的Constructor(构造器)对象
  • 调用Constructor对象的newInstance()方法创建该 Class 对象对应类的实例
// 根据类名获取对应的Class对象
Class<?> cla = Car.class;
// 使用Class对象对应类的空参构造器(公共的)
Constructor car = cla.getConstructor()
// 根据参数列表,获取指定构造函数(公共的)
Constructor car2 = cla.getConstructor(String.class, int.class);  
// 通过构造函数的newInstance()实例化对象
Object c = car2.newInstance();
  • 直接通过Class对象的newInstance()方法直接获得(实际也是调了默认的空构造器)
// 创建被反射类的对象实例的快捷方式 (常用)。 
// 前提:必须要有公有的空参构造器 (如果是默认权限,反射的类必须在本包中)
Object c2 = cla.newInstance();
5、操作数组

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

// 创建一个元素类型为String,长度为10的数组
Object arr = Array.newInstance(String.class, 5);
// 赋值
Array.set(arr, 3, "这是下标为3的元素的值");
// 取值, 值为 "这是下标为3的元素的值"
Object arrValue = Array.get(arr, 3);
6、使用反射生成JDK动态代理

在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,可以生成JDK动态代理类或者动态代理对象

注:代理模式,特征是代理类与委托类有同样的接口,代理对象不真正实现服务

// 创建一个InvocationHandler对象
// 其中,MyInvocationHandler 实现 InvocationHandler 接口
InvocationHandler handler = new MyInvocationHandler(...);

// 使用 Proxy 直接生成一个动态代理对象
Car c = (Car)Proxy.newProxyInstance(Car.class.getClassLoater(),
                                   new Class[]{Car.class},
                                   handler);

未完待补充!

7、泛型和反射

在反射中,可以使用泛型,避免使用反射生成的对象需要强转

当强转可能导致出错:

public class MyFactory{
    // 代码省略了异常处理
    public static Object getInstance(String className){
        // 创建指定类对应的Class对象
        Class cla = ClassName.class;
        // 返回使用该Class对象直接创建实例(其实也是调了构造器)
        return cla.newInstance();
    }
}

// 获取实例后需要强制类型转换,
Car c = (Car)MyFactory.getInstance("Car");

使用泛型避免强转:

public class MyFactory{
    // 返回一个 T 对象,参数为泛型化的Class对象
    public static <T> T getInstance(Class<T> cls){
        // 返回使用该Class对象直接创建实例(其实也是调了构造器)
        return cls.newInstance();
    }
}

// 获取实例后不用强制类型转换
Car c = MyFactory.getInstance(Car.class);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值