注解
1.1 自定义注解
用过的注解:@Override,@Test,@Data,@NoArgsConstructor,@AllArgsConstructor,@Deprecated等。
这些注解是别人定义好的,要么是核心类库中,要么是第三方,例如:JUnit,lombok等。
1.1.1 自定义注解的格式
public @interface 注解名{
}
示例:
package com.mytest.annotation;
public @interface OneAnnotation {
}
1.1.2 使用注解
使用注解的语法格式:在类或方法或属性上加@注解名
例如:
@OneAnnotation //在类上面加注解
public class MyDemo {
@OneAnnotation //在属性上面加注解
private int num;
@OneAnnotation //在方法上面加注解
public void print(){
System.out.println("我是一个方法而已");
}
}
1.1.3 读取注解
如果只有声明和使用,注解没有任何意义,也不会对代码产生任何影响。
注解必须有一段程序来读取它,然后赋予它意义。
@Override注解是由 Java编译器来读取的。当编译器读取到某个方法上加了 @Override,注解,编译器就会执行一段代码,这段代码是检查该方法是不是严格按照重写的要求来编写的。如果不符合重写的要求,就会报错。
@Deprecated 注解是由 Java编译器和 javadoc.exe 来读取。 Java编译器如果到程序员使用了@Deprecated 标记的类或方法,就会弹出警告。 javadoc.exe 读取到@Deprecated 标记的类或方法,就会在API文档中标记 已过时。
@Test注解是由JUnit组件来读取的,就会让这个@Test标记的方法称为一个测试方法,可以独立运行。
下面,编写一段代码读取MyDemo类上面的@OneAnnotation:
package com.mytest.annotation;
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.FIELD,ElementType.TYPE,ElementType.METHOD}) //元注解
public @interface OneAnnotation {
}
package com.mytest.annotation;
@OneAnnotation //在类上面加注解
public class MyDemo {
@OneAnnotation //在属性上面加注解
private int num;
@OneAnnotation //在方法上面加注解
public void print(){
System.out.println("我是一个方法而已");
}
}
package com.mytest.annotation;
import org.junit.Test;
import java.lang.annotation.Annotation;
public class TestReadOneAnnotation {
@Test
public void test1()throws Exception{
//想要读取MyDemo类上面的@OneAnnotation
//如果读取到了,我们就打印一句话:MyDemo类也有注解了
Class clazz = Class.forName("com.mytest.annotation.MyDemo");
Annotation[] annotations = clazz.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
if(annotation instanceof OneAnnotation){
System.out.println("MyDemo类也有注解了");
}
}
}
}
1.1.4 元注解
当我们定义一个新注解时,加在注解上面的注解,称为元注解。它们是用于对这个新注解进行说明用的。
1、@Target
用于描述新注解可以使用的位置。这个位置由ElementType枚举类的常量对象决定,常见的常量对象有:METHOD,FIELD等。
2、@Retention
用于描述新注解可以保留到哪个阶段,即生命周期。这个生命周期由RetentionPolicy枚举类的3个常量对象之一决定:SOURCE,CLASS,RUNTIME。
- SOURCE:源代码阶段,从java文件到.class文件,这个注解就没用了。
- CLASS:字节码阶段,从字节码被加载到JVM,这个注解就没用了。
- RUNTIME:运行时阶段,可以保留到JVM中。只有这个阶段的注解,才可以被反射读取。
3、@Documented
用于描述新注解要不要在生产API文档时,加上它的信息。
4、@Inherited
用于描述新注解能不能被子类继承
1.1.5 注解中的抽象方法
注解被看成是接口,但是它不是真的接口。
注解中也可以定义抽象方法,也只能定义抽象方法。而且这个抽象方法与普通接口的抽象方法有区别:
- 这个抽象方法的返回值类型只能是8种基本数据类型及其包装类,String,枚举,Class类型以及它们的数组。
- 这个抽象方法不能有形参列表,必须是空参
- 这个抽象方法可以指定默认返回值,通过default关键字来指定
- 这个抽象方法在使用注解的时候“重写”,它是在@注解名(抽象方法名=返回值)
- 如果抽象方法名是value,且只有一个抽象方法必须重写的话,那么重写时,可以省略value=
package com.mytest.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface TwoAnnotation {
String method() default "test";
String show();
}
package com.mytest.annotation;
@OneAnnotation
@TwoAnnotation(show = "测试")
public class Example {
}
package com.mytest.annotation;
import org.junit.Test;
import java.lang.annotation.Annotation;
public class TestReadTwoAnnotation {
@Test
public void test1()throws Exception{
Class clazz = Example.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
if(annotation instanceof TwoAnnotation){
TwoAnnotation t = (TwoAnnotation) annotation;
System.out.println(t.method());//调用注解的抽象方法
System.out.println(t.show());//调用注解的抽象方法
}
}
}
}
1.2 总结
Java中一切皆对象。
- Class:Java中把所有数据类型,都看成是Class类的对象。你的程序中有几个类型(包括基本数据类型,类、接口等),相当于JVM中有几个Class类的对象。
- Constructor:Java中把所有构造器都看成Constructor类的对象。你在程序中写了几个构造器,相当于底层就有几个Constructor类的对象。
- 所有类的构造器都有修饰符、构造名、形参列表、都能new对象
- Field:Java中把所有类的属性/成员变量都看成Field类的对象。
- 所有类的属性都有修饰符、数据类型、属性名、属性值、都可以get/set值
- Method:Java中把所有类的每一个方法都看成Method类的对象。
- 所有类的方法都有修饰符、返回值类型、方法名、形参列表、都可以被执行方法体
- Modifier:Java中所有每一个修饰符都是Modifier类中的一个常量值。
Java类的概念:一类具有相同特性的事物的抽象描述。
到底什么是反射?
反射机制是Java语言提供的一种 powerful 机制,允许程序在“运行时`”获取类的信息以及操作类的内部属性和方法。以下是反射机制的主要特点:
(1)动态获取类信息:可以通过Class对象获取类的构造函数、方法、字段等信息。
(2)创建实例:即使没有类的引用,也可以通过反射创建类的实例。
(3)调用方法:可以调用类中的方法,包括私有方法。
(4)设置和获取字段值:能够访问并修改类的私有字段。
需要注意的是,反射虽然强大但也有其缺点,比如性能开销较大,破坏了封装性,并且可能导致代码难以理解和维护。因此,在实际开发中应谨慎使用反射。