JAVA反射机制
反射基本概念
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力, 并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。Java 中,反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码链接。反射允许我们在编写与执行时,使我们的程序代码能够接入装载到 JVM 中的类的内部信息,而不是源代码中选定的类协作的代码。这使反射成为构建灵活的应用的主要工具。但需注意的是:如果使用不当,反射的成本很高。
java中的类反射
通过java语言中的反射机制可以操作字节码文件(.class文件)
反射机制的相关类在java.lang.reflect.*;包下
反射机制相关的重要的类:
- java.lang.Class:代表整个字节码,代表一个类型,代表整个类。
- java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
- java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法
- java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。
获取Class对象
要操作一个类的字节码,首先需要获取到这个类的字节码,也就是Class对象,获取Class对象有三种方式
- 通过类路径获取
Class.forName()
- 这是一个静态方法
- 方法的参数是一个字符串
- 字符串需要的是一个完整的类名
- 完整类名必须带有包名
public class Reflect01 {
public static void main(String[] args) {
Class clazz= null;
try {
clazz = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(clazz);
}
}
- java中的任何一个对象都有一个getClass()方法,Object类中,反射获得的就是它对应类型的字节码文件
public class Reflect02 {
public static void main(String[] args) {
String i="reflection";
Class a=i.getClass();
System.out.println(a);
}
}
- 通过类来获取Class对象
public class Reflect03 {
public static void main(String[] args) {
Class stringClass=String.class;
System.out.println(stringClass);
}
}
最后注意:字节码文件装载到JVM中,只装载一份;下面的例子就说明了a和stringClass指向的是同一个地址。
public class Reflect04 {
public static void main(String[] args) {
String i="reflection";
Class a=i.getClass();
Class stringClass=String.class;
System.out.println(a==stringClass);
}
}
结果:
通过反射获取Class,通过Class来实例对象
先获得Class对象,通过Class对象的newInstance()来实例化对象,而newInstance()底层实际是调用Class对象的无参构造方法,所以说当一个类没有无参构造方法时会报错实例化异常:java.lang.NoSuchMethodException(无法初始化实例)
public class Reflect01 {
public static void main(String[] args) {
try {
Class c =Class.forName("com.hyg.MyTest");
MyTest myTest= (MyTest) c.newInstance();
System.out.println(myTest);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
结果
反射获取属性Field
StudentBean:分别采用不同的访问控制权限修饰符
Field: public int no; 这一整个部分 就是一个Field对象
public class StudentBean {
public int no;
private String name;
protected int age;
boolean sex;
}
从下面的演示可以看出clazz.getFields()这个方法时获取类中所有的public修饰的field
public class Reflect02 {
public static void main(String[] args) throws ClassNotFoundException {
//获取类
Class clazz=Class.forName("com.hyg.reflection.StudentBean");
//获取field
Field[] fields = clazz.getFields();
System.out.println(fields.length);
Field field = fields[0];
String fieldName=field.getName();
System.out.println(fieldName);
}
}
结果
下面的演示可以得知clazz.getDeclaredFields()方法可以得到类中所有的field,不管权限修饰符是什么,都可以获取到
public class Reflect02 {
public static void main(String[] args) throws ClassNotFoundException {
//获取类
Class clazz=Class.forName("com.hyg.reflection.StudentBean");
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println(declaredFields.length);
for (Field f:declaredFields) {
System.out.println(f.getName());
}
}
}
结果
获取Field中的可使用信息
public class Reflect02 {
public static void main(String[] args) throws ClassNotFoundException {
//获取类
Class clazz=Class.forName("com.hyg.reflection.StudentBean");
String name = clazz.getName();
System.out.println("类的路径名:"+name);
String simpleName = clazz.getSimpleName();
System.out.println("类的简单名字:"+simpleName);
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println("属性的个数:"+declaredFields.length);
for (Field f:declaredFields) {
//获取属性修饰符getModifiers 表示属性可能有多个修饰符
int modifiers = f.getModifiers();//返回的是一个数字,每个数字是修饰符的代号
//将代号数字转换成字符串
System.out.println("属性的修饰符:"+Modifier.toString(modifiers));
//获取属性的类型
Class<?> type = f.getType();
System.out.println("属性的类型:"+type.getName());
//获取属性名
System.out.println("属性名:"+f.getName());
System.out.println(Modifier.toString(modifiers)+" "+type.getName()+" "+f.getName());
}
}
}
结果
反编译Class和里面的属性
public class Reflect03 {
public static void main(String[] args) throws ClassNotFoundException {
StringBuilder bean=new StringBuilder();
Class studentBean=Class.forName("com.hyg.reflection.StudentBean");
bean.append(Modifier.toString(studentBean.getModifiers())+" class "+studentBean.getSimpleName()+" {\n");
Field[] declaredFields = studentBean.getDeclaredFields();
for (Field f:declaredFields) {
bean.append("\t");
bean.append(Modifier.toString(f.getModifiers())+" "+f.getType().getSimpleName()+" "+f.getName()+";\n" );
}
bean.append("}");
System.out.println(bean.toString());
}
}
结果
通过反射机制访问一个对象的属性,给属性赋值set,获取属性的值 get
public class reflect04 {
public static void main(String[] args) throws Exception {
Class studentClass=Class.forName("com.hyg.reflection.StudentBean");
//创建对象
Object o = studentClass.newInstance();
//获取指定属性名的Field
Field no = studentClass.getDeclaredField("no");
//给o 对象的no 属性赋值
no.set(o,2020);
//获取 o 对象的 no 值
System.out.println( no.get(o));
//获取私有属性
Field name = studentClass.getDeclaredField("name");
//上面这种方式只能访问public修饰的属性,private修饰的属性不能通过这种方式访问
//强制访问 打破封装
name.setAccessible(true);
name.set(o,"reflect");
System.out.println(name.get(o));
}
}
结果
反射获取Method
public class UserLogin {
public boolean login(String name,String password){
if ("admin".equals(name)&&"123".equals(password)){
return true;
}
return false;
}
public void loginOut(){
System.out.println("退出");
}
}
public class Reflect05 {
public static void main(String[] args) throws ClassNotFoundException {
Class userLogin=Class.forName("com.hyg.reflection.UserLogin");
//获取所有的方法,包括私有的
Method[] declaredMethods = userLogin.getDeclaredMethods();
System.out.println(declaredMethods.length);
for (Method method:declaredMethods) {
//获取修饰符列表
System.out.println("修饰符列表:"+ Modifier.toString(method.getModifiers()));
//获取方法返回值类型
System.out.println("方法放回值类型:"+method.getReturnType().getSimpleName());
//获取方法名
System.out.println("方法名:"+method.getName());
//获取参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class clazz:parameterTypes) {
System.out.println("参数类型:"+clazz.getSimpleName());
}
}
}
}
结果
反编译类和其中方法
public class Reflect06 {
public static void main(String[] args) throws ClassNotFoundException {
StringBuilder a=new StringBuilder();
Class userLogin=Class.forName("com.hyg.reflection.UserLogin");
a.append(Modifier.toString(userLogin.getModifiers())+" class "+userLogin.getSimpleName()+" {\n");
Method[] declaredMethods = userLogin.getDeclaredMethods();
for (Method m:declaredMethods) {
a.append("\t");
a.append(Modifier.toString(m.getModifiers())+" "+m.getReturnType().getSimpleName()+" "+ m.getName()+"(");
Class<?>[] parameterTypes = m.getParameterTypes();
for (Class c:parameterTypes) {
a.append(c.getSimpleName()+",");
}
a.deleteCharAt(a.length()-1);
a.append("){\n");
a.append("}\n");
}
a.append("}");
System.out.println(a.toString());
}
}
结果
通过反射机制调用一个对象的方法
调用方法的要素
- 对象
- 方法名
- 形参列表
- 返回值
public class Reflect07 {
public static void main(String[] args) throws Exception {
Class userLogin=Class.forName("com.hyg.reflection.UserLogin");
Object o = userLogin.newInstance();
//获取方法 由于重载的存在,获取一个方法时需要方法名 加参数列表
Method login= userLogin.getDeclaredMethod("login", String.class, String.class);
//调用方法
Object value = login.invoke(o, "admin", "123");
System.out.println(value);
}
}
结果
反编译一个类的Constructor构造方法
public class StudentInfo {
private int no;
private String name;
public StudentInfo() {
}
public StudentInfo(int no) {
this.no = no;
}
public StudentInfo(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "StudentInfo{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
public class Reflect08 {
public static void main(String[] args) throws ClassNotFoundException {
StringBuilder a=new StringBuilder();
Class studentInfo=Class.forName("com.hyg.reflection.StudentInfo");
a.append(Modifier.toString(studentInfo.getModifiers())+" class "+studentInfo.getSimpleName()+" {\n");
Constructor[] declaredConstructors =studentInfo.getDeclaredConstructors();
for (Constructor c:declaredConstructors) {
a.append("\t");
a.append(Modifier.toString(c.getModifiers())+" "+studentInfo.getSimpleName()+"(");
Class[] clazz=c.getParameterTypes();
for (Class i:clazz) {
a.append(i.getSimpleName()+",");
}
if (clazz.length>0) {
a.deleteCharAt(a.length() - 1);
}
a.append("){\n");
a.append("}\n");
}
a.append("}");
System.out.println(a.toString());
}
}
结果
利用反射机制使用构造方法创建对象
public class Reflect09 {
public static void main(String[] args) throws Exception {
Class studentInfo=Class.forName("com.hyg.reflection.StudentInfo");
//无参构造方法创建对象
Object o = studentInfo.newInstance();
//有参 构造方法创建对象
//先获取有参构造方法 感觉参数列表
Constructor declaredConstructor = studentInfo.getDeclaredConstructor(int.class);
Constructor constructor=studentInfo.getDeclaredConstructor(int.class,String.class);
//调用有参构造方法new 对象
Object o1 = declaredConstructor.newInstance(10);
Object constructor1 = constructor.newInstance(100, "constructor");
System.out.println(o1);
System.out.println(constructor1);
}
}
结果
获取类的父类 和实现的接口
public class Reflect10 {
public static void main(String[] args) throws ClassNotFoundException {
Class clazz=Class.forName("java.lang.String");
//获取父类
Class superclass = clazz.getSuperclass();
System.out.println("父类:"+superclass.getName());
//获取 实现的接口 一个类可以实现多个接口
Class[] interfaces = clazz.getInterfaces();
for (Class i:interfaces) {
System.out.println("实现的接口:"+i.getName());
}
}
}
结果
安全性和反射
在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,我们可
能希望框架能够全面介入代码,无需考虑常规的介入限制。但是,在其它情况下,不受控制的
介入会带来严重的安全性风险,例如当代码在不值得信任的代码共享的环境中运行时。
反射的两个缺点
反射是一种强大的工具,但也存在一些不足。
- 性能问题。使用反射基本上是一种解释操作,我们可以告诉 JVM,我们希望做什么并
且它满足我们的要求。用于字段和方法接入时反射要远慢于直接代码。性能问题的程
度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓
慢的性能将不会是一个问题。 - 使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻
辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂。
解决这些问题的最佳方案是保守地使用反射——仅在它可以真正增加灵活性的地方
——记录其在目标类中的使用。