1. 反射需要用到的API
1.1 获取字节码对象
Class.forName(“类的全路径”); 注意:传入的是类的全路径名,包含包名.类名,而且会抛出异常
类名.class 注意:这个写法需要自己手动接一下获取到的字节码对象,不能用快捷方式的
对象.getClass(); 注意:经常与匿名对象一起使用
1.2 常用方法
获取包名 类名
clazz.getPackage().getName()//包名 clazz.getSimpleName()//类名 clazz.getName()//完整类名
获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量 getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量 getField(变量名) getDeclaredField(变量名)
获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法 getConstructors()//获取所有的公开的构造方法 getDeclaredConstructors()//获取所有的构造方法,包括私有 getDeclaredConstructor(int.class,String.class)
获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法 getMethod(方法名,参数类型列表) getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法 getDeclaredMethod(方法名,int.class,String.class)
反射新建实例
clazz.newInstance();//执行无参构造创建对象 clazz.getConstructor(int.class,String.class)//要先获取构造方法 clazz.newInstance(666,”海绵宝宝”);//再执行含参构造创建对象
反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量 field.setAccessible(true);//使私有成员允许访问 field.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null field.get(实例);//访问指定实例变量的值,静态变量,第一参数给null
反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表); m.setAccessible(true);//使私有方法允许被调用 m.invoke(实例,参数数据);//让指定实例来执行该方法
package cn.tedu.reflection;
/*本类用于复习反射的物料类*/
public class Student{
//1.定义成员变量
public String name;
public int age;
//2.提供本类的无参与全参构造
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
public void play(){
System.out.println("今天大结局,放学后写10000行代码");
}
public void eat(int n){
System.out.println("在忙我也要吃"+n+"碗大米饭");
}
//4.重写toString(),为了查看对象的类型 属性 属性值
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package cn.tedu.reflection;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
/*本类用于反射测试*/
public class TestReflect {
/*单元测试方法:是java中最小的测试单位,使用灵活,推荐指数:5颗星*/
@Test
public void getClazz() throws ClassNotFoundException {
//练习获取字节码对象的3种方式
Class<?> Clazz1 = Class.forName("cn.tedu.reflection.Student");
Class<?> Clazz2 = Student.class;
Class<? extends Student> Clazz3 = new Student().getClass();
System.out.println(Clazz1.getClass().getName());//java.lang.Class
System.out.println(Clazz1);//class cn.tedu.reflection.Student
System.out.println(Clazz1.getName());//cn.tedu.reflection.Student
System.out.println(Clazz1.getSimpleName());//Student
System.out.println(Clazz1.getPackage());//package cn.tedu.reflection
System.out.println(Clazz1.getPackage().getName());//cn.tedu.reflection
}
/*通过单元测试方法,获取Student目标类中的成员变量*/
@Test
public void getFie() throws ClassNotFoundException {
//1.获取字节码对象
Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
//2.通过字节码对象,获取目标类中所有的成员变量
Field[] fs = clazz.getFields();
//3.遍历数组,获取每个成员变量的具体信息
/*注意:目前要获取的成员变量的修饰符必须是public才能获取到的,
*不然,像默认修饰符的成员变量也是获取不到的*/
for (Field f :
fs) {
System.out.println(f.getName());//name age
System.out.println(f.getType());//class java.lang.String int
// name
// class java.lang.String
// age
// int
}
}
/*辅助理解的单元测试*/
@Test
public void getSu() {
//1.创建Student类的3个对象
Student s1 = new Student("张三", 3);
Student s2 = new Student("李四", 4);
Student s3 = new Student("王五", 5);
//2.将创建好的3个对象放入Student类型的数组中
Student[] s = {s1, s2, s3};
//3.通过遍历,在每轮循环获取每一个学生对象
for (Student hh :
s) {
System.out.println(hh.name);
hh.eat(8);
hh.play();
/*张三
在忙我也要吃8碗大米饭
今天大结局,放学后写10000行代码
李四
在忙我也要吃8碗大米饭
今天大结局,放学后写10000行代码
王五
在忙我也要吃8碗大米饭
今天大结局,放学后写10000行代码
*/
}
}
/*通过单元测试,获取Student目标类的成员方法*/
@Test
public void getFunction() {
//1.获取字节码对象
Class<?> clazz = Student.class;
//通过字节码对象获取目标类对应的所有成员方法们
Method[] ms = clazz.getMethods();
//3.通过高效for循环遍历数组,每轮循环拿到一个方法对象
for (Method s :
ms) {
// System.out.println(s+" ");
// public java.lang.String cn.tedu.reflection.Student.toString()
// public void cn.tedu.reflection.Student.play()
// public void cn.tedu.reflection.Student.eat(int)
// public final void java.lang.Object.wait() throws java.lang.InterruptedException
// public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
// public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
// public boolean java.lang.Object.equals(java.lang.Object)
// public native int java.lang.Object.hashCode()
// public final native java.lang.Class java.lang.Object.getClass()
// public final native void java.lang.Object.notify()
// public final native void java.lang.Object.notifyAll()
// System.out.println("方法名字"+s.getName());
// 方法名字toString
// 方法名字play
// 方法名字eat
// 方法名字wait
// 方法名字wait
// 方法名字wait
// 方法名字equals
// 方法名字hashCode
// 方法名字getClass
// 方法名字notify
// 方法名字notifyAll
//通过本轮循环获取到方法对象,进一步获取这个方法的参数类型
Class<?>[] pt = s.getParameterTypes();
// System.out.println(pt);
// [Ljava.lang.Class;@5e8c92f4
// [Ljava.lang.Class;@61e4705b
// [Ljava.lang.Class;@50134894
// [Ljava.lang.Class;@2957fcb0
// [Ljava.lang.Class;@1376c05c
// [Ljava.lang.Class;@51521cc1
// [Ljava.lang.Class;@1b4fb997
// [Ljava.lang.Class;@deb6432
//[Ljava.lang.Class;@28ba21f3
// [Ljava.lang.Class;@694f9431
// [Ljava.lang.Class;@f2a0b8e
System.out.println(Arrays.toString(pt));
// []
//[]
//[int]
//[]
//[long, int]
//[long]
//[class java.lang.Object]
//[]
//[]
//[]
//[]
}
}
/*通过单元测试,获取Student目标类的构造方法*/
@Test
public void getCons(){
//1.获取字节码对象
Class<? extends Student> clazz= new Student().getClass();
//2.通过字节码对象 获取目标类中的构造方法们
Constructor<?>[] cs = clazz.getConstructors();
//3.通过高效for循环遍历,拿到每一个构造函数对象
for (Constructor c:
cs) {
System.out.println(c.getName());//cn.tedu.reflection.Student cn.tedu.reflection.Student
Class[] pt = c.getParameterTypes();
for (Class p:
pt ) {
System.out.println(Arrays.toString(pt));//[class java.lang.String, int] [class java.lang.String, int]
}
}
}
/*通过单元测试,利用反射创建目标类对象*/
@Test
public void getObject() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1.获取字节码对象
Class<?> clazz = Student.class;
//2.利用反射创建目标业务类对象
/*反射创建对象的方案1:通过触发目标类的无参构造创建对象*/
Object o = clazz.newInstance();
System.out.println(o);//Student{name='null', age=0}
/*反射创建对象的方案2:先获取指定的全参构造对象,再通过构造对象创建Student对象*/
//3.获取指定的构造函数对象,注意要获取构造方法,需要指定参数类型,而且是字节码对象
Constructor<?> c = clazz.getConstructor(String.class ,int.class);
//4.利用获取到的全参构造对象,创建目标类Student的对象
//构造方法 传参数
Object o2 = c.newInstance("赵六", 18);
//5.打印创建好的学生类对象,可以看到属性也被赋值了
System.out.println(o2);
}
}
练习2
package cn.tedu.reflection;
/*本类用作暴力反射的物料类*/
public class Person {
//1.提供私有属性
private String name;
private int age;
private String addr;
//2.提供私有方法
private void save(int n,String s){
System.out.println("save方法"+n+s);
}
private void update(){
System.out.println("update方法");
}
}
package cn.tedu.reflection;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*本类用于测试暴力反射*/
public class Testreflect2 {
/*1.通过暴力反射获取与操作属性*/
@Test
public void getFile2() throws Exception {
//1.获取字节码对象
Class<?> clazz = Person.class;
//2.获取指定它的私有属性,注意需要传入属性名,确认要获取哪个私有属性
Field field = clazz.getDeclaredField("name");
//3.根据获取到的属性值,查看属性的类型
System.out.println(field);//private java.lang.String cn.tedu.reflection.Person.name
System.out.println(field.getType().getName());//java.lang.String
System.out.println(field.getType());//class java.lang.String
//4.设置属性的值
//4.1需要指定到底给哪个对象的name属性设置一个什么值
Object obj = clazz.newInstance();//触发无参构造创建对象
//4.2暴力反射,需要设置私有资源可见权限
field.setAccessible(true);
//fileld就是我们刚刚获取的name值
//set(m,n)---m是给哪个对象的name属性设置值,n是设置的具体值
field.set(obj, "赵六");
//4.4打印查看刚刚的属性值是否设置成功
//filed.get(m),m代表的就是给哪个对象的这个name属性的值
System.out.println(field.get(obj));//赵六
}
/*巩固练习:通过暴力反射获取与操作属性*/
@Test
public void getFile3() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Class<?> clazz = Person.class;//获取字节码对象
Field addr = clazz.getDeclaredField("addr");//获取addr属性对象
addr.setAccessible(true);//设置属性私有可见的权限
Object o = clazz.newInstance();//准备好要给哪个对象设置属性值
addr.set(o, "北京");//给p3对象的field(addr属性)设置值为“北京”
System.out.println(addr.get(o));//北京,查看属性值是否设置成功
}
/*2.通过暴力反射获取与操作私有方法*/
@Test
public void getFunction2() throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//1.获取字节码对象
Class<? extends Person> clazz = new Person().getClass();
//2.通过字节码对象获取私有方法
/*getDeclaredMethod(m,x,y,z...)
* m:要获取方法的名字
* x,y,z...可变参数,指的是目标方法的参数个数可以根据实际情况改变,0/1/2...个参数
* x y z传入的是参数对应的类型,而且这个类型也必须是字节码对象*/
Method save = clazz.getDeclaredMethod("save", int.class, String.class);
//通过反射创建目标类Person对应的对象
Person p1 = clazz.newInstance();
save.setAccessible(true);
/*invoke(o,x,y,z)表示通过反射技术执行方法
* o:要给哪个对象执行方法
* x,y,z...可变参数,之前获取的方法参数类型是什么,此处就传对应个数与类型的值*/
save.invoke(p1, 18, "秦仙女");//save方法18秦仙女
}
/*巩固练习:通过暴力反射获取与操作私有方法*/
@Test
public void getFunction3() throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class<?> clazz = Person.class;
Method update = clazz.getDeclaredMethod("update");
update.setAccessible(true);
Object o = clazz.newInstance();
update.invoke(o);//update方法
}
}