Java快速开发学习

锁清秋

反射和注解和类加载器和类的加载方式

反射机制概念 (运行状态中知道类所有的属性和方法)

在 Java 中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为 Java 语言的反射机制。

Java 反射 API

反射 API 用来生成 JVM 中的类、接口或则对象的信息。

  1. Class 类:反射的核心类,可以获取类的属性,方法等信息。
  2. Field 类: Java.lang.reflec 包中的类, 表示类的成员变量,可以用来获取和设置类之中的属性值。
  3. Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
  4. Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。

    反射使用步骤(获取 Class 对象、调用对象方法)

  5. 获取想要操作的类的 Class 对象,他是反射的核心,通过 Class 对象我们可以任意调用类的方法。

  6. 调用 Class 类中的方法,既就是反射的使用阶段。
  7. 使用反射 API 来操作这些信息。

    获取字节码的三种方法

    获取class对象的三种方法

  8. Class.forName(全类名)

  9. 类名.class
  10. 对象.getClass()

如:

  1. //1.Class.forName(全类名)
  2. Class<?> clazz1 = Class.forName("com.njau.construct.Person");
  3. //2.类名.class
  4. Class<Person> clazz2 = Person.class;
  5. //3.对象.getClass()
  6. Person person = new Person();
  7. Class<? extends Person> clazz3 = person.getClass();

创建对象的两种方法

Class 对象的 newInstance()

  1. 使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。

调用 Constructor 对象的 newInstance()

  1. 先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()
    方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。 ``` //获取 Person 类的 Class 对象 Class clazz=Class.forName(“reflection.Person”); //使用.newInstane 方法创建对象 Person p=(Person) clazz.newInstance(); //获取构造方法并创建对象 Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class); //创建对象并设置属性 Person p1=(Person) c.newInstance(“李四”,”男”,20);
  1. > **(其他操作可看JDK文档的Class类进行)**
  2. ### new关键字和newInstance()方法的区别
  3. - newInstance:弱类型,低效率,只能调用无参构造;
  4. - new:强类型,相对高效,能调用任何Public构造。
  5. ## 类加载器
  6. 虚拟机设计团队把加载动作放到 JVM 外部实现,以便让应用程序决定如何获取所需的类, JVM 提供了 3 种类加载器:
  7. ### 1.启动类加载器(Bootstrap ClassLoader)
  8. 1. 负责加载 JAVA_HOME\lib 目录中的, 或通过 -Xbootclasspath 参数指定路径中的, 且被虚拟机认可(按文件名识别, rt.jar 的类。
  9. ### 2.扩展类加载器(Extension ClassLoader)
  10. 1. 负责加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类库。
  11. ### 3.应用程序类加载器(Application ClassLoader)
  12. 3. 负责加载用户路径(classpath)上的类库。<br />
  13. JVM 通过**双亲委派**模型进行类的加载, 当然我们也可以通过继承 java.lang.ClassLoader实现自定义的类加载器。
  14. ![classloader](https://zhangwenjun-1258908231.cos.ap-nanjing.myqcloud.com/njauit/1587831647.png)
  15. ### 双亲委派
  16. 当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class), 子类加载器才会尝试自己去加载。<br />
  17. 采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就**保证了使用不同的类加载器最终得到的都是同样一个 Object 对象**。(避免了重复加在和官方的类发生冲突)<br />![双亲委派](https://zhangwenjun-1258908231.cos.ap-nanjing.myqcloud.com/njauit/1587840199.png)
  18. ## 类的加载(三种方式)
  19. ### Class的装载
  20. Class的装载包括3个步骤:加载(loading),连接(link),初始化(initialize
  21. 1. 加载:通过 Classloader 加载.class文件字节码,生成 Class对象;
  22. 1. 连接:<br />
  23. 1)、验证:确保被加载的类的正确性;<br />
  24. 2)、准备:为类的静态变量分配内存,并将其初始化为默认值(这里的‘默认值’是类变量类型的的初始值,而不是实质值);<br />
  25. 3)、解析:JVM 将常量池内的符号引用转换为直接引用;
  26. 1. 初始化:执行类变量赋值和静态代码块
  27. 类加载分为动态加载和静态加载。动态加载是从外存储器中加载类,一般类加载机制分析的也是动态加载。而静态加载本质上是从内存中创建类的实例对象,此时类已经被加载到内存中。
  28. ### 类的加载方式 ()
  29. 隐式加载:(静态加载)
  30. 1.
  31. `new`关键字创建一个类的实例<br />
  32. 在由运行时刻用 new 方法载入<br />
  33. 例:

Person person = new Person();

  1. 显式加载:(动态加载)
  2. 2.
  3. 使用`Class.forName()`<br />
  4. 通过反射加载类型,并创建对象实例<br />
  5. 例:

Class clazz = Class.forName(“Person”); Object person =clazz.newInstance();

  1. 2.
  2. 使用某个`ClassLoader`实例的`loadClass()`方法<br />
  3. 通过该 ClassLoader 实例的 loadClass() 方法载入。应用程序可以通过继承 ClassLoader 实现自己的类装载器。<br />
  4. 例:

Class clazz = classLoader.loadClass(“Person”); Object person =clazz.newInstance();

  1. ### 三种类加载方式的比较(new , Class.forName() , ClassLoader 实例的 loadClass()方法)
  2. #### 区别
  3. 1. 加载器不同
  4. 1 2 使用的类加载器是相同的,都是当前类加载器(即:`this.getClass.getClassLoader`)。<br />
  5. 3 由用户指定类加载器。如果需要在当前类路径以外寻找类,则只能采用第3种方式。即第3种方式加载的类与当前类分属不同的命名空间。
  6. 2. 加载本质不同
  7. 1是静态加载,23是动态加载
  8. 3. 得到的 class 对象的装载执行程度不同
  9. Class.forName得到的 Class对象是已经初始化了的,而Classloader.loaderclass得到的Class对象是还没有连接的。即两者都是 动态加载,但是加载的程度是不相同的,加载的速度也是不同,应为loaderclass()方法加载的Class对象还没有连接,而通过forName()方法加载的Class 对象以及初始化完成,所以两者的速的上比较,显然前者的加载速度比后者快很多,而且前者的延迟加载的程度要远大于后者,这也是 Spring IOC 为了加快初始化速度大量而大量采用 Classloader.loaderclass()加载类的原因。
  10. ## 注解
  11. 例:

/**

  • @author 张文军
  • @Description:
  • @Company:南京农业大学工学院
  • @version:1.0
  • @date 2019/8/3113:57 / @Target({ElementType.TYPE})//注解可以用于什么位置 @Retention(RetentionPolicy.RUNTIME)//注解保留的时期(源码,Class,RunTime) public @interface Pro {//注解的形式//public @interface 注解名称 /** 以下为注解的属性,即接口的方法 / /**

    • 类的全路径
    • @return */ String clazzPath();

      /**

    • 方法名
    • @return */ String methName(); }
  1. ## 解析注解

//获取当前类的Class(字节码文件对象)、 Class testProClass = TestPro.class; /* 获取注解的对象(注解在哪里就获取哪里的注解对象,这里是在类上, 所以获取该类的注解对象)用相对应的对象获取指定的注解对象,如当前是Pro.class 注解 / Pro proClassAnnotation = testProClass.getAnnotation(Pro.class); //获取注解的属性 String clazzPath = proClassAnnotation.clazzPath(); String methName = proClassAnnotation.methName(); //用反射的方法创建对象和运行方法(注解的使用) Class<?> aClass = Class.forName(clazzPath); Object instance = aClass.newInstance(); Method method = aClass.getMethod(methName); method.invoke(instance);

```