每一年都奔走在自己热爱里

没有人是一座孤岛,总有谁爱着你

注解与反射基础知识梳理

注解

1.什么是注解

  • 不是必须的,但是可以对程序做出解释,可以被其他程序(比如:编译器)读取。

  • 格式:@注释名 ,可以对其添加一些参数。

    例如 :@SuppressWarnings(value="unchecked")

  • 注释可以使用在package,class,method,field等上面,给其添加辅助信息。

2.内置注解

@Override @Deprecated @SuppressWarnings

3.元注解

  • @Target :用于描述注解的使用范围
//自定义注解的方法
@Target(value=ElementType.MEYHOD)
public @interface MyAnnotation{
    
}

//Target源码
@Documented //注解被包含于javadoc
@Retention(RetentionPolicy.RUNTIME)//运行时作用
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();//一个枚举类型的数组
}

//可以传入的参数
package java.lang.annotation;

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */

    FIELD,              /* 字段声明(包括枚举常量)  */

    METHOD,             /* 方法声明  */

    PARAMETER,          /* 参数声明  */

    CONSTRUCTOR,        /* 构造方法声明  */

    LOCAL_VARIABLE,     /* 局部变量声明  */

    ANNOTATION_TYPE,    /* 注释类型声明  */

    PACKAGE             /* 包声明  */
}
  • @Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期
    • SOURCE<CLASS<RUNTIME
/**
 * Indicates how long annotations with the annotated type are to
 * be retained.(指示生命周期的长度)  If no Retention annotation is present on
 * an annotation type declaration, the retention policy defaults to
 * {@code RetentionPolicy.CLASS}.(默认是CLASS)
 *
 * <p>A Retention meta-annotation has effect only if the
 * meta-annotated type is used directly for annotation.  It has no
 * effect if the meta-annotated type is used as a member type in
 * another annotation type.(不能用于其他注解的成员类型)
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.4.2 @Retention
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

//参数取值的说明
package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,            /* 若 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。 例如," @Override" 标志就是一个 Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处理完后,"@Override" 就没有任何作用了。  */

    CLASS,             /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */

    RUNTIME            /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
  • @Document :说明该注解将被包含在Javadoc中
  • @Inherited :说明子类可以继承父类中的注解
    • 假设,我们定义了某个 Annotaion,它的名称是 MyAnnotation,并且 MyAnnotation 被标注为 @Inherited。现在,某个类 Base 使用了MyAnnotation,则 Base 具有了"具有了注解 MyAnnotation";现在,Sub 继承了 Base,由于 MyAnnotation 是 @Inherited的(具有继承性),所以,Sub 也 "具有了注解 MyAnnotation"。

4.自定义注解

  • 使用 @interface,格式为:public @interface 注解名
  • 每个方法实际上声明了一个配置该注解的参数
  • 方法的名称就是参数的名称
  • 返回值类型就配置参数的类型(参数类型只能是Class,String,enum)
  • 可以通过默认default来声明参数的默认值
  • 只有一个方法设置方法名称为value,在使用时可以省略参数的名称
//自定义注解
@Target({ElementType.Type,ElementType.METHOD})//可以用在类,方法上
@Retention(RetentionPolicy.RUNTIME)//运行时作用
public @interface MyAnnotation{
    String name() default ""; //设置默认值为空,可以另外显示赋值
}

反射

1.动态语言与静态语言

  • 动态语言:可以在运行时根据某些条件改变自身结构
    • 主要动态语言:C#,JavaScript,PHP,Python,Object-C
  • 静态语言:运行时结构不可变的语言就是静态语言
    • 如:java,c,c++

2.反射

  • Reflection(反射)是java被视为动态语言的关键,反射机制允许程序在 在执行期间通过反射取得任何类的内部信息,并能够直接操作任意对象。

    正常方式:引入需要的"包类"名称---->通过new实例化---->取得实例化对象

    反射方式:实例化对象---->getClass()方法---->得到完整的"包类"

  • 在加载完类之后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整类的结构信息。

    Class c = Class.forName("  ")
    

3. Class类

  • 对象照镜子后的得到的信息:某个类的属性,方法,构造器,实现的接口,对每个类而言,JRE都为其保留了一个不变的Class类型的对象。一个Class对象包含了特定某个结构的有关信息。

    • Class本身也是一个类

    • Class只能由系统建立对象

    • 一个加载的类在JVM中只会有一个Class实例

    • 一个Class对象对应于一个加载到JVM的一个Class文件

    • 每个实例都可溯源其由哪一个Class实例所创建

    • 通过Class可以完整的得到一个类中所被加载的结构

    • Class类是Reflection的根源,唯有先获取对应的Class对象才能动态的加载

    • 只要元素类型和维度一样就是同一个Class

      //举例
      public class Demo2 {
          public static void main(String[] args) {
              //Cylinder是一个圆柱体类
              Class<Cylinder> aClass = Cylinder.class;
              Cylinder[] cylinders = new Cylinder[10];//维度不同
              Class<? extends Cylinder[]> aClass1 = cylinders.getClass();
              
              System.out.println(aClass1.hashCode());
              System.out.println(aClass.hashCode());
          }
      }
      
      //结果:
      //所设置的圆柱体的体积是:3141.59
      //1556595366
      //985922955
      
      
  • Class类中常用的方法

    • static forName(String name)  //返回指定类名的Class对象
      
    • Object newInstance()  //调用缺省构造函数函数(不带参数的构造函数),返回Class对象的一个实例
      
    •  Field[] getDeclaredFields()  //返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 
      
  • 获取Class类的方法(Runtime阶段、Class类对象阶段、Source源代码阶段)

    • 若已知某个对象的实例,调用该实例的getClass()方法获取Class对象

      Class c = person.getClass();
      
    • a.若已知具体的类,通过class属性获取,该方法最为安全可靠,程序性能最高

      Class c = Preson.class;
      
    • 已知一个类的全名称,且该类在类的路径下,可以通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

      Class c = Class.forName("demo.Person");
      
    • 基本数据类型的包装类

      Inteage.TYPE
      
    Java内存

    java内存图解

    public class Student {
    	int score;
    	int age;
    	String name;
    
    	Computer computer;
    
    	public void study() {
    
    		System.out.println("studying...");
    	}
    }
    
  1. 栈空间(stack),连续的存储空间,遵循后进先出的原则,用于存放局部变量、基本变量类型及其值、引用对象的变量即引用的地址。
  2. 堆空间(heap),不连续的空间,用于存放new出的对象,或者说是类的实例,可以被所用的线程共享,不会存放别的对象的引用。
  3. 方法区(method),方法区在堆空间内,用于存放①Class类的代码信息;②静态变量和方法;③常量池(字符串常量等,具有共享机制)
  • 类的加载及ClassLoder的理解

    • 加载:将class文件的字节码内容加载到内存中,并将这些静态数据转换成为方法区的运行时数据结构,然后生成一个代表该类的java.lang.Class对象。(意味着不可自己创建class对象,必须由系统创建)

    • 链接:将java类的二进制代码合并到JVM的运行状态之中的过程。

      • 验证:验证类的信息是否符合JVM规范及安全要求
      • 准备:为类变量(static)分配内存空间并设置默认初始值的阶段,内存在方法区中被分配(static首先分配内存)
      • 解析:将常量池中的符号引用替换为直接引用(地址)
    • 初始化

      静态代码块---->构造方法---->代码

      • 什么时候会发生类初始化
        • 当虚拟机启动时,先初始化main方法所在的类
        • new一个类的对象(new 一个对象的数组不会发生初始化)
        • 调用类的静态成员(除了final常量)和静态方法
        • 使用java.lang.reflect报的方法对类进行反射调用
        • 初始化一个类但是其父类没有被初始化,会先初始化其父类
      • 是么时候不会被动引用
        • 当访问一个静态域时只有真正声明这个域的类才会被初始化。如:通过子类引用弗雷德静态变量,子类不会被初始化。
        • 通过数组定义类的引用,不会触发此类的初始化。(只是开辟了内存空间)
        • 引用常量不会触发(例如final修饰的)
    • 加载器的类型

      • 引导类加载器:C++编写,JVM自带的类加载器,负责java核心库,无法直接获取,rt.jar包下。

      • 扩展类加载器

      • 系统类加载器

        双亲委派机制:权限由上至下降低

动态创建对象执行

//获取指定类的class对象
Class<Cylinder> cylinderClass = Cylinder.class;
//获取指定的构造器方法
Constructor<Cylinder> constructor =cylinderClass.getConstructor(double.class);
//使用指定的构造器方法创建一个实例对象
Cylinder cylinder = constructor.newInstance(10); //此时已经可以以直接调用方法
//获取指定类的方法
Method volume = cylinderClass.getMethod("volume", double.class);//指定方法的名字,返回值的clss对象
System.out.println(volume.invoke(cylinder,10));//调用invoke()方法传递一个实例化对象去执行及需要执行的方法的参数列表,invoke激活。

Cylinder cylinder1 = constructor.newInstance(20);
//获取指定的字段
Field height = cylinderClass.getDeclaredField("height");
//关闭安全检测,暴力反射
height.setAccessible(true);
//设置属性
height.set(cylinder1,30);
System.out.println(cylinder1.getHeight());//输出结果为30

Object invoke(Object obj,Object... args)
    //Object对应原方法的返回值,如原方法没有返回值,此时返回null
    //若原方法为静态方法,此时形参Object obj可为null
    //若原方法的形参列表为空,则Object[] args为null
    

public class Cylinder {
    private double height;

    public double getHeight() {
        return height;
    }

    public Cylinder(double height) {
        this.height = height;
    }

    public double  volume(double radius){
        return height*(new AreaImpl().CalculateArea(radius));
    }
}

获取注解信息

  • ORM ---->对象关系映射

    • 类和表结构的对应
    • 属性和字段的对应
    • 对象和记录对应
  • 反射操作注解

    //首先获取要获得的注解所对应的元素
    Class c = Person.class; //举例获得的是一个类的Class对象,想要获得其注解
    <A extends Annotation> A getAnnotation(Class<A> annotationClass) //如果存在该元素的指定类型的注释,则返回这些注释,否则返回 
    //获得注解的对象获得其信息
    
posted @ 2020-10-21 14:10  雨下整夜~  阅读(82)  评论(0)    收藏  举报