文章目录
类加载
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是我们前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。开发者可以通过继承ClassLoader基类来创建自己的类加载器。
类加载的方式:
- 从本地文件系统来加载class文件,这是前面绝大部分示例程序的类加载方式。
- 从JAR包中加载class文件,这种方式也是很常见的,前面介绍JDBC编程时用到的数据库驱动类就是放在JAR文件中,JVM可以从JAR文件直接加载该class文件。
- 通过网络加载class文件。
- 把一个Java源文件动态编译、并执行加载。类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。
类连接的过程
- 验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。
- 准备:类准备阶段则负责为类的静态属性分配内存,并设置默认初始值。
- 解析:将类的二进制数据中的符号引用替换成直接引用。
类初始化的过程
虚拟机负责对类进行初始化,主要就是对静态属性进行初始化。
对静态属性指定初始值的两种方式
- 声明静态属性时指定初始值。
- 使用静态初始化块为静态属性指定初始值。
触发类初始化的6种方式
- 创建类的实例(包括new和反射getinstance)
- 调用类的静态方法
- 访问类或接口的静态属性或为该属性赋值
- 使用Class.forName()强制创建对应的Class对象
- 初始化这个类的子类
- 直接使用java.exe命令运行某个主类,这个主类会被先初始化
类加载器以及实现机制
类加载器负责将.class文件加载到内存中,并生成对应的java.lang.Class对象。
JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构:
- Bootstrap ClassLoader:根类加载器
- Extension ClassLoader:扩展类加载器
- System ClassLoader:系统类加载器
其中在扩展类加载器中,只要我们将自己开发的类打包成jar包并放在JAVA_HOME_/jre/lib/ext路径下,就会被这个加载器加载。
写在系统环境变量下的jar包会被系统类加载器加载
类加载的三种机制
- 全盘负责:所谓全盘负责,激素hi说当一个类加载器负责加载某个Class的时候,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
- 父类委托:所谓父类委托则是先让parent类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:缓存机制将会保证所有被加载过的class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存中搜寻该class,只有当缓存中不存在该class对象时,系统才会重读该类对应的二进制数据,并将其转换成Class对象,并存入cache。
继承ClassLoader实现自定义加载器(暂缺)
目前还不经常用,先不写
使用URLClassLoader
URLClassLoader是ClassLoader的一个实现类,该类也是系统类加载器和扩展类加载器的父类,它既可以从本地文件系统获取二进制文件来加载类,也可以从远程主机获取二进制文件来加载类。
URLClassLoader的构造器:
构造方法 | 作用 |
---|---|
URLClassLoader(URL[] urls) | 使用默认的父类加载器创建一个ClassLoader对象,该对象将从urls所指定的系列路径来查询并加载类 |
URLClassLoader(URL[] urls,ClassLoader parent) | 使用指定的父类加载器创建一个ClassLoader对象,其他功能与前一个构造器相同 |
举一个用URLClassLoader加载mysql驱动的例子
public class URLClassLoaderTest
{
private static Connection conn;
//定义一个获取数据库连接的方法
public static Connection getConn(String url,String user,String pass)throws Exception{
if(conn==null){
//创建一个URL数组
URL[] urls={new URL("file:mysql-connector-java-3.1.10-bin.jar")};
//以默认的ClassLoader 作为父ClassLoader,创建URLClassLoader
URLClassLoader myClassLoader=new URLClassLoader(urls);
//加载Mysql的JDBC驱动,并创建默认实例
Driver driver=(Driver) myClassLoader.loadClass("com.mysql.jdbc.Driver").newInstance();
//创建一个设置JDBC连接属性的properties对象
Properties props=new Properties();
//至少需要为该对象传入user和password
props.setProperty("user",user);
props.setProperty("password",pass);
//调用Driver对象的connect方法来取得数据库连接
conn=driver.connect("jdbc:mysql://localhost:3306/mysql",props);
}
return conn;
}
public static void main(String[] args) throws Exception{
System.out.println(etConn("jdbc:mysql///xxxx","账号","密码"));
}
}
这里需要注意的是,URL可以
以file:为前缀,表明从本地文件系统加载。
以http:为前缀,表明从互联网通过HTTP访问来加载
以ftp:为前缀,表明从互联网通过FTP访问来加载
使用Class对象
获取class对象的三种方法
- 使用Class类的forName静态方法
- 调用某个类的class属性来获取该类对应的class对象
- 调用某个对象的getClass()方法。
动态创建Java对象
在通过反射获得类对象后,有两种方法生成类实例对象(Person per=new Person(),Person是类对象,per是类实例对象)
- 使用Class对象的newInstance()方法来创建该Class对象对应类的实例。
- 先使用Class对象获取指定的Constructor(构造器)对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。
动态调用方法
首先通过Class对象的getMethods()或者getMethod()方法来获取全部方法或指定方法(Method类)。使用Method类的invoke(类实例对象名,参数)执行方法。
访问并修改Java对象的属性值
这个功能常常用在想通过方法修改基本数据类型属性值的问题上(方法参数如果是基本数据类型是值传递,不是基本数据类型则是引用传递)。
举个使用的例子看看就知道了:
import java.lang.reflect.Field;
class Person {
private String name;
private String age;
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class FieldTest {
public static void main(String[] args) throws Exception {
//创建一个person对象
Person person=new Person();
//获取person类对应的calss对象
Class<Person> personClazz=Person.class;
//获取Person类名为name的属性
//使用getDeclaredFiled,表明可获取各种访问控制符的field
Field nameField=personClazz.getDeclaredField("name");
//设置通过反射访问该Field时取消访问权限检查
nameField.setAccessible(true);
//调用set方法为p对象的指定Field设置值
nameField.set(person,"yeeku");
//获取person名为age的属性
Field ageField=personClazz.getDeclaredField("age");
//设置通过反射访问该Field时取消权限检查
ageField.setAccessible(true);
ageField.setInt(person, 30);
System.out.println(person);
}
}
使用反射创建并操作数组
在java.lang.reflect包下的Array类提供如下方法:
方法 | 作用 |
---|---|
static Object newInstance(Class<?> componentType,int…length) | 创建一个具有指定的元素类型、指定维度的新数组 |
static xxx getXxx(Object array,int index) | 返回数组中第index个元素。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变为get(Object array,int index) |
static void setXxx(Object array,int index,xxx val) | 将数组中第index元素的值设为val。 |
使用Proxy和invacatonHandler创建动态代理
Proxy类提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果我们在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类,如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。
Proxy提供了如下两个方法来创建动态代理类和动态代理实例: