反射
一. 反射概述
Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
二. 获取Class类的对象
我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象
这里我们提供三种方式获取Class类型的对象
-
使用类的class属性来获取该类对应的Class对象。
举例:Student.class将会返回Student类对应的Class对象 -
调用对象的getClass()方法,返回该对象所属类对应的Class对象
该方法是Object类中的方法,所有的Java对象都可以调用该方法 -
使用Class类中静态方法forName(String className)
该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
代码如下:
package Demo.ReflectDemo;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//如果简单测试,就用1和2
// 使用类的class属性来获取该类对应的Class对象
Class<Student> c1 = Student.class;
System.out.println(c1);//class Demo.ReflectDemo.Student
Class<Student> c2 = Student.class;
System.out.println(c1==c2);//true
System.out.println("------");
// 调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1==c3);//true
//以下方式灵活性更高
// 使用Class类中静态方法forName(String className)
Class<?> c4 = Class.forName("Demo.ReflectDemo.Student");//?代表的是泛型通配符
System.out.println(c1==c4);//true
}
}
三. 反射获取构造方法并使用
常用四种:
Class类中用于获取构造方法的方法
方法名 | 方法说明 |
---|---|
Constructor< T > getConstructor(Class< ? >… parameterTypes); | 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数。 |
Constructor< ? >[ ] getConstructors(); | 返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数。 |
Constructor< T > getDeclaredConstructor(Class< ? >… parameterTypes); | 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数。 |
Constructor< ? >[ ] getDeclaredConstructors(); | 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组 |
-
Class类中用于
创建对象的方法
方法名 方法说明 T newInstance(Object… initargs); 根据指定的构造方法创建对象。 代码如下:
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
反射获取构造方法并使用
Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数。
Constructor<?>[] getConstructors() 返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数。
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数。
Constructor<?>[] getDeclaredConstructors() 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组
T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
void setAccessible(boolean flag) 将此反射对象的 accessible标志设置为指示的布尔值。
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取Class对象
Class<?> c=Class.forName("Demo.ReflectDemo.Student");
//反射获取构造方法并使用
/*
System.out.println("返回Class表示类指定所有公共的构造器");
Constructor<?>[] cp = c.getConstructors();//返回Class表示类指定所有公共的构造器
for (Constructor<?> cc : cp) {
System.out.println(cc);
}
System.out.println("------------------------------------------");
System.out.println("返回Class表示类指定公有的构造器");
Constructor<?> cp1 = c.getConstructor(String.class,int.class,String.class);//返回Class表示类指定公有的构造器
System.out.println(cp1);
System.out.println("------------------------------------------");
System.out.println("返回Class表示类指定所有的构造器");
Constructor<?>[] cp2 = c.getDeclaredConstructors();//返回Class表示类指定所有的构造器
for (Constructor<?> cc : cp2) {
System.out.println(cc);
}
System.out.println("------------------------------------------");
*/
System.out.println("返回Class表示类指定的构造器");
Constructor<?> cp3 = c.getDeclaredConstructor(String.class,int.class);//返回Class表示类指定的构造器
System.out.println(cp3);
System.out.println("**************************************");
//Constructor提供了一个类的单个构造函数的信息和访问权限
Object o = cp3.newInstance("坤坤",25);
System.out.println(o);
}
}
(一) 练习
- 例题:
通过反射实现如下操作- Student s=new Student(“坤坤”,25,“鸡你太美村”);
- System.out.println(s);
代码如下:
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
反射获取构造方法并使用
Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数。
Constructor<?>[] getConstructors() 返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数。
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数。
Constructor<?>[] getDeclaredConstructors() 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组
T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
void setAccessible(boolean flag) 将此反射对象的 accessible标志设置为指示的布尔值。
通过反射实现如下操作
Student s=new Student("坤坤",25,"鸡你太美村");
System.out.println(s);
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取字节码文件
Class<?> c = Class.forName("Demo.ReflectDemo.Student");
//"林青霞",30,"西安" 并且构造函数为public Student(String name, int age, String address)
//所以:Constructor<T> getConstructor(Class<?>... parameterTypes);返回指定的公有的构造函数
Constructor<?> constructor = c.getConstructor(String.class, int.class, String.class);
//T newInstance(Object… initargs); 根据指定的构造方法创建对象。
Object o = constructor.newInstance("坤坤", 25, "鸡你太美村");
System.out.println(o);
}
}
- 例题:
通过反射实现如下操作- Student s=new Student(“坤坤”);
- System.out.println(s);//
代码如下:
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取字节码文件
Class<?> c = Class.forName("Demo.ReflectDemo.Student");
//反射获取构造方法并使用 ->("iKun")
Constructor<?> declaredConstructor = c.getDeclaredConstructor(String.class);
//T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
Object iKun = declaredConstructor.newInstance("iKun");
System.out.println(iKun);
}
}
修改后
的代码如下:
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取字节码文件
Class<?> c = Class.forName("Demo.ReflectDemo.Student");
//反射获取构造方法并使用 ->("iKun")
Constructor<?> declaredConstructor = c.getDeclaredConstructor(String.class);
//T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
declaredConstructor.setAccessible(true);
Object iKun = declaredConstructor.newInstance("iKun");
System.out.println(iKun);
}
}
总结:
基本数据类型也可以通过.class得到对应的Class类型
void setAccessible(boolean flag);值为true,取消访问检查(!!暴力反射很直接)
四. 反射获取成员变量并使用
常用四种:
Class类中用于反射获取成员变量的方法
获取成员变量的方法名 | 获取成员变量的方法说明 |
---|---|
Field getDeclaredField(String name); | 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定声明字段 |
Field[] getDeclaredFields(); | 返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段 |
Field getField(String name); | 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定公共成员字段 |
Field[] getFields(); | 返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段 |
- File类中用于给成员变量赋值和获取赋值变量的方法
方法名 | 方法说明 |
---|---|
void set(Object obj,Object value); | 给obj对象的成员变量赋值为value |
Object get(Object obj); | 返回由该 Field表示的字段在指定对象上的值。 |
代码如下:
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/*
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取字节码文件
Student student = new Student();
Class<? extends Student> c = student.getClass();
/*
//反射获取成员变量并使用
// Field[] fields = c.getFields();
Field[] fields = c.getDeclaredFields();
for(Field field : fields){
System.out.println(field);
}
*/
//反射获取成员变量并使用(例如:获取指定的"address"变量)
Field addressField = c.getDeclaredField("address");
//获取无参构造方法创建对象
Constructor<?> con = c.getConstructor();
Object obj= con.newInstance();
//Field提供有关类或接口的单个字段的信息和动态访问 void set(Object obj,Object value);给obj对象的成员变量赋值为value
addressField.set(obj,"鸡你太美村");
System.out.println(obj);
/* Object o = addressField.get(obj);
System.out.println(o);*/
// obj.addressField="西安"; 不能这样写会出现错误
/*
通过反射↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
Student s1 = new Student();
s1.address="西安";
System.out.println(s1.address);
*/
}
}
- 注意:
(一) 练习
- 例题:
通过反射实现如下操作:- Student s=new Student(“坤坤”,25,“鸡你太美村”);
- s.name=“iKun”;
- s.age=30;
- s.address=“西安”;
- System.out.println(s);
代码如下:
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取字节码文件
Class<Student> c = Student.class;
//反射获取构造方法并使用
Constructor<?> cs = c.getConstructor();
Object o= cs.newInstance();
//反射获取成员变量并使用
Field de1 = c.getDeclaredField("name");
de1.setAccessible(true);
Field de2 = c.getDeclaredField("age");
de1.setAccessible(true);
Field de3 = c.getDeclaredField("address");
de1.setAccessible(true);
//File类中用于给成员变量赋值和获取赋值变量的方法
de1.set(o,"坤坤");
de2.set(o,25);
de3.set(o,"鸡你太美村");
System.out.println(de1);
System.out.println(de2);
System.out.println(de3);
}
}
五. 反射获取成员方法并使用
方法名 | 方法说明 |
---|---|
Method getDeclaredMethod(String name, Class<?>… parameterTypes); | 返回一个方法对象,它反映此表示的类或接口的指定声明的方法 Class对象。 |
Method[] getDeclaredMethods(); | 返回一个包含 方法对象的数组, 方法对象反映由 Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法。 |
Method getMethod(String name, Class<?>… parameterTypes); | 返回一个 方法对象,该对象反映由该 Class对象表示的类或接口的指定公共成员方法。 |
Method[] getMethods(); | 返回一个包含 方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类。 |
常用四种:
Class类中用于反射获取成员方法的方法(有区别)
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
//获取字节码文件
Class<?> c = Class.forName("Demo.ReflectDemo.Student");
System.out.println("---------1------------");
//Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个方法对象,它反映此表示的类或接口的指定声明的方法 Class对象。
Method dm = c.getDeclaredMethod("function");
System.out.println(dm);
System.out.println("---------2------------");
//Method[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映由 Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法。
Method[] dms = c.getDeclaredMethods();
for (Method m:dms){
System.out.println(m);
}//但不包括继承方法 本类所有
System.out.println("---------3------------");
//Method[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类。
Method[] dms2 = c.getMethods();
for (Method m:dms2){
System.out.println(m);
}//包括继承方法 所有公共的
System.out.println("---------4------------");
Method dm3 = c.getMethod("method1");
System.out.println(dm3);
}
}
- 方法在类或接口上提供有关单一方法的信息和访问权限
方法名 | 方法说明 |
---|---|
Object invoke(Object obj, Object… args); | 在具有指定参数的指定对象上调用此方法对象表示的基础方法 |
Method类中的Object invoke(Object obj, Object… args);在具有指定参数的指定对象上调用此方法对象表示的基础方法
Object:返回值类型
obj:调用方法的对象
args:方法需要的参数
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
//获取字节码文件
Class<?> c = Class.forName("Demo.ReflectDemo.Student");
//获取反射指定方法method1
Method dm1 = c.getMethod("method1");
//获取无参构造方法获取对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//方法在类或接口上提供有关单一方法的信息和访问权限 Object invoke(Object obj, Object… args); 在具有指定参数的指定对象上调用此方法对象表示的基础方法
dm1.invoke(obj);
//o.dm;错误的写法,不能这样!!
/*
通过实例化获取对象↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
Student s=new Student();
s.method();
*/
}
}
(一) 练习
- 例题:
通过反射实现如下操作:- Student s=new Student();
- s.method1();
- s.method2(“iKun”);
- String ss=s.method3(“iKun”,30)
- System.out.println(ss);
- s.function();
代码如下:
package Demo.ReflectDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
//获取字节码文件
Class<?> c = Class.forName("Demo.ReflectDemo.Student");
//通过反射获取构造器后得到对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
//通过反射获取方法
Method m1 = c.getMethod("method1");
m1.invoke(obj);//method1
System.out.println("------------------------------");
Method m2 = c.getMethod("method2",String.class);
m2.invoke(obj,"iKun");
System.out.println("------------------------------");
Method m3 = c.getMethod("method3", String.class, int.class);
Object iKun = m3.invoke(obj, "iKun", 30);
System.out.println(iKun);
System.out.println("------------------------------");
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(obj);
}
}
注意:
m4.setAccessible(true);是可以允许访问私有权限的
六. 反射练习之超过泛型检查(重点!!!!!!!!!!!)
- 例如:我有ArrayList< Integer >集合,现在我想在这个集合中添加一个字符串数据,如何实现?
注意泛型为< Integer > (说白点就是卡bug)
package Demo.ReflectDemo1;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//创建集合
ArrayList<Integer> ai = new ArrayList<Integer>();
ai.add(1);
Class<? extends ArrayList> c = ai.getClass();//字节码文件
//通过反射获取方法
Method addMethod = c.getDeclaredMethod("add", Object.class);
//加入字符串
addMethod.invoke(ai, "鸡你太美村");
System.out.println(ai);
}
}
- 原因
七. 通过配置文件运行类中的方法(重点!!!!!!!!!!!)
例如:
- 定义一个学生类,里面有一个study()方法
- 定义一个老师类,里面有一个teach()方法
package Demo.ReflectDemo2;
public class ReflectDemo2 {
public static void main(String[] args) {
Student s = new Student();
s.study();
Teacher t = new Teacher();
t.teach();
}
}
分析:
1.我们不可能来回调用方法在类中使用,这样非常繁琐
2.我们可以考虑用反射制定一个模板,修改关键值
通过反射我们可以指定模板(此时会牵扯IO流中的)
详细博客地址:
https://blog.csdn.net/o676448/article/details/124194706
修改后的代码:
package Demo.ReflectDemo2;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectDemo2 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
/*
class.txt
className=xxx
methodName=xxx
*/
Properties pro = new Properties();
FileReader fileReader = new FileReader("C:\\untitled\\src\\Demo\\ReflectDemo2");
pro.load(fileReader);
fileReader.close();
String className = pro.getProperty("className");//Demo.ReflectDemo.Student
String methodName = pro.getProperty("methodName");//Study
//通过反射来使用
Class<?> c = Class.forName(className);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);
m.invoke(obj);
}
}