我们日常写的代码基本上都是固定的,所以无论运行多少次,其结果也都是固定的,但是在某些特殊场合中编写代码时不确定要创建什么类型的对象,也不确定要调用什么样的方法,这些都希望通过运行时传递 的参数来决定,该机制叫做动态编程技术,也就是反射机制。
简而言之一句话:反射机制就是用于动态创建对象并且动态调用方法的机制
而且,更重要的是,目前主流的框架底层都是采用反射机制来实现的
动态编程技术也就是要编写通用的代码,这就要借助一个Java的lang包下的Class类
Class类
基本概念:
1.java.lang.Class类的实例可以用于描述Java应用程序中的类和接口,不再仅仅表示堆区中的一块内存了,也表示一种数据类型。
2.该类没有公共构造方法,该类的实例(也就是整个类的信息,这个整体)由Java虚拟机和类加载器自动构造完成,本质上就是加载到内存中的运行时类。
获取Class对象的方式:
使用数据类型.class的方式可以获取对应类型的Class对象。
使用引用/对象.getClass()的方式可以获取对应类型的Class对象。
使用包装类.TYPE的方式可以获取对应基本数据类型的Class对象。
使用Class.forName()的方式来获取参数指定类型的Class对象。
使用类加载器ClassLoader的方式获取指定类型的Class对象
例子:
public class ClassTest {
public static void main(String[] args) throws Exception{
// 1.使用数据类型.class的方式可以获取对应类型的Class对象
Class c1 = String.class;
// 打印会自动调用toString()方法,查询手册可知
// Class类重写了toString()方法,打印的是类或者接口的完全限定名,也就是包名.类名
System.out.println("使用数据类型.class的方式获取到的对应类型的Class对象为c1 = " + c1);
c1 = int.class;
// 如果是基本数据类型,则打印的就是基本数据类型的名称
System.out.println("使用数据类型.class的方式获取到的对应类型的Class对象为c1 = " + c1);
c1 = void.class;
// 如果是void,返回的就是void
System.out.println("使用数据类型.class的方式获取到的对应类型的Class对象为c1 = " + c1);
System.out.println("============================================================");
// 2.使用对象.getClass()的方式获取对应的Class对象
String str1 = new String("hello");
c1 = str1.getClass();
System.out.println("使用对象.getClass()的方式获取对应的Class对象为c1 = " + c1);
Integer it1 = 20;
c1 = it1.getClass();
System.out.println("使用对象.getClass()的方式获取对应的Class对象为c1 = " + c1);
int num = 5;
// num.getClass();
// 上面这一句会报错,因为基本数据类型的变量不能调用方法,基本数据类型不是对象
System.out.println("============================================================");
// 3.使用包装类.TYPE的方式来获取对应基本数据类型的Class对象
c1 = Integer.TYPE;
System.out.println("使用包装类.TYPE的方式来获取对应基本数据类型的Class对象为c1 = " + c1);
c1 = Integer.class;
System.out.println("使用包装类.TYPE的方式来获取对应基本数据类型的Class对象为c1 = " + c1);
System.out.println("============================================================");
// 4.调用Class类中的forName方法来获取对应的Class对象
//c1 = Class.forName("String"); // 报错,要求写完整的名称
c1 = Class.forName("java.lang.String");
System.out.println("调用Class类中的forName方法来获取对应的Class对象为c1 = " + c1);
c1 = Class.forName("java.util.Date");
System.out.println("调用Class类中的forName方法来获取对应的Class对象为c1 = " + c1);
// c1 = Class.forName("int"); // 报错,不能获取基本数据类型的Class对象
System.out.println("============================================================");
// 5.使用类加载器的方式来获取Class对象
ClassLoader classLoader = ClassTest.class.getClassLoader();
c1 = classLoader.loadClass("java.lang.String");
System.out.println("使用类加载器的方式来获取Class对象为c1 = " + c1);
}
}
结果为
使用数据类型.class的方式获取到的对应类型的Class对象为c1 = class java.lang.String
使用数据类型.class的方式获取到的对应类型的Class对象为c1 = int
使用数据类型.class的方式获取到的对应类型的Class对象为c1 = void
============================================================
使用对象.getClass()的方式获取对应的Class对象为c1 = class java.lang.String
使用对象.getClass()的方式获取对应的Class对象为c1 = class java.lang.Integer
============================================================
使用包装类.TYPE的方式来获取对应基本数据类型的Class对象为c1 = int
使用包装类.TYPE的方式来获取对应基本数据类型的Class对象为c1 = class java.lang.Integer
============================================================
调用Class类中的forName方法来获取对应的Class对象为c1 = class java.lang.String
调用Class类中的forName方法来获取对应的Class对象为c1 = class java.util.Date
============================================================
使用类加载器的方式来获取Class对象为c1 = class java.lang.String
常用方法:
方法声明 | 功能介绍 |
---|---|
static Class<?> forName(String className) | 用于获取参数指定类型对应的Class对象并返回 |
T newInstance() | 用于创建该Class对象所表示类的新实例 |
Constructor类
基本概念:
java.lang.reflect.Constructor类主要用于描述获取到的构造方法信息
Class类的常用方法:
方法声明 | 功能介绍 |
---|---|
Constructor getConstructor(Class<?>... parameterTypes) | 用于获取此Class对象所表示类型中参数指定的公共构造方法 |
Constructor<?>[] getConstructors() | 用于获取此Class对象所表示类型中所有 的公共构造方法 |
Constructor类的常用方法:
方法声明 | 功能介绍 |
---|---|
T newInstance(Object... initargs) | 使用此Constructor对象描述的构造方法对象代表类型的新实例 |
int getModififiers() | 获取方法的访问修饰符 |
String getName() | 获取方法的名称 |
Class<?>[] getParameterTypes() | 获取方法所有参数的类型 |
例子:
先定义Person类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
public class PersonConstructorTest {
public static void main(String[] args) throws Exception{
// 1.使用原始方式以无参形式构造Person类型的对象并打印
Person p1 = new Person();
System.out.println("原始方式以无参方式构建的对象是: " + p1);
System.out.println("============================================================");
// 2.使用反射机制以无参形式构造Person类型的对象并打印
// 动态创建对象,创建对象的类型可以从键盘输入,也可以从配置文件中读取
Class c1 = Class.forName("ExperimentOnMyOwn.reflect.Person");
// 这个方法过时了
System.out.println("反射机制以无参方式构建的对象是: " + c1.newInstance());
System.out.println("============================================================");
// 优化
// 获取Class对象对应类中的无参构造方法,也就是Person类中的无参构造方法
Constructor constructor = c1.getConstructor();
// 使用获取到的无参构造方法来构造对应类型的对象,也就是Person类型的对象
System.out.println("反射机制优化的方法以无参方式构建的对象是: " + constructor.newInstance());
// 3.使用原始方式以有参方式构造Person类型的对象并打印
Person p2 = new Person("zhangfei", 30);
System.out.println("原始方式以有参方式构建的对象是: " + p2);
System.out.println("============================================================");
// 4.使用反射机制以有参方式构造Person类型的对象并打印
// 获取Class对象对应类中的有参构造方法,也就是Person类中的有参构造方法
Constructor constructor1 = c1.getConstructor(String.class, int.class);
// 使用获取到的有参构造方法来构造对应类型的对象,也就是Person类型的对象
// newInstance方法中的实参是用于给有参数构造方法的形参进行初始化的,也即是给name和age进行初始化的
System.out.println("反射机制优化的方法以有参方式构建的对象是: " + constructor1.newInstance("zhangfei", 30));
System.out.println("============================================================");
// 5.使用反射机制获取Person类中所有的公共构造方法并打印
Constructor[] constructors = c1.getConstructors();
System.out.println("反射机制获取Person类中的所有公共构造方法有:");
System.out.println();
for (Constructor ct : constructors) {
System.out.println("构造方法的访问修饰符是: " + ct.getModifiers()); // 查询手册可知,打印1就是public
System.out.println("构造方法的方法名称是: " + ct.getName());
Class[] parametersType = ct.getParameterTypes();
System.out.println("构造方法的所有参数类型是: ");
for (Class cs : parametersType) {
System.out.print(cs + " ");
}
System.out.println();
System.out.println("============================================================");
}
}
}
结果为
原始方式以无参方式构建的对象是: Person{name='null', age=0}
============================================================
反射机制以无参方式构建的对象是: Person{name='null', age=0}
============================================================
反射机制优化的方法以无参方式构建的对象是: Person{name='null', age=0}
原始方式以有参方式构建的对象是: Person{name='zhangfei', age=30}
============================================================
反射机制优化的方法以有参方式构建的对象是: Person{name='zhangfei', age=30}
============================================================
反射机制获取Person类中的所有公共构造方法有:
构造方法的访问修饰符是: 1
构造方法的方法名称是: ExperimentOnMyOwn.reflect.Person
构造方法的所有参数类型是:
============================================================
构造方法的访问修饰符是: 1
构造方法的方法名称是: ExperimentOnMyOwn.reflect.Person
构造方法的所有参数类型是:
class java.lang.String int
============================================================
Fileld类
基本概念:
java.lang.reflect.Field类主要用于描述获取到的单个成员变量信息。
Class类的常用方法:
方法声明 | 功能介绍 |
---|---|
Field getDeclaredField(String name) | 用于获取此Class对象所表示类中参数指定的单个成员 变量信息 |
Field[] getDeclaredFields() | 用于获取此Class对象所表示类中所有成员变量信息 |
Field类的常用方法:
方法声明 | 功能介绍 |
---|---|
Object get(Object obj) | 获取参数对象obj中此Field对象所表示成员变量的数 值 |
void set(Object obj, Object value) | 将参数对象obj中此Field对象表示成员变量的数值修 改为参数value的数值 |
void setAccessible(boolean flag) | 当实参传递true时,则反射对象在使用时应该取消 Java 语言访问检查 |
int getModififiers() | 获取成员变量的访问修饰符 |
Class<?> getType() | 获取成员变量的数据类型 |
String getName() | 获取成员变量的名称 |
例子:
public class PeronFieldTest {
public static void main(String[] args) throws Exception{
// 1.使用原始方式来构造对象以及获取成员变量的数值并打印
Person p1 = new Person("zhangfei", 30);
System.out.println("原始方式获取到的成员变量数值为: " + p1.getName());
System.out.println("============================================================");
// 2.使用反射机制来构造对象以及获取成员变量的数值并打印
// 2.1 获取Class对象
Class c1 = Class.forName("ExperimentOnMyOwn.reflect.Person");
// 2.2 根据Class对象获取对应的有参构造方法
Constructor constructor = c1.getConstructor(String.class, int.class);
// 2.3 使用有参构造方法来得到Person类型的对象
Object object = constructor.newInstance("zhangfei", 30);
// 2.4 根据Class对象获取对应的成员变量信息
Field field = c1.getDeclaredField("name");
// 设置Java语言访问检查的取消 暴力反射
field.setAccessible(true);
// 2.5 使用Person类型的对象来获取成员变量的数值,也就是成员变量name的数值
System.out.println("反射机制获取到的成员变量数值为: " + field.get(object));
System.out.println("============================================================");
// 3.使用原始方式修改指定对象中成员变量的数值后再打印
p1.setName("guanyu");
System.out.println("修改后成员变量的数值为: " + p1.getName());
System.out.println("============================================================");
// 4.使用反射机制修改指定对象中成员变量的值后再次打印
field.set(object, "guanyu");
System.out.println("获取到的成员变量数值为: " + field.get(object));
System.out.println("============================================================");
// 5.获取Class对象对应类中所有的成员变量
Field[] declaredFields = c1.getDeclaredFields();
for (Field ft : declaredFields) {
System.out.println("获取到的访问修饰符为: " + ft.getModifiers()); // 打印2为private
System.out.println("获取到的数据类型为: " + ft.getType());
System.out.println("获取到的成员变量名称为: " + ft.getName());
System.out.println("================================");
}
}
}
Method类
基本概念:
java.lang.reflect.Method类主要用于描述获取到的单个成员方法信息。
Class类的常用方法:
方法声明 | 功能介绍 |
---|---|
Method getMethod(String name, Class<?>... parameterTypes) | 用于获取该Class对象表示类中名字为name参数parameterTypes的指定公共成员方法 |
Method[] getMethods() | 用于获取该Class对象表示类中所有公共成员方法 |
Method类的常用方法:
方法声明 | 功能介绍 |
---|---|
Object invoke(Object obj, Object... args) | 使用对象obj来调用此Method对象所表示的成 员方法,实参传递args |
int getModififiers() | 获取方法的访问修饰符 |
Class<?> getReturnType() | 获取方法的返回值类型 |
String getName() | 获取方法的名称 |
Class<?>[] getParameterTypes() | 获取方法所有参数的类型 |
Class<?>[] getExceptionTypes() | 获取方法的异常信息 |
例子:
public class PersonMethodTest {
public static void main(String[] args) throws Exception{
// 1.使用原始方式构造对象并调用方法打印结果
Person p1 = new Person("zhangfei", 30);
System.out.println("调用方法的返回值是:" + p1.getName());
System.out.println("============================================================");
// 2.使用反射机制构造对象并调用方法打印结果
// 2.1 获取Class对象
Class c1 = Class.forName("ExperimentOnMyOwn.reflect.Person");
// 2.2 根据Class对象来获取对应的有参构造方法
Constructor constructor = c1.getConstructor(String.class, int.class);
// 2.3 使用有参构造方法构造对象并记录
Object object = constructor.newInstance("zhangfei", 30);
// 2.4 根据Class对象来获取对应的成员方法
Method method = c1.getMethod("getName");
// 2.5 使用对象调用成员方法进行打印
// 表示使用object对象调用method表示的方法,也就是调用getName方法来获取姓名
System.out.println("调用方法的返回值是:" + method.invoke(object));
System.out.println("============================================================");
// 3.使用反射机制来获取类中的所有成员方法并打印
Method[] methods = c1.getMethods();
for (Method mt : methods) {
System.out.println("成员方法的修饰符是: " + mt.getModifiers());
System.out.println("成员方法的返回值类型是: " + mt.getReturnType());
System.out.println("成员方法的名称是: " + mt.getName());
System.out.println("成员方法形参列表的类型是: ");
Class<?>[] parameterTypes = mt.getParameterTypes();
for (Class ct : parameterTypes) {
System.out.print(ct + " ");
}
System.out.println();
System.out.println("成员方法的异常类型列表是: ");
Class<?>[] exceptionTypes = mt.getExceptionTypes();
for (Class ct: exceptionTypes) {
System.out.print(ct + " ");
}
System.out.println();
System.out.println("=========================================");
}
}
}
结果为
调用方法的返回值是:zhangfei
============================================================
调用方法的返回值是:zhangfei
============================================================
成员方法的修饰符是: 1
成员方法的返回值类型是: class java.lang.String
成员方法的名称是: toString
成员方法形参列表的类型是:
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: class java.lang.String
成员方法的名称是: getName
成员方法形参列表的类型是:
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: void
成员方法的名称是: setName
成员方法形参列表的类型是:
class java.lang.String
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: int
成员方法的名称是: getAge
成员方法形参列表的类型是:
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: void
成员方法的名称是: setAge
成员方法形参列表的类型是:
int
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 273
成员方法的返回值类型是: void
成员方法的名称是: wait
成员方法形参列表的类型是:
long
成员方法的异常类型列表是:
class java.lang.InterruptedException
=========================================
成员方法的修饰符是: 17
成员方法的返回值类型是: void
成员方法的名称是: wait
成员方法形参列表的类型是:
long int
成员方法的异常类型列表是:
class java.lang.InterruptedException
=========================================
成员方法的修饰符是: 17
成员方法的返回值类型是: void
成员方法的名称是: wait
成员方法形参列表的类型是:
成员方法的异常类型列表是:
class java.lang.InterruptedException
=========================================
成员方法的修饰符是: 1
成员方法的返回值类型是: boolean
成员方法的名称是: equals
成员方法形参列表的类型是:
class java.lang.Object
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 257
成员方法的返回值类型是: int
成员方法的名称是: hashCode
成员方法形参列表的类型是:
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 273
成员方法的返回值类型是: class java.lang.Class
成员方法的名称是: getClass
成员方法形参列表的类型是:
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 273
成员方法的返回值类型是: void
成员方法的名称是: notify
成员方法形参列表的类型是:
成员方法的异常类型列表是:
=========================================
成员方法的修饰符是: 273
成员方法的返回值类型是: void
成员方法的名称是: notifyAll
成员方法形参列表的类型是:
成员方法的异常类型列表是:
=========================================
获取其他结构信息
方法声明 | 功能介绍 |
---|---|
Package getPackage() | 获取所在的包信息 |
Class<? super T> getSuperclass() | 获取继承的父类信息 |
Class<?>[] getInterfaces() | 获取实现的所有接口 |
Annotation[] getAnnotations() | 获取注解信息 |
Type[] getGenericInterfaces() | 获取泛型信息 |
例子:
先定义Student类和MyAnnotation注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
@MyAnnotation
public class Student<T, E> extends Person implements Comparable, Serializable {
@Override
public int compareTo(Object o) {
return 0;
}
}
测试类
public class StudentTest {
public static void main(String[] args) throws Exception{
// 获取Student类型的Class对象
Class c1 = Class.forName("ExperimentOnMyOwn.reflect.Student");
System.out.println("获取到的包信息是:" + c1.getPackage());
System.out.println("获取到的父类信息是:" + c1.getSuperclass());
System.out.println("获取到的接口信息是:"); //
Class[] interfaces = c1.getInterfaces();
for (Class ct : interfaces) {
System.out.print(ct + " ");
}
System.out.println();
System.out.println("获取到的注解信息是:");
Annotation[] annotations = c1.getAnnotations();
for (Annotation at : annotations) {
System.out.print(at + " ");
}
System.out.println();
System.out.println("获取到的泛型信息是:");
Type[] genericInterfaces = c1.getGenericInterfaces();
for (Type tt : genericInterfaces) {
System.out.print(tt + " ");
}
System.out.println();
}
}
结果为
获取到的包信息是:package ExperimentOnMyOwn.reflect
获取到的父类信息是:class ExperimentOnMyOwn.reflect.Person
获取到的接口信息是:
interface java.lang.Comparable interface java.io.Serializable
获取到的注解信息是:
@ExperimentOnMyOwn.reflect.MyAnnotation()
获取到的泛型信息是:
interface java.lang.Comparable interface java.io.Serializable