当前位置: 首页 > news >正文

08 反射与注解

目录

1.Java类加载机制

类加载器

双亲委派模型

工作流程

优点

2.反射

基本概念

常见用法

1. 获取 Class 对象

2.获取构造方法

3.获取成员方法

4.获取成员变量

3.注解

注解的基本概念

定义和使用注解

定义注解

使用注解

解释

元注解详解

常见内置注解

总结


1.Java类加载机制

ava 类加载机制是 Java 运行时环境的核心组成部分之一,它负责将字节码文件加载到内存中,并对其进行解析和初始化。Java 类加载器采用了一种称为“双亲委派模型”的机制,这种机制确保了类的加载过程是安全和高效的。

Java 类加载器主要负责以下几个任务:

  1. 加载:将字节码文件从文件系统或网络中读取到内存中。

  2. 验证:确保加载的字节码文件符合 JVM 规范,没有安全问题。

  3. 准备:为类的静态变量分配内存,并设置默认初始值。

  4. 解析:将符号引用替换为直接引用。

  5. 初始化:执行类的初始化代码,即静态初始化块和静态变量的赋值操作。

类加载器

Java 类加载器主要有以下几种:

  1. Bootstrap ClassLoader(引导类加载器)

    • 负责加载核心的 Java 类库,如 java.lang.*java.util.* 等。

    • 通常由 C++ 编写,是 JVM 的一部分。

    • 不可以通过 Java 代码直接访问。

  2. Extension ClassLoader(扩展类加载器)

    • 负责加载 Java 的扩展库,如 javax.* 包下的类。

    • 通常加载位于 JRE/lib/ext 目录下的 JAR 文件。

  3. Application ClassLoader(应用类加载器)

    • 负责加载应用程序的类路径(classpath)上的类。

    • 通常加载用户自定义的类。

  4. 自定义类加载器

    • 用户可以根据需要编写自定义的类加载器,继承 java.lang.ClassLoader 类。

双亲委派模型

双亲委派模型是 Java 类加载器的一种层次关系,其主要目的是为了保证类的唯一性和安全性。在双亲委派模型中,当一个类加载器收到类加载请求时,它会先委托其父类加载器去加载该类,只有当父类加载器无法加载时,才会尝试自己加载。

工作流程
  1. 检查是否已经加载

    • 类加载器首先检查该类是否已经被加载过,如果已经加载,则直接返回已加载的类。

  2. 委托给父类加载器

    • 如果类未被加载,则委托给父类加载器去加载。

  3. 父类加载器递归委托

    • 父类加载器重复上述步骤,直到到达最顶层的 Bootstrap ClassLoader。

  4. 加载类

    • 如果所有父类加载器都无法加载该类,则由当前类加载器尝试加载。

优点
  1. 避免类的重复加载

    • 通过双亲委派模型,确保了类的唯一性,避免了不同类加载器加载同一个类导致的问题。

  2. 安全性

    • 核心类库由 Bootstrap ClassLoader 加载,确保了核心类库的安全性,防止被恶意代码篡改。

2.反射

Java 反射(Reflection)是一种强大的机制,允许程序在运行时动态地获取类的信息并操作对象。通过反射,你可以获取类的构造器、方法、字段等信息,甚至可以在运行时调用私有方法和修改私有字段。以下是关于 Java 反射的一些基本概念和常见用法。

基本概念
  1. Class 类

    • Class 是 Java 反射的核心类,代表类的类型信息。

    • 可以通过 Class.forName、对象的 .getClass 方法或直接使用类的 .class 字段来获取 Class 对象。

  2. Constructor 类

    • 表示类的构造器。

    • 可以通过 Class 对象的 getConstructorsgetDeclaredConstructorsgetConstructorgetDeclaredConstructor 方法获取。

  3. Method 类

    • 表示类的方法。

    • 可以通过 Class 对象的 getMethodsgetDeclaredMethodsgetMethodgetDeclaredMethod 方法获取。

  4. Field 类

    • 表示类的字段。

    • 可以通过 Class 对象的 getFieldsgetDeclaredFieldsgetFieldgetDeclaredField 方法获取。

常见用法
1. 获取 Class 对象
  • 通过类名获取对象

  • 通过对象getClass()方法获取

  • 通过.class属性获取

    // 通过类名获取
    Class<?> clazz1 = Class.forName("com.example.MyClass");
    ​
    // 通过对象获取
    MyClass obj = new MyClass();
    Class<?> clazz2 = obj.getClass();
    ​
    // 通过类的 .class 属性获取
    Class<?> clazz3 = MyClass.class;
2.获取构造方法
  • 获取所有构造方法

  • 获取指定参数类型构造方法

    // 获取所有public的构造器
    Constructor<?>[] constructors = MyClass.class.getConstructors();
    ​
    // 获取指定类型参数构造器
    Constructor<MyClass> constructor = MyClass.class.getConstructor(int.class, String.class);
3.获取成员方法
  • 获取public方法

  • 获取所有方法,包含private的

    // 获取指定方法
    Method method = MyClass.class.getMethod("myMethod", String.class);
    ​
    // 调用方法
    String result = (String) method.invoke(obj, "parameter");
    ​
    // 获取所有public方法
    Method[] methods = MyClass.class.getMethods();
    ​
    // 获取所有方法,包含private
    Method[] privateMethods = MyClass.class.getDeclaredMethods();
4.获取成员变量
  • 获取public变量

  • 获取所有变量,包括private的

    // 获取所有public的成员变量
    Field[] fields = MyClass.class.getFields();
    ​
    // 获取所有属性,包括private的成员变量
    Field[] privateFields = MyClass.class.getDeclaredFields();
    ​
    // 获取指定字段
    Field field = MyClass.class.getDeclaredField("myField");
    ​
    // 设置字段可访问(即使它是私有的)
    field.setAccessible(true);
    ​
    // 设置字段值
    field.set(obj, "new value");
    ​
    // 获取字段值
    String value = (String) field.get(obj);

定义一个Person类

public class Person {private String name;private Integer age;
​public Person() {}
​//私有构造private Person(String name){this.name = name;}
​public Person(String name, Integer age) {this.name = name;this.age = age;}
​public String getName() {return name;}
​public void setName(String name) {this.name = name;}
​public Integer getAge() {return age;}
​public void setAge(Integer age) {this.age = age;}
​@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}

简单练习:

public class ReflectTest {public static void main(String[] args) {try {System.out.println("=========反射获取对象:通过全限定类名=========");// 反射:通过类名获取类对象,类名必须是全限定类名:包名+类名Class<?> clazz = Class.forName("com.haha.test.Person");System.out.println(clazz);
​System.out.println("=========反射获取对象:通过对象getClass()方法=========");// 反射:通过类名获取类对象Person person = new Person("xiaoming", 18, "male");Class<?> clazz2 = person.getClass();System.out.println(clazz2);
​System.out.println("=========反射获取对象:通过.class属性=========");// 反射:通过类名获取类对象Class<?> clazz3 = Person.class;System.out.println(clazz3);
​System.out.println("=========反射获取所有public的构造方法=========");Constructor<?>[] constructors = clazz.getConstructors();for (Constructor<?> constructor : constructors) {System.out.println(constructor);}
​System.out.println("=========反射获取指定参数类型的构造方法=========");Constructor<?> constructor = clazz.getConstructor(String.class, int.class, String.class);System.out.println(constructor);
​System.out.println("=========反射获取所有public的成员方法=========");Method[] methods = clazz.getMethods();for (Method method : methods) {System.out.println(method);}
​System.out.println("=========反射获取私有的成员方法=========");Method[] privateMethods = clazz.getDeclaredMethods();for (Method method : privateMethods) {System.out.println(method);}
​System.out.println("=========反射获取所有public的成员变量=========");Field[] fields = clazz.getFields();for (Field field : fields) {System.out.println(field);}
​System.out.println("=========反射获取所有属性,包括private的成员变量=========");Field[] privateFields = clazz.getDeclaredFields();for (Field field : privateFields) {System.out.println(field);// 设置字段可访问(即使它是私有的)field.setAccessible(true);
​// 获取字段名称和类型String fieldName = field.getName();Class<?> fieldType = field.getType();
​// 根据字段类型设置新值if (fieldType.equals(String.class)) {field.set(person, "New " + fieldName);} else {field.set(person, 35);}
​// 打印字段值System.out.println(field.get(person));}} catch (Exception e) {System.out.println(e.getMessage());}}
}

3.注解

注解(Annotations)是 Java 语言的一个重要特性,用于提供元数据信息,这些信息可以被编译器、运行时环境或其他工具使用。注解本身并不会直接影响程序的行为,但可以通过反射机制在运行时读取这些注解,并根据注解的信息执行相应的逻辑。

注解的基本概念
  1. 元注解

    • 元注解是用于注解其他注解的注解。常见的元注解包括:

      • @Retention:指定注解的保留策略。

      • @Target:指定注解可以应用的目标。

      • @Documented:指定注解是否会被包含在 JavaDoc 中。

      • @Inherited:指定注解是否可以被子类继承。

  2. 内置注解

    • @Override:表示方法重写父类或实现接口中的方法。

    • @Deprecated:表示方法或类已过时。

    • @SuppressWarnings:抑制编译器警告。

  3. 自定义注解

    • 开发者可以自定义注解,以满足特定的需求。

定义和使用注解
定义注解

定义一个注解需要使用 @interface 关键字。以下是一个简单的自定义注解示例:

java深色版本

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
​
// 指定注解的保留策略为运行时
@Retention(RetentionPolicy.RUNTIME)
// 指定注解可以应用于方法和字段
@Target({ ElementType.METHOD, ElementType.FIELD })
public @interface MyAnnotation {// 注解的属性String value() default "";int id() default -1;
}
使用注解

在类、方法或字段上使用自定义注解:

java深色版本

public class MyClass {
​@MyAnnotation(value = "Hello", id = 1)private String myField;
​@MyAnnotation(value = "World", id = 2)public void myMethod() {System.out.println("This is my method.");}
​public static void main(String[] args) {MyClass obj = new MyClass();obj.myMethod();
​// 通过反射读取注解信息readAnnotations(obj);}
​private static void readAnnotations(Object obj) {// 获取类的 Class 对象Class<?> clazz = obj.getClass();
​// 获取类的所有字段Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// 检查字段是否有 MyAnnotation 注解if (field.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);System.out.println("Field: " + field.getName());System.out.println("Value: " + annotation.value());System.out.println("ID: " + annotation.id());}}
​// 获取类的所有方法Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {// 检查方法是否有 MyAnnotation 注解if (method.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);System.out.println("Method: " + method.getName());System.out.println("Value: " + annotation.value());System.out.println("ID: " + annotation.id());}}}
}
解释
  1. 定义注解

    • 使用 @interface 关键字定义注解 MyAnnotation

    • 使用 @Retention(RetentionPolicy.RUNTIME) 指定注解的保留策略为运行时,这意味着注解信息会在运行时可用。

    • 使用 @Target({ ElementType.METHOD, ElementType.FIELD }) 指定注解可以应用于方法和字段。

    • 注解包含两个属性 valueid,并分别设置了默认值。

  2. 使用注解

    • MyClass 类中,使用 @MyAnnotation 注解标注字段 myField 和方法 myMethod

    • main 方法中,创建 MyClass 的实例并调用 myMethod

  3. 通过反射读取注解信息

    • 使用 Class 对象获取类的所有字段和方法。

    • 使用 isAnnotationPresent 方法检查字段或方法是否有 MyAnnotation 注解。

    • 使用 getAnnotation 方法获取注解实例,并读取注解的属性值。

元注解详解
  1. @Retention

    • RetentionPolicy.SOURCE:注解仅保留在源代码中,编译时会被忽略。

    • RetentionPolicy.CLASS:注解保留在字节码文件中,但运行时不可用。

    • RetentionPolicy.RUNTIME:注解保留在字节码文件中,运行时可通过反射读取。

  2. @Target

    • ElementType.TYPE:类、接口、枚举。

    • ElementType.FIELD:字段。

    • ElementType.METHOD:方法。

    • ElementType.PARAMETER:方法参数。

    • ElementType.CONSTRUCTOR:构造器。

    • ElementType.LOCAL_VARIABLE:局部变量。

    • ElementType.ANNOTATION_TYPE:注解类型。

    • ElementType.PACKAGE:包。

  3. @Documented

    • 指定注解是否会被包含在 JavaDoc 中。

  4. @Inherited

    • 指定注解是否可以被子类继承。

常见内置注解
  1. @Override

    • 表示方法重写父类或实现接口中的方法。

  2. @Deprecated

    • 表示方法或类已过时,不推荐使用。

  3. @SuppressWarnings

    • 抑制编译器警告。

总结

注解是 Java 中一个非常强大的特性,可以用于提供元数据信息,帮助编译器、运行时环境或其他工具更好地理解和处理代码。通过自定义注解和反射机制,可以实现灵活的代码管理和功能扩展。希望这些示例和解释对你有所帮助!


http://www.mrgr.cn/news/68552.html

相关文章:

  • (三)使用Vite创建Vue项目,了解Vue3生命周期
  • 十款外贸软件盘点,专注企业订单业务管理
  • 通过url参数控制组件显示
  • 深入解析:物联网技术及其应用
  • 如何评估Elasticsearch查询性能的具体指标?
  • ExceptionHandler的实践
  • 【Linux】解锁操作系统潜能,高效线程管理的实战技巧
  • vue3使用easy-player播放hls监控流
  • AIGC--如何在内容创作中合理使用AI生成工具?
  • 1 天通关 AWS AI 认证,AWS AI 从业者证书最强考试指南
  • LangChain 快速入门
  • 信息安全工程师(83)Windows操作系统安全分析与防护
  • 淘宝商品详情API大揭秘:用Python开启探险之旅
  • 自动驾驶中,2d图像目标检测(分割),融合激光雷达点云信息
  • ChatGLM2-6B微调记录【1】
  • 【计网不挂科】计算机网络期末考试——【选择题&填空题&判断题&简述题】题库(2)
  • ​解决‌win11无法打开msi安装程序包的方法‌
  • AI预测体彩排3采取888=3策略+和值012路+胆码+通杀1码测试11月8日升级新模型预测第128弹
  • 虚假新闻检测:CSV格式数据集的预处理与模型选择
  • 改变财务规划思维方式,迎接创新技术新时代
  • 数据分析的力量如何驱动商业决策和创新发展
  • 文件系统和日志管理 附实验:远程访问第一台虚拟机日志
  • 基于Springboot+Vue的网上拍卖系统 (含源码数据库)
  • 如何简化App Store提现?——作为游戏开发者的跨境收款体验分享
  • openGauss 一主一备 从5.0 LTS 版本升级至 6.0 LTS 版本实战
  • MySQL的SQL书写顺序和执行顺序