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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 什么是注解?Annotation是从JDK1.5开始引入的技术Annotation的作用

注解

什么是注解?

Annotation是从JDK1.5开始引入的技术

Annotation的作用:

不是程序本身,可以对程序做出解释。(和注释差不多)

可以被其他程序读取

Annotation的格式:

注解是以“@参数名”在代码中存在的,还可以添加一些参数值,比如元注解:@SuppressWarnings(value="unchecked").

Annotation在哪里使用?

可以附加在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:说明子类可以继承父类的注解

反射

什么是反射


image.png

Java中的反射是指程序在运行时动态地获取类的信息以及操作类的成员变量、方法和构造方法的能力。

通过反射,可以在运行时检查类的属性和方法,获取类的构造函数并实例化对象,调用类的成员变量和方法,甚至可以在运行时动态地生成新的类,这使得Java程序具有更大的灵活性和动态性。

但是,反射机制也会导致一些性能上的问题,因为反射调用的速度通常比直接调用要慢得多。

Java中的反射主要API:


Class类:用于表示Java类的信息,包括类的名称、父类、接口、构造函数、成员变量和方法等。

Constructor类:用于表示Java类的构造函数信息。

Method类:用于表示Java类的方法信息。

Field类:代表类的成员变量

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


静态语言和动态语言

动态语言

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

常见的动态语言包括Python、JavaScript和Ruby等。

静态语言

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

Java、C++和C#等语言都是静态语言。

对比

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

Class类

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


getName():获取类的完整名称;

newInstance():创建类的实例,等同于使用new关键字构造对象;

getConstructors():获取类的所有公共构造器;

getMethods():获取类的所有公共方法,包括继承的方法;

getDeclaredFields():获取类的所有成员变量,不包括继承的变量;

getDeclaredMethods():获取类的所有方法,不包括继承的方法;

getSuperclass():获取类的父类;

isAssignableFrom(Class c):判断当前类是否可以赋值给参数类c;

isInstance(Object obj):判断当前对象是否为指定类的实例;

isArray():判断当前类是否为数组类型。

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


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


Java内存分析



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


堆(Heap):用于存储对象实例,堆是Java中最大的内存分配区域。堆内存的大小可以通过-Xmx和-Xms参数来指定,-Xmx表示最大堆内存,-Xms表示初始堆内存。

栈(Stack):用于存储方法调用的信息,每个线程都有自己的栈空间。栈内存的大小是固定的,并且在线程创建时分配。栈中存储着局部变量、方法参数、方法返回值和方法调用的状态等信息。

方法区(Method Area):用于存储类的信息、静态变量、常量等数据。方法区属于堆的一部分,但是它的作用和用途与堆不同。

类加载过程


image.png

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


加载:将类的字节码文件加载到内存中,并在内存中创建一个Class对象,用于表示该类的信息。ClassLoader负责查找和加载类的字节码文件,将字节码文件读入内存,并创建Class对象,并将其保存在方法区中。

链接:在链接阶段,虚拟机会对类进行验证、准备和解析。

验证:验证字节码文件的正确性,包括验证文件格式、元数据、字节码和符号引用等是否符合规范。

准备:为类的静态变量分配内存,并设置默认初始值,例如int类型的默认值为0,对象类型的默认值为null。这些内存都在方法区中进行分配。

解析:将符号引用解析为直接引用,例如将方法调用的符号引用解析为实际的方法地址。

初始化:在类被首次使用时进行,虚拟机会执行类的初始化操作,包括执行类的静态代码块和静态变量的初始化。

执行静态代码块:静态代码块是在类被初始化时执行的,它可以用来进行一些静态资源的初始化工作。

静态变量初始化:静态变量的初始化也是在类被初始化时进行的,它可以通过赋初值或静态代码块来进行初始化。

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


类加载器

获取运行时类的完整结构

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

image.png

获取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();
}


相关文章
|
23天前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
86 43
Java学习十六—掌握注解:让编程更简单
|
18天前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
40 14
|
18天前
|
前端开发 Java
[Java]讲解@CallerSensitive注解
本文介绍了 `@CallerSensitive` 注解及其作用,通过 `Reflection.getCallerClass()` 方法返回调用方的 Class 对象。文章还详细解释了如何通过配置 VM Options 使自定义类被启动类加载器加载,以识别该注解。涉及的 VM Options 包括 `-Xbootclasspath`、`-Xbootclasspath/a` 和 `-Xbootclasspath/p`。最后,推荐了几篇关于 ClassLoader 的详细文章,供读者进一步学习。
28 12
|
20天前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
13 0
[Java]反射
|
2月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
62 9
Java——反射&枚举
|
1月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
25 2
|
12天前
|
Java 编译器
Java进阶之标准注解
Java进阶之标准注解
24 0
|
2月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
1月前
|
JSON Java 数据库
java 常用注解大全、注解笔记
关于Java常用注解的大全和笔记,涵盖了实体类、JSON处理、HTTP请求映射等多个方面的注解使用。
34 0
java 常用注解大全、注解笔记
|
2月前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解