Java面向对象高级【注解和反射】

news2024/9/26 22:49:06

目录

注解

什么是注解?

自定义注解

元注解

反射

什么是反射

静态语言和动态语言

动态语言

静态语言

对比

Class类

Java内存分析

类加载过程

类加载器

获取运行时类的完整结构

通过Class对象实例化对象

1.调用Class对象的newInstance

2.Constructor类的newInstance

调用对象的方法和属性 

调用指定的方法

调用指定的属性

反射操纵注解


注解

什么是注解?

  1. Annotation是从JDK1.5开始引入的技术
  2. Annotation的作用:
    1. 不是程序本身,可以对程序做出解释。(和注释差不多)
    2. 可以被其他程序读取
  3. Annotation的格式:
    1. 注解是以“@参数名”在代码中存在的,还可以添加一些参数值,比如元注解:@SuppressWarnings(value="unchecked").
  4. Annotation在哪里使用?
    1. 可以附加在package、class、method、field等上面,相当于给他们添加了额外的辅助信息,我们可以同反射机制来实现对这些元数据的访问

自定义注解

  • 使用 @interface 自定义注解时,自动继承了java.lang.annotation.Annotation 接口。
  • 分析
    • @interface 用来声明一个注解,格式:public @interface 注解名 { 方法名()... }
    • 其中每一个方法实际上是一个配置参数
    • 方法名称就是参数名称
    • 返回值类型就是参数类型
    • 可以通过default来声明参数的默认值
    • 如果只有一个参数成员,一般参数名用value
    • 注解必须要有值,我们定义注解时,经常使用空字符串、0作为默认值

元注解

  • 元注解的作用就是负责注解其他注解,Java定义了4个标准的 meta-annotation类型,它们被用来提供对其他annotation类型作说明
  • 这些类型和它们所支持的类在 java.lang.annotation中可以找到(@Target、#Documented、@Inherrited、@Retention)
    • @Target:用于描述注解的使用范围(即注解可以标注在什么地方,类上面或者方法、属性等)
    • @Retention:表示在什么级别保存该注释信息,用于描述注解的声明周期
      • SOURCE:源码阶段-就是写代码的时候] 
      • CLASS :表示注解将在编译时被保留,并且会被包含在类文件中,但在运行时不可用。但这意味着,当程序运行时,无法访问该注解。
      • RUNTINUE :程序运行时,可以访问该注解(通过反射)。
    • @Documented:说明该注解将被包含在javadoc中。(javadoc是Java语言中自带的一种工具,用于生成API文档。)
    • @Inherited:说明子类可以继承父类的注解

反射

什么是反射

  • Java中的反射是指程序在运行时动态地获取类的信息以及操作类的成员变量、方法和构造方法的能力。
  • 通过反射,可以在运行时检查类的属性和方法,获取类的构造函数并实例化对象,调用类的成员变量和方法,甚至可以在运行时动态地生成新的类,这使得Java程序具有更大的灵活性和动态性。
  • 但是,反射机制也会导致一些性能上的问题,因为反射调用的速度通常比直接调用要慢得多。

Java中的反射主要API:

  1. Class类:用于表示Java类的信息,包括类的名称、父类、接口、构造函数、成员变量和方法等。
  2. Constructor类:用于表示Java类的构造函数信息。
  3. Method类:用于表示Java类的方法信息。
  4. Field类:代表类的成员变量

        反射机制的核心是在运行时动态地获取类的信息,并通过这些信息来调用类的成员变量和方法,这种能力使得Java程序可以在运行时动态地加载和执行代码,从而实现更加灵活和动态的功能。

静态语言和动态语言

动态语言

  • 动态语言是指在运行时进行类型检查的语言。在编写程序时,不需要明确指定变量的数据类型,变量的类型会在运行时根据赋值的内容自动推断。因此,动态语言往往比静态语言更灵活,更容易编写和修改。
  • 常见的动态语言包括Python、JavaScript和Ruby等。

静态语言

  • 静态语言是指在编译时进行类型检查的语言。在编写程序时,需要明确指定变量的数据类型,并在编译时检查所有变量和表达式的类型是否匹配。如果存在类型不匹配的情况,编译器会报错并终止编译。
  • Java、C++和C#等语言都是静态语言。

对比

  • 总之,静态语言在编写时需要明确指定类型,编译时会进行类型检查,更加严格和安全,但也更加繁琐;动态语言在编写时不需要指定类型,运行时会自动推断类型,更加灵活和易用,但也更加容易出错。

Class类

        在Java中,Class类是代表类的实体,它是Java反射机制的核心,用于获取类的信息、创建对象和调用方法等。Class类提供了以下常用的方法:

  1. getName():获取类的完整名称;
  2. newInstance():创建类的实例,等同于使用new关键字构造对象;
  3. getConstructors():获取类的所有公共构造器;
  4. getMethods():获取类的所有公共方法,包括继承的方法;
  5. getDeclaredFields():获取类的所有成员变量,不包括继承的变量;
  6. getDeclaredMethods():获取类的所有方法,不包括继承的方法;
  7. getSuperclass():获取类的父类;
  8. isAssignableFrom(Class c):判断当前类是否可以赋值给参数类c;
  9. isInstance(Object obj):判断当前对象是否为指定类的实例;
  10. isArray():判断当前类是否为数组类型。

        Class类的一个重要应用是Java反射机制。通过Class类获取类的信息,可以实现在运行时动态创建对象、调用方法和访问成员变量等功能,增加了程序的灵活性和动态性。同时,反射机制也带来了一定的性能开销,因此应该慎重使用。

无论一个.java文件中有多少个类,最终都只有一个Class对象。

Java内存分析

在Java应用程序中,内存主要分为三个区域:堆、栈和方法区。

  1. 堆(Heap)用于存储对象实例,堆是Java中最大的内存分配区域。堆内存的大小可以通过-Xmx和-Xms参数来指定,-Xmx表示最大堆内存,-Xms表示初始堆内存。
  2. 栈(Stack)用于存储方法调用的信息,每个线程都有自己的栈空间。栈内存的大小是固定的,并且在线程创建时分配。栈中存储着局部变量、方法参数、方法返回值和方法调用的状态等信息。
  3. 方法区(Method Area)用于存储类的信息、静态变量、常量等数据。方法区属于堆的一部分,但是它的作用和用途与堆不同。

类加载过程

Java的类加载过程分为三个步骤:加载、链接和初始化。其中,加载和链接是在程序运行时进行的,而初始化在类被首次使用时进行。

  • 加载:将类的字节码文件加载到内存中,并在内存中创建一个Class对象,用于表示该类的信息。ClassLoader负责查找和加载类的字节码文件,将字节码文件读入内存,并创建Class对象,并将其保存在方法区中。
  • 链接:在链接阶段,虚拟机会对类进行验证、准备和解析。
  • 验证:验证字节码文件的正确性,包括验证文件格式、元数据、字节码和符号引用等是否符合规范。
  • 准备:为类的静态变量分配内存,并设置默认初始值,例如int类型的默认值为0,对象类型的默认值为null。这些内存都在方法区中进行分配。
  • 解析:将符号引用解析为直接引用,例如将方法调用的符号引用解析为实际的方法地址。
  • 初始化:在类被首次使用时进行,虚拟机会执行类的初始化操作,包括执行类的静态代码块和静态变量的初始化。
  • 执行静态代码块:静态代码块是在类被初始化时执行的,它可以用来进行一些静态资源的初始化工作。
  • 静态变量初始化:静态变量的初始化也是在类被初始化时进行的,它可以通过赋初值或静态代码块来进行初始化。

        类的加载过程是Java虚拟机实现动态性的重要基础,也是Java的重要特性之一。通过自定义ClassLoader,可以实现类的动态加载和替换,这为Java应用程序带来了更大的灵活性和动态性。

类加载器

获取运行时类的完整结构

        通过Java反射可以获取类的运行时完整结构,包括类的构造器方法字段注解泛型等信息,具体步骤如下:

  • 获取Class对象:使用 lass.forName() 方法或者类名.class 获取需要反射的类的Class对象。
  • 获取构造器:使用 getConstructors() 方法获取类的所有公共构造器,使用getDeclaredConstructors() 方法获取类的所有构造器(包括私有构造器)
  • 获取方法:使用 getMethods() 方法获取类的所有公共方法,使用 getDeclaredMethods() 方法获取类的所有方法(包括私有方法)
  • 获取字段:使用 getFields() 方法获取类的所有公共字段,使用 getDeclaredFields() 方法获取类的所有字段(包括私有字段)
  • 获取注解:使用 getAnnotations() 方法获取类的所有注解,使用 getDeclaredAnnotations()方法获取类的所有注解(包括私有注解)。
  • 获取泛型:使用 getGenericSuperclass() 方法获取类的带有泛型的父类,使用getGenericInterfaces() 方法获取实现的所有接口(包括带有泛型的接口)。

        通过反射获取类的运行时完整结构可以实现动态调用、动态代理和动态生成代码等功能,但是反射的性能较低,应该尽量避免在性能要求高的场景中使用。

通过Class对象实例化对象

1.调用Class对象的newInstance

        使用newInstance()方法创建类的实例,该方法会调用类的默认构造器来创建对象。

        需要注意的是,newInstance()方法只能调用类的默认构造器来创建对象,如果类没有默认构造器或者默认构造器不可访问,则会抛出InstantiationException异常。如果需要调用其他构造器来创建对象,则需要使用Constructor类的newInstance()方法。

2.Constructor类的newInstance

Class<?> c1 = Class.forName("com.reflection.test.User");
//用含参的构造器创建对象
        Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user1 = (User)constructor.newInstance("张三", 1, 20);

调用对象的方法和属性 

调用指定的方法

通过反射,调用类中的方法,通过Method类完成。

通过Class对象的 getMethod() 或者 getDeclaredMethod() 方法来获得一个Method对象,并设置此方法操作时需要的参数类型。

然后使用 invoke 进行调用

Class<?> c1 = Class.forName("com.refelection.test.User");//获得Class类对象
User user = (User) c1.newInstance();//初始化对象

Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user,"张三");    //调用user的setName方法将user对象的name属性设置为"张三"

 如果方法为private,需要关闭安全检测setAccessible(true)

setName.setAccessible(true);

调用指定的属性

 //获得属性
Field name = c1.getDeclaredField("name");
name.set("name","张三");//设置属性

同样如果属性为private,需要关闭安全检测setAccessible(true)

name.setAccessible(true);//关闭权限检测

反射操纵注解

在Java中,通过反射可以操作注解,具体步骤如下:

  • 获取注解信息使用Class、Method、Field等类的getAnnotation()方法获取注解信息,例如使用getAnnotation()方法获取类、方法或属性上的注解信息,使用getAnnotations()方法获取类、方法或属性上的所有注解信息。
  • 解析注解信息:使用Annotation类的相关方法解析注解信息,例如使用annotationType()方法获取注解的类型,使用value()方法获取注解的属性值等
import java.lang.annotation.*;
import java.lang.reflect.Field;

/**
 * 反射操作注解
 */
public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //获得Class对象
        Class<?> c1 = Class.forName("reflection.Student2");

        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);//@reflection.TableInfo(value=db_student)
        }

        //获得注解的 value 的值
        TableInfo tableInfo = c1.getAnnotation(TableInfo.class);
        String value = tableInfo.value();
        System.out.println(value);//db_student

        //获得类指定的注解
        Field f = c1.getDeclaredField("name");
        FieldInfo fieldInfo = f.getAnnotation(FieldInfo.class);
        System.out.println(fieldInfo.columnName());//db_name
        System.out.println(fieldInfo.type());//varchar
        System.out.println(fieldInfo.length());//10
    }
}

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

    public Student2(){

    }
    public Student2(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 "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//类名的注解
@Target(ElementType.TYPE)//作用范围 类
@Retention(RetentionPolicy.RUNTIME)//作用的生命周期 运行时
@interface TableInfo{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface  FieldInfo{
    String columnName();
    String type();
    int length();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430277.html

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

相关文章

保姆级教程!如何在 Anolis 8 上构建基于 Nydus 和 Dragonfly 的镜像加速解决方案?

文/云原生 SIG 01 背景 镜像是容器技术的基础之一&#xff0c;在云原生场景下&#xff0c;业务的正常运作离不开对镜像的制作、分发和运行。当前的镜像在使用的过程中&#xff0c;需要将镜像从仓库中全量拉取到本地&#xff0c;再由容器 engine 进行解压&#xff0c;堆叠挂载…

【JUC进阶】详解synchronized锁升级

文章目录 1. synchronized概述2. synchronized 的实现原理2.1 Java对象组成2.2 Monitor2.3 从字节码角度看synchronized 3. 锁升级3.1 偏向锁3.2 轻量级锁 1. synchronized概述 synchronized是一个悲观锁&#xff0c;可以实现线程同步&#xff0c;在多线程的环境下&#xff0c…

信盈达CorexM4核心板STM32F407VGT6电路原理图\电源原理图\USB的工作原理\miniUSB的分类

一、电源部分电路 Micro_USB插座中的5V与GND就是直接提供VCC与GND&#xff0c;其中的D与D-是接在CH340芯片上的实现串口通信的数据口。 电源VCC通过一个自恢复保险丝接在一个自锁开关上,这样就分析完一个miniUSB的提供电源的原理图了。 在原理图的右上角有一个AMS1117-3.3的器件…

RabbitMQ 高级篇 | 黑马

一、消息可靠投递 生产端的 在使用 RabbitMQ的时候&#xff0c;作为消息发送方希望杜绝任何消息丢失或者投递失败场景。 RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。 confirm 确认模式return 退回模式 rabbitmq整个消息投递的路径为: producer--->ra…

HCIP-6.6BGP配置中IGP路由表和BGP路由表、IP路由表关系

BGP配置中IGP路由表和BGP路由表、IP路由表关系1、基础配置2、配置建立IBGP对等体3、配置EBGP对等体4、使用network命令宣告路由5、路由的学习5.1、R2的路由表5.2、R1的BGP路由表5.3、R3的BGP路由表5.4、R5的BGP路由表6、AS200与AS100通信6.1、AS200出接口注入AS100IP路由表6.2、…

Go 汇编详解

动手点关注干货不迷路前言我们知道 Go 语言的三位领导者中有两位来自 Plan 9 项目&#xff0c;这直接导致了 Go 语言的汇编采用了比较有个性的 Plan 9 风格。不过&#xff0c;我们不能因咽废食而放弃无所不能的汇编。1、 Go 汇编基础知识1.1、通用寄存器不同体系结构的 CPU&…

【Docker】使用Docker Compose部署项目

目录 前言 使用 前言 如果你部署项目需要很多个容器, 并且还是在手动一个一个启动的话来试试Docker Compose, 只需要写好Docker Compose文件运行命令就能帮你一次性全启动, 对微服务很友好啊~ 使用 运行以下命令查看是否有版本号, 确保你已经安装了Docker Compose docker…

使用sealos工具部署k8s

为什么使用sealos工具&#xff1a;简单、快、完全兼容 k8s、给100年认证 sealos使用最新版本&#xff1a; 官网&#xff1a;https://www.sealyun.com/ 码&#xff1a;https://github.com/labring/sealos 官方介绍什么是sealos Sealos 是以 kubernetes 为内核的云操作系统发行版…

精彩回顾|4.8 Beijing Rust Meetup

2023年4月8日&#xff0c;达坦科技联合南京大学、CloudWeGo、华为等技术专家成功举办了题为Rust X的meetup。开发者们不仅线下积极报名参与&#xff0c;更在线上直播中踊跃参与互动&#xff0c;一起探讨Rust作为一种强调性能、安全和并发性的编程语言的各种应用和实践。演讲者与…

从EXCEL BOM 描述中提取部分信息---正则表达式使用

从EXCEL BOM描述中提取关键的信息&#xff0c;用于建库填写内容&#xff0c;或者检查BOM等都会用到&#xff0c;如下大概通过两种方式实现信息的提取 1.手动Excel中提取 2.将如上1的方式用python实现&#xff0c;可以实现批量操作&#xff0c;减少操作带来的错误&#xff0c;…

[网络安全]第三次作业

目录 1. 什么是IDS&#xff1f; 2. IDS和防火墙有什么不同&#xff1f; 3. IDS工作原理&#xff1f; 4. IDS的主要检测方法有哪些详细说明&#xff1f; 5. IDS的部署方式有哪些&#xff1f; 6. IDS的签名是什么意思&#xff1f;签名过滤器有什么作用&#xff1f;例外签名…

温故c语言——深度剖析数据在内存中的存储

目录 数据类型详细介绍整形在内存中的存储&#xff1a;原码、反码、补码大小端字节序介绍及判断浮点型在内存中的存储解析 1. 数据类型介绍 基本内置数据类型有&#xff1a; //在内存中占用空间的大小 char //字符数据类型 占用1个字节 short //短整型 占用2个…

linux及openEuler破解root密码

第一步&#xff1a;开机的时候按键盘的字母 E 键&#xff0c; 进入引导模式 第二步&#xff1a;进入引导模式 &#xff1a;找到linux这一行&#xff0c;按键盘上的end 键&#xff0c;跳转到行尾&#xff0c;输入&#xff1a; init/bin/sh 修改完后&#xff0c;按键盘上的 ctr…

电脑开机出现英文字母开不了机U盘重装系统教学

电脑开机出现英文字母开不了机U盘重装系统教学。有用户电脑开机之后出现了错误代码字母&#xff0c;无法正常的开机了。遇到这个问题要怎么去进行系统的重新安装呢&#xff1f;一起来看看以下的具体解决方法教学吧。 准备工作&#xff1a; 1、U盘一个&#xff08;尽量使用8G以上…

基于LS1028 TSN时间敏感网络交换机方案(一)TSN介绍

2.1 时间敏感网络介绍 时间敏感网络小组的前身是 AVB &#xff0c;即以太网音视频桥接技术&#xff08; Ethernet Audio/Video Bridging, 简称 Ethernet AVB &#xff09; [10] 。它在传统以太网络的基础上&#xff0c;通过 精确时钟同步、预留带宽、流量整形&#xff0…

Spring和IDEA都不推荐用的@Autowired注解,为什么还有那么多人用?

Autowired的默认装配 我们都知道在spring中Autowired注解&#xff0c;是用来自动装配对象的。通常&#xff0c;我们在项目中是这样用的&#xff1a; package com.sue.cache.service;import org.springframework.stereotype.Service;Service public class TestService1 {publ…

今天面了个京东拿 38K 出来的,让我见识到了基础的天花板

今年的春招已经开始了&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的软件测试面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0…

ChatGPT接入Siri(保姆级教程)

ChatGPT接入Siri&#xff08;保姆级教程&#xff09;第一步&#xff1a;获取OpenAPI的Key第二步&#xff1a;制作快捷指令今天&#xff0c;我将为大家分享如何将GPT应用集成到苹果手机的Siri中&#xff08;当然手机是需要魔法&#xff08;TZ&#xff09;的&#xff09; 第一步…

协议篇之UART协议

协议篇之UART协议一、写在前面二、UART协议简介三、UART协议数据帧结构3.1 UART发送过程3.2 UART接收过程四、UART传输速率五、写在最后一、写在前面 由于设计需要&#xff0c;需要入门学习一下UART协议。本文主要学习UART协议的数据帧结构。 二、UART协议简介 通用异步收发传…

AutoGPT自主AI正在路上#趋势:自主人工智能、人机交互、终身学习

hi&#xff0c;大家好&#xff0c;我是shadow。今天分享一些我看到的人工智能正在发生的趋势&#xff0c;以及创新的机会。一些动态经过几个月的筹备&#xff0c;慢慢地我会开启2023的Mixlab故事&#xff0c;可查看公众号菜单栏里的2023专栏。本周六和下周六我将在上海的活动分…