反射
本文章为个人“Java林基础实战”学习笔记,仅供参考学习,侵权删
反射,它完成的是通过一个实例化对象映射到类,这样我们在程序运行期间就可以获取类的信息了。注意这里讲的通过反射获取类信息是在程序运行期间,我们可以直接看到类结构是静态的,而程序运行起来是动态的,两者是不同的两个概念。我们要做的就是在运行期间获取类的结构然后完成某些特定功能。一句话来简单理解反射:常规情况下是通过类来创建实例化对象的,反射就是将这一过程进行了反转,通过实例化对象来获取对应的类信息。
Class 类
Class 类是反射的基础,反射就是通过对象来获取类的信息。Java 世界中的一切都可以看做对象,这里也不例外,可以将通过反射获取的类信息抽象成对象。
既然是对象,那么就一定有对应的类,这个类就是 Class。所以 Class 这个类你可以把它理解为专门用来描述其他类的类,Class 的每一个实例化对象对应的都是其他类的结构特征。
在外部不能通过构造函数来实例化 Class 对象,应为 Class 类只有一个私有的构造函数。既然 Class 的实例化对象是描述其他类的,那么在创建 Class 对象时就需要用到被描述的类,创建 Class 实例化对象的方式有 3 种:
- 调用 Class 的静态方法 forName(String className) 创建,将目标类的全限定类名(全限定类名就是包含所在包信息的类名全称,如java.lang.String) 作为参数传入,即可获取对应的 Class 对象。
- 通过目标类的 class 类创建,Java 中的每一个类都可以调用类.class,这里的 class不是属性,它叫作 ”类字面量“ ,其作用是获取内存中目标类型 class 对象的引用。
- 通过目标类实例化对象的 getClass() 方法创建。getClass() 方法定义在 Object 类中,被所有类继承,通过实例化对象获取内存中该类 class 对象的引用
public class TestClass {
public static void main(String[] args) {
Class clazz = null;
try {
clazz = Class.forName("java.lang.String");
System.out.println(clazz);
} catch (ClassNotFoundException e) {
// TODO: handle exception
}
Class clazz2 = String.class;
System.out.println(clazz2);
String str = new String("Hello");
Class clazz3 = str.getClass();
System.out.println(clazz3);
}
}
运行结果:
class java.lang.String
class java.lang.String
class java.lang.String
我们可以看到在结果中打印了 3 次 “class java.lang.String” ,为什么会是这样的结果呢?因为在打印对象时,会自动调用它的 toString() 方法将该对象转为一个字符串。toString() 是定义在 Object 类中的方法,所有的类都可以继承并进行重写,很显然 Class 类就对 toString() 方法进行了重写 。
在上述代码中我们获取了 3 个 Class 对象,那么 这 3 个 Class 对象是否相等,即在内存中到底是创建了一个 Class 对象还是 3 个 Class 对象呢?
public class TestClass {
public static void main(String[] args) {
Class clazz = null;
try {
clazz = Class.forName("java.lang.String");
System.out.println(clazz);
} catch (ClassNotFoundException e) {
// TODO: handle exception
}
Class clazz2 = String.class;
System.out.println(clazz2);
String str = new String("Hello");
Class clazz3 = str.getClass();
System.out.println(clazz3);
System.out.println(clazz.hashCode());
System.out.println(clazz2.hashCode());
System.out.println(clazz3.hashCode());
System.out.println(clazz == clazz2);
System.out.println(clazz2 == clazz3);
}
}
运行结果:
class java.lang.String
class java.lang.String
class java.lang.String
1829164700
1829164700
1829164700
true
true
通过结果可以看到 3 个 Class 对象的散列值相同,同时以此比较 clazz 和 clazz2,clazz2和clazz3 的内存地址也相同,证明这 3 个 Class 对象是相等的,但内存中只有一份。这一点很好理解,因为 Class 的实例对象是用来描述某个目标类的,而每一个目标类的运行时类在内存中只有一份,所以队形的 Class 对象也只有一份
获取类结构
Class 是整个反射机制的源头,跟反射相关的操作大部分是基于对 Class 对象的操作,即获取目标类的信息都是通过调用 Class 的相关方法来完成的。类的信息包括其内部的成员变量、方法、构造函数、继承的父类和实现的接口等,本节我们就逐一讲解如何获取类的这些信息,Class 类的常用方法如表:
方法 | 描述 |
---|---|
public native boolean isInterface() | 判断该类是否为接口 |
public native boolean isArray() | 判断该类是否为数组 |
public boolean isAnnotaion() | 判断该类是否为注解 |
public String getName() | 获取该类的全限定类名 |
public ClassLoader getClassLoader() | 获取类加载器 |
public native Class<? super T> getSuperclass() | 获取该类的直接父类 |
public Package getPackage() | 获取该类所在的包 |
public String getPackageName() | 获取该类所在包的名称 |
public Class<?>[] getInterfaces() | 获取该类的全部接口 |
public native int getModifiers() | 获取该类的访问权限修饰符 |
public Field[] getFields() throws SecurityException | 获取该类的全部公有成员变量,包括继承自父类的和自己定义的 |
public Field[] getDeclaredFields() throws SecurityException | 获取该类的自己定义的成员变量 |
public Field getField(String name) throws NoSuchFieldException,SecurityException | 通过名称获取该类的公有成员变量,包括继承自父类和自己定义的 |
public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException | 通过名称来获取该类的自己定义成员变量 |
public Method[] getMethods() throws SecurityException | 获取该类的全部公有方法 ,包括继承自父类的和自己定义的 |
public Method[] getDeclaredMethods() throws SecurityException | 获取该类的自定义方法 |
public Method getMethod(String name,Class<?>…parameterTypes) throws NoSuchFieldException,SecurityException | 通过名称和参数信息获取该类的公有方法,包括继承自父类的自己定义的 |
public Method getDeclaredMethdo(String name,Class<?>…parameterTypes) throws NoSuchFieldException, SecurityException | 通过名称和参数信息获取该类的自定义方法 |
public Constructor<?>[] getConstructors() throws SecurityException | 获取该类的公有构造函数 |
public Constructor<?>[] getDeclaredConstructors() throws SecurityException | 获取该类的全部构造函数 |
public Constructor getConstructor(Class<?>…parameterTypes) throws NoSuchMethodException, SecurityException | 通过参数信息获取该类的公有构造函数 |
public Constructor getDeclaredConstructor(Class<?>…paramenterTypes) throws NoSuchMethodException, SecurityException | 通过参数信息获取该类的构造函数 |
获取类的接口
getInterface() 方法的返回值是一个 Class 类型的数组,首先为什么是数组呢?因为一个类是可以同时实现多个接口的,所以需要用数组来保存返回值,表示一个或多个。数组的类型为什么是 Class 呢?因为这里同样是用对象来描述接口的信息,所以返回的是 Class 类型的对象。getInterfaces() 方法的具体使用如下:
class Student implements Serializable,Comparable<Student>{
@Override
public int compareTo(Student o) {
return 0;
}
}
public class TestInterface {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.al.classs.Student");
Class[] interfaces = clazz.getInterfaces();
for(Class c : interfaces) {
System.out.println(c);
}
} catch (ClassNotFoundException e) {
// TODO: handle exception
}
}
}
运行结果:
interface java.io.Serializable
interface java.lang.Comparable
获取父类
Class 类提供了 getSuperclass() 方法来获取目标类的父类
package com.al.classs;
class People{
}
class Student2 extends People{
}
public class TestGetSuperclass {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.al.classs.Student2");
Class superClass = clazz.getSuperclass();
System.out.println("Student2 的父类是:"+superClass);
clazz = Class.forName("com.al.classs.People");
superClass = clazz.getSuperclass();
System.out.println("People 的父类是:"+superClass);
} catch (ClassNotFoundException e) {
// TODO: handle exception
}
}
}
运行结果:
Student2 的父类是:class com.al.classs.People
People 的父类是:class java.lang.Object
获取构造函数
Class 提供了 4 个方法获取构造函数,分别是 getConstructor(Class<?>…parameterTypes);getConstructors(); getDeclaredConstructor(Class<?>…parameterTypes) 和 getDeclaredConstructors().
class People3{}
class Student3 extends People3{
public Student3() {
}
private Student3(int id) {
}
}
public class TestGouZao {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.al.classs.Student3");
Constructor[] constructors = clazz.getConstructors();
System.out.println("*************公有构造函数******************");
for(Constructor c : constructors) {
System.out.println(c);
}
System.out.println("**************全部构造函数******************");
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(Constructor c : declaredConstructors) {
System.out.println(c);
}
System.out.println("**************公无参构造函数***************");
Constructor constructor = clazz.getConstructor(null);
System.out.println(constructor);
System.out.println("**************私有带参构造函数**************");
Constructor declaredConstructor = clazz.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);
} catch (ClassNotFoundException e) {
// TODO: handle exception
} catch(NoSuchMethodException e) {
} catch(SecurityException e) {
}
}
}
运行结果:
公有构造函数*****
public com.al.classs.Student3()
全部构造函数****
public com.al.classs.Student3()
private com.al.classs.Student3(int)
公无参构造函数*
public com.al.classs.Student3()
私有带参构造函数
private com.al.classs.Student3(int)
获取方法
class People4{
public void setName(String name) {}
private int getId() {
return 1;
}
}
class Student4 extends People4{
public void setId() {}
private String getName() {
return "张三";
}
}
public class TestGetMethod {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.al.classs.Student4");
Method[] methods = clazz.getMethods();
System.out.println("************公有方法************");
for(Method m : methods) {
System.out.println(m);
}
System.out.println("************本类的方法*************");
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m : declaredMethods) {
System.out.println(m);
}
System.out.println("***********公有方法setName***********");
Method method = clazz.getMethod("setName", String.class);
System.out.println(method);
System.out.println("************本类方法GetName************");
Method declaredMethod = clazz.getDeclaredMethod("getName", null);
System.out.println(declaredMethod);
} catch (ClassNotFoundException e) {
// TODO: handle exception
}catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
公有方法
public void com.al.classs.Student4.setId()
public void com.al.classs.People4.setName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
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()
本类的方法*
private java.lang.String com.al.classs.Student4.getName()
public void com.al.classs.Student4.setId()
公有方法setName
public void com.al.classs.People4.setName(java.lang.String)
本类方法GetName
private java.lang.String com.al.classs.Student4.getName()
获取成员变量
class People5{
public int id;
private String name;
}
class Student5 extends People5{
public int age;
private String hobby;
}
public class TestGetField {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.al.classs.Student5");
Field[] fields = clazz.getFields();
System.out.println("*************公有成员变量**********");
for(Field f : fields) {
System.out.println(f);
}
System.out.println("************本类成员变量***********");
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields) {
System.out.println(f);
}
System.out.println("***********公有成员变量id**********");
Field field = clazz.getField("id");
System.out.println(field);
System.out.println("************本类成员变量hobby*********");
Field declaredField = clazz.getDeclaredField("hobby");
System.out.println(declaredField);
} catch (ClassNotFoundException e) {
// TODO: handle exception
}catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
***公有成员变量
public int com.al.classs.Student5.age
public int com.al.classs.People5.id
*本类成员变量
public int com.al.classs.Student5.age
private java.lang.String com.al.classs.Student5.hobby
*公有成员变量id
public int com.al.classs.People5.id
***本类成员变量hobby
private java.lang.String com.al.classs.Student5.hobby
反射的应用
前面的章节我们了解了 Class 类的常用方法,如获取成员变量、获取方法等。那么我们获取到这些类信息到底有什么用呢?在实际开发中应该如何使用反射呢?
反射调用方法
常规情况下,我们需要先创建实例化对象,然后调用对象的方法,操作的是实例化对象。反射就是将常规方式进行反转,首先创建实例化对象,然后改对象的方法抽象成对象,我们称之为方法对象。调用方法对象的 invoke 方法来实现业务需求,并将实例化对象作为参数传入 invoke 方法中。整个过程操作的是方法对象,与常规方式恰好相反。
我们先定义一个Student类
public class Student {
private int id;
public String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student() {
}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public void showInfo() {
System.out.println("学生信息");
System.out.println("ID:"+this.id);
System.out.println("姓名:"+this.name);
}
@Override
public String toString() {
return "Student [id="+id+", name="+name+"]";
}
}
使用 invoke 方法
public class MethodDemo {
public static void main(String[] args) {
Student student = new Student();
student.setId(1);
student.setName("张三");
Class clazz = student.getClass();
try {
//获取showInfo方法
Method method = clazz.getDeclaredMethod("showInfo", null);
//通过 invoke 方法调用
method.invoke(student, null);
} catch (Exception e) {
// TODO: handle exception
}
}
}
运行结果:
学生信息
ID:1
姓名:张三
可以看到是通过 invoke 方法来完成目标方法调用的,invoke 是 Method 类中定义的方法,该方法的作用是通过反射来执行目标方法该方法的参数报考两类,一类是一个 Object 类型的参数,另一类是一组 Object 类型的参数。前者表示调用该方法的对象,因为虽然是通过反射机制来执行,但是 Java 中非静态方法的调用都是由对象来完成的。后者是一组可变参数,表示调用该方法需要传入参数。
同时方法的返回值为 Object 类型,这里用到了多态,因为方法定义时并不知道开发者需要调用的方法返回值是什么类型,所以这里定义为 Object,可以指代任意数据类型。接下来我们演示有返回值的 getName() 方法
public class MethodDemo2 {
public static void main(String[] args) {
Student student = new Student(1,"张三");
Class clazz = student.getClass();
try {
//获取 getName 方法
Method method = clazz.getDeclaredMethod("getName", null);
//通过 invoke 方法调用
String name = (String) method.invoke(student, null);
System.out.println(name);
} catch (Exception e) {
// TODO: handle exception
}
}
}
运行结果:
张三
反射访问成员变量
通过反射机制,我们也可以在程序运行期间访问成员变量,并获取成员变量的相关信息,如名称、数据类型、访问权限等
public class FieldDemo {
public static void main(String[] args) {
Class clazz = Student.class;
Field[] fields = clazz.getDeclaredFields();
for(Field f : fields) {
int modifiers = f.getModifiers();
Class typeClass = f.getType();
String name = f.getName();
System.out.println("成员变量"+name+"的数据类型是:"+typeClass.getName()
+",访问权限是:"+getModifiers(modifiers));
}
}
public static String getModifiers(int modifiers) {
String result = null;
switch(modifiers) {
case 0:
result = "";
break;
case 1:
result = "public";
break;
case 2:
result = "private";
break;
case 3:
result = "protected";
break;
}
return result;
}
}
运行结果:
成员变量id的数据类型是:int,访问权限是:private
成员变量name的数据类型是:java.lang.String,访问权限是:public
f.getModifiers() 方法返回的是 int 类型的数据,不同的值对应不同的访问权限,通过 getModifiers(int modifiers) 完成转换
上面的代码完成了对成员变量的访问,后去了成员变量的信息。同时我们也可以对成员变量的值进行修改,通过调用 Field 的 set() 方法即可
public void set(Object obj, Object value) 参数列表为 Object obj 和 Object value,这两个 Object 分别表示什么呢?obj 表示被修改的对象,value 表示修改之后的值,这里的用法和上一届介绍的 Method 的 invoke() 方法很相似
public class FieldDemo2 {
public static void main(String[] args) {
Class clazz = Student.class;
Field[] fields = clazz.getDeclaredFields();
Student student = new Student();
for(Field field : fields) {
try {
if(field.getName().equals("id")) {
field.set(student, 2);
}
if(field.getName().equals("name")) {
field.set(student, "张三");
}
} catch (Exception e) {
// TODO: handle exception
}
}
student.showInfo();
//或者这样
try {
Method method = clazz.getDeclaredMethod("showInfo", null);
method.invoke(student, null);
} catch (Exception e) {
// TODO: handle exception
}
}
}
运行结果:
运行程序会抛出异常
异常是因为修改了 private 修饰的成员变量,我们知道私有成员变量在外部是无法访问的,所以会抛出异常,这个问题如何解决呢?第 1 种方法可以修改成员变量的访问权限修饰符,让其变为公有,允许外部直接访问。第 2 种方法是通过反射的 “暴力修改” 来解决,所谓的 “暴力修改” 是指修改 private 成员变量的访问权限,设置为 true时,表示可以修改,false 表示不能修改,会抛出异常,默认值为 false。可以通过调用 setAccessible(boolean flag) 方法完成权限设置。
通过暴力修改来设置成员变量的访问权限
public class FieldDemo2 {
public static void main(String[] args) {
Class clazz = Student.class;
Field[] fields = clazz.getDeclaredFields();
Student student = new Student();
for(Field field : fields) {
try {
if(field.getName().equals("id")) {
field.setAccessible(true);
field.set(student, 2);
}
if(field.getName().equals("name")) {
field.set(student, "张三");
}
} catch (Exception e) {
// TODO: handle exception
}
}
student.showInfo();
//或者这样
try {
Method method = clazz.getDeclaredMethod("showInfo", null);
method.invoke(student, null);
} catch (Exception e) {
// TODO: handle exception
}
}
}
运行结果:
学生信息
ID:2
姓名:张三
学生信息
ID:2
姓名:张三
反射调用构造函数
反射就是将传统的开发方式进行反转,通过对象来获取类的结构信息,并且用一个 Class 对象来描述。类的结构信息包括了成员变量、方法、构造函数等,前两节我们分别演示了反射访问成员变量以及反射调用方法,本节我们来学习通过反射机制调用构造函数创建实例化对象。
反射机制下用 Constructor 类来描述构造函数,同时一个类可以拥有多个构造函数,通过 getConstructors() 方法和 getConstructor(Classs<?>…parameterTypes) 方法来获取相应的构造函数。本节我们重点学习如何使用构造函数创建对象,通过调用 Constructor 类的 newInstance (Object … initargs) 来完成对象的创建。
public class ConstructorDemo {
public static void main(String[] args) {
Class clazz = Student.class;
try {
//获取student午餐构造函数
Constructor<Student> constructor = clazz.getConstructor(null);
Student student = constructor.newInstance(null);
System.out.println(student);
//获取有参构造函数
Constructor<Student> constructor2 = clazz.getConstructor(int.class, String.class);
Student student2 = constructor2.newInstance(1,"张三");
System.out.println(student2);
} catch (Exception e) {
// TODO: handle exception
}
}
}
运行结果:
Student [id=0, name=null]
Student [id=1, name=张三]
代理模式
Java 中的动态代理就是反射的一个重要应用,本节我们就来详细讲解动态代理。
代理模式是一种常用的 Java 设计模式,值的是软件设计所遵循的一套理论和准则。代理模式就是指在处理一个业务逻辑时,通过代理的方式完成。既然是代理方式,就一定有被代理方或者叫委托方,和代理方,即委托方委托代理方帮他完成某些工作。在所有的代理关系中,委托方和代理方都有一个共性,即双方都就别完成需求的能力。在程序中如何标书这种关系呢?在 Java 程序中,我们把对象所具有的能力封装成接口,所以 Java 中代理模式的特点是委托类和代理类实现了同样的接口,代理类可以代替委托类完成一些核心业务以外的工作,例如消息处理、过滤消息以及事后处理消息等。
代理类与委托类之间通过依赖注入进行关联,即在设计程序时需要将委托类定义为代理类的成员变量。代理类本身并不会去执行业务逻辑,而是通过调用委托类的方法来完成的。简单来讲,我们在访问委托类对象时,是通过代理类对象来简介访问的。代理模式就是通过这种间接访问的方式,为程序预留出了可处理的空间,利用洗空间,在不影响核心业务的基础上可以附加其他的业务,这就是代理模式的优点。
代理模式又可以分为静态代理和动态代理,两者的区别在于静态代理需要预先写好代理类的代码,在编译期代理类的 calss 文件就已经生成了。而动态代理是指在编译期并没有确定具体的代理类,在程序运行期间根据 Java 代码的知识动态地生成的方式。简单地理解静态代理是预先写好代理类,动态代理是程序运行期间动态生产代理类。很显然动态代理的方式更加灵活,可以很方便对代理类的方法进行统一的处理,而不需要注意修改每一个代理类中的方法。期中静态代理跟反射没有什么直接联系,动态代理就是运用反射机制来实现的。
我们先来了解一下静态代理,通过一个例子来编写静态代理的代码。例如销售 iPhone 和 华为两个牌子的手机,首先需要定义一个接口 Phone 表示销售手机的功能,然后定义两个实现类分别销售 iPhone 和 华为手机。
interface Phone{
public String salePhone();
}
class Apple implements Phone{
@Override
public String salePhone() {
return "销售 iPhone 手机";
}
}
class HuaWei implements Phone{
@Override
public String salePhone() {
return "销售华为手机";
}
}
public class Test {
public static void main(String[] args) {
Phone phone1 = new Apple();
System.out.println(phone1.salePhone());
Phone phone2 = new HuaWei();
System.out.println(phone2.salePhone());
}
}
运行结果:
销售 iPhone 手机
销售华为手机
- 现在有一家代理手机厂商,即可以销售 iPhone 手机也可以销售华为手机,用 Java 程序实现这一过程需要用到静态代理模式。先创建 PhoneProxy 类,实现 Phone 接口,同时定义一个 Phone 类型的成员变量,用来接收委托对象,并代理委托对象完成 salePhone() 方法。实际是调用委托对象的 salePhone 方法,并且可以添加其他的业务逻辑
class PhoneProxy implements Phone{
private Phone phone;
public PhoneProxy(Phone phone) {
this.phone = phone;
}
@Override
public String salePhone() {
System.out.println("代理模式");
return this.phone.salePhone();
}
}
public class Test2 {
public static void main(String[] args) {
PhoneProxy phoneProxy = new PhoneProxy(new Apple());
System.out.println(phoneProxy.salePhone());
phoneProxy = new PhoneProxy(new HuaWei());
System.out.println(phoneProxy.salePhone());
}
}
运行结果:
代理模式
销售 iPhone 手机
代理模式
销售华为手机
这样就完成了一个静态代理,其优势在于如果完成业务扩展,不需要修改委托类 Apple 或者 HuaWei 中的 salePhone() 方法,只需要改动代理类 PhoneProxy 中的 salePhone() 方法即可,这在分离不同业务的同时保证代码的整洁。统一我们来完成一个代理厂商可以同时销售宝马和奔驰两个品牌汽车的例子。
interface Car{
public String saleCar();
}
class BMW implements Car{
@Override
public String saleCar() {
return "销售宝马汽车";
}
}
class Benz implements Car{
@Override
public String saleCar() {
return "销售奔驰汽车";
}
}
class CarProxy implements Car {
private Car car;
public CarProxy(Car car) {
this.car = car;
}
@Override
public String saleCar() {
return this.car.saleCar();
}
}
public class Test3 {
public static void main(String[] args) {
CarProxy carProxy = new CarProxy(new BMW());
System.out.println(carProxy.saleCar());
carProxy = new CarProxy(new Benz());
System.out.println(carProxy.saleCar());
}
}
运行结果:
代理模式
销售宝马汽车
代理模式
销售奔驰汽车
好了,现在的需求是创建一个厂商,既可以代理销售手机又可以代理销售汽车。那么使用静态代理肯定是不行的,因为无论是 PhoneProxy 还是 CarProxy ,都只能代理一种商品的销售,要么是汽车,要么是手机。因为 PhoneProxy 类和 CarPhone 类都是提前写好的,无法根据需求动态修改
使用动态代理就可以很好地完成这一需求,前面我们提到过动态代理是指在程序运行时动态生成代理类,那么这个动态生成的功能谁来完成的呢?java.lang.reflect 包中提供了 InvocationHandler 接口,通过该接口可以在程序运行期间动态生成代理类。首先,自定义一个类 MyInvocationHandler, 实现 InvocationHandler 接口,这个类就是动态代理类的模板。
class MyInvocationHandler implements InvocationHandler{
private Object obj = null;
public Object bind(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(),
obj.getClass().getInterfaces(), (InvocationHandler) this);
}
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
System.out.println("代理模式");
Object result = method.invoke(obj, args);
return result;
}
}
MyInvocationHandler 类中定义的委托类成员变量的数据类型为 Object,它可以接受任意数据类型的委托类,即这个代理商什么样的产品都可以代理,这里就用到了多态的机制。MyInvocationHandler 类的 bind() 方法的作用是返回一个代理对象提供外部调用,这个代理对象是通过 Proxy 类的 newProxyInstance() 方法来创建的。public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
参数列表中的 loader 是类加载器,在程序运行期将动态生成的代理类加载到内存中。 interfaces 是委托类的接口,动态代理机制需要获取到委托类的所有接口信息,以便让动态代理类也具备相同的功能,h 表示当前的 InvocationHandler 对象。MyInvocationHandler 类中还有一个 invoke() 方法,在该方法中可以获取到委托类的方法对象 Method,然后通过反射机制来调用委托对象的业务方法,同时可以添加其他的业务逻辑代码
public class Test4 {
public static void main(String[] args) {
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Phone phone = (Phone) myInvocationHandler.bind(new Apple());
System.out.println(phone.salePhone());
Car car = (Car) myInvocationHandler.bind(new BMW());
System.out.println(car.saleCar());
}
}
运行结果:
代理模式
销售 iPhone 手机
代理模式
销售宝马汽车