在文章开头,我们先看看反射是什么?
- Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
- 可以简单理解为,在运行状态下,控制台输入一个(全)类名就可以得到一个该类对象,或者获取到该类的信息
- 可以实现类的动态加载
- 对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。
- 而这也是Java被视为动态或准动态语言的一个关键性质。(为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)
既然反射这么nb,那么Java是怎么实现反射的呢?答:依靠Class对象。
Java有两种对象,第一种是实例对象,第二种是Class对象,每一个类运行的类型信息就是用Class对象表示的,每一个对象都有一个到 java.lang.Class(用于描述对象的结构)的实例的引用。
Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
1.如何获取Class对象?
Class对象有三种获取方式:
- Class.forName(“全类名”)
- 将字节码文件加载进内存,返回Class对象。
- 多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
- 类名.class
- 通过类名的属性class获取。
- 多用于参数的传递。
- 对象.getClass()
- getclass方法在object类中定义。
- 多用与对象的获取字节码的方式。
这里再强调一次,同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,所以无论通过哪一种方式获取的class对象都是一个。三种方式的代码示例如下:
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
// 1.通过Class.forName获取Class对象
Class aClass = Class.forName("com.xupt.yzh.entity.Persion");
// 2.通过类名.class获取Class对象
Class bClass = Persion.class;
// 3.通过对象.getClass获取Class对象
Persion persion = new Persion();
Class cClass = persion.getClass();
Persion persion1= new Persion();
Class dClass = persion.getClass();
persion1.getClass();
System.out.println(persion==persion1); // false
System.out.println(aClass==bClass); // true
System.out.println(aClass==dClass); // true
System.out.println(aClass==cClass); // true
System.out.println(cClass==dClass); // true
}
}
2.Class对象有什么用?
Class中的方法有很多,但是最常用的就三种:获取字段(Filed对象),获取构造器(Constructor对象),获取方法(Method对象)。下面就通过代码示例来逐个说明…
// Person的Class对象是后面示例的主角
public class Person {
public String name;
private int age;
// 两个构造方法,带参和空参
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
// get、set
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;
}
// 两个额外方法
public void eat(){
System.out.println("eat....");
}
public void hello(String a){
System.out.println("say"+ a);
}
}
可以看到Person有两个成员变量,还有一个是private私有的,但是在反射面前,没有什么私有共有之分,向下看!
3.1 获取Filed,然后设置值
// Class获取Filed对象:
Field[] getFields() // 获取public的所有成员变量
Field getField(String name) // 根据属性名获取指定Field
Field[] getDeclaredFields() // 获取private的所有成员变量
Field getDeclaredField(String name) // 根据属性名获取指定private的Field
---------------------------------------------------------------------
// Filed的核心方法:
Object get(Object obj) // 获取指定对象(obj)当前Filed的值
void set(Object obj, Object value) // 设置指定对象(obj)当前Filed为value
void setAccessible(boolean flag) // 在对private变量操作前,必须设置Accessible为true(该方法在父类AccessibleObject中)
示例代码如下:
public class TestField {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{
// 通过类名拿到Class对象
Class p = Person.class;
// Class#getFields:获取public 字段Filed[]
Field[] fields = p.getFields();
for (Field f:fields) {
System.out.println(f);
}
System.out.println("-------------------------");
// Class#getField(name): 获取指定字段Field
Field field = p.getField("name");
Person person = new Person();
// Field#get(Obj):获取指定对象的当前字段值
Object name = field.get(person);
System.out.println(name);
// Field#set(obj,value):设置一个对象的当前字段值
field.set(person,"zhangsan");
System.out.println(person);
// Class#getDeclaredFields:获取所有成员变量,不考虑修饰符Field[]
Field[] declaredFields = p.getDeclaredFields();
for (Field field1:declaredFields) {
System.out.println(field1);
}
System.out.println("-------------------------");
// Class#getDeclaredField(name):获取指定private字段Field
Field field1 = p.getDeclaredField("age");
// Filed#setAccessible:暴力反射,在对private字段操作前必须设置Accessible为true
field1.setAccessible(true);
Person person1 =new Person();
Object o = field1.get(person1);
System.out.println(o);
}
}
运行结果:
3.2 获取Constructor ,然后构造新对象
// Class获取Constructor对象
Constructor<?>[] getConstructors() // 获取所有public构造器
Constructor<T> getConstructor(Class<?>... parameterTypes) // 根据参数返回public构造器
Constructor<?>[] getDeclaredConstructors() // 获取所有private构造器
Constructor<?>[] getDeclaredConstructors() // 根据参数返回private构造器
T newInstance() // 通过空参构造创建一个对象(注:类中必须要有空参构造)
------------------------------------------------------------------------------------------------------
// Constructor的核心方法:
T newInstance(Object ... initargs) // 通过Constructor构造一个对象(注:可变参数,所以可以空参构造时不用传参数)
示例代码:
public class TestConstructor{
public static void main(String[] args) throws Exception {
// 通过对象获取Class对象
Class personClass = Person.class;
// Class#getConstructor(Class paramType...):获取指定构造器Constructor,要传入参数类型的Class对象
Constructor constructor = personClass.getConstructor(String.class, int.class);
System.out.println(constructor);
// Constructor#newInstance(param):用构造方法创建一个对象
Object asd = constructor.newInstance("asd", 10);
System.out.println(asd);
// Class#newInstance:空参构造方法创建对象
Object o = personClass.newInstance();
System.out.println(o);
}
}
运行结果:
3.3 获取Method,然后执行
// Class获取Method对象:
Method[] getMethods() // 获取所有public方法
Method getMethod(String name, Class<?>... parameterTypes) // 根据方法名和类型获取public方法(注:只传入类型或方法名并不能唯一确定一个参数)
Method[] getDeclaredMethods() // 获取所有private方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 根据方法名和类型获取private方法
------------------------------------------------------------------------------------------
// Method的核心方法:
Object invoke(Object obj, Object... args) // 执行方法,传入执行的对象(obj)和参数(可变参数),返回执行结果
示例代码:
public class TestMethod{
public static void main(String[] args) throws Exception {
// 获取Class对象
Class<Person> personClass = Person.class;
// Class#getMethod(name):同过方法名获取方法Method
Method method = personClass.getMethod("eat");
Person person = new Person();
// Method#invoke(obj):执行方法
method.invoke(person);
System.out.println("----------------");
Method hello = personClass.getMethod("hello", String.class);
// Method#invoke(obj,param):执行方法,并传入参数
hello.invoke(person,"hello");
}
}
运行结果: