Class 类 在学习 java 反射之前,我们先来看看 Class类。我们知道所有的 class
的本质都是一个数据类型。而 JVM
在第一次读取到一种class
类型时会将其加载进内存。每加载一种class
,JVM都会为其创建一个Class
类型的实例,并关联起来。这里我们需要注意 Class 它只是一个名字比较特殊的class类 ,我们可以理解它长这个样子:
1 2 3 public final class Class { private Class() {} }
JVM为每个加载了的 class都创建了对应的Class实例,并且这个实例中保存了该class
的所有信息。也就是说,如果我们获取到了某个Class实例,我们就可以通过这个实例获取到对应的class所有信息。
如何获取到一个 class的 Class实例? 示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package test1; public class reflect { public static void main(String[] args) { Person p = new Person(); /*获取其Class实例处*/ } } class Person { private int age; private int getAge() { return age; } }
这里我们介绍三种:
一、直接通过一个class
的静态变量来获取 1 Class cls = Person.class;
二、通过一个实例对象来获取,getClass() 1 Class cls = p.getClass();
三、通过class的完整类名来获取,class.forName() 1 2 Class cls = Class.forName("test1.Person"); // 完整路径包括包名以及类名
因为Class 实例在JVM中是唯一的,所以上述方法获取到的Class实例都是相等的(同一个实例)。
利用 Class 实例来进行访问类字段 首先我们缕一缕思路,首先我们要获得这个Class实例,然后我们需要通过Class实例获得特定的field(字段),最后通过指定方法进行修改查看,我们实例化的对象(p)的该field。 获取这个Class实例我们已经知道了,那我们该如何获取字段呢?
通过Class实例获取到 field 这里有四种方法:
Field getField(name):根据字段名获取某个public的field(包括父类)
Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
Field[] getFields():获取所有public的field(包括父类)
Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
我一般使用最多的还是getDeclaredField(name)
因为它可以访问到private修饰的field。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package test1; import java.lang.reflect.Field; public class reflect { public static void main(String[] args) throws NoSuchFieldException{ Person p = new Person(); /*获取其Class实例处*/ Class cls = Person.class; Field age = cls.getDeclaredField("age"); System.out.print(age); } } class Person { private int age; private int getAge() { return age; } } // private int test1.Person.age
同时我们注意到,进行Field
赋值的时候我们需要导入一个java.lang.reflect.Field
包,并且使用getDeclaredField
我们需要捕获异常。
通过 Feild实例 来进行修改和查看字段 这里我们获取字段基本信息如:
getName():返回字段名称,例如,”name”;
getType():返回字段类型,也是一个Class实例,例如,String.class;
getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
但是我们用的更多的还是利用Feild实例来获取实例化的class值(这里的p)。 此时我们可以使用 get() 和set()方法。但是我们前面获取的是private修饰的 讲道理是不可以被我们调用的。但是我们利用反射,且在使用前加入age.setAccessible(true);
就可以访问。(当然,有办法设置为不能使用setAccessible)。那我们直接来看看代码吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package test1; import java.lang.reflect.Field; public class reflect { public static void main(String[] args) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException{ Person p = new Person(); /*主要代码*/ Class cls = Person.class; Field age = cls.getDeclaredField("age"); age.setAccessible(true); age.set(p, 20); Object r = age.get(p); System.out.print(r); } } class Person { private int age; private int getAge() { return age; } }
这里我们注意到,我们又throws一个异常一般我们根据idea或者eclipse自动添加捕获异常即可。
利用 Class 实例来调用类方法 想要调用类方法和访问字段类似,首先我们需要根据Class实例获取到 Method对象。
获取Method对象 类似得到field一样,我们也有四种方法:
Method getMethod(name, Class…):获取某个public的Method(包括父类)
Method getDeclaredMethod(name, Class…):获取当前类的某个Method(不包括父类)
Method[] getMethods():获取所有public的Method(包括父类) Method[]
getDeclaredMethods():获取当前类的所有Method(不包括父类)
这里我经常使用的是getDeclaredMethod()
这里我们需要注意的是,前2个函数中都有2个参数,其中name是必须的而 Class 是根据对应函数中的定义而设置的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package test1; import java.lang.reflect.Field; import java.lang.reflect.Method; public class reflect { public static void main(String[] args) throws NoSuchMethodException, SecurityException{ Person p = new Person(); /*主要代码*/ Class cls = Person.class; Method getAage = cls.getDeclaredMethod("getAge"); System.out.println(getAage); Method setAage = cls.getDeclaredMethod("setAge",int.class); System.out.println(setAage); } } class Person { private int age = 112; private void setAge(int age) { this.age =age; } private int getAge() { return age; } }
同样这里我们也可以根据Method对象获取到一些基本信息,如:
getName():返回方法名称,例如:”getScore”;
getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
但是,我们最想使用的还是调用指定函数
使用 invoke调用函数 和Field类似,对于非public方法,我们虽然可以通过Class.getDeclaredMethod()获取该方法实例,但直接对其调用将得到一个IllegalAccessException。为了调用非public方法,我们通过Method.setAccessible(true)允许其调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package test1; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class reflect { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ Person p = new Person(); /*主要代码*/ Class cls = Person.class; Method getAage = cls.getDeclaredMethod("getAge"); getAage.setAccessible(true); Object get = getAage.invoke(p); System.out.println(get); Method setAage = cls.getDeclaredMethod("setAge",int.class); setAage.setAccessible(true); setAage.invoke(p, 666); get = getAage.invoke(p); System.out.println(get); } } class Person { private int age = 112; private void setAge(int age) { this.age =age; } private int getAge() { return age; } } // 112 // 666
多态 这里顺便提一下,假如我们获取到一个父类的Class,然后getMethod作用于子类,且调用的方法子类有覆写。此时调用的是子类的方法。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package test1; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class reflect { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ Person1 p = new Student(); /*主要代码*/ Class cls = Person1.class; Method hello = cls.getDeclaredMethod("hello"); hello.setAccessible(true); hello.invoke(p); }} class Person1 { public void hello() { System.out.println("Person:hello"); } } class Student extends Person1 { public void hello() { System.out.println("Student:hello"); } } // Student:hello
调用构造方法 我们通常使用new
来创建实例,但是其实我们也可以利用反射来创建实例。
1 Person p = Person.class.newInstance();
这里使用Class.newInstance,也是有局限的,要求只能是无参数构造方法。如果是有参或者不是public就无法这样来调用。 为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象和Method非常类似,不同之处仅在于它是一个构造方法,并且,调用结果总是返回实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package test1; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class reflect { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InstantiationException, InvocationTargetException{ /*主要代码*/ Class cls = Person.class; Constructor cons = cls.getDeclaredConstructor(int.class); cons.setAccessible(true); Person p = (Person)cons.newInstance(20); System.out.print(p.age); } } class Person { public int age; private void name() { } Person(int age) { // TODO Auto-generated constructor stub this.age=age; } } // 20
好啦,这就先记到这里。
【参考链接】