Java学习笔记@反射
笔者:unirithe
日期:11/12/2021
反射概述
- 反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分
- 例如,在运行时可直接得到这个类的构造器对象:
Constructor - 在运行时,可直接得到这个类的成员变量对象:
Filed - 在运行时,可直接得到这个类的成员方法对象:
Method - 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制
反射操作
获取Class对象
User.java
package com.uni;
public class User {}
方式一Class.forName
调用静态方法 : Class.forName(包名+类名)
Class c = Class.forName("com.uni.User");
System.out.println(c);
运行结果
class com.uni.User
方式二类名.class
直接取类的class属性
Class c = com.uni.User.class
方式三对象.getClass()
调用对象的getClass方法
Class c = new com.uni.User().getClass()
获取构造器对象 Constructor
Class类中获取构造器的方法
| 返回类型 | 方法名 | 描述 |
|---|---|---|
| Constructor<?>[] | getConstructors() |
返回所有构造器对象的数组(只能是public) |
| Constructor<?>[] | getDeclaredCounstructors() |
返回所有构造器对象的数组,存在就能拿到 |
Constructor<T> |
getConstructor(Class <?> ... parameterTypes) |
返回单个构造器对象(只能拿public) |
Constructor<T> |
getDeclaredConstructor(Class<?>... parameterTypes) |
返回单个构造器对象,存在就能拿到 |
User.java
package com.uni;
public class User {
public User(){
System.out.println("无参构造");
}
public User(String id){
System.out.println("有参构造1");
}
private User(String id, String pw){
System.out.println("有参构造2");
}
}
测试 getConstructors() 方法 ( 只能获取public构造方法)
Demo.java
Class classUser = User.class;
Constructor [] constructors = classUser.getConstructors();
for (Constructor constructor : constructors)
System.out.println("该构造方法的参数个数: " + constructor.getParameterCount());
运行结果
该构造方法的参数个数: 0
该构造方法的参数个数: 1
测试 getDeclaredCounstructors() 方法 (可获取所有构造方法)
Class classUser = User.class;
Constructor [] constructors = classUser.getDeclaredConstructors();
for (Constructor constructor : constructors)
System.out.println("该构造方法的参数个数: " + constructor.getParameterCount());
运行结果
该构造方法的参数个数: 0
该构造方法的参数个数: 1
该构造方法的参数个数: 2
测试获取单个public构造器的方法 getConstructor ()
classUser.getConstructor(); // 无参构造
classUser.getConstructor(String.class); // 指定类型的有参构造
classUser.getConstructor(String.class, String.class); // 该句报错,因为对应的构造方法是private类型
获取praivte修饰的构造器方法 getDeclaredConstructor()
classUser.getDeclaredConstructor(String.class, String.class);
调用构造器方法 newInstance
在获取到构造器对象后,通过 Constructor对象的newInstance() 方法 进行实例化
Class classUser = User.class;
Constructor constructor = classUser.getConstructor(String.class);
User user =(User) constructor.newInstance("uni");
特别的是,对于 private 修饰的构造方法,在使用 newInstance() 方法前需打开权限即 setAccessible(true)
Class classUser = User.class;
Constructor constructor = classUser.getDeclaredConstructor(String.class, String.class);
constructor.setAccessible(true);
User user =(User) constructor.newInstance("hello", "world");
获取成员变量对象 Field
java.lang.reflect.Field 取值赋值方法
- 赋值
void set(Object obj, Object value) - 取值
Object get(Object obj)
范例:获取类的所有成员变量,包括final常量、private私有成员变量
User.java
public class User {
public String name = "uni";
private String password = "123456";
public static char sex = '男';
public static final String lcoation = "中国";
public String getPW() {return password;}
}
Class classUser = User.class;
Field[] fileds = classUser.getDeclaredFields();
for (Field filed : fileds) {
System.out.println(filed.getName() + "的类型是: " + filed.getType());
}
结果
name的类型是: class java.lang.String
password的类型是: class java.lang.String
sex的类型是: char
lcoation的类型是: class java.lang.String
范例:获取类的私有成员变量并进行修改
Class classUser = User.class;
Field userF = classUser.getDeclaredField("password");
User user = new User();
System.out.println(user.getPW());
userF.setAccessible(true); //打开权限
userF.set(user, "123");
System.out.println(user.getPW());
运行结果
123456
123
获取方法对象 Method
常用API的get方法和之前相同,只是将后缀替换成了Method
User.java
public class User {
public User(){}
private void eat(String food){
System.out.println(food + " nice!");
}
public String sleep(String time){return time + " good!";}
public static boolean study(){return true; }
}
范例:获取类的所有方法名称及其返回结果
Class classUser = User.class;
Method[] methods = classUser.getDeclaredMethods();
for (Method method : methods)
System.out.println(method.getName() +": " + method.getReturnType());
运行结果
sleep: class java.lang.String
eat: void
study: boolean
范例:获取类的public方法
Method method = classUser.getMethod("sleep", String.class);
调用Method方法
Object invoke(Object obj, Object ..args)
参数说明
-
参数一:用obj对象调用方法
-
参数二:调用方法的传递的参数(没有则不写)
返回值:方法的返回值(没有则不写)
范例:调用有返回值的public方法
Class classUser = User.class;
Method method = classUser.getMethod("sleep", String.class);
User user = new User();
String res = method.invoke(user, "2021")
System.out.println(res);
运行结果
2021 good!
范例:调用无返回值的private方法
Class classUser = User.class;
Method method = classUser.getDeclaredMethod("eat", String.class);
User user = new User();
//调用 private 方法需打开权限
method.setAccessible(true);
Object res = method.invoke(user, "fish");
System.out.println(res);
运行结果
fish nice!
null
反射应用
擦除泛型
- 反射是作用在运行时的计数,此时集合的泛型将不能产生约束,此时可以为集合存入其他任意类型元素。
- 泛型只是在编译阶段可以约束集合只能操作某种类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList,泛型被擦除
案例:绕过编译阶段为集合添加数据
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) throws Exception {
ArrayList<String> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // 结果为 True
Class c = list1.getClass(); // ArrayList.class ==> public boolean add(E e)
Method add = c.getDeclaredMethod("add", Object.class);
boolean rs = (boolean) add.invoke(list1, "测试");
System.out.println("add执行结果: " + rs);
System.out.println(list1);
}
}
通用框架的底层原理
案例:
需求:给任意对象,把未知的字段名称和对应值存储到文件中
分析:
- 定义一个方法可接收任意类的对象
- 每收到一个对象需解析该对象的全部成员变量名称
- 使用反射获取对象的Class类对象,然后获取全部成员变量信息
- 遍历成员变量信息,然后提取本成员变量在对象中的具体值
- 存入成员变量名称和值到文件中去
User.java
public class User {
private String id;
public User(String id) {
this.id = id;
}
public void setId(String id) {
this.id = id;
}
public String getId(){
return id;
}
}
UserUtils.java
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
public class UserUtils {
public static void save(Object obj){
try(PrintStream ps = new PrintStream(new FileOutputStream("data.txt", true))){
Class userClass = obj.getClass();
// 输出当前类名
ps.println("=========" + userClass.getSimpleName() + "=========");
// 获取所有成员变量
Field[] fields = userClass.getDeclaredFields();
for (Field field : fields) {
String name = field.getName();
field.setAccessible(true);
String value = field.get(obj) + "";
ps.println(name + " = " + value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Demo.java
import java.lang.reflect.Method;
import java.util.ArrayList;
public class Demo{
public static void main(String[] args) {
UserUtils.save(new User("uni"));
UserUtils.save(new User("abc"));
UserUtils.save(new User("java"));
}
}
运行结果 data.txt
=========User========= id = uni =========User========= id = abc =========User========= id = java
动态代理
java.lang.reflect.Proxy
构造方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数说明:
- loader 类加载器,负责加载代理类别到内存中使用
- interfaces 获取被代理对象实现的全部接口,代理要为全部接口的全部方法进行代理
- h 代理的核心处理逻辑
代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的
关键步骤:
- 必须有接口,实现类要实现接口(代理通常是基于接口实现)
- 创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象
案例:模拟企业业务功能开发,并完成每个功能的性能统计
需求:模拟某企业用户管理业务,需包含用户登陆,用户删除,用户查询功能,并要统计每个功能的耗时
定义:
UserService表示用户业务接口,完成用户登陆、删除、查询功能
UserServiceImpl实现UserService,并完成相关功能
定义测试类,创建实现类对象,调用方法
UserService.java
public interface UserService {
String login(String username, String password);
void selectUsers();
boolean deleteUsers();
}
UserServiceImpl.java
public class UserServiceImpl implements UserService{
@Override
public String login(String username, String password) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if ("admin".equals(username) && "123456".equals(password))
return "success";
else
return "密码错误";
}
@Override
public void selectUsers() {
System.out.println("模拟查询 正在执行...");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean deleteUsers() {
System.out.println("模拟删除 正在执行...");
try {
Thread.sleep(2000);
return true;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
}
ProxyUtil.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
public static UserService getProxy(UserService obj) {
return (UserService) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 参数1: 代理对象本身,一般不管
// 参数2:正在被代理的方法
// 参数3:被代理方法,应该传入的参数
long startTime = System.currentTimeMillis();
// 触发方法的真正执行
Object result = method.invoke(obj, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " 方法耗时: " + (endTime - startTime) / 1000.0 + " s");
// 把业务功能方法执行的结果返回给调用者
return result;
}
});
}
}
TestDemo.java
public class TestDemo {
public static void main(String[] args) {
UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
System.out.println(userService.login("admin", "123456"));
System.out.println(userService.deleteUsers());
userService.selectUsers();
}
}
运行结果
login 方法耗时: 1.001 s
success
模拟删除 正在执行...
deleteUsers 方法耗时: 2.002 s
true
模拟查询 正在执行...
selectUsers 方法耗时: 2.002 s
其中 ProxyUtil.java 的作用是在执行对象的方法时候进行性能评优,即输出方法执行的耗时,除了固定的类型外,可以使用泛型
public class ProxyUtil {
public static <T> T getProxy(T obj) {
return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = method.invoke(obj, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " 方法耗时: " + (endTime - startTime) / 1000.0 + " s");
return result;
}
});
}
}
这样就可输出任意对象的任意方法执行的耗时结果,非常方便。
动态代理优点
- 非常灵活,支持任意接口类型的实现类对象做代理,也可直接为本身做代理
- 可以为被代理对象的所有方法做代理
- 可以在不改变方法源码的情况下,实现对方法功能的增强
- 不仅简化了编程工作,提高了软件系统的可扩展性,同时也提高了开发效率

浙公网安备 33010602011771号