反射体系是Java中最有代表性的体系之一,反射是所有Java框架的基础,没有反射体系的话就没有任何JavaEE的框架。
什么是反射?通俗的来讲就是对象的反向处理操作,就是根据已有的类的实例化对象来反推类的组成。一般情况下我们采用的实例化方式是:类名 对象名 = new 构造方法();这是正向操作。
我们现在来观察一下正反两种操作的例子
import java.util.Date;
public class Main{
public static void main(String[] args)throws Exception{
Date date = new Date();//正向操作
System.out.println(date);
Class<?> cls = Class.forName("java.util.Date");//反向操作
System.out.println(cls.newInstance());
}
}
这里有一个Class<?>,是返向操作(反射)的核心。反向操作是根据对象来取得对象的来源信息,对象的来源信息,主要集中在Class类里,现在我们来介绍一下Class类。
Class类:
首先我们要分清Class和class的区别:class是关键字,Class是一个类,这个类是描述类的类,有点绕口。
可以举一个通俗的例子:对象是由类来创建的,我们可以把对象想象成产品,类是生产产品的图纸,那么,Class类就是画图纸和管理图纸的地方。Class类产生的对象,可以理解成产生产品的图纸。Class类的对象不是由用户自己,而是由JVM来创建的。类类装载的时候,JVM会产生唯一的Class类对象,就是生产出唯一的产品图纸。举个例子,假设用户的程序用到了4个类,那么在程序运行的时候,Class类对象就只有4个,Class类对象的个数与类对象的个数没有关系,就像有很多产品但却只有一份图纸。
我们有三种方法可以拿到某个类的Class对象。
1:Class<?> cls = 已创建对象名.getclass();
public final native Class<?> getClass();
这是getclass()方法的源码,它是Object下的方法,意味着所有的类都可以通过此方式拿到其Class对象。此方法返回一个Class<?>对象,这个Class对象描述的就是调用此方法的对象的模板类。这个方法可以更好的体现反操作的本质:通过对象取得其来源的类。不过使用此方法的前提是必须要有一个已经创建好的对象。
2:Class<?> cls = Class.forName(“完整的类名”);
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
以上是此方法的源码,同样的,这个方法返回一个Class对象,这种方式的好处是,不需要类的实例化对象就可以拿到Class对象,此方法在我们以后的开发中是最常见到的,因为此方法可以从任何地方拿到Class对象,总之我们现在必须对这个方法有所掌握。
3:Class<?> cls = 类名称.class
这个好理解一点,相当于我们知道了类名程之后直接拿到Class对象,相当于直接赋值,在平时的编程中,这种方法也是最常用的一种。
现在我们拿到了Class对象,相当于我们已经拿到了产品的设计图纸,接下来,我们就要生产对象了。
当然,反射的重点根本就不是对象,而是对象的组成,我们可以通过反射拿到对象中的方法,属性,甚至我们可以对其成员无视权限的进行访问......
newInstance()方法
这个方法就是在我们取得Class对象后,实例化类的一个方法。我们先看一下源码。
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
此方法返回类型为T,我们一般用Object来接收,在有一些情况下,我们会把返回类型强转为其他的类型。
要注意的是,这个方法实例化对象的时候调用的是默认的无参构造。如果Class对象中有其他的构造方法,这个方法就会失灵,我们就要取得该Class对象的构造方法然后才能实例化对象。所以为了方便起见,一般我们的POJO中都会有一个由用户自己提供的一个无参构造方法。
反射体系与类
我们通过反射创建了类的对象,我们就可以通过反射来做出一个对象所能做出所有行为。
取得父类的信息
所有的类都是有父类的,我们拿到Class类对象之后就可以获得其实现的接口或者继承的父类。
interface Brother{ }
interface Consin{ }
class Father{ }
class SonClass extends Father implements Brother,Consin{ }
public class Main{
public static void main(String[] args){
Class<?> cls = SonClass.class;
System.out.println(cls.getSuperclass());
Class<?>[] classes = cls.getInterfaces();
for(Class<?> c : classes){
System.out.println(c);
}
}
}
因为实现的接口是可以有多个的,所以用数组来存放。
反射调用类的构造
一个类中的构造方法可以重载很多,所以如果我们要取得类中的构造方法,需要用到Class类中一些方法。
Constructor类:描述类中构造方法的类
getDeclareConstructor():取得类中指定构造,无视权限
@CallerSensitive
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.DECLARED);
}
这个方法可以取得指定参数的,任何权限的构造方法,我们可以发现此方法的参数都是Class对象,所以我们传参时的参数应该是:类型名.class。这里和包装类没什么关系,比如“int.class”指的其实是描述基本类型int的Class类对象。
getConstructors()取得类中所有public权限构造
@CallerSensitive
public Constructor<?>[] getConstructors() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(true));
}
这个方法返回的时Constructor<?>[]数组,它可以把一个类中的所有构造方法都返回存到一个数组里,这个方法只能把一个类中权限为public的构造方法返回,至于父类的构造方法,是无法获得的。
getDeclaredConstructors()取得类中所有构造,无视权限
@CallerSensitive
public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(false));
}
这个方法和getConstructors()差不多,唯一的区别就是它可以把类中的所有构造方法(无权限限制)全部返回。同样无法获得父类的构造方法。
我们拿到构造方法的时候,就要创建实例化对象了,这时候就要用到newInstance方法了,接下来看测试反射获取构造方法的代码的示范
import java.lang.reflect.Constructor;
class Person{
int ss;
public Person(){}
public Person(int ss){
this.ss = ss;
}
}
class Student extends Person{
private String name;
private int age;
public String grade;
public Student(){}
public Student(String name){
this.name = name;
}
protected Student(String name, int age){
this.name = name;
this.age = age;
}
private Student(String name, int age, String grade){
this.name = name;
this.age = age;
this.grade = grade;
}
@Override
public String toString() {
return this.name+this.age+this.grade;
}
}
public class Main {
public static void main(String[] args)throws Exception {
Class<?> cls4 = Student.class;
Constructor constructor = cls4.getDeclaredConstructor(String.class,int.class);//取得某种构造方法,无权限限制
Student student = (Student) constructor.newInstance("liu",20);//实例化对象
// System.out.println(student);
Constructor[] constructors = cls4.getConstructors();//拿到类中所有属性为public的构造,拿不到父类的
for(Constructor c : constructors){
System.out.println(c);
}
Constructor[] constructors1 = cls4.getDeclaredConstructors();//拿到类中所有构造,无权限限制,拿不到父类的
for(Constructor c1 : constructors1){
System.out.println(c1);
}
}
}
反射调用类的普通方法
既然我们可以通过反射调用构造方法,那么同样的,我们也可以调用普通方法,不过与调用构造方法不同的时,使用的类也不同了。我们在获取普通方法时用的是Method类,它用来描述类中的普通方法。
getMethods();取得本类以及父类中所有权限为public的普通方法
@CallerSensitive
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}
该方法返回Method[]数组,存放了本类以及父类中的所有权限为public的普通方法,存放在一个Method[]数组里,该方法优先返回本类中的方法,就是说数组前面的元素是本类的普通方法,后面的是父类中的普通方法。
getDeclaredMethods();取得本类中所有普通方法,无视权限
@CallerSensitive
public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyMethods(privateGetDeclaredMethods(false));
}
该方法和getMethods()差不多,只不过只会返回本类中所有普通方法,无视权限,相对于上面的方法,这个方法还是比较常用的。
getMethod(String name,class<?>...)
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
该方法会根据方法名以及参数列表取得指定的普通方法,权限为public,如果指定名称的方法在本类中没有,会继续查找父类。如果找到的话,就返回找到的方法。
getDclaredMethod(String name,class<?>...)
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
该方法只在本类中找指定名称和参数的普通方法,不在父类查找,查找的方法与权限无关。
调用拿到的普通方法(invoke(Object obj,Object...))
参数Obj:具体类的对象
method.invoke(类对象,方法的参数列表...);
调用获取到的普通方法时,必须要有类的对象,因为方法是通过对象调用的。如果调用的方法没有参数,那么就不用写参数列表那一部分了。
其实,还存在一个问题:如果拿到了private的方法,不就调用不了了么?我们通过反射可以强制破坏封装性,使private方法可见。
setAccessible()方法
public void setAccessible(boolean flag) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
setAccessible0(this, flag);
}
这里有一点要强调的是:此方法属于Object类的方法,就是说类的构造方法,普通方法,属性,都可以通过这个方法来动态设置封装或者破坏封装。此方法的参数是Boolean类型的,如果要把构造方法,普通方法或者属性的破坏性破坏掉,只需要传入true就可以了,这样我们就可以调用private方法了。不过,并不是说private被改成public了,权限并没有改变,只是对外可见了。同理,如果我们想把public的成员变为不可见,就传入false。然而,这种特性只能维持在当前的JVM的进程中。这是反射很强大的一点了。
以下是我演示反射调用普通方法的代码
import java.lang.reflect.Method;
class Person{
public Person(){}
public void PersonMethod_A(){
System.out.println("This is PersonMethod_A");
}
private void PersonMethod_B(){
System.out.println("This is PersonMethod_B");
}
protected void PersonMethod_C(){
System.out.println("This is PersonMethod_C");
}
}
class Student extends Person{
private String name;
public int age;
public Student(){}//一般来说POJO需要自己设置一个无参构造
//因为newinstance()默认使用无参构造
public void Method_A(){
System.out.println("This is Studebt's Method_A");
}
public void Method_B(){
System.out.println("This is Studebt's Method_B");
}
private void Method_C(){
System.out.println("This is Studebt's Method_C");
}
protected void Method_D(){
System.out.println("This is Studebt's Method_D");
}
public void Method_Print(String name){
this.name = name;
System.out.println(this.name);
}
@Override
public String toString() {
return this.name+this.age;
}
}
public class Main {
public static void main(String[] args) throws Exception {
Class<?> cls = Student.class;
Method[] methods = cls.getMethods();//取得本类以及父类中所有权限为public的方法,优先搜索本类
for (Method m : methods) {
System.out.println(m);
}
Method[] methods1 = cls.getDeclaredMethods();//拿到本类中所有普通方法,无关权限
for (Method m1 : methods1) {
System.out.println(m1);
}
Object object = cls.newInstance();//方法的调用必须要有类对象
Method method = cls.getMethod("Method_Print", String.class);//取得本类或者父类的public普通方法,可能会找不到,要抛异常
method.invoke(object, "调用方法设置的名字");//invoke方法的第一个参数是类的实例化对象,接下来的参数列表是方法要参数
Method method1 = cls.getDeclaredMethod("Method_B");///取得本类的普通方法,无关权限
method1.invoke(object);//因为取得的方法无参数,所以直接是对象调用
Method method2 = cls.getDeclaredMethod("Method_C");//使用此方法拿到了一个privata方法
method2.setAccessible(true);//在一次JVM进程中暂时破环封装,是Object的方法,意味着构造,普通方法,属性的封装性都可以被破坏
System.out.println(method2);//查看此方法,仍然是private
method2.invoke(object);//调用,发现调用没问题
}
}
反射获取类的属性
我们获取Class对象后,也可以获取其中的属性,获取的方法,和获取普通方法差不多。描述类中属性的类是Field类。获取属性的方法主要是以下四种
getFields();取得所有权限为public的属性,先查找本类再查找父类
@CallerSensitive
public Field[] getFields() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyFields(privateGetPublicFields(null));
}
此方法返回Field[]数组,该方法会拿到本类和父类中所有权限为public的属性,并把它存放在数组中返回,查找顺序是先本类再父类。
getDeclaredFields();取得本类中所有属性,无关权限
@CallerSensitive
public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyFields(privateGetDeclaredFields(false));
}
该方法同样返回Field数组,不过该方法不会返回父类的属性,但是会返回本类的所有权限的属性。
getField(String name):查找指定名称的属性,权限为public
@CallerSensitive
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Field field = getField0(name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
我们可以通过此方法来查找属性,方法的参数就是属性的名称,查找范围是本类以及父类所有public权限的属性,先查找本类,再查找父类。
getDclaredMethod(String name):查找本类中指定名字的属性,无关权限
@CallerSensitive
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Field field = searchFields(privateGetDeclaredFields(false), name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
按照属性名称来查找,查找范围是本类的所有权限的属性。不查找父类的属性。
field.set()与field.get()方法
在我们找到属性后,我们可以对它进行设置。设置使用set(Object obj,Object...)方法,第一个参数obj是实例化的类对象,后面的是要设置的属性值。get(Object obj)返回obj对象的属性。两个方法都通过 取得的属性.get()/set()来调用。
如果我们想改变属性的封装性,可以直接采用属性.setAccessible(Boolean Flag)方法来改变封装性即可。
有关反射调用属性的演示代码如下
import java.lang.reflect.Field;
class Person{
public String Person_field1;
protected String Person_field2;
private String Person_field3;
}
class Student extends Person{
public String field1;
protected String field2;
private String field3;
public void Method_A(){
System.out.println(this.field1+this.field2+this.field3);
}
}
public class Main{
public static void main(String[] args)throws Exception{
Class<?> cls = Student.class;
Object object = cls.newInstance();
Field[] fields = cls.getFields();//取得本类以及父类的public属性
// for(Field f : fields){
// System.out.println(f);
// }
Field[] fields1 = cls.getDeclaredFields();//取得本类中所有属性,不限权限
// for(Field f1 : fields1){
// System.out.println(f1);
// }
Field field = cls.getField("Person_field1");//按名字取得本类或者父类的public属性
// System.out.println(field);
Field field1 = cls.getDeclaredField("field3");//按名字取得本类中的属性,不限权限
// System.out.println(field1);
field1.setAccessible(true);//因为setAccessible()方法是Object的类方法,所以在此JVM进程中破坏field的封装性
System.out.println(field1);//发现field1还是privata的属性
field1.set(object,"被改变的private属性");//属性设置,第一个参数是实例化的对象,第二个是要设置的值
System.out.println(field1.get(object));//属性的值获取,参数是实例化的对象
}
}
有关反射的知识点就先总结到这里,如果有错误还请大家指出。