1.Java反射专题
反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对像的属性及方法。反射在设计模式和框架底层都会用到。反射面前一切都是纸老虎
1.1.反射是什么—一个需求引出反射
1
2
3
|
根据配置文件re.properties内容,创建Cat对象并调用hi方法
classfullpath=com.edu.Cat
method=hi
|
开闭原则(ocp原则):不修改源码,来扩展功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class Question {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//根据配置文件re.properties内容,创建Cat对象并调用hi方法
//使用之前学的无法解决,只能使用反射进行解决
//使用Properties类读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
System.out.println(classfullpath);
System.out.println(methodName);
//1.加载类,返回class类型的对象cls
Class cls = Class.forName(classfullpath);
//2.通过cls得到你加载类的对象示例
Object object = cls.newInstance();
System.out.println("object对象的运行类型"+object.getClass()); //运行类型
//3.通过cls得到你加载的类com.edu.Cat的methodName "hi"对象
//即:在反射中,可以把方法视为对象,万物皆对象
Method method1 = cls.getMethod(methodName);
//通过method1调用方法invode
method1.invoke(object);
}
}
|
1.2.反射原理图
这张图片展示了Java程序的三个主要阶段:代码阶段/编译阶段、Class类阶段(加载阶段)和Runtime运行阶段。
1
2
3
|
在这个阶段,开发者编写Java源代码文件,例如Cat.java。
代码中定义了类Cat,包括私有字符串变量name、公共构造函数Cat()和公共方法hi()。
通过Java编译器,源代码被编译成字节码文件Cat.class。
|
1
2
3
|
在这个阶段,类加载器(ClassLoader)将字节码文件Cat.class加载到内存中。
加载后,形成了Class对象,包含了类的元数据,如成员变量(Field)、构造器(Constructor)和成员方法(Method)。
这些元数据存储在堆内存中,供后续的运行时使用。
|
1
2
3
4
5
6
|
在运行时阶段,Java虚拟机(JVM)执行字节码。
通过new Cat()创建Cat类的实例对象cat。
该对象知道它属于哪个Class对象,并且可以通过该Class对象访问其元数据。
得到Class对象后,可以进行以下操作:
创建对象,调用构造函数。
操作对象的属性和方法。
|

1.3.反射机制

1.3.1.java.lang.reflect.Field—获取某个类的字段
** java.lang.reflect.Field:代表类的成员变量,Field类表示某个类的成员变量**
获取某个类的字段
getField不能获取private修饰的私有属性
getDeclaredField可用来获取私有属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package com.edu.reflection;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class Reflection1 {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//使用Properties类读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
//1.加载类,返回class类型的对象cls
Class cls = Class.forName(classfullpath);
//2.通过cls得到你加载类的对象示例
Object object = cls.newInstance();
System.out.println("object对象的运行类型"+object.getClass()); //运行类型
//3.通过cls得到你加载的类com.edu.Cat的methodName "hi"对象
//即:在反射中,可以把方法视为对象,万物皆对象
Method method1 = cls.getMethod(methodName);
//通过method1调用方法invode
method1.invoke(object);
//java.lang.reflect.Field:代表类的成员变量,Field类表示某个类的成员变量
//获取某个类的字段
// getField不能获取私有的属性
Field name = cls.getField("age");
System.out.println(name.get(object)); //10
//getDeclaredField可用来获取私有属性
Field name1 = cls.getDeclaredField("name");
name1.setAccessible(true);
System.out.println(name1.get(object));
}
}
|
1.3.2.java.lang.reflect.Constructor:获取构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
java.lang.reflect.Constructor:代表类的构造方法,Constructor表示构造器
//1.加载类,返回class类型的对象cls
Class cls = Class.forName(classfullpath);
//2.通过cls得到你加载类的对象示例
Object object = cls.newInstance();
System.out.println("object对象的运行类型"+object.getClass()); //运行类型
//3.通过cls得到你加载的类com.edu.Cat的methodName "hi"对象
//即:在反射中,可以把方法视为对象,万物皆对象
Method method1 = cls.getMethod(methodName);
//通过method1调用方法invode
method1.invoke(object);
//java.lang.reflect.Constructor:代表类的构造方法,Constructor表示构造器
//获取无参构造器
Constructor constructor = cls.getConstructor();
System.out.println(constructor);
//获取有参构造器
Constructor constructor1 = cls.getConstructor(String.class); //这里的String.class就是String类的Class对象
System.out.println(constructor1);
|
1.4.反射的优点与缺点
优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
缺点:使用反射是基于解释执行,对执行速度有影响
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class Reflection2 {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
m1();
m2();
m3();
}
//使用传统方法创建Cat对象耗时
public static void m1(){
long start = System.currentTimeMillis();
for (int i = 0;i<1000000;i++){
Cat cat = new Cat();
}
long end = System.currentTimeMillis();
System.out.println("传统方法耗时:"+(end-start));
}
public static void m2() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
long start = System.currentTimeMillis();
Class<?> cls = Class.forName("com.edu.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
for (int i = 0;i<1000000;i++){
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射方法耗时:"+(end-start));
}
public static void m3() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
long start = System.currentTimeMillis();
Class<?> cls = Class.forName("com.edu.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
hi.setAccessible(true);
for (int i = 0;i<1000000;i++){
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射方法耗时:"+(end-start));
}
}
|
1.5.通过反射获取类的结构信息
//getName:获取全类名
//getSimpleName:获取简单类名
//getFields:获取所有public修饰的属性,包括本类以及父类的
//getDeclaredFields:获取本类中所有属性
//getMethods:获取所有public修饰的方法,包含本类以及父类
//getDeclaredMethods:获取本类中所有方法
//getConstructors:获取public修饰的构造器,只包含本类不包含父类
//getDeclaredConstructors:获取本类中所有构造器
//getPackage:以package形式返回包信息
//getSuperclass:以class形式返回父类信息
//getInterfaces:以Class形式返回接口信息
//getAnnotations:以Annotation[]形式返回注解信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
package com.edu.reflection;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/*
* 演示如何通过反射获取类的结构信息
* */
public class ReflectionUtils {
public static void main(String[] args) {
}
//第一组方法API
@Test
public void api01() throws ClassNotFoundException {
//得到class对象
Class<?> cls = Class.forName("com.edu.reflection.Person");
//getName:获取全类名
System.out.println(cls.getName()); //com.edu.reflection.Person
//getSimpleName:获取简单类名
System.out.println(cls.getSimpleName());//Person
//getFields:获取所有public修饰的属性,包括本类以及父类的
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field.getName()); //hobby,name
}
//getDeclaredFields:获取本类中所有属性
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName()); //name,age,job,sal
}
//getMethods:获取所有public修饰的方法,包含本类以及父类
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
//getDeclaredMethods:获取本类中所有方法
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
//getConstructors:获取public修饰的构造器,只包含本类
Constructor<?>[] constructors = cls.getConstructors();
//getDeclaredConstructors:获取本类中所有构造器
Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
//getPackage:以package形式返回包信息
System.out.println(cls.getPackage()); //com.edu.reflection
//getSuperclass:以class形式返回父类信息
Class<?> superclass = cls.getSuperclass();
System.out.println(superclass); //com.edu.reflection.A
//getInterfaces:以Class形式返回接口信息
Class<?>[] interfaces = cls.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface.getName());
}
//getAnnotations:以Annotation[]形式返回注解信息
Annotation[] annotations = cls.getAnnotations();
System.out.println(annotations); //Deprecated
}
}
class A{
public String hobby;
public void hi(){}
}
interface IA{
}
interface IB{
}
@Deprecated
class Person extends A implements IA,IB{
//四种不同访问权限的属性
public String name;
protected int age;
String job;
private double sal;
public void m1(){}
protected void m2(){}
void m3(){}
private void m4(){}
}
|
//getModifiers:以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
如果有属性是punlic(1) static(8) = 9 修饰,那么他的结果是相加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public void api02() throws ClassNotFoundException{
//得到class对象
Class<?> cls = Class.forName("com.edu.reflection.Person");
//getDeclaredFields:获取本类中所有属性
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName()); //name,age,job,sal
System.out.println("该属性的修饰符值="+declaredField.getModifiers());
System.out.println("该属性对应的类型"+declaredField.getType()); //class java.lang.String
}
//getDeclaredMethods:获取本类中所有方法
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类中所有方法:"+declaredMethod.getName());
System.out.println("本类中方法所对应修饰符的值"+declaredMethod.getModifiers());
System.out.println("该方法返回类型"+declaredMethod.getReturnType()); //void
//输出当前这个方法的形参数组情况
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该方法的形参类型"+parameterType);
}
}
}
|
1.6.通过反射爆破创建对象实例
方法1:调用类中的public修饰的无参构造器
方法2:调用类中的指定构造器
Class类相关方法:
1
2
3
|
newInstance:调用类中的无参构造器,获取对应类的对象
getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象
getDecalaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器对象
|
Constructor类相关方法:
1
2
|
setAccessible:爆破
newInstance(Object...obj):调用构造器
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
package com.edu;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* 演示通过反射机制创建实例
* */
public class CreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.先获取User类的Class对象
Class<?> Userclass = Class.forName("com.edu.User");
//2.通过public的无参构造器创建实例
Object o = Userclass.newInstance();
System.out.println(o); //姓名为张三*******年龄为13
//3.通过public的有参构造器创建实例
//此时此刻,这个constructor对象就是下面这个构造器
/*
* public User(String name){ //public有参构造器
this.name = name;
*/
//3.1.先得到对应的构造器,getConstructor返回public的构造器
Constructor<?> constructor = Userclass.getConstructor(String.class);
//3.2.创建实例,并传入实参
Object lisi = constructor.newInstance("lisi");
System.out.println(lisi); //姓名为lisi*******年龄为13
//4.通过非public的有参构造器创建实例---私有
Constructor<?> declaredConstructor = Userclass.getDeclaredConstructor(int.class, String.class);
declaredConstructor.setAccessible(true); //爆破,使用反射可以访问私有的private构造器
Object o1 = declaredConstructor.newInstance(18,"wangwu");
System.out.println(o1);
}
}
class User{
private int age = 13;
private String name = "张三";
public User(){} //无参 public
public User(String name){ //public有参构造器
this.name = name;
}
private User(int age,String name){ //有参 私有
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "姓名为"+name+"*******年龄为"+age;
}
}
|
1.7.通过反射爆破操作属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package com.edu;
import java.lang.reflect.Field;
/*
* 演示反射访问操作属性
* */
public class ReflectAccess {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.得到Student类对应的class对象
Class<?> StuClass = Class.forName("com.edu.Student");
//2.创建一个对象
Object o = StuClass.newInstance(); //o的运行类型为Student
//3.使用反射得到age属性对象
Field age = StuClass.getField("age");
//通过反射操作age属性
age.set(o,18);
//反射操作name属性,name属性私有且静态
Field name = StuClass.getDeclaredField("name");
//对name进行爆破
name.setAccessible(true);
//name.set(o,"张三");
name.set(null,"张三"); //因为name有static属性,所以o也可以写成null
System.out.println(o);
}
}
class Student{
public int age;
private static String name;
public Student(){
}
@Override
public String toString() {
return "Student{" +
"age=" + age + "......." +
"name" + name +
'}';
}
}
|
1.8.通过反射爆破操作方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package com.edu;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* 演示反射调用方法
* */
public class ReflectAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.得到Boss类对应的class对象
Class<?> cls = Class.forName("com.edu.Boss");
//2.创建对象
Object o = cls.newInstance();
//3.得到hi方法对象
Method hi = cls.getMethod("hi",String.class);
//4.调用方法
Object invoke = hi.invoke(o,"666");
Method say = cls.getDeclaredMethod("say", int.class, String.class, char.class);
say.setAccessible(true);
Object invoke1 = say.invoke(o, 100, "张三", '男');
System.out.println(invoke1);
//因为say方法是静态的,所以对象位置可以使用null代替
System.out.println(say.invoke(null, 99, "小红", '女'));
//5.在反射中,如果方法有返回值,统一返回object
}
}
class Boss{
public int age;
private static String name;
public Boss(){}
private static String say(int n,String s,char c){
return n + "" + s + "" + c;
}
public void hi(String s){
System.out.println("hi"+s);
}
}
|
1.9.小练习
利用反射和File完成以下功能
1.利用Class类的forName方法得到File类的class对象
2.在控制台打印File类的所有构造器
3.通过newInstance的方法创建File对象,并创建mynew.txt文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package com.edu.homework;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* */
public class homework2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.利用Class类的forName方法得到File类的class对象
Class<?> cls = Class.forName("java.io.File");
//2.得到所有构造器
Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("File的构造器"+declaredConstructor);
}
//3.通过指定构造器创建文件file对象
Constructor<?> declaredConstructor = cls.getDeclaredConstructor(String.class);
String filepath = "E:\\mynew.txt";
Object fileobject = declaredConstructor.newInstance(filepath); //创建file对象,运行类型就是File
//4.得到createNewFile的方法对象
Method createNewFile = cls.getMethod("createNewFile");
createNewFile.invoke(fileobject); //创建文件,调用createNewFile
//
System.out.println(fileobject.getClass());
System.out.println("创建文件成功:"+filepath);
}
}
|
2.Class类

1
2
3
4
5
6
7
8
|
1.Class也是类,因此也继承object类[类图]
2.Class类对象不是new出来的,而是系统创建的
3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
4.每个类的实例都会记得自己是由哪个Class实例所生成
5.通过Class可以完整地得到一个类的完整结构,通过一系列API
6.Class对象是存放在堆的
7.类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,
变量名,方法名,访问权限等等)
|
https://www.zhihu.com/question/38496907
1
2
3
4
5
|
//3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
Class<?> cls1 = Class.forName("com.edu.Cat");
Class<?> cls2 = Class.forName("com.edu.Cat");
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
|
2.1.Class类常用方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package com.edu.Class;
import com.edu.Car;
import java.lang.reflect.Field;
public class class2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
String classPath = "com.edu.Car";
//1.获取Car类对应的Class对象
//<?>表示不确定的Java类型
Class<?> cls = Class.forName(classPath);
System.out.println(cls); //显示cls对象,是哪个类的Class对象 com.edu.Car
System.out.println(cls.getClass()); //输出cls的运行类型 java.lang.Class
//2.得到包名 ---- getPackage().getName()
System.out.println(cls.getPackage().getName()); //com.edu
//3.得到全类名 ----- getName()
System.out.println(cls.getName()); //com.edu.Car
//4.通过cls创建对象实例 ----newInstance()
Car car = (Car)cls.newInstance();
System.out.println(car); //Car{brand='宝马', price=19999999, color='红色'}
//5.通过反射获取属性 ---getField
Field field = cls.getField("brand");
System.out.println(field.get(car)); //宝马
//6.通过反射修改属性 -------field.set(car,"奔驰");
field.set(car,"奔驰");
System.out.println(field.get(car)); //奔驰
//7.我希望获取到所有属性 --------cls.getFields()
Field[] fields = cls.getFields();
for (Field field1 : fields) {
System.out.println(field1.getName()); //brand price color
}
}
}
|
2.2.获取Class对象的六种方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
/*
* 获取class对象的各种方式
* */
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1.已知一个类的全类名,且在类的类路径下,可通过Class的静态方法forname()获取
//应用场景:多用于配置文件,读取全路径加载类
Class<?> cls1 = Class.forName("com.edu.Car");
System.out.println(cls1);
//2.若知道具体类,通过类的class获取,该方式是最安全可靠,程序性能最高实例
//应用场景:多用于参数传递,比如通过反射得到对应构造器对象
Class<Car> cls2 = Car.class;
System.out.println(cls2);
//3.已知某个类的实例,调用该实例的getClass()方法获取Class对象
//应用场景:通过创建好的对象,获取Class对象
Car car = new Car();
Class<? extends Car> cls3 = car.getClass();
System.out.println(cls3);
//4.通过类加载器【有4种类加载器】获取到类的Class对象
//(1)先获取类加载器 Car
ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通过类加载器得到class对象
Class<?> cls4 = classLoader.loadClass("com.edu.Car");
System.out.println(cls4);
//cls1 cls2 cls3 cls4其实是同一个对象
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());
//5.基本数据类型(int,char,boolean,float,double,byte,long,short)
//获取Class对象
Class cls5 = long.class;
System.out.println(cls5);
//6.基本数据类型对应的包装类,可通过.type得到Class类对象
Class cls6 = Integer.TYPE;
System.out.println(cls6);
}
}
|
2.3.哪些类型有Class对象
1.外部类,成员内部类,静态内部类,局部内部类,匿名内部类
2.interface 接口
3.数组
4.enum 枚举类型
5.annotation:注解
6.基本数据类型
7.Void
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.edu.Class;
import java.io.Serializable;
public class AlltypeClass {
public static void main(String[] args) {
Class<String> cls1 = String.class; //外部类
Class<Serializable> cls2 = Serializable.class; //接口
Class<Integer[]> cls3 = Integer[].class; //数组
Class<float[][]> cls4 = float[][].class; //二维数组
Class<Deprecated> cls5 = Deprecated.class; //注解
Class<Thread.State> cls6 = Thread.State.class; //枚举
Class<Long> cls7 = long.class; //基本数据类型
Class<Void> cls8 = void.class; //void类型
Class<Class> cls9 = Class.class; //
}
}
|
3.类加载
3.1.动态加载与静态加载
静态加载:编译时加载相关类,如果没用则报错,依赖性太强
动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class Classload1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入key");
String key = sc.next();
switch (key){
case "1" :
Dog dog = new Dog(); //静态加载,编译时直接报错,依赖性强
dog.cry();
break;
case "2":
//反射--->动态加载
Class<?> cls = Class.forName("Dog"); //动态加载,编译时可通过,运行时没用该类才会报错
Object o = cls.newInstance();
Method method = cls.getMethod("h1");
method.invoke(o);
break;
default:
System.out.println("do nothing....");
}
}
}
|
3.2.类加载流程图

类加载每个阶段需要干的事

3.3.类加载5个阶段
3.3.1.加载阶段
JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象
3.3.2.连接阶段
验证阶段
目的是确保当前Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全
包括:文件格式验证(是否以魔数 0xcafebabe开头),元数据验证,字节码验证和符号引用验证
可以考虑使用-X verify:none参数来关闭大部分类验证措施,缩短虚拟机类加载的时间

准备阶段
1
2
3
4
5
6
7
8
|
class A{
//1.n1是实例变量,不是静态变量,因此在准备阶段,是不会分配内存的
//2.n2是静态变量,准备阶段需要分配内存,n2的默认值为0,而不是20,20是在初始化阶段进行赋值的
//3.n3是常量,他和静态变量不一样,因为一旦赋值就不变,n3 = 30
public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;
}
|
解析阶段
虚拟机将常量池内的符号引用替换为直接引用的过程
3.3.3.初始化阶段
到初始化阶段才真正开始执行类中定义的Java程序代码,此阶段是执行()方法的过程。
()方法由编译器**按语句在源文件中出现的顺序,依次收集类中所有静态变量的赋值动作和静态代码块中**的语句,并进行合并
虚拟机会保证一个类的()方法在多线程环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package com.edu.ClassLoad;
/*
* 类加载初始化阶段
* */
public class ClassLoad3 {
public static void main(String[] args) {
//1.加载B类,并生成 B的class对象
//2.连接 num = 0;
//3.初始化阶段:依次收集类中所有静态变量的赋值动作和静态代码块中的语句,并合并
//client(){
// static {
// System.out.println("B 的静态代码块被执行");
// num = 300;
// num = 100;
// }
//合并之后num=100
// }
//new B();
System.out.println(B.num);
}
}
class B{
static {
System.out.println("B 的静态代码块被执行");
num = 300;
}
static int num = 100;
public B(){ //构造器不会执行,除非你new 了B这个对象才会执行
System.out.println("B 的构造器被执行");
}
}
|