什么是反射
允许运行中的Java程序对自身进行检查。
反射可以直接操作程序的私有属性,反射像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到包括(成员变量、成员方法、构造器等),并且可以操纵类的字段、方法、构造器等部分。
反射用到的API
获取字节码对象
Class.forName(“类的全路径”);
类名.class
对象.getClass();
常用方法
- 获取包名 类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名- 获取成员变量定义信息
getFields()//获取所有公开的成员变量,包括继承变量
getDeclareds()//获取本类定义的成员变量,包括私有,但不包括继承的变量
getField(变量名)
getDeclaredField(变量名)- 获取构造方法定义信息
getConstructor(参数类型列表)//获取公开的构造方法
getConstructors()//获取所有的公开的构造方法
getDeclaredConstructors()//获取所有的构造方法,包括私有
getDeclaredConstructor(int.class, String.class)- 获取方法定义信息
getMethods()//获取所有可见的方法,包括继承的方法
getMethod(方法名, 参数类型列表)
getDeclaredMethods()//获取本类定义的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名, int.class, String.class)- 反射新建实例
clazz.newInstance();//执行无参构造创建对象
clazz.newInstance(666, “海绵宝宝”);//执行含参构造创建对象
clazz.getConstructor(int.class, String.class)//获取构造方法- 反射调用成员变量
clazz.getDeclaredField(变量名);//获取变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例, 值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null- 反射调用成员方法
Method m = Clazz.getDeclaredMethod(方法名, 参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例, 参数数据);//让指定实例来执行该方法
反射的应用
创建:测试物料类
package com.review;
public class Student {
private String name;
public int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void play() {
System.out.println("今天要把反射彻底搞明白,不然不睡了~~");
}
public void sunDay(int n) {
System.out.println("今天写了" + n + "个接口");
}
//重写toString来查看学生对象的具体属性和属性值
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
练习:获取类对象
pakage com.reflection;
public class TestReflect {
//单元测试方法:@Test + public + void + 没有参数
//导包:import org.junit.Test
@Test
public void getClazz() throws ClassNotFoundException {
//练习获取字节码对象的3种方式
Class<?> clazz1 = Class.forName("com.review.Student");
Class<?> clazz2 = Student.class;
Class<?> clazz3 = new Student().getClass();
//获取Student类对应的字节码对象
System.out.println(clazz1);//class com.reflection.Student
//获取当前字节码对象clazz1的名字
System.out.println(clazz1.getName());//com.reflection.Student
//获取Student类的类名
System.out.println(clazz2.getSimpleName());
//获取Student类对应的包对象
System.out.println(clazz3.getPackage());
//获取Student类对应的包对象,再获取这个包对象的名字
System.out.println(clazz3.getPackage().getName());
}
}
练习:获取成员变量
package com.reflection;
public class TestReflect {
@Test
public void getStu() {
Student s1 = new Student("张三", 3);
Student s2 = new Student("李四", 4);
Student s3 = new Student("王五", 5);
Student[] s = {s1, s2, s3};
System.out.println(Arrays.toString());
for(Student stu : s) {
stu.play();
System.out.println(stu.age);
}
}
@Test
public void getFie() throw ClassNotFoundException {
//获取字节码对象
Class<?> clazz = Class.forName("com.review.Student");
//获取所有成员变量
Field[] fs = clazz.getFields();
//遍历数组,获取每个成员变量的具体信息
//注意!目前的成员变量必须是public修饰才能获取到
for(Field f : fs) {
System.out.println(f.getName());
System.out.println(f.getType());
}
}
}
练习:通过字节码对象获取类的成员方法
package com.reflection;
public class TestReflect {
@Test
public void getFunction() {
Class<?> clazz = Student.class;
Method[] ms = clazz.getMethods();
for(Method m : ms) {
System.out.println(m);
System.out.println(m.getName());
//通过方法对象获取方法所有参数的数组
Class<?>[] pt = m.getParameterTypes();
System.out.println(Arrays.toString(pt));
}
}
}
练习:通过字节码对象获取类的构造方法
package com.reflection;
public class TestReflect {
@Test
public void getCons() {
Class<?> clazz = new Student().getClass();
Constructors<?>[] cs = clazz.getConstructors();
for(Constructor c : cs){
System.out.println(c.getName());//打印本轮遍历到的构造方法的名字
Class[] pt = c.getParameterTypes();//获取构造函数的参数类型
System.out.println(Arrays.toString(pt));
}
}
}
练习:创建对象
package com.reflection;
public class TestReflect {
@Test
public void getObject() = throws Exception {
Class<?> clazz = Student.class;
/*反射创建对象方案1:通过触发目标类的无参构造创建对象*/
Object o = clazz.newInstance();
System.out.println(o);
/*反射创建对象方案2:通过触发目标类的全参构造创建对象
* 思路:
* 1.先获取指定的构造函数对象,注意需要指定构造函数的参数,传入的是.class字节码对象
* 2.通过刚刚获取到的构造函数对象创建Student目标类的对象,并且给对象的属性赋值
* */
//3.获取目标类中指定的全参构造
Constructor<?> c = clazz.getConstructor(String.class, int.class);
//4.通过获取到的构造函数:创建对象+给对象的属性赋值
Object o2 = c.newInstance("小六", 6);
System.out.println(o2);
}
}
暴力反射
指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。
getDeclaredAnnotation(Class annotationClass):A - Class
getDeclaredAnnotations():Annotation[] - Class
getDeclaredAnnotationsByType(Class annotationClass):A[] - Class
getDeclaredClasses():Class<?>[] - Class getDeclaredConstructor(Class<?>…parameterTypes):Constructor - Class
getDeclaredConstructors():Constructor<?>[] - Class getDeclaredField(String name):Field - Class getDeclaredFields():Field[] - Class getDeclaredMethod(String name,Class<?>…parameterTypes):Method - Class
getDeclaredMethods():Method[] - Class
getDeclaringClass():Class<?> - Class
创建:测试物料类
package com.review;
public class Person {
private String name;
private int age;
private void save(int n,String s) {
System.out.println("save()..."+n+s);
}
private void update() {
System.out.println("update()...");
}
}
练习:创建测试类
package com.reflection;
public class TestReflect{
@Test
public void getFie() throws Exception {
Class<?> clazz = Person.class;
Field field = clazz.getDeclaredField("name");
System.out.println(field);
System.out.println(field.getType().getName());
System.out.println(field.getType());
Object obj = clazz.newInstance();//触发无参构造利用反射创建对象
field.setAccessible(true);
field.set(obj,"海绵宝宝");
System.out.println(field.get(obj));
}
@Test
public void getFie1() throws Exception {
Class<?> clazz = Person.class;
Field fields = clazz.getDeclaredField("age");
System.out.println(fields.getType().getName());
Object obj = clazz.newInstance();
fields.setAccessible(true);
fields.set(obj,17);
System.out.println(fields.get(obj));
}
@Test
public void getFunction() throws Exception {
//1.获取Class字节码对象
Class<?> clazz = Person.class;
//2.通过暴力反射获取私有方法
/*getDeclaredMethod(m,x,y,z...)
* m:要获取的方法名
* x,y,z...可变参数,是这个方法的参数类型,但注意要加“.class”
* */
Method method = clazz.getDeclaredMethod("save" ,int.class ,String.class)
//没有对象就通过反射的方式创建对象
Object obj = clazz.newInstance();
//想要执行私有方法,也需要先设置私有可见
method.setAccessible(true);
/*invoke(o,x,y,z...),表示通过反射技术执行方法
* o :要执行的是哪个对象的方法
* x,y,z...:执行这个方法【method对象代表的之前获取到的save()】时需要传入的参数
* */
//通过反射技术invoke(),执行目标对象obj的目标方法method【save()】
//save()被调用时传入的参数是100,"反射成功啦~"
method.invoke(obj ,100 ,"反射成功啦~");
}
}