Java反射
概述:
反射是指在加载类型之后,通过该类的字节码对象来获取需要的信息,然后通过获取到 的信息来实现对应的功能,这种机制就是反射。由于这种动态性,可以极大的增强程序 的灵活性和扩展性。
获取类字节码对象的方式:(使用反射的前提)
(1)类名.class
(2)对象名的.getClass()
(3)Class.forName(类的全类名)
代码
public class TestClass {
public static void main(String[] args) throws ClassNotFoundException {
//获取Student类的字节码对象
//使用类的静态属性class
Class<? extends Student> stu = Student.class;
//使用对象的方法:getClass
Class<? extends Student> stu1 = new Student().getClass();
//使用Class类的静态方法:forName
//使用的最多
//虽然参数给的是字符串:但是底层会根据传递的字符串,
// 去class文件中寻找有没有和字符串相同的类名,
// 如果有这个和字符串相同的类名,就直接加载了,
// 如果没有就抛出异常
//特点:虽然第三种方式不是最简单的,但是参数是一个字符串,该字符串来源广泛,所以使用第三种方式可以提高程序的扩展性。
Class<?> stu2 = Class.forName("Day22.Student");
}
}
反射获取字节码对象中的构造方法并实例化
1、概述:根据类的字节码对象获取该类的构造方法,并创造该类对象
2、获取构造方法的方式:
(1)getConstructors():返回所有公共的构造方法对象
(2)getDeclaredConstructors():返回所有构造方法对象
(3)getConstructor():返回空参构造对象
(4)getConstructor(Class<?>… parameterTypes):
返回单个指定参数的公共有参构造方法对象
(5)getDeclaredConstructor(Class<?>…parameterTypes):
返回单个指定参数的有参构造方法对象
3、通过构造方法对象,创建类的实例:
newInstance(Object…initargs)
如果是空参构造,就不需要给出实参
如果是有参构造对象,就需要给出对应的实际参数
代码
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class TestClass1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("Day22.Student");
//获取字节码对象中的构造方法
//Constructor构造方法对象所属的类型
//Constructor<?>[] con = c.getConstructors();获取所有公有的构造方法
Constructor<?>[] con = c.getConstructors();
//遍历
for (Constructor<?> con1 : con) {
System.out.println(con1);
}
System.out.println("-----------");
//获取所有构造方法包括私有的构造方法
Constructor<?>[] con2 = c.getDeclaredConstructors();
for (Constructor<?> constructor : con2) {
System.out.println(constructor);
}
System.out.println("----------------");
//获取空参构造方法
Constructor<?> con3= c.getConstructor();
System.out.println(con3);
System.out.println("--------------");
//获取有参构造
Constructor<?> con4 = c.getConstructor(String.class, int.class);
System.out.println(con4);
System.out.println("----------------");
//获取所有私有有参构造
Constructor<?> con5 = c.getDeclaredConstructor(String.class);
System.out.println(con5);
System.out.println("----------------");
//通过获取的构造方法对象,来创建实例
Student s=(Student)con3.newInstance();
System.out.println(s+s.getName()+s.getAge());
System.out.println("---------------");
Object o = con4.newInstance("张三", 20);//多态,父类引用指向子类对象,方法左右
System.out.println(o);
System.out.println("--------------");
}
}
反射获取成员变量并使用
获取方法:
(1)getFields():返回所有公共成员变量对象
(2)getDeclaredFields():返回所有成员变量对象
(3)getField(String name):返回指定的公共成员变量对象
(4)getDeclaredField(String name):返回单个成员变量对象
访问成员属性的方法:
(1)set(Object obj,Object value): 用于给obj对象中的该成员变量赋value值
(2)get(Object obj):用于获取obj对象的指定成员变量值
暴力反射:
概述:如果类型中的某些属性是私有化的,那么就不能直接使用方法访问该属性
所以只能使用暴力反射来强制访问。
相关方法:
isAccessible():判断当前属性对象是否需要参与虚拟机的检测
setAccessible(boolean flag):将当前对象设置为不需要被虚拟机检测
代码
import java.lang.reflect.Field;
public class TestField {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {
Class<?> c = Class.forName("Day22.Student");
//获取字节码对象中的所有的属性:public
System.out.println("遍历输出字节码对象中的所有属性(public)");
Field[] fs = c.getFields();
for (Field f : fs) {
System.out.println(f);
}
//获取所有属性:包括私有
System.out.println("获取所有属性包括私有");
Field[] df = c.getDeclaredFields();
for (Field field : df) {
System.out.println(field);
}
System.out.println("获取其中一个属性根据属性的名称指定字符串传递---------------------");
//获取其中的一个属性根据属性的名称指定字符串传递
Field address = c.getField("address");
System.out.println(address);
System.out.println("获取私有属性-----------");
//获取私有的属性
Field name = c.getDeclaredField("name");
System.out.println(name);
System.out.println("通过空参创建对象,并且根据属性名获取属性值----------");
//通过空参创建对象,并且根据属性名获取属性值
Object o = c.newInstance();
System.out.println(address.get(o));
address.set(o,"北京");
System.out.println(address.get(o));
System.out.println("判断属性是否需要经过虚拟机的检测,如果为false,表示属性需要经过虚拟机检测-----");
//isAccessible():该方法是判断属性是否需要经过虚拟机的检测,如果为false,表示属性需要经过虚拟机检测
//在检测的时候如果发现私有修饰,不能直接使用,如果是共有的可以直接使用
System.out.println(name.isAccessible());//false
System.out.println(address.isAccessible());//false
//将属性的检测方式设置为true之后,表示该属性在被使用时不需要经过检测,可以直接使用
name.setAccessible(true);
System.out.println(name.get(o));
name.set(o,"张三");
System.out.println(name.get(o));
}
}
反射获取成员方法并使用
1、方法:
(1)getMethods():返回所有公共成员方法对象
(2)getDeclaredMethods():返回所有成员方法对象
(3)getMethod(String methodName, Class<?>…parameterTypes):
返回指定的公共成员方法对象
(4)getDeclaredMethod(String methodName, Class<?>…parameterTypes):
返回指定的成员方法对象
2、执行方法的方式:
invoke(Object o,Class…paramsTypre)
3、如果方法是已经非静态方法,需要传递一个对象执行
4、如果方法是一个静态方法,不需要传入对象,直接传入Null
代码
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestClassGetMethod {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> c = Class.forName("Day22.Student");
//获取字节码对象中的所有的方法:public 包括父类中继承的
Method[] m = c.getMethods();
for (Method method : m) {
System.out.println(method);
}
//获取所有的方法,包括私有的,但是父类中继承的没有重写的方法方法不获取
Method[] dm = c.getDeclaredMethods();
for (Method method : dm) {
System.out.println(method);
}
//获取某一个指定的方法
Method m1 = c.getMethod("show");
Method m2 = c.getMethod("show", String.class);
Method m3 = c.getMethod("getNum");
Method m4 = c.getDeclaredMethod("print");
Method m5 = c.getMethod("useStatic");
Object o = c.newInstance();
//执行方法,invoke
// 如果执行的方法是私有的,使用暴力反射,如果不是直接使用
//如果方法有参数就给实参,没有就不给
m1.invoke(o);
m2.invoke(o,"你好");
Object invoke = m3.invoke(o);//100->Integer->Object
System.out.println(invoke);
m4.setAccessible(true);
m4.invoke(o);
m5.invoke(null);//如果方法是静态的,可以不给对象,给null代替即可
}
}
练习
需求:
定义一个List集合(泛型定义为Integer),如:ArrayList list = new ArrayList<>();
要求利用反射的原理,在集合中添加若干字符串并且不报错,最终输出集合中的内容
代码
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class TestExercises {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
ArrayList<Integer> list = new ArrayList<>();
Class<? extends ArrayList> c = list.getClass();
//参数擦除:当某个类型的方法的参数定义的是泛型时,在使用反射调用该方法的时候,参数的类型是Object类型,而不能是泛型类型
Method add = c.getMethod("add", Object.class);//类中使用的是泛型所以就不能用指定的数据类型,只能用Object
add.invoke(list,"asdf");
System.out.println(list);
}
}
反射练习
1、自定义一个Stu类型
属性:String name; int age (私有化,定义公共的访问方式)
方法:toString(展示当前属性的信息)
2、手动将这个类型的全类名写在文件中的第一行
3、读取文件中的类名,并创建一个该类对象(通过有参构造创建)
4、使用toString方法展示对象属性
5、使用公共的访问方法给属性改值,并再次调用show方法展示属性值
代码
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Exercises1 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//创建字符高速缓冲流对象
BufferedReader br = new BufferedReader(new FileReader("Day13/src/main/java/Day22/a.txt"));
//调用readLine()方法读取文件中的一行
String s = br.readLine();
//使用反射获取类字节码对象
Class<?> c = Class.forName(s);
//获取有参构造
Constructor<?> con = c.getConstructor(String.class,int.class);
//创建对象
Object o = con.newInstance("张三", 20);
//获取toString方法
Method toString = c.getMethod("toString");
//运行toString方法传入对象
Object o1 = toString.invoke(o);
System.out.println(o1);
//获取setName方法
Method setName = c.getMethod("setName",String.class);
//修改对象的name属性
setName.invoke(o,"李四");
System.out.println(toString.invoke(o));
//获取成员变量
Field name = c.getDeclaredField("name");
Field age = c.getDeclaredField("age");
//因为成员变量已经私有化,所以需要使用暴力反射
name.setAccessible(true);
age.setAccessible(true);
name.set(o,"王五");
age.set(o,60);
System.out.println(toString.invoke(o));
}
}