1、类的加载、连接和初始化
加载类的时机
第一次使用该类
采用预加载机制加载类
Java虚拟机进程(JVM进程)
同一个JVM里的所有线程、所有变量都处于同一个进程里,都使用该JVM进程的内存区
JVM进程终止时机
程序运行到最后正常结束。
程序运行到使用System.exit()或Runtime.getRuntime().exit()代码处结束程序。
程序执行过程中遇到未捕捉的异常或错误而结束。
程序所在平台强行结束了JVM进程。
两个JVM进程间并不会共享数据
类的加载
加载、连接、初始化(当程序使用某个类时,如果该类还未加载进内存中,执行这三个步骤来对类进行初始化。)(这三个步骤通常连续完成,故统称为类加载、类初始化)
类加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象。
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是java程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。
开发者可以通过继承ClassLoader基类来创建自己的类加载器。
通过使用不同的类加载器,可以从不同来源加载类的二进制数据
从本地文件系统加载class文件
从JAR包加载class文件
通过网络加载class文件
把一个java源文件动态编译,并执行加载
类加载器通常无需等到“首次使用”该类时才加载该类,Java虚拟机规范允许预先加载某些类。
类的连接
当类被加载后,系统为之生成一个对应的Class对象,接着进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。类连接分为如下三个阶段
验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致。
准备:为类的类变量分配内存,并设置默认值
解析:将类的二进制数据中的符号引用替换成直接引用
类的初始化
在类的初始化阶段,虚拟机负责对类进行初始化,主要是对类变量进行初始化。在Java类中对类变量进行初始化有两种方式:(1)声明类变量时指定初始值;(2)使用静态初始化块为类变量指定初始值。
声明变量时指定初始值,静态初始化块都将被当成类的初始化语句。
JVM初始化一个类的几个步骤:
假如这个类还没有被加载和连接,则程序先加载并连接改类;
假如这个类的直接父类还没有被初始化,则初始化其直接父类;
假如l类中有初始化语句,则系统依次执行这些初始化语句。
类初始化的时机(类加载时机?)
当java程序首次通过下面6种方式来使用某个类或接口时,系统就会初始化该类或接口:
创建类的实例:通过new创建实例、通过反射创建实例、通过反序列化的方式来创建实例。
调用某个类的类方法(静态方法)
访问某个类或接口的类变量,或为该类变量赋值
使用反射方式强制创建某个类或接口对应的java.lang.Class对象。例如代码:Class.forName(“Person”),如果系统还未初始化Person类,则这行代码将会导致Person类被初始化,并返回Person类对应的java.lang.Class对象。
直接使用java.exe命令来运行某个主类。当运行某个主类时,程序会先初始化该主类。
特别指出的几个情形
对于一个final型的类变量,如果该类变量的值在编译时可以确定下来,那么这个类变量相当于“宏变量”。Java编译器会在编译时直接把这个类变量出现的地方替换成它的值,因此即使程序该静态类变量,也不会导致类初始化。
反之,final修饰的类变量的值无法在编译时确定下来,则通过该类来访问t它的类变量,会导致该类初始化。
当使用ClassLoader类的loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行类的初始化。使用Class的forName()方法才会导致强制初始化。
2、类加载器
类加载器负责将.class文件(磁盘、网络)加载到内存中,并为之生成java.lang.Class对象。
类加载器负责加载所有的类,系统会为所有被载入内存的类生成一个java.lang.Class对象。一旦一个类被载入JVM中,同一个类就不会被再次载入了。
一个载入JVM中的类有一个唯一的标识,
java中,一个类用其全限定类名(包括包名和类名)作为标识;
JVM中,一个类用其全限定类名和其类加载器作为其唯一标识;
包pg,类Person,被类加载器ClassLoader的实例k1负责加载,则该Person类对应的Class对象在JVM中表示为(Person、pg、k1).
这意味着两个类加载器加载的同名类:(Person、pg、k1)和(Person、pg、k2)是不同的、它们所加载的类也是完全不同、互不兼容的。
当JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构。
BootStrapClassLoader:根类加载器
ExtensionClassLoader:扩展类加载器
SystemClassLoader:系统类加载器
BootStrapClassLoader:引导(原始或根)类加载器,负责加载Java的核心类。在Sun的JVM中,当执行java.exe命令时,使用-Xbootclasspath选项或-D选项指定sun.boot.class.path系统属性值可以指定加载附加的类。其不是ClassLoader的子类,而是由JVM自身实现的。
ExtensionClassLoader:负责加载JRE的扩展目录(%JAVA_HOME%/jre/lib/ext或者由java.ext.dirs系统属性指定的目录)中JAR包的类。通过这种方式可以为java扩展核心类以外的功能,只要将类打包成jar文件,放入%JAVA_HOME%/jre/lib/ext路径即可。
SystemClassLoader:系统(应用)类加载器,负责在JVM启动时加载来自java命令d的-classpath选项、java.class.path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。程序通过ClassLoader的getSystemClassLoader()方法来获取x系统类加载器。如果没有特别指定,则用户自定义的类加载器都以类加载器作为父加载器。
类加载机制
全盘负责:当一个类加载器负责加载某个类时,该Class所依赖的和所引用的的其他Classy也将由该类加载器负责载入,除非显式指定另外一个类加载器来载入。
父类委托:先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
缓存机制:保证所有加载过的类都会被缓存,先从缓存区中搜索,不存在再读取该类对应的二进制数据,并将其转换成Class对象。
类加载器之间的父子关系并不是类继承上的父子关系,这里的父子关系是类加载器实例之间的关系。
用户通过继承ClassLoader实现自己的类加载器。(用户类加载器)
3、通过反射查看类信息
获得Class对象的三个方式:
Class类的forName(String className)静态方法;
调用某个类class属性获取该类对应的Class对象;
调用某个对象的getClass()方法。该方法是java.lang.Object类的一个方法。
从Class中获取信息
获取Class对应类所包含的构造器
获取Class对应类所包含的方法
获取Class对应类所包含的成员变量
获取Class对应类所包含的Annotation
获取Class对应类所包含的内部类
获取Class对应类所包含的修饰符、所在包、类名等信息
4、使用反射生成并操作对象
5、使用反射生成JDK动态代理
java.lang.reflect.Proxy类
java.lang.reflect.InvocationHandler接口
通过这个类和 接口可以生成JDK动态代理或动态代理对象
Proxy类提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,亦可以使用Proxy来创建动态代理实例。
static Class<?> getProxyClass(ClassLoader loader, Class<?>…interfaces):创建一个动态代理类所对应的Class对象,该代理类将实现interface所指定的多个接口。第一个ClassLoader参数指定生成动态代理类的类加载器。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interface, InvocationHandler h):直接创建一个动态代理对象,该代理对象的实现类实现了interface指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。
package com.xyc;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Person{
void walk();
void sayHello(String name);
}
class MyInvocationHandler implements InvocationHandler{
/**
* 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
* 其中:
* proxy:代表动态代理对象
* method:代表正在执行的方法
* args:代表调用目标方法时传入的实参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("----正在执行的方法" + method);
if(args != null) {
System.out.println("下面是执行该方法时传入的实参为:");
for(Object var : args) {
System.out.println(var);
}
}
else {
System.out.println("调用该方法没有实参!");
}
return null;
}
}
public class ProxyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 创建一个InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler();
// 使用指定的InvocationHandler来生成一个动态代理对象
Person p = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[] {Person.class}, handler);
// 调用动态代理对象的walk和sayHello方法
p.walk();
p.sayHello("test");
}
}
来源:CSDN
作者:xuran_
链接:https://blog.csdn.net/xuran_/article/details/104523311