注解

Annotation

  • 作用:
    • 不是程序本身,可以对程序做出解释
    • 可以被其他程序(比如:编译器等)读取
  • 格式:
    • 注解是以“@注释名”在代码中国存在的,可以添加一些参数值
  • 可以附加在 package、class、method、field 等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问

内置注解

  • @Override:定义在java.lang.Override 中,只适用于修饰方法,表示重写方法
  • @Deprecated: 定义在 java.lang.Deprecated 中 ,可以用于修饰方法、属性、类,表示不鼓励程序员使用这样的元素
  • @SuppressWarnings: 定义在java.labg.SuppressWarnings 中,用来抑制编译时的警告信息,但该注释需要添加参数

元注解

  • 作用:负责注解其他注解的注解

  • @Target:用于描述注解的使用范围

    • ElementType可取值:TYPE、FIELD、METHOD、PARAMETER等
  • @Retention:表示需要在什么级别保存该注释信息,表示注解的生命周期

    • RetentionPolicy可取值:(runtime>class>sources)
      • SOURCE:在原文件中有效,被编译器丢弃。
      • CLASS:在class文件有效,可能会被虚拟机忽略。
      • RUNTIME: 在运行时有效。
  • @Documented:说明该注解将被包含在 javadoc 中

  • @Inherited:说明子类可以继承父类中的该注解

自定义注解

使用 @interface 自定义注解,自动继承了 java.lang.annotation.Annotation 接口

反射

静态语言 &动态语言

动态语言:运行时可以改变其结构的语言。例:Object-C、C#、JavaScript、PHP、Python

静态语言:运行时结构不可变的语言。例:Java、C、C++

Java 不是动态语言,但 Java 可以称之为“准动态语言”,即 Java 有一定的动态性,我们可以利用反射机制获得类似动态语言的特性

Java Reflection

  • Reflection(反射)是 Java 被视为动态语言的关键,反射机制允许程序在执行期间借助 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

得到 Class 类的方式

Class 类

在Object 类中定义了以下方法,此方法将被所有子类继承

这个方法的返回值类型是一个 Class 类,Class 类是 Java 反射的源头


Class 类的常用方法:

获取 Class 类的方式

  • 已知具体的类,通过类的 class 属性获取,
  • 已知某个类的实例,调用该实例的 getClass() 方法获取 Class 对象
  • 已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException

Class 对象的类型

Java 内存分析

类加载过程:

参考:https://javaguide.cn/java/jvm/class-loading-process.html

  • **加载:**将类的 class 文件字节码加载到内存,并将静态数据转化为方法区的运行时数据结构,然后生成一个 java.lang.Class 对象
  • **链接:**将java类二进制数据合并到 JRE中
    • **验证:**确保加载的类信息符合JVM规范,没有安全方面的问题
    • **准备:**正式为类变量 (static) 分配内存并设置类变量默认初始值的阶段(所以说static在初始化之前就已经有了一个值),这些内存都将在方法区中进行分配
    • **解析:**虚拟机将常量池内的符号引用替换为直接引用的过程
  • **初始化:**执行初始化方法 <clinit> ()方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。<clinit> ()方法会将该类的静态变量合并。

类加载器:

双亲委派机制

参考:https://javaguide.cn/java/jvm/classloader.html#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B

https://www.cnblogs.com/luckforefforts/p/13642685.html

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。

获取运行时类的完整结构:

获取类的名称:

  • clazz.getName(); //类的全限定名
  • clazz.getSimpleName(); //简单类名
  • clazz.getPackage(); // 包信息
  • clazz.getModifiers(); // 修饰符

获得类的属性:

  • getFields() :获得 public 属性
  • getDeclaredFields() : 获得全部属性
  • getDeclaredField(String name) :获得指定属性的值

获得类的方法:

  • getMethods():获取本类及父类的所有public方法
  • getDeclaredMethods(): 获取本类的所有方法
  • getMethod(String name, Class… parameterTypes): 获取本类及父类的指定public方法
  • getDeclaredMethod(String name, Class… parameterTypes) :获取本类的所有方法

获得构造器

  • getConstructors():获得所有 public 构造器
  • getDeclaredConstructors():获得所有构造器
  • getConstructor():获取本类及父类的指定public构造器
  • getDeclaredConstructor(Class… parameterTypes):获取本类的所有构造器

**Class<?>... parameterTypes** = 方法参数的类型列表 ,它的值根据他前面的参数所需的类型确定。

通过反射动态创建对象

  1. 通过无参构造器创建对象 Class.newInstance()
  • 使用 Class 对象的 newInstance() 方法调用类(该类必须有一个无参构造器且访问权限足够)的无参构造器
  • Class.newInstance() Java 9 起已过时, 推荐改用 getDeclaredConstructor().newInstance()
  • newInstance() 返回值为 Object,需要强转类型为 User

  1. 通过有参构造器创建对象

  1. 通过反射调用普通方法

那么在第一步已经用 (User) 强转了 user ,user 变量的真实类型便是 User,在之后的调用应属于正常的 Java 方法调用,不需要反射也可以直接调用:

而通过反射调用方法也有好处:

  • 可使用 setAccessible(true); 调用 private 私有方法
  • 动态调整调用的方法
  1. 通过反射操作属性

性能对比:

反射操作泛型

泛型(Generics)就是 参数化类型 —— 把数据类型当作参数传递。
它让类、接口、方法能在 编写时不指定具体类型,而是在 使用时再确定类型。

  • 泛型的好处
  1. 类型安全:编译时就能发现类型错误。
  2. 避免强制类型转换:减少 ClassCastException。
  3. 提高代码复用性:写一次逻辑,支持多种数据类型。

反射操作注解

ORM (Object relationship Mapping) 对象关系映射

  • 类和表结构对应
  • 属性和字段对应
  • 对象和记录对应