JAVA基础之注解和反射

JAVA基础之注解和反射

注解

基础语法

定义注解

public @interface Table {
}

添加属性

public @interface Table {
	public String name();
}

增加默认值

public @interface Column {
	public String name() default "";
}

元注解

@Retention:设置注解保存策略。

@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
	String name() default "";
}
  • SOURCE:注解将被编译器丢弃,编译后的class文件不包含注解内容。
  • CLASS:默认策略,注解将被编译保留在class文件中,但VM运行时不会保留。
  • RUNTIME:注解将被编译保留在class文件中,VM加载时也会保留,所以可以通过反射方式进行读取。

@Documented:标记注解是否包含在javadoc等工具生成的文档中。

@Documented
public @interface Author {
	String name() default "";
}

@Target:设置注解可以用在Java那些成员上,限制注解的使用范围预防造成滥用(例如:@Override只能在方法上使用)。

@Target({ElementType.TYPE})
public @interface Author {
	String name() default "";
}
  • TYPE :Class、interface(包括注解接口)、enum、record。
  • FIELD:字段(包含枚举常量)。
  • METHOD:方法。
  • PARAMETER:方法参数。
  • CONSTRUCTOR:构造函数。
  • LOCAL_VARIABLE:局部变量。
  • ANNOTATION_TYPE:注释接口(以前称为注释类型)。
  • PACKAGE:包。
  • TYPE_PARAMETER:泛型类型参数(1.8版本添加)。
  • TYPE_USE:使用类型(1.8版本添加)。
  • MODULE:模块(9版本添加)。
  • RECORD_COMPONENT:Record组件(16版本添加)。

**@Inherited **:如果类上的注解是@Inherited修饰的,那么类的子类也会继承这个注解。

@Inherited
public @interface Author {
	String name() default "";
}
  • 接口上的注解即使被@Inherited修饰,其实现类也不会继承注解。
  • 类方法上的注解即使被@Inherited修饰,子类不会继承注解。

内置注解

@Override

public class User {
	@Override
	public String toString() {
		return super.toString();
	}
}
  • 检查方法是否为重写方法,如果发现其父类或引用的接口中没有该方法时,IDE会报错。

@Deprecated

@Deprecated
public Date(int year, int month, int date) {
    this(year, month, date, 0, 0, 0);
}
  • 标记方法已过时,使用该方法时IDE会给出警告。

**@SuppressWarnings **

public static void main(String[] args) {
    @SuppressWarnings("unused")
    Date date = new Date(10000l);
}
  • 告诉IDE忽略注解中声明的警告。

@SafeVarargs

public class User<T> {
	
	@SafeVarargs
	public User(T...ts) {
		
	}
	
	@SuppressWarnings("unchecked")
	public void test3( T...ts) {
	}
	
	@SafeVarargs
	public final void test1(T...ts) {
	}
	
	@SafeVarargs
	public static <V> void test2(V...ts) {
	}
	
}
  • 该注解是在Java 7版本添加,向IDE声明,忽略泛型可变参数的 Type safety: Potential heap pollution via varargs parameter ts 警告(可以用在构造函数、静态方法、不可变方法、其它方法需要使用 @SuppressWarnings 处理)。

@FunctionalInterface

@FunctionalInterface 
interface Calculate {
	public int t();
}
  • 该注解是Java8版本添加,标识匿名函数或函数式接口,使用 @FunctionalInterface 注解的接口只能有一个方法(函数式接口定义)。

@Repeatable

public @interface Authors {
	Author[] value();
}
@Repeatable(Authors.class)
public @interface Author {
	String name() default "";
	int level();
}
public class User {
	@Author(name = "赵又廷",level = 0)
	@Author(name = "高圆圆",level = 1)
	public void run() {
	}
}
  • Java8版本添加,标识注解可以同时使用多次。

反射

Class API

测试文件
/**
 * @ClassName: Animal
 * @Description: 动物基类
 */
public class Animal {
	// 名称
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
/**
 * @ClassName: Vertebrate
 * @Description: 脊椎动物
 */
public class Vertebrate extends Animal {
	// 生存环境
	private String env;
	public String getEnv() {
		return env;
	}
	public void setEnv(String env) {
		this.env = env;
	}
}
获取类字段
  • 找不到字段时会报异常 java.lang.NoSuchFieldException
Class API继承私有
getDeclaredField()×
getField()×
getDeclaredFields()×
getFields()×
public static void main(String[] args) throws NoSuchFieldException, SecurityException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 获取env成功,获取name会报异常
    Field f_env = cls.getDeclaredField("env");
    System.out.println(f_env.getName());
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 获取name、env都会报异常,将两个字段的修饰符改为public可以获取。
    Field f_env = cls.getField("name");
    System.out.println(f_env.getName());
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException{
    Class<Vertebrate> cls = Vertebrate.class;
    // f数组只有一个元素(env字段)
    Field[] f = cls.getDeclaredFields();
    System.out.println(f.length);
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException{
    Class<Vertebrate> cls = Vertebrate.class;
    // f数组是空的(将env和name字段的修饰符改为public可以获取)
    Field[] f = cls.getFields();
    System.out.println(f.length);
}
获取类方法
Class API继承私有
getDeclaredMethod(String name, Class<?>… parameterTypes)×
getMethod(String name, Class<?>… parameterTypes)×
getDeclaredMethods()×
getMethods()×
// 测试之前我们先向Vertebrate类中增加下列方法
private void breed() {
    System.out.println(super.getName()+"无配偶");
}
public static void main(String[] args) throws NoSuchMethodException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 可以获取到方法
    Method m = cls.getDeclaredMethod("breed");
    System.out.println(m.getName());
}
public static void main(String[] args) throws NoSuchMethodException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 因为breed方法是私有方法,固报错
    Method m = cls.getMethod("breed");
    System.out.println(m.getName());
}
  • **注意:**getDeclaredMethod、getMethod函数只有name参数时表示匹配无参的方法,如果没有则报错。
public static void main(String[] args) throws NoSuchMethodException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 获取到getEnv、setEnv、breed三个方法,getName、setName由于是基类方法无法获取
    Method[] m = cls.getDeclaredMethods();
    for(int i=0;i<m.length;i++) {
        System.out.println(m[i].getName());
    }
}
public static void main(String[] args) throws NoSuchMethodException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 无法获取breed方法,其它都可以获取
    Method[] m = cls.getMethods();
    for(int i=0;i<m.length;i++) {
        System.out.println(m[i].getName());
    }
}
获取类构造函数
Class API私有
getDeclaredConstructor(Class<?>… parameterTypes)
getConstructor(Class<?>… parameterTypes)×
getDeclaredConstructors()
getConstructors()×
// 测试之前,我们先将Vertebrate的默认构造函数改为私有的
private Vertebrate() {
}
public static void main(String[] args) throws NoSuchMethodException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 可以获取到无参的构造函数
    Constructor<Vertebrate> c = cls.getDeclaredConstructor();
    System.out.println(c.getName());
}
public static void main(String[] args) throws NoSuchMethodException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 无法获取默认无参的构造函数
    Constructor<Vertebrate> c = cls.getConstructor();
    System.out.println(c.getName());
}
public static void main(String[] args) throws NoSuchMethodException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 可以获取到无参的构造函数
    Constructor<?>[] c = cls.getDeclaredConstructors();
    System.out.println(c.length);
}
public static void main(String[] args) throws NoSuchMethodException{
    Class<Vertebrate> cls = Vertebrate.class;
    // 无法获取默认无参的构造函数
    Constructor<?>[] c = cls.getConstructors();
    System.out.println(c.length);
}

Field API

字段类型
public static void main(String[] args) throws NoSuchFieldException {
    Class<Vertebrate> cls = Vertebrate.class;
    Field f = cls.getDeclaredField("env");
    Class<?> type = f.getType();
    System.out.println(type.getCanonicalName());
}
字段修饰符
public static void main(String[] args) throws NoSuchFieldException {
    Class<Vertebrate> cls = Vertebrate.class;
    Field f = cls.getDeclaredField("env");

    int modifiers = f.getModifiers();

    System.out.println(Modifier.PRIVATE == modifiers);
    System.out.println(Modifier.FINAL == modifiers);
}
修改字段值
public static void main(String[] args) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException{
    Class<Vertebrate> cls = Vertebrate.class;
    Field f = cls.getDeclaredField("env");

    Vertebrate obj = new Vertebrate();
    f.setAccessible(true);
    f.set(obj, "海洋");

    System.out.println(obj.getEnv());
}
  • 当字段的访问修饰符是私有的,我们需要通过 setAccessible(true) 打开访问限制。

面试题(奇葩)

public static void main(String[] args) {
    String a = new String("a");
    String b = a;
    // TODO 将字符串的值由a改为b,仍打印true
    System.out.println(a == b);
}
public static void main(String[] args) throws NoSuchFieldException,
								IllegalArgumentException, IllegalAccessException{
    String a = new String("a");
    String b = a;
    System.out.println("a:"+a);

    // 通过反射从新设置a对象的value字段值
    Class<String> cls = String.class;
    Field f = cls.getDeclaredField("value");
    f.setAccessible(true);

    char[] c = new char[1];
    c[0] = 'b';
    f.set(a, c);

    System.out.println(a == b);
    System.out.println("a:"+a);
}
  • 这段代码必须限制在1.8版本之下,不然会报错Unable to make field private final byte[] java.lang.String.value accessible: module java.base does not "opens java.lang" to unnamed module
字段注解
public static void main(String[] args) throws NoSuchFieldException {
    Class<Vertebrate> cls = Vertebrate.class;
    Field f = cls.getDeclaredField("env");

    Annotation[] a = f.getAnnotations();
    for(int i=0;i<a.length;i++) {
        System.out.println(a[i].toString());
    }
}
  • 前置修改:给env字段增加注解(注意自定义注解的元注解 @Retention 需要设置为 RetentionPolicy.RUNTIME。)

Method API

返回类型
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
    Class<Vertebrate> cls = Vertebrate.class;
    Method method = cls.getDeclaredMethod("breed");

    Class<?> returnType = method.getReturnType();
    System.out.println(returnType.getName());
}
参数列表
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
    Class<Vertebrate> cls = Vertebrate.class;
    Method method = cls.getDeclaredMethod("setEnv",String.class);

    Parameter[] parameters = method.getParameters();
    for(int i=0;i<parameters.length;i++) {
        System.out.println(parameters[i].getName());
        System.out.println(parameters[i].getType());
        System.out.println(parameters[i].getAnnotations().length);
    }
}
  • 正常情况下,基于节省资源的考虑,类文件是不保留原始参数名称的(可以通过增加编译参数 javac -parameters 的方式保留形参名称,或者Eclipse将下图中的选项选中)。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ViFgkHgV-1673947516797)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20230116172625760.png)]

访问修饰符
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
    Class<Vertebrate> cls = Vertebrate.class;
    Method method = cls.getDeclaredMethod("setEnv",String.class);

    int modifiers = method.getModifiers();
    System.out.println(Modifier.PUBLIC == modifiers);
}
调用方法
public static void main(String[] args) throws NoSuchMethodException, SecurityException, 
	IllegalAccessException, IllegalArgumentException, InvocationTargetException  {
    Class<Vertebrate> cls = Vertebrate.class;
    Method method = cls.getDeclaredMethod("setEnv",String.class);

    Vertebrate obj = new Vertebrate();
    method.setAccessible(true);
    Object ret = method.invoke(obj, "海洋");
    System.out.println(ret);
    System.out.println(obj.getEnv());
}

Constructor API

访问修饰符
public static void main(String[] args){
    Class<Vertebrate> cls = Vertebrate.class;
    Constructor<?>[] constructors = cls.getConstructors();
    for(int i=0;i<constructors.length;i++) {
        System.out.println(Modifier.PUBLIC == constructors[i].getModifiers());
    }
}
创建实例
public static void main(String[] args) throws NoSuchMethodException, SecurityException, 
                                                InstantiationException, IllegalAccessException, 
                                                IllegalArgumentException, InvocationTargetException{
    Class<Vertebrate> cls = Vertebrate.class;
    Constructor<Vertebrate> constructor = cls.getConstructor(String.class);
    Vertebrate obj = constructor.newInstance("海洋");
    System.out.println(obj.getEnv());
}
  • Constructor.newInstance()Class.newInstance() 相比:

    • Class.newInstance()只能调用无参构造函数。

    • 当构造函数报错时,Class.newInstance() 会抛出原始异常,Constructor.newInstance() 会使用 java.lang.IllegalArgumentException 进行包装。

    • Class.newInstance() 要求构造函数是公共的,Constructor.newInstance() 可以调用私有构造方法。

      public static void main(String[] args){
          Class<Vertebrate> cls = Vertebrate.class;
      
          Constructor<Vertebrate> constructor = cls.getDeclaredConstructor(String.class);
          constructor.setAccessible(true);
          Vertebrate obj = constructor.newInstance("海洋");
      
          System.out.println(obj.getEnv());
      }
      
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值