注解与反射(Java,类加载机制,双亲委派机制)

我的相关文章:

JavaSE 学习记录-CSDN博客
多线程笔记-CSDN博客
单例模式(Java实现)-CSDN博客
JUC笔记-CSDN博客
注解与反射(Java,类加载机制,双亲委派机制)-CSDN博客

1.注解

1.注解入门

  • Annotation是jdk1.5开始引入的新技术。

  • Annotation的作用:

    • 不是程序本身,可以对程序作出解释;
    • 可以被其他程序(例如编译器)读取。
  • Annotation的格式

    • “@注解名”,也可以带参数,例如:@SuppressWarnings(value=“unchcked”)
  • Annotation在哪里使用?

    • 可以附加在package、class、method、field上,相当于给它们添加了额外的辅助信息,还可以通过反射机制编程实现对这些元数据的访问。
package github.Annotation;

/** * 什么是注解? * @author subeiLY * @create 2021-06-07 14:02 */
public class Test01 extends Object{
    // Override 重写的注解
    @Override
    public String toString(){
        return super.toString();
    }
}

2.内置注解

  • @ Override:定义在 java. lang Override中,此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。
  • @ Deprecated:定义在 Java. lang. Deprecated中,此注释可以用于修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择。
  • @ SuppressWarnings:定义在 Java. lang. SuppressWarnings中,用来抑制编译时的警告信息。
  • 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好了。
    • @SuppressWarnings ( “all”)
    • @SuppressWarnings (unchecked")
    • @ SuppressWarnings(value=f"unchecked", " deprecation ")
    • 等等……
package github.Annotation.Demo01;

import java.util.ArrayList;
import java.util.List;

/** * 什么是注解? * @author subeiLY * @create 2021-06-07 14:02 */
public class Test01 extends Object{
    // Override 重写的注解
    @Override
    public String toString(){
        return super.toString();
    }

    // @Deprecated 不推荐使用,但可以使用,或者存在更好的更新方式
    @Deprecated
    public static void test(){
        System.out.println("Deprecated");
    }

    // @SuppressWarnings 镇压警告
    @SuppressWarnings("all")
    public void test01(){
        List<String> list = new ArrayList<>();
    }

    public static void main(String[] args) {
        test();
    }
}

3.自定义注解

  • 元注解的作用就是负责注解其他注解,Java定叉了4个标准的meta- annotation类型,他们被用来提供对其他 annotation类型作说明。

  • 这些类型和它们所支持的类在 java. lang annotation包中可以找到。(@Target,@Retention,@Documented, @Inherited)

    • @ Target:用于描述注解的使用范围(即被描述的注解可以用在什么地方)。

      • @Target 注解用于指定被修饰的注解可以应用的目标元素类型。它包含一个 ElementType 类型的数组作为参数,指定了注解可以应用的元素类型,如类、方法、字段等。

      • 例如,如果一个注解被 @Target({ElementType.METHOD, ElementType.FIELD}) 修饰,则该注解可以应用在方法和字段上。

      • ElementType 是一个枚举类型,用于表示注解可以应用的目标元素类型。

        1. TYPE:类、接口或枚举类型
        2. FIELD:字段
        3. METHOD:方法
        4. PARAMETER:方法参数
        5. CONSTRUCTOR:构造方法
        6. LOCAL_VARIABLE:局部变量
        7. ANNOTATION_TYPE:注解类型
        8. PACKAGE:包
        9. TYPE_PARAMETER:类型参数(Java 8 新增),泛型中的类型参数,如MyClass<T>
        10. TYPE_USE:类型使用(Java 8 新增),在代码中使用类型的地方,如ist<@NonNull String> process

        源代码如下:ElementType (Java Platform SE 8 )

    • @ Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。

      • @Retention 注解用于指定被修饰的注解的保留策略,即注解在运行时的保留方式。它包含一个 RetentionPolicy 类型的参数,有三个可能的值:SOURCECLASSRUNTIME

      • SOURCE < CLASS < RUNTIME

      • SOURCE : 源代码级别保留策略。表示注解仅存在于源代码中,在编译后不包含在类文件中;通常用于开发工具的处理,比如代码检查或代码生成。

      • CLASS 类级别保留策略。这意味着注解被包含在编译后的类文件中,但不会被加载到 JVM 中,在运行时不可用。表示注解被包含在类文件中,但在运行时不可通过反射获取;用于一些框架和工具,比如一些静态检查工具或部分框架的配置。

      • RUNTIME 运行时级别保留策略。表示注解在运行时可通过反射获取。用于开发框架,如 Spring 框架中的依赖注入、JUnit 测试框架中的测试标记等。

      • 通常,我们希望自定义注解的 RetentionRUNTIME,以便在运行时能够使用反射获取注解信息。

      • Java 中注解的默认保留策略是 RetentionPolicy.CLASS。如果在注解上没有显式地指定 Retention 注解,并且没有在使用注解时指定保留策略,则默认情况下注解会被保留到编译后的类文件中(.class 文件),但在运行时不可用。

    • @Documented:说明该注解将被包含在 Javadoc中。

      注解的说明部分能够被包含在生成的文档中,使得代码的用户更容易理解注解的含义和用法

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

package github.Annotation.Demo01;

import java.lang.annotation.*;

/** * 测试元注解 * @author subeiLY * @create 2021-06-07 14:09 */
public class TestAnnotation {
    @MyAnnotion
    public void test(){

    }
}

// 定义一个注解
/*Target 注解可以用在什么地方ElementType.METHOD 方法上有效  ElementType.TYPE类上有效 */
@Target(value = ElementType.METHOD)
/*@Retention 在什么地方有效RUNTIME > CLASS > SOURCES */
@Retention(value = RetentionPolicy.RUNTIME)
// @Documented 表示是否将我们的注解生成在Javadoc中
@Documented
// @Inherited 子类可以继承父类的注解
@Inherited
@interface MyAnnotion{

}

自定义注解

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

  • 分析:

    • @ interface用来声明一个注解,格式:public@ interface注解名{定义内容}

    • 其中的每一个方法实际上是声明了一个配置参数;

    • 方法的名称就是参数的名称。

    • 返回值类型就是参数的类型

      1. 基本数据类型(如int、double、boolean等)

      2. 字符串类型(String)

      3. 枚举类型(enum)

      4. Class 类型

      5. 注解类型(可以是自定义注解或者预定义注解,如 @Override、@Deprecated 等)

      6. 以上类型的数组

    • 可以通过 default 来声明参数的默认值;

    • 如果只有一个参数成员,一般参数名为vaue;

    • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值

package com.fatfish.myannotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
    int id() default 1;

    String name() default "张三";

    Class<?> clazz();

    Deprecated deprecatedAnnotation() default @Deprecated;

    String[] values() default {};
}
package com.fatfish.myannotation;

import java.lang.reflect.Method;
import java.util.Arrays;

public class TestMyAnnotation {

    @MyAnnotation(id = 2, name = "李四", clazz = TestMyAnnotation.class, deprecatedAnnotation = @Deprecated, values = {"Hello", "World!"})
    public void myMethod() {}

    public static void main(String[] args) {
        Method[] declaredMethods = TestMyAnnotation.class.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            String name = declaredMethod.getName();
            if (declaredMethod.isAnnotationPresent(MyAnnotation.class)) {
                MyAnnotation annotation = declaredMethod.getAnnotation(MyAnnotation.class);
                System.out.println("Method: " + name + ", Annotation params: ");
                System.out.println("id: " + annotation.id());
                System.out.println("name: " + annotation.name());
                System.out.println("class: " + annotation.clazz());
                System.out.println("deprecatedAnnotation: " + annotation.deprecatedAnnotation());
                System.out.println("values: " + Arrays.toString(annotation.values()));
            }
        }
    }
}

2.反射机制

1.Java反射机制概念

  1. 静态 & 动态语言
  • 动态语言
    • 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
    • 运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。
    • 主要动态语言:Object-C、C#、 JavaScript、PHP、 Python等。
  • 静态语言
    • 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。
    • 编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型。
    • Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活。
  1. 反射机制概念
  • Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection AP取得仼何类的内部信息,并能直接操作任意对象的内部属性及方法。
    • Class c= Class.forName(“java. lang String”);
  • 加载完类之后,在堆內存的方法区中就产生了一个 Class类型的对象(一个类只有一个Cass对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

在这里插入图片描述

  1. 反射机制研究与应用
  • Java反射机制提供的功能
    • 在运行时判断任意一个对象所属的类;
    • 在运行时构造任意一个类的对象;
    • 在运行时判断任意一个类所具有的成员变量和方法;
    • 在运行时获取泛型信息;
    • 在运行时调用任意一个对象的成员变量和方法;
    • 在运行时处理注解;
    • 生成动态代理;
    • ……
  1. 反射机制优缺点
  • 优点:
    • 可以实现动态创建对象和编译,体现出很大的灵活性。
  • 缺点
    • 对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
  1. 实现
package github.Annotation.Demo01;

/** * 什么叫反射 * @author subeiLY * @create 2021-06-07 14:23 */
public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 通过反射获取class对象
        Class name = Class.forName("github.Annotation.Demo01.User");
        System.out.println(name);

        Class c1 = Class.forName("github.Annotation.Demo01.User");
        Class c2 = Class.forName("github.Annotation.Demo01.User");
        Class c3 = Class.forName("github.Annotation.Demo01.User");
        Class c4 = Class.forName("github.Annotation.Demo01.User");

        /*        一个类在内存中只有一个Class对象        
一个类被加载后,类的整个结构都会被封装在Class对象中        
public native int hashCode();返回该对象的hash码值        
注:哈希值是根据哈希算法算出来的一个值,这个值跟地址值有关,但不是实际地址值。
         */

        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4.hashCode());
    }
}

// 实体类:pojo entity
class User{
    private int id;
    private int age;
    private String name;

    public User() {
    }

    public User(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

在这里插入图片描述

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

  1. class类介绍
  • 在 Object类中定义了以下的方法,此方法将被所有子类继承
    • public final Class getclass()
  • 以上的方法返回值的类型是一个 Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

在这里插入图片描述

  • 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口对于每个类而言,JRE都为其保留一个不变的Cass类型的对象。一个Class对象包含了特定某个结构( class/interface/enum/annotation/ primitive type/void/[])的有关信息。

    • Class本身也是一个类;
    • Class对象只能由系统建立对象;
    • 一个加载的类在JVM中只会有一个Class实例;
    • 一个Class对象对应的是一个加载到JVM中的一个class文件;
    • 每个类的实例都会记得自己是由哪个Class实例所生成;
    • 通过class可以完整地得到一个类中的所有被加载的结构;
    • class类是 Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
  • class类的常用方法

方法名功能说明
static Class.forName (String name)返回指定类名name的class对象
Object newInstance ()调用缺省构造函数,返回 Class对象的一个实例
getName ()返回此Class对象所表示的实体(类,接口,数组类或void)的名称。
Class getSuperClass ()返回当前class对象的父类的class对象
Class[] getinterfaces ()获取当前 Class对象的接口
ClassLoader getclassLoader ()返回该类的类加载器
Constructor getConstructors ()返回一个包含某些 Constructor对象的数组
Method getMethod (String name, Class…T)返回一个 Method对象,此对象的形参类型为paramType
Field[] getDeclaredFields ()返回Field对象的一个数组
  1. 获取Class类的实例
  • 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。

    • Class<User> userClass = User.class;
      
  • 已知某个类的实例,调用该实例的 getclass () 方法获取Class对象。

    • User user = new User();
      Class<? extends User> aClass = user.getClass();
      
  • 已知一个类的全类名,且该类在类路径下,可通过class类的静态方法 forName(获取,可能抛出 ClassNotFound Exception。

    •         try {
                  Class<?> aClass1 = Class.forName("com.fatfish.User");
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
      
  • 内置基本数据类型可以直接用类名.Type。

    • Class<Integer> type = Integer.TYPE;
      
  • 还可以利用 Classloader。

    •         // 获取当前类的类加载器
              ClassLoader classLoader = MyReflection.class.getClassLoader();
      
              // 使用类加载器加载指定类
              try {
                  Class<?> loadClass = classLoader.loadClass("com.fatfish.User");
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
      
package github.Annotation.Demo01;

/** * 测试class类的创建方式有哪些 * @author subeiLY * @create 2021-06-07 14:36 */
public class TestCreateClass {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是:" + person);

        // 方式一:通过对象查询
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        // 方式二:forName获得
        Class c2 = Class.forName("github.Annotation.Demo01.Student");
        System.out.println(c2.hashCode());

        // 方式三:通过类名.class获得
        Class c3 = Student.class;
        System.out.println(c3.hashCode());

        // 方式四:基本类型的包装类都有一个Type
        Class c4 = Integer.TYPE;
        System.out.println(c4);

        // 获得父类类型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);

    }
}

class Person{
    String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Student extends Person{
    public Student() {
        this.name = "学生";
    }
}

class Teacher extends Person{
    public Teacher(){
        this.name = "老师";
    }
}

在这里插入图片描述

  1. 哪些类型可以有Class对象
  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解@interface
  • primitive type:基本数据类型
  • void
package github.Annotation.Demo02;

import java.lang.annotation.ElementType;

/** * 所有类型的Class * @author subeiLY * @create 2021-06-07 14:50 */
public class TestAllTypeClass {
    public static void main(String[] args) {
        Class c1 = Object.class;    // 类
        Class c2 = Comparable.class;    // 接口
        Class c3 = String[].class; // 一维数组
        Class c4 = int[][].class; // 二维数组
        Class c5 = Override.class; // 注解
        Class c6 = ElementType.class; // 枚举
        Class c7 = Integer.class; // 基本数据类型
        Class c8 = void.class; // void
        Class c9 = Class.class; // class

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        // 只要元素类型与维度一样,就是同一个Class
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());

    }
}

在这里插入图片描述

3.类的加载与ClassLoader

  1. Java内存分析

在这里插入图片描述

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

在这里插入图片描述

  • 加载:将 class文件字节码內容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的 java. lang . Class对象。
  • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
    • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题。
    • 准备:正式为类变量( static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
  • 初始化:
    • 执行类构造器< clinit>貟方法的过程。类构造器< clinit>O方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
    • 虛拟机会保证一个类的< clinit >()方法在多线程环境中被正确加锁和同步。
  • 深刻理解类加载

类加载/类初始化过程:

  • 1. 加载(Load)

    将类的 class 文件读入内存,并为之创建一个 java.lang.Class 对象;

    类的加载由类加载器完成,类加载器通常由JVM提供;

    四种不同的加载类的方式:从本地文件系统加载、从JAR包加载、通过网络加载以及动态编译加载;

    类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。Java核心类库、常用的第三方库、以及应用程序自身的核心类

  • 2. 连接(Link)

    把类的二进制数据合并到JRE中

    • 2.1 验证(Verification)

      检验被加载的类是否有正确的内部结构,并和其他类协调一致。

      • 文件格式验证:包括 版本号验证 和 常量池验证

      • 元数据验证:对字节码描述的信息进行语义分析

      • 字节码验证:最重要的验证环节,分析数据流和控制,确定语义是合法的,符合逻辑的。

      • 符号引用验证:针对符号引用转换为直接引用的时候,保证引用一定会被访问到,不会出现类等无法访问的问题。与 2.3 解析(Resolution) 相关。

    • 2.2 准备(Preparation)

      为类的静态变量分配内存,并设置默认初始值

    • 2.3 解析(Resolution).

      将类的二进制数据中的符号引用替换成直接引用。

  • 3. 初始化(Initialize)

    为类的静态变量赋予正确的初始值

JVM的类加载机制:

全盘负责:

当一个类加载器负责加载某个 Class 时,该 Class 以及它所依赖的和引用的其他 Class 都由该加载器负责载入。

虽简单直接,但造成资源浪费、可能导致冲突。

双亲委派:

当一个类加载器需要加载某个 Class 时,它首先委托其父类加载器加载,如果父类加载器无法加载,则依次向上委托,直至最顶层的 Bootstrap ClassLoader。只有当父类加载器无法加载时,才会由子类加载器自行加载。

Bootstrap ClassLoader -> Ext ClassLoader -> App ClassLoader

过程总结

  • 1.类加载器收到类加载的请求

  • 2.将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器

  • 3.启动类加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常,一层一层向下,通知子加载器进行加载

  • 4.重复步骤3

在这里插入图片描述

从上到下第一个ExtClassLoader 改为 BootstrapClassLoader

优点

  1. 安全性: 双亲委派机制可以确保核心类库不会被篡改,提高了系统的安全性。由于核心类库由 Bootstrap ClassLoader 加载,且在委派链中位于最顶层,因此可以有效防止恶意代码替换核心类库。

  2. 保护性: 双亲委派机制可以避免类的重复加载,提高了系统的稳定性和内存利用率。如果一个类已经被加载,那么它会直接从缓存中获取,而不需要重新加载,减少了资源的浪费。

缓存机制:

缓存机制是指加载过的类会被缓存起来,以便下次使用时直接从缓存中获取,而不需要重新加载。

虽可以提高性能、减少资源消耗、避免冲突,但占用大量内存、卸载困难。

package github.Annotation.Demo02;

/** * @author subeiLY * @create 2021-06-07 14:57 */
public class Test05 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(A.m);
        /*        
1. 加载到内存,会产生一个类对应Class对象        
2. 链接,连接结束后m=0        
3. 初始化           
<clinit>(){
System.out.println("A类静态代码块初始化");                
m = 300;                
m = 100;         
}
         */
    }
}
class A{
    static{
        System.out.println("A类静态代码块初始化");
        m=300;
    }

    static int m=100;

    public A(){
        System.out.println("A类无参构造初始化");
    }
}

在这里插入图片描述

  1. 什么时候会发生类初始化
  • 类的主动引用(一定会发生类的初始化)
    • 当虚拟机启动,先初始化main方法所在的类;
    • new一个类的对象;
    • 调用类的静态成员(除了fina常量)和静态方法;
    • 使用 java. lang. reflect包的方法对类进行反射调用;
    • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化;
    • 通过数组定义类引用,不会触发此类的初始化;
    • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)。

在Java中,一个字段被声明为常量时,通常应该使用final关键字修饰,这样可以确保该字段的值不能被修改。常量字段通常还会使用static关键字,表示该常量属于类而不是实例。因此,一个字段被声明为常量时,通常会被finalstatic这两个关键字修饰。

package github.Annotation.Demo02;

/** * 测试类什么时候会初始化 * @author subeiLY * @create 2021-06-07 15:07 */
public class TestActiveReference {
    static{
        System.out.println("Main类被加载!");
    }

// 当虚拟机启动,先初始化main方法所在的类;
    public static void main(String[] args) throws ClassNotFoundException {

//        1. 主动调用
        Son son = new Son(); // 1)new一个类的对象; 2)当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。

        System.out.println(Son.c); // 调用类的静态成员(除了fina常量)和静态方法;

//        反射也会产生主动引用
        Class.forName("github.Annotation.Demo02.Son");//使用 java. lang. reflect包的方法对类进行反射调用;

//        不会产生类的引用的方法
        System.out.println(Son.b); //当通过子类引用父类的静态变量,不会导致子类初始化;

        Son[] array = new Son[5];// 通过数组定义类引用,不会触发此类的初始化;

        System.out.println(Son.a); // 引用常量不会触发此类的初始化
    }
}

class Father{
    static final int b=2;
    static {
        System.out.println("父类被加载");
    }
}

class Son extends Father{
    static {
        System.out.println("子类被加载");
        m=100;
    }

    static int m=300;
    static final int a=1;
    static int c = 3;
}

在这里插入图片描述

  1. 类加载器的作用
  • 类加载的作用:将 class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的 java. lang Class对象,作为方法区中类数据的访问入口。
  • 类缓存:标准的 JavaSe类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过丿M垃圾回收机制可以回收这些 Classi对象

在这里插入图片描述

  • 类加载器作用是用来把类(αlass)装载进内存的。丿VM规范定义了如下类型的类的加载器。

在这里插入图片描述

  • ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();//获取系统类的加载器
  • ClassLoader parent = systemClassLoader.getParent();//获取系统类加载器的父类加载器–>扩展类加载器 jre1.8.0_91\lib\ext
    • 在标准的Java环境中,引导类加载器(Bootstrap Class Loader)是用本地代码(通常是C/C++)实现的,并且它没有一个对应的Java对象。因此,当你调用getParent()方法来获取引导类加载器的父加载器时,它会返回null
  • ClassLoader parent1 = parent.getParent();//获取扩展类加载器父类加载器–>根加载器(c/c++) jre1.8.0_91\lib\rt.jar
public class MyReflection {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2

        // 获取系统类加载器的父类加载器-->扩展类加载器    jre1.8.0_91\lib\ext
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent); // sun.misc.Launcher$ExtClassLoader@677327b6

        // 获取扩展类加载器父类加载器-->根加载器(c/c++)  jre1.8.0_91\lib\rt.jar
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1); // null

        // 测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("com.fatfish.reflection.MyReflection").getClassLoader();
        System.out.println(classLoader); // sun.misc.Launcher$AppClassLoader@18b4aac2

        // 测试JDK内置的类是谁加载的
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader); // null

        // 如何获得系统类加载器可以加载的路径
        String property = System.getProperty("java.class.path");
        String[] split = property.split(";");
        for (String s : split) {
            System.out.println(s);
        }
        /*
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\charsets.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\deploy.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\access-bridge-64.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\cldrdata.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\dnsns.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\jaccess.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\jfxrt.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\localedata.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\nashorn.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\sunec.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\sunjce_provider.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\sunmscapi.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\sunpkcs11.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\ext\zipfs.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\javaws.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\jce.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\jfr.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\jfxswt.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\jsse.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\management-agent.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\plugin.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\resources.jar
D:\Softwares\IDEA\jdk_8\jdk1.8.0_351\jre\lib\rt.jar
D:\Softwares\IDEA\IdeaProjects\javase\out\production\one
D:\Softwares\IDEA\IdeaProjects\javase\one\lib\asm-7.2.jar
D:\Softwares\IDEA\IdeaProjects\javase\one\lib\cglib-3.1.jar
D:\Softwares\IDEA\install\lib\idea_rt.jar
         */

        // 双亲委派机制  检测安全性 你写的类和跟加载器一样的不会用你写的类
        // java.lang.String -->往上推
    }
}
  1. 为什么当前类由系统类加载器加载而非其他两个?

当你在代码中使用Class.forName("com.fatfish.reflection.MyReflection")来加载MyReflection类时,默认情况下,类加载器会使用调用者的类加载器,也就是系统类加载器。因此,MyReflection类会由系统类加载器加载,除非你显式指定使用其他类加载器。

  1. JDK内置的类为什么由引导类加载器加载?

JDK内置的类,如java.lang.Object等,是Java核心类库的一部分。这些类在Java虚拟机启动时就需要被加载,因此它们必须由一个在Java虚拟机启动时就已经存在的类加载器来加载,这个加载器就是引导类加载器。引导类加载器由Java虚拟机实现,通常是用本地代码(如C/C++)实现的,而不是由Java代码实现的。因此,JDK内置的类由引导类加载器加载,以确保它们在Java虚拟机启动时就可用。

4.获取运行类的完整结构

  • 通过反射获取运行时类的完整结构
  • Field、 Method、 Constructor.、 Superclass、 Interface、 Annotation
  • 实现的全部接口
  • 所继承的父类
  • 全部的构造器
  • 全部的方法
  • 全部的Feld
  • 注解
  • ……
作用code
获得包名 + 类名class.getName();
获得类名class.getSimpleName();
获得类的public属性class.getFields();
获得类的全部属性class.getDeclaredFields();
获得类的指定属性class.getDeclaredField(String name);
获得本类以及父类的全部 public 方法class.getMethods();
获得本类的所有方法class.getDeclaredMethods();
获得本类以及父类中指定的方法class.getMethod(name, parameter(s));
获得类的 public 构造器class.getConstructors();
获得类的所有构造器class.getDeclaredConstructors();
获得类的指定构造器class.getDeclaredConstructor(parameter(s));
package com.fatfish.reflection;

import com.fatfish.User;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class MyReflection {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.fatfish.User");
        User user = new User();
        c1 = user.getClass();

        // 获得类的名字
        System.out.println("=======================获得类的名字=======================");
        System.out.println("c1.getName(): " + c1.getName());// 获得包名 + 类名
        System.out.println("c1.getSimpleName(): " + c1.getSimpleName());// 获得类名

        // 获得类的属性
        System.out.println("=======================获得类的属性=======================");
        Field[] fields = c1.getFields();// 只能找到public属性
        System.out.println("c1.getFields(): ");
        Arrays.stream(fields).forEach(System.out::println);

        fields = c1.getDeclaredFields();// 找到全部的属性
        System.out.println("c1.getDeclaredFields(): ");
        Arrays.stream(fields).forEach(System.out::println);

        // 获得指定属性的值
        Field name = c1.getDeclaredField("username");
        System.out.println("c1.getDeclaredField(\"username\"): " + name);

        // 获得类的方法
        System.out.println("=======================获得类的方法=======================");
        Method[] methods = c1.getMethods(); // 获得本类及父类的全部public方法
        System.out.println("c1.getMethods(): ");
        Arrays.stream(methods).forEach(System.out::println);

        methods = c1.getDeclaredMethods(); // 获得本类的所有方法
        Arrays.stream(methods).forEach(System.out::println);

        // 获得指定的方法
        // 重载
        Method getUsername = c1.getMethod("getUsername");
        Method setUsername = c1.getMethod("setUsername", String.class);
        Method wait = c1.getMethod("wait", long.class);
        System.out.println("c1.getMethod(\"getUsername\", null): " + getUsername);
        System.out.println("c1.getMethod(\"setUsername\", String.class): " + setUsername);
        System.out.println("c1.getMethod(\"wait\", long.class): " + wait);


        // 获得类的构造器
        System.out.println("=======================获得类的构造器=======================");
        Constructor[] constructors = c1.getConstructors();
        System.out.println("c1.getConstructors(): ");
        Arrays.stream(constructors).forEach(System.out::println);

        constructors = c1.getDeclaredConstructors();
        System.out.println("c1.getDeclaredConstructors(): ");
        Arrays.stream(constructors).forEach(System.out::println);

        // 获得指定的构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(long.class, String.class, int.class);
        System.out.println("指定构造器 c1.getDeclaredConstructor(long.class, String.class, int.class): " + declaredConstructor);
    }
}
=======================获得类的名字=======================
c1.getName(): com.fatfish.User
c1.getSimpleName(): User
=======================获得类的属性=======================
c1.getFields(): 
public static final java.lang.String com.fatfish.User.publicFieldForTest
c1.getDeclaredFields(): 
private long com.fatfish.User.id
private java.lang.String com.fatfish.User.username
private int com.fatfish.User.age
public static final java.lang.String com.fatfish.User.publicFieldForTest
c1.getDeclaredField("username"): private java.lang.String com.fatfish.User.username
=======================获得类的方法=======================
c1.getMethods(): 
public boolean com.fatfish.User.equals(java.lang.Object)
public java.lang.String com.fatfish.User.toString()
public int com.fatfish.User.hashCode()
public static com.fatfish.User com.fatfish.User.getInstance()
public long com.fatfish.User.getId()
public int com.fatfish.User.getAge()
public void com.fatfish.User.setId(long)
public void com.fatfish.User.setUsername(java.lang.String)
public void com.fatfish.User.setAge(int)
public java.lang.String com.fatfish.User.getUsername()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
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()
public boolean com.fatfish.User.equals(java.lang.Object)
public java.lang.String com.fatfish.User.toString()
public int com.fatfish.User.hashCode()
public static com.fatfish.User com.fatfish.User.getInstance()
public long com.fatfish.User.getId()
public int com.fatfish.User.getAge()
public void com.fatfish.User.setId(long)
public void com.fatfish.User.setUsername(java.lang.String)
public void com.fatfish.User.setAge(int)
public java.lang.String com.fatfish.User.getUsername()
protected boolean com.fatfish.User.canEqual(java.lang.Object)
c1.getMethod("getUsername", null): public java.lang.String com.fatfish.User.getUsername()
c1.getMethod("setUsername", String.class): public void com.fatfish.User.setUsername(java.lang.String)
c1.getMethod("wait", long.class): public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
=======================获得类的构造器=======================
c1.getConstructors(): 
public com.fatfish.User()
public com.fatfish.User(long,java.lang.String,int)
c1.getDeclaredConstructors(): 
public com.fatfish.User()
public com.fatfish.User(long,java.lang.String,int)
private com.fatfish.User(int)
指定构造器 c1.getDeclaredConstructor(long.class, String.class, int.class): public com.fatfish.User(long,java.lang.String,int)

进程已结束,退出代码 0

User.java

package com.fatfish;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private long id;
    private String username;
    private int age;
    public static final String publicFieldForTest = "PUBLIC_FIELD";

    private User(int i) {}

    public static User getInstance() {
        return new User();
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}
  • 在实际的操作中,取得类的信息的操作代码,并不会经常开发。
  • 一定要熟悉 java. lang .reflect包的作用,反射机制。
  • 如何取得属性、方法、构造器的名称,修饰符等。

5.调用运行时类的指定结构

  1. 有Class对象,能做什么
  • 创建类的对象:调用 Class对象的 newInstance()方法

    • 1)类必须有一个无参数的构造器。

      否则 java.lang.InstantiationException, java.lang.NoSuchMethodException

    • 2)类的构造器的访问权限需要足够。

      否则 java.lang.IllegalAccessException

      java.lang.IllegalAccessException: Class com.fatfish.reflection.MyReflection can not access a member of class com.fatfish.User with modifiers “private”

  • 思考?难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

  • 步骤如下:

    • 1)通过class类的 getDeclaredConstructor( Class…, parameterTypes)取得本类的指定形参类型的构造器;
    • 2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
    • 3)通过 Constructor实例化对象
  1. 方法及使用
package com.fatfish.reflection;

import com.fatfish.User;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyReflection {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> userClass = Class.forName("com.fatfish.User");

        System.out.println("==========================构造对象==========================");
        // 方式一:newInstance(),本质是使用无参构造器
        User user = (User) userClass.newInstance();
        System.out.println(user.toString());

        // 方式二:反射
        Constructor<?> declaredConstructor = userClass.getDeclaredConstructor(long.class, String.class, int.class);
        // 关闭访问权限检测
        declaredConstructor.setAccessible(true);
        user = (User) declaredConstructor.newInstance(1, "张三", 35);
        System.out.println(user.toString());

        System.out.println("==========================方法==========================");
        // public 方法
        Method setUsernameMethod = userClass.getDeclaredMethod("setUsername", String.class);
        setUsernameMethod.invoke(user, "李四");
        System.out.println(user);

        // private 方法
        Method privateMethod = userClass.getDeclaredMethod("privateMethod");
        privateMethod.setAccessible(true);
        privateMethod.invoke(user);

        System.out.println("==========================属性==========================");
        Field username = userClass.getDeclaredField("username");
        // 不能直接操作私有属性,我们需要关闭程序的安全检测,属性或方法的setAccessible(true)
        username.setAccessible(true);
        username.set(user, "王五");
        System.out.println(user);
    }
}
==========================构造对象==========================
User(id=0, username=null, age=0)
User(id=1, username=张三, age=35)
==========================方法==========================
User(id=1, username=李四, age=35)
com.fatfish.User的私有方法被调用。
==========================属性==========================
User(id=1, username=王五, age=35)
  • 通过反射,调用类中的方法,通过 Method类完成。
    • ①通过Cas类的 getMethod( String name, Class… parameterTypes)方法取得一个 Method对象,并设置此方法操作时所需要的参数类型。
    • ②之后使用 Object invoke( Object obj,Object[] args)进行调用,并向方法中传递要设置的ob对象的参数信息。

在这里插入图片描述

调用指定的方法:

  • Object invoke(object obj, Object… args)
  • Object对应原方法的返回值,若原方法无返回值,此时返回null;
  • 若原方法若为静态方法,此时形参 Object obj可为null;
  • 若原方法形参列表为空,则 Object[] args为null;
  • 若原方法声明为 private,则需要在调用此 invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问 private的方法。

setAccessible()

  • Method和 Field、 Constructor对象都有 setAccessible()方法。
  • setAccessible作用是启动和禁用访问安全检查的开关。
  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检査。
    • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true;
    • 使得原本无法访问的私有成员也可以访问;
  • 参数值为false则指示反射的对象应该实施Java语言访问检查。
  1. 性能检测
public class MyReflection {
    // 普通方式调用
    public static void test01() {
        User user = new User();
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1_000_000_000; i++) {
            String username = user.getUsername();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行10亿次:" + (endTime - startTime) + "ms");
    }

    // 反射方式调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getUsername");
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1_000_000_000; i++) {
            getName.invoke(user);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次:" + (endTime - startTime) + "ms");
    }

    // 反射方式调用,关闭检测
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getUsername");
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();

        for (int i = 0; i < 1_000_000_000; i++) {
            getName.invoke(user);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次,关闭检测:" + (endTime - startTime) + "ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }
}
普通方式执行10亿次:12ms
反射方式执行10亿次:867ms
反射方式执行10亿次,关闭检测:628ms

6.反射操作泛型

  • Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
  • 为了通过反射操作这些类型,Java新增了 ParameterizedType, GenericArray Type, Type Variable和 WildcardType几种类型来代表不能被归一到class类中的类型但是又和原始类型齐名的类型。
  • ParameterizedType:表示一种参数化类型,比如 Collection< String>
  • GenericArray Type:表示一种元素类型是参数化类型或者类型变量的数组类型;
  • Type Variable:是各种类型变量的公共父接口;
  • WildcardType:代表一种通配符类型表达式。
  1. ParameterizedType(参数化类型): ParameterizedType 表示一个参数化的泛型类型,即一个泛型类或泛型接口的类型实例。它包含了泛型类或泛型接口的原始类型以及泛型类型的实际类型参数。例如,List<String> 是一个参数化类型,其中 List 是原始类型,而 String 是实际类型参数。

  2. GenericArrayType(泛型数组类型): GenericArrayType 表示一个元素类型是参数化类型或类型变量的数组类型。例如,T[]List<String>[] 都是泛型数组类型。它通常用于描述泛型数组的类型,因为 Java 中无法直接创建泛型数组。

  3. TypeVariable(类型变量): TypeVariable 表示一个类型变量,即泛型类型声明中的类型参数。例如,对于泛型类 class MyClass<T> {...} 中的 T,就是一个类型变量。类型变量在编译时会被擦除,但在反射中可以通过 TypeVariable 获取到泛型类型参数的信息。

  4. WildcardType(通配符类型): WildcardType 表示一种通配符类型,即带有通配符(?)的泛型类型。通配符类型通常出现在泛型方法、泛型边界和通配符类型作为方法参数等地方。它可以是未知类型、确定的类型或者有上下界限定的类型。

package com.fatfish.reflection;

import com.fatfish.User;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class MyReflection {
    public void test02(Map<String, User> map, List<User> list) {
        System.out.println("test02");
    }

    public Map<String, User> test03() {
        System.out.println("Test03");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = MyReflection.class.getMethod("test02", Map.class, List.class);

        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("#genericParameterType: " + genericParameterType);
            if (genericParameterType instanceof ParameterizedType) {
                Type[] typeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type typeArgument : typeArguments) {
                    System.out.println(typeArgument);
                }
            }
        }

        method = MyReflection.class.getMethod("test03", null);
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType) {
            System.out.println("#returnType: " + returnType);
            Type[] typeArguments = ((ParameterizedType) returnType).getActualTypeArguments();
            for (Type typeArgument : typeArguments) {
                System.out.println(typeArgument);
            }
        }

    }
}
#genericParameterType: java.util.Map<java.lang.String, com.fatfish.User>
class java.lang.String
class com.fatfish.User
#genericParameterType: java.util.List<com.fatfish.User>
class com.fatfish.User
#returnType: java.util.Map<java.lang.String, com.fatfish.User>
class java.lang.String
class com.fatfish.User

7.反射操作注解

  • getAnnotations
  • getAnnotation

在这里插入图片描述

通过反射获取注解(只能获取RetentionPolicy为RUNTIME的注解,不能获取 SOURCE 和 CLASS)

package github.Annotation.Demo03;

import java.lang.annotation.*;
import java.lang.reflect.Field;

/** * 练习反射操作注解 * @author subeiLY * @create 2021-06-07 16:21 */
public class TestORM {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("github.Annotation.Demo03.Student2");
        //通过反射获取注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //通过反射获取注解(只能获取RetentionPolicy为RUNTIME的注解,不能获取 SOURCE 和 CLASS)
        TableDoris tableDoris = (TableDoris) c1.getAnnotation(TableDoris.class);
        String value = tableDoris.value();
        System.out.println(value);

        //获得类指定的注解
        Field name = c1.getDeclaredField("name");
        FiledDoris annotation = name.getAnnotation(FiledDoris.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@TableDoris("db_student")
class Student2 {
    @FiledDoris(columnName = "db_id", type = "int", length = 10)
    private int id;
    @FiledDoris(columnName = "db_age", type = "int", length = 3)
    private int age;
    @FiledDoris(columnName = "db_name", type = "varchar", length = 200)
    private String name;

    public Student2() {
    }

    public Student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

// 类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableDoris {
    String value();
}

// 属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledDoris {
    String columnName();
    String type();

    int length();
}

参考

狂神说笔记——注解和反射06 - subeiLY - 博客园

jvm之java类加载机制和类加载器(ClassLoader)的详解_classloader 是否包含-CSDN博客

狂神说笔记——JVM入门07 - subeiLY - 博客园

遇见狂神说的个人空间-遇见狂神说个人主页-哔哩哔哩视频

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值