反射与注解基本用法

前言

记录一些,反射与注解使用的一些基本方法。


一、注解

1.自定义注解

  • 注解定义方式:用@interface来声明该类为注解类。
  • 元注解:TODO
  • 注解的属性定义方式
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解
 *
 * @Author: John Smith
 * @Date: 2022/4/23
 */
@Target(ElementType.METHOD) // 注解针对什么生效
@Retention(RetentionPolicy.RUNTIME) // 注解啥时候生效
public @interface InitMethod {
    // 这是注解的参数 后面是他的默认值。在没有默认值的情况下,如果不给值的话,会报错。
    String name() default "";

    // int 类型如果默认值为-1,代表不存在
    int id() default -1; 
}

2.测试类

/**
 * 测试用类
 *
 * @Author: John Smith
 * @Date: 2022/4/23
 */
public class InitDemo {

    @InitMethod
    public void init(){
        System.out.println("init........");
    }

    @InitMethod
    public void demo() {
        System.out.println("demo.........");
    }

}

3.测试结果 

执行该main方法,会自动执行InitDemo类中所有加了@InitMethod注解的方法。

执行方式在下面的注解章中讲到。

import java.lang.reflect.Method;

/**
 *
 *
 * @Author: John Smith
 * @Date: 2022/4/23
 */
public class TestMain {
    public static void main(String[] args) throws Exception {
        // 通过反射创建对象
        Class<?> clazz = Class.forName("org.example.spring.annotation.InitDemo");
        // 拿到该对象所有的方法
        Method[] methods = clazz.getMethods();
        // 遍历所有方法
        for (Method method : methods) {
            // 判断哪个方法上加了这个InitMethod注解
            boolean b = method.isAnnotationPresent(InitMethod.class);
            // 如果加了这个注解的,就反射执行该方法
            if (b) {
                method.invoke(clazz.getConstructor(null).newInstance(null),null);
            }
        }
    }
}
  • 执行结果

二、反射

0.测试用类

使用了lombok,省去多余代码。

import lombok.Data;

/**
 * 父类
 *
 * @Author: John Smith
 * @Date: 2022/4/26
 */
@Data
public class Person {

    public Person(){}

    public Person(String name){
        this.name = name;
    }

    String name;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 子类
 *
 * @Author: John Smith
 * @Date: 2022/4/26
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
class User extends Person{
    private int id;
    private int age;
}

 

1.基本用法

1.1 获取Class的几种方式

1.1.1 获取类的对象

        // 方式一
        // 这种方式不管获取几次都是同一个对象,可以通过打印类的hashCode来判断
        // 一个类在内存中只有一个class对象,一个类被加载后整个类的结构全部存在这个class当中
        Class<?> c1 = Class.forName("org.example.spring.reflection.User");

        // 方式二
        User user = new User();
        Class c2 = user.getClass();

        // 方式三
        Class<User> c3 = User.class;

        // 方式四(只有内置类型包装类有)
        Class<Integer> type = Integer.TYPE;

1.1.2 获取父类对象 

Class<?> superclass = c1.getSuperclass();
System.out.println(superclass);

1.2 获取类的信息

1.2.1 获取类的名称

        // 获取类全限定名
        System.out.println(c1.getName());
        // 获取简单类名
        System.out.println(c1.getSimpleName());

 1.2.2 获取类的属性

        // 类的属性(这个没办法获取到)这种方式只能找到public
        Field[] fields = c1.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        // 获得类的属性(可以找到所有的)
        Field[] declaredFields = c1.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        // 获取类的指定属性(同样,不加declared的话只能获取到public修饰的属性)
        System.out.println(c1.getDeclaredField("age"));

下面是用了getDeclaredFields才获取出来的: 

1.2.3 获取类的方法

        // 获取方法(获取所有的方法 包括父类的一些方法 全是public的)
        Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println("没加declare:" + method);
        }

        // 获取方法(只获得本类的 private的也可以)
        Method[] declaredMethods = c1.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println("加declare:" + method);
        }

 获取类的指定方法:

        /*
        获得指定方法 第一个属性为方法名称 第二个属性为方法的入参
        getAge方法由于没有入参所以null可写可不写
        在方法重载的时候,第二个属性就很重要了,尽量都写上
         */
        Method setAge = c1.getMethod("setAge", int.class);
        Method getAge = c1.getMethod("getAge", null);
        System.out.println(setAge);
        System.out.println(getAge);

 1.2.4 获取类的构造器

        // 获取所有public的构造器 但不会获得父类的
        Constructor<?>[] constructors = c1.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("不加declared:" + constructor);
        }

        // 获取本类的全部构造器
        Constructor<?>[] declaredConstructors = c1.getDeclaredConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
            System.out.println("加了declare" + constructor);
        }


        // 获得指定构造器 入参填构造器的入参
        Constructor<?> constructor = c1.getConstructor(int.class, int.class);
        System.out.println("指定构造器:" + constructor);

 

1.3 通过获取类的构造器来实例化对象

1.3.1 通过无参构造器创建对象

        Class<?> c1 = Class.forName("org.example.spring.reflection.User");
        // 这种方式创建对象是调用了无参构造器 如果没有的话可能创建不了
        User user = (User)c1.newInstance();
        System.out.println(user);

 

1.3.2 通过有参构造器创建对象

        // 获取到有参构造器
        Constructor<?> declaredConstructor = c1.getDeclaredConstructor(int.class, int.class);
        // 通过该构造器,传入参数并实例化对象(强转一下)
        User u2 = (User) declaredConstructor.newInstance(1, 1);
        System.out.println(u2);

 

 

1.4 通过反射调用方法

复用1.3.1中创建的Class对象和User对象

        // 获取到普通方法,后面传入方法名和该方法的入参
        Method setAge = c1.getDeclaredMethod("setAge", int.class);
        // invoke是执行该方法,传入的是:1.调用该方法的对象 2.该方法的入参
        setAge.invoke(user, 20);
        System.out.println(user);

 

 

1.5 通过反射操作属性

复用1.3.1中创建的Class对象和User对象

会报错:

        // 按照名称得到属性
        Field id = c1.getDeclaredField("id");
        // 下面直接执行的话无法成功,因为类里的id属性为private
        // 设置属性值,传入的是:1.持有该属性的对象 2.给属性赋的值
        id.set(user,2);
        System.out.println(user.getId());

 正确用法:

        // 按照名称得到属性
        Field id = c1.getDeclaredField("id");
        // 关掉id属性的权限校验,让反射可以访问private属性
        // method、constructor、field 都有setAccessible方法,都可以设置访问权限
        id.setAccessible(true);
        id.set(user, 2);
        System.out.println(user.getId());

 

1.6 通过反射获取泛型

前提:在一个类中包含如下两个方法,分别是入参和出参含有泛型。

    public void m1(Map<String, User> map, List<User> list){
        System.out.println("m1");
    }

    public Map<String,User> m2() {
        System.out.println("m2");
        return null;
    }
    public static void main(String[] args) throws Exception {
        // 获取指定方法
        Method m1 = TestMain3.class.getMethod("m1", Map.class, List.class);
        // 获取该方法的泛型参数信息
        Type[] genericParameterTypes = m1.getGenericParameterTypes();
        // 循环该参数泛型数组
        for (Type genericParameterType : genericParameterTypes) {
            // 输出
            System.out.println("m1泛型类型" + genericParameterType);
            // 如果泛型属于是“参数化类型”
            if (genericParameterType instanceof ParameterizedType) {
                // 强转成参数化类型并获取他的真实类型
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                // 循环该真实类型
                for (Type actualTypeArgument : actualTypeArguments) {
                    // 输出泛型参数的真实类型
                    System.out.println("m1泛型真实类型" + actualTypeArgument);
                }
            }
        }

        // 得到m2方法
        Method m2 = TestMain3.class.getMethod("m2", null);
        // 得到m2方法的返回值泛型
        Type genericReturnType = m2.getGenericReturnType();
        // 如果泛型属于是“参数化类型”
        if (genericReturnType instanceof ParameterizedType) {
            // // 强转成参数化类型并获取他的真实类型
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            // 循环并输出
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("m2: "+actualTypeArgument);
            }
        }

    }

 

1.7 通过反射获取注解

定义两个注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableZ {
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldZ {
    String colName();
    String type();
    int length();
}

定义实体类及属性,并打上自定义注解:

@AllArgsConstructor
@NoArgsConstructor
@Data
@TableZ("db_student")
class Student {
    @FieldZ(colName = "db_id", type = "int", length = 10)
    private int id;
    @FieldZ(colName = "db_colName", type = "int", length = 10)
    private int age;
    @FieldZ(colName = "db_name", type = "String", length = 3)
    private String name;
}

测试:

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<?> c1 = Class.forName("org.example.spring.reflection.Student");
        // 获取类的注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("student类的注解为:" + annotation);
        }
        // 获得类的注解的值
        TableZ tableZ = c1.getAnnotation(TableZ.class);
        System.out.println("student类的注解的value值为:" + tableZ.value());

        // 获得类指定的注解
        Field field = c1.getDeclaredField("name");
        FieldZ annotation = field.getAnnotation(FieldZ.class);
        System.out.println("student类的name属性的注解的colName值为:" + annotation.colName());
        System.out.println("student类的name属性的注解的length值为:" + annotation.length());
        System.out.println("student类的name属性的注解的type值为:" + annotation.type());
    }

 

三、类的加载顺序(题外)

有如下类:

class Temp {

    static int a = 100;

    static {
        a = 300;
    }
}

 那么a的结果到底为多少?

a的结果为300。

实践证明,静态代码块和属性赋值,哪个写在前面便先执行哪个,所以后面的静态代码块中的代码后执行会覆盖前面的,故a = 300


 

总结

个人简单记录下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值