JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
以下主要介绍通过反射获取私有的和公共的构造方法、成员变量、方法;
Class类:
Class 类的实例对象表示正在运行的 Java 应用程序中的类和接口;也就是jvm中有很多的实例,每个类都有唯一的字节码对象(Class对象);通过唯一的字节码对象就可以反射任意一个类中的构造,成员变量和方法;
Class 类没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机自动构造的;我们不需要创建,JVM已经帮我们创建了;
Class 对象用于提供类本身的信息,比如有几种构造方法, 有多少属性,有哪些普通方法;
三种方式获取的字节码对象是同一个;
private static void demo1() throws ClassNotFoundException {
//第一种
Class clazz = Class.forName("com.ang.Teacher");
//第二种
Class clazz2 = Teacher.class;
Teacher t = new Teacher();
//第三种
Class clazz3 = t.getClass();
System.out.println(clazz == clazz2);
System.out.println(clazz2 == clazz3);
}
被反射的对象:根据具体情况修改构造类型(私有的:private和公共的:public),各种情况都有注释;
public class Teacher {
private String name;
private int age;
// private Teacher(){
//
// }
public Teacher(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Teacher [name=" + name + ", age=" + age + "]";
}
private int add(int a,int b){
return a+b;
}
}
获取构造方法步骤
1,首先获取要反射类的字节码对象(Class)
2,通过字节码对象获取有参或无参构造器Constructor
private static void demo2() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class clazz = Class.forName("com.ang.Teacher");
//通过Teacher空参构造创建对象,如果没用空参构造就会报错;而且必须是public类型的空参构造方法;
//Teacher t = (Teacher) clazz.newInstance(); //如果没有空参构造或者空参构造是私有的不能使用此方法获取对象
//通过有参构造获取对象,有参构造必须是public类型的
Constructor c = clazz.getConstructor(String.class,int.class); //获取有参构造器
Teacher t = (Teacher) c.newInstance("李四",50);//通过有参构造创建对象
System.out.println(t);
}
private static void demo3() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getConstructor();//获取public类型的空参构造
Teacher t = (Teacher) c.newInstance();//通过空参构造获取对象
t.setAge(40);
System.out.println(t);
}
private static void demo6() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class> clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getDeclaredConstructor(String.class,int.class);//获取私有有参构造
c.setAccessible(true);//去除私有权限
Teacher t = (Teacher) c.newInstance("小高",50);
System.out.println(t);
}
步骤
1,首先获取要反射类的字节码对象(Class)
2,通过字节码对象的newInstanc()方法获取实例对象;(注意:需要类的无参构造是public或者默认生成的)
//通过空参构造获取对象
private static void demo5() throws ClassNotFoundException,
InstantiationException, IllegalAccessException {
Class> clazz = Class.forName("com.ang.Teacher");
//空参构造必须是public类型的
Teacher t = (Teacher) clazz.newInstance();
t.setName("赵敏");
System.out.println(t);
}
private static void demo6() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class> clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getDeclaredConstructor(); //获取私有构造,方法参数是可变的,所以有参构造直接传入参数类型的字节码对象;
c.setAccessible(true); //去除私有权限
Teacher t = (Teacher) c.newInstance();
t.setAge(100);
System.out.println(t);
}
步骤
1,首先获取要反射类的字节码对象(Class)
2,根据反射创建实例
3,通过字节码对象中的getField()或者getDeclaredField()方法获取Field(提供有关类或接口的单个成员变量的信息)
4,通过Field就可以修改和使用成员变量了
private static void demo4() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException,
NoSuchFieldException {
Class> clazz = Class.forName("com.ang.Teacher");
//通过有参构造获取对象,有参构造必须为public类型
Constructor c = clazz.getConstructor(String.class,int.class);//字节码阶段,构造方法的参数只能是字节码对象;
Teacher t = (Teacher) c.newInstance("王艳",100); //创建实例对象
Field field = clazz.getDeclaredField("name");//获取私有成员变量
field.setAccessible(true);//去除私有权限
field.set(t, "李代理"); //修改私有成员变量的值
System.out.println(t);
}
private static void demo7() throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Class> clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getDeclaredConstructor(); //获取私有无参构造
c.setAccessible(true); //去除私有权限
Teacher t = (Teacher) c.newInstance();//通过私有构造获取实例
Method method = clazz.getDeclaredMethod("add", int.class,int.class);//字节码阶段反射有参方法是,需传入参数类型的字节码
method.setAccessible(true); //去除私有权限
int num = (Integer) method.invoke(t, 20,8);
System.out.println(num);
}
反射常用的几个类:
1,Class:反射的入口,类的实例表示正在运行的 Java 应用程序中的类和接口。
2,Constructor :提供关于类的单个构造方法的信息以及对它的访问权限。
3,Field :提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
4,Method :提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
1,通过反射越过泛型检查
private static void demo() throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
List list = new ArrayList();
list.add(11);
list.add(22);
Class clazz = list.getClass();
Method m = clazz.getMethod("add", Object.class);
m.invoke(list, "哈喽");
System.out.println(list);
}
打印输出:
[11, 22, 哈喽] //List泛型指定的是Integer,结果通过反射存入了String
2,通过反射写一个通用的设置某个对象的某个属性为指定的值
package com.ang;
import java.lang.reflect.Field;
public class Tool {
public void setProperty(Object obj,String propertyName,Object values) throws Exception{
Class clazz = obj.getClass();
Field f = clazz.getDeclaredField(propertyName); //暴力反射获取属性
f.setAccessible(true); //去除私有权限
f.set(obj, values); //修改属性值
}
}
private static void demos() throws Exception {
Student s = new Student("张无忌", 40);
System.out.println(s);
Tool t = new Tool();
t.setProperty(s, "name", "赵敏");
System.out.println(s);
}
打印输出:可以看到,把同一个对象中的属性值修改了
Student [name=张无忌, age=40]
Student [name=赵敏, age=40]
package com.ang;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3, 写一个Properties格式的配置文件,配置指定类的完整名称。再写一个程序,读取这个Properties配置文件,获得类的完整名称并加载这个类,用反射的方式运行run方法。
指定的类:
package com.ang;
public class Animal {
public void run(){
System.out.println("欢迎来到动物世界。。。");
}
}
properties:name.properties配置文件内容
com.ang.Animal
测试类:
public class Test {
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new FileReader("name.properties"));
Class clz = Class.forName(br.readLine());
Animal a= (Animal) clz.newInstance();
a.run();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
欢迎来到动物世界。。。