Java反射、注解、枚举

1.反射

一、什么是反射?

Java反射是Java被视为动态语言的很重要的一个特性。
Java通过反射的Api可以做到获取运行时类的任何信息。包括其类、属性、方法、注解、父类等任何信息。并且可以直接操作任意对象的内部属性及方法。

二、反射能用来做什么?

  1. 在运行时判断任意一个对象所属的类。
  2. 在运行时构造任意一个类的对象。
  3. 在运行时判断任意一个类所具有的成员变量和方法。
  4. 在运行时获取泛型信息。
  5. 在运行时调用任意一个对象成员变量和方法。
  6. 在运行时处理注解。
  7. 生成动态代理。

三、获取class实例的方式。

获取Class实例的方式

  • 方式一:调用运行时类的属性。 .class Class<Person> clazz1 = Person.class;
  • 方式二:通过运行时类的对象 调用getClass这个方法 Class<? extends Person> aClass = person.getClass();
  • 方式三:调用Class的静态方法 Class.forName(String classPath)
  • 方式四:使用类的加载器:ClassLoader

Class的实例就对应着一个运行时类。 加载到内存中的运行时类,会缓存一定的时间,在此时间之内,我们可以通过不同的方式来获取此运行时类。

四、通过反射创建运行时类的对象。

newInstance():调用此方法创建对应的运行时类的对象。内部调用了运行时类的空参构造器。

要想此方法能够正常的创建运行时类的对象,要求:

  1. 运行时类必须提供空参构造器。
  2. 空参构造器的访问权限得够(通常设置为public的)

五、类加载器

系统类加载器:加载自己创建的一些类。
扩展类加载器:加载Java的lib下得扩展类。
引导类加载器:主要负责加载java的核心类库,无法加载自定义类的。

六、随机创建运行时类对象,体验动态性

	@Test
    public void testNewInstance1() throws Exception {

        int num = new Random().nextInt(3);
        String classPath = "";

        switch (num) {
            case 0: classPath = "java.util.Date"; break;
            case 1: classPath = "java.lang.Object"; break;
            case 2: classPath = "com.starcpdk.Person"; break;
        }

        Object obj = getInstance(classPath);
        System.out.println(obj);


    }

    /**
     * 创建一个指定类的对象
     * @param classPath 指定类的全类名
     * @return
     * @throws Exception
     */
    public Object getInstance(String classPath) throws Exception {
        Class<?> clazz = Class.forName(classPath);
        return clazz.newInstance();

    }

七、创建对象的几种方式

  1. new + 构造器
  2. 调用方法获取实例。XXX、XXXs、XXXFactory、XXXBuilder等。
  3. clone方法克隆对象。
  4. 反射方式创建对象。调用newInstance()使用类或者构造器调用。

八、clazz获取一些结构。

  1. 获取构造器。
// 获取当前运行时类中声明为public的构造器
Constructor<?>[] constructors = personClass.getConstructors();
// 获取当前运行时类的所有构造器
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
  1. 获取运行时类的父类
// 获取运行时类的父类
Class<? super Person> superclass = personClass.getSuperclass();

// 获取运行时类的带泛型的父类
Type genericSuperclass = personClass.getGenericSuperclass();

// 获取运行时类的带泛型的父类
Type genericSuperclass = personClass.getGenericSuperclass();
// 获取父类的泛型类型
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
System.out.println(((Class)actualTypeArguments[0]).getName());
  1. 获取运行时类实现的接口
// 获取运行时类实现的接口
Class<?>[] interfaces = personClass.getInterfaces();
// 获取运行时类的父类实现的接口。
Class<?>[] interfaces1 = personClass.getSuperclass().getInterfaces();
  1. 获取当前运行时类 所在的包
Package aPackage = personClass.getPackage();
  1. 获取运行时类 声明的注解
Annotation[] annotations = personClass.getAnnotations();
  1. 获取运行时类的方法
// 获取当前运行时类及其所以父类中声明为public的所有方法
Method[] methods = personClass.getMethods();
// 获取当前运行时类中的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = personClass.getDeclaredMethods();

@Test
public void test2() throws Exception {
    Class<Person> personClass = Person.class;
    Method[] declaredMethods = personClass.getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {

        // 4. 获取方法名
        System.out.print("方法名是:" + declaredMethod.getName() + ":\t");

        // 只能获取Runtime的生命周期的注解
        // 1. 获取方法声明的注解
        Annotation[] annotations = declaredMethod.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.print("注解是:" + annotation + "\t");
        }

        // 2、获取权限修饰符
        int modifiers = declaredMethod.getModifiers();
        System.out.print("权限修饰符是:" + Modifier.toString(modifiers) + "\t");

        // 3.获取返回值类型
        Class<?> returnType = declaredMethod.getReturnType();
        System.out.print("返回值类型是:" + returnType.getName() + "\t");

        // 5. 获取形参列表
        Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
        if (!(parameterTypes == null || parameterTypes.length == 0)) {
            for (Class<?> parameterType : parameterTypes) {
                System.out.print("形参类型:" + parameterType.getName() + " args_" + " ");
            }
        }

        // 6. 抛出的异常
        Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();
        for (Class<?> exceptionType : exceptionTypes) {
            System.out.print("异常:" + exceptionType.getName());
        }

        System.out.println();
    }
}
  1. 获取运行时类的属性结构
// getFields() :获取当前运行时类及其父类声明为public访问权限的属性
Field[] fields = personClass.getFields();
// getDeclaredFields():获取当前运行时类中所有的属性。(不包含父类的属性)
Field[] declaredFields = personClass.getDeclaredFields();

// 权限修饰符 数据类型  变量名
@Test
public void test1() {

    Class<Person> personClass = Person.class;
    Field[] declaredFields = personClass.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        // 权限修饰符
        int modifiers = declaredField.getModifiers();
//            System.out.println(modifiers);
        System.out.print(Modifier.toString(modifiers) + "\t");

        // 数据类型
        Class<?> type = declaredField.getType();
        System.out.print(type.getName() + "\t");

        // 变量名
        String name = declaredField.getName();
        System.out.println(name);
    }

}
  1. 使用运行时类中的一些属性或方法
package com.starcpdk.java2;

/**
 * @Author 姚云峰
 * @Email
 * @Date 2022/10/27 14:02
 * @Version 1.0
 */

import com.starcpdk.test.Person;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 调用运行类中指定的结构:属性、方法、构造器
 */
public class ReflectionTest {

    /**
     * 获取
     */
    @Test
    public void testField() throws Exception {

        // 获取指定的属性
        Class<Person> personClass = Person.class;

        // 创建运行时类的对象。
        Person person = personClass.newInstance();

        // 获取指定属性 , 要求运行时类中的属性声明为public的。
        // 通常不采用此方式
        Field id = personClass.getField("id");

        // 设置当前属性的值
        // 参数1:指明设置哪儿个对象的属性, 参数2:指明将此属性值设置为多少
        id.set(person , 1234);

        // 获取当前属性的值
        // 参数1:获取哪儿个对象的当前属性的值
        int pId = (int) id.get(person);
        System.out.println(pId);
    }


    /**
     * 操作运行时类中的属性。
     * @throws Exception
     */
    @Test
    public void testField1() throws Exception {
        Class<Person> personClass = Person.class;

        // 创建运行时类的对象。
        Person person = personClass.newInstance();

        // 获取指定变量名的属性
        Field age = personClass.getDeclaredField("age");
        // 保证当前属性是可访问的。
        age.setAccessible(true);
        // 获取、设置指定对象的此属性的值。
        age.set(person , 12);

        Object o = age.get(person);
        System.out.println(o);
    }

    /**
     * 操作运行时类中的方法
     * 参数1:指明获取方法的名称。
     * 参数2:指明获取方法的形参列表。
     */
    @Test
    public void testMethod() throws Exception {
        Class<Person> personClass = Person.class;
        Person person = personClass.newInstance();

        Method show = personClass.getDeclaredMethod("show", String.class);
        /**
         * invoke()
         * 参数1:方法的调用者。
         * 参数2:给方法形参赋值的实参。
         * invoke()的返回值就是对应类中调用的方法的返回值。
         */
        show.setAccessible(true);
        Object hahah = show.invoke(person, "哈哈哈哈");
        System.out.println(hahah);


        Method showDesc = personClass.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        // 如果调用的方法没有=返回值 , 则返回null
        Object invoke = showDesc.invoke(Person.class);
        System.out.println(invoke);

    }

    /**
     * 调用运行时类中指定的构造器。
     *
     * 参数:指明构造器的参数列表
     *
     * @throws Exception
     */
    @Test
    public void testConstructor() throws Exception {
        Class<Person> personClass = Person.class;
        Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
        Person tom = declaredConstructor.newInstance("Tom");

        System.out.println(tom);

    }
}

2. 枚举

一、什么是枚举

类中对象的个数是确定的,有限个。
枚举类中的属性的修饰符是 private final 的。
枚举类中的对象的修饰符是 public static final 的。

二、枚举类常用方法

  • values(): 返回一个数组,即枚举类中的所有对象
  • valueOf(String str) 找指定名的对象 , 如果提供的str,在枚举类对象中没有,则抛异常。IllegalArgumentException
  • toString() 默认返回枚举类对象名称

三、例子

如何自定义枚举

/**
 * @Author 姚云峰
 * @Email
 * @Date 2022/8/20 16:02
 * @Version 1.0
 * <p>
 * <p>
 * 枚举类的使用
 */
public class SeasonTest {

}

/**
 * 自定义枚举实现
 */
class Season {

    private final String seasonName;
    private final String seasonDescription;

    private Season(String seasonName, String seasonDescription) {
        this.seasonName = seasonName;
        this.seasonDescription = seasonDescription;
    }

    public static final Season SPRING = new Season("春天" , "春暖花开");
    public static final Season SUMMER = new Season("夏天" , "夏日炎炎");
    public static final Season AUTUMN = new Season("秋天" , "秋高气爽");
    public static final Season WINTER = new Season("冬天" , "冰天雪地");

}
/**
 * @Author 姚云峰
 * @Email
 * @Date 2022/8/20 16:16
 * @Version 1.0
 *
 * 使用enum关键字创建枚举类
 *
 * 常用方法:
 *  values(): 返回一个数组,即枚举类中的所有对象
 *  valueOf(String str)  找指定名的对象 , 如果提供的str,在枚举类对象中没有,则抛异常。IllegalArgumentException
 *  toString()   默认返回枚举类对象名称
 *
 *
 */
public class SeasonTestEnum {
    public static void main(String[] args) {
        System.out.println(Season1.SPRING);
    }
}

interface Info{
    void show();
}

enum枚举类。

// 枚举类实现接口。
// 可以统一重写接口方法
// 也可以每个枚举值都重写接口方法
enum Season1 implements Info{


    // 提供当前类枚举对象
    SPRING("春天","春暖花开"){
        @Override
        public void show() {
            System.out.println("春天在哪儿里");
        }
    },
    SUMMER("夏天","夏日炎炎") {
        @Override
        public void show() {
            System.out.println("夏宁");
        }
    },
    AUTUMN("秋天","秋高气爽") {
        @Override
        public void show() {
            System.out.println("秋天不回来");
        }
    },
    WINTER("冬天","冰天雪地") {
        @Override
        public void show() {
            System.out.println("大约在冬季");
        }
    };

    private final String seasonName;
    private final String seasonDescription;

    private Season1(String seasonName, String seasonDescription) {
        this.seasonName = seasonName;
        this.seasonDescription = seasonDescription;
    }

    // @Override
    // public void show() {
    //     System.out.println("这是一个季节。。。。。。");
    // }
}

注解

一、什么是注解,怎么定义

使用@interface关键字声明。
内部定义成员,通常使用value表示
可以指定成员的默认值,使用default定义默认值。
如果自定义的注解没有成员,表明是一个标识作用。
如果注解有成员,使用注解时需要指明成员的值。
自定义注解必须配上注解的信息处理流程(反射的方式)才有意义。
自定义注解通常都会指明两个元注解,即:@Retention \ @Target。

@Repeatable(MyAnnotations.class)
//@Inherited // 可继承注解。
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE , TYPE_PARAMETER , TYPE_USE})
public @interface MyAnnotation {
    String value() default "world";
}

二、什么是元注解

元注解:对现有注解进行解释说明的注解。
@Retention:指定所修饰的Annotation的声明周期: SOURCE\CLASS(默认行为)\RUNTIME
只有声明为RUNTIME声明的注解,才能通过反射获取。
@Target:用于指定被修饰的Annotation能用于修饰哪儿些程序元素。
@Documented:表示所修饰的注解在被javadoc解析时,保留下来。
@Inherited:具有继承性,如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

通过反射获取注解信息:

Class<Student> studentClass = Student.class;
Annotation[] annotations = studentClass.getAnnotations();
  • 可重复注解、类型注解

    • 可重复注解:
      • MyAnnotation上声明@Repeatable,成员值为MyAnnotations.class
      • MyAnnotation的Target和Retention和MyAnnotations相同。
  • 类型注解:

    • ElementType.TYPE_PARAMETER : 表示该注解能写在类型变量的声明语句中(如:泛型声明)
    • ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中。

三、注解使用场景

  1. 生成文档相关注解
  2. 在编译时进行格式检查
    @Override:限定重写父类方法,该注解只能用于方法
    @Deprecate:用于标识锁修饰的元素(类、方法)已过时。通常是因为锁修饰的结构危险或存在更好的选择。
    @SuppressWarnings:抑制编译器警告。

四、例子

@Repeatable(MyAnnotations.class)
//@Inherited // 可继承注解。
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE , TYPE_PARAMETER , TYPE_USE})
public @interface MyAnnotation {
    String value() default "world";
}
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;

@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
public @interface MyAnnotations {
    MyAnnotation[] value();
}
import org.junit.Test;

import java.lang.annotation.Annotation;
import java.util.ArrayList;


/**
 *
 */
public class AnnotationTest {
    @Test
    public void testAnnotation() {
        Class<Student> studentClass = Student.class;
        Annotation[] annotations = studentClass.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }
}

@MyAnnotation(value = "hello")
@MyAnnotation(value = "abc")
// jdk8之前使用重复注解的方式
//@MyAnnotations({@MyAnnotation(value = "hello") , @MyAnnotation(value = "abc")})


class Person{
    private String name;
    private int age;

    public Person() {
    }

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

    public void walk(){
        System.out.println("人走路");
    }
}

class Student extends Person {
    @Override
    public void walk() {
        super.walk();
    }
}

class Generic<@MyAnnotation T>{
    public void show() throws @MyAnnotation RuntimeException{
        ArrayList<@MyAnnotation String> objects = new ArrayList<>();
        int num = (@MyAnnotation int)10L;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值