《Java语言高级特性(阿里云大学)》笔记 第21~27章 关于反射(文档+思维导图)

课程链接:https://edu.aliyun.com/course/1012

第1~6章 关于线程:https://blog.csdn.net/weixin_43494837/article/details/113764127?spm=1001.2014.3001.5501

第7~14章 类库+正则+国际化+比较器:https://blog.csdn.net/weixin_43494837/article/details/113764156?spm=1001.2014.3001.5501

第15~20章 文件+IO+序列化:https://blog.csdn.net/weixin_43494837/article/details/113764173?spm=1001.2014.3001.5501

(思维导图在最后)

————————————————————————————————————

第21章:认识反射机制

课时99:反射机制简介

  • Java最大的特征及精髓:反射机制。

  • 所有奇数实现的目标:重用性。

  • 关于反射技术的“反”操作:“正”操作是指使用类时,需要导包,然后进行对象实例化,再调用类中的方法;而“反”则是根据实例化反推出其类型。(举例:小屁孩的你闯祸了,受害者就会揪着你去找你的父母。)

  • “反”操作的实现:Object类提供了一个方法:

    • 根据Class对象获取其信息:public final Class<?> getClass()
  • 示例:

    • 正向操作(正常):

      import java.util.Date; // 1. 导包
      public class JavaDemo{
          public static void main(String[] args) {
              Date date = new Date(); // 2. 实例化对象
              System.out.println(date.getTime()); // 3. 调用方法
          }
      }
      
    • 反向操作(反射):

      import java.util.Date; // 1. 导包
      public class JavaDemo{
          public static void main(String[] args) {
              Date date = new Date(); // 2. 实例化对象
              System.out.println(date.getClass()); // 3. 返回实例化对象的类型
              // 输出:
              // class java.util.Date
          }
      }
      

课时100:Class类对象的三种实例化模式

  • 反射中所有的核心操作都是通过Class类对象展开的,可以说Class类是反射操作的根源所在。

  • java.lang.Class类的定义:

    public final class Class<T> extends Object 
    implements Serializable, GenericDeclaration, Type, AnnotatedElement
    

    从JDK1.5开始,Class类在定义的时可以使用泛型进行标记,这样的用法主要是希望可以避免所谓的向下转型。

  • 获取Class对象:

    • 【Object类支持】通过Object类的getClass()方法:

      package cn.pjh;
      import cn.pjh.test.testPerson; // 1. import导包
      public class ClassDemo {
          public static void main(String[] args) {
              testPerson per = new testPerson(); // 2. 实例化对象
              Class<? extends testPerson> clazz = per.getClass(); // 3. 通过Object类的getClass()方法获取Class对象
              System.out.println(clazz.getName()); // 输出:cn.pjh.test.testPerson
          }
      }
      
    • 【JVM直接支持】直接通过“类.class”:

      package cn.pjh;
      import cn.pjh.test.testPerson; // 1. import导包
      public class ClassDemo {
          public static void main(String[] args) {
              Class<? extends testPerson> clazz = testPerson.class; // 2. 直接通过“类.class”获取Class对象
              System.out.println(clazz.getName()); // 输出:cn.pjh.test.testPerson
          }
      }
      
    • 【Class类支持】通过Class类中的forName()静态方法:

      package cn.pjh;
      public class ClassDemo {
          public static void main(String[] args) throws Exception {
              Class<?> clazz = Class.forName("cn.pjh.test.testPerson"); // 1. 直接通过Class类的forName()静态方法获取Class对象
              System.out.println(clazz.getName()); // 输出:cn.pjh.test.testPerson
          }
      }
      

      此模式优点:① 不需要import导包;② 可直接通过字符串进行操作。

      🔺 如类不存在时,会抛出“java.lang.ClassNotFoundException”的异常。

第22章:反射应用案例

课时101:反射实例化对象

(随着技术的不断积累,如果一开始写的代码按十分来定义的话,等到学到最后的时候,代码只剩三分了,其余的七分已被框架实现了。)

  • 获取Class对象后的最大意义,实际上并不是在于对象的实例化操作,更重要的是Class类中提供了一个对象的反射实例化方法(代替了关键字new):

    • JDK1.9之前的实例化(java.lang.Class类):

      @Deprecated(since="9")
      public T newInstance()
      throws InstantiationException, IllegalAccessException
      
    • JDK1.9之后的实例化(java.lang.reflect.Constructor类):

      public T newInstance(Object ... initargs)
      throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
      
  • 示例:

    • Person类代码:

      package cn.pjh.test;
      public class Person {
          public Person() {
              System.out.println("********* Person类构造方法 *********");
          }
      }
      
      
    • 主类实现:

      package cn.pjh;
      public class ClassDemo {
          public static void main(String[] args) throws Exception {
              Class<?> personClazz = Class.forName("cn.pjh.test.Person");
      //        Object obj = personClazz.newInstance(); // JDK1.9之前的用法
              Object obj = personClazz.getDeclaredConstructor().newInstance(); // JDK1.9之后的用法
      //        输出:
      //        ********* Person类构造方法 *********
          }
      }
      
      • newInstance()只能调用无参构造;
      • getDeclaredConstructor.newInstance()可以调用有参构造。

      (个人补充:Class类中有 getConstructor() 和 getDeclaredConstructor() 两个可以获取构造方法。区别:getConstructor()只能返回public的构造方法,getDeclaredConstructor()可以返回非public的构造方法。)

课时102:反射与工厂设计模式

  • 对象的实例化方法:

    • 使用关键字new
    • 使用反射机制
  • 疑问:那为什么要使用反射进行实例化呢?

  • 使用反射机制进行实例化的应用:

    • 工厂模式:
      • 最大特点:客户端的程序类不直接牵扯到对象的实例化管理,只与接口发生关系。(在实际开发中,接口的主要作用是为不同层提供一个操作的标准,而如果此时直接使用子类进行接口的实例化,那么就一定会有耦合的问题。)
  • 示例:

    • 传统工厂设计模式:

      interface IMessage {
          void send();
      }
      class NetMessage implements IMessage {
          @Override
          public void send() {
              System.out.println("【网络消息发送】pikaqiu~~~");
          }
      }
      class CloudMessage implements IMessage {
          @Override
          public void send() {
              System.out.println("【云消息发送】pikaqiu~~~");
          }
      }
      class Factory {
      	private Factory() {}
          public static IMessage getMessageInstance(String className) {
              if ("netMessage".equals(className)) {
                  return new NetMessage();
              }
              return null;
          }
      }
      

      上述的工厂设计模式属于静态工厂设计模式,如果现在要追加一个IMessge接口的子类,则工厂类也一定要进行新子类的实例化代码:

      class CloudMessage implements IMessage {
          @Override
          public void send() {
              System.out.println("【云消息发送】pikaqiu~~~");
          }
      }
      class Factory {
      	private Factory() {}
          public static IMessage getMessageInstance(String className) {
              if ("NetMessage".equals(className)) {
                  return new NetMessage();
              }
              else if ("CloudMessage".equals(className)) {
                  return new CloudMessage();
              }
              return null;
          }
      }
      

      如此一来,随着项目的发展,工厂类就总是要进行修改。那么想要解决此问题,最好的方案就是不使用关键字new,因为使用关键字new时,需要明确new的类。

    • 使用反射机制的工厂设计模式:

      class Factory {
      	private Factory() {}
          public static IMessage getMessageInstance(String className) {
              IMessage instance = null;
              try {
                  instance = (IMessage) Class.forName(className).getDeclaredConstructor().newInstance();
              } catch (Exception e) {
                  e.printStackTrace();
              }
              return instance;
          }
      }
      

      此时,就算要增加接口的子类,也不需要再修改工厂类了。

      但是,实际项目开发中,会存在有大量的接口,这些接口可能都要通过工厂类进行实例化,所以工厂类应该为所有的接口提供服务。

    • 使用反射及泛型的工厂设计模式(高可用):

      class Factory {
          private Factory() {}
          public static <T> T getInstance(String className) {
              T instance = null;
              try {
                  instance = (T) Class.forName(className).getDeclaredConstructor().newInstance();
              } catch (Exception e) {
                  e.printStackTrace();
              }
              return instance;
          }
      }
      

      此时的工厂模式将不再受限于指定接口,可以为所有接口提供实例化服务,达到可重用性。

  • 工厂类演化的个人分析:

    • 传统模式:一个接口类型一个方法,一个子类对应一个判断 (增加子类时,需增加判断);
    • 演化一:一个接口类型一个方法,使用反射直接返回对应子类(增加子类时,无需修改);
    • 演化二:方法使用泛型,一个方法可对应不同接口类型,使用反射直接返回对应子类(通用工厂类)。

课时103:反射与单例设计模式

  • 单例设计模式的核心本质:构造方法私有化,在类内部实例化,并通过static方法获取实例化对象。(单例模式有懒汉式和饿汉式,本节将使用懒汉式来进行单例设计模式的讨论。)

  • 示例:

    • 传统的单例设计模式(懒汉式):

      class Singleton {
          private static Singleton instance = null;
          private Singleton() {}
          public static Singleton getInstance() {
              if (null == instance) {
                  instance = new Singleton();
              }
              return instance;
          }
          public void print() {
              System.out.println("我是一个单例类");
          }
      }
      public class JavaDemo{
          public static void main(String[] args) {
              Singleton.getInstance().print();
          }
      }
      
    • 使用多线程的单例设计模式:

      class Singleton {
          private static Singleton instance = null;
          private Singleton() {
              System.out.println(Thread.currentThread().getName() + " ****** 实例化Singleton对象 ******");
          }
          public static Singleton getInstance() {
              if (null == instance) {
                  instance = new Singleton();
              }
              return instance;
          }
          public void print() {
              System.out.println("我是一个单例类");
          }
      }
      public class JavaDemo{
          public static void main(String[] args) {
              for (int i = 0; i < 3; i++) {
                  new Thread(()->{
                      Singleton.getInstance().print();
                  }, "单例消费端"+i).start();
              }
          }
      }
      

      执行结果如下:

      单例消费端0 ****** 实例化Singleton对象 ******
      我是一个单例类
      单例消费端2 ****** 实例化Singleton对象 ******
      我是一个单例类
      单例消费端1 ****** 实例化Singleton对象 ******
      我是一个单例类
      

      单例设计模式的最大特点,是在整体的运行程序中只允许产生一个实例化对象,但使用多线程之后,实际上当前的程序就会产生多个实例化对象了。

      单例设计模式多线程问题.png

      上述问题的关键在于,不同步的问题。

  • 使用多线程 + 同步方法的单例设计模式:

    class Singleton {
        private static Singleton instance = null;
        private Singleton() {
            System.out.println(Thread.currentThread().getName() + " ****** 实例化Singleton对象 ******");
        }
        public static synchronized Singleton getInstance() {
            if (null == instance) {
                instance = new Singleton();
            }
            return instance;
        }
        public void print() {
            System.out.println("我是一个单例类");
        }
    }
    

    执行结果如下:

    单例消费端2 ****** 实例化Singleton对象 ******
    我是一个单例类
    我是一个单例类
    我是一个单例类
    

    此时确实是只产生了一个实例化对象,但如果要产生很多个线程的话,效率就会很低了,而实际上需要同步处理的只是实例化对象的时候。

  • 使用多线程 + 方法内同步块的单例设计模式(标准单例):

    class Singleton {
        // 3. 对象实例化时,应立即与主内存中的数据保持同步,所以需要使用volatile直接操作内存里的变量
        private static volatile Singleton instance = null;
        private Singleton() {
            System.out.println(Thread.currentThread().getName() + " ****** 实例化Singleton对象 ******");
        }
        public static Singleton getInstance() {
            if (null == instance) {
                // 1. 因为是static方法,所以这里不能使用this,而是要用“类.class”
                synchronized (Singleton.class) {
                    // 2. 因为instance为null这里进来的还是会有很多线程,所以要在同步代码块里再判断一次
                    if (null == instance) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
        public void print() {
            System.out.println("我是一个单例类");
        }
    }
    
  • 面试题:关于单例设计模式:

    • 问题一:请编写单例设计模式。
      • 直接编写一个饿汉式的单例设计模式,并且实现构造方法私有化。【得分100%】
    • 问题二:在Java中哪里使用了单例设计模式?
      • Runtime类、Spring框架。【得分120%】
    • 问题三:懒汉式单例设计模式的问题?【得分200%】
  • 单例设计模式演化的个人分析:

    • 传统单例:构造方法私有化,静态方法只实例化一次本类对象。
    • 多线程单例:
      • 会导致实例化不止一个对象,实质上则变成了多例模式;
      • 解决:直接在静态获取实例化对象的方法上进行同步处理(线程多时,效率低下);
      • 改进:同步处理改为在静态方法实例化对象时,并给静态变量增加volatile进行直接操作处理。

第23章:反射与类操作

课时104:反射获取类结构信息

  • 在反射机制的处理过程中,不仅只有实例化对象的处理操作,更多的情况下还有获取类组成结构的操作。任何一个类的基本组成结构都是父类(父接口)、包、属性、方法(构造方法、普通方法)。

  • 获取类的基本信息:

    • 获取包的信息:public Package getPackage()
    • 获取继承父类的信息:public Class<? super T> getSuperclass()
    • 获取实现父接口的信息:public Class<?>[] getInterfaces()
  • 示例(个人实现,原版代码请查看:https://developer.aliyun.com/article/768248?spm=a2c6h.12873639.0.0.29d872b04HAVA4):

    • 定义基本类及接口:

      • 父接口:

        package cn.pjh.service;
        public interface IMessageService {
            void send();
        }
        
        package cn.pjh.service;
        public interface IChannelService {
            boolean connect();
        }
        
      • 父类:

        package cn.pjh.abs;
        public class AbstractBase {
        }
        
      • 基本类:

        package cn.pjh.vo;
        import cn.pjh.abs.AbstractBase;
        import cn.pjh.service.IChannelService;
        import cn.pjh.service.IMessageService;
        public class Person extends AbstractBase implements IMessageService, IChannelService {
            @Override
            public boolean connect() {
                return true;
            }
            @Override
            public void send() {
                if (this.connect()) System.out.println("【发送消息】pikaqiu~~~");
            }
        }
        
    • 获取类的基本信息:

      • 获取包名:

        package cn.pjh;
        import cn.pjh.vo.Person;
        public class ClassDemo {
            public static void main(String[] args) {
                Class<?> clazz = Person.class;
                // 方式一(间接获取包名):getPackage().getName()
                // 底层源码实现:
                // 获取package时,已经先获取了包名,实际上也是调用getPackageName()
                Package pack = clazz.getPackage(); // 1. 获取包
                System.out.println(pack.getName()); // 2. 获取包名
                // 方式二(直接获取包名):getPackageName()
                // 底层源码实现:
                // 要获取包名的类非原始类型时,
                // 通过Class类的getName()方法获取完整类名,然后再截取出前面的包名
                System.out.println(clazz.getPackageName());
        //         输出:
        //        cn.pjh.vo
        //        cn.pjh.vo
            }
        }
        
      • 获取父类信息(个人实现):

        package cn.pjh;
        import cn.pjh.vo.Person;
        import java.lang.reflect.Method;
        public class ClassDemo {
            public static void main(String[] args) {
                Class<?> clazz = Person.class;
                // 1. 获取父类
                Class<?> superClazz = clazz.getSuperclass();
                // 获取父类名称
                System.out.println("类:" + superClazz);
                // 获取父类方法
                Method[] methods = superClazz.getMethods();
                for (Method method : methods) {
                    System.out.println(" - 方法:" + method);
                }
        //        输出:
        //        类:class java.lang.Object
        //          - 方法: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 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()
            }
        }
        

        拓展:在上面获取类方法时可以发现,如果该类内没有定义任何方法,则实际上会获取到所有Object类的方法。

      • 获取父接口信息(个人实现):

        package cn.pjh;
        import cn.pjh.vo.Person;
        import java.lang.reflect.Method;
        public class ClassDemo {
            public static void main(String[] args) {
                Class<?> clazz = Person.class;
                // 1. 获取父接口数组
                Class<?>[] superInterfaces = clazz.getInterfaces();
                // 2. 循环数组获取接口
                for (Class cls : superInterfaces) {
                    System.out.println("接口:" + cls);
                    // 获取接口中的方法
                    Method[] methods = cls.getMethods();
                    for (Method method : methods) {
                        System.out.println(" - 方法:" + method);
                        System.out.println("    - 修饰符:" + method.getModifiers());
                        System.out.println("    - 返回类型:" + method.getReturnType());
                    }
                }
        //        输出:
        //        接口:interface cn.pjh.service.IMessageService
        //                - 方法:public abstract void cn.pjh.service.IMessageService.send()
        //                - 修饰符:1025
        //                - 返回类型:void
        //        接口:interface cn.pjh.service.IChannelService
        //                - 方法:public abstract boolean cn.pjh.service.IChannelService.connect()
        //                - 修饰符:1025
        //                - 返回类型:boolean
            }
        }
        

        关于修饰符的小拓展:

        • 修饰符定义(JDK1.9):

          • 定义位置:java.lang.reflect.Modifier类

          • 种类:

            • 类修饰符:

              private static final int CLASS_MODIFIERS =
                      Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
                      Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.FINAL   |
                      Modifier.STRICT;
              
            • 接口修饰符:

              private static final int INTERFACE_MODIFIERS =
                      Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
                      Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.STRICT;
              
            • 构造方法修饰符:

              private static final int CONSTRUCTOR_MODIFIERS =
                      Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE;
              
            • 方法修饰符:

              private static final int METHOD_MODIFIERS =
                      Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
                      Modifier.ABSTRACT       | Modifier.STATIC       | Modifier.FINAL   |
                      Modifier.SYNCHRONIZED   | Modifier.NATIVE       | Modifier.STRICT;
              
            • 属性修饰符:

              private static final int FIELD_MODIFIERS =
                      Modifier.PUBLIC         | Modifier.PROTECTED    | Modifier.PRIVATE |
                      Modifier.STATIC         | Modifier.FINAL        | Modifier.TRANSIENT |
                      Modifier.VOLATILE;
              
            • 参数修饰符:

              private static final int PARAMETER_MODIFIERS =
                      Modifier.FINAL;
              
          • 值:

            public static final int PUBLIC           = 0x00000001; // 二进制:1
            public static final int PRIVATE          = 0x00000002; // 二进制:2
            public static final int PROTECTED        = 0x00000004; // 二进制:4
            public static final int STATIC           = 0x00000008; // 二进制:10
            public static final int FINAL            = 0x00000010; // 二进制:16
            public static final int SYNCHRONIZED     = 0x00000020; // 二进制:32
            public static final int VOLATILE         = 0x00000040; // 二进制:64
            public static final int TRANSIENT        = 0x00000080; // 二进制:200
            public static final int NATIVE           = 0x00000100; // 二进制:256
            public static final int INTERFACE        = 0x00000200; // 二进制:152
            public static final int ABSTRACT         = 0x00000400; // 二进制:1024
            public static final int STRICT           = 0x00000800; // 二进制:2048
            
        • getModifiers()结果值:

          • 修饰符的值的总和
          • 如接口普通方法返回的1025 = 1(PUBLIC) + 1024(ABSTRACT)

课时105:反射调用构造方法

  • 通过Class类获取类的构造方法:

    • 获取所有构造方法:

      • 只获取public构造:

        public Constructor<?>[] getConstructors() throws SecurityException

      • 可获取非public构造:

        public Constructor<?>[] getDeclaredConstructors() throws SecurityException

    • 获取指定构造方法:

      • 获取指定public构造:

        public Constructor getConstructor(Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException

      • 可获取非public指定构造:

        public Constructor getDeclaredConstructor(Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException

  • 示例(个人实现,原版代码请查看:https://developer.aliyun.com/article/768346?spm=a2c6h.12873639.0.0.358d2dc5XneXlf):

    • 获取所有构造:

      • 定义AbstractBase类:

        package cn.pjh.abs;
        public class AbstractBase {
            public AbstractBase() {
                System.out.println("我是public的无参构造");
            }
            public AbstractBase(String str) {
                System.out.println("我是public的单参构造");
            }
            protected AbstractBase(String str1, String str2) {
                System.out.println("我是protected的双参构造");
            }
            private AbstractBase(String str1, String str2, String str3) {
                System.out.println("我是private的三参构造");
            }
        }
        
      • 定义主类进行测试:

        package cn.pjh;
        import cn.pjh.abs.AbstractBase;
        import java.lang.reflect.Constructor;
        public class ClassDemo {
            public static void main(String[] args) {
                Class clazz = AbstractBase.class;
                { // 获取public构造
                    Constructor[] cons = clazz.getConstructors();
                    System.out.println("getConstructors():");
                    printConstructor(cons);
                }
                { // 获取所有构造
                    System.out.println("getDeclaredConstructors():");
                    Constructor[] cons = clazz.getDeclaredConstructors();
                    printConstructor(cons);
                }
        //        输出:
        //        getConstructors():
        //            - public cn.pjh.abs.AbstractBase(java.lang.String)
        //            - public cn.pjh.abs.AbstractBase()
        //        getDeclaredConstructors():
        //            - private cn.pjh.abs.AbstractBase(java.lang.String,java.lang.String,java.lang.String)
        //            - protected cn.pjh.abs.AbstractBase(java.lang.String, java.lang.String)
        //            - public cn.pjh.abs.AbstractBase(java.lang.String)
        //            - public cn.pjh.abs.AbstractBase()
            }
            public static void printConstructor(Constructor<?>[] cons) {
                for (Constructor con : cons) {
                    System.out.println(" - " + con);
                }
            }
        }
        
    • 获取指定构造:

      • 定义Person类:

        package cn.pjh.vo;
        public class Person {
            private String name;
            private int age;
            private String area;
            public Person() {}
            public Person(String name, int age) {
                this.name = name;
                this.age = age;
            }
            private Person(String name, int age, String area) {
                this(name, age);
                this.area = area;
            }
            public String toString() {
                return "姓名:" + this.name + ",年龄:" + this.age + ",地区:" + this.area;
            }
        }
        
      • 定义主类进行测试:

        package cn.pjh;
        import cn.pjh.vo.Person;
        import java.lang.reflect.Constructor;
        public class ClassDemo {
            public static void main(String[] args) throws Exception {
                Class clazz = Person.class;
                System.out.println("调用public构造进行实例化:");
                {
                    Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
                    Object obj = con.newInstance("皮卡丘", 10);
                    System.out.println(" - " + obj);
                }
                {
                    Constructor con = clazz.getConstructor(String.class, int.class);
                    Object obj = con.newInstance("皮卡丘", 11);
                    System.out.println(" - " + obj);
                }
                System.out.println("调用private构造进行实例化:");
                {
                    Constructor con = clazz.getDeclaredConstructor(String.class, int.class, String.class);
                    con.setAccessible(true); // 设置是否跳过Java语言访问检查。设置为true则可以访问private属性
                    Object obj = con.newInstance("皮卡丘", 12, "宝可梦"); // java.lang.IllegalAccessException
                    System.out.println(" - " + obj);
                }
                {
                    Constructor con = clazz.getConstructor(String.class, int.class, String.class); // java.lang.NoSuchMethodException
                    Object obj = con.newInstance("皮卡丘", 13, "宝可梦");
                    System.out.println(" - " + obj);
                }
            }
        }
        

        可以发现,在获取构造方法并进行实例化对象时:

        • 构造方法为public:

          getConstructor() 和 getDeclaredConstructor() 都能正常获取,然后也能正常实例化对象。

        • 构造方法为private:

          • getConstructor():不能正常获取构造方法,会抛出NoSuchMethodException的异常;
          • getDeclaredConstructor():可以正常获取构造方法,但在newInstance()仍会抛出IllegalAccessException的异常(如使用setAccessible()则可避免抛出此异常)。

课时106:反射调用普通方法

  • 通过Class类获取类的普通方法:

    • 获取所有普通方法:

      • 只获取public方法(本类及父类):

        public Method[] getMethods() throws SecurityException

      • 可获取非public方法(本类):

        public Method[] getDeclaredMethods() throws SecurityException

    • 获取指定普通方法:

      • 获取指定public方法(本类及父类):

        public Method getMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException

      • 可获取非public方法(本类):

        public Method getDeclaredMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException

  • 示例:

    • 获取所有普通方法:

      • 定义Person类:

        package cn.pjh.vo;
        public class Person {
            private String name;
            private int age;
            public Person() {}
            public Person(String name, int age) {
                this.name = name;
                this.age = age;
            }
            public String toString() {
                return "姓名:" + this.name + ",年龄:" + this.age;
            }
            public void pubMethod() {
                System.out.println("我是public的方法");
            }
            protected void proMethod() {
                System.out.println("我是protected的方法");
            }
            private void priMethod() {
                System.out.println("我是private的方法");
            }
        }
        
      • 定义主类进行测试:

        package cn.pjh;
        import cn.pjh.vo.Person;
        import java.lang.reflect.Method;
        public class ClassDemo {
            public static void main(String[] args) {
                Class clazz = Person.class;
                { // 获取本类及父类中public的方法
                    Method[] methods = clazz.getMethods();
                    System.out.println("getMethods():");
                    for (Method method : methods) {
                        System.out.println(" - " + method);
                    }
                }
                { // 获取本类中所有的方法
                    Method[] methods = clazz.getDeclaredMethods();
                    System.out.println("getDeclaredMethods():");
                    for (Method method : methods) {
                        System.out.println(" - " + method);
                    }
                }
        //        输出:
        //        getMethods():
        //            - public java.lang.String cn.pjh.vo.Person.toString()
        //            - public void cn.pjh.vo.Person.pubMethod()
        //            - 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 final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
        //            - 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()
        //        getDeclaredMethods():
        //            - public java.lang.String cn.pjh.vo.Person.toString()
        //            - protected void cn.pjh.vo.Person.proMethod()
        //            - private void cn.pjh.vo.Person.priMethod()
        //            - public void cn.pjh.vo.Person.pubMethod()
            }
        }
        

        可以发现:

        • getMethods():返回本类及父类的所有public方法
        • getDeclaredMethods():返回本类的所有public和非public方法
    • (仅作了解)自定义方法显示:

      package cn.pjh;
      import cn.pjh.vo.Person;
      import java.lang.reflect.Method;
      import java.lang.reflect.Modifier;
      public class ClassDemo {
          public static void main(String[] args) {
              Class clazz = Person.class;
              Method[] methods = clazz.getMethods();
              for (Method method : methods) {
                  System.out.print(Modifier.toString(method.getModifiers()) + " "); // 获取方法的修饰符
                  System.out.print(method.getReturnType() + " "); // 获取方法的返回值类型
                  System.out.print(method.getName() + "("); // 获取方法的名称
                  Class[] clazzArr = method.getParameterTypes(); // 获取方法的参数类型
                  for (int i = 0; i < clazzArr.length; i++) {
                      System.out.print(clazzArr[i].getName() + " arg" + i);
                      if (i != clazzArr.length-1) System.out.print(", ");
                  }
                  System.out.print(")");
                  clazzArr = method.getExceptionTypes(); // 获取方法的异常类型
                  for (int i = 0; i < clazzArr.length; i++) {
                      if (0 == i) System.out.print(" throws ");
                      System.out.print(clazzArr[i].getName());
                      if (i != clazzArr.length-1) System.out.print(", ");
                  }
                  System.out.println();
              }
      //        输出:
      //        public class java.lang.String toString()
      //        public void pubMethod()
      //        public final void wait(long arg0, int arg1) throws java.lang.InterruptedException
      //        public final void wait() throws java.lang.InterruptedException
      //        public final native void wait(long arg0) throws java.lang.InterruptedException
      //        public boolean equals(java.lang.Object arg0)
      //        public native int hashCode()
      //        public final native class java.lang.Class getClass()
      //        public final native void notify()
      //        public final native void notifyAll()
          }
      }
      
    • Method类的重要方法:

      • 通过反射调用实例化对象的指定方法:

        public Object invoke(Object obj, Object… args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

        • 定义Person类:

          package cn.pjh.vo;
          public class Person {
              private String name;
              private int age;
              public Person() {}
              public Person(String name, int age) {
                  this.name = name;
                  this.age = age;
              }
              public String getName() {
                  return this.name;
              }
              public void setName(String name) {
                  this.name = name;
              }
              public int getAge() {
                  return this.age;
              }
              public void setAge(int age) {
                  this.age = age;
              }
              public String toString() {
                  return "姓名:" + this.name + ",年龄:" + this.age;
              }
          }
          
        • 定义主类进行测试:

          package cn.pjh;
          import java.lang.reflect.Method;
          public class ClassDemo {
              public static void main(String[] args) throws Exception {
                  Class clazz = Class.forName("cn.pjh.vo.Person");
                  // 1. 获取类的实例化对象
                  Object obj = clazz.getDeclaredConstructor().newInstance();
                  // 2. 获取要调用的方法
                  Method method = clazz.getMethod("setName", String.class);
                  // 3. 使用invoke()调用方法
                  method.invoke(obj, "皮卡丘"); // 等价于Person对象.setName("皮卡丘")
                  // ————————————————
                  method = clazz.getMethod("getName");
                  System.out.println(method.invoke(obj)); // 等价于Person对象.getName()
              }
          }
          

          上述操作,不会有任何明确的类对象产生,一切都是依靠反射机制处理的,这样的处理避免了与某一个类耦合问题。这在较复杂的类设计上使用是非常方便的。

课时107:反射调用成员

  • 类结构中的最后一个核心的组成就是成员(Field),大部分情况下都会将其称为成员属性。

  • 通过Class类获取类的属性:

    • 获取所有属性:

      • 只获取public属性(本类及父类):

        public Field[] getFields() throws SecurityException

      • 可获取非public属性(本类):

        public Field[] getDeclaredFields() throws SecurityException

    • 获取指定属性:

      • 获取指定public属性(本类及父类):

        public Field getField(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException

      • 可获取非public属性(本类):

        public Field getDeclaredField(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException

  • 示例:

    • 获取所有成员:

      • 定义AbstractBase类:

        package cn.pjh.abs;
        public class AbstractBase {
            public String absName;
            private int absValue;
        }
        
      • 定义Person类:

        package cn.pjh.vo;
        import cn.pjh.abs.AbstractBase;
        public class Person extends AbstractBase {
            public String obj;
            private String name;
            private int age;
            public Person() {}
            public Person(String name, int age) {
                this.name = name;
                this.age = age;
            }
            public String toString() {
                return "姓名:" + this.name + ",年龄:" + this.age;
            }
        }
        
      • 定义主类进行测试:

        package cn.pjh;
        import java.lang.reflect.Field;
        public class ClassDemo {
            public static void main(String[] args) throws ClassNotFoundException {
                Class clazz = Class.forName("cn.pjh.vo.Person");
                { // 获取本类及父类中public的成员
                    Field[] fields = clazz.getFields();
                    System.out.println("getFields():");
                    for (Field field : fields) {
                        System.out.println(" - " + field);
                    }
                }
                { // 获取本类中所有的成员
                    Field[] fields = clazz.getDeclaredFields();
                    System.out.println("getDeclaredFields():");
                    for (Field field : fields) {
                        System.out.println(" - " + field);
                    }
                }
        //        输出:
        //        getFields():
        //            - public java.lang.String cn.pjh.vo.Person.obj
        //            - public java.lang.String cn.pjh.abs.AbstractBase.absName
        //        getDeclaredFields():
        //            - public java.lang.String cn.pjh.vo.Person.obj
        //            - private java.lang.String cn.pjh.vo.Person.name
        //            - private int cn.pjh.vo.Person.age
            }
        }
        
  • Field类中最重要的操作方法:

    • 设置属性内容:

      public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException

    • 获取属性内容:

      public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException

    • 解除封装:

      public void setAccessible(boolean flag)

    package cn.pjh;
    import java.lang.reflect.Field;
    public class ClassDemo {
        public static void main(String[] args) throws Exception {
            Class clazz = Class.forName("cn.pjh.vo.Person");
            Object obj = clazz.getDeclaredConstructor().newInstance(); // 获取实例化对象
            Field fieldName = clazz.getDeclaredField("name"); // 获取属性
            fieldName.setAccessible(true); // 解除属性的封装
            fieldName.set(obj, "皮卡丘"); // 设置属性值
            System.out.println(fieldName.get(obj)); // 获取属性值
        }
    }
    
  • 类中的构造、方法、成员属性,都可以通过反射实现调用,但对于成员的反射很少这样直接处理,大部分操作都应该使用setter和getter进行处理,所以对于以上的代码只能说是反射的特色,但不具备实际的使用能力。

  • Field类在实际开发中最为常用的方法:

    • 获取成员类型:public Class<?> getType()

      package cn.pjh;
      import java.lang.reflect.Field;
      public class ClassDemo {
          public static void main(String[] args) throws Exception {
              Class clazz = Class.forName("cn.pjh.vo.Person");
              Field fieldName = clazz.getDeclaredField("name"); // 获取属性
              clazz = fieldName.getType(); // 获取属性的类型
              System.out.println(clazz); // 输出:class java.lang.String
              System.out.println(clazz.getName()); // 输出:java.lang.String
              System.out.println(clazz.getSimpleName()); // 输出:String
          }
      }
      
  • 在以后开发中进行反射处理时,往往会利用Field类和Method类实现类中setter方法的调用。

课时108:Unsafe工具类

  • 反射是Java的第一大特点,一旦打开反射的大门就可以有更加丰富的类设计形式。除了JVM本身支持的反射处理之外,在Java中还提供了一个sun.misc.Unsafe类(不安全的操作)。这个类的主要特点是可以利用反射来获取对象,并且直接使用底层的C++来代替JVM执行,即:可以绕过JVM的相关的对象管理机制。但一旦使用Unsafe,那么项目之中将无法继续使用JVM的内存管理机制以及垃圾回收处理。

  • Unsafe类定义:

    • 构造方法:private Unsafe() {}
    • 常量:private static final Unsafe theUnsafe = new Unsaffe();

    在传统的开发中,程序类必须要实例化对象后才可以调用类中的普通方法,尤其是单例设计模式。

    然而,Unsafe类中并没有提供static的方法,即:不能通过类似于传统的单例设计模式中提供的样式来进行操作,如果想要获得这个类的对象,就必须利用反射机制来完成。

    package cn.pjh;
    import sun.misc.Unsafe;
    import java.lang.reflect.Field;
    public class ClassDemo {
        public static void main(String[] args) throws Exception {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafeObj = (Unsafe) field.get(null); // static属性不需要传递实例化对象
            // 利用Unsafe类绕过JVM管理机制,可以在没有实例化对象的情况下获取实例化对象
            Singleton instance = (Singleton) unsafeObj.allocateInstance(Singleton.class);
            instance.print(); // 输出:pikaqiu
        }
    }
    class Singleton {
        private Singleton() {
            System.out.println("******** Singleton类构造方法 ********");
        }
        public void print() {
            System.out.println("pikaqiu");
        }
    }
    

    (可以发现,通过Unsafe类直接获取实例化对象时,并没有调用要实例化对象的类的构造方法。)

  • Unsafe类,只能说为开发提供了一些更方便的处理机制,但这种操作并不受JVM管理,所以如果不是必须的情况下,不建议使用。讲解这个类的主要目的是帮助大家巩固对于反射的理解,同时也帮助大家在面试中回答关于单例设计模式问题时,可以获得更好的面试效果。

第24章:反射与简单Java类

课时109:传统属性赋值弊端

  • 简单Java类主要是由属性组成,并且提供有相应的setter、getter处理方法,同时简单Java类最大的特征就是通过对象保存相应的类属性内容。但是如果使用传统的简单Java类开发,那么也会面临非常麻烦的困难。

  • 如果一个类中有50个属性,那么在给属性赋值的时候则需要调用一堆的setter方法,此时代码的重复性将会非常地高。而想要解决对象的重复处理操作,唯一的解决方案就是反射机制。

课时110:属性自动赋值实现思路

​ 按照传统的直观的编程方式,所带来的问题就是代码会存在大量的重复操作,如果要想解决对象的重复处理操作,那么唯一的解决方案就是反射机制,反射机制最大的特征是可以根据其自身的特点(Object类直接操作,可以直接操作属性或方法)实现相同功能类的重复操作的抽象处理。

  • 想要实现属性内容的自动设置,则强烈建议采用字符串的形式来描述类型。

  • 属性自动赋值实现思路:

    1. 定义为属性初始化的字符串结构,如“属性名:内容|属性名:内容”;

    2. 类设计的基本结构:应该由一个专门的ClassInstanceFactory类负责所有的反射处理,即:接收反射对象与要设置的属性内容,同时可以获取指定类的实例化对象。

      反射与简单Java类.png

    3. 设计的基本结构:

      class Emp {
          private String ename;
          private String job;
          public String getEname() {
              return this.ename;
          }
          public void setEname(String ename) {
              this.ename = ename;
          }
          public String getJob() {
              return this.job;
          }
          public void setJob(String job) {
              this.job = job;
          }
      }
      class ClassInstanceFactory {
          private ClassInstanceFactory() {}
          /**
           * 实例化对象的创建方法,该对象可以根据传入的字符结构进行处理
           * @param clazz 要实例化的Class类对象
           * @param value 对象的属性内容
           * @param <T>
           * @return 配置好属性内容的对象
           */
          public static <T> T create(Class clazz, String value) {
              return null;
          }
      }
      public class JavaDemo{
          public static void main(String[] args) {
              String value = "ename:Pikaqiu|job:CEO";
              Emp emp = ClassInstanceFactory.create(Emp.class, value);
              System.out.println("姓名:" + emp.getEname() + ",职位:" + emp.getJob());
          }
      }
      

课时111:单级属性赋值

  • 单级设置:属性的数据类型没有引用关联(属性为自定义类的对象)。
  • 步骤:
    1. 通过反射进行指定类对象的实例化处理;
    2. 进行属性内容的设置。

反射与简单Java类2.PNG

  • 实现:

    1. 定义StringUtils工具类,实现首字母大写的功能:

      class StringUtils {
          private StringUtils() {}
          public static String initCap(String str) {
              if (null == str || "".equals(str.trim())) return str;
              else if (1 == str.length()) return str.toUpperCase();
              else return str.substring(0, 1).toUpperCase() + str.substring(1);
          }
      }
      
    2. 定义BeanUtils工具类,实现属性的设置:

      class BeanUtils {
          private BeanUtils() {}
          /**
           * 实现指定对象的属性设置
           * @param obj 实例化对象
           * @param value 要给属性设置的内容
           */
          public static void setValue(Object obj, String value) {
              // value格式为"属性名:内容|属性名:内容"
              String[] strArr = value.split("\\|");
              for (String str : strArr) {
                  String[] fieldArr = str.split(":");
                  try { // try...catch...之后,如属性名错误,也不会抛出异常了
                      Field field = obj.getClass().getDeclaredField(fieldArr[0]);
                      Method method = obj.getClass().getDeclaredMethod(
                              "set" + StringUtils.initCap(fieldArr[0]), field.getType());
                      method.invoke(obj, fieldArr[1]);
                  } catch (Exception e) {}
              }
          }
      }
      
    3. 完善ClassInstanceFactory类:

      class ClassInstanceFactory {
          private ClassInstanceFactory() {}
          /**
           * 实例化对象的创建方法,该对象可以根据传入的字符结构进行处理
           * @param clazz 要实例化的Class类对象
           * @param value 对象的属性内容
           * @param <T>
           * @return 配置好属性内容的对象
           */
          public static <T> T create(Class clazz, String value) {
              try { // 实例化的类中必须要有无参构造
                  Object obj = clazz.getDeclaredConstructor().newInstance();
                  BeanUtils.setValue(obj, value);
                  return (T) obj;
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      }
      
    4. 修改主类进行测试:

      public class ClassDemo{
          public static void main(String[] args) {
      //        String value = "ename:Pikaqiu|jobs:CEO"; // 姓名:Pikaqiu,职位:null
              String value = "ename:Pikaqiu|job:CEO"; // 姓名:Pikaqiu,职位:CEO
              Emp emp = ClassInstanceFactory.create(Emp.class, value);
              System.out.println("姓名:" + emp.getEname() + ",职位:" + emp.getJob());
          }
      }
      

课时112:设置多种数据类型

  • 然而在实际开发中,属性的数据类型并不一定都是String,所以我们应该实现各种数据类型属性的设置。

  • 实现:

    1. 修改Emp类,增加其余数据类型的属性:

      class Emp {
          private String ename;
          private String job;
          private int eno;
          private double salary;
          private Date hiredate;
          public String getEname() {
              return this.ename;
          }
          public void setEname(String ename) {
              this.ename = ename;
          }
          public String getJob() {
              return this.job;
          }
          public void setJob(String job) {
              this.job = job;
          }
          public int getEno() {
              return this.eno;
          }
          public void setEno(int eno) {
              this.eno = eno;
          }
          public double getSalary() {
              return this.salary;
          }
          public void setSalary(double salary) {
              this.salary = salary;
          }
          public Date getHiredate() {
              return this.hiredate;
          }
          public void setHiredate(Date hiredate) {
              this.hiredate = hiredate;
          }
      }
      
    2. 修改BeanUtils工具类:

      1. 新增一个属性转换的方法:

        /**
             * 实现属性类型转换处理
             * @param type 属性的类型
             * @param value 属性的内容
             * @return 根据类型转换的内容对象
             */
            public static Object convValue(String type, String value) {
                if ("byte".equals(type) || "java.lang.Byte".equals(type))
                    return Byte.parseByte(value);
                else if ("int".equals(type) || "java.lang.Integer".equals(type))
                    return Integer.parseInt(value);
                else if ("long".equals(type) || "java.lang.Long".equals(type))
                    return Long.parseLong(value);
                else if ("float".equals(type) || "java.lang.Float".equals(type))
                    return Float.parseFloat(value);
                else if ("double".equals(type) || "java.lang.Double".equals(type))
                    return Double.parseDouble(value);
                else if ("java.util.Date".equals(type)) {
                    SimpleDateFormat sdf = null;
                    if (8 == value.length() && value.matches("\\d{8}"))
                        sdf = new SimpleDateFormat("yyyyMMdd");
                    else if (10 == value.length()) {
                        if (value.matches("\\d{4}-\\d{2}-\\d{2}")) {
                            sdf = new SimpleDateFormat("yyyy-MM-dd");
                        }
                        else if (value.matches("\\d{4}\\/\\d{2}\\/\\d{2}")) {
                            sdf = new SimpleDateFormat("yyyy/MM/dd");
                        }
                    }
                    else {
                        sdf = new SimpleDateFormat("yyyy-MM-dd");
                        value = "1970-01-01";
                    }
                    try {
                        return sdf.parse(value);
                    } catch (Exception e) {
                        return null;
                    }
                }
                else return value;
            }
        
      2. 修改setValue()方法:

        public static void setValue(Object obj, String value) {
                // value格式为"属性名:内容|属性名:内容"
                String[] strArr = value.split("\\|");
                for (String str : strArr) {
                    String[] fieldArr = str.split(":");
                    try {
                        Field field = obj.getClass().getDeclaredField(fieldArr[0]);
                        Method method = obj.getClass().getDeclaredMethod(
                                "set" + StringUtils.initCap(fieldArr[0]), field.getType());
                        Object objValue = convValue(field.getType().getName(), fieldArr[1]);
                        method.invoke(obj, objValue);
                    } catch (Exception e) {}
                }
            }
        
    3. 修改主类进行测试:

      public class ClassDemo{
          public static void main(String[] args) {
              String value = "ename:Pikaqiu|job:CEO|eno:1|salary:10000.00|hiredate:2014-06-01";
              Emp emp = ClassInstanceFactory.create(Emp.class, value);
              System.out.println(emp);
          }
      }
      

课时113:级联对象实例化

  • 多级设置:属性的数据类型有引用关联(属性为自定义类的对象),如一个雇员属于一个部门,一个部门属于一个公司。

  • 实现:

    1. 定义Company类:

      class Company {
          private String cname;
          private Date createdate;
          public String getCname() {
              return this.cname;
          }
          public void setCname(String cname) {
              this.cname = cname;
          }
          public Date getCreatedate() {
              return this.createdate;
          }
          public void setCreatedate(Date createdate) {
              this.createdate = createdate;
          }
          public String toString() {
              return "公司名称:" + this.cname + ",公司成立日期:" + this.createdate;
          }
      }
      
    2. 定义Dept类:

      class Dept {
          private String dname;
          private String loc;
          private Company company;
          public String getDname() {
              return this.dname;
          }
          public void setDname(String dname) {
              this.dname = dname;
          }
          public String getLoc() {
              return this.loc;
          }
          public void setLoc(String loc) {
              this.loc = loc;
          }
          public Company getCompany() {
              return this.company;
          }
          public void setCompany(Company company) {
              this.company = company;
          }
          public String toString() {
              return "部门名称:" + this.dname + ",部门位置:" + this.loc + ",所属公司:【" + this.company + "】";
          }
      }
      
    3. 修改Emp类:

      class Emp {
          private String ename;
          private String job;
          private int eno;
          private double salary;
          private Date hiredate;
          private Dept dept;
          public String getEname() {
              return this.ename;
          }
          public void setEname(String ename) {
              this.ename = ename;
          }
          public String getJob() {
              return this.job;
          }
          public void setJob(String job) {
              this.job = job;
          }
          public int getEno() {
              return this.eno;
          }
          public void setEno(int eno) {
              this.eno = eno;
          }
          public double getSalary() {
              return this.salary;
          }
          public void setSalary(double salary) {
              this.salary = salary;
          }
          public Date getHiredate() {
              return this.hiredate;
          }
          public void setHiredate(Date hiredate) {
              this.hiredate = hiredate;
          }
          public Dept getDept() {
              return this.dept;
          }
          public void setDept(Dept dept) {
              this.dept = dept;
          }
          public String toString() {
              return "姓名:" + this.ename + ",职位:" + this.job +
                      ",工号:" + this.eno + ",薪水:" + this.salary + ",入职日期:" + this.hiredate +
                      ", 部门:【" + this.dept + "】";
          }
      }
      
    4. 修改BeanUtils类:

      1. 方式一(视频实现):

        1. 直接修改setValue()方法:

          public static void setValue(Object obj, String value) {
                  // value格式为"属性名:内容|属性名:内容|"
                  String[] strArr = value.split("\\|"); // 根据属性进行分割
                  for (String str : strArr) {
                      String[] fieldArr = str.split(":"); // 根据属性名与属性内容进行分割
                      try {
                          if (fieldArr[0].contains(".")) { // 属性为级联对象
                              String[] fieldObjArr = fieldArr[0].split("\\.");
                              Object currObj = obj;
                              Class clazz = currObj.getClass();
                              for (int i = 0; i < fieldObjArr.length - 1; i++) {
                                  String fieldName = fieldObjArr[i];
                                  Method method = clazz.getDeclaredMethod("get" + StringUtils.initCap(fieldName));
                                  Object currFieldObj = method.invoke(currObj);
                                  if (null == currFieldObj) { // 属性对象没有创建,则需要创建对象并set
                                      Field field = clazz.getDeclaredField(fieldName);
                                      Class fieldType = field.getType();
                                      currFieldObj = fieldType.getDeclaredConstructor().newInstance();
                                      method = clazz.getDeclaredMethod("set" + StringUtils.initCap(fieldName), fieldType);
                                      method.invoke(currObj, currFieldObj);
                                  }
                                  currObj = currFieldObj;
                                  clazz = currObj.getClass();
                              }
                              // 给最终属性进行赋值
                              String fieldName = fieldObjArr[fieldObjArr.length - 1];
                              Field field = clazz.getDeclaredField(fieldName);
                              Method method = clazz.getDeclaredMethod("set" + StringUtils.initCap(fieldName), field.getType());
                              Object objValue = convValue(field.getType().getName(), fieldArr[1]);
                              method.invoke(currObj, objValue);
                          }
                          else { // 属性非级联对象
                              Field field = obj.getClass().getDeclaredField(fieldArr[0]);
                              Method method = obj.getClass().getDeclaredMethod(
                                      "set" + StringUtils.initCap(fieldArr[0]), field.getType());
                              Object objValue = convValue(field.getType().getName(), fieldArr[1]);
                              method.invoke(obj, objValue);
                          }
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
              }
          
      2. 方式二(个人实现):

        1. 新增可实现级联对象属性设置的方法:

          /**
               * 根据字符串设置属性对象,支持单级属性和级联对象属性设置(如ename和dept.company.cname)
               * @param obj 属性对象(如dept)
               * @param leftFieldName 剩余的属性名称字符串(如company.cname)
               * @param value 属性内容
               * @return 已经递归设置好最内层属性内容value的属性对象obj
               * @throws Exception
               */
              private static Object setObjValue(Object obj, String leftFieldName, String value) throws Exception {
                  Class clazz = obj.getClass();
                  if (leftFieldName.contains(".")) { // 剩余属性名称字符串中仍有“.”,表示非最终属性
                      String[] fieldNameArr = leftFieldName.split("\\.", 2);
                      String CapFieldName = StringUtils.initCap(fieldNameArr[0]);
                      // 获取属性对象
                      Method method = clazz.getDeclaredMethod("get" + CapFieldName);
                      Object fieldObj = method.invoke(obj);
                      Field field = clazz.getDeclaredField(fieldNameArr[0]);
                      // 如属性对象未创建,则进行创建
                      if (null == fieldObj) {
                          fieldObj = field.getType().getDeclaredConstructor().newInstance();
                      }
                      // 继续递归设置属性对象
                      fieldObj = setObjValue(fieldObj, fieldNameArr[1], value);
                      // 将设置好内层属性内容的属性对象进行set
                      method = clazz.getDeclaredMethod("set" + CapFieldName, field.getType());
                      method.invoke(obj, fieldObj);
                  }
                  else { // 剩余属性名称字符串中没有“.”,表示为最终属性(不为嵌套对象的属性),可直接设置属性内容
                      Field field = clazz.getDeclaredField(leftFieldName);
                      Class fieldType = field.getType();
                      Method method = clazz.getDeclaredMethod("set" + StringUtils.initCap(leftFieldName), fieldType);
                      Object objValue = convValue(fieldType.getName(), value);
                      method.invoke(obj, objValue);
                  }
                  return obj;
              }
          
        2. 修改setValue()方法:

          public static void setValue(Object obj, String value) {
                  // value格式为"属性名:内容|属性名:内容|"
                  String[] strArr = value.split("\\|");
                  for (String str : strArr) {
                      String[] fieldArr = str.split(":");
                      try {
                          setObjValue(obj, fieldArr[0], fieldArr[1]);
                      } catch (Exception e) {}
                  }
              }
          
    5. 修改主类进行测试:

      public class ClassDemo{
          public static void main(String[] args) {
              String value = "ename:Pikaqiu|job:CEO|eno:1|salary:10000.00|hiredate:2014-06-01|" +
                      "dept.dname:总经办|dept.company.cname:pokemon";
              Emp emp = ClassInstanceFactory.create(Emp.class, value);
              System.out.println(emp);
          }
      }
      

课时114:级联属性赋值

  • 略。个人已在上节代码中实现。

第25章:ClassLoader类加载器

课时115:ClassLoader类加载器简介

  • 在Java语言中提供了一个系统的环境变量:CLASSPATH,这个环境属性的作用主要是在JVM进程启动时进行类加载路径的定义。在JVM中可以根据类加载器进行指定路径中类的加载,也就是说找到了类的加载器就意味着找到了类的来源。

    ClassLoader.PNG

  • 系统类加载器:

    • 获取类的加载器:通过Class类来获取ClassLoader类的对象。
    • 调用方法:
      • 获取该类的类加载器:public ClassLoader getClassLoader()
      • 获取该类的父类的类加载器:public final ClassLoader getParent()
  • 示例:

    package cn.pjh;
    class Person {}
    public class ClassDemo{
        public static void main(String[] args) {
            Class clazz = Person.class;
            System.out.println(clazz.getClassLoader()); // 获取Person类的ClassLoader
            System.out.println(clazz.getClassLoader().getParent()); // 获取Person类父类(Object类)的ClassLoader
            System.out.println(clazz.getClassLoader().getParent().getParent()); // 获取Person类父类(Object类)父类的ClassLoader
    //        输出:
    //        jdk.internal.loader.ClassLoaders$AppClassLoader@726f3b58
    //        jdk.internal.loader.ClassLoaders$PlatformClassLoader@e73f9ac
    //        null
        }
    }
    
  • 在JDK1.8之后的版本,提供有一个“PlatformClassLoader”类加载器;而在JDK1.8及以前的版本中提供的加载器为“ ExtClassLoader”,因为在JDK的安装目录中提供了一个ext的目录,开发者可以将*.jar文件拷贝到此目录中,这样就可以直接执行了,但是这样的处理开发并不安全,最初的时候也是不提倡使用的,所以从JDK1.9开始将其彻底废除了,同时为了与系统类加载器和应用类加载器之间保持设计的平衡,提供有“PlatformClassLoader”平台类加载器。

    ClassLoader2.PNG

    (Bootstrap系统类加载器是看不见的,也就是调用getClassLoader()方法是不会输出Bootstrap加载器的。)

    (类加载器是由下往上查找,然后由上往下执行的。)

  • 当你获得了类加载器后就可以利用类加载器来实现类的反射加载处理:

    • ClassLoader类的方法:

      protected Class<?> findClass(String name) throws ClassNotFoundException

课时116:自定义ClassLoader处理类

  • 清楚了类加载器的功能后就可以根据自身的需求来实现自定义的类加载器,但千万要记住一点:自定义类加载器的加载顺序是在所有类加载器的最后。系统类中的类加载器都是根据CLASSPATH路径进行加载的,而如果有了自定义的类加载器,就可以由开发者任意指定类的加载位置。

    自定义类加载器.PNG

  • 将字节数据转换为类结构(加载类):

    • ClassLoader类的方法:

      protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError

  • 实现:

    1. 编写一个程序类:

      package cn.pjh.util;
      public class Message {
          public void send(){
              System.out.println("pikaqiu");
          }
      }
      
    2. 将其拷贝到磁盘中,并进行编译获得.class文件来读取数据;

    3. 自定义类加载器:

      package cn.pjh;
      import java.io.*;
      public class myClassLoader extends ClassLoader {
          // 1. 定义需要读取的.class文件名称(用来获取字节数据)
          private static final String MESSAGE_CLASSPATH = "d:" + File.separator + "test" + File.separator + "Message.class";
          // 2. 使用defineClass()方法来将字节数据转换成类结构
          public Class loadData(String className) throws Exception {
              byte[] data = loadClassData();
              if (null != data) return super.defineClass(className, data, 0, data.length);
              return null;
          }
          // 3. 读取.class文件的数据,返回字节数据
          private byte[] loadClassData() throws Exception {
              InputStream is = null;
              ByteArrayOutputStream bos = null;
              byte[] data = null;
              try {
                  bos = new ByteArrayOutputStream();
                  is = new FileInputStream(new File(MESSAGE_CLASSPATH));
                  is.transferTo(bos);
                  data = bos.toByteArray();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  if (null != is) is.close();
                  if (null != bos) bos.close();
              }
              return data;
          }
      }
      
      
    4. 编写主类进行测试:

      package cn.pjh.test;
      import cn.pjh.myClassLoader;
      import java.lang.reflect.Method;
      public class ClassDemo{
          public static void main(String[] args) throws Exception {
              myClassLoader classLoader = new myClassLoader();
              // loadData()方法的参数,不需要写包名,写了包名反而抛出java.lang.NoClassDefFoundError的异常(JDK9)
              // Class clazz = classLoader.loadData("cn.pjh.util.Message");
              Class clazz = classLoader.loadData("Message");
              Object obj = clazz.getDeclaredConstructor().newInstance();
              Method method = clazz.getDeclaredMethod("send");
              method.invoke(obj);
          }
      }
      
    5. 查看类加载器:

      package cn.pjh.test;
      import cn.pjh.myClassLoader;
      import java.lang.reflect.Method;
      public class ClassDemo{
          public static void main(String[] args) throws Exception {
              myClassLoader classLoader = new myClassLoader();
              Class clazz = classLoader.loadData("Message");
              System.out.println(clazz.getClassLoader());
              System.out.println(clazz.getClassLoader().getParent());
              System.out.println(clazz.getClassLoader().getParent().getParent());
      //        输出:
      //        cn.pjh.myClassLoader@7f63425a
      //        jdk.internal.loader.ClassLoaders$AppClassLoader@726f3b58
      //        jdk.internal.loader.ClassLoaders$PlatformClassLoader@e73f9ac
          }
      }
      
  • 应用项目:

    如果结合网络程序开发的话,就可以通过一个远程的服务器来确定一个类的功能。

    自定义类加载器应用项目.PNG

  • 如果自定义了一个类:java.lang.String,并且利用自定义的类加载器进行加载处理,但这个类将不会被加载。因为Java之中针对于类加载器提供有双亲委派机制,如果现在要加载的程序类,在系统类中已经有提供,那么就会由系统类加载器进行加载,这是为了保证系统的安全性。

第26章:反射与代理设计模式

课时117:静态代理设计模式

  • 代理设计模式是在程序开发中使用最多的设计模式,代理设计模式的核心是:有真实业务实现类和代理业务实现类,并且代理类要完成比真实业务更多的处理操作。

  • 传统代理设计模式:

    interface IMessage { // 消息接口
        void send();
    }
    class MessageReal implements IMessage { // 消息真实(实现)类
        @Override
        public void send() {
            System.out.println("【发送消息】pikaqiu~~~");
        }
    }
    class MessageProxy implements IMessage { // 消息代理类
        private IMessage message; // 代理接口
        public MessageProxy(IMessage message) {
            this.message = message;
        }
        @Override
        public void send() {
            if (this.connect()) {
                this.message.send();
                this.close();
            }
        }
        public boolean connect() {
            System.out.println("【消息代理】进行消息发送通道的连接。");
            return true;
        }
        public void close() {
            System.out.println("【消息代理】关系消息通道。");
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            IMessage message = new MessageProxy(new MessageReal()); // 代理类与实现子类的耦合
            message.send();
        }
    }
    

    以上是一个最为标准的代理设计,但客户端的接口与具体的子类产生了耦合,所以如果从实际的开发来讲,最好再引入工厂设计模式进行代理对象的获取。

    以上的代理设计模式为静态代理设计,这种静态代理设计的特点在于:一个代理类只为一个接口服务(代理类需要实现接口类),如果现在准备有3000个业务接口,则按照此种做法就意味着需要编写3000个代理类,并且这些代理类操作形式类似。

    所以现在需要解决的问题在于:如何可以让一个代理类满足于所有的业务接口操作要求,此时就需要用到动态代理设计。

课时118:动态代理设计模式

  • 动态代理设计模式:为所有功能一致的业务操作接口提供统一的代理操作。

    动态代理设计模式.PNG

  • 在进行动态代理实现的操作中,首先需要关注的就是一个InvocationHandler接口,这个接口规定了代理方法的执行:

    public interface InvocationHandler{
        /**
         * 代理方法调用,代理主体类中执行的方法最终都是此方法
         * @param proxy 要代理的对象
         * @param method 要执行的接口方法名称
         * @param args 传递的参数
         * @return 某一个方法的返回值
         * @throws Throwable 方法调用时出现的错误继续向上抛出
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }
    
  • 在进行动态代理设计时,对于动态对象的创建是由JVM底层完成的,此时主要依靠的是java.lang.reflect.Proxy程序类:

    • 获取代理对象的实例化方法:
      • public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        • ClassLoader loader:获取当前真实业务类的ClassLoader;
        • Class<?>[] interfaces:真实业务类的接口Class;
        • InvocationHandler h:代理类对象;
  • 实现:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    interface IMessage { // 消息接口
        void send();
    }
    class MessageReal implements IMessage { // 消息真实(实现)类
        @Override
        public void send() {
            System.out.println("【发送消息】pikaqiu~~~");
        }
    }
    class myProxy implements InvocationHandler { // 代理类
        private Object target; // 真实业务对象
        public Object bind(Object target) {
            this.target = target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
        public boolean connect() {
            System.out.println("【消息代理】进行消息发送通道的连接。");
            return true;
        }
        public void close() {
            System.out.println("【消息代理】关系消息通道。");
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("******【执行方法】" + method);
            Object returnData = null;
            if (this.connect()) {
                returnData = method.invoke(this.target, args);
                this.close();
            }
            return returnData;
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            IMessage message = (IMessage) new myProxy().bind(new MessageReal());
            message.send();
        }
    }
    

    此时,代理类只关心具体的业务处理,无需实现指定类接口,可为需要实现同样业务处理的多接口进行代理。

课时119:CGLIB实现代理设计模式

  • Java官方只提供了基于接口的代理设计模式,但有开发者认为代理设计模式并不一定需要基于接口,因此就有了第三方包CGLIB的产生。(使用CGLIB,需要在ide中进行配置。)
  • 如使用CGLIB包进行代理设计模式的实现,则真实业务子类不需要实现业务接口,代理类实现类似于动态代理的实现,但在创建代理类对象时,需要进行一系列的CGLIB处理。
  • 具体实现代码及分析可查看:https://developer.aliyun.com/article/768669?spm=a2c6h.12873639.0.0.615a5ff1mF1dcg

第27章:反射与Annotation

课时120:反射取得Annotation信息

  • 从JDK1.5后,Java开发提供了Annotation技术支持,这种技术为项目的编写带来了新的模型,而后经过了十多年的发展,Annotation的技术得到了非常广泛的应用,并且在所有的项目开发中都会存在。

  • 在进行类或方法定义时,可以使用一系列的Annotation进行声明,如果想要获得这些Annotation的信息,可以通过反射来完成。

  • 在java.lang.reflect里面有一个AccessibleObject类,在此类中提供有获取Annotation类的方法:

    • 获取全部Annotation:

      public Annotation[] getAnnotations()

    • 获取指定Annotation:

      public T getAnnotation(Class annotationClass)

    AccessibleObject.png

  • 示例:

    import java.io.Serializable;
    import java.lang.annotation.Annotation;
    @FunctionalInterface
    @Deprecated(since = "1.1")
    interface IMessage {
        void send(String msg);
    }
    @SuppressWarnings("serial") // 执行程序后无法获取
    class MessageImpl implements IMessage, Serializable {
        @Override // 执行程序后无法获取
        public void send(String msg) {
            System.out.println("【消息发送】" + msg);
        }
    }
    public class JavaDemo{
        public static void main(String[] args) throws Exception {
            { // 获取IMessage接口的所有注解
                Annotation[] annotations = IMessage.class.getAnnotations();
                printAnnotation(annotations);
            }
            System.out.println("————————————————");
            { // 获取MessageImpl类的所有注解
                Annotation[] annotations = MessageImpl.class.getAnnotations();
                printAnnotation(annotations);
            }
            System.out.println("————————————————");
            { // 获取MessageImpl类中send()方法上的注解
                Annotation[] annotations = MessageImpl.class.getDeclaredMethod("send", String.class).getAnnotations();
                printAnnotation(annotations);
            }
    //        输出:
    //        @java.lang.FunctionalInterface()
    //        @java.lang.Deprecated(forRemoval=false, since="1.1")
    //        ————————————————
    //        ————————————————
        }
        public static void printAnnotation(Annotation[] annotations) {
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
        }
    }
    

    查看注解:

    • @FunctionalInterface:

      @Documented
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.TYPE)
      public @interface FunctionalInterface {}
      
    • @SuppressWarnings:

      @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
      @Retention(RetentionPolicy.SOURCE)
      public @interface SuppressWarnings {
          String[] value();
      }
      

    可以发现,它们的RetentionPolicy的值不一样。

    RetentionPolicy为注解保留策略,它是一个enum枚举类,有三个值:

    • SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
    • CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
    • RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。

课时121:自定义Annotation

  • 自定义Annotation:使用“@interface”。

  • 实现:

    import java.lang.annotation.Annotation;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.reflect.Method;
    
    @Retention(RetentionPolicy.RUNTIME) // 定义保存策略
    @interface MessageAnnotation { // 自定义注解
        public String title(); // 没有默认值,使用注解时必须传入
        public String to() default "pikaqiu";  // 有默认值
    }
    class Message {
        @MessageAnnotation(title = "hello")
        public void send(String msg) {
            System.out.println("【消息发送】" + msg);
        }
    }
    public class JavaDemo{
        public static void main(String[] args) throws Exception {
            Class clazz = Message.class;
            Method method = clazz.getDeclaredMethod("send", String.class);
            MessageAnnotation anno = method.getAnnotation(MessageAnnotation.class);
    //        System.out.println(anno); // 输出:@MessageAnnotation(to="pikaqiu", title="hello")
    //        System.out.println(anno.title()); // 输出:hello
    //        System.out.println(anno.to()); // 输出:pikaqiu
            String msg = anno.title() + "," + anno.to();
            method.invoke(clazz.getDeclaredConstructor().newInstance(), msg); // 输出:【消息发送】hello,pikaqiu
        }
    }
    
  • Annotation的最大特点:结合反射机制实现程序的处理(如利用注解的内容进行方法的调用)。

课时122:工厂设计模式与Annotation整合

  • 示例:

    • 标准的工厂+代理模式:

      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      interface IMessage {
          void send(String msg);
      }
      class MessageImpl implements IMessage {
          @Override
          public void send(String msg) {
              System.out.println("【消息发送】" + msg);
          }
      }
      class MessageProxy implements InvocationHandler {
          private Object target;
          public Object bind(Object target) {
              this.target = target;
              return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
          }
          public boolean connect() {
              System.out.println("【代理操作】进行消息发送通道的连接。");
              return true;
          }
          public void close() {
              System.out.println("【代理操作】关闭连接通道。");
          }
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              try {
                  if (this.connect()) return method.invoke(this.target, args);
                  else throw new Exception("【ERROR】消息无法进行发送!");
              } finally {
                  this.close();
              }
          }
      }
      class Factory {
          private Factory() {}
          public static <T> T getInstatnce(Class<T> clazz) {
              try {
                  return (T) new MessageProxy().bind(clazz.getDeclaredConstructor().newInstance());
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      }
      class MessageService { // 进行包装的操作类
          private IMessage message;
          public MessageService() {
              this.message = Factory.getInstatnce(MessageImpl.class);
          }
          public void send(String msg) {
              this.message.send(msg);
          }
      }
      public class JavaDemo{
          public static void main(String[] args) {
      //        IMessage msg = Factory.getInstatnce(MessageImpl.class);
      //        msg.send("pikaqiu~~~");
              MessageService messageService = new MessageService();
              messageService.send("pikaqiu~~~");
          }
      }
      
    • 使用注解进行真实业务类的配置:

      1. 新增注解:

        @Retention(RetentionPolicy.RUNTIME)
        @interface UseMessage {
            public Class clazz();
        }
        
      2. 修改MessageService类:

        @UseMessage(clazz = MessageImpl.class) // 利用注解配置真实业务类
        class MessageService { // 进行包装的操作类
            private IMessage message;
            public MessageService() {
                UseMessage use = MessageService.class.getAnnotation(UseMessage.class);
                this.message = (IMessage) Factory.getInstatnce(use.clazz());
            }
            public void send(String msg) {
                this.message.send(msg);
            }
        }
        
    • 更换实现真实业务类:

      1. 新增真实业务类:

        class NetMessageImpl implements IMessage {
            @Override
            public void send(String msg) {
                System.out.println("【网络消息发送】" + msg);
            }
        }
        
      2. 修改MessageService注解的参数:

        // 更改注解的clazz参数即可更换真实业务类
        @UseMessage(clazz = NetMessageImpl.class)
        class MessageService { // 进行包装的操作类
            private IMessage message;
            public MessageService() {
                UseMessage use = MessageService.class.getAnnotation(UseMessage.class);
                this.message = (IMessage) Factory.getInstatnce(use.clazz());
            }
            public void send(String msg) {
                this.message.send(msg);
            }
        }
        
  • 使用Annotation的属性,可以使整体代码变得整洁。

————————————————————————————————————

思维导图

《Java语言高级特性》笔记 第21~27章反射.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值