反射
目录
反射
1. 反射知识点前期准备
1.1 Java文件和对应代码内容关系
按照正常的代码开发要求,一个 .Java 文件对应一个 class or interface。当前 Java 文件包含文件中对应 class or interface 接口的所有内容。.java包含以下内容
class className 类名 {
Field 成员变量
Method 成员方法
Constructor 构造方法
}
tips:
1. 构造代码块在编译之后,会将所有代码内容,给类内的每一个构造方法人手一份,所以不作为类内容分析
2. 静态代码块严格来说和当前类作为数据类型描述内容无关,仅作为类加载阶段初始化执行内容,有且只执行一
次。
interface 接口名 {
Field 成员变量
Method 成员方法
}
1.2 .class 字节码文件和.java文件的关系
.java ==> javac 编译 ==> .class
.java 文件中所有和代码执行相关的内容,都会通过该 javac 编译器 操作之后,转换为 .class 字节码文件。.class字节码文件包含 .java 文件的所有参与代码执行内容。
.class 字节码文件中包含以下内容:
class className 类名 {
Field 成员变量
Method 成员方法
Constructor 构造方法
}
tips:
1. 构造代码块在编译之后,会将所有代码内容,给类内的每一个构造方法人手一份,所以不作为类内容分析
2. 静态代码块严格来说和当前类作为数据类型描述内容无关,仅作为类加载阶段初始化执行内容,有且只执行一
次。
interface 接口名 {
Field 成员变量
Method 成员方法
}
1.3 类加载过程
类加载过程,是将 .class 字节码文件加载到执行任务的内容中,加载的内存区域是内存的【方法区】。
.class字节码文件在内存方法区占用的内存空间,是对应整个 Java 程序所有内容,包含
class className 类名 {
Field 成员变量
Method 成员方法
Constructor 构造方法
}
tips:
1. 构造代码块在编译之后,会将所有代码内容,给类内的每一个构造方法人手一份,所以不作为类内容分析
2. 静态代码块严格来说和当前类作为数据类型描述内容无关,仅作为类加载阶段初始化执行内容,有且只执行一
次。
interface 接口名 {
Field 成员变量
Method 成员方法
}
1.4 万物皆对象
1. 你觉得 Java 中定义类的格式一样吗???
是一致的!!!
都有 类名,可以选择 成员变量,成员方法,构造方法。
2. 是否可以将 Java 中定义类的格式看作一个模版???把 类 看作是一个 【类】???
Java定义类是一个规范的格式,从宏观角度来看,Java中定义类格式可以认为是一个类
【Class 类】 描述Java中定义类的模型/模式/模版
定义了类必要的格式
必须有类名/接口名
可选内容
成员变量
成员方法
构造方法
3. 成员变量是否可以看作是一个类???
修饰符(权限,静态,final) 数据类型 成员变量名 = 数据存储;
是否可以看做是一个模版???是否可以看做是一个类???
定义成员变量都是固定格式,成员变量可以看做是一个类 【Field 类】
4. 成员方法是否可以看作是一个类???
权限修饰符 是否静态修饰符 返回值类型 方法名(形式参数列表) {
方法体;
}
Modifiers returnType methodName(parameterTypes);
声明成员方法格式都是固定,成员方法可以看做是一个类【Method 类】
5. 构造方法是否可以看作是一个类???
权限修饰符 类名(形式参数列表) {
}
Modifiers className(parameterTypes);
构造方法格式都是固定,构造方法可以看做是一个类【Constructor 类】
6. Class 是 Java 中所有类的模版,其中的成员变量是 Field ,Method ,Constructor 对象
类 {
Field
Method
Constructor
}
2. 万恶之源 Class 对象获取
2.1 Class 对象是什么
Class 对象是 Class 类对应对象,所对应的内容是加载到内存【方法区】占用内存空间。
2.2 方法
static Class forName(String packageAndClassName);
静态成员方法,通过 Class 直接调用,所需参数是对应类完整的包名.类名,通过包名.类名获取对应类 Class 对象。
Class getClass();
Object类内方法,任何一个对象都可以通过 getClass() 方法获取对应类型的 Class 对象
类名.class
属性获取,获取当前类对应的 Class 对象
package com.qfedu.a_reflect;
/*
* 获取 Class 对象相关操作
*/
public class GetClassObject {
public static void main(String[] args) throws ClassNotFoundException {
/*
* Class.forName()
*
* 动态!!!只要用户提供完整的包名.类名,我可以获取到任意 Class 对象
* 【工厂模式】
*/
Class<?> cls1 = Class.forName("com.qfedu.a_reflect.Person");
/*
* 通过实例化 Person 对象,利用 getClass() 方法获取 Class 对象
*
* 用于类型判断,已知实例化对象情况下,获取对应类型。
*/
Class<? extends Person> cls2 = new Person().getClass();
/*
* 通过 类名获取 Class 对象属性
*
* 常用于类型约束,类型表示,方法参数类型约束,成员变量数据类型描述,返回值类型数据类型描述
*/
Class<Person> cls3 = Person.class;
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
/*
* 以上三种方式获取的 Class 对象是不是同一个对象???
* 不管通过那种方式获取 Class 对象都是同一个 Class 对象,因为 com.qfedu.a_reflect.Person
* 类型在整个代码中有且只有一个。
*/
System.out.println(cls1 == cls2);
System.out.println(cls2 == cls3);
System.out.println(cls3 == cls1);
}
}
3. 通过Class 对象获取对应类 Constructor 对象
3.1 Constructor 构造方法关注的内容
格式:
权限修饰符 类名(形式参数列表) {
方法体;
}
public Person() {}
public Person(String name) {this.name = name}
对应当前 Person 类而言,选择利用不同的构造方法 + new 关键字实例化对象,你认为具有唯一性的数据是哪一个???
调用:
Person p1 = new Person();
Person p2 = new Person("张三");
如果可以得到 Class 对象,证明已经明确当前对应类型是哪一个类,需要获取构造方法,有且只有构造方法形式参数列表不同。可以通过不同的形式参数列表,获取对应的构造方法 Constructor 对象。
3.2 获取 Constructor 对象相关操作方法
Constructor[] getConstructors();
通过 Class 对象调用,获取对应类型所有非私有化构造方法 Constructor 类对象数组
Constructor[] getDeclaredConstructors();
【暴力反射】
通过 Class 对象调用,获取对应类型所有构造方法 Constructor 类对象数组,包括私有化构造方法
Constructor getConstructor(Class... parameterTypes);
通过 Class 对象调用,获取指定参数类型的非私有化构造方法。
Class... parameterTypes 不定长参数 Class 类型,使用 Class 类型来约束构造方法参数类型。
案例:
Class<Person> cls = Person.class;
// 获取 Person 无参数构造方法
Constructor c1 = cls.getConstructor();
==> public Person();
// 获取 Person 类型 int, String 参数构造方法
Constructor c1 = cls.getConstructor(int.class, String.class);
==> public Person(int, String);
Constructor getDeclaredConstructor(Class... parameterTypes);
【暴力反射】
通过 Class 对象调用,获取指定参数类型的构造方法。包括私有化构造方法
Class... parameterTypes 不定长参数 Class 类型,使用 Class 类型来约束构造方法参数类型。
案例:
Class<Person> cls = Person.class;
// 获取 Person 私有化 String 类型构造方法
Constructor c1 = cls.getDeclaredConstructor(String.class);
==> private Person(String)
3.4 通过 Constructor 对象实例化对应类对象
Object newInstance(Object... parameterValues);
parameterValues 要求数据类型为 Object 类型,且参数个数不限制。不定长 Object 类型参数
通过 Constructor 对象调用,并且通过 newInstance 参数列表提供 实例化对象所需的必要参数,得到对应类型对象。
3.4 代码演示
package com.qfedu.a_reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* 获取指定类构造方法 Constructor 对象
*/
public class GetConstructorObject {
public static void main(String[] args)
throws ClassNotFoundException, NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
/*
* Class.forName()
*
* 动态!!!只要用户提供完整的包名.类名,我可以获取到任意 Class 对象
* 【工厂模式】
*/
Class<?> cls1 = Class.forName("com.qfedu.a_reflect.Person");
/*
* Constructor[] getConstructors();
* 获取指定类所有构造方法对象数组,不可以获取私有化构造方法
*/
Constructor<?>[] constructors = cls1.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println();
/*
* Constructor[] getDeclaredConstructors();
* 获取指定类所有构造方法对象数组,包括私有化构造方法【暴力反射】
*/
Constructor<?>[] declaredConstructors = cls1.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
System.out.println(constructor);
}
System.out.println();
/*
* Constructor getConstructor(Class... parameterTypes);
* 根据指定构造方法参数数据类型,顺序,个数获取对应的构造方法类对象,只可以获取非私有化构造方法
*/
// 无参数构造方法
Constructor<?> constructor1 = cls1.getConstructor();
Constructor<?> constructor2 = cls1.getConstructor(int.class);
Constructor<?> constructor3 = cls1.getConstructor(int.class, String.class);
System.out.println(constructor1);
System.out.println(constructor2);
System.out.println(constructor3);
System.out.println();
/*
* Constructor getDeclaredConstructor(Class... parameterTypes);
* 根据指定构造方法参数数据类型,顺序,个数获取对应的构造方法类对象,可以获取任意权限的构造方法 【暴力反射】
*/
Constructor<?> constructor4 = cls1.getDeclaredConstructor(String.class);
System.out.println(constructor4);
System.out.println();
/*
* 通过 Constructor 构造方法类对象 利用 newInstance 实例化对象
*/
Person p1 = (Person) constructor1.newInstance();
System.out.println(p1);
Person p2 = (Person) constructor2.newInstance(10);
System.out.println(p2);
Person p3 = (Person) constructor3.newInstance(20, "王某乾");
System.out.println(p3);
/*
* constructor4 对应的是一个【暴力反射】得到的 private 私有化修饰的构造方法,按照
* private 关键字语法要求,无法在类外调用私有化内容。
*
* void setAccessible(boolean flag);
* 修改暴力反射得到的 private 修饰内容操作权限,如果赋值 true,可以在类外获取到 私有化内容操作权限
*/
constructor4.setAccessible(true);
Person p4 = (Person) constructor4.newInstance("王·渣男·某平");
System.out.println(p4);
}
}
4. 通过 Class 对象获取对应类 Method 对象
4.1 Method 成员方法关注的内容
格式:
权限修饰符 是否静态 返回值类型 方法名(形式参数列表) {
}
调用方法时,必须明确哪些内容???
静态成员方法:
类名.方法名(实际参数);
非静态成员方法:
类对象.方法名(实际参数);
重载:
1. 在共同一个类内或者接口内
2. 要求方法名必须一致
3. 要求形式参数列表类型,顺序,个数不一致
调用时:
需要根据实际参数数据类型,顺序,个数来明确执行的是哪一个方法。
明确方法:
1. 方法名
2. 方法所需参数类型
4.2 获取 Method 对象相关操作方法
Method[] getMethods();
通过 Class 对象调用,获取 Class 对象对应类中的所有非私有化成员方法 Method 对象,包括从父类继承而来子类可以使用的成员方法。
Method[] getDeclaredMethods();
【暴力反射】
通过 Class 对象调用,获取 Class 对象对应类中的所有成员方法 Method 对象,包括私有化成员方法,不包括从父类继承而来的子类可以使用的成员方法。
Method getMethod(String methodName, Class... parameterTypes);
通过 Class 对象调用,根据指定的方法名 methodName, 和对应成员方法参数类型 Class... parameterTypes 获取对应非私有化成员方法对象。
Class... parameterTypes 不定长参数 Class 类型,使用 Class 类型来约束构造方法参数类型。
例如:
Class<Person> cls = Person.class;
// 获取 Person 类 game() 方法
Method method1 = cls.getMethod("game");
==> public void game();
// 获取 Person 类 game(String) 方法
Method method2 = cls.getMethod("game", String.class);
==> public void game(String);
Method getDeclaredMethod(String methodName, Class... parameterTypes);
通过 Class 对象调用,根据指定的方法名 methodName, 和对应成员方法参数类型 Class... parameterTypes 获取成员方法对象,包括私有化成员方法
Class... parameterTypes 不定长参数 Class 类型,使用 Class 类型来约束构造方法参数类型。
例如:
Class<Person> cls = Person.class;
// 获取 Person 类 testPrivate() 方法
Method method1 = cls.getDeclaredMethod("testPrivate");
==> private void testPrivate();
// 获取 Person 类 testPrivate(String) 方法
Method method2 = cls.getDeclaredMethod("testPrivate", String.class);
==> private void testPrivate(String);
4.3 操作 Method 类对象执行对应方法
Object invoke(Object obj, Object... values);
通过 Method 对象调用,参数 obj 是执行当前方法的对象,values 是当前方法执行所需的实际参数。
4.4 代码演示
package com.qfedu.a_reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* Method 成员方法操作演示
*/
public class GetMethodObject {
public static void main(String[] args)
throws ClassNotFoundException, NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
/*
* Class.forName()
*
* 动态!!!只要用户提供完整的包名.类名,我可以获取到任意 Class 对象
* 【工厂模式】
*/
Class<?> cls1 = Class.forName("com.qfedu.a_reflect.Person");
/*
* Method[] getMethods()
* 获取当前类对象的所有非私有化成员方法 Method 对象,包括从父类继承到子类的成员方法
*/
Method[] methods = cls1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println();
/*
* Method[] getDeclaredMethods()
* 【暴力反射】
* 获取当前类对象的所有化成员方法 Method 对象,有且只有当前类自有方法,不包括父类方法
*/
Method[] declaredMethods = cls1.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method);
}
System.out.println();
/*
* Method getMethod(String methodName, Class... parameterTypes);
* 根据指定方法名称和对应参数列表数据类型获取指定非私有化成员方法
*/
Method game1 = cls1.getMethod("game");
Method game2 = cls1.getMethod("game", String.class);
System.out.println(game1);
System.out.println(game2);
System.out.println();
/*
* Method getDeclaredMethod(String methodName, Class... parameterTypes);
* 【暴力反射】
* 根据指定方法名称和对应参数列表数据类型获取指定成员方法,包括私有化成员方法
*/
Method testPrivate1 = cls1.getDeclaredMethod("testPrivate");
Method testPrivate2 = cls1.getDeclaredMethod("testPrivate", String.class);
System.out.println(testPrivate1);
System.out.println(testPrivate2);
System.out.println();
/*
* 实例化 Person 类对象,直接使用无参数构造方法实例化操作。
*/
Object obj = cls1.getConstructor().newInstance();
game1.invoke(obj);
game2.invoke(obj, "WOT 坦克世界");
System.out.println();
/*
* 给予暴力反射操作得到的 private 私有化修饰的成员方法对应操作权限
*/
testPrivate1.setAccessible(true);
testPrivate2.setAccessible(true);
testPrivate1.invoke(obj);
testPrivate2.invoke(obj, "烤羊排");
}
}
5. 通过 Class 对象获取对应类 Field 对象
5.1 Field 成员变量关注的内容
一个类内,成员变量唯一性标识【变量名】
数据类型, 是否静态,权限修饰,数据存储 可以认为是成员变量类对应的信息。
5.2 获取 Field 对象相关操作方法
Field[] getFields();
获取类内所有非私有化成员变量 Field 对象数组。
Field[] getDeclaredFields();
【暴力反射】
获取类内所有成员变量 Field 对象数组。包括私有化成员变量
Field getField(String fieldName);
根据指定的成员变量名称,获取非私有化成员变量 Field 对象
Field getDeclaredField(String fieldName);
【暴力反射】
根据指定的成员变量名称,获取成员变量 Field 对象,包括私有化成员变量
5.3 操作 Field 对象赋值取值成员变量
void set(Objec obj, Object value);
通过 Field 对象调用,第一个参数 是需要设置对应成员变量的类对象 第二个参数 是赋值给当前成员变量的数据
Object get(Object obj);
通过 Field 对象调用,得到对应成员变量数据,参数是从哪一个类对象中,获取对应成员变量数据
5.4 代码演示
package com.qfedu.a_reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/*
* Field 成员变量类对象
*/
public class GetFieldObject {
public static void main(String[] args)
throws ClassNotFoundException, NoSuchFieldException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
/*
* Class.forName()
*
* 动态!!!只要用户提供完整的包名.类名,我可以获取到任意 Class 对象 【工厂模式】
*/
Class<?> cls1 = Class.forName("com.qfedu.a_reflect.Person");
/*
* Field[] getFields();
* 获取类内所有非私有化成员变量 Field 对象数组。
*/
Field[] fields = cls1.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println();
/*
* Field[] getDeclaredFields();
* 【暴力反射】
* 获取类内所有成员变量 Field 对象数组。包括私有化成员变量
*/
Field[] declaredFields = cls1.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
System.out.println();
/*
* Field getField(String fieldName);
* 根据指定的成员变量名称,获取非私有化成员变量 Field 对象
*/
Field test = cls1.getField("test");
System.out.println(test);
System.out.println();
/*
* Field getDeclaredField(String fieldName);
* 【暴力反射】 根据指定的成员变量名称,获取成员变量 Field
* 对象,包括私有化成员变量
*/
Field id = cls1.getDeclaredField("id");
Field name = cls1.getDeclaredField("name");
System.out.println(id);
System.out.println(name);
System.out.println();
Object obj = cls1.getConstructor().newInstance();
System.out.println(obj);
/*
* 赋值成员变量数据
*/
test.set(obj, 100);
System.out.println(obj);
id.setAccessible(true);
name.setAccessible(true);
id.set(obj, 1);
name.set(obj, "张三");
System.out.println(obj);
/*
* 取值操作
*/
System.out.println();
System.out.println(test.get(obj));
System.out.println(id.get(obj));
System.out.println(name.get(obj));
}
}
6. 试题
文件名 student.txt
文件内容
className=com.qfedu.entity.Student;[id=1,name=张三,age=16,gender=男];[id=2,name=李四,age=26,gender=男];[id=3,name=王五,age=36,gender=男];[id=4,name=孙武,age=16,gender=男];
要求完成:
从文件中读取数据,解析字符串内容,选择合适数据类型设置 Student 对象。每一条数据做成一个 Student 类型对象,同时存储到 ArrayList 集合中