1 java的反射机制
1.1 概述
- 反射是被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以,我们形象地称之为:反射。
1.2 动态语言 vs 静态语言
1.2.1 动态语言
- 是一个类在运行的时候可以改变其结构的语言:例如新的函数、对象、甚至对象可以被引进,已有的函数可以被删除或者其他结构上的变化。通俗的说就是在运行时代码可以根据某些条件改变自身结构。
- 主要的动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang等。
1.2.2 静态语言
- 和动态语言相对应的,运行时结构不可变的语言就是静态语言。
- 主要的静态语言:Java、C、C++等。
1.2.3 准动态语言
- java不是动态语言,但java可以称之为“准动态语言”。即java有一定的动态性,我们可以利用反射机制、字节码操作获取类似动态语言的特性。
- java的动态性让编程的时候更加灵活。
1.3 Java反射机制的应用
- 在运行的时候判断任意一个对象所属的类。
- 在运行的时候构造任意一个类的对象。
- 在运行的时候判断任意一个类所具有的成员变量和方法。
- 在运行的时候获取泛型信息。
- 在运行的时候调用任意一个对象的成员变量和方法。
- 在运行的时候处理注解。
- 生成动态代理。
1.4 反射相关的主要API
- java.lang.Class:代表一个类。
- java.lang.reflect.Method:代表类的方法。
- java.lang.reflect.Field:代表类的成员变量。
- java.lang.reflect.Constructor:代表类的构造器。
- ……
2 理解Class类并获取Class的实例
2.1 Class类
- 在Object类中定义了一下的方法,此方法将被所有子类继承:
public final native Class<?> getClass();
- 以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
- 对象通过反射后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了那些接口。对于每个类而言,JRE都为其保留了一个不变的Class类型的对象。一个Class对象包含了特定某个结构的有关信息。
- Class本身也是一个类。
- Class对象只能由系统建立对象。
- 一个加载的类在JVM中只会有一个Class实例。
- 一个Class对象对应的是一个加载到JVM中的一个.class文件。
- 每个类的实例都会记得是由哪个Class实例所生成。
- 通过Class可以完整的得到一个类中的所有被加载的结构。
- Class类是反射的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
2.2 获取Class类的实例
2.2.1 第一种方式
- 如果已知具体的类,那么可以通过类的class属性获取,该方法最为安全可靠,程序性能最高。
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ClassTest {
public static void main(String[] args) {
Class<?> clazz = String.class;
}
}
2.2.2 第二种方式
- 已经知道某个类的实例,调用该实例的getClass()方法获取Class对象。
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ClassTest {
public static void main(String[] args) {
Class<?> clazz = "helloworld".getClass();
}
}
2.2.3 第三种方式
- 如果知道一个类的全类名(完整的包名.类名),并且该类在类路径下,可以通过Class类的静态方法forName()虎丘,可以抛出ClassNotFoundException。
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ClassTest {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
3 那些类型可以有Class对象?
- 类:外部类、内部类(成员内部类、静态内部类)、局部内部类、匿名内部类。
- 接口
- 数组
- 枚举
- 注解
- 基本数据类型
- void
package day24;
import java.lang.annotation.ElementType;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ClassTest {
public static void main(String[] args) {
Class<Object> c1 = Object.class;
Class<Comparable> c2 = Comparable.class;
Class<String[]> c3 = String[].class;
Class<int[][]> c4 = int[][].class;
Class<ElementType> c5 = ElementType.class;
Class<Override> c6 = Override.class;
Class<Integer> c7 = int.class;
Class<Void> c8 = void.class;
Class<Class> c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class<? extends int[]> c10 = a.getClass();
Class<? extends int[]> c11 = b.getClass();
System.out.println(c10 == c11);//只要元素类型和纬度一样,就是同一个Class
}
}
4 类的加载和ClassLoader的理解
4.1 类的加载过程
- 当程序主动使用某个类的时候,如果该类还没被加载到内存中,则系统会通过如下的步骤来对该类进行初始化。

- 加载:将class字节码文件内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载过程需要类加载器的参与。
- 链接:将Java类的二进制代码合并到JCM的运行状态的过程。
- 验证:确保加载的类信息符合JVM规则。
- 准备:正式为类变量(static)分配内存并设置类变量的默认初始化值的节点,这些内存都将在方法去中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
- 初始化:
- 执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静代码块中的语言合并产生的。(类构造器是构造类信息的,并不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加载和同步。
4.2 类的加载器
4.2.1 类的加载器图示

4.2.2 类加载器的作用
- 将class字节码文件加载到内存中,并将这些静态数据转换成方法区的运行时的数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
4.2.3 类缓存
- 标准的类加载器可以按要求查找类,一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾回收机制可以回收这些class对象。
4.3 ClassLoader
4.3.1 JVM规范中定义的类的加载器

4.3.2 各种类加载器的说明
- 引导类加载器:用C++编写的,是Java自带的类加载器,负责java平台核心库,用来装载核心类库。该加载器无法直接获取。
- 扩展类加载器:负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库。
- 系统类加载器:负责java的classpath或java.class.path所指的目录下的类和jar包装入工作,是最常用的加载器。
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ClassLoaderTest {
public static void main(String[] args) {
//String类是核心库中的,所以无法获取其引导类加载器
ClassLoader classLoader = String.class.getClassLoader();
System.out.println(classLoader);
}
}
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ClassLoaderTest {
public static void main(String[] args) {
//系统类记载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器:" + systemClassLoader);
//扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println("扩展类加载器:" + parent);
//引导类加载器
ClassLoader parent1 = parent.getParent();
System.out.println("引导类加载器:" + parent1);//null
}
}
5 创建运行时类的对象
5.1 通过调用无参构造的方式创建类的对象
5.1.1 通过调用Class对象的newInstance()方法来创建类的对象
- 前提:
- ①类必须提供一个无参的构造器。
- ②类的构造器的访问权限需要足够。


package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
View Code
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ReflectTest {
public static void main(String[] args) {
Class<Person> personClass = Person.class;
try {
Person person = personClass.newInstance();
System.out.println(person);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
5.1.1 通过Constructor对象的getDeclaredConstructor()方法


package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
View Code
package day24;
import java.lang.reflect.Constructor;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;
Constructor<?> constructor = clazz.getDeclaredConstructor();
//取消类的访问控制
constructor.setAccessible(true);
Object o = constructor.newInstance();
System.out.println(o);
}
}
5.2 通过调用有参的构造器创建类的对象


package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
View Code
package day24;
import java.lang.reflect.Constructor;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, Integer.class);
//取消java语言的访问检查
constructor.setAccessible(true);
Object o = constructor.newInstance("张三", 20);
System.out.println(o);
}
}
6 获取运行时类的完整结构
6.1 常用方法
6.1.1 Class类中常用方法
public Class<?>[] getInterfaces(){}
public native Class<? super T> getSuperclass();
- 获取此Class对象所表示的类的所有public构造器:
public Constructor<?>[] getConstructors() throws SecurityException{}
public Constructor<?>[] getDeclaredConstructors() throws SecurityException{}
- 获取此Class对象所表示的类的指定参数的构造器:
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException{}
- 获取此Class对象所表示的类的指定参数的public构造器:
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {}
public Method[] getDeclaredMethods() throws SecurityException{}
- 获取Class对象所表示类或接口的public的方法:
public Method[] getMethods() throws SecurityException{}
- 获取Class对象所表示类或接口的指定名称的方法:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException{}
- 获取Class对象所表示类或接口的指定的public的指定名称的方法:
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
- 获取Class对象所表示的类或接口的所有Field:
public Field[] getDeclaredFields() throws SecurityException{}
- 获取Class对象所表示的类或接口的所有public的Field:
public Field[] getFields() throws SecurityException{}
- 获取Class对象所表示的类或接口的指定名称的Field:
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException{}
- 获取Class对象所表示的类或接口的指定名称的public的Field:
public Field getField(String name)
throws NoSuchFieldException, SecurityException {}
public Annotation[] getDeclaredAnnotations(){}
public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass){}
- 获取Class对象所表示的类或接口的所有public的注解:
public Annotation[] getAnnotations(){}
- 获取Class对象所表示的类或接口的指定的public的注解:
public <A extends Annotation> A getAnnotation(Class<A> annotationClass){}
public Package getPackage(){}
6.1.2 Constructor类中的常用方法
public int getModifiers(){}
public String getName(){}
public Class<?>[] getParameterTypes(){}
6.1.3 Method类中的常用方法:
public Class<?> getReturnType(){}
public Class<?>[] getParameterTypes(){}
public int getModifiers(){}
public Class<?>[] getExceptionTypes(){}
6.1.4 File类中的常用方法
public int getModifiers(){}
public Class<?> getType(){}
public String getName(){}
6.1.5 反射中泛型相关的常用方法
public Type getGenericSuperclass(){}
public interface ParameterizedType extends Type{
//获取实际的泛型类型参数数组
Type[] getActualTypeArguments();
}
6.2 应用示例
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class Person {
private String name;
private Integer age;
public Person() {
}
private Person(String name) {
this.name = name;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public void show(String a,String b) throws RuntimeException,Exception{
System.out.println(a);
System.out.println(b);
}
public String getName() throws RuntimeException,Exception {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package day24;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("day24.Person");
//解析构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
int modifiers = constructor.getModifiers();
String name = constructor.getName();
Class<?>[] parameterTypes = constructor.getParameterTypes();
System.out.print(Modifier.toString(modifiers) + " " + name);
if (null == parameterTypes || 0 == parameterTypes.length) {
System.out.println("( ){}");
} else {
System.out.print("( ");
StringBuilder sb = new StringBuilder();
for (Class<?> parameterType : parameterTypes) {
if (1 == parameterTypes.length) {
System.out.print(parameterType.getSimpleName());
} else {
sb.append(parameterType.getSimpleName()).append(",");
}
}
if (sb.length() > 0) {
sb.delete(sb.length() - 1, sb.length());
}
System.out.print(sb);
System.out.println(" ){}");
}
}
//解析属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
int modifiers = field.getModifiers();
String name = field.getName();
Class<?> type = field.getType();
System.out.println(Modifier.toString(modifiers) + " " + type.getSimpleName() + " " + name + ";");
}
//解析方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
int modifiers = method.getModifiers();
String name = method.getName();
Class<?> returnType = method.getReturnType();
Class<?>[] exceptionTypes = method.getExceptionTypes();
if (null == exceptionTypes || 0 == exceptionTypes.length) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (null == parameterTypes || 0 == parameterTypes.length) {
System.out.println(Modifier.toString(modifiers) + " " + returnType.getSimpleName() + " " + name + "(){}");
} else {
System.out.print(Modifier.toString(modifiers) + " " + returnType.getSimpleName() + " " + name + "(");
StringBuilder sb = new StringBuilder();
for (Class<?> parameterType : parameterTypes) {
if (1 == parameterTypes.length) {
System.out.print(parameterType.getSimpleName());
} else {
sb.append(parameterType.getSimpleName()).append(",");
}
}
if (sb.length() > 0) {
sb.delete(sb.length() - 1, sb.length());
}
System.out.print(sb);
System.out.println(" )");
}
} else {
Class<?>[] parameterTypes = method.getParameterTypes();
if (null == parameterTypes || 0 == parameterTypes.length) {
System.out.print(Modifier.toString(modifiers) + " " + returnType.getSimpleName() + " " + name + "()");
System.out.print(" throws ");
StringBuffer sb = new StringBuffer();
for (Class<?> exceptionType : exceptionTypes) {
if (1 == exceptionTypes.length) {
System.out.print(exceptionType.getSimpleName());
} else {
sb.append(exceptionType.getSimpleName() + ",");
}
}
if (sb.length() > 0) {
sb.delete(sb.length() - 1, sb.length());
}
System.out.print(sb);
System.out.println(" {}");
} else {
System.out.print(Modifier.toString(modifiers) + " " + returnType.getSimpleName() + " " + name + "(");
StringBuilder sb = new StringBuilder();
for (Class<?> parameterType : parameterTypes) {
if (1 == parameterTypes.length) {
System.out.print(parameterType.getSimpleName());
} else {
sb.append(parameterType.getSimpleName()).append(",");
}
}
if (sb.length() > 0) {
sb.delete(sb.length() - 1, sb.length());
}
System.out.print(sb);
System.out.print(")");
System.out.print(" throws ");
sb = new StringBuilder();
for (Class<?> exceptionType : exceptionTypes) {
if (1 == exceptionTypes.length) {
System.out.print(exceptionType.getSimpleName());
} else {
sb.append(exceptionType.getSimpleName() + ",");
}
}
if (sb.length() > 0) {
sb.delete(sb.length() - 1, sb.length());
}
System.out.print(sb);
System.out.println("{}");
}
}
}
}
}
7 调用运行时类的指定结构
7.1 调用指定方法
7.1.1 概述
- 通过Class类的getMethod(String name,Class... parameterTypes)方法或其他类似的方法获取一个Method对象,并设置此方法操作时所需要的参数类型。
- 然后使用调用Method类的Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
7.1.2 应用示例
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class Person {
private String name;
private Integer age;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package day24;
import java.lang.reflect.Method;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;
Object instance = clazz.newInstance();
Method setName = clazz.getDeclaredMethod("setName", String.class);
//取消java语言的安全检查
setName.setAccessible(true);
setName.invoke(instance, "张三");
Method getName = clazz.getDeclaredMethod("getName");
//取消java语言的安全检查
getName.setAccessible(true);
Object obj = getName.invoke(instance);
System.out.println(obj);
}
}
7.2 调用指定属性
7.2.1 概述
- 在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
7.2.2 应用示例
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class Person {
private String name;
private Integer age;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package day24;
import java.lang.reflect.Field;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = Person.class;
Object instance = clazz.newInstance();
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(instance,"张三");
Object o = name.get(instance);
System.out.println(o);
}
}
8 动态代理
8.1 概述
- 动态代理是指客户通过代理类来调用其他对象的方法,并且在程序运行时根据需要动态创建目标类的代理对象。
- 动态代理相对于静态代理的优点:抽象角色中(接口)声明的是所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和同一的处理众多的方法。
8.2 应用示例
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public interface Human {
String getBelief();
void eat(String food);
}
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class SuperMan implements Human {
@Override
public String getBelief() {
return "超人没有信仰!!!";
}
@Override
public void eat(String food) {
System.out.println("吃" + food);
}
}
package day24;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ProxyFactory {
/**
* 调用此方法,返回代理类的对象
*
* @param obj 被代理类的对象
* @return 代理类的对象
*/
public static Object getProxyInstance(Object obj) {
Object o = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(obj, args);
}
});
return o;
}
}
package day24;
/**
* @motto: 一身转战三千里,一剑可当百万兵。
* @author: 不为往事扰,余生只爱笑。
* @version: 1.0
* @since: 2019-07-17
*/
public class ProxyTest {
public static void main(String[] args) {
Human human = (Human) ProxyFactory.getProxyInstance(new SuperMan());
String belief = human.getBelief();
System.out.println("信仰是:" + belief);
human.eat("苹果");
}
}