java反射机制

1. java反射机制概述

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个 类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可 以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看 到类的结构,所以,我们形象的称之为:反射。

1.1. Java反射机制提供的功能

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判断任意一个类所具有的成员变量和方法

在运行时获取泛型信息

在运行时调用任意一个对象的成员变量和方法

在运行时处理注解

生成动态代理

1.2. 反射相关的主要API

java.lang.Class:代表一个类

java.lang.reflect.Method:代表类的方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor:代表类的构造器

2. 理解Class类并获取Class实例

2.1. 反射之前类的实例化操作

  1. public class Person {
  2. private String name;
  3. public int age;
  4. @Override
  5. public String toString() {
  6. return "Person{" +
  7. "name='" + name + '\'' +
  8. ", age=" + age +
  9. '}';
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public int getAge() {
  18. return age;
  19. }
  20. public void setAge(int age) {
  21. this.age = age;
  22. }
  23. public Person(String name, int age) {
  24. this.name = name;
  25. this.age = age;
  26. }
  27. private Person(String name) {
  28. this.name = name;
  29. }
  30. public Person() {
  31. System.out.println("Person()");
  32. }
  33. public void show(){
  34. System.out.println("你好,我是一个人");
  35. }
  36. private String showNation(String nation){
  37. System.out.println("我的国籍是:" + nation);
  38. return nation;
  39. }
  40. }
  1. //反射之前,对于Person的操作
  2. @Test
  3. public void test1() {
  4. //1.创建Person类的对象
  5. Person p1 = new Person("Tom", 12);
  6. //2.通过对象,调用其内部的属性、方法
  7. p1.age = 10;
  8. System.out.println(p1.toString());
  9. p1.show();
  10. //在Person类外部,不可以通过Person类的对象调用其内部私有结构。
  11. //比如:name、showNation()以及私有的构造器
  12. }

2.2. 使用反射实现同上的操作

  1. //反射之后,对于Person的操作
  2. @Test
  3. public void test2() throws Exception{
  4. Class clazz = Person.class;
  5. //1.通过反射,创建Person类的对象
  6. Constructor cons = clazz.getConstructor(String.class,int.class);
  7. Object obj = cons.newInstance("Tom", 12);
  8. Person p = (Person) obj;
  9. System.out.println(p.toString());
  10. //2.通过反射,调用对象指定的属性、方法
  11. //调用属性
  12. Field age = clazz.getDeclaredField("age");
  13. age.set(p,10);
  14. System.out.println(p.toString());
  15. //调用方法
  16. Method show = clazz.getDeclaredMethod("show");
  17. show.invoke(p);
  18. }

2.3. 通过反射调用类的私有结构

  1. //通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
  2. //调用私有的构造器
  3. Constructor cons1 = clazz.getDeclaredConstructor(String.class);
  4. cons1.setAccessible(true);
  5. Person p1 = (Person) cons1.newInstance("Jerry");
  6. System.out.println(p1);
  7. //调用私有的属性
  8. Field name = clazz.getDeclaredField("name");
  9. name.setAccessible(true);
  10. name.set(p1,"HanMeimei");
  11. System.out.println(p1);
  12. //调用私有的方法
  13. Method showNation = clazz.getDeclaredMethod("showNation", String.class);
  14. showNation.setAccessible(true);
  15. String nation = (String) showNation.invoke(p1,"中国");//相当于String nation = p1.showNation("中国")
  16. System.out.println(nation);

2.4. 反射相关疑问

疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?

建议:直接new的方式。

什么时候会使用:反射的方式。一开始在编译的时候不能确定要new哪个类的对象,在运行时才能确定。这时候就使用反射。

疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?

  1. 两种技术看起来似乎有矛盾,封装性告诉我们私有的别的地方不能用,而反射呢告诉我们可以用私有的 这样看不就白封装了吗?
  2. 但其实两者之间并不矛盾
  3. 对于封装性我们设计一个类的时候 属性 方法 构造器等等 该私有的时候私有(private) 公共的时候公共(public)
  4. 封装性给我们的启示是:当我们看到一个类写了一些私有的方法,一些公共的方法时 就告诉我们私有的方法就不要用了
  5. 就用公共的方法就可以了 因为私有的方法可能类内部用了 这里体现了封装性。比如单例模式 你要想造对象 就不要用
  6. 私有的构造器了 我已经把对象造好了直接用就行
  7. 反射 告诉我们可以调 但是不建议调私有的方法,因为可能公共的方法更好 加了一些逻辑
  8. 封装性解决的问题是 建议 调那个的问题 公共的调就可以了 私有的不要掉了就 私有的属性不建议你直接修改 建议你通过get set方法修改。
  9. 反射解决的是能不能调的问题
  10. 所以两者不矛盾。

2.5. 关于java.lang.Class类的理解

程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。换句话说,Class的实例就对应着一个运行时类。

加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

2.6. 获取Class实例的方式

  1. //获取Class的实例的方式(前三种方式需要掌握)
  2. @Test
  3. public void test3() throws ClassNotFoundException {
  4. //方式一:调用运行时类的属性:.class
  5. Class clazz1 = Person.class;
  6. System.out.println(clazz1);
  7. //方式二:通过运行时类的对象,调用getClass()
  8. Person p1 = new Person();
  9. Class clazz2 = p1.getClass();
  10. System.out.println(clazz2);
  11. //方式三:调用Class的静态方法:forName(String classPath)
  12. Class clazz3 = Class.forName("com.atguigu.java.Person");
  13. // clazz3 = Class.forName("java.lang.String");
  14. System.out.println(clazz3);
  15. System.out.println(clazz1 == clazz2);
  16. System.out.println(clazz1 == clazz3);
  17. //方式四:使用类的加载器:ClassLoader (了解)
  18. ClassLoader classLoader = ReflectionTest.class.getClassLoader();
  19. Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");
  20. System.out.println(clazz4);
  21. System.out.println(clazz1 == clazz4);
  22. }

2.7. 哪些类型可以有Class对象

(1)class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

(2)interface:接口

(3)[]:数组

(4)enum:枚举

(5)annotation:注解@interface

(6)primitive type:基本数据类型

(7)void

  1. //Class实例可以是哪些结构的说明:
  2. @Test
  3. public void test4(){
  4. Class c1 = Object.class;
  5. Class c2 = Comparable.class;
  6. Class c3 = String[].class;
  7. Class c4 = int[][].class;
  8. Class c5 = ElementType.class;
  9. Class c6 = Override.class;
  10. Class c7 = int.class;
  11. Class c8 = void.class;
  12. Class c9 = Class.class;
  13. int[] a = new int[10];
  14. int[] b = new int[100];
  15. Class c10 = a.getClass();
  16. Class c11 = b.getClass();
  17. // 只要数组的元素类型与维度一样,就是同一个Class
  18. System.out.println(c10 == c11);//true
  19. }

3. 类的加载与ClassLoader的理解

3.1. 类的加载过程(了解)

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过 如下三个步骤来对该类进行初始化。

day28_java反射学习笔记 - 图1

  1. public class ClassLoadingTest {
  2. public static void main(String[] args) {
  3. System.out.println(A.m);
  4. }
  5. }
  6. class A {
  7. static {
  8. m = 300;
  9. }
  10. static int m = 100;
  11. }
  12. //第二步:链接结束后m=0
  13. //第三步:初始化后,m的值由<clinit>()方法执行决定
  14. // 这个A的类构造器<clinit>()方法由类变量的赋值和静态代码块中的语句按照顺序合并
  15. 产生,类似于
  16. // <clinit>(){
  17. // m = 300;
  18. // m = 100;
  19. // }

3.2. 类的加载器ClassLoader(了解)

  1. @Test
  2. public void test1(){
  3. //对于自定义类,使用系统类加载器进行加载
  4. ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
  5. System.out.println(classLoader);
  6. //调用系统类加载器的getParent():获取扩展类加载器
  7. ClassLoader classLoader1 = classLoader.getParent();
  8. System.out.println(classLoader1);
  9. //调用扩展类加载器的getParent():无法获取引导类加载器
  10. //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
  11. ClassLoader classLoader2 = classLoader1.getParent();
  12. System.out.println(classLoader2);
  13. ClassLoader classLoader3 = String.class.getClassLoader();
  14. System.out.println(classLoader3);
  15. }

3.3. 使用ClassLoader加载配置文件(掌握)

  1. /*
  2. Properties:用来读取配置文件。
  3. */
  4. @Test
  5. public void test2() throws Exception {
  6. Properties pros = new Properties();
  7. //此时的文件默认在当前的module下。
  8. //读取配置文件的方式一:
  9. // FileInputStream fis = new FileInputStream("jdbc.properties");
  10. // FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
  11. // pros.load(fis);
  12. //读取配置文件的方式二:使用ClassLoader
  13. //配置文件默认识别为:当前module的src下
  14. ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
  15. InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
  16. pros.load(is);
  17. String user = pros.getProperty("user");
  18. String password = pros.getProperty("password");
  19. System.out.println("user = " + user + ",password = " + password);
  20. }

4. 创建运行时类的对象

  1. @Test
  2. public void test1() throws IllegalAccessException, InstantiationException {
  3. Class<Person> clazz = Person.class;
  4. /*
  5. newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
  6. 要想此方法正常的创建运行时类的对象,要求:
  7. 1.运行时类必须提供空参的构造器
  8. 2.空参的构造器的访问权限得够。通常,设置为public。
  9. 在javabean中要求提供一个public的空参构造器。原因:
  10. 1.便于通过反射,创建运行时类的对象
  11. 2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
  12. */
  13. Person obj = clazz.newInstance();
  14. System.out.println(obj);
  15. }

4.1. 体会反射的动态性

  1. //体会反射的动态性
  2. @Test
  3. public void test2(){
  4. for(int i = 0;i < 100;i++){
  5. int num = new Random().nextInt(3);//0,1,2
  6. String classPath = "";
  7. switch(num){
  8. case 0:
  9. classPath = "java.util.Date";
  10. break;
  11. case 1:
  12. classPath = "java.lang.Object";
  13. break;
  14. case 2:
  15. classPath = "com.atguigu.java.Person";
  16. break;
  17. }
  18. try {
  19. Object obj = getInstance(classPath);
  20. System.out.println(obj);
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }
  26. /*
  27. 创建一个指定类的对象。
  28. classPath:指定类的全类名
  29. */
  30. public Object getInstance(String classPath) throws Exception {
  31. Class clazz = Class.forName(classPath);
  32. return clazz.newInstance();
  33. }

5. 获取运行时类的完整结构

5.1. 获取当前运行时类的属性结构

  1. /**
  2. * 获取当前运行时类的属性结构
  3. *
  4. * @author shkstart
  5. * @create 2019 下午 3:23
  6. */
  7. public class FieldTest {
  8. @Test
  9. public void test1(){
  10. Class clazz = Person.class;
  11. //获取属性结构
  12. //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
  13. Field[] fields = clazz.getFields();
  14. for(Field f : fields){
  15. System.out.println(f);
  16. }
  17. System.out.println();
  18. //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
  19. Field[] declaredFields = clazz.getDeclaredFields();
  20. for(Field f : declaredFields){
  21. System.out.println(f);
  22. }
  23. }
  24. //权限修饰符 数据类型 变量名
  25. @Test
  26. public void test2(){
  27. Class clazz = Person.class;
  28. Field[] declaredFields = clazz.getDeclaredFields();
  29. for(Field f : declaredFields){
  30. //1.权限修饰符
  31. int modifier = f.getModifiers();
  32. System.out.print(Modifier.toString(modifier) + "\t");
  33. //2.数据类型
  34. Class type = f.getType();
  35. System.out.print(type.getName() + "\t");
  36. //3.变量名
  37. String fName = f.getName();
  38. System.out.print(fName);
  39. System.out.println();
  40. }
  41. }
  42. }

5.2. 获取当前运行时类的方法结构

  1. /**
  2. * 获取运行时类的方法结构
  3. *
  4. * @author shkstart
  5. * @create 2019 下午 3:37
  6. */
  7. public class MethodTest {
  8. @Test
  9. public void test1(){
  10. Class clazz = Person.class;
  11. //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
  12. Method[] methods = clazz.getMethods();
  13. for(Method m : methods){
  14. System.out.println(m);
  15. }
  16. System.out.println();
  17. //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
  18. Method[] declaredMethods = clazz.getDeclaredMethods();
  19. for(Method m : declaredMethods){
  20. System.out.println(m);
  21. }
  22. }
  23. /*
  24. @Xxxx
  25. 权限修饰符 返回值类型 方法名(参数类型1 形参名1,...) throws XxxException{}
  26. */
  27. @Test
  28. public void test2(){
  29. Class clazz = Person.class;
  30. Method[] declaredMethods = clazz.getDeclaredMethods();
  31. for(Method m : declaredMethods){
  32. //1.获取方法声明的注解
  33. Annotation[] annos = m.getAnnotations();
  34. for(Annotation a : annos){
  35. System.out.println(a);
  36. }
  37. //2.权限修饰符
  38. System.out.print(Modifier.toString(m.getModifiers()) + "\t");
  39. //3.返回值类型
  40. System.out.print(m.getReturnType().getName() + "\t");
  41. //4.方法名
  42. System.out.print(m.getName());
  43. System.out.print("(");
  44. //5.形参列表
  45. Class[] parameterTypes = m.getParameterTypes();
  46. if(!(parameterTypes == null && parameterTypes.length == 0)){
  47. for(int i = 0;i < parameterTypes.length;i++){
  48. if(i == parameterTypes.length - 1){
  49. System.out.print(parameterTypes[i].getName() + " args_" + i);
  50. break;
  51. }
  52. System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
  53. }
  54. }
  55. System.out.print(")");
  56. //6.抛出的异常
  57. Class[] exceptionTypes = m.getExceptionTypes();
  58. if(exceptionTypes.length > 0){
  59. System.out.print("throws ");
  60. for(int i = 0;i < exceptionTypes.length;i++){
  61. if(i == exceptionTypes.length - 1){
  62. System.out.print(exceptionTypes[i].getName());
  63. break;
  64. }
  65. System.out.print(exceptionTypes[i].getName() + ",");
  66. }
  67. }
  68. System.out.println();
  69. }
  70. }
  71. }

5.3. 获取当前运行时类的构造器

  1. /*
  2. 获取构造器结构
  3. */
  4. @Test
  5. public void test1(){
  6. Class clazz = Person.class;
  7. //getConstructors():获取当前运行时类中声明为public的构造器
  8. Constructor[] constructors = clazz.getConstructors();
  9. for(Constructor c : constructors){
  10. System.out.println(c);
  11. }
  12. System.out.println();
  13. //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
  14. Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
  15. for(Constructor c : declaredConstructors){
  16. System.out.println(c);
  17. }
  18. }

5.4. 获取运行时类的父类

  1. /*
  2. 获取运行时类的父类
  3. */
  4. @Test
  5. public void test2(){
  6. Class clazz = Person.class;
  7. Class superclass = clazz.getSuperclass();
  8. System.out.println(superclass);
  9. }

5.5. 获取运行时类的带泛型的父类

  1. /*
  2. 获取运行时类的带泛型的父类
  3. */
  4. @Test
  5. public void test3(){
  6. Class clazz = Person.class;
  7. Type genericSuperclass = clazz.getGenericSuperclass();
  8. System.out.println(genericSuperclass);
  9. }

5.6. 获取运行时类的带泛型的父类的泛型

  1. /*
  2. 获取运行时类的带泛型的父类的泛型
  3. 代码:逻辑性代码 vs 功能性代码
  4. */
  5. @Test
  6. public void test4(){
  7. Class clazz = Person.class;
  8. Type genericSuperclass = clazz.getGenericSuperclass();
  9. ParameterizedType paramType = (ParameterizedType) genericSuperclass;
  10. //获取泛型类型
  11. Type[] actualTypeArguments = paramType.getActualTypeArguments();
  12. // System.out.println(actualTypeArguments[0].getTypeName());
  13. System.out.println(((Class)actualTypeArguments[0]).getName());
  14. }

5.7. 获取运行时类实现的接口

  1. /*
  2. 获取运行时类实现的接口
  3. */
  4. @Test
  5. public void test5(){
  6. Class clazz = Person.class;
  7. Class[] interfaces = clazz.getInterfaces();
  8. for(Class c : interfaces){
  9. System.out.println(c);
  10. }
  11. System.out.println();
  12. //获取运行时类的父类实现的接口
  13. Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
  14. for(Class c : interfaces1){
  15. System.out.println(c);
  16. }
  17. }

5.8. 获取运行时类所在的包

  1. /*
  2. 获取运行时类所在的包
  3. */
  4. @Test
  5. public void test6(){
  6. Class clazz = Person.class;
  7. Package pack = clazz.getPackage();
  8. System.out.println(pack);
  9. }

5.9. 获取运行时类声明的注解

  1. /*
  2. 获取运行时类声明的注解
  3. */
  4. @Test
  5. public void test7(){
  6. Class clazz = Person.class;
  7. Annotation[] annotations = clazz.getAnnotations();
  8. for(Annotation annos : annotations){
  9. System.out.println(annos);
  10. }
  11. }

6. 调用运行时类的指定结构:属性、方法、构造器(掌握)

6.1. 如何操作运行时类中的指定的属性

  1. /*
  2. 不需要掌握
  3. */
  4. @Test
  5. public void testField() throws Exception {
  6. Class clazz = Person.class;
  7. //创建运行时类的对象
  8. Person p = (Person) clazz.newInstance();
  9. //获取指定的属性:要求运行时类中属性声明为public
  10. //通常不采用此方法
  11. Field id = clazz.getField("id");
  12. /*
  13. 设置当前属性的值
  14. set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
  15. */
  16. id.set(p,1001);
  17. /*
  18. 获取当前属性的值
  19. get():参数1:获取哪个对象的当前属性值
  20. */
  21. int pId = (int) id.get(p);
  22. System.out.println(pId);
  23. }
  24. /*
  25. 如何操作运行时类中的指定的属性 -- 需要掌握
  26. */
  27. @Test
  28. public void testField1() throws Exception {
  29. Class clazz = Person.class;
  30. //创建运行时类的对象
  31. Person p = (Person) clazz.newInstance();
  32. //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
  33. Field name = clazz.getDeclaredField("name");
  34. //2.保证当前属性是可访问的
  35. name.setAccessible(true);
  36. //3.获取、设置指定对象的此属性值
  37. name.set(p,"Tom");
  38. System.out.println(name.get(p));
  39. }

6.2. 如何操作运行时类中的指定的方法

  1. /*
  2. 如何操作运行时类中的指定的方法 -- 需要掌握
  3. */
  4. @Test
  5. public void testMethod() throws Exception {
  6. Class clazz = Person.class;
  7. //创建运行时类的对象
  8. Person p = (Person) clazz.newInstance();
  9. /*
  10. 1.获取指定的某个方法
  11. getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
  12. */
  13. Method show = clazz.getDeclaredMethod("show", String.class);
  14. //2.保证当前方法是可访问的
  15. show.setAccessible(true);
  16. /*
  17. 3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
  18. invoke()的返回值即为对应类中调用的方法的返回值。
  19. */
  20. Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
  21. System.out.println(returnValue);
  22. System.out.println("*************如何调用静态方法*****************");
  23. // private static void showDesc()
  24. Method showDesc = clazz.getDeclaredMethod("showDesc");
  25. showDesc.setAccessible(true);
  26. //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
  27. // Object returnVal = showDesc.invoke(null);
  28. Object returnVal = showDesc.invoke(Person.class);
  29. System.out.println(returnVal);//null
  30. }

6.3. 如何操作运行时类中的指定的构造器

  1. /*
  2. 如何调用运行时类中的指定的构造器
  3. */
  4. @Test
  5. public void testConstructor() throws Exception {
  6. Class clazz = Person.class;
  7. //private Person(String name)
  8. /*
  9. 1.获取指定的构造器
  10. getDeclaredConstructor():参数:指明构造器的参数列表
  11. */
  12. Constructor constructor = clazz.getDeclaredConstructor(String.class);
  13. //2.保证此构造器是可访问的
  14. constructor.setAccessible(true);
  15. //3.调用此构造器创建运行时类的对象
  16. Person per = (Person) constructor.newInstance("Tom");
  17. System.out.println(per);
  18. }

7. 反射的应用:动态代理

7.1. 代理设计模式的原理

使用一个代理类对象将被代理对象包装起来, 然后用该代理对象取代被代理对象。任何对被代理对象的调用都要通过代理对象。代理类对象决定是否以及何时将方法调用转到被代理对象上。

7.2. 动态代理的概念

动态代理是指客户通过代理类来调用其它对象(指的是被代理类对象)的方法,并且是在程序运行时 根据需要动态创建目标类的代理对象。

7.3. 静态代理举例

  1. /**
  2. * 静态代理举例
  3. *
  4. * 特点:代理类和被代理类在编译期间,就确定下来了。
  5. *
  6. * @author shkstart
  7. * @create 2019 上午 10:11
  8. */
  9. interface ClothFactory{
  10. void produceCloth();
  11. }
  12. //代理类
  13. class ProxyClothFactory implements ClothFactory{
  14. private ClothFactory factory;//用被代理类对象进行实例化
  15. public ProxyClothFactory(ClothFactory factory){
  16. this.factory = factory;
  17. }
  18. @Override
  19. public void produceCloth() {
  20. System.out.println("代理工厂做一些准备工作");
  21. factory.produceCloth();
  22. System.out.println("代理工厂做一些后续的收尾工作");
  23. }
  24. }
  25. //被代理类
  26. class NikeClothFactory implements ClothFactory{
  27. @Override
  28. public void produceCloth() {
  29. System.out.println("Nike工厂生产一批运动服");
  30. }
  31. }
  32. public class StaticProxyTest {
  33. public static void main(String[] args) {
  34. //创建被代理类的对象
  35. ClothFactory nike = new NikeClothFactory();
  36. //创建代理类的对象
  37. ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
  38. proxyClothFactory.produceCloth();
  39. }
  40. }

静态代理的缺陷

  • 代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展

  • 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能

7.4. 实现动态代理需要解决的两个主要问题

问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。

  1. 通过Proxy.newProxyInstance()实现

问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。

  1. 通过InvocationHandler接口的实现类及其方法invoke()

7.5. 动态代理举例

  1. package com.atguigu.java;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. /**
  6. *
  7. * 动态代理的举例
  8. *
  9. * @author shkstart
  10. * @create 2019 上午 10:18
  11. */
  12. interface Human{
  13. String getBelief();
  14. void eat(String food);
  15. }
  16. //被代理类
  17. class SuperMan implements Human{
  18. @Override
  19. public String getBelief() {
  20. return "I believe I can fly!";
  21. }
  22. @Override
  23. public void eat(String food) {
  24. System.out.println("我喜欢吃" + food);
  25. }
  26. }
  27. class HumanUtil{
  28. public void method1(){
  29. System.out.println("====================通用方法一====================");
  30. }
  31. public void method2(){
  32. System.out.println("====================通用方法二====================");
  33. }
  34. }
  35. /*
  36. 要想实现动态代理,需要解决的问题?
  37. 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
  38. 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
  39. */
  40. class ProxyFactory{
  41. //调用此方法,返回一个代理类的对象。解决问题一
  42. public static Object getProxyInstance(Object obj){//obj:被代理类的对象
  43. MyInvocationHandler handler = new MyInvocationHandler();
  44. handler.bind(obj);
  45. return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
  46. }
  47. }
  48. class MyInvocationHandler implements InvocationHandler{
  49. private Object obj;//需要使用被代理类的对象进行赋值
  50. public void bind(Object obj){
  51. this.obj = obj;
  52. }
  53. //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
  54. //将被代理类要执行的方法a的功能就声明在invoke()中
  55. @Override
  56. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  57. HumanUtil util = new HumanUtil();
  58. util.method1();
  59. //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
  60. //obj:被代理类的对象
  61. Object returnValue = method.invoke(obj,args);
  62. util.method2();
  63. //上述方法的返回值就作为当前类中的invoke()的返回值。
  64. return returnValue;
  65. }
  66. }
  67. public class ProxyTest {
  68. public static void main(String[] args) {
  69. SuperMan superMan = new SuperMan();
  70. //proxyInstance:代理类的对象
  71. Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
  72. //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
  73. String belief = proxyInstance.getBelief();
  74. System.out.println(belief);
  75. proxyInstance.eat("四川麻辣烫");
  76. System.out.println("*****************************");
  77. NikeClothFactory nikeClothFactory = new NikeClothFactory();
  78. ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
  79. proxyClothFactory.produceCloth();
  80. }
  81. }