本节学习目标:
- 了解Java反射的概念与机制;
- 了解并掌握Class类的概念与获取Class实例的方法;
- 了解并掌握创建运行时类的对象的方法;
- 了解并掌握运行时类的对象的成员的获取与调用的方法;
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
JAVA反射机制 - 百度百科
反射(Reflection)是被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
类加载完毕后,在堆内存的方法区中就产生了一个和这个类对应的Class类型的对象(一个类只有一个Class对象),Class对象包含了对应类完整的内部信息,用户可以使用Class对象获取类的任何成员(甚至私有成员)。
1.2 Java 反射结构
- 动态语言:在运行时可以改变其结构的语言。可以加入新的函数,新的对象,甚至可以引入新的代码。如Object-C、C#、Javascript、PHP、Python等语言。
- 静态语言:与动态语言相对的,在运行时不可改变其结构的语言。如Java、C、C++等语言。
Java语言不是动态语言,但是Java语言的反射机制让其拥有一定的动态性。用户可以利用反射机制、字节码操作等让Java代码获得类似动态语言的特性。所以Java语言可以称之为“准动态语言”。
与反射有关的类都在java.lang.reflect包下,除了Class类在java.lang包下:
| 全限定类名 | 说明 |
|---|---|
| java.lang.Class | 描述一个结构(类,接口等) |
| java.lang.reflect.Constructor | 描述类的构造方法 |
| java.lang.reflect.Field | 描述结构的属性(成员属性或静态属性) |
| java.lang.reflect.Method | 描述结构的方法(成员方法或静态方法) |
| java.lang.reflect.Modifier | 描述修饰符(修饰类、属性或方法的关键字,如public,final等) |
| java.lang.reflect.Parameter | 描述方法的参数 |
Class类位于java.lang包下。它可以看做是加载到Java虚拟机中每个结构(包括类,接口,数组,甚至基本数据类型)的类。加载到Java虚拟机中的每一个类,都可以看做是Class类的一个实例。
通过一个类的Class实例,我们可以访问这个类的内部信息。
为了限制Class实例指代的类型,Java使用泛型T来指定某个类的Class实例。
2.1 Class 实例的获取方式Class类由于其特殊性,Java不允许直接使用new关键字创建Class实例(其唯一的构造方法被private关键字修饰)。
Java中获取Class实例主要有四种方式:
- 使用类的类字面常量(.class);
- 此方式需要显式调用具体某个类,需要用户明确知道此类,如果此类不存在将无法通过编译。
ClassstringClass1 = String.class;
- 调用类的对象的getClass()方法;
- 和第一种方式相似,需要借助某个类的对象获取此类的Class实例,但无需用户明确知道此类。
ClassstringClass2 = new String().getClass();
- 调用Class类提供的静态方法forName(String className):
- 参数className为要获取Class实例的类的全限定类名;
- 如果指定的类不存在,则抛出ClassNotFoundException异常;
- 常用方式,无需确认类也无需对象,在运行时才会确认加载,体现了动态性。
ClassstringClass3 = Class.forName("java.lang.String");
- 使用类加载器(ClassLoader)获取Class实例:
- 不常用,仅做了解。
ClassLoader classLoader = Test.class.getClassLoader(); ClassstringClass4 = classLoader.loadClass("java.lang.String");
对上述四种方式进行测试:
System.out.println(stringClass1); System.out.println(stringClass2); System.out.println(stringClass3); System.out.println(stringClass4); System.out.println(stringClass1 == stringClass2); System.out.println(stringClass2 == stringClass3); System.out.println(stringClass3 == stringClass4); System.out.println(stringClass4 == stringClass1);
通过运行结果可知,上述四种方式获取的都是同一个String类的Class实例。
2.2 对 Class 类的理解Java源码经过javac命令编译后,会生成一个或多个字节码文件(.class)。接着使用java命令解释运行某个字节码文件时,Java虚拟机将会把这个字节码文件加载进内存中。加载到内存中的类被称为运行时类(Runtime class),每个运行时类就作为Class类的一个实例。
不是只有类(class关键字)才有对应的Class实例,像接口(注解)、枚举类、内部类(包括局部内部类和匿名内部类)、数组、甚至是基本数据类型和void关键字等结构都有它们对应的Class实例:
class A {
static class B {
}
}
interface C {
}
enum D {
}
public class Test {
public static void main(String[] args) {
System.out.println(A.class);
System.out.println(A.B.class);
System.out.println(C.class);
System.out.println(D.class);
System.out.println(int[].class);
System.out.println(Override.class);
System.out.println(int.class);
System.out.println(void.class);
}
}
当运行某个字节码文件时,由Java虚拟机(或通过类加载器的defineClass()方法)自动创建对应的Class实例,对于每个结构只会创建一个对应的Class实例。
所以使用四种获取方式获取的都是同一个Class实例。
Class类提供了以下方法获取运行时类的相关信息:
3. 反射访问运行时类及其对象编写JavaBean类Person(为了进行演示,这里不按照规范编写JavaBean):
public class Person {
private String name;
public int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 省略成员变量的getter和setter方法
private String toWork() {
return "Person去工作";
}
public void work() {
System.out.println(toWork());
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
编写JavaBean类Student,并继承Person类:
public class Student extends Person {
public int stuID;
String tel;
private String address;
private Student() {
super();
}
public Student(String name, int age, int stuID, String tel, String address) {
super(name, age);
this.stuID = stuID;
this.tel = tel;
this.address = address;
}
// 省略成员变量的getter和setter方法
protected String toStudy() {
return "Student去学习";
}
@Override
public void work() {
System.out.println(toStudy());
}
}
3.1 访问构造方法(创建对象)
对于无参构造方法,可以直接使用Class类提供的newInstance()方法获取运行时类的对象:
ClasspersonClass = Person.class; Person person = personClass.newInstance(); System.out.println(person);
使用Class实例的newInstance()方法的要求:
- 运行时类必须是一个类(class),像接口则不能直接实例化;
- 运行时类必须提供无参构造方法,且无参构造方法必须被public关键字修饰。
对于有参构造方法,可以使用Class类提供的下列方法获取运行时类的构造方法(Constructor):
| 方法 | 返回值 | 功能 |
|---|---|---|
| getConstructor(Class>... parameterTypes) | Constructor | 获取运行时类的含有指定参数的构造方法 (仅被public关键字修饰) |
| getConstructors() | Constructor>[] | 获取运行时类的所有构造方法 (仅被public关键字修饰) |
| getDeclaredConstructor(Class>... parameterTypes) | Constructor | 获取运行时类的含有指定参数的构造方法 |
| getDeclaredConstructors() | Constructor>[] | 获取运行时类的所有构造方法 |
Constructor类位于java.lang.reflect包下,用于描述运行时类的构造方法,它使用泛型T来限制对应的运行时类。它的常用方法:
| 方法 | 返回值 | 功能 |
|---|---|---|
| getModifiers() | int | 获取构造方法的权限修饰符 |
| getName() | String | 获取构造方法的标识符 |
| getParameterCount() | int | 获取构造方法的参数个数 |
| getParameterTypes() | Class>[] | 获取构造方法的参数列表 |
| newInstance(Object ... initargs) | T | 使用此构造方法创建运行时类的一个实例, initargs为传入构造方法的参数 |
编写代码进行测试:
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
System.out.println(Arrays.toString(personClass.getConstructors()));
System.out.println(Arrays.toString(personClass.getDeclaredConstructors()));
Constructor personConstructor = personClass.getDeclaredConstructor(String.class, int.class);
System.out.println(personConstructor);
System.out.println(Modifier.toString(personConstructor.getModifiers()));
System.out.println(personConstructor.getName());
System.out.println(Arrays.toString(personConstructor.getParameterTypes()));
System.out.println(personConstructor.newInstance("张三", 28));
}
}
3.2 访问成员属性及其属性
Class类提供以下方法获取运行时类的属性(Field):
| 方法 | 返回值 | 功能 |
|---|---|---|
| getField(String name) | Field | 获取运行时类及其父类的指定属性(仅被public关键字修饰) |
| getFields() | Field[] | 获取运行时类及其父类的全部属性(仅被public关键字修饰) |
| getDeclaredField(String name) | Field | 获取运行时类的指定属性 |
| getDeclaredFields() | Field[] | 获取运行时类的全部属性 |
Field类位于java.lang.reflect包下,用于描述结构中的属性(成员变量,静态变量与常量)。它的常用方法:
| 方法 | 返回值 | 功能 |
|---|---|---|
| getModifiers() | int | 获取该属性的权限修饰符 |
| getName() | String | 获取该属性的标识符 |
| getType() | Class> | 获取该属性的数据类型 |
| get(Object obj) | Object | 获取运行时类的某个对象obj的此属性的值 |
| set(Object obj, Object value) | void | 将指定值value赋给运行时类的某个对象obj的此属性 |
编写代码进行测试:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Test {
public static void main(String[] args) throws Exception {
Class studentClass = Student.class;
showFieldInfo(studentClass.getFields());
System.out.println("--------");
showFieldInfo(studentClass.getDeclaredFields());
Constructor studentConstructor = studentClass.getDeclaredConstructor();
studentConstructor.setAccessible(true);
Student student = studentConstructor.newInstance();
Field stuID = studentClass.getField("stuID");
stuID.set(student, 1001);
System.out.println(stuID.get(student));
System.out.println(student.toString());
}
private static void showFieldInfo(Field[] fields) {
for (Field field : fields) {
System.out.print(Modifier.toString(field.getModifiers()) + " ");
System.out.print(field.getType() + " ");
System.out.print(field.getName());
System.out.println();
}
}
}
3.3 访问成员方法及其属性
Class类提供以下方法获取运行时类的方法(Method):
| 方法 | 返回值 | 功能 |
|---|---|---|
| getMethod(String name, Class>... parameterTypes) | Method | 获取运行时类及其父类的指定名称指定参数的方法 (仅被public关键字修饰) |
| getMethods() | Method[] | 获取运行时类及其父类的所有方法 (仅被public关键字修饰) |
| getDeclaredMethod(String name, Class>... parameterTypes) | Method[] | 获取运行时类的指定名称指定参数的方法 |
| getDeclaredMethods() | Method | 获取运行时类的所有方法 |
Method类位于java.lang.reflect包下,用于描述结构中的方法,它的常用方法:
| 方法 | 返回值 | 功能 |
|---|---|---|
| getModifiers() | int | 获取该方法的权限修饰符 |
| getName() | String | 获取该方法的标识符 |
| getReturnType() | Class> | 获取该方法的返回值类型 |
| getParameterTypes() | Class>[] | 获取该方法的所有参数类型 |
| getParameterCount() | int | 获取该方法的参数个数 |
| getExceptionTypes() | Class>[] | 获取该方法抛出的所有异常类型 |
| invoke(Object obj, Object... args) | Object | 传入参数args并执行运行时类的某个对象obj的此方法 |
编写代码进行测试:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception {
Class studentClass = Student.class;
showMethodInfo(studentClass.getMethods());
System.out.println("-------");
showMethodInfo(studentClass.getDeclaredMethods());
Constructor studentConstructor = studentClass.getDeclaredConstructor();
studentConstructor.setAccessible(true);
Student student = studentConstructor.newInstance();
Method work = studentClass.getDeclaredMethod("work", String.class);
Method toWork = studentClass.getDeclaredMethod("toStudy");
String str = (String) toWork.invoke(student);
work.invoke(student, str);
}
private static void showMethodInfo(Method[] methods) {
for (Method method : methods) {
System.out.print(Modifier.toString(method.getModifiers()) + " ");
System.out.print(method.getReturnType() + " ");
System.out.print(method.getName() + " ");
System.out.print(Arrays.toString(method.getParameterTypes()) + " ");
System.out.print(Arrays.toString(method.getExceptionTypes()) + " ");
System.out.println();
}
}
}
运行结果:
public class java.lang.String toString [] [] public class java.lang.String getAddress [] [] public void work [class java.lang.String] [] public class java.lang.String getTel [] [] public int getStuID [] [] public void setTel [class java.lang.String] [] public void setStuID [int] [] public void setAddress [class java.lang.String] [] public class java.lang.String getName [] [] public void setName [class java.lang.String] [] public int getAge [] [] public void setAge [int] [] public final void wait [long, int] [class java.lang.InterruptedException] public final native void wait [long] [class java.lang.InterruptedException] public final void wait [] [class java.lang.InterruptedException] public boolean equals [class java.lang.Object] [] public native int hashCode [] [] public final native class java.lang.Class getClass [] [] public final native void notify [] [] public final native void notifyAll [] [] ------- public class java.lang.String toString [] [] public class java.lang.String getAddress [] [] public void work [class java.lang.String] [] public class java.lang.String getTel [] [] protected class java.lang.String toStudy [] [] public int getStuID [] [] public void setTel [class java.lang.String] [] public void setStuID [int] [] public void setAddress [class java.lang.String] [] Student去学习



