Class 介绍
# Class 介绍
Class 是反射的基础
# 获取 Class 对象的方式
要用反射来操作某个类的变量或方法,首先要获取该类的 Class 对象。
一共 3 种方式,对应 3 个阶段.
Class.forName("全类名")
:将字节码文件加载进内存,返回 Class 对象。因为在第一阶段,只有字节码文件,需要手动加载进内存。多用于配置文件,将类名定义在配置文件中。读取文件,加载类。(全类名:包名.类名)类名.class
:通过类名的属性 class 获取。 在 Class 类对象阶段,在第二阶段,已经在内存了,直接获取就可以了。多用于参数的传递对象.getClass()
:getClass()
方法在 Object 类中定义着,所有类都继承了 Object。在 Runtime 运行时阶段,第三阶段。多用于对象的获取字节码的方式,即已经有了对象了,直接获取。
以下代码的结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的 Class 对象都是同一个。不同的字节码文件(也就是不同的类),则当然会不同,也就不会相等。
/**
获取Class对象的方式:
1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
2. 类名.class:通过类名的属性class获取
3. 对象.getClass():getClass()方法在Object类中定义着。
*/
package com.peterjxl.reflect;
public class ReflectDemo1GetClassObject{
public static void main(String[] args) throws Exception {
Person person = new Person();
//1.Class.forName("全类名") 静态方法
Class class1 = Class.forName("com.peterjxl.reflect.Person");
//2.类名.class
Class class2 = Person.class;
//3.对象.getClass()
Class class3 = person.getClass();
System.out.println("class1: "+ class1); //class com.peterjxl.reflect.Person
System.out.println("class2: "+ class2); //class com.peterjxl.reflect.Person
System.out.println("class3: "+ class3); //class com.peterjxl.reflect.Person
System.out.println("class1 == class2: " + (class1 == class2)); //true
System.out.println("class2 == class3: " + (class2 == class3)); //true
Class classStu = Student.class;
System.out.println("classStu == class1: " + (classStu == class1)); //false
}
}
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
package com.peterjxl.reflect;
public class Person {
}
2
3
4
5
package com.peterjxl.reflect;
public class Student {
}
2
3
4
5
目录结构:
当前路径:D:\Projects\LearnJava\02.JavaSenior\01.Reflect
目录结构:
01.Reflect
└── com
└── peterjxl
└── reflect
├── Person.java
├── ReflectDemo1GetClassObject.java
└── Student.java
2
3
4
5
6
7
8
9
编译和运行:
> javac -d ./bin ./com/peterjxl/reflect/*.java -encoding utf8
cd ./bin
> java -cp . com.peterjxl.reflect.ReflectDemo1GetClassObject
class1: class com.peterjxl.reflect.Person
class2: class com.peterjxl.reflect.Person
class3: class com.peterjxl.reflect.Person
class1 == class2: true
class2 == class3: true
classStu == class1: false
2
3
4
5
6
7
8
9
10
注意:因为有可能有必检异常,因此必须对异常进行处理(我这里在 main 方法里抛出了),不然会报错:
错误: 未报告的异常错误ClassNotFoundException; 必须对其进行捕获或声明以便抛出
# Class 类的常用方法
查看 Class 类的文档,该类里面大部分都是 get 开头的方法。
获取成员变量们
Field[] getFields()
:获取所有 public 修饰的成员变量,包括父类Field getField(String name)
: 获取指定名称的 public 修饰的成员变量(不包括父类)Field[] getDeclaredFields()
: 获取所有的成员变量,不考虑修饰符。经简单测试,Filed 数组里的顺序和类里面成员定义的顺序是一致的。Field getDeclaredField(String name)
:获取指定名称的成员变量,不考虑修饰符(不包括父类)
获取构造方法们
Constructor<?>[] getConstructors()
:获取所有 public 修饰的构造方法Constructor<T> getConstructor(类<?>... parameterTypes)
:获取指定的、public 修饰的构造方法Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
:获取所有的的构造方法,不考虑修饰符Constructor<?>[] getDeclaredConstructors()
获取指定的构造方法,不考虑修饰符
获取成员方法们:
Method[] getMethods()
:获取所有 public 修饰的成员方法Method getMethod(String name, 类<?>... parameterTypes)
获取指定的、public 修饰的成员方法Method[] getDeclaredMethods()
:获取所有成员方法Method getDeclaredMethod(String name, 类<?>... parameterTypes)
获取指定的的成员方法,不考虑修饰符
获取全类名和包名
String getName()
Package getPackage()
Class getSuperclass()
: 获取父类的 Class,Object
的父类是null
Class[] getInterfaces()
:查询到实现的接口类型。只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型。如果没有实现任何interface
,返回空数组。
如果父类的属性用
protected
修饰,利用反射是无法获取到的。protected 修饰符的作用范围:只允许
同一个包下
或者子类
访问,可以继承到子类。
getFields()
只能获取到本类的public
属性的变量值;
getDeclaredFields()
只能获取到本类的所有属性,不包括父类的;所以,无论如何都获取不到父类的 protected 属性修饰的变量,但是它的的确确存在于子类中。
# 实践 Field 成员变量
Field 对象常用方法:
- 设置值:void set(Object obj, Object value)
- 获取值:get(Object obj)
- 忽略访问权限修饰符的安全检查:setAccessible(true) ,也叫暴力反射
getName()
:返回字段名称,例如,"name"
;getType()
:返回字段类型,也是一个Class
实例,例如,String.class
;getModifiers()
:返回字段的修饰符,它是一个int
,不同的 bit 表示不同的含义。
以 String
类的 value
字段为例,它的定义是:
public final class String {
private final byte[] value;
}
2
3
我们用反射获取该字段的信息,代码如下:
Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false
2
3
4
5
6
7
8
9
我们实践下,定义一个 Person 类先:
package com.peterjxl.reflect;
public class Person {
public String pubName;
protected String proName;
String defName;
private String priName;
public Person(){
}
public Person(String pubName){
this.pubName = pubName;
}
public void eat(){
System.out.println("Person eating.....");
}
@Override
public String toString(){
return "pubName: " + this.pubName;
}
public void setPriName(String priName) {
this.priName = priName;
}
}
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
/*
演示Class获取成员变量
1.Field[] getFields():获取所有public修饰的成员变量
2.Field getField(String name): 获取指定名称的 public修饰的成员变量
3.Field[] getDeclaredFields() : 获取所有的成员变量,不考虑修饰符
4.Field getDeclaredField(String name):获取指定名称的成员变量,不考虑修饰符
*/
package com.peterjxl.reflect;
import java.lang.reflect.Field;
public class ReflectDemo2GetMember{
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
//1.getFields():获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println(field); //public java.lang.String com.peterjxl.reflect.Person.pubName
}
//2.getField(String name): 获取指定名称的 public修饰的成员变量
Field pubNameField = personClass.getField("pubName");
System.out.println("pubNameField: " + pubNameField); //pubNameField: public java.lang.String com.peterjxl.reflect.Person.pubName
Person zhangsan = new Person("zhangsan");
//获取成员变量 pubName 的值
Object value = pubNameField.get(zhangsan);
System.out.println("zhangsan value = " + value); //zhangsan value = zhangsan
// 设置成员变量 pubName 的值
pubNameField.set(zhangsan, "lisi");
System.out.println("After set zhangsan value: " + zhangsan); //After set zhangsan value: pubName: lisi
//3.getDeclaredFields() : 获取所有的成员变量,不考虑修饰符
Field[] declarFields = personClass.getDeclaredFields();
for (Field declarField : declarFields) {
System.out.println(declarField);
}
/*
public java.lang.String com.peterjxl.reflect.Person.pubName
protected java.lang.String com.peterjxl.reflect.Person.proName
java.lang.String com.peterjxl.reflect.Person.defName
private java.lang.String com.peterjxl.reflect.Person.priName
*/
//4.getDeclaredField(String name):获取指定名称的成员变量,不考虑修饰符
Person wangwu = new Person();
wangwu.setPriName("wangwu");
Field priNameField = personClass.getDeclaredField("priName");
System.out.println("priNameField: " + priNameField); // priNameField: private java.lang.String com.peterjxl.reflect.Person.priName
//忽略访问权限修饰符的安全检查 因为priName是Person的私有变量,默认是不允许类外部访问和设置,但可以通过设置强制获取。
priNameField.setAccessible(true);
System.out.println(priNameField.get(wangwu)); //wangwu
priNameField.set(wangwu, "wangwu2");
System.out.println("After set wangwu priName:" + priNameField.get(wangwu)); //After set wangwu priName:wangwu2
}
}
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
输出举例:
public java.lang.String com.peterjxl.reflect.Person.pubName
pubNameField: public java.lang.String com.peterjxl.reflect.Person.pubName
zhangsan value = zhangsan
After set zhangsan value: pubName: lisi
public java.lang.String com.peterjxl.reflect.Person.pubName
protected java.lang.String com.peterjxl.reflect.Person.proName
java.lang.String com.peterjxl.reflect.Person.defName
private java.lang.String com.peterjxl.reflect.Person.priName
priNameField: private java.lang.String com.peterjxl.reflect.Person.priName
wangwu
After set wangwu priName:wangwu2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
如果没有忽略访问权限修饰符的安全检查,会抛出异常 IllegalAccessException:
//Field getDeclaredField(String name)
Person wangwu = new Person("wangwu");
Field priNameField = personClass.getDeclaredField("priName");
System.out.println(priNameField.get(wangwu));
2
3
4
Exception in thread "main" java.lang.IllegalAccessException: Class com.peterjxl.reflect.ReflectDemo2GetMember can not access a member of class com.peterjxl.reflect.Person with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Field.get(Field.java:390)
at com.peterjxl.reflect.ReflectDemo2GetMember.main(ReflectDemo2GetMember.java:64)
2
3
4
5
6
# 实践 Constructor 构造方法
由于构造方法的方法名全部一样,所以区分方法要看参数列表。
我们可以用它来创建对象:
- T newInstance(Object... initargs)
- 如果使用空参数构造方法创建对象,操作可以简化:Class 对象的 newInstance 方法
- 如果要调用私有的构造器,也需要使用暴力反射
setAccessible(true);
/**
获取构造方法们
1. Constructor<?>[] getConstructors() 获取所有public修饰的构造方法
2. Constructor<T> getConstructor(类<?>... parameterTypes) 获取指定的、public修饰的构造方法
3. Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) 获取所有的的构造方法,不考虑修饰符
4. Constructor<?>[] getDeclaredConstructors() 获取指定的构造方法,不考虑修饰符
3和4比较少见,这里不演示
*/
package com.peterjxl.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectDemo3GetConstructor {
public static void main(String[] args) throws Exception {
// 0.获取Person的Class对象
Class personClass = Person.class;
// 1. Constructor<?>[] getConstructors() 获取所有public修饰的构造方法
Constructor[] constructors = personClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
// 2. 获取指定的、public修饰的构造方法
Constructor constructor = personClass.getConstructor(String.class);
Constructor constructor2 = personClass.getConstructor(); //获取空参构造函数
System.out.println("constructor: " + constructor);
// 创建对象newInstance
Object person = constructor.newInstance("爱莉希雅");
System.out.println(person);
// 调用Class对象的newInstance方法,简化对象的创建
Object person2 = personClass.newInstance();
System.out.println(person2);
}
}
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
输出:
public com.peterjxl.reflect.Person()
public com.peterjxl.reflect.Person(java.lang.String)
constructor: public com.peterjxl.reflect.Person(java.lang.String)
pubName: 爱莉希雅
pubName: null
2
3
4
5
6
7
8
# 实践 Methods
Method:方法对象
- 获取方法:需要方法名,参数列表
- 执行方法:
Object invoke(Object obj, Object... args)
,args 是方法的参数列表。如果是静态方法,obj 传入 null 即可。 - 可以使用暴力反射
setAccessible(true);
其他常用:
getName()
:返回方法名称,例如:"getScore"
;getReturnType()
:返回方法返回值类型,也是一个 Class 实例,例如:String.class
;getParameterTypes()
:返回方法的参数类型,是一个 Class 数组,例如:{String.class, int.class}
;getModifiers()
:返回方法的修饰符,它是一个int
,不同的 bit 表示不同的含义。
/**
获取成员方法们:
1.Method[] getMethods() 获取所有public修饰的成员方法
2.Method getMethod(String name, 类<?>... parameterTypes) 获取指定的、public修饰的成员方法
3.Method[] getDeclaredMethods() 获取所有成员方法
4.Method getDeclaredMethod(String name, 类<?>... parameterTypes) 获取指定的的成员方法,不考虑修饰符
*/
package com.peterjxl.reflect;
import java.lang.reflect.Method;
public class ReflectDemo4GetMethods {
public static void main(String[] args) throws Exception{
//0.获取Person的Class对象
Class personClass = Person.class;
// 1. getMethods() 获取所有public修饰的成员方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println("method.getName(): " + method.getName());
System.out.println(method);
}
// 2.getMethod(String name, ... parameterTypes) 获取指定的、public修饰的成员方法
Person person = new Person();
Method eat_method = personClass.getMethod("eat", String.class);
// 执行该方法
eat_method.invoke(person, "fries");
}
}
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
编译和运行:
method.getName(): toString
public java.lang.String com.peterjxl.reflect.Person.toString()
method.getName(): setPriName
public void com.peterjxl.reflect.Person.setPriName(java.lang.String)method.getName(): eat
public void com.peterjxl.reflect.Person.eat()
method.getName(): eat
public void com.peterjxl.reflect.Person.eat(java.lang.String)
method.getName(): wait
public final void java.lang.Object.wait() throws java.lang.InterruptedException
method.getName(): wait
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
method.getName(): hashCode
public native int java.lang.Object.hashCode()
method.getName(): getClass
public final native java.lang.Class java.lang.Object.getClass()
method.getName(): notify
public final native void java.lang.Object.notify()
method.getName(): notifyAll
public final native void java.lang.Object.notifyAll()
Person eating.....fries
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 实践 类名和包名
/*
获取全类名和包名
String getName()
String getPackage()
*/
package com.peterjxl.reflect;
public class ReflectDemo5GetName {
public static void main(String[] args) {
Class personClass = Person.class;
System.out.println("getName(): " + personClass.getName()); //getName(): com.peterjxl.reflect.Person
System.out.println("getPackage(): " + personClass.getPackage()); //getPackage(): package com.peterjxl.reflect
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 实践 小型 Spring
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
改代码的弊端:修改了代码后,需要重新测试,部署上线
实现方法:配置文件 + 反射
步骤:
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 创建对象
- 执行方法
新建 pro.properties 文件
className=cn.itcast.domain.Student
methodName=sleep
2
package com.peterjxl.reflect;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectSpring {
public static void main(String[] args) throws Exception {
// 1.加载配置文件
// 1.1创建Properties对象
Properties pro = new Properties();
// 1.2加载配置文件,转换为一个集合
// 1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectSpring.class.getClassLoader(); // 首先获取类加载器
InputStream is = classLoader.getResourceAsStream("pro.properties"); // 然后通过类加载器,获取文件的字节流
pro.load(is);
// 2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
// 3.加载该类进内存
Class cls = Class.forName(className);
// 4.创建对象
Object obj = cls.newInstance();
// 5.获取方法对象
Method method = cls.getMethod(methodName);
// 6.执行方法
method.invoke(obj);
}
}
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
如果是在命令行下运行:需要将配置文件挪到执行命令的地方,目录结构如下:
bin
├── com
│ └── peterjxl
│ └── reflect
│ ├── Person.class
│ ├── ReflectDemo1GetClassObject.class
│ ├── ReflectDemo2GetMember.class
│ ├── ReflectDemo3GetConstructor.class
│ ├── ReflectDemo4GetMethods.class
│ ├── ReflectDemo5GetName.class
│ ├── ReflectSpring.class
│ └── Student.class
└── pro.properties
2
3
4
5
6
7
8
9
10
11
12
13
运行结果
java -cp . com.peterjxl.reflect.ReflectSpring
Person eating.....
2
其他加载文件的方式:
package com.peterjxl.reflect;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectDemo6Spring {
public static void main(String[] args) throws Exception {
//1.加载配置文件
Properties properties = new Properties();
try (FileInputStream fis = new FileInputStream("pro.properties")) {
properties.load(fis);
}catch (Exception e){
e.printStackTrace();
}
// 2.执行,和上述例子一样
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
Class cls = Class.forName(className);
Object obj = cls.newInstance();
Method method = cls.getMethod(methodName);
method.invoke(obj);
}
}
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
# 其他注意事项
- 使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)
setAccessible(true)
可能会失败。如果 JVM 运行期存在SecurityManager
,那么它会根据规则进行检查,有可能阻止setAccessible(true)
。例如,某个SecurityManager
可能不允许对java
和javax
开头的package
的类调用setAccessible(true)
,这样可以保证 JVM 核心库的安全。