目录
@Documented:执行 javadoc 的时候,标记这些注解是否包含在生成的用户文档中。
注解Annotation
什么是注解?
注解就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,从而做相对应的处理。
注:注释时给人看的,注解是给程序看的,可以被编译器读取。
注解的作用
注解大多时候与反射或者 AOP 切面结合使用,它的作用有很多,比如标记和检查,最重要的一点就是简化代码,降低耦合性,提高执行效率。
提供元数据:注解可以在代码中添加元数据,用于描述、标记和配置代码的特性、属性或行为。通过注解,开发者可以在代码中附加额外的信息,以便在编译时、运行时或其他工具处理过程中进行解析和利用。
代码生成和处理:许多框架和工具可以读取注解信息,并根据注解执行相应的代码生成和处理操作。例如,基于注解的框架可以根据注解生成数据库模式、生成文档、生成测试代码、实现依赖注入等。
编译时检查和验证:编译器可以利用注解来进行静态检查和验证。通过定义自定义的注解,并使用注解处理器,可以在编译时对代码进行额外的检查和验证,以确保代码符合特定的约束、规范或安全要求。
运行时行为和逻辑:注解可以用于在运行时实现特定的行为和逻辑。通过使用反射机制,可以在运行时读取注解信息,并根据注解配置对象的行为、调用特定的方法或触发相应的事件。
文档生成和文档化:注解可以用于生成文档和文档化代码。通过使用特定的注解标记和描述代码的元素,可以生成详细的文档,例如API文档、接口规范、使用说明等。
框架和扩展机制:注解是许多框架和扩展机制的基础。通过使用注解,开发者可以在框架中进行配置、声明和扩展,以实现特定的行为、逻辑或定制化需求。
注解元素的数据类型
所有基本类型(int,float,boolean,byte,double,char,long,short)
String
Class
enum
Annotation
上述类型的数组
注:声明注解元素时可以使用基本类型但不允许使用任何包装类型,同时注解也可以作为元素的类型,也就是嵌套注解
注解元素的分类
内置注解
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。
JDK7 之后又加了 3 个,@SafeVarargs - Java 7开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
元注解——修饰注解的注解
- @Target:用来指定注解的作用域(如方法、类或字段),其中 ElementType 是枚举类型,其定义如下,也代表可能的取值范围 。注,如果 @Target 无指定作用域,则默认可以作用于任何元素上。
- @Retention:用来指定注解的生命周期,它有三个值,对应
RetentionPolicy
中的三个枚举值,分别是:源码级别(source),类文件级别(class)或者运行时级别(runtime)@Documented:执行 javadoc 的时候,标记这些注解是否包含在生成的用户文档中。
- @Inherited:标记这个注解具有继承性,比如 A 类被注解 @Table 标记,而 @Table 注解被 @Inherited 声明(具备继承性);继承于 A 的子类,也继承 @Table 注解。
自定义注解
所有基本类型(int,float,boolean,byte,double,char,long,short)
String
Class
enum
Annotation
上述类型的数组
注:声明注解元素时可以使用基本类型但不允许使用任何包装类型,同时注解也可以作为元素的类型,也就是嵌套注解
代码示例
//自定义注解
public class Demo03_CustomAnnotation {
//注解可以显示赋值,如果没有默认值,就必须给注解赋值
@MyAnnotation2(name = "王五")
public void test() {
}
}
@Target(value = {ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2 {
//注解的参数:参数类型+参数名()
//String name();
String name() default "";
int age() default 0;
int id() default -1;//-1代表不存在
String[] schools() default {"西部开源","清华大学"};
其中, @interface与interface的区别以及@interface的用法
interface:声明一个Java接口。在接口中只能有常量和抽象方法;接口不可以实例化,接口中的方法没有方法体,继承接口的类必须实现接口中定义的方法。
@interface:一个继承了
java.lang.annotation.Annotation
接口的自定义注解,定义注释类型。
- @Interface用来声明一个注解,格式:public @Interface 注解名{定义内容}
- 其中的每一个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能时基本类型,Class String,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value,如果元素的名字为value,使用这个注释的时候,元素的名字和等号可以省略。eg: public @interface Copyright { String value(); }
- 没有元素/方法的注释被成为标记(marker)注释类型。标记注释在使用的时候,其后面的括号可以省略
- 注解元素必须要有值,我们定义注解元素时经常使用空字符串,0作为默认值
静态VS动态语言
动态语言
- 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
- 主要动态语言:0bject-C、C#、JavaScript、PHP、Python等。
静态语言
- 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++、Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性
- 我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!
Java 反射 Reflection
1、Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。 这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。
2、加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个对象只有
一个Class类对象),这个对象就包含了完整的类的结构信息。可以通过这个对象看
类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之
为:反射。
- 正常方式:引入包名-->通过new实例化-->取得实例化对象
- 反射方式:实例化对象-->getClass()方法-->得到完整的"包类“名称
Java 反射的作用
反射通过Java的
java.lang.reflect
包提供了一组API,可以在运行时读取和操作类的结构、成员和注解等信息。反射可以实现动态创建对象、调用方法、访问属性、获取注解信息等功能,使得代码更加灵活和动态。
==这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制==
- 在运行时判定任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判定任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和 方法
- 在运行调用任意一个对象的方法
- 生成动态代码
Java 反射的实现方法
- Class.forName(“类的路径”)
- 类名.class
- 对象名.getClass()
- 基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象
利用反射动态创建对象实例
Class 对象的 newInstance()
使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。
调用 Constructor 对象的 newInstance()
先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。
//获取 Person 类的 Class 对象 Class clazz=Class.forName("reflection.Person"); //使用.newInstane 方法创建对象 Person p=(Person) clazz.newInstance(); //获取构造方法并创建对象 Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class); //创建对象并设置属性13/04/2018 Person p1=(Person) c.newInstance("李四","男",20);
实现反射的类
- Class:表示正在运行的Java应用程序中的类和接口。
注意: 所有获取对象的信息都需要Class类来实现。- Field:提供有关类和接口的属性信息,以及对它的动态访问权限。
- Constructor:提供关于类的单个构造方法的信息以及它的访问权限。
- Method:提供类或接口中某个方法的信息。
实现反射的优缺点
优点:可以实现动态创建对象和编译,体现出很大的灵活性;
缺点: 使用反射性能较低,需要解析字节码,将内存中的对象进行解析。
解决方案
- 通过setAccessible(true)关闭JDK的安全检查来提升反射速度;
- 多次创建一个类的实例时,有缓存会快很多
- ReflflectASM工具类,通过字节码生成的方式加快反射速度
- 相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性)
Java反射API
反射 API 用来生成 JVM 中的类、接口或则对象的信息
- Class 类:反射的核心类,可以获取类的属性,方法等信息。
- Field 类:Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性
值。- Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或
者执行方法。- Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。
反射使用步骤(获取 Class 对象、调用对象方法)
- 获取想要操作的类的 Class 对象,他是反射的核心,通过 Class 对象我们可以任意调用类的方法。
- 调用 Class 类中的方法,既就是反射的使用阶段。
- 使用反射 API 来操作这些信息。
注解和反射的关系
- 注解和反射可以结合使用,通过反射机制可以读取和处理注解信息,从而实现一些动态行为和逻辑。
- 反射可以读取类、方法、字段上的注解信息,并根据注解的值来实现相应的操作。
- 注解可以提供元数据,帮助反射读取和解释代码的特性和行为,从而实现更灵活、可配置和可扩展的代码逻辑。
Java的内存分析
类的加载过程
类的加载与ClassLoader的理解