概念:
当程序要使用某个类时,如果该类未被加载到内存中,则系统会通过类的加载、类的连接、类的初始化三个步骤对类进行初始化。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);//AppClassLoader(应用程序类加载器)
// 父类加载器
ClassLoader c2 = c.getParent();
System.out.println(c2);//ExtClassLoader(扩展类加载器) 视频中是 PlatformClassLoader
// java8 的ExtClassLoader = java9 的 PlatformClassLoader
ClassLoader c3 = c2.getParent();
System.out.println(c3);// null
}
}
反射
概念:
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属性**来获取该类对应的Class对象
Class c1 = Student.class;
System.out.println(c1);
// 输出内容是:class itiheima315.test2.Student
// 一个类在内存中只有一个字节码文件
Class c2 = Student.class;
System.out.println(c1 == c2);
// 输出:true
System.out.println("------");
// 调用**对象的getClass()方法**
Student s = new Student();
Class extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
// 使用Class类中的静态方法forName(String className)
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对象,也就是类的字节码对象
Class> c = Class.forName("itiheima315.test2.Student");
// 得到Student类的字节码文件对象
// 要想获取构造方法,要在Class类中找方法获取构造方法。
// Constructor< ? >[] getConstructors() |返回一个包含Constructor对象的数组
// Constructor>[] arrayC = c.getConstructors();
// for(Constructor con:arrayC){
// System.out.println(con);
// }
// 结果:
// public itiheima315.test2.Student(java.lang.String,int,java.lang.String)
//public itiheima315.test2.Student()
// Constructor>[] getDeclaredConstructors()|返回反映该Class对象的类声明的所有构造函数的Constructor对象的数组
// Constructor>[] conS = c.getDeclaredConstructors();
// for (Constructor con : conS) {
// System.out.println(con);
// }
// 结果
//Constructor< T> getConstructor(Class>.....parameterType)|返回一个Constructor对象,该对象反映该Class对象表示的类的指定公共构造函数|
Constructor> c0 = c.getConstructor();//拿public的无参构造方法
// System.out.println(c0);
// 使用Constructor对象来实现通过类创建对象。
Object obj = c0.newInstance();
System.out.println(obj);
// 输出Student{name='null', age=0, address='null'}
// Constructor< T> getDeclaredConstructor(Class>...parameterType)|返回一个Constructor对象,该对象反映由此Class对象表示的类或接口的指定构造函数|
}
}
案例:反射
案例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");
// 获取Class对象
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);
// 暴力反射
// public void setAccessible(boolean flag):值为true,取消访问检查
con.setAccessible(true);
Object obj = con.newInstance("林俊杰");
System.out.println(obj);
// 获取到的私有构造方法不能创建对象 会报错 IllegalAccessException
// 加了setAccessible方法 可以实现私有方法创建对象
// 输出:Student{name='林俊杰', age=0, address='null'}
}
}
反射获取成员变量并使用 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.getFields();//获取公共的成员变量
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对象
Class> c = Class.forName("itiheima315.test2.Student");
// 获取无参构造方法
Constructor> con = c.getConstructor();
// 创建对象
Object obj = con.newInstance();
// 获取成员变量
// Field fname = c.getField("name");
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[] methods = c.getMethods();//本类的 以及 继承的公共方法
// Method[] methods = c.getDeclaredMethods();// 本类所有的方法
// for(Method method:methods){
// System.out.println(method);
// }
// }
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 {
// //使用反射实现如下操作:
// Student s = new Student();
// s.method1();
// s.method2("汪苏泷");
// String ss = s.method3("许嵩",32);
// System.out.println(ss);
// s.function();
Class> c = Class.forName("itiheima315.test2.Student");
Constructor> con = c.getConstructor();
Object obj = con.newInstance();
// s.method1();
Method method1 = c.getDeclaredMethod("method1");
method1.invoke(obj );
// s.method2("汪苏泷");
Method method2 = c.getDeclaredMethod("method2", String.class);
method2.invoke(obj,"汪苏泷");
//String ss = s.method3("许嵩",32);
Method method3 = c.getDeclaredMethod("method3", String.class, int.class);
Object ss = method3.invoke(obj, "许嵩", 30);
// System.out.println(ss);
String st = (String)ss;
System.out.println(ss);
// s.function();
Method function1 = c.getDeclaredMethod("function");
// 由于function是私有成员,所以需要暴力反射
function1.setAccessible(true);
function1.invoke(obj);
}
}
练习2
有一个ArrayList< Integer>集合,在这个集合中添加一个字符串数据,如何实现?
反射可以完成一些正常情况下无法完成的事情
反射可以越过泛型检查的,获取到原始的方法所需要的参数类型
代码:
public class ReflectTes5 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException,
IllegalAccessException {
ArrayList array = new ArrayList();
// array.add(10);
// array.add(20);
// 获取Class对象,使用对象.getClass方法实现。
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 {
// Student s = new Student();
// s.study();
//
// Teacher t = new Teacher();
// t.teach();
// 为了方便使用Student和Teacher两个类,可以使用配置文件完成 而不用每次修改main方法
// 加载数据
Properties prop = new Properties();
FileReader fr = new FileReader(".\class.txt");
// char[] chs = new char[1024];
// int len;
// while((len = fr.read(chs)) != -1){
// System.out.println(new String(chs,0,len));
// }
// 输出结果是:className=itiheima315.test3.Student
//MethodName=study
// 说明 内容已被读入
prop.load(fr);
fr.close();
// 数据文件加载成功
String className = prop.getProperty("className");
String methodName = prop.getProperty("MethodName");
// 使用不使用 类以及其成员方法 在class.txt文件中修改
// 通过反射来使用
Class> c = Class.forName(className);//得到的是itiheima315.test3.Student
Constructor> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);
// System.out.println(m);
m.invoke(obj);
}
}
模块化
概念
随着java语言的发展,逐渐成为一个“臃肿”的语言,无论是大系统还是小软件,JVM都需要加载整个JRE环境;java9实现了模块化,成功给java实现了瘦身,允许java程序可以根据需求选择要加载的模块;整体的项目project,下面依次是:模块、包、类或者接口;模块与模块之间是独立的,也可以作为访问权限的界定边界,可以通过模块的描述文件设置包是否被暴露处理、隐藏处理,对于隐藏的包即使它所包含的java类型使用了public修饰,别的模块仍旧不能访问这些类型。 模块基本使用
基本使用步骤:
创建模块 …包、类、定义方法
定义两个模块,myOne和MyTwo在模块的src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块名、访问权限、模块依赖等信息,描述性文件中使用模块导出和模块依赖进行配置并使用。
所要使用的和被使用的两个模块都创建module-info.java文件模块中所有未导出的包都是模块私有的,他们不能在模块之外被访问
模块导出格式:exports 包名;一个模块要访问其他模块,必须明确指定依赖那些模块,未明确指定依赖的模块不能访问
模块依赖格式:requires 模块名;
注意:写模块名报错,需要按下Alt+enter,选择模块依赖。
代码:
MyOne模块
package itiheima1;
public class Student {
public void study(){
System.out.println("你的自律,给你自信");
}
}
package itiheima2;
public class Teacher {
public void teach(){
System.out.println("全心付出只为金榜题名");
}
}
module MyOne {
exports itiheima1;
}
MyTwo模块:
package itiheima315;
import itiheima1.Student;
public class Test1 {
public static void main(String[] args) {
Student s = new Student();
s.study();
}
}
module-info.java文件
module MyTwo {
requires MyOne;
}
模块服务的使用
概述:
java6开始,提供了服务机制 = 允许服务提供者和服务使用者之间完成解耦 = 服务使用者只面向接口编程,不清楚服务提供者的实现类java9的模块化编程系统进一步简化了java的服务机制java9允许 = 》服务接口定义在模块中 =》使用uses语句声明该服务接口=》针对服务接口提供不同的服务实现类=》服务实现模块使用provides语句为服务接口指定实现类。 同样是服务使用者面向接口编程
模块服务的使用步骤
在模块a下创建包a,创建接口a定义抽象方法a在包a下创建包b,创建接口a的两个实现类 类a和类b模块a中的描述文件module-info.java文件中写如下配置:
模块导出:exports 包a
服务提供:provides 接口a with 类a/类b在模块b的描述文件module-info.java中添加如下配置:
声明服务接口:uses 接口a;在模块b的类中使用接口a提供的服务
ServiceLoader:一种加载服务实现的工具
ServiceLoader
在java.util包下 使用需要导包public final class ServiceLoader< S > extends Object implements Iterable< S>说明是最终类,且可以使用增强for循环遍历一种加载服务实现的工具获取服务装载程序 =》 通过 ServiceLoader的静态方法load
例子:ServiceLoader.load(服务名.class)参数是服务的class对象
代码:
详细是在MyOne和MyTwo中,
package itiheima315;
import itiheima3.MyService;
import java.util.ServiceLoader;
public class Test2 {
public static void main(String[] args) {
// 加载服务
ServiceLoader myService = ServiceLoader.load(MyService.class);
// 遍历服务
for(MyService my:myService){
my.service();
}
}
}



