注解与反射

注解

内置注解

  • @Deprecated 废弃的,过时的
  • @Override 重写,覆盖
  • @SuppressWarnings 压缩警告,抑制编译器产生的警告

自定义注解

  1. @Target:用于表明注解的适用范围

    • ElementType.Type,可以作用于类上
    • ElementType.METHOD,可以作用于方法上
    • ElementType.FIELD,可以作用于成员变量上
  2. @Retention表示需要在什么级别保存该注释信息(生命周期)

    • RetentionPolicy.RUNTIME : 在运行时保留注解
  3. @Documented 描述注解是否被抽取到api文档中

  4. @Inherited 描述注解是否被子类继承。

自定义注解例子:

  • 注意:只有一个参数的时候,可以写成value().
/**
 * 自定义注解
 * @author Created by zhuzqc on 2022/5/31 23:03
 */
public class CustomAnnotation {

    /**
     * 注解中可以为参数赋值,如果没有默认值,那么必须为注解的参数赋值
     * */
    @MyAnnotation(value = "解释")
    public void test(){
    }
}
/**
 * @author zhuzqc
 */
//自定义注解必须的元注解target,指明注解的作用域(此处指明的是在类和方法上起作用)
@Target({ElementType.TYPE,ElementType.METHOD})
//元注解retention声明该注解在何时起作用(此处指明的是在运行时起作用)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{

    //注解中需声明参数,格式为:参数类型 + 参数名();
    String value() default "";

}

反射

获取Class类实例的六种方法

  1. 通过类的class属性获取,最安全可靠,性能高
Class user = User.class;
  1. 通过类的实例,getClass方法获取
Class user = user.getClass();
  1. 通过全类名获取
Class user = class.forName("com.george.pojo.User");
  1. 利用ClassLoader(类加载器)获取。

  2. 通过子类来获取父类的Class

    Class person = Student.getSuperClass();
    
  3. 内置基本数据类型可以使用类名加type获取

Class<Integer> type = Integer.Type;

哪些类可以有Class对象

  1. class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  2. interface:接口 (Comparable.class)
  3. []: 数组 (String[].class)
  4. enum: 枚举 (ElementType.class)
  5. annotation:注解@interface (Override.class)
  6. primitive type:基本数据类型 (Integer.class, int.class)
  7. void (void.class)

Class类的常用方法

image-20230710113822746

类加载器

作用:将class文件字节码内容加载到内容中,并将这些静态数据转换为方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

分类:系统的类加载器、扩展类加载器、根加载器

image-20230710112816966

package com.njit.reflection;

public class Test03 {

    public static void main(String[] args) throws ClassNotFoundException {
        // 获取系统的类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        // 获取系统类加载器的父类加载器--> 扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        // 获取扩展类加载器的父类加载器 --> 根加载器(c/c++),会返回null,这个根加载器是无法直接获取的
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        // 测试当前类的类加载器
        ClassLoader classLoader = Class.forName("com.njit.reflection.Test03").getClassLoader();
        System.out.println(classLoader);

        // 测试jdk内置的类加载器 ,也是根类加载器
        ClassLoader commonClassLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(commonClassLoader);

        // 获取系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));

    }
}

获取Class类实例后,能干什么

  1. 创建类的对象

    1.1 调用Class对象的newInstance()方法

    要求:

    • 类必须要有一个无参数的构造器
    • 类的构造器的访问权限需要足够

    1.2 没有无参数构造器的话,需要在操作时明确的调用类中的构造器,并传递参数

    步骤

    1. 通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
    2. 向构造器的形参中传递对象数组,包含需要的各个参数
    3. 通过Constructor实例化对象
    Construct constructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
    User user = (User)constructor.newInstance("zxj",001,18);
    
  2. 调用指定的方法

    2.1 调用getMethod(String name , Class…parameterTypes)取得Method对象。

    2.2 调用Obejct invoke(Object obj,Object[] args)进行调用,并向方法中传递参数信息。

    **注意:**若方法为private,则需要在invoke之前,调用setAccessible(true),取消语言访问检查

    User user = (User)c1.newInstance();
    Method setName = c1.getDeclaredMethod("setName",string.class);
    setName.invoke(user3,"zxj");
    
    //--------------------------------------------------------------------
    
    //获得本类和父类的全部方法
    c1.getMethods();
    
    //获得本类的全部方法
    c1.getDeclaredMethods();
    
    //获得指定的方法
    c1.getMethod("getName",null);
    c1.getMethod("setName",String.class);
    

    提问:为什么已经有了user对象了,还需要getDeclaredMethod和invoke呢,为什么不直接调用方法呢?

    1. 方法的访问控制:使用newInstance()只是创建了一个对象实例,它并没有改变原始类或方法的访问修饰符。如果一个方法是私有的或受保护的,直接使用newInstance()是无法访问和调用的。而getMethod()可以获取到指定名称和参数类型的方法,不论其访问修饰符如何,并将其封装成Method对象,然后可以使用invoke()执行该方法。

    2. 方法的重载:如果一个类中存在多个具有相同名字但参数类型不同的方法(方法重载),直接使用newInstance()创建的对象无法明确指定要调用哪个方法。需要使用getMethod()来获取到特定的方法,并提供准确的参数类型信息,以便选择正确的方法调用。

    3. 动态代理时需要调用invoke,并在前后增加功能

  3. 获取指定的属性和值

    //找到public属性
    Field[] fields = c1.getFields(); 
    
    //找到全部属性
    fields = c1.getDeclaredFields();
    
    //获得指定属性的值
    Fields = c1.getDeclaredField("name");
    
    
  4. 反射操作注解

Tips:注解也是一种class

package com.njit.reflection;

import java.lang.annotation.*;
import java.lang.reflect.Field;


public class Test08 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.njit.reflection.Animal");

        // 通过反射获取注解
        Annotation[] annotations = c1.getAnnotations();
        System.out.println(annotations);

        // 获取注解的value值
        MyTest myTest = (MyTest) c1.getAnnotation(MyTest.class);
        System.out.println(myTest.value());

        // 获取类的字段的指定注解
        Field field = c1.getDeclaredField("name");
        FieldTest annotation = field.getAnnotation(FieldTest.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@MyTest("db_test")
class Animal{

    @FieldTest(columnName = "id", type = "Integer", length = 10)
    private int id;

    @FieldTest(columnName = "age", type = "Integer", length = 8)
    private int age;

    @FieldTest(columnName = "name", type = "String", length = 10)
    private String name;

    public Animal() {
    }

    public Animal(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 "Animal{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

/**
 * 自定义的简单注解,注解在类上
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest{
    String value();
}

/**
 * 自定义的简单注解,注解在属性上
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTest{
    String columnName();
    String type();
    int length();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值