类加载
类加载概述
概念:
- 当程序要使用某个类时,如果该类未被加载到内存中,则系统会通过类的加载、类的连接、类的初始化三个步骤对类进行初始化。
- JVM会将类加载、连接、初始化连续完成,这三个步骤被称为类加载或类初始化。
- 类的加载
- 指将.class文件读入内存,并为之创建一个java.lang.Class对象类加载器的作用
- 任何类被使用的时候,系统都会为之创建一个java.lang.Class对象
- 类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并与其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
- 类的初始化
- 主要对类变量进行初始化
类的初始化步骤: - 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化被直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
注意: 在执行第二个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3 一个类被载入java虚拟机的时候,同一个类就不会再次被载入了。 类的初始化时机: 强调首次 - 创建类的实例
- 调用类的类方法(静态方法)
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类(初始化子类首先就会初始化父类)
- 直接使用java.exe命令来运行某个主类
JVM的类加载机制
JVM的类加载机制有三种:全盘负责、父类委托、缓存机制。
- 全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也由该类加载器负责载入,除非显示使用另外一个类加载器载入;
- 父类委托:当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类;
- 缓存机制:保证所有被加载的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储在缓存区。
ClassLoader: 负责加载类的对象 java运行时具有一下内置类加载器:
类名 | 说明 |
---|
Bootstrap class loader | 它是虚拟机的内置类加载器,通常表示null,并且没有父null 也就是这个类是类加载器的祖宗 | Platform class loader | 平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的java SE平台API,其实现类和JDK特定的运行时类 | System class loader | 它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径、模块路径和JDK特定工具上的类 |
类加载器的继承关系:System的父加载器为Platform(java9)Ext(java8),Platform的父加载器为Bootstrap。 ClassLoader中的两个方法:
方法名 | 说明 |
---|
static ClassLoader getSystemClassLoader() | 返回用于委派的系统类加载器 | ClassLoader getParent() | 返回父类加载器进行委派 |
案例:
public class ClassLoaderDemo {
public static void main(String[] args) {
ClassLoader c = ClassLoader.getSystemClassLoader();
System.out.println(c);
ClassLoader c2 = c.getParent();
System.out.println(c2);
ClassLoader c3 = c2.getParent();
System.out.println(c3);
}
}
反射
概念:
- java反射机制:运行时去获取一个类的变量和方法信息,通过获取到的信息创建对象,调用方法的一种机制;
- 动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展;
理解xhj:
- 自己定义类要想使用–》类加载器加载对应的.class文件–》每一个.class文件都会包含成员变量、构造方法、成员方法…信息
- Class类就是所有.class文件对应的类型、躯体。
- 不再通过自己定义类的去使用成员变量、构造方法、成员方法,而是用Class类去使用成员变量、构造方法、成员方法,这就是反射。
- 反射就是把java类中的各种成分映射成相应的java类。
获取Class类的对象 Class.forName
原因: 通过反射去使用一个类。首先获取该类的 字节码文件对象=类型为Class类型的对象。 获取Class类型对象的方法:
方法 | 说明 |
---|
使用类的class属性来获取该类对应的Class对象 | Student.class将会返回Student类对应的Class对象 | 调用对象的getClass()方法,返回该对象所属类对应的Class对象 | 该方法是Object类中的方法,所有的Java对象都可以调用该方法 | 使用Class类中的静态方法forName(String className) | 该方法需要出入 字符串参数 = 某个类的全路径=完整包名的路径 |
案例:
public class ReflectDemo {
public static void main(String[] args) {
Class<Student> c1 = Student.class;
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1 == c2);
System.out.println("------");
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
Class<?> c4 = null;
try {
c4 = Class.forName("itiheima315.test2.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(c1 == c4);
}
}
反射获取构造方法并使用 Constructor
Class类
- 在java.lang包下,使用需要导包;Class Class< T >
- public final class Class< T > extends Object implements …是最终类,说明不能被继承
- Class类的实例表示正在运行的java应用程序的类和接口
- 通过Class对象获取Constructor对象的数据
所属类 | 方法名 | 说明 |
---|
Class类 | Constructor< ? >[] getConstructors() | 返回所有公共构造方法对象的数组 | Class类 | Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法对象的数组 | Class类 | Constructor< T> getConstructor(Class<?>…parameterType) | 返回单个公共构造方法对象 要获取的构造方法的参数的个数和数据类型对应的字节码文件对象 使用的是:String类型,则用String.class;int类型,则用的是int.class | Class类 | Constructor< T> getDeclaredConstructor(Class<?>…parameterType) | 返回单个构造方法对象 要获取的构造方法的参数的个数和数据类型对应的字节码文件对象 |
Constructor类
- 在java.lang.reflect包下,使用需要导包
- public final class Constructor< T> extends Executable 是最终类
- 提供了一个类的单个构造函数的信息和访问权限
- 通过Constructor类中的newInstance方法创建对象:
方法名 | 说明 |
---|
T newInstance(Object…initargs | 使用由此Constructor对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例 |
理解xhj:
- 在反射中,实际上是将类中的成员变量、构造方法、成员方法,看成一个个对象;
- 通过反射创建类的对象:
1 通过Class.forName方法得到 类的字节码文件对象
Class<?> c = Class.forName("类的路径")
抛出异常 ClassNotFoundException
2 通过字节码文件对象的getConstructor方法得到单个构造函数
Constructor<?> con = c.getConstructor();
抛出异常 NoSuchMethodException
3 通过Constructor对象的newInstance方法创建对象
Object obj = con.newInstance();
抛出异常 IllegalAccessException, InvocationTargetException, InstantiationException
案例:
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("itiheima315.test2.Student");
Constructor<?> c0 = c.getConstructor();
Object obj = c0.newInstance();
System.out.println(obj);
}
}
案例:反射
案例1
需求: 通过反射实现以下操作: Student s = new Student(“林俊杰”,30,“西安”); System.out.println(s); 基本数据类型可以通过.class得到对应的Class类型
public class ReflectTest1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("itiheima315.test2.Student");
Constructor<?> con = c.getDeclaredConstructor(String.class,int.class,String.class);
Object obj = con.newInstance("林俊杰", 30, "西安");
System.out.println(obj);
}
}
案例2
需求: 通过反射实现以下操作: Student s = new Student(“林俊杰”); System.out.println(s); public void setAccessible(boolean flag):值为true,取消访问检查
public class ReflectTest2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("itiheima315.test2.Student");
Constructor<?> con = c.getDeclaredConstructor(String.class);
con.setAccessible(true);
Object obj = con.newInstance("林俊杰");
System.out.println(obj);
}
}
反射获取成员变量并使用 Field
获取成员变量的方法
方法名 | 说明 |
---|
Field[] getFields() | 返回类或接口的所有可访问的公共字段组成的数组 | Field[] getDeclaredFields() | 返回类或接口声明的所有字段组成的数组 | Field getField(String name) | 返回类或接口的指定公共成员变量 | Field getDeclaredField(String name) | 返回类或接口的指定声明成员变量 |
Field类
- Field提供有关类或接口的单个字段的信息和动态访问
- 所给成员变量赋值的方法:
方法名 | 说明 |
---|
void set(Object obj,Object value) | 将指定的对象参数,设置为由Field对象表示的字段 成员变量名.set(对象,“具体值”) # 给对象的成员变量赋值为具体值 |
反射获取成员变量具体的代码过程:
1 获取Class对象
Class<?> c = Class.forName("类的路径");
2 获取Class对象的无参构造方法
Constructor<?> con = c.getConstructor();
3 获取Class对象的成员变量
Field fName = c.getField("成员变量名");
4 通过无参构造方法创建对象
Object obj = con.newInstance();
5 给对象的成员变量赋值
fName.set(obj,"设置的值");
需求:反射获取成员变量并使用
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException
, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("itiheima315.test2.Student");
Field[] fields = c.getDeclaredFields();
for(Field f:fields){
System.out.println(f);
}
System.out.println("-------");
Field addressField = c.getField("address");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
addressField.set(obj,"西安");
System.out.println(obj);
}
}
练习: 需求: 通过反射实现: Student s = new Student(); s.name = “汪苏泷”; s.age = 30; s.address = “阳泉”; System.out.println(s);
public class ReflectTest3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class<?> c = Class.forName("itiheima315.test2.Student");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Field fname = c.getDeclaredField("name");
fname.setAccessible(true);
Field fage = c.getDeclaredField("age");
fage.setAccessible(true);
Field faddress = c.getField("address");
fname.set(obj, "汪苏泷");
fage.set(obj, 30);
faddress.set(obj,"北京");
System.out.println(obj);
}
}
注意: 当想使用私有的成员变量的时候,需要进行如下操作:
Field f = conStructor对象.getDeclaredField("成员变量名");
f.setAccessible(ture);
反射获取成员方法并使用 Method
获取成员方法的方法:
方法名 | 说明 |
---|
Method[] getMethods() | 返回类或接口的所有公共方法 包括类或接口声明的对象以及从超类和超级接口继承的类 | Method[] getDeclaredMethods() | 返回类或接口声明的所有方法 包括public、protected、default和private,不包括继承方法 | Method[] getMethod(String name,Class<?> … parametreTyoes) | 返回类或接口的指定公共成员方法 | Method[] getDeclaredMethod(String name,Class<?> … parametreTyoes) | 返回类或接口的指定声明的方法 |
Method方法
- 在类或接口上提供有关单一方法的信息和访问权限
- 实现对象调用方法
方法名 | 说明 |
---|
Object invoke(Object obj,Object …args) | 在具有指定参数的指定对象上调用此方法表示的基础方法 Object返回值类型;obj调用方法的对象;args方法需要的参数 |
反射获取成员方法的具体代码实现:
1 获取Class对象
Class<?> c = Class.forName("类的路径");
2 获取无参构造方法
Constructor con = c.getConstructor();
3 无参构造方法创建对象
Object obj = con.newInstance();
4 获取成员方法
Method m = c.getMethod("成员方法名");
5 对象使用成员方法对象
m.invoke(obj,"成员方法所需参数");
代码:
public class ReflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("itiheima315.test2.Student");
Method m1 = c.getMethod("method1");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
m1.invoke(obj);
}
}
练习
练习1
通过反射完成: Student s = new Student(); s.method1(); s.method2(“汪苏泷”); String ss = s.method3(“许嵩”,32); System.out.println(ss); s.function(); 代码:
public class ReflectTest4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> c = Class.forName("itiheima315.test2.Student");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method method1 = c.getDeclaredMethod("method1");
method1.invoke(obj );
Method method2 = c.getDeclaredMethod("method2", String.class);
method2.invoke(obj,"汪苏泷");
Method method3 = c.getDeclaredMethod("method3", String.class, int.class);
Object ss = method3.invoke(obj, "许嵩", 30);
String st = (String)ss;
System.out.println(ss);
Method function1 = c.getDeclaredMethod("function");
function1.setAccessible(true);
function1.invoke(obj);
}
}
练习2
有一个ArrayList< Integer>集合,在这个集合中添加一个字符串数据,如何实现? 反射可以完成一些正常情况下无法完成的事情 反射可以越过泛型检查的,获取到原始的方法所需要的参数类型 代码:
public class ReflectTes5 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException,
IllegalAccessException {
ArrayList<Integer> array = new ArrayList<Integer>();
Class<? extends ArrayList> c = array.getClass();
Method madd = c.getMethod("add",Object.class);
madd.invoke(array, "pretty");
madd.invoke(array,"sunshine");
System.out.println(array);
}
}
练习3
通过配置文件运行类中的方法 代码:
public class ReflectDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException, InstantiationException {
Properties prop = new Properties();
FileReader fr = new FileReader(".\\class.txt");
prop.load(fr);
fr.close();
String className = prop.getProperty("className");
String methodName = prop.getProperty("MethodName");
Class<?> c = Class.forName(className);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);
m.invoke(obj);
}
}
模块化
概念
- 随着java语言的发展,逐渐成为一个“臃肿”的语言,无论是大系统还是小软件,JVM都需要加载整个JRE环境;
- java9实现了模块化,成功给java实现了瘦身,允许java程序可以根据需求选择要加载的模块;
- 整体的项目project,下面依次是:模块、包、类或者接口;
- 模块与模块之间是独立的,也可以作为访问权限的界定边界,可以通过模块的描述文件设置包是否被暴露处理、隐藏处理,对于隐藏的包即使它所包含的java类型使用了public修饰,别的模块仍旧不能访问这些类型。
模块基本使用
基本使用步骤:
- 创建模块 …包、类、定义方法
定义两个模块,myOne和MyTwo - 在模块的src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块名、访问权限、模块依赖等信息,描述性文件中使用模块导出和模块依赖进行配置并使用。
所要使用的和被使用的两个模块都创建module-info.java文件 - 模块中所有未导出的包都是模块私有的,他们不能在模块之外被访问
模块导出格式:exports 包名; - 一个模块要访问其他模块,必须明确指定依赖那些模块,未明确指定依赖的模块不能访问
模块依赖格式:requires 模块名;
|