反射
反射即Reflection
,指程序可以在运行期间拿到一个对象的所有信息,存在的意义是为了解决在运行时对实例不了解的情况下,如何调用其方法;
Class类
-
class
(包括interface
)的本质是数据类型Type
; -
获取一个类
class
的Class
实例的三种方法:-
直接通过一个类
class
的静态变量class
获取;Class cls = String.class;
-
若有一个实例变量,则可以通过该实例变量提供的
getClass()
方法获取;String s = "cunyu"; Class cls = s.getClass();
-
若知道一个类
class
的完整类名,可以通过静态方法Class.forName()
获取;Class cls = Class.forName("java.lang.String");
-
-
动态加载
JVM
执行Java
程序时,并非一次性将所有类class
全部加载到内存中,而是需要用到类class
时才加载;
访问字段
-
Class
类提供的获取字段的方法:-
Field getField(name)
:根据字段名获取某个public
的field
(包括父类); -
Field getDeclaredField(name)
:根据字段名获取当前类的某个field
(不含父类); -
Field[] getFields()
:获取所有public
的field
(包括父类); -
Field[] getDeclaredFields()
:获取当前类的所有field
(不含父类);public class Main{ public static void main(String[] args) throws Exception{ // 通过Student类的静态变量获取Class实例 Class stdClass = Student.class; // 获取public字段"score" System.out.println(stdClass.getField("score")); // 获取继承的public字段"name" System.out.println(stdClass.getField("name")); // 获取private字段"grade" System.out.println(stdClass.getDeclaredField("grade")); } } class Student extends Person{ public int score; private int grade; } class Person{ public String name; }
-
-
利用反射获取类的字段信息;
public final class String{ private final byte[] value; } // 通过反射来获取String的字段的信息 Field f = String.class.getDeclaredField("value"); // 获取字段名 f.getName(); // 获取字段类型 f.getType(); // 获取字段修饰符 int m = f.getModifiers(); Modifier.isFinal(m); // true Modifier.isPublic(m); // false Modifier.isProtected(m); // false Modifier.isPrivate(m); // true Modifier.isStatic(m); // false
-
通过
Field
实例可以读取(get(Object)
)或设置(set(Object, Object)
)某个对象的字段,若存在访问限制,则需要先调用setAccessible(true)
来访问public
字段;
调用方法
-
通过
Class
实例获取所有Method
信息的方法:-
Method getMethod(name, Class...)
:获取某public
的Method
(包括父类); -
Method getDeclaredMethod(name, Class...)
:获取当前类的某一方法(不含父类); -
Method getMethods(name, Class...)
:获取所有public
的Method
(包括父类); -
Method getDeclaredMethods(name, Class...)
:获取当前类所有Method
(不含父类);public class Main{ public static void main(String[] args) throws Exception{ Class stdClass = Student.class; // 获取包括父类在内的public方法getScore,参数为String System.out.println(stdClass.getMethod("getScore", String.class)); // 获取继承的public方法getName,无参数 System.out.println(stdClass.getMethod("getName")); // 获取private方法getGrade,参数为int System.out.println(stdClass.getDeclaredMethod("getGrade()", int.class)); } } class Student extends Person{ public int getScore(String type){ return 100; } private int getGrade(int year){ return 1; } } class Person{ public int getName(){ return "Person"; } }
-
-
一个
Method
对象包含一个方法的所有信息:getName()
:返回方法名称;getReturnType()
:返回方法返回值类型,是一个Class
实例;getParameterTypes()
:返回方法参数类型,是一个Class
数组;getModifiers()
:返回方法修饰符;
-
PS:使用反射调用方法时,仍然遵循多态的原则:即总是调用实际类型的重写方法(如果存在);
调用构造方法
-
利用反射创建新实例:利用
Class
提供的newInstance()
方法,但此时只能调用该类的public
无参构造方法;Person p = Person.class.newInstance(); // 两者等价 Person p = new Person();
-
调用任意构造方法,可通过
Java
的反射API
提供的Constructor
对象,它包含了一个构造方法所有信息,可创建一个实例;public class Main{ public static void main(String[] args) throws Exception{ // 获取构造方法Integer(int) Constructor cons1 = Integer.class.getConstructor(int.class); // 调用构造方法 Integer n1 = (Integer) cons1.newInstance(123); System.out.println(n1); // 获取构造方法Integer(String) Constructor cons2 = Integer.class.getConstructor(String.class); // 调用构造方法 Integer n2 = (Integer) cons2.newInstance("345"); System.out.println(n2); } }
-
通过
Class
实例获取Constructor
的方法:getConstructor(Class...)
:获取某一public
的Constructor
;getDeclaredConstructor(Class…)
:获取某个Constructor
;getConstructors()
:获取所有public
的Constructor
;getDeclaredConstructors()
:获取所有Constructor
;
获取继承关系
-
获取父类
Class
:public class Main{ public static void main(String[] args) throws Exception { Class thisCls = Integer.class; Class superCls = thisCls.getSuperclass(); System.out.println(superCls); Class objectCls = superClass.getSuperclass(); System.out.println(ObjectCls); System.out.println(ObjectCls.getSuperclass()); } }
-
获取当前类实现的所有接口
interface
:public class Main{ public static void main(String[] args) throws Exception { Class thisCls = Integer.class; Class[] interCls = thisCls.getInterfaces(); for(Class cls: interCls){ System.out.println(cls); } } }
动态代理
-
class
和interface
区别:- 可以实例化
class
(abstract
); - 不能实例化
interface
;
- 可以实例化
-
动态代理(
Dynamic Proxy
)机制:在运行期间动态创建某个interface
的实例; -
动态代码:未实现类但是在运行期间动态创建了一个接口对象的方式;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method); if (method.getName().equals("morning")) { System.out.println("Good morning, " + args[0]); } return null; } }; Hello hello = (Hello) Proxy.newProxyInstance( Hello.class.getClassLoader(), // 传入ClassLoader new Class[] { Hello.class }, // 传入要实现的接口 handler); // 传入处理调用方法的InvocationHandler hello.morning("Bob"); } } interface Hello { void morning(String name); }
-
通过
Proxy.newProxyInstance()
创建interface
实例所需的3个参数:- 使用的
ClassLoader
,通常是接口类的ClassLoader
; - 需要实现的接口数组,至少需要传入一个接口进去;
- 用于处理接口方法调用
InvocationHandler
实例;
- 使用的