Java 反射
1、什么是反射
Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
2、反射的原理
下图是类的正常加载过程、反射原理与class对象:
Class对象的由来是将.class文件读入内存,并为之创建一个Class对象。
3、反射的优缺点
优点
-
在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
-
允许程序创建和控制任何类的对象,无需提前硬编码目标类
-
提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力
-
反射的应用领域 (开源框架,如MyBatis、Spring等)
缺点
- 性能问题
反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射
反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
- 代码维护问题
反射会模糊程序内部逻辑,可读性较差.
反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题
4、反射的用途
-
反编译:.class–>.java
-
通过反射机制访问java对象的属性,方法,构造方法等
-
当我们在使用IDEA,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。
-
反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
5、反射技术常用API
反射常用的Java类型
java.lang.Class<T> //可获取类和类的成员信息
java.lang.reflect.Constructor<T>//可调用类的构造方法
java.lang.reflect.Field //可访问类的属性
java.lang.reflect.Method //可调用类的方法
6、反射技术使用步骤
- 导入 java.lang.reflect.*
- 获得需要操作的类的java.lang.Class对象
- 调用Class的方法获取Field、Method等对象
- 使用反射API操作实例成员
7、反射的入口类Class
Class类是Java反射机制的起源和入口
- 每个类都有自己向关的Class对象
- 提供了获取类信息的相关方法
Class类存放类的结构信息
- 类名
- 父类﹑接口
- 构造方法﹑方法﹑属性
- 注解
- ……
获取Class实例常用方式
//方法1:对象.getClass()
Student stu = new Student();
Class clazz = stu.getClass();
//方法2:类.class
Class clazz = Student.class;
//方法3:Class.forName()
Class clazz = Class.forName("xxx.xxx.Student");
获取类型的基本信息的常用方法:
定一个BaseClass为父类
package reflect.entity;
public class BaseClass {
}
定一个Person类并继承BaseClass父类实现
Serializable
接口
package reflect.entity;
import java.io.Serializable;
import java.io.IOException;
public final class Person extends BaseClass implements Serializable {
// 成员变量
private String name;
static final int age = 30;
protected String address;
public String message;
// 成员方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
static final int getAge() {
return age;
}
protected String getAddress() {
return address;
}
private void silentMethod() throws IOException, NullPointerException {
System.out.println("这是悄悄话");
}
/*[以下声明用于通过反射获取构造方法信息测试]*/
//无参构造函数
public Person() { }
//有参构造函数
private Person(String name) {
this.name = name;
}
protected Person(String name, String address, String message) {
this.name = name;
this.address = address;
this.message = message;
}
@Override
public String toString() {
return "{name:" + name + ", age:" + age + ", address:" + address
+ ", message:" + message + "}";
}
}
通过反射获取类的信息示例:
package reflect.classinfo;
import java.lang.reflect.Modifier;
import reflect.entity.Person;
/**
* 获取类型的基本信息
*/
public class GetClassInfo {
public static void main(String[] args) {
Class clz = Person.class;
String fullName = clz.getName();
String simpleName = clz.getSimpleName();
System.out.println("以下是 " + fullName + " 类的基本信息");
// 获取Person类所在的包
Package pkg = clz.getPackage();
// 获得此对象所表示的实体(类、接口、基本类型或 void)的超类的 Class
// 如果此对象表示 Object 类、一个接口、一个基本类型或 void,则返回 null
// 如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象
Class superClass = clz.getSuperclass();
System.out.println(simpleName + " 类的超类是:" + superClass.getName());
// 获得此对象所表示的类或接口实现的接口
// 如果此对象表示一个不实现任何接口的类或接口,则此方法返回一个长度为 0 的数组。
// 如果此对象表示一个基本类型或 void,则此方法返回一个长度为 0 的数组。
Class[] interfaces = clz.getInterfaces();
//getModifiers()方法返回int类型值表示该字段的修饰符。其中,该修饰符是java.lang.reflect.Modifier的静态属性
int modifier = clz.getModifiers();
}
}
获取构造方法信息的常用方法:
获取构造方法信息示例代码:
package reflect.classinfo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import reflect.entity.Person;
/**
* 获取构造方法信息
*/
public class GetClassConstructorsInfo {
public static void main(String[] args) {
// 获取Person类声明的所有构造方法
// 它们是公共、保护、默认(包)访问和私有构造方法
// 如果此 Class 实例表示一个接口、一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组
Constructor[] cons = Person.class.getDeclaredConstructors();
// 构造方法的一些信息
System.out.println("=========构造方法展示=========");
for (Constructor con : cons) {
System.out.print("访问修饰符:");
int modifier = con.getModifiers();
// 判断该构造方法的访问修饰符
if ((modifier & Modifier.PUBLIC) == Modifier.PUBLIC)
System.out.println("public");
else if ((modifier & Modifier.PROTECTED) == Modifier.PROTECTED)
System.out.println("protected");
else if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
System.out.println("private");
else System.out.println("default(package)");
// 获取构造方法的参数列表
Class[] params = con.getParameterTypes();
if (params.length == 0) {
System.out.println("该构造方法没有参数");
} else {
System.out.print("该构造方法的参数列表为:[");
for (int i = 0; i < params.length; i++) {
if (i != 0)System.out.print(", ");
System.out.print(params[i].getName());
}
System.out.println("]");
}
}
}
}
获取属性信息的常用方法:
获取属性信息示例:
package reflect.classinfo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import reflect.entity.Person;
/**
* 获取属性信息
*/
public class GetClassFieldsInfo {
public static void main(String[] args) {
// 获取Person中的所有属性,
// 包括公共、保护、默认(包)访问和私有属性,但不包括继承的属性,
// 如果该类或接口不声明任何属性,或者此 Class 实例表示一个基本类型、一个数组或 void,则此方法返回一个长度为 0 的数组。
Field[] fields = Person.class.getDeclaredFields();
// 展示属性的一些信息
System.out.println("===========属性展示==========");
for (Field field : fields) {
System.out.println("属性名:" + field.getName());
System.out.println("类型:" + field.getType().getName());
System.out.print("访问修饰符:");
int modifier = field.getModifiers();
// 判断该属性的访问修饰符
if ((modifier & Modifier.PUBLIC) == Modifier.PUBLIC)
System.out.println("public");
else if ((modifier & Modifier.PROTECTED) == Modifier.PROTECTED)
System.out.println("protected");
else if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
System.out.println("private");
else
System.out.println("default(package)");
// 判断该属性是否有static修饰符
if ((modifier & Modifier.STATIC) == Modifier.STATIC)
System.out.println("这是一个静态属性");
// 判断该属性是否有final修饰符
if ((modifier & Modifier.FINAL) == Modifier.FINAL)
System.out.println("这是一个final属性");
}
}
}
获取方法信息的常用方法:
获取方法信息示例:
package reflect.classinfo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import reflect.entity.Person;
/**
* 获取方法信息
*/
public class GetClassMethodsInfo {
public static void main(String[] args) {
// 获取Person中的所有方法,
// 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法,
// 如果该类或接口不声明任何方法,或者此 Class 实例表示一个基本类型、一个数组或 void,则此方法返回一个长度为 0 的数组。
Method[] methods = Person.class.getDeclaredMethods();
// 展示方法的一些信息
System.out.println("===========方法展示==========");
for (Method method : methods) {
System.out.println("方法名:" + method.getName());
System.out.println("返回值类型:" + method.getReturnType().getName());
// 获取方法的参数列表
Class[] params = method.getParameterTypes();
if (params.length == 0) {
System.out.println("该方法没有参数");
} else {
System.out.print("该方法的参数列表为:[");
for (int i = 0; i < params.length; i++) {
if (i != 0)
System.out.print(", ");
System.out.print(params[i].getName());
}
System.out.println("]");
}
System.out.print("访问修饰符:");
int modifier = method.getModifiers();
// 判断该方法的访问修饰符
if ((modifier & Modifier.PUBLIC) == Modifier.PUBLIC)
System.out.println("public");
else if ((modifier & Modifier.PROTECTED) == Modifier.PROTECTED)
System.out.println("protected");
else if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
System.out.println("private");
else
System.out.println("default(package)");
// 判断该方法是否有static修饰符
if ((modifier & Modifier.STATIC) == Modifier.STATIC)
System.out.println("这是一个静态方法");
// 判断该方法是否有final修饰符
if ((modifier & Modifier.FINAL) == Modifier.FINAL)
System.out.println("这是一个final方法");
// 判断该方法是否有abstract修饰符
if ((modifier & Modifier.ABSTRACT) == Modifier.ABSTRACT)
System.out.println("这是一个抽象方法");
// 判断该方法是否有synchronized修饰符
if ((modifier & Modifier.SYNCHRONIZED) == Modifier.SYNCHRONIZED)
System.out.println("这是一个同步方法");
// 获取方法所属的类或接口的Class实例
Class declaringClass = method.getDeclaringClass();
System.out.println("方法声明在:" + declaringClass.getName() + " 中");
// 获取方法抛出的异常类型,即throws子句中声明的异常
Class[] exceptions = method.getExceptionTypes();
if (exceptions.length > 0) {
System.out.print("该方法抛出的异常有:[");
for (int i = 0; i < exceptions.length; i++) {
if (i != 0)
System.out.print(", ");
System.out.print(exceptions[i].getName());
}
System.out.println("]");
}
System.out.println("----------------------------");
}
}
}
8、反射实现类的实例化
java.lang.Class
public T newInstance()
//关键代码:
Class clazz = Class.forName("reflect.entity.Person");
Object obj = clazz.newInstance();
java.lang.reflect.Constructor
public T newInstance(Object… initargs)
Constructor cons = clazz.getDeclaredConstructor(String.class);
cons.setAccessible(true);
Object obj = cons.newInstance("New Person");
package reflect.access;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class AccessPersonConstructors {
// 测试反射调用构造方法
public static void main(String[] args) {
try {
Class clz = Class.forName("reflect.entity.Person");
Object obj = clz.newInstance();
System.out.println(obj);
// 获取Person的无参构造
Constructor c1 = clz.getDeclaredConstructor();
// Person的无参构造为public,这里可以直接访问
obj = c1.newInstance();
System.out.println(obj);
// 获取Person的单参构造
Constructor c2 = clz.getDeclaredConstructor(String.class);
// Person的单参构造为private,这里已超出其访问范围,不能直接访问
// 通过setAccessable方法,设定为可以访问
c2.setAccessible(true);
obj = c2.newInstance("New Person");
System.out.println(obj);
// 获取Person的三参构造
Constructor c3 = clz.getDeclaredConstructor(String.class,
String.class, String.class);
// Person的三参构造为protected,这里已超出其访问范围,不能直接访问
// 通过setAccessable方法,设定为可以访问
c3.setAccessible(true);
obj = c3.newInstance("New Person", "beijing", "Hello!");
System.out.println(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
9、反射访问实例的字段
java.lang.reflect.Field
package reflect.access;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import reflect.entity.Person;
/**
* 通过反射方式访问属性
*/
public class AccessPersonFields {
// 通过反射操作Person的属性
public static void main(String[] args) {
try {
// 通过反射加载一个Person实例
Class cls = Class.forName("reflect.entity.Person");
Object person = cls.newInstance();
// 获取name属性
Field name = cls.getDeclaredField("name");
// name属性为private,这里已超出其访问范围,不能直接访问
// 通过setAccessable方法,设定为可以访问
name.setAccessible(true);
// 先取值看一下
System.out.println("赋值前的name:" + name.get(person));
// 为name属性赋值
name.set(person, "New Person");
// 展示一下赋值效果
System.out.println("赋值后的name:" + name.get(person));
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
10、反射调用实例的方法
java.lang.reflect.Method
public Object invoke( Object obj, Object...args)
package reflect.access;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import reflect.entity.Person;
/**
* 反射调用实例的方法
*/
public class AccessPersonMethods {
public static void main(String[] args) {
//反射操作Person的方法
try {
//通过反射加载Person类
Class clz = Class.forName("reflect.entity.Person");
//根据方法名和参数列表获取static final int getAge()方法,无参可以不写或用null表示
Method getAge = clz.getDeclaredMethod("getAge", null);
//getAge方法为default(package),这里已超出其访问范围,不能直接访问
//通过setAccessable方法,设定为可以访问
getAge.setAccessible(true);
//调用getAge方法并传参,没有参数可以不写或用null表示
//getAge方法为静态方法,调用时可以不指定具体Person实例
Object returnAge = getAge.invoke(null, null);
System.out.println("年龄是:" + returnAge);
Object person = clz.newInstance(); //创建Person实例
//根据方法名和参数列表获取private void silentMethod()方法
Method silentMethod = clz.getDeclaredMethod("silentMethod", null);
//silentMethod方法为private,这里已超出其访问范围,不能直接访问
//通过setAccessable方法,设定为可以访问
silentMethod.setAccessible(true);
//调用silentMethod方法并传参,没有参数可以不写或用null表示
silentMethod.invoke(person, null);
//根据方法名和参数列表获取public void setName(String)方法
Method setName = clz.getDeclaredMethod("setName", String.class);
//setName方法为public,这里可以直接访问
//调用setName方法并传参
setName.invoke(person, "New Person");
//验证一下结果,调用public String getName()方法得到name的值
Object returnName = clz.getDeclaredMethod("getName").invoke(person);
System.out.println("刚才设定的name是:" + returnName);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}