1.引进反射
1.1 Java文件和.class文件的关系
Java文件
Java文件中包含代码的多有内容,如类、接口、成员变量、成员方法
.class文件
.java文件通过 javac编译工具生成对应的.class字节码文件
使用JDK中提供的反编译工具,可以看到.class文件中包含有:
Class 完整的包名.类名
Field 成员变量,成员变量的名字和成员变量的数据类型(如果为引用数据类型,也是完整的包名.类名)
Method 成员方法,方法权限修饰符,返回值类型,方法名,形参列表数据类型
总结
.class字节码文件中,包含了Java文件的所有内容。
1.2 程序加载过程和.class文件的关系
在Java文件运行过程中,当前程序需要哪一个类【参与代码执行】,那么就需要【加载这个类】的.class字节码文件。
.class字节码文件在程序的加载阶段,存在于内存的【代码区】。
代码区存在一块空间==> .class ==> Java程序的所有内容
1.3 Java中的万物皆对象
在Java代码中,把在【内存代码区】保存的【.class字节码内存空间】,看作是一个对象。而该对象中包含了对应的Java文件的所有内容。
图示:
1.4 Class到底是什么?
用例子说明:
class Person {
int age;
String name;
public Person() {}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public void test() {
sout("方法");
}
}
class Dog {
String name;
char gender;
public Dog() {}
public Dog(String name, char gender) {
this.name = name;
this.gender = gender;
}
public void eat() {
sout("狗狗吃肉");
}
}
1.5Class类对象
(1)【Class类对象】是自定义类(如Person、Dog类)生成的.class文件所占的【内存代码块空间】,而不是具体的类(如Person)。
(2)自定义类是一个数据类型
(3)【反射】是将该数据类型占用的内存代码块空间看作是一个Class对象。
2.反射理解
反射主要是指程序可以【访问】、【检测】和【修改】它本身【状态】或【行为】的一种能力。
某种程度上类似于光的反射,比如照镜子,人可以通反射看到自己的全身上下,但也仅能看。而Java中的反射又有所不同,它们不仅能【自描述】,还可以【自控制】。即反射不单单指反射本身,还包括对反射结果所采取的措施。
也就是说,这类反射应用通过采用某种机制来实现对自己行为的描述和监测,并能根据自身行为的状态进行调整或修改。
3.反射重要的方法【重点】
3.1 Class涉及到的方法
(1)Class Class.forName(String packageNameAndClassName);
Class类的静态成员方法,通过【完整的包名.类名】获取对应的.class 文件的Class对象(该对象即为.class文件所占的内存代码空间)
因为只要自定义类【参与】代码的执行,则该类文件就会为加载。因此,该方法也可作为.class文件加载的方法。
(2)Class 类名.class;
通过类名.class 方法,获取对应的Class类对象,通常用于方法的参数类型。
(3)Class 类对象.getClass();
通过类对象获取对应.class文件的Class类对象、方法参数、或者说数据类型判断。
以上方法的代码演示
(1)Class<?> forName = Class.forName("com.qfedu.a_reflect.Person");
(2)Class<com.qfedu.a_reflect.Person> cls = Person.class;
(3)Class<? extends Person> class1 = new Person().getClass();
注意:
- 上述三种方式所得的Class对象是同一个Class对象
- Class对象对应的是在内存代码区的.class文件占用的内存空间
- Class引用数据类型变量保存的就是当前空间首地址
- Java程序中,.class字节码文件有且之加载一次
- .class文件占用的空间独一份,不管通过哪一种方式获取对应的Class类对象都是同一个。
3.2 Constructor(构造方法类)涉及到的方法
(1) public Constructor[] getConstructors();
获取当前Class类对象对应Java文件中,所有【public】修饰的【构造方法】的类对象数组
(2) public Constructor[] getDeclaredConstructors();
【暴力反射】
获取当前Class类对象对应Java文件中,所有【构造方法】的类对象数组,包括【private】修饰的构造方法。
(3) public Constructor getConstructor(Class... initArgumentTypes);
(a) Class :【约束数据类型】,当前方法所需的参数类型
如:需要int类型,int.class
需要String类型, String.class
(b) ... :【不定长参数】。构造方法需要的参数类型有很多,有的无参数,有的有参数但参数个数不一致。因此用“...”概括所有,增强代码的普适性。
(c) initArgumentTypes : 【参数名】,初始化参数类型复数
(d)根据指定的数据类型,来选择对应的构造方法,这里可能会抛出异常。
(e)这里有且只能获取类内的【public】修饰的【指定数据类型】的构造方法类对象。
(4) public Constructor getDeclaredConstructor(Class... initArgumentTypes);
【暴力反射】
(a)根据指定的数据类型,选择对应的构造方法,可能会抛出异常。
(b)这里可以获取指定参数类型的所有构造方法,包括【private】修饰的构造方法。
(5) Object newInstance(Object... initArguments);
(a)通过Constructor对象来调用,传入当前构造方法所需创建对象的初始化参数,创建对象。
(b) Object类是Java中所有类的基类,这里可以传入任意类型的参数
(c) ... :不定长参数,因为Constructor类对象在获取的过程中,约束的参数个数都不确定。
以上方法的代码演示
【注意】以上方法所需抛出的异常较多:
public static void main(String[] args)
throws ClassNotFoundException,
NoSuchMethodException, SecurityException,
InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException{}
获取Class对象,方便下列方法使用
//根据指定的包名.类名,获取对应的Class对象
Class<?> cls = Class.forName("com.a_reflect.Person");
(1)//获取当前Person类内所有非【private】构造方法
Constructor<?>[] constructors = cls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
(2)//暴力反射,获取Person类内所有的构造方法,包括【private】构造方法
Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
System.out.println(constructor);
}
(3)//根据指定参数类型获取【public】构造方法对象
//如果没有指定参数的构造方法,运行异常java.lang.NoSuchMethodException
Constructor<?> constructor1 = cls.getConstructor();
Constructor<?> constructor2 = cls.getConstructor(int.class);
Constructor<?> constructor3 = cls.getConstructor(int.class, String.class);
System.out.println(constructor1);
System.out.println(constructor2);
System.out.println(constructor3);
(4)//通过暴力反射可以获取任意权限修饰符,符合参数要求的构造方法对象
Constructor<?> declaredConstructor1 = cls.getDeclaredConstructor();
Constructor<?> declaredConstructor2 = cls.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor1);
System.out.println(declaredConstructor2);
(5)a.
//通过无参数Constructor对象执行newInstance方法
//这里明确是一个Person类型,可以使用强制类型转换
//这里使用的是public修饰的构造方法
Person p1 = (Person) constructor1.newInstance();
System.out.println(p1);
System.out.println(new Person());
System.out.println(constructor3.newInstance(1, "骚磊"));
b.
// 给予通过暴力反射获取到的非公开权限成员变量,成员方法,构造方法,操作权限
// 暴力反射的为所欲为操作
declaredConstructor2.setAccessible(true);
Person p2 = (Person) declaredConstructor2.newInstance("骚磊");
System.out.println(p2);