java学习笔记——基础31(类加载器,反射)

1、类加载器
2、反射构造方法
3、反射成员变量
4、反射成员方法
5、反射配置文件运行类中的方法

1JVM运行时数据区




2类加载器

*A.类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

a 加载
……
就是指将class文件读入内存,并为之创建一个Class文件的对象。
……* 任何类被使用时系统都会建立一个Class对象

b 连接
……
验证 是否有正确的内部结构,并和其他类协调一致
…… 准备 负责为类的静态成员分配内存,并设置默认初始化值
……
解析 将类的二进制数据中的符号引用替换为直接引用

c 初始化
……
就是之前的初始化步骤(new 对象)
……* 注:简单的说就是:把.class文件加载到内存里,并把这个.class文件封装成一个Class类型的对象。

B.类的加载时机

以下的情况,会加载这个类。

  • a. 创建类的实例
  • b. 类的静态变量,或者为静态变量赋值
  • c. 类的静态方法
  • d. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • e. 初始化某个类的子类
  • f. 直接使用java.exe命令来运行某个主类

* C: 类加载器

类加载器(Class Loader):顾名思义,指的是可以加载类的工具。JVM自身定义了三个类加载器:引导类加载器(Bootstrap Class Loader)、拓展类加载器(Extension Class Loader )、应用加载器(Application Class Loader)。当然,我们有时候也会自己定义一些类加载器来满足自身的需要。
负责将.class文件加载到内在中,并为之生成对应的Class对象。

* a. Bootstrap ClassLoader 引导类加载器
  • 也被称为根类加载器负责Java核心类的加载
  • 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
    引导类加载器(Bootstrap Class Loader): 该类加载器使JVM使用C/C++底层代码实现的加载器,用以加载JVM运行时所需要的系统类,这些系统类在{JRE_HOME}/lib目录下。由于类加载器是使用平台相关的底层C/C++语言实现的, 所以该加载器不能被Java代码访问到。但是,我们可以查询某个类是否被引导类加载器加载过。我们经常使用的系统类如:java.lang.String,java.lang.Object,java.lang*……. 这些都被放在 {JRE_HOME}/lib/rt.jar包内, 当JVM系统启动的时候,引导类加载器会将其加载到 JVM内存的方法区中
* b. Extension ClassLoader 扩展类加载器
  • 负责JRE的扩展目录中jar包的加载。
    * 在JRE的lib目录下ext目录
    拓展类加载器(Extension Class Loader): 该加载器是用于加载 java 的拓展类 ,拓展类一般会放在 {JRE_HOME}/lib/ext/ 目录下,用来提供除了系统类之外的额外功能。拓展类加载器是是整个JVM加载器的Java代码可以访问到的类加载器的最顶端,即是超级父加载器,拓展类加载器是没有父类加载器的。
* c. Applocatoin ClassLoader 应用类加载器

又称为是System ClassLoader 系统类加载器;

  • 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
  • 我们用的是System ClassLoader 系统类加载器

应用类加载器(Applocatoin Class Loader): 该类加载器是用于加载用户代码,是用户代码的入口。我经常执行指令 java xxx.x.xxx.x.x.XClass , 实际上,JVM就是使用的AppClassLoader加载 xxx.x.xxx.x.x.XClass 类的。应用类加载器将拓展类加载器当成自己的父类加载器,当其尝试加载类的时候,首先尝试让其父加载器-拓展类加载器加载;如果拓展类加载器加载成功,则直接返回加载结果Class instance;如果加载失败,则会询问是否引导类加载器已经加载了该类;只有没有加载的时候,应用类加载器才会尝试自己加载。由于xxx.x.xxx.x.x.XClass是整个用户代码的入口,在Java虚拟机规范中,称其为 初始类(Initial Class).

* d. Customized ClassLoader 用户自定义类加载器

用户自定义类加载器(Customized Class Loader):用户可以自己定义类加载器来加载类。所有的类加载器都要继承java.lang.ClassLoader类。

3类加载过程

1、创建一个引导类加载器实例,初步加载系统类到内存方法区区域中:

JVM申请好内存空间后,JVM会创建一个引导类加载器(Bootstrap Classloader)实例,引导类加载器是使用C++语言实现的,负责加载JVM虚拟机运行时所需的基本系统级别的类,如java.lang.String, java.lang.Object等等。
引导类加载器(Bootstrap Classloader)会读取 {JRE_HOME}/lib 下的jar包和配置,然后将这些系统类加载到方法区内

  1. 引导类加载器将类信息加载到方法区中,以特定方式组织,对于某一个特定的类而言,在方法区中它应该有 运行时常量池、类型信息、字段信息、方法信息、类加载器的引用,对应class实例的引用等信息。
  1. 类加载器的引用,由于这些类是由引导类加载器(Bootstrap Classloader)进行加载的,而 引导类加载器是有C++语言实现的,所以是无法访问的,故而该引用为NULL
  1. 对应class实例的引用, 类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点。

当我们在代码中尝试获取系统类如java.lang.Object的类加载器时,你会始终得到NULL:

1
2
3
4
System.out.println(String.class.getClassLoader());//null
System.out.println(Object.class.getClassLoader());//null
System.out.println(Math.class.getClassLoader());//null
System.out.println(System.class.getClassLoader());//null

2、创建JVM 启动器实例 Launcher,并取得类加载器ClassLoader

加载过程

上述步骤完成,JVM基本运行环境就准备就绪了。接着,我们要让JVM工作起来了:运行我们定义的程序 org.luanlouis,jvm.load.Main。

此时,JVM虚拟机调用已经加载在方法区的类sun.misc.Launcher 的静态方法getLauncher(), 获取sun.misc.Launcher 实例:

1
2
sun.misc.Launcher launcher = sun.misc.Launcher.getLauncher(); //获取Java启动器
ClassLoader classLoader = launcher.getClassLoader(); //获取类加载器ClassLoader用来加载class到内存来

sun.misc.Launcher 使用了单例模式设计,保证一个JVM虚拟机内只有一个sun.misc.Launcher实例。
在Launcher的内部,其定义了两个类加载器(ClassLoader),分别是sun.misc.Launcher.ExtClassLoader和sun.misc.Launcher.AppClassLoader,这两个类加载器分别被称为拓展类加载器(Extension ClassLoader) 和 应用类加载器(Application ClassLoader).如下图所示:

图例注释:除了引导类加载器(Bootstrap Class Loader )的所有类加载器,都有一个能力,就是判断某一个类是否被引导类加载器加载过,如果加载过,可以直接返回对应的Class instance,如果没有,则返回null. 图上的指向引导类加载器的虚线表示类加载器的这个有限的访问 引导类加载器的功能。

当AppClassLoader加载类时,会首先尝试让父加载器ExtClassLoader进行加载,如果父加载器ExtClassLoader加载成功,则AppClassLoader直接返回父加载器ExtClassLoader加载的结果;如果父加载器ExtClassLoader加载失败,AppClassLoader则会判断该类是否是引导的系统类(即是否是通过Bootstrap类加载器加载,这会调用Native方法进行查找);若要加载的类不是系统引导类,那么ClassLoader将会尝试自己加载,加载失败将会抛出“ClassNotFoundException”。

双亲委派模型(parent-delegation model)

上面讨论的应用类加载器AppClassLoader的加载类的模式就是我们常说的双亲委派模型(parent-delegation model).
对于某个特定的类加载器而言,应该为其指定一个父类加载器,当用其进行加载类的时候:

  1. 委托父类加载器帮忙加载;
  2. 父类加载器加载不了,则查询引导类加载器有没有加载过该类;
  3. 如果引导类加载器没有加载过该类,则当前的类加载器应该自己加载该类;
  4. 若加载成功,返回 对应的Class 对象;若失败,抛出异常“ClassNotFoundException”。

请注意:
双亲委派模型中的”双亲”并不是指它有两个父类加载器的意思,一个类加载器只应该有一个父加载器。上面的步骤中,有两个角色:

  1. 父类加载器(parent classloader):它可以替子加载器尝试加载类
  2. 引导类加载器(bootstrap classloader): 子类加载器只能判断某个类是否被引导类加载器加载过,而不能委托它加载某个类;换句话说,就是子类加载器不能接触到引导类加载器,引导类加载器对其他类加载器而言是透明的。

一般情况下,双亲加载模型如下所示:

3使用类加载器ClassLoader加载Main类

加载顺序:

  1. 加载java.lang.Object、java.lang.System、java.io.PrintStream、java,lang.Class

    AppClassLoader尝试加载这些类的时候,会先委托ExtClassLoader进行加载;而ExtClassLoader发现不是其加载范围,其返回null;AppClassLoader发现父类加载器ExtClassLoader无法加载,则会查询这些类是否已经被BootstrapClassLoader加载过,结果表明这些类已经被BootstrapClassLoader加载过,则无需重复加载,直接返回对应的Class实例;

  2. 加载sun.security.pkcs11.P11Util

    此在{JRE_HOME}/lib/ext/sunpkcs11.jar包内,属于ExtClassLoader负责加载的范畴。AppClassLoader尝试加载这些类的时候,会先委托ExtClassLoader进行加载;而ExtClassLoader发现其正好属于加载范围,故ExtClassLoader负责将其加载到内存中。ExtClassLoader在加载sun.security.pkcs11.P11Util时也分析这个类内都使用了哪些类,并将这些类先加载内存后,才开始加载sun.security.pkcs11.P11Util,加载成功后直接返回对应的Class<sun.security.pkcs11.P11Util>实例;

  3. 加载org.luanlouis.jvm.load.Main

    AppClassLoader尝试加载这些类的时候,会先委托ExtClassLoader进行加载;而ExtClassLoader发现不是其加载范围,其返回null;AppClassLoader发现父类加载器ExtClassLoader无法加载,则会查询这些类是否已经被BootstrapClassLoader加载过。而结果表明BootstrapClassLoader 没有加载过它,这时候AppClassLoader只能自己动手负责将其加载到内存中,然后返回对应的Class<org.luanlouis.jvm.load.Main>实例引用;

以上三步骤都成功,才表示classLoader.loadClass(“org.luanlouis.jvm.load.Main”)完成,上述操作完成后,JVM内存方法区的格局会如下所示:

*A. 反射定义

  • a. JAVA反射机制是在运行状态中,
    对于任意一个类,都能够知道这个类的所有属性和方法;
    对于任意一个对象,都能够调用它的任意一个方法和属性;

    这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

  • b.反射技术

条件:处于 运行状态

已知:一个类或一个对象(根本是已知.class文件)

结果:得到这个类或对象的所有方法和属性

功能:

  1. 在运行时判断任意一个对象所属的类
  1. 在运行时构造 任意一个类的对象
  1. 在运行时判断任意一个类所具有的成员变量和方法

  2. 在运行时调用 任意一个对象的方法

  3. 生成动态代理

  • 注: 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

* B. Class类

Class类及Class对象的了解

要想解剖一个类,必须先了解Class对象。
阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

  • . 得到Class对象
    1. 有三个方法
  • 方式一: 通过Object类中的getClass()方法
    Person person = new Person();
    Class clazz = person.getClass();

  • 方式二: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)
    Class clazz = Person.class;

  • 方式三: 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
    Class c3 = Class.forName(“Person”);

注:第三种和前两种的区别是:
前两种你必须明确Person类型.
后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了

2. 得到Class对象的三个方法代码演示:

1
2
3
4
5
6
7
8
9
	代码演示
*
* 获取.class字节码文件对象的方式
* 1:通过Object类中的getClass()方法
* 2: 通过 类名.class 获取到字节码文件对象
* 3: 反射中的方法,
* public static Class<?> forName(String className) throws ClassNotFoundException
* 返回与带有给定字符串名的类或接口相关联的 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
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
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
"// 1: 通过Object类中的getClass()方法"
// Person p1 = new Person();
// Class c1 = p1.getClass();
// System.out.println("c1 = "+ c1);

"// 2: 通过 类名.class 获取到字节码文件对象"
// Class c2 = Person.class;
// System.out.println("c2 = "+ c2);
System.out.println(c2==c1);//true
System.out.println(c2.equals(c1));//true

"// 3: 反射中的方法"
Class c3 = Class.forName("cn.itcast_01_Reflect.Person");"// 包名.类名"
System.out.println("c3 = " + c3);
System.out.println(c3==c1);//true
System.out.println(c3.equals(c1));//true
}
}

* 注: Class类型的唯一性
"因为一个.class文件在内存里只生成一个Class对象,
所以无论那一种方法得到Class对象,得到的都是同一个对象。"

Person类
package cn.itcast_01_Reflect;
public class Person {
//成员变量
public String name;
public int age;
private String address;

//构造方法
public Person() {
System.out.println("空参数构造方法");
}

public Person(String name) {
this.name = name;
System.out.println("带有String的构造方法");
}
//私有的构造方法
private Person(String name, int age){
this.name = name;
this.age = age;
System.out.println("带有String,int的构造方法");
}

public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
System.out.println("带有String, int, String的构造方法");
}

//成员方法
//没有返回值没有参数的方法
public void method1(){
System.out.println("没有返回值没有参数的方法");
}
//没有返回值,有参数的方法
public void method2(String name){
System.out.println("没有返回值,有参数的方法 name= "+ name);
}
//有返回值,没有参数
public int method3(){
System.out.println("有返回值,没有参数的方法");
return 123;
}
//有返回值,有参数的方法
public String method4(String name){
System.out.println("有返回值,有参数的方法");
return "哈哈" + name;
}
//私有方法
private void method5(){
System.out.println("私有方法");
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
}
}
* 注: Class类型的唯一性
因为一个.class文件在内存里只生成一个Class对象,
所以无论那一种方法得到Class对象,得到的都是同一个对象。

* C.通过反射获取无参构造方法并使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。
其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:

1.返回一个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取public修饰, 指定参数类型所对应的构造方法

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定参数类型所对应的构造方法(包含私有的)


2.返回多个构造方法
public Constructor<?>[] getConstructors()
获取所有的public 修饰的构造方法

public Constructor<?>[] getDeclaredConstructors()
获取所有的构造方法(包含私有的)

3.运行构造方法创建实例
通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object... initargs)
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
                                                                                             void test()
* a. 得到构造方法
public Constructor<?>[] getConstructors()
获取所有的public 修饰的构造方法。

public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取public修饰, 指定参数类型所对应的构造方法。
不传参数得到无参构造方法。

* b. 运行构造方法
public T newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,
并用指定的初始化参数初始化该实例。
因为是无参构造,所以不传参数。

* c. 通过反射获取无参构造方法并使用的代码演示:
package cn.itcast.demo1;

import java.lang.reflect.Constructor;

"/*
* 通过反射获取class文件中的构造方法,运行构造方法
* 运行构造方法,创建对象
* 获取class文件对象
* 从class文件对象中,获取需要的成员
*
* Constructor 描述构造方法对象类
*/"
public class ReflectDemo1 {
public static void main(String[] args) throws Exception {

Class c = Class.forName("cn.itcast.demo1.Person");
//使用class文件对象,获取类中的构造方法
// Constructor[] getConstructors() 获取class文件对象中的所有公共的构造方法
/*Constructor[] cons = c.getConstructors();
for(Constructor con : cons){
System.out.println(con);
}*/
//获取指定的构造方法,空参数的构造方法
Constructor con = c.getConstructor();
//运行空参数构造方法,Constructor类方法 newInstance()运行获取到的构造方法
Object obj = con.newInstance();
System.out.println(obj.toString());
}
}

* D. 通过反射获取有参构造方法并使用

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
* a. 得到有参的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取public修饰, 指定参数类型所对应的构造方法。
传相应的参数类型得到有参构造方法。

* b. 运行构造方法
public T newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
因为是有参构造,所以传相应的参数值。

* c. 通过反射获取有参构造方法并使用的代码演示:
package cn.itcast.demo1;

import java.lang.reflect.Constructor;

/*
* 通过反射,获取有参数的构造方法并运行
* 方法getConstructor,传递可以构造方法相对应的参数列表即可
*/
public class ReflectDemo2 {
public static void main(String[] args)throws Exception {
Class c = Class.forName("cn.itcast.demo1.Person");
//获取带有,String和int参数的构造方法
//Constructor<T> getConstructor(Class<?>... parameterTypes)
//Class<?>... parameterTypes 传递要获取的构造方法的参数列表
Constructor con = c.getConstructor(String.class,int.class);
//运行构造方法
// T newInstance(Object... initargs)
//Object... initargs 运行构造方法后,传递的实际参数
Object obj = con.newInstance("张三",20);
System.out.println(obj);
}
}

* E. 通过反射获取有参构造方法并使用快捷方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
* a. 使用的前提
"类有空参的public构造方法。(如果是同包,默认权限也可以)"
* b. 使用的基础
Class类的 public T newInstance() 方法
创建此 Class 对象所表示的类的一个新实例。
* c. 通过反射获取有参构造方法并使用快捷方式的代码演示:
*
package cn.itcast.demo1;
/*
* 反射获取构造方法并运行,有快捷点的方式
* 有前提:
* 被反射的类,必须具有空参数构造方法
* 构造方法权限必须public
*/
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
Class c = Class.forName("cn.itcast.demo1.Person");
// Class类中定义方法, T newInstance() 直接创建被反射类的对象实例
Object obj = c.newInstance();
System.out.println(obj);
}
}

* F. 通过反射获取私有构造方法并使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。
"它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力"
对于公共成员、默认(打包)访问成员、受保护成员和私有成员,
在分别使用" Field "" Method ""Constructor "对象来设置或获取字段、调用方法,
或者创建和初始化类的新实例的时候,"会执行访问检查"

常用方法如下:
public void setAccessible(boolean flag) throws SecurityException
参数值为 true 则指示反射的对象在使用时应该【取消】 Java 语言访问检查。
参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。

获取私有构造方法,步骤如下:
1. 获取到Class对象
2. 获取指定的构造方法
3. 暴力访问, 通过setAccessible(boolean flag)方法
4. 通过构造方法类Constructor中的方法,创建对象
public T newInstance(Object... initargs)
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
* a. 得到私有的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定参数类型所对应的构造方法(包含私有的)
public Constructor<?>[] getDeclaredConstructors()
获取所有的构造方法(包含私有的)

* b. 运行私有构造方法
public void setAccessible(boolean flag)
将此对象的 accessible 标志设置为指示的布尔值。
设置为true,这个方法保证我们得到的私有构造方法的运行。(取消运行时期的权限检查。)
public T newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,
并用指定的初始化参数初始化该实例。

* c. 通过反射获取私有构造方法并使用的代码演示:
package cn.itcast.demo1;

import java.lang.reflect.Constructor;

/*
* 反射获取私有的构造方法运行
* 不推荐,破坏了程序的封装性,安全性
* 暴力反射
*/
public class ReflectDemo4 {
public static void main(String[] args) throws Exception{
Class c = Class.forName("cn.itcast.demo1.Person");
//Constructor[] getDeclaredConstructors()获取所有的构造方法,包括私有的
/*Constructor[] cons = c.getDeclaredConstructors();
for(Constructor con : cons){
System.out.println(con);
}*/
//Constructor getDeclaredConstructor(Class...c)获取到指定参数列表的构造方法
Constructor con = c.getDeclaredConstructor(int.class,String.class);

//Constructor类,父类AccessibleObject,定义方法setAccessible(boolean b)
con.setAccessible(true);

Object obj = con.newInstance(18,"lisi");
System.out.println(obj);
}
}
* 注:不推荐,破坏了程序的封装性,安全性。

* G. 反射获取成员变量并改值

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
* a. 获取成员变量
* 得到公共的成员变量
public Field getField(String name)
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。

public Field[] getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。


* 得到所有的成员变量(
包括私有的,如果要进行修改私有成员变量,要先进行public void setAccessible(boolean flag) 设置。)

public Field getDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。

public Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。

获取成员变量,步骤如下:
1. 获取Class对象
2. 获取构造方法
3. 通过构造方法,创建对象
4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)
5. 通过方法,给指定对象的指定成员变量赋值或者获取值
public void set(Object obj, Object value)
在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值
public Object get(Object obj)
返回指定对象obj中,此 Field 对象表示的成员变量的值



* b. 修改成员变量(Field)的值
* 修改公共的成员变量
public void set(Object obj, Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
obj指的是修改的是那个对象的这个成员变量值。


* c. 反射获取成员变量并改值的代码演示
package cn.itcast.demo1;
import java.lang.reflect.Field;
/*
* 反射获取成员变量,并修改值
* Person类中的成员String name
*/
public class ReflectDemo5 {
public static void main(String[] args) throws Exception{
Class c = Class.forName("cn.itcast.demo1.Person");
Object obj = c.newInstance();
//获取成员变量 Class类的方法 getFields() class文件中的所有公共的成员变量
//返回值是Field[] Field类描述成员变量对象的类
Field[] fields = c.getFields();
for(Field f : fields){
System.out.println(f);
}

//获取指定的成员变量 String name
//Class类的方法 Field getField(传递字符串类型的变量名) 获取指定的成员变量
Field field = c.getField("name");

//Field类的方法 void set(Object obj, Object value) ,修改成员变量的值
//Object obj 必须有对象的支持, Object value 修改后的值
field.set(obj,"王五");
System.out.println(obj);
//获得指定对象的成员变量的值
System.out.println(field.get(obj));

}
}

* H. 反射获取空参数成员方法并运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
 返回获取一个方法:
public Method getMethod(String name, Class<?>... parameterTypes)
获取public 修饰的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取任意的方法,包含私有的
参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型

 返回获取多个方法:
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)


获取成员方法,步骤如下:
1. 获取Class对象
2. 获取构造方法
3. 通过构造方法,创建对象
4. 获取指定的方法
5. 执行找到的方法
public Object invoke(Object obj, Object... args)
执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

.
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
* a. 获取空参数成员方法
* 得到公共的成员方法
public Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定public成员方法。

public Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class对象所表示的类或接口;
"(包括那些由该类或接口声明的以及"从超类和超接口继承"的那些的类或接口)"public member 方法。


* 得到全部的成员方法
* (包括私有的,如果要使用私有成员方法,要先进行public void setAccessible(boolean flag) 设置。)
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
public Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法;
包括公共public、保护protected、默认(包)访问和私有private方法,【但"不包括"继承的方法】。

* b. 使用Method方法对象
public Object invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
obj 指的是调这个方法的对象。
args 指的是调用这个方法所要用到的参数列表。
返回值Object就是方法的返回对象。如果方法没有返回值 ,返回的是null.
* c. 反射获取空参数成员方法并运行代码演示
package cn.itcast.demo1;

import java.lang.reflect.Method;

/*
* 反射获取成员方法并运行
* public void eat(){}
*/
public class ReflectDemo6 {
public static void main(String[] args) throws Exception{
Class c = Class.forName("cn.itcast.demo1.Person");
Object obj = c.newInstance();
//获取class对象中的成员方法
"// Method[] getMethods()获取的是class文件中的所有公共成员方法,"包括继承的""
// Method类是描述成员方法的对象
Method[] methods = c.getMethods();
for(Method m : methods){
System.out.println(m);
}

//获取指定的方法eat运行
// Method getMethod(String methodName,Class...c)
// methodName获取的方法名 c 方法的参数列表
Method method = c.getMethod("eat");
//使用Method类中的方法,运行获取到的方法eat
//Object invoke(Object obj, Object...o)
method.invoke(obj);
}
}

* I. 反射获取有参数成员方法并运行

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
* a. 获取有参数成员方法
* 得到公共的成员方法
public Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
public Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
* 得到全部的成员方法(包括私有的,如果要使用私有成员方法,要先进行public void setAccessible(boolean flag) 设置。)
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
public Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
* b. 使用Method方法对象
public Object invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
obj 指的是调这个方法的对象。
args 指的是调用这个方法所要用到的参数列表。
返回值Object就是方法的返回对象。如果方法没有返回值 ,返回的是null.
* c. 反射获取有参数成员方法并运行代码演示
package cn.itcast.demo1;
import java.lang.reflect.Method;

/*
* 反射获取有参数的成员方法并执行
* public void sleep(String,int,double){}
*/
public class ReflectDemo7 {
public static void main(String[] args) throws Exception{
Class c = Class.forName("cn.itcast.demo1.Person");
Object obj = c.newInstance();
//调用Class类的方法getMethod获取指定的方法sleep
Method method = c.getMethod("sleep", String.class,int.class,double.class);
//调用Method类的方法invoke运行sleep方法
method.invoke(obj, "休眠",100,888.99);
}
}

* J. 反射与泛型擦除

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
* a. 使用情况
* 我们可以通过"反射技术",来完成向"有泛型约束"的集合中,添加"任意类型"的元素:
例如:"在泛型为String的集合里,添加Integer的数据"
ArrayList<String> list = new ArrayList<String>();
list.add(100);

* b. 能用泛型擦除的理论
伪泛型:"在编译后的.class文件"里面是"没有""泛型约束"的,这种现象我们称为"泛型的擦除"

ArrayList<T>:ArrayList<String>或者ArrayList<Integer> "擦除后ArrayList的类型变成Object";

"泛型擦除后类型为变为 T 的父类"
如:ArrayList<T extends Comaparable> "擦除后ArrayList的类型变成Comaparable"

"用反射的方法绕过编译,得到Class文件对象,直接调用add方法"
即可向"有泛型约束"的集合中,添加"任意类型"的元素。

* c. 反射泛型擦除的代码演示
package cn.itcast.demo2;
import java.lang.reflect.Method;
import java.util.ArrayList;

/*
* 定义集合类,泛型String
* 要求向集合中添加Integer类型
*
* 反射方式,获取出集合ArrayList类的class文件对象
* 通过class文件对象,调用add方法
*
* 对反射调用方法是否理解
*/
public class ReflectTest {
public static void main(String[] args)throws Exception {
ArrayList<String> alist =new ArrayList<>();
alist.add("Hust");
alist.add("哈喽");
//反射方式,获取出集合ArrayList类的class文件对象
Class c=alist.getClass();
Method method = c.getMethod("add", Object.class);
//使用invoke运行ArrayList方法add
method.invoke(alist,10909092);
method.invoke(alist,new Person("李强",21));
method.invoke(alist,123.67676);
System.out.println(alist);
//获取alist中的Person对象
Object oj =alist.get(3);
Person p = (Person) oj;
System.out.println(p.name +">>>"+ p.age);



}
}

* K. 反射通过配置文件来决定运行的步骤

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
* a. 操作依据
通过配置文件得到类名和要运行的方法名,用反射的操作类名得到对象和调用方法
* b. 实现步骤:
* 1. 准备配置文件,键值对
* 2. IO流读取配置文件 Reader
* 3. 文件中的键值对存储到集合中 Properties
* 集合保存的键值对,就是类名和方法名
* 4. 反射获取指定类的class文件对象
* 5. class文件对象,获取指定的方法
* 6. 运行方法
* c. 代码演示
代码:
package cn.itcast.demo3;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

"/*
* 调用Person方法,调用Student方法,调用Worker方法
* 类不清楚,方法也不清楚
* 通过配置文件实现此功能
* 运行的类名和方法名字,以键值对的形式,写在文本中
* 运行哪个类,读取配置文件即可
* 实现步骤:
* 1. 准备配置文件,键值对
* 2. IO流读取配置文件 Reader
* 3. 文件中的键值对存储到集合中 Properties
* 集合保存的键值对,就是类名和方法名
* 4. 反射获取指定类的class文件对象
* 5. class文件对象,获取指定的方法
* 6. 运行方法
*/"
public class Test {
public static void main(String[] args) throws Exception{
//IO流读取配置文件
FileReader r = new FileReader("config.properties");
//创建集合对象
Properties pro = new Properties();
//调用集合方法load,传递流对象
pro.load(r);
r.close();
//通过键获取值
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//反射获取指定类的class文件对象
Class c = Class.forName(className);
Object obj = c.newInstance();
//获取指定的方法名
Method method = c.getMethod(methodName);
method.invoke(obj);
}
}

config.properties配置文件:
#className=cn.itcast.demo3.Student
#methodName=study
className=cn.itcast.demo3.Person
methodName=eat
#className=cn.itcast.demo3.Worker
#methodName=job

总结

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
如何获取.Class文件对象
1, 通过"Object类 getClass()方法"获取 Class对象
2, 通过"类名.class 方式" 获取 Class对象
3, 通过反射的方式, " Class.forName(String classname) 获取Class对象"
public static Class<?> forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象



通过反射, 获取类中的构造方法,并完成对象的创建
获取指定的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取指定的public修饰的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定的构造方法,包含私有的
获取所有的构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法,包含私有的



通过反射, 获取类中的构造方法,并完成对象的创建
步骤:
1,获取字节码文件对象
2,通过字节码文件对象 ,获取到指定的构造方法 getConstructor(参数);
3,通过构造方法,创建对象 public T newInstance(Object... initargs)



私有构造方法,创建对象
1,获取字节码文件对象
2,通过字节码文件对象 ,获取到指定的构造方法
getDeclaredConstructor (参数);
3,暴力访问
con.setAccessible(true);
4,通过构造方法,创建对象
public T newInstance(Object... initargs)



通过反射,获取Class文件中的方法
获取指定的方法
public Method getMethod(String name, Class<?>... parameterTypes)
获取指定的public方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取指定的任意方法,包含私有的
获取所有的方法
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的



通过反射,调用方法
步骤:
1,获取Class对象
2,获取构造方法,创建对象
3,获取指定的public方法
4,执行方法
public Object invoke(Object obj, Object... args)



私有方法的调用:
1,获取Class对象
2,获取构造方法,创建对象
3,获取指定的private方法
4,开启暴力访问
m5.setAccessible(true);
5,执行方法
public Object invoke(Object obj, Object... args)



通过反射,获取成员变量(字段)
获取指定的成员变量
public Field getField(String name) 获取public修饰的成员变量
public Field getDeclaredField(String name) 获取任意的成员变量,包含私有
获取所有的成员变量
public Field[] getFields() 获取所有public修饰的成员变量
public Field[] getDeclaredFields() 获取司所有的成员变量,包含私有



通过反射,获取成员 变量,并赋值使用
步骤:
1,获取字节码文件对象
2,获取构造方法,创建对象
3,获取指定的成员变量
4,对成员变量赋值\获取值操作
public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值



私有成员变量的使用
步骤:
1,获取字节码文件对象
2,获取构造方法,创建对象
3,获取指定的成员变量
4,开启暴力访问
5,对成员变量赋值\获取值操作
public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值
-------------本文结束感谢您的阅读-------------
感谢您的支持,我会继续努力的!
0%