反射(Reflect)
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。
- 在运行时构造任意一个类的对象。
- 运行时获取任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法(属性)。
Java 是一门面向对象的语言。在面向对象的世界里,万事万物皆对象,既然万事万物皆对象。我们写的每一个类都可以看成一个对象,是 java.lang.Class 类的对象。当我们写完一个类的Java文件,编译成class文件的时候,编译器都会将这个类的对应的class对象放在class文件的末尾。保存了类的元数据信息,一个类的元数据信息包括属性,方法,构造器,实现了哪些接口等等,这些信息在Java里都有对应的类来表示。
Class类
Class是一个类,封装了当前对象所对应的类的信息。
Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等。
对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。对象只能由系统建立对象,一个类(而不是一个对象)在 JVM 中只会有一个Class实例。
获取Class对象的三种方式
- 通过类名获取 类名.class
- 通过对象获取 对象名.getClass()
- 通过全类名获取 Class.forName(全类名)
Class类的常用方法
方法名 | 功能说明 |
---|---|
static Class forNmae(String name) | 返回指定类名的Class对象 |
Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 |
Object newInstantce(Object []args) | 调用当前格式构造函数,返回Class对象的一个实例 |
getName() | 返回Class对象锁表示的实体(类,接口,数组类,基本类型或者void)名称 |
Class getSuperClass() | 返回当前Class对象的父类Class对象 |
Class [] getInterfases() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
类加载器、构造器、Method、Field
ClassLoader
public static void testClassLoader() throws ClassNotFoundException,
FileNotFoundException {
//1. 获取一个系统的类加载器(可以获取)
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//2. 获取系统类加载器的父类加载器(扩展类加载器,可以获取).
classLoader = classLoader.getParent();
System.out.println(classLoader);
//3. 获取扩展类加载器的父类加载器(引导类加载器,不可获取).
classLoader = classLoader.getParent();
System.out.println(classLoader);
//4. 测试当前类由哪个类加载器进行加载(系统类加载器):
classLoader = Class.forName("xx.xx.xxxx")
.getClassLoader();
System.out.println(classLoader);
//5. 测试 JDK 提供的 Object 类由哪个类加载器负责加载(引导类)
classLoader = Class.forName("java.lang.Object")
.getClassLoader();
System.out.println(classLoader);
}
Constructor
/*构造器相关*/
public void testConstructor() throws Exception{
String className = "xx.xx.xxxx.Person";
Class<Person> clazz = (Class<Person>) Class.forName(className);
System.out.println("获取全部Constructor对象-----");
Constructor<Person>[] constructors
= (Constructor<Person>[]) clazz.getConstructors();
for(Constructor<Person> constructor: constructors){
System.out.println(constructor);
}
System.out.println("获取某一个Constructor 对象,需要参数列表----");
Constructor<Person> constructor
= clazz.getConstructor(String.class, int.class);
System.out.println(constructor);
//2. 调用构造器的 newInstance() 方法创建对象
System.out.println("调用构造器的 newInstance() 方法创建对象-----");
Person obj = constructor.newInstance("Mark", 18);
System.out.println(obj.getName());
}
Field
/*域相关*/
public void testField() throws Exception{
String className = "xx.xx.xxxx.Person";
Class clazz = Class.forName(className);
System.out.println("获取公用和私有的所有字段,但不能获取父类字段");
Field[] fields = clazz.getDeclaredFields();
for(Field field: fields){
System.out.print(" "+ field.getName());
}
System.out.println();
System.out.println("---------------------------");
System.out.println("获取指定字段");
Field field = clazz.getDeclaredField("name");
System.out.println(field.getName());
Person person = new Person("ABC",12);
System.out.println("获取指定字段的值");
Object val = field.get(person);
System.out.println(field.getName()+"="+val);
System.out.println("设置指定对象指定字段的值");
field.set(person,"DEF");
System.out.println(field.getName()+"="+person.getName());
System.out.println("字段是私有的,不管是读值还是写值," +
"都必须先调用setAccessible(true)方法");
field = clazz.getDeclaredField("age");
field.setAccessible(true);
System.out.println(field.get(person));
}
Method
/*方法相关*/
public void testMethod() throws Exception{
Class clazz = Class.forName("xx.xx.xxxx.Person");
System.out.println("获取clazz对应类中的所有方法," +
"不能获取private方法,且获取从父类继承来的所有方法");
Method[] methods = clazz.getMethods();
for(Method method:methods){
System.out.print(" "+method.getName()+"()");
}
System.out.println("");
System.out.println("---------------------------");
System.out.println("获取所有方法,包括私有方法," +
"所有声明的方法,都可以获取到,且只获取当前类的方法");
methods = clazz.getDeclaredMethods();
for(Method method:methods){
System.out.print(" "+method.getName()+"()");
}
System.out.println("");
System.out.println("---------------------------");
System.out.println("获取指定的方法," +
"需要参数名称和参数列表,无参则不需要写");
// 方法public void setName(String name) { }
Method method = clazz.getDeclaredMethod("setName", String.class);
System.out.println(method);
System.out.println("---");
// 方法public void setAge(int age) { }
/* 这样写是获取不到的,如果方法的参数类型是int型
如果方法用于反射,那么要么int类型写成Integer: public void setAge(Integer age) { }
要么获取方法的参数写成int.class*/
method = clazz.getDeclaredMethod("setAge", int.class);
System.out.println(method);
System.out.println("---------------------------");
System.out.println("执行方法,第一个参数表示执行哪个对象的方法" +
",剩下的参数是执行方法时需要传入的参数");
Object obje = clazz.newInstance();
method.invoke(obje,18);
/*私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true);*/
method = clazz.getDeclaredMethod("privateMethod");
System.out.println(method);
System.out.println("---------------------------");
System.out.println("执行私有方法");
method.setAccessible(true);
method.invoke(obje);
}