反射

  • JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  • 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

反射就是把java类中的各种成分映射成一个个的Java对象

  • 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。

image

反射机制有什么用?

  1. 运行时动态获取类的信息:在编写代码时,对于类的信息是必须在编译时确定的,但在运行时,有时需要根据某些条件,动态获取某个类的信息,这时就可以使用Java中的反射机制。
  2. 动态生成对象:反射机制可以在运行时生成对象,这样就可以根据参数的不同,动态的创建不同的类的实例对象。
  3. 动态调用方法:通过反射机制可以调用类中的方法,不论这些方法是否是公共的,也不论这些方法的参数个数和类型是什么,反射机制都具有这样的能力。
  4. 动态修改属性:利用反射机制可以获取到类中的所有成员变量,并可以对其进行修改。
  5. 实现动态代理:利用反射机制可以实现代理模式,通过代理对象完成原对象对某些方法的调用,同时也可以在这些方法的调用前后做一些额外的处理。

Java反射机制的优点

  1. 增加灵活性和扩展性:使用反射机制可以在程序运行时动态加载、修改、创建、调用类和方法等,从而增加了程序的灵活性和可扩展性。
  2. 提高代码的通用性:通过反射机制可以动态的获取类信息,从而可以编写通用的代码,使得不同的类能够以相同的方式来处理。
  3. 规范代码结构:反射机制可以使代码结构清晰明了,减少了代码中的冗余部分。
  4. 实现框架和插件:反射机制在很多框架和插件中都有广泛的应用,比如Spring框架、JUnit测试框架等。
  5. 动态代理:反射机制的另一个重要应用是实现动态代理,可以在不修改原来代码的情况下,通过代理对象对原对象的方法进行增强。
// 反射的基本用法 - 获取class中的属性和方法以及一些基本类中的参数信息
package Chapter09;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Reflect_01 {
public static void main(String[] args) throws Exception {
// TODO 反射

// 多态
User user = new Child();
user.test1();
//user.test2();

// 类对象(必须要获取到类对象后才能获取类中的其他数据,也就是下面的操作)
Class<? extends User> aClass = user.getClass();
// Class<Child> aClass = Child.class; // 与上面的代码实现效果一致
// Class<?> aClass1 = Class.forName("Chapter09.Child"); // 与上面的代码实现效果一致

// TODO 获取类的名称
System.out.println(aClass.getName()); // 获取类的完整名称(包含包名) - Chapter09.Child
System.out.println(aClass.getSimpleName()); // 获取类的名称 - Child
System.out.println(aClass.getPackageName()); // 获取类的包的名称 - Chapter09

// TODO 获取类的父类
Class<?> superclass = aClass.getSuperclass();
System.out.println(superclass); // class Chapter09.User

// TODO 获取类的接口
Class<?>[] interfaces = aClass.getInterfaces(); // 获取接口
System.out.println(interfaces.length); // 0

// TODO 获取类的属性
Field f = aClass.getField("xxxxx"); // 只能获取 public 声明的对应的属性权限
Field f1 = aClass.getDeclaredField("xxxxx"); // 可以获取所有权限对应的属性

Field[] fields = aClass.getFields();// 只能获取 public 声明的所有属性权限
Field[] declaredFields = aClass.getDeclaredFields(); // 可以获取所有权限

// TODO 获取类的方法
Method method = aClass.getMethod("test2");// 只能获取 public 声明的对应方法权限
Method xxxx = aClass.getDeclaredMethod("xxxx"); // 可以获取所有权限对应的方法

Method[] methods = aClass.getMethods();// 只能获取 public 声明的所有方法权限
Method[] declaredMethods = aClass.getDeclaredMethods(); // 可以获取所有权限

// TODO 构造方法
Constructor<? extends User> constructor = aClass.getConstructor(); // 获取钩爪方法
Constructor<?>[] constructors = aClass.getConstructors(); // 获取所有的构造方法
aClass.getDeclaredConstructor();

// TODO 获取权限(修饰符,例如:Private,Protect等..) :多个修饰符会融合成一个int值
int modifiers = aClass.getModifiers();
boolean aPrivate = Modifier.isPrivate(modifiers); // 判断是否由对应的修饰符
}
}

class User {
public void test1() {
System.out.println("test1...");
}
}
class Child extends User {
public void test2() {
System.out.println("test2...");
}
}

小测试:

  • 使用反射实现一个简单的登录功能
package Chapter09;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Reflect_02 {
public static void main(String[] args) throws Exception {

// TODO 反射 - 练习
// 员工的登录功能,

// 构造方法对象 .class获取目标类对象
Class empClass = Emp.class;
// Class<? extends Emp> empClass = new Emp().getClass(); // 与上面的代码实现效果一致

Constructor declaredConstructor = empClass.getDeclaredConstructor(); // 获取构造方法
// 构建对象
Object emp = declaredConstructor.newInstance();

// 获取对象的属性 - getField("属性名"),获取public属性的
Field account = empClass.getField("account");
Field password = empClass.getField("password");

// 给属性赋值 - 目标类中的属性.set(目标类 , "属性值")
account.set(emp, "admin");
password.set(emp, "admin");

// 获取登录方法 - getMethod("方法名"),获取public权限的
Method login = empClass.getMethod("login");

// 调用方法(invoke) - 方法对象.invoke(目标类)
Object result = login.invoke(emp);

System.out.println(result); // true
}
}

// 员工类
class Emp {
public String account; // 定义账号属性为public
public String password;// 定义密码属性为public

// 定义登陆方法
public boolean login() {
if ( "admin".equals(account) && "admin".equals(password) ) {
return true;
} else {
return false;
}
}
}