【Java】反射与动态代理篇
目录
- 反射的基本概念
- 获取 class 对象的三种方式
- Class.forName("全类名");
- 类名.class
- 对象.getClass();
- 获取类内元素
- 获取构造方法
- 获取成员变量
- 获取成员方法
- 反射的作用
- 案例 1:保存信息
- 案例 2:跟配置文件结合动态创建
- 动态代理
- 概述
- 创建代理对象
- 案例
反射的基本概念
反射允许对成员变量、成员方法和构造方法的信息进行编程访问
如图所示,反射可以获取类中的成员变量、成员方法和构造方法并解剖其中的所有信息
获取 class 对象的三种方式
获取对象有三种方式:
- Class.forName(“全类名”);
- 类名.class
- 对象.getClass();
使用的时候分别对应图中的三种阶段
Class.forName(“全类名”);
全类名 = 包名 + 类名
代码示例:
package MyReflect;
public class Student {
private String name;
private int age;
public Student() {}
public Student(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;}
public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
public static void main(String[] args) throws ClassNotFoundException {
Class cs = Class.forName("MyReflect.Student");
System.out.println(cs);
}
}
结果如下:
class MyReflect.Student
类名.class
代码示例:
package MyReflect;
public class Student {
private String name;
private int age;
public Student() {}
public Student(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;}
public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
public static void main(String[] args) throws ClassNotFoundException {
Class cs1 = Student.class;
System.out.println(cs1);
}
}
结果如下:
class MyReflect.Student
对象.getClass();
代码示例:
package MyReflect;
public class Student {
private String name;
private int age;
public Student() {}
public Student(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;}
public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
public static void main(String[] args) throws ClassNotFoundException {
Student s = new Student();
Class cs = s.getClass();
System.out.println(cs);
}
}
结果如下:
class MyReflect.Student
由结果可以看出,这三个方式获取的字节码文件对象是相同的
三个方法的使用场景:
- 第一种是最常用的
- 第二种一般当做参数进行传递,比如多线程中的锁对象
- 第三种只有当有了类对象后才可以使用
获取类内元素
在 Java 中可以万物皆对象:
- 构造方法 → Constructor 对象
- 字段(成员变量)→ Field 对象
- 成员方法 → Method 对象
获取构造方法
Class 类中用于获取构造方法的方法;
Constructor<?>[] getConstructors()
返回所有公共构造方法对象的数组Constructor<?>[] getDeclaredConstructors()
返回所有构造方法对象的数组<T> Constructor<T> getConstructor(Class<?>... parameterTypes)
返回单个公共构造方法对象<T> Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
返回单个构造方法对象
代码示例:
Student 类:
package MyReflect;
public class Student {
private String name;
private int age;
public Student() {}
public Student(String name) {
this.name = name;
}
protected Student(int age) {
this.age = age;
}
private Student(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;}
public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
reflectDemo 类:
package MyReflect;
import java.lang.reflect.Constructor;
public class reflectDemo {
public static void main(String[] args) throws Throwable {
Class cs = Class.forName("MyReflect.Student");
// Constructor<?>[] getConstructors()
Constructor[] cons1 = cs.getConstructors();
for(Constructor con : cons1) {
System.out.println(con);
}
System.out.println("--------------");
// Constructor<?>[] getDeclaredConstructors()
Constructor[] cons2 = cs.getDeclaredConstructors();
for(Constructor con : cons2) {
System.out.println(con);
}
System.out.println("--------------");
// <T> Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor con3 = cs.getConstructor(String.class);
System.out.println(con3);
System.out.println("--------------");
// <T> Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
Constructor con4 = cs.getDeclaredConstructor(String.class,int.class);
System.out.println(con4);
}
}
结果如下:
public MyReflect.Student()
public MyReflect.Student(java.lang.String)
--------------
public MyReflect.Student()
private MyReflect.Student(java.lang.String,int)
protected MyReflect.Student(int)
public MyReflect.Student(java.lang.String)
--------------
public MyReflect.Student(java.lang.String)
--------------
private MyReflect.Student(java.lang.String,int)
获取构造方法中的信息
getModifiers()
获取权限修饰符getParameters()
获取所有参数
代码示例:
package MyReflect;
import java.lang.reflect.Constructor;
public class reflectDemo {
public static void main(String[] args) throws Throwable {
Class cs = Class.forName("MyReflect.Student");
Constructor con4 = cs.getDeclaredConstructor(String.class,int.class);
int modifiers = con4.getModifiers();
System.out.println(modifiers);
}
}
结果如下:
2
输出结果 2 为常量字段值,其他修饰符的常量字段值如下图所示:
Constructor 类中用于创建对象的方法:
T newInstance(Object...initargs)
根据指定的构造方法创建对象setAccessible(boolean flag)
设置为 true,表示取消访问检查
代码示例:
package MyReflect;
import java.lang.reflect.Constructor;
public class reflectDemo {
public static void main(String[] args) throws Throwable {
Class cs = Class.forName("MyReflect.Student");
Constructor con4 = cs.getDeclaredConstructor(String.class,int.class);
con4.setAccessible(true);
Student s = (Student) con4.newInstance("张三",18);
System.out.println(s);
}
}
结果如下:
Student{name = 张三, age = 18}
创建 s 对象用的是 private Student(String name,int age)
,由于是用 private 修饰的,所以创建对象的时候会被禁止访问,这时候就需要在创建对象前使用 con4.setAccessible(true);
将访问权限打开
获取成员变量
获取成员变量的方法:
Field[] getFields()
返回所有公共成员变量对象的数组Field[] getDeclaredFields()
返回所有成员变量对象的数组Field getField(String name)
返回单个公共成员变量对象Field getDeclaredField(String name)
返回单个成员变量对象
代码示例:
Student 类:
package MyReflect;
public class Student {
private String name;
private int age;
public String gender;
public Student() {}
public Student(String name,int age,String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
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;}
public String getGender() {return gender;}
public void setGender(String name) {this.gender = gender;}
public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";}
}
reflectDemo 类:
package MyReflect;
import java.lang.reflect.Field;
public class reflectDemo {
public static void main(String[] args) throws Throwable {
Class cs = Class.forName("MyReflect.Student");
// Field[] getFields()
Field[] fields1 = cs.getFields();
for(Field field : fields1) {
System.out.println(field);
}
System.out.println("--------------");
// Field[] getDeclaredFields()
Field[] fields2 = cs.getDeclaredFields();
for(Field field : fields2) {
System.out.println(field);
}
System.out.println("--------------");
// Field getField(String name)
Field fields3 = cs.getField("gender");
System.out.println(fields3);
System.out.println("--------------");
// Field getDeclaredField(String name)
Field fields4 = cs.getDeclaredField("name");
System.out.println(fields4);
}
}
结果如下:
public java.lang.String MyReflect.Student.gender
--------------
private java.lang.String MyReflect.Student.name
private int MyReflect.Student.age
public java.lang.String MyReflect.Student.gender
--------------
public java.lang.String MyReflect.Student.gender
--------------
private java.lang.String MyReflect.Student.name
获取成员变量中的信息
getModifiers()
获取权限修饰符getName()
获取成员变量的名字getType()
获取成员变量的类型
package MyReflect;
import java.lang.reflect.Field;
public class reflectDemo {
public static void main(String[] args) throws Throwable {
Class cs = Class.forName("MyReflect.Student");
Field name = cs.getDeclaredField("name");
System.out.println(name);
int modifiers = name.getModifiers();
System.out.println(modifiers);
String n = name.getName();
System.out.println(n);
Class<?> type = name.getType();
System.out.println(type);
}
}
结果如下:
private java.lang.String MyReflect.Student.name
2
name
class java.lang.String
Field 类中用于创建对象的方法
void set(Object obj, Object value)
为指定对象的成员变量赋值Object get(Object obj)
获取指定对象中成员变量的值
代码示例:
package MyReflect;
import java.lang.reflect.Field;
public class reflectDemo {
public static void main(String[] args) throws Throwable {
Class cs = Class.forName("反射.Student");
Field name = cs.getDeclaredField("name");
System.out.println(name);
Student s = new Student("张三",18,"男");
name.setAccessible(true);
name.set(s, "李四");
String value = (String)name.get(s);
System.out.println(value);
}
}
结果如下:
private java.lang.String MyReflect.Student.name
李四
获取成员方法
获取成员方法的方法
Method[] getMethods()
返回所有公共成员方法对象的数组,包括继承的Method[] getDeclaredMethods()
返回所有成员方法对象的数组,不包括继承的Method getMethod(String name, Class<?>... parameterTypes)
返回单个公共成员方法对象Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回单个成员方法对象
代码示例:
Student 类:
package MyReflect;
public class Student {
private String name;
private int age;
public Student() {}
public Student(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;}
public void sleep() {
System.out.println("睡觉");
}
private void eat(String something) {
System.out.println("在吃" + something);
}
public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
reflectDemo 类:
package MyReflect;
import java.lang.reflect.Method;
public class reflectDemo {
public static void main(String[] args) throws Throwable {
Class cs = Class.forName("MyReflect.Student");
// Method[] getMethods()
Method[] methods1 = cs.getMethods();
for(Method method : methods1) {
System.out.println(method);
}
System.out.println("---------------------");
// Method[] getDeclaredMethods()
Method[] methods2 = cs.getDeclaredMethods();
for(Method method : methods2) {
System.out.println(method);
}
System.out.println("---------------------");
// Method getMethod(String name, Class<?>... parameterTypes)
Method method3 = cs.getMethod("sleep");
System.out.println(method3);
System.out.println("---------------------");
// Method getDeclaredMethod(String name, Class<?>... parameterTypes)
Method method4 = cs.getDeclaredMethod("eat",String.class);
System.out.println(method4);
}
}
结果如下:
public java.lang.String MyReflect.Student.getName()
public java.lang.String MyReflect.Student.toString()
public void MyReflect.Student.sleep()
public void MyReflect.Student.setName(java.lang.String)
public int MyReflect.Student.getAge()
public void MyReflect.Student.setAge(int)
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public final void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
---------------------
public java.lang.String MyReflect.Student.getName()
public java.lang.String MyReflect.Student.toString()
public void MyReflect.Student.sleep()
public void MyReflect.Student.setName(java.lang.String)
public int MyReflect.Student.getAge()
public void MyReflect.Student.setAge(int)
private void MyReflect.Student.eat(java.lang.String)
---------------------
public void MyReflect.Student.sleep()
---------------------
private void MyReflect.Student.eat(java.lang.String)
获取成员变量中的信息
getModifiers()
获取权限修饰符getName()
获取成员方法的名字getParameters()
获取成员方法的形参
在 Student 类中的 eat 方法 throws Throwable
代码示例:
package MyReflect;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class reflectDemo {
public static void main(String[] args) throws Throwable {
Class cs = Class.forName("MyReflect.Student");
Method method = cs.getDeclaredMethod("eat",String.class);
System.out.println(method);
Parameter[] parameters = method.getParameters();
for(Parameter parameter : parameters) {
System.out.println(parameter);
}
Class[] exceptionTypes = method.getExceptionTypes();
for(Class exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
}
}
结果如下:
private void MyReflect.Student.eat(java.lang.String) throws java.io.IOException
java.lang.String arg0
class java.io.IOException
Method 类中用于运行方法的方法
Object invoke(Object obj, Object... args)
运行方法- 参数一:用
obj
对象调用该方法 - 参数二:调用方法时传递的参数(若无参数则不写)
- 返回值:方法的返回值(若无返回值则不写)
- 参数一:用
代码示例:
package MyReflect;
import java.lang.reflect.Method;
public class reflectDemo {
public static void main(String[] args) throws Exception {
Class cs = Class.forName("MyReflect.Student");
Method method = cs.getDeclaredMethod("eat",String.class);
System.out.println(method);
method.setAccessible(true);
Student s = new Student();
method.invoke(s, "汉堡包");
}
}
结果如下:
private void MyReflect.Student.eat(java.lang.String) throws java.io.IOException
在吃汉堡包
假设 eat 方法改为:
private String eat(String something) throws IOException{
System.out.println("在吃" + something);
return "真好吃!";
}
reflectDemo 类:
package MyReflect;
import java.lang.reflect.Method;
public class reflectDemo {
public static void main(String[] args) throws Exception {
Class cs = Class.forName("MyReflect.Student");
Method method = cs.getDeclaredMethod("eat",String.class);
System.out.println(method);
method.setAccessible(true);
Student s = new Student();
Object result = method.invoke(s, "汉堡包");
System.out.println(result);
}
}
结果如下:
private java.lang.String MyReflect.Student.eat(java.lang.String) throws java.io.IOException
在吃汉堡包
真好吃!
反射的作用
- 获取一个类里面所有的信息,获取到之后,再执行其他的业务逻辑
- 结合配置文件,动态的创建对象并调用方法
案例 1:保存信息
对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中
Student 类:
package MyReflect;
public class Student {
private String name;
private int age;
public Student() {}
public Student(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;}
}
reflectDemo 类:
package MyReflect;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
public class reflectDemo {
public static void main(String[] args) throws Exception {
Student s1 = new Student("张三",18);
saveObject(s1);
}
public static void saveObject(Object obj) throws IOException, IllegalAccessException {
// 获取字节码文件的对象
Class cs = obj.getClass();
// 创建IO流
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
// 获取所有成员变量
Field[] fields = cs.getDeclaredFields();
for(Field field : fields) {
field.setAccessible(true);
// 获取成员变量的名字
String name = field.getName();
// 获取成员变量的值
Object value = field.get(obj);
// 写出数据
bw.write(name + " = " + value);
bw.newLine();
}
bw.close();
}
}
结果如下:
name = 张三
age = 18
案例 2:跟配置文件结合动态创建
反射可以跟配置文件结合的方式,动态的创建对象,并调用方法
prop.properties 配置文件:
classname = MyReflect.Student
method = study
Student 类:
package MyReflect;
public class Student {
private String name;
private int age;
public Student() {}
public Student(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;}
public void study() {
System.out.println("正在学习!");
}
}
reflectDemo 类:
package MyReflect;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
public class reflectDemo {
public static void main(String[] args) throws Exception {
// 读取配置文件中的信息
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("prop.properties");
prop.load(fis);
fis.close();
System.out.println(prop);
// 获取全类名和方法名
String className = (String) prop.get("classname");
String methodName = (String) prop.get("method");
System.out.println(className);
System.out.println(methodName);
// 利用反射创建对象并运行方法
Class cs = Class.forName(className);
// 获取构造方法
Constructor con = cs.getDeclaredConstructor();
Object o = con.newInstance();
System.out.println(o);
// 获取成员方法并运行
Method method = cs.getDeclaredMethod(methodName);
method.setAccessible(true);
method.invoke(o);
}
}
结果如下:
{classname=MyReflect.Student, method=study}
MyReflect.Student
study
MyReflect.Student@136432db
正在学习!
动态代理
概述
特点:无入侵式的给代码增加额外的功能
为什么要有代理?
- 代理是为了在不改变原有对象代码的前提下,实现功能增强、访问控制、简化复杂操作和方便测试,提升代码的灵活性、可维护性和可测试性(对象如果嫌身上干的事太多了,可以通过代理来转移部分职责)
代理是什么样的?
- 代理是一个与目标对象实现相同接口并包裹其引用的类,分为静态编写和动态生成两种形式,用于控制访问或增强功能(对象有什么方法想被代理,代理就一定要有对应的方法)
创建代理对象
java.lang.reflect.Proxy 类:提供了为对象产生代理对象的方法
public static Object newProxyInstance(ClasLoader loader, Class<?>[] interfaces, InvocationHandler h)
- 参数一:用于指定哪个类加载器,去生成类加载器的代理类
- 参数二:指定接口,这些接口用于指定生成的代理长什么样,有哪些方法
- 用来指定生成的代理对象要做什么
InvocationHandler
是一个接口,其中的方法是:
public Object invoke(Object proxy, Method method, Object[] args)
- 参数一:代理的对象(一般不用)
- 参数二:要运行的方法
- 参数三:调用方法时传递的实参
- 使用匿名内部类去写
案例
大明星坤坤准备开个唱跳演唱会,但是他不能直接去向粉丝收取演唱会的费用,这个时候公司就会给坤坤分配一个代理——经纪人,让经纪人来收取费用
坤坤 BigStar 类:
public class BigStar implements firm{
private String name;
public BigStar() {}
public BigStar(String name) {
this.name = name;
}
public String sing(String name) {
System.out.println(this.name + "开始唱歌");
System.out.println(this.name + "正在唱" + name);
System.out.println(this.name + "结束唱歌");
return "唱歌部分结束,接下来是跳舞";
}
public String dance() {
System.out.println(this.name + "正在跳舞");
System.out.println(this.name + "结束跳舞");
return "演唱会结束了";
}
}
公司安排经纪人 firm 接口:
public interface firm {
public String sing(String name);
public String dance();
}
代理经纪人 ProxyUtil 类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
public static firm creatProxy(BigStar bigstar) {
firm starProxy = (firm) Proxy.newProxyInstance(
// 用当前类的类加载器去加载代理
ProxyUtil.class.getClassLoader(),
// 可以有多个接口
new Class[] {firm.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("sing".equals(method.getName())) {
System.out.println("演唱会费用已收完");
}else if("dance".equals(method.getName())) {
System.out.println("准备开始跳舞");
}
// 这里的invoke方法是反射获取成员方法中的方法
return method.invoke(bigstar, args);
}
});
return starProxy;
}
}
演唱会 Concert 类:
public class Concert {
public static void main(String[] args) {
// 获取代理的对象
BigStar bigstar = new BigStar("坤坤");
firm firmProxy = ProxyUtil.creatProxy(bigstar);
String song = firmProxy.sing("及你太美");
System.out.println(song);
String dance = firmProxy.dance();
System.out.println(dance);
}
}
结果如下:
演唱会费用已收完
坤坤开始唱歌
坤坤正在唱及你太美
坤坤结束唱歌
唱歌部分结束,接下来是跳舞
准备开始跳舞
坤坤正在跳舞
坤坤结束跳舞
演唱会结束了