反射,一直是觉得一个比较高级而且非常核心的技术,很多框架和工具都有用到反射,今天简单的总结一下反射常用的方法.
1.反射的概念
java的方式机制是在运行状态中,对任意一个类,都能够知道这个类的属性和方法,对于任意一个对象,都能调用它的任意一个方法,这种动态的获取以及动态的调用对象的方法的功能成为java语言的反射机制.
2.字节码对象
在类加载后,会在堆内存中生成一个Class类对象,我们通过这个对象就可以字节码对象类型的和属性等.
3.获取字节码对象的方法
①Class.getForName(“类的全类名”)
②类名.class
③对象.getClass()
为了演示后面的方法,我们先上一些代码:
package domain;
public class Person{
private String name;
public Integer age;
public Person() {
super();
}
private Person(String name) {
super();
this.name = name;
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
private void speak(){
System.out.println("人讲话!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
下面演示一下如何获取字节码对象:
/**
* 演示获取类的字节码对象的3种方法
* @throws ClassNotFoundException
*/
@Test
public void testGetClass() throws ClassNotFoundException{
Person person = new Person();
//通过Class.getForName(“类的全类名”)获取字节码对象
Class clazz = Class.forName("domain.Person");
//通过 类名.class
Class clazz2 = Person.class;
//通过对象.getClass()
Class clazz3 = person.getClass();
System.out.println(clazz); //class domain.Person
System.out.println(clazz2 == clazz); //true
System.out.println(clazz3 == clazz); //true
}
4.获取类的构造方法并创造对象
我们可以通过获取到的Class类的对象获取构造类(Constructor)的实例,具体方法如下:
Constructor<?>[] getConstructs() 返回clazz对象所表示类的所有公共有参方法;
Constructor<?>[] getDeclaredConstructors()
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
Constructor<T> getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
T newInstance() 创建此 Class 对象所表示的类的一个新实例。(使用此方法必须原对象有公共的无参构造方法)
然后在使用Constructor类的实例创造Pserson类的对象,涉及到的方法如下:
T newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
下面演示一下如何获取构造方法,并创造对象:
public class RefectDemo {
public static void main(String[] args) throws Exception {
testConstruct();
}
/**
* 获取构造方法
* @throws Exception
* @throws NoSuchMethodException
*/
private static void testConstruct() throws NoSuchMethodException, Exception {
Class<Person> clazz = Person.class;
//获取所有公共构造
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
//public domain.Person(java.lang.String,java.lang.Integer)
//public domain.Person()
}
//获取所有的构造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.println(constructor);
//public domain.Person(java.lang.String,java.lang.Integer)
//private domain.Person(java.lang.String)
//public domain.Person()
}
//通过Class对象直接创建Person类的对象(此方法要求Person类要有公共的空参构造方法)
Person person1 = (Person) clazz.newInstance();
System.out.println(person1); //Person [name=null, age=null]
//获取指定的公共有参构造,并创建person类的实例对象
Constructor<Person> constructor = clazz.getConstructor(String.class,Integer.class);
Person person2 = constructor.newInstance("张三",23);
System.out.println(person2); //Person [name=张三, age=23]
//获取指定的有参构造,并创建Person类的实例(此方法公共和私有的构造都能获取)
Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true); //设置反射的对象在使用时取消 Java 语言访问检查
Person person3 = declaredConstructor.newInstance("张三");
System.out.println(person3); //Person [name=张三, age=null]
}
}
在上面的这段代码中用到了一个setAccessible(boolean flag)的方法,此方法是Constructor类的父类AccessibleObject的方法(它的子类还有Methd类和Field类),作用是:使用单一安全性检查(为了提高效率)为一组对象设置 accessible 标志的便捷方法。说白了就是设置为ture的时候,不检查访问权限.
4.获取成员变量 我们也可以通过Class对象直接获取Person类的成员变量,常用的方法有这些:
Field[] getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
Field[] getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
Field getDeclaredField(String name) 返回一个Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
Field getField(String name) 返回一个Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
然后可以通过获得的Field对象设置和指定对象的值:
void set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。
下面用代码简单的演示:
public class RefectDemo {
public static void main(String[] args) throws Exception {
testField();
}
/**
* 获取成员变量
*/
private static void testField() throws Exception {
//获取所有公共的字段
Class<Person> clazz = Person.class;
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
//public java.lang.Integer domain.Person.age
}
//获取所有字段
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
//private java.lang.String domain.Person.name public java.lang.Integer domain.Person.age
}
//获取指定公共字段,并设置字段的值
Person person = clazz.newInstance();
Field field = clazz.getField("age");
field.set(person, 23);
System.out.println(person); //Person [name=null, age=23]
System.out.println(field.get(person)); //23
//获取指定字段,并设置值
Field declaredField = clazz.getDeclaredField("name");
declaredField.setAccessible(true);
declaredField.set(person, "张三");
System.out.println(person); //Person [name=张三, age=23]
System.out.println(declaredField.get(person)); //张三
}
5.获取成员方法
我们也可以通过Class对象直接获取Person类的成员方法,常用的方法如下:
Method getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[] getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
Method[] getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
然后通过获得的Method对象来执行对应的方法:
Object invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
6.总结本文演示了反射的基本操作,比如泛型类型的获取等并没有演示,如需要深入学习可以看看这篇文章:https://blog.csdn.net/xiangnan10/article/details/80403179,动态代理的底层实现其实也可以是反射,下篇文章将分析下继承,装饰者模式和动态代理.