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.mzph.cn/pingmian/59829.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux第八课-进程间通信】管道、共享内存、消息队列、信号量、信号、可重入函数、volatile

目录 进程间通信为什么&#xff1f;是什么&#xff1f;怎么办&#xff1f;一般规律具体做法 匿名管道原理代码 命名管道原理代码 system V共享内存消息队列信号量信号量的接口 信号概念为什么&#xff1f;怎么办&#xff1f;准备信号的产生信号的保存概念三张表匹配的操作和系统…

Android 应用插件化及其进程关系梳理

插件应用的AndroidManifest.xml <manifest xmlns:android"http://schemas.android.com/apk/res/android"coreApp"true"package"com.demo.phone"android:sharedUserId"android.uid.phone"><uses-sdk android:minSdkVersion&q…

C# 集合与泛型

文章目录 前言1.什么是集合&#xff1f;2.非泛型集合&#xff08;了解即可&#xff09;2.1常见的非泛型集合 3.泛型的概念4.常用的泛型集合4.1 List < T > <T> <T>4.2 Dictionary<TKey, TValue>4.3 Queue < T > <T> <T>4.4 S t a c…

sql单表查询练习题

1. 查看course表结构的SQL命令是什么&#xff1f; A. SELECT * FROM exam.course; B. \d exam.course; C. \d exam.course; D. DESCRIBE exam.course; 答案&#xff1a;C 2. 使用哪个SQL命令可以查看exam.course表中的所有数据&#xff1f; A. SELECT * FROM e…

京东商品详情API接口获取(jd.item_get)和展示

获取京东商品详情 API 接口主要有以下步骤&#xff1a; 一、注册成为开发者&#xff1a; 注册账号获取key和secret&#xff0c;这是获取 API 访问权限的基础。在京东开放平台中创建一个应用&#xff0c;并填写相关信息&#xff0c;如应用程序名称、应用描述等。 二、申请 API…

数据分析-41-时间序列预测之机器学习方法XGBoost

文章目录 1 时间序列1.1 时间序列特点1.1.1 原始信号1.1.2 趋势1.1.3 季节性和周期性1.1.4 噪声1.2 时间序列预测方法1.2.1 统计方法1.2.2 机器学习方法1.2.3 深度学习方法2 XGBoost2.1 模拟数据2.2 生成滞后特征2.3 切分训练集和测试集2.4 封装专用格式2.5 模型训练和预测3 参…

【LeetCode】【算法】209. 课程表

LeetCode 209. 课程表 题目描述 你这个学期必须选修numCourses门课程&#xff0c;记为0到numCourses- 1 。 在选修某些课程之前需要一些先修课程。先修课程按数组prerequisites给出&#xff0c;其中 prerequisites[i] [a_i,b_i] &#xff0c;表示如果要学习课程a_i则必须先学…

基于大语言模型的规划

文章目录 整体框架方案生成反馈获取虽然上下文学习和思维链提示方法形式上较为简洁且较为通用,但是在面对诸如几何数学求解、游戏、代码编程以及日常生活任务等复杂任务时仍然表现不佳。为了解决这类复杂任务,可以使用基于大语言模型的规划(Planning)。该方法的核心思想在于…

【一些正经的思考】牵牛花在秋天播种可以开花吗

这是一篇正经的思考&#xff0c;因为是发生在工位上的事情&#xff0c;所以这也是上班记录~ 我入职新公司已经两个月了&#xff0c;和部门的新伙伴出去吃饭的频率高了1000%&#xff0c;不得不说&#xff0c;这边的食堂确实不是那么好吃&#xff0c;就和小伙伴经常去一个在江边…

零基础Java第十四期:继承与多态(二)

目录 一、继承 1.1. 继承的方式 1.2. final关键字 1.3. 继承与组合 1.4. protected关键字 二、多态 2.1. 多态的概念 2.2. 向上转型 2.3. 重写 2.4. 向下转型 2.5. 多态的优缺点 一、继承 1.1. 继承的方式 猫类可以继承动物类&#xff0c;中华田园猫类可以继承猫类…

Django 详细入门介绍

Django 详细入门介绍 1. 什么是 Django&#xff1f; Django 是一个开源的、用 Python 编写的 Web 框架。它遵循了“快速开发”和“不要重复自己”&#xff08;DRY&#xff09;的设计原则&#xff0c;旨在简化复杂的 Web 开发。Django 提供了多种强大的功能模块&#xff0c;如…

RabbitMQ 不公平分发介绍

RabbitMQ 是一个流行的开源消息代理软件&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;。在 RabbitMQ 中&#xff0c;消息分发策略对于系统的性能和负载均衡至关重要。默认情况下&#xff0c;RabbitMQ 使用公平分发&#xff08;Fair Dispatch&#xff09;策…

WebRTC REMB算法

WebRTC REMB&#xff08;Receiver Estimated Maximum Bitrate&#xff09;是一种带宽估计算法&#xff0c;用于在WebRTC中动态地调整视频发送端的码率&#xff0c;以适应网络带宽的变化。以下是对WebRTC REMB的详细解释&#xff1a; 一、定义与原理 定义&#xff1a;REMB是一…

RocketMQ 广播消息

所谓的广播消息就是发送的一条消息会被多个消费者收到。 ⼴播是向主题&#xff08; topic &#xff09;的所有订阅者发送消息。订阅同⼀个 topic 的多个消费者&#xff0c;能全量收到⽣产者发送的所有消息。 生产者发送了10个order&#xff0c;每个order里面有5个消息&#xff…

.Net IOC理解及代码实现

IOC理解 IoC(Inversion of Control)&#xff1a;即控制反转&#xff0c;这是一种设计思想&#xff0c;指将对象的控制权交给IOC容器&#xff0c;由容器来实现对象的创建、管理&#xff0c;程序员只需要从容器获取想要的对象就可以了。DI(Dependency Injection)&#xff0c;即依…

kafka面试十五题

1、kafka消息发送的流程 消息经过main线程里的拦截器&#xff08;可选&#xff09;、序列化器、分区器。分区器将数据发送到分区中&#xff0c;每个分区创建一个双端队列&#xff08;分区是在内存中完成的&#xff09;&#xff0c;内存总大小为32M&#xff0c;每个批次的大小为…

CSS Position 定位如何使用?

写在前面 在网页设计中&#xff0c;定位元素是非常重要的。CSS 提供了多种定位方式&#xff0c;其中最常用的是 position 属性。position 属性允许你精确地控制元素在页面中的位置和布局。 1. static static 是默认的定位方式&#xff0c;表示元素将按照正常的文档流进行布局…

react的创建与书写

一&#xff1a;创建项目 超全面详细一条龙教程&#xff01;从零搭建React项目全家桶&#xff08;上篇&#xff09; - 知乎 1.创建一个文件夹&#xff0c;shift鼠标右键选择在此处打开powershell 2.为了加速npm下载速度&#xff0c;先把npm设置为淘宝镜像地址。 npm config s…

量化交易系统开发-实时行情自动化交易-数据源选择

19年创业做过一年的量化交易但没有成功&#xff0c;作为交易系统的开发人员积累了一些经验&#xff0c;最近想重新研究交易系统&#xff0c;一边整理一边写出来一些思考供大家参考&#xff0c;也希望跟做量化的朋友有更多的交流和合作。 关于数据采集方面&#xff0c;首先聊聊…

【微信小游戏学习心得】

这里是引用 微信小游戏学习心得 简介了解微信小游戏理解2d游戏原理数据驱动视图总结 简介 本人通过学习了解微信小游戏&#xff0c;学习微信小游戏&#xff0c;加深了对前端框架&#xff0c;vue和react基于数据驱动视图的理解&#xff0c;及浏览器文档模型和javaScript之间的关…