简介
反射:将类的各个组成部分封装为其他对象。结合下面的图解释就是将类的成员方法、成员属性、构造方法分别封装成Method数组、Field数组、Constructor数组。
百度百科对反射的定义:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
那么,如何“知道这个类的所有属性和方法、调用对象的任意方法和属性”?
获取到Class类对象即可。类的成员方法、成员属性、构造方法通过类加载器加载进内存分别封装成Method数组、Field数组、Constructor数组,这些属性、成员方法、构造方法可以在程序运行期间通过下面将要介绍的很多API进行操作。
反射的优点:
- 可以在程序运行过程中,操作这些对象
- 可以解耦,提高程序的可扩展性
获取Class对象的三种方式
下面的三种方式分别对应上图Java代码在计算机中经历的三个阶段。
-
Class.forName(“全类名”)【将字节码文件加载进内存,返回Class对象 】
第一阶段,java代码还未进入内存,则需要手动通过Class.forName("全类名")
的方式将字节码文件加载进内存。
多用于配置文件,将类名定义在配置文件中,读取文件,加载类 -
类名.class
第二阶段,此时内存中已存在Class类对象,可以直接通过类名.class属性获取Class对象。
多用于参数的传递 -
对象.getClass()
所有的类都默认继承了Object类,Object类中有个getClass()方法,通过该方法可以获取Class类对象。
多用于通过对象获取字节码的方式
【注意】同一个字节码文件在一次程序运行过程中,无论通过哪一种方式获取的Class对象,只会被加载一次。
示例和API
获取字节码的三种方式示例
Class class1 = Class.forName("cn.ith.demo.Person");
System.out.println(class1);
Class class2 = Person.class;
System.out.println(class2);
Person p = new Person();
Class class3 = p.getClass();
System.out.println(class3);
//验证 同一个字节码文件(*.class)在一次程序运行过程中,不论通过哪一种方式获取的Class对象,只会被加载一次
System.out.println(class1 == class2);
System.out.println(class2 == class3);
System.out.println(class2 == class3);
结果:
class demo1.test.domain.Person
class demo1.test.domain.Person
class demo1.test.domain.Person
true
true
true
获取成员变量的API
- Field[] getFields():获取所有public修饰的成员变量
- Field getField(String name):获取指定的public修饰的成员变量
- Field[] getDeclaredFields():获取所有成员变量
- Field getDeclaredField(String name):获取指定名称的成员变量
通过getDeclaredFields()获取到的所有成员变量数组元素顺序不代表属性在对象中的代码位置,即name属性在Person类中的第一个属性位置,但是Field[]数组的第一号元素不一定是name属性
public class Person {
private String name;
private int age;
public String a;
protected String b;
String c;
private String d;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("eat");
}
private void drink(String things) {
System.out.println("drink "+things);
}
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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
}
@Test
public void classObjectFun1() throws NoSuchFieldException, IllegalAccessException {
Class perCls = Person.class;
//获取类名
String name = perCls.getName();
System.out.println(name);
//获取所有public修饰的成员变量
Field[] field = perCls.getFields();
for(Field item: field) {
System.out.println(item);
}
System.out.println("--------------------");
//获取指定的public修饰的成员变量
Field a = perCls.getField("a");
System.out.println(a);
System.out.println("++++++++++++++++++++");
Person p = new Person();
//获取成员变量的值
Object aValue = a.get(p);
System.out.println(aValue);
//设置成员变量的值
a.set(p, "zs");
Object aValue2 = a.get(p);
System.out.println(aValue2);
System.out.println("**********************");
//获取私有成员变量
Field d = perCls.getDeclaredField("d");
System.out.println(d);
System.out.println("//////////////////////");
//忽略访问权限修饰符的安全检查,否则无法访问非public的属性值
d.setAccessible(true); //暴力反射
Object dValue = d.get(p);
System.out.println(dValue);
d.set(p, "aa");
Object dValue2 = d.get(p);
System.out.println(dValue2);
}
结果:
demo1.test.domain.Person
public java.lang.String demo1.test.domain.Person.a
--------------------
public java.lang.String demo1.test.domain.Person.a
++++++++++++++++++++
null
zs
**********************
private java.lang.String demo1.test.domain.Person.d
//////////////////////
null
aa
Process finished with exit code 0
获取构造方法的API
- Constructor<?>[] getConstructors():获取所有公共构造方法
- Constructor getConstructor(类<?>… parameterTypes):获取某个公共构造方法
- Constructor getDeclaredConstructor(类<?>… parameterTypes):获取所有构造方法
- Constructor<?>[] getDeclaredConstructors():获取某个构造方法
@Test
public void classObjectFun2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class perCls = Person.class;
//获取所有的构造方法
Constructor[] constructors = perCls.getConstructors();
for(Constructor item: constructors) {
System.out.println(item);
}
System.out.println("--------------------");
//获取某个构造方法
Constructor constructor = perCls.getConstructor(String.class, int.class);
System.out.println(constructor);
System.out.println("++++++++++++++++++++");
//用构造器创建对象
Object person = constructor.newInstance("zs", 23);
System.out.println(person);
//获取某个构造方法 根据构造器创建对象
Constructor constructor2 = perCls.getConstructor();
Object person2 = constructor2.newInstance();
System.out.println(person2);
//调用Class类的newInstance()方法创建对象
Object person3 = perCls.newInstance();
System.out.println(person3);
}
结果:
public demo1.test.domain.Person()
public demo1.test.domain.Person(java.lang.String,int)
--------------------
public demo1.test.domain.Person(java.lang.String,int)
++++++++++++++++++++
Person{name='zs', age=23, a='null', b='null', c='null', d='null'}
Person{name='null', age=0, a='null', b='null', c='null', d='null'}
Person{name='null', age=0, a='null', b='null', c='null', d='null'}
获取成员方法的API
- Method[] getMethods():获取所有public修饰的成员方法
- Method getMethod(String name, 类<?>… parameterTypes):获取指定public修饰的成员方法
- Method[] getDeclaredMethods():获取所有成员方法
- Method getDeclaredMethod(String name, 类<?>… parameterTypes):获取指定成员方法
public void classObjectFun3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class perCls = Person.class;
//获取方法
Method eat = perCls.getMethod("eat");
//执行方法
Person person = new Person();
eat.invoke(person);
Method drink = perCls.getDeclaredMethod("drink", String.class);
drink.setAccessible(true); //暴力反射,否则无法调用非public方法
//执行方法
Person person2 = new Person();
drink.invoke(person2, "water");
//获取方法名称
String name = drink.getName();
System.out.println(name);
}
结果:
eat
drink water
drink
其他API
- 获取类名
String getName():class.getName() - 获取方法名称
String getName():method.getName()
实例
下面利用反射机制和API实现这样一个功能:对于一个包含全限定类名和方法名的配置文件,能够实例化该类并执行其方法。
pro.properties:
className=demo1.test.domain.Person
methodName=eat
public class ReflectDemo2 {
//可以创建任意类对象,可以执行任意方法
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1. 加载配置文件
//1.1 创建Properties对象
Properties pro = new Properties();
//1.2 加载配置文件,转换为一个集合
//获取ReflectDemo2类对应的类加载器,通过类加载器可以获取该类所在包的根路径下的文件
ClassLoader classLoader = ReflectDemo2.class.getClassLoader();
//方式一:返回资源的路径并加载资源
// URL resourcePath = classLoader.getResource("pro.properties");
// String path = resourcePath.getPath();
// pro.load(new FileReader(path));
//方式二:返回资源的字节流并加载资源
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2. 获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3. 获取类对象
Class cls = Class.forName(className);
//4. 创建对象
Object obj = cls.newInstance();
//5. 获取方法
Method method = cls.getMethod(methodName);
method.invoke(obj);
}
}