Java 可以对类、方法、变量、参数和包等进行注解。Java 应用程序在需要时可以通过反射机制获取这些注解信息,从而针对不同的注解执行不同的逻辑操作。
注解(Annotation)是 Java 提供的设置程序中元素的关联信息和元数据(MetaData)的方法,它是一个接口,程序可以通过反射获取指定程序中元素的注解对象,然后通过该注解对象获取注解中的元数据信息。
元注解(Meta-Annotation)负责注解其它注解。在 Java 中定义了 4 种标准的元注解类型:@Target、@Retention、@Documented、@Inherited,用于定义不同类型的注解。
名称 | 修饰目标 |
---|---|
TYPE | 用于描述类、接口(包括注解类型)或enum声明 |
FIELD | 用于描述域 |
METHOD | 用于描述方法 |
PARAMETER | 用于描述参数 |
CONSTUCTOR | 用于描述的构造器 |
LOCAL_VARIABLE | 用于描述局部变量 |
ANNOTATION_TYPE | 用于声明一个注解 |
PACKAGE | 用于描述包 |
TYPE_PARAMETER | 对普通变量的声明 |
TYPE_USE | 能标注任何类型的名称 |
注解用于描述元数据的信息,使用的重点在于对注解处理器的定义。Java SE5 扩展定义了反射机制的 API ,以帮助程序快速构造自定义的注解处理器。对注解的使用一般包含自定义及使用注解接口,一般通过封装统一的注解工具来使用注解。
下面代码定义了一个 FruitProvider 注解接口,其中有 name 和 adress 两个属性:
//1.定义接口
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
//供应商编号
public int id() default -1;
//供应商名称
public String name() default "";
//供应商地址
public String address() default "";
}
下面的代码定义了一个 Apple 类,并通过注解方式定义了一个 FruitProvider:
public class Apple {
//2.使用注解接口
@FruitProvider(id = 1, name = "苹果", address = "中国")
private String appleProvider;
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
public String getAppleProvider() {
return appleProvider;
}
}
下面代码定义了一个 FruitInfoUtil 注解处理器,并通过反射信息获取注解数据,最后通过 main 方法调用该注解处理器使用注解:
package com.example.demo.test;
import java.lang.reflect.Field;
//3.定义注解处理器
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz) {
String strFruitProvicer = "供应商信息:";
//通过反射信息获取注解数据
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(FruitProvider.class)) {
FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class);
//使用注解数据
strFruitProvicer = " 供应商编号: " + fruitProvider.id() +
" 供应商名称: " + fruitProvider.name() +
" 供应商地址: " + fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
}
public class FruitRun {
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
//输出结果为:
//供应商编号: 1 供应商名称: 苹果 供应商地址: 中国
}
}
相关面试题:
定义在类内部的类被称为内部类。内部类根据不同的定义方式,可分为静态内部类、成员内部类、局部内部类和匿名内部类这 4 种。
定义在类内部的静态类被称为静态内部类。静态内部类可以访问外部类的静态变量和方法;在静态内部类中可以定义静态变量、方法、构造函数等;静态内部类通过“外部类.静态内部类”的方式来调用,具体的实现代码如下:
public class OuterClass {
private static String className = "staticInnerClass";
//定义一个静态内部类
public static class StaticInnerClass {
public void getClassName() {
System.out.println("className:" + className);
}
}
public static void main(String[] args) {
//调用静态内部类
OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass();
staticInnerClass.getClassName();
}
}
上面代码通过 public static class StaticInnerClass{} 定义了一个静态内部类 StaticInnerClass,然后定义了静态内部类的 getClassName 方法,在使用的过程中通过“外部类.静态内部类”的方式进行调用,具体的实现代码如下:
OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass();
这样就定义一个静态内部类并可以像普通类那样调用静态内部类的方法。
Java 集合 HashMap 在内部维护了一个静态内部类 Node 数组用于存放元素,但 Node 数组对使用者是透明的。像这种和外部类关系密切且不依赖外部类实例的类,可以使用静态内部类实现。
定义在类内部的非静态类叫做成员内部类,在成员内部类中不能定义静态方法和变量(final 修饰的除外),因为成员内部类是非静态的,而在 Java 的非静态代码块中不能定义静态方法和变量。成员内部类的具体实现代码如下:
public class OutClass {
private static int a;
private int b;
//定义一个成员内部类
public class MemberInnerClass {
public void print() {
System.out.println(a);
System.out.println(b);
}
}
}
从以上代码可以看到,在 OutClass 中通过 public class MemberInnerClass{} 定义了一个成员内部类,其使用方式和静态内部类相同。
定义在方法中的类叫做局部内部类。当一个类只需在某个方法中使用某个特定的类时,可以通过局部类来优雅地实现,具体的实现代码如下:
public class OutClass {
private static int a;
private int b;
public void partClassTest(final int c) {
final int d = 1;
//在 partClassTest 方法中定义一个局部内部类 PartClass
class PartClass {
public void print() {
System.out.printf("c");
}
}
}
}
以上代码在 partClassTest 方法中通过 class PartClass{} 语句块定义了一个局部内部类。
匿名内部类指通过继承一个父类或者实现一个接口的方式直接定义并使用的类。匿名内部类没有 class 关键字,这是因为匿名内部类直接使用 new 生成一个对象的引用。具体的实现代码如下:
public abstract class Worker {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract int workTime();
}
public class Test {
public void test(Worker worker) {
System.out.println(worker.getName() + " 工作时间: " + worker.workTime());
}
public static void main(String[] args) {
Test test = new Test();
test.test(new Worker() {
public int workTime() {
return 8;
}
public String getName() {
return "alex";
}
});
}
}
在以上代码中首先定义了一个抽象类 Worker 和一个抽象方法 workTime ,然后定义了一个 Test 类,在 Test 类中定义了一个方法,该方法接收一个 Worker 参数,这时匿名类需要的准备工作都已做好。在需要一个根据不同场景有不同实现的匿名内部类时,直接在 test 方法中新建匿名内部类并重写相关方法即可。
相关面试题: