1.枚举(Enum)

1.1概述

1.1.1使用背景

类的对象只有有限个,确定的 举例:

  • 星期 : 周一 周二 . . .
  • 性别 : 男 女
  • 季节 : 春 夏 秋 冬
  • 线程状态: 创建 就绪 运行 阻塞 死亡

1.1.2使用

  • 理解:类的对象只有有限个,确定的
  • 当需要定义一组常量时,强烈建议使用枚举类
  • 如果枚举类中只有一个对象,则可以作为单例模式的实现方式

1.1.3JDK5之前创建枚举类

public class Season {

    //1.私有化类的构造器 保证了类的对象在外面不能造
    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    //属性 定义为私有常量
    final private String name;
    final private String desc;

    //3.提供当前枚举类的多个对象  public static final 全局常量
    // 符合创建 一组常量
    public static final Season SPRING = new Season("春天", "春暖花开");
    public static final Season SUMER = new Season("夏天", "夏日炎炎");
    public static final Season AUTUMN = new Season("秋天", "秋高气爽");
    public static final Season WINTER = new Season("冬天", "冰天雪地");

    //提供get()方法
    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

1.1.4JDK5后使用enum关键字创建

  • 原理就是基于JDK1.5之前手动的
  • 使用enum创建枚举类时,其父类默认是java.lang.Enum
  • 类内部的枚举类默认是static的
//父类是 java.lang.Enum
public enum SeasonE {
    //直接定义对象的变量 必须写在最前面 变量之间用逗号分割 最后一个使用分号
    // 相当于
    // private static final SeasonE SPRING = new SeasonE("春天", "春暖花开")
    // 如果没有属性的话 直接写常量名即可 (参考Thread类内部的Statu)
    SPRING("春天", "春暖花开"),
    SUNMER("夏天", "夏日炎炎");

    // 构造器 默认是私有的 不能加除paivate权限外的其他权限
    SeasonE(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    //属性 private不能省
    private String name;
    private String desc;
	
    //提供get方法  不提供set方法
    public String getName() {
        return name;
    }
    
    public String getDesc() {
        return desc;
    }
}

1.1.5java.lang.Enum类的主要方法

java获取注解里边内容 java通过注解获取枚举值_数组

1.1.5.1主要方法
  • toString()
//返回当前枚举类对象常量的名称  
Thread.State aNew = Thread.State.NEW;
String s = aNew.toString();
输出:
//   NEW
  • values()
//返回枚举类的所有对象 一个数组
Thread.State[] values = Thread.State.values();
for (int i = 0; i < values.length; i++) {
    System.out.println(values[i]);
}
输出:
//        NEW
//        RUNNABLE
//        BLOCKED
//        WAITING
//        TIMED_WAITING
//        TERMINATED
  • valueOf(String str)
// 根据提供的名称 返回内部的常量对象 如果不存在 抛出异常 IllegalArgumentException非法参数异常
Thread.State blocked  = Thread.State.valueOf("BLOCKED");
System.out.println(blocked);

输出:
//   BLOCKED

1.1.6实现接口

1.1.6.1普通实现

枚举类本质也是一个类,实现接口正常重写方法即可

enum OrderStatus implements InterfaceTest{

    NEW(0,"新建"),
    COMMITTED(1,"已提交订单"),
    HASPAY(2,"已付款"),
    HASSEND(3,"已发货"),
    HASGET(4,"确认收货"),
    HASFINASH(5,"订单已完成");
    
    private int code;
    private String msg;

    OrderStatus(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
	
   //正常重写
    @Override
    public void show() {
        System.out.println("这是一个订单状态的枚举类");
    }
}


interface InterfaceTest{
    void show();
}
1.1.6.2对象分别实现
enum  OrderStatus implements InterfaceTest{
	
    //如果想要实现的逻辑不一样 在对象后直接重写 
    NEW(0,"新建"){
        @Override
        public void show() {
            
        }
    },
    COMMITTED(1,"已提交订单"){
        @Override
        public void show() {
            
        }
    },
    HASPAY(2,"已付款"){
        @Override
        public void show() {
            
        }
    },
    HASSEND(3,"已发货"){
        @Override
        public void show() {
            
        }
    },
    HASGET(4,"确认收货"){
        @Override
        public void show() {
            
        }
    },
    HASFINASH(5,"订单已完成"){
        @Override
        public void show() {
            
        }
    };
   
    private int code;
    private String msg;
    OrderStatus(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

}

interface InterfaceTest{
    void show();
}

2.注解(Annotation)

2.1注解概述

  • JDK5.0的新特性
  • 注解(Annotation)其实就是代码里的特殊标记,这些标记可以在编译期类加载运行时被读取,并执行相应的处理,
  • 注解可以像修饰符一样被使用,可修饰 构造器方法属性参数局部变量
  • 注解一般配合反射使用,在运行期通过反射读取注解,然后进行相应的操作
  • 注解的本质就是接口,默认的注解都继承java.lang.Annotation接口,所以在使用的时候都是接口的实现类

2.2JDK中的元注解&常见的注解

2.2.1JDK的四种元注解

  • 元注解用于修饰其他注解的定义
  • JDK5提供了4个标准的元注解,
@Retention
  • (英文意思是保留) `表示注解的生命周期
  • 源码 只有一个属性 是一个枚举类RetentionPolicy,里面的三个属性代表生命周期的三个状态
public @interface Retention {
    RetentionPolicy value();
}

public enum RetentionPolicy {
    SOURCE,
    CLASS, //默认
    RUNTIME
}
  • 注解的生命周期有三种 默认是CLASS,即只在编译器有效

java获取注解里边内容 java通过注解获取枚举值_数组_02

@Target
  • 表示注解可以修饰的结构
  • 源码 只有一个属性 是一个ElementType枚举类型的数组,这个类里面定义了注解可以修饰的结构
public @interface Target {
    ElementType[] value();
}
public enum ElementType {
    /* 类 接口 枚举类*/
    TYPE,
    
    /*属性*/
    FIELD,
    
    /*方法*/
    METHOD,
    
    /*参数*/
    PARAMETER,
    
	/*构造器*/
    CONSTRUCTOR,
    
     /*局部变量*/
    LOCAL_VARIABLE,
    
	/*注解*/
    ANNOTATION_TYPE,
    
    /*包*/
    PACKAGE,
    
    /*JDK8新特性 表示注解可以标注在类型变量的声明语句中 (如泛型声明)**/
    TYPE_PARAMETER,
    
    /*JDK8新特性 表示注解可以标注在使用类型的任何语句中**/
    TYPE_USE
}
@Documented
  • 用于指定被该元注解修饰的注解类将会被javadoc工具提取为文档,默认情况下,javadoc(API文档)不包括注解
  • 定义了@Documented的注解的生命周期的值(@Retention)必须指定为RUNTIME
@Inherited

表示被它修饰的注解具有继承性

/*意思就是:
    当我的自定义注解加了@Inherited元注解时,我的Dad类加了这个自定义注解,那么默认Dad的子类Son也有自定义注解,反之子类就没有这个注解*/

@MyTestInter("h1")
class Dad{
    
}

class Son extends Dad{
    
}

//自定义注解
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTestInter{
   
    String value() default "sds";
    
}

2.2.2JDK内置的三个编译期注解

  • @Override :修饰方法 限定方法的重写 (类和接口)
  • @Deprecated : 修饰类 方法 属性等… 表示已过时 通常是因为所修饰的结构危险或者存在更好的选择
  • @SuppressWarnings : 抑制编译器警告

2.3自定义注解

  • 自定义注解一般都会写@Retention@Target指明生命周期和修饰的结构
  • 使用@interface定义注解 所有的注解默认继承java.lang.annotation.Annotation接口
  • 定义成员变量要加括号,要使用默认值的后面加上default关键字指定默认值,在使用注解时没有默认值的属性必须赋值加了默认值的属性可以不赋值
  • 属性的类型可以是简单类型、String、简单类型/String数组、Class类型、枚举、枚举数组类不可以是自定义的引用类型(如Person List类等)
  • 使用属性时:(属性名=类型的值,属性名=类型的值…)
    1、如果属性只有一个,推荐属性名使用value,因为在赋值的时候value=可以省略
    2、如果一个属性是一个数组且只赋一个值,那么大括号可以省略

基本使用

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.TYPE_PARAMETER})
public @interface MyAnnotation {

    String[] value(); //String类型的数组
    int index();  //简单类型
    Class[] clazz(); //Class类型的数组
    Tenum[] TENUMS(); //枚举类型的数组
    TAnnotation T_ANNOTATION(); //注解类型的数组
}

------ 使用
@MyAnnotation(value = {"test1","wsh"},index = 1,clazz = Calendar.class,TENUMS = Tenum.TESTF,T_ANNOTATION = @TAnnotation("test"))
public class Person {

}

2.4利用反射获取注解

2.5JDK8注解新特性

2.5.1可重复注解

2.5.1.1JDK8之前
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ReAnnotation {
    String value();
}

--------------------------------------
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ReAnnotationImpl {
    ReAnnotation[] RE_ANNOTATIONS();
}

-----------------------------------
//在JDK8之前如果我们想要在一个结构上定义两个相同的注解,必须重新定义一个注解 里面的属性是一个数组
 @ReAnnotationImpl(RE_ANNOTATIONS{@ReAnnotation("test1"),@ReAnnotation("test2")})
public class Person {

}
2.5.1.1JDK8之后
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ReAnnotationImpl.class)
public @interface ReAnnotation {
    String value();
}

-----------------------------------------------------
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReAnnotationImpl {
    ReAnnotation[] value(); //死格式 属性名必须是value 
}

--------------------------------------------------
JDK8新特性,只需在注解的元注解上标注 @Repeatable(ReAnnotationImpl.class)即可 里面的值里面必须有一个属性是原注解的数组  并且生命周期作用位置必须一致 
@ReAnnotation("var1")
@ReAnnotation("var2")
public class Person {

}

2.5.2类型注解(JDK8)

// ElementType.TYPE_PARAMETER 可以标注在泛型上
//ElementType.TYPE_USE 标注在任意的数据类型上

@Target({ElementType.TYPE_PARAMETER,ElementType.TYPE_USE})
public @interface MyAnnoType {
    String value();
}
-----------------------------------------

public class Test<@MyAnnoType("ss")T> {

    public static void main(@MyAnnoType("sd") String[] args) {

        ArrayList<@MyAnnoType("ss")String> list = new ArrayList<>();

        @MyAnnoType("sd") int a = 1;

        @MyAnnoType("as") String str = "1";
        
    }
}

3.反射

3.1概述

特征: 动态性 在运行时加载、探知、使用编译期完全未知的类

通过获取一个类的Class实例,进而获取类的各种信息,然后去创建对象调用方法等。。。

作用:

主要用于Spring中的IOC工厂,创建出不同的对象,核心原理就是读取配置文件,然后根据反射创建出指定的对象,并设置其属性

3.2反射相关的API

  • java.lang.Class :代表一个类
  • java.lang.reflect.Method :代表类的方法
  • java.lang.reflect.Field : 代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器
  • java.lang.annotation接口

代表着反射的基本思路,我们先去获取一个类的Class对象,然后通过Class对象去获取它的构造器,方法,属性,然后去创建对象调用方法。。。

3.2.1Class类

  • 在类的加载过程中,.java文件变为.class文件,然后class文件通过类的加载器加载到JVM中去,就生成一个Class类的一个实例,这个就是大的Class对象,所以说这个大的Class对象在内存中只有一个,因为类只会加载一次
  • Class类的实例称为类对象
  • 哪些类型可以有Class对象
    外部类 成员(成员内部类,静态内部类) 局部内部类 匿名内部类接口 数组 枚举 注解 基本数据类型 void(int.class String.class … )
  • 只要数组的元素类型与维度一样,就是同一个Class对象
int[] a= new int[100];
 int[] b= new int[100];
//a.getClass == b.getClass 是true 都是int型数组 都是一维的
  • 这个Class对象里面的API可以获取到这个类的各种结构,所以说反射的第一步就是获取一个类的Class的对象
3.2.1.1获得Class对象(类对象)的四种方式

1.通过类的属性 .class

Class clazz1 = Person.class;

2.通过一个对象的getClass方法 (Object类中的方法)

Person person = new Person();
Class clazz2 = person.getClass();
//经常用于 判断这个对象所属的类

3.通过Class类的方法 forName()方法 常用

Class clazz3 = Class.forName("全类名");

4.通过类的加载器

//注意 我们自定义的类使用的基本都是 System Class Loader 系统类加载器
//所以可以借用我们自定义的其他类的类加载器去加载类 生成一个Class对象
ClassLoader classLoader = Test.class.getClassLoader();
Class class4 = classLoader.loadClass("反射.Person");
3.2.1.2Class类的方法
• getName() 获得全类名
• getSimpleName() 获得类名
• getPackage() 返回类的包对象
• getFields/Methods返回本类和父类中声明为public的属性和方法
• newInstance() 创建指定类的对象 ,调用的是类的无参构造器
• getDeclaredField(String fieldname) 通过属性名 获得属性对象
• getDeclaredFields() 只能获取本类中所有的属性,返回一个Field数组
• getDeclaredMethod(String methodName, Class ... argType) 根据方法名、参数列表获得一个方法对象
• getDeclaredMethods() 获得类中所有的方法
• getDeclaredConstructor(Class ... argType) 根据构造器的参数列表获得一个构造器对象
• getDeclaredConstructors() 获得本类中的所有的构造器,返回Constructor数组
• getClassLoader() 获取当前类的类加载器
• isXXX() 判断当前的Class对象的类型是否是某个类型
• toString() 输出当前Class对象的类型信息 + 全类名
• isAnnotationPresent(Class annotationClass)判断某个类对象是否含有指定的注解
3.2.1.3Annotaion接口中的的方法
  • toString() 返回注解的全类名和值
  • annotationType() 返回当前注解实现的接口的类对象,这个才能获取注解类对象类型
  • 注意:调用getClass返回的是当前对象的类对象,因为注解都是接口,运行时都是实现类,使用的代理模式,所以调用这个getClass获取的是代理类的Class对象
for (Annotation annotation : declaredAnnotations) { 
//@反射.通过反射获取类结构.AnnotMe(value=Field_sonAge, index=1, STATE=NEW)
    System.out.println(annotation.toString());
    System.out.println(annotation.annotationType.getName);
  if (annotation.annotationType().getName() == "指定的注解全名"){
         AnnotMe annotMe = (AnnotMe) annotation; //强转
         System.out.println(annotMe.value()); //得到注解里的某一属性值
   			....
}
3.2.1.4Field类的常用方法
for (Field field : declaredFields) {
    
String s1 = field.toString();//获取整个定义 所有修饰符+类型+全类名.属性名
    
String name = field.getName();//获取属性名
    
Class<?> type = field.getType();//获取属性的类型 返回一个Class类型
    
int modifiers = field.getModifiers(); //获取属性的修饰符 返回int值 调用Modifier.toString()获取权限修饰符 转换为
String s = Modifier.toString(modifiers); //
    
Annotation[] annotations = field.getDeclaredAnnotations(); //获取属性上的所有注解 返回一个集合
    
field.isAnnotationPresent(Class class) //判断属性上是否含有某一个注解
3.2.1.5Method类的常用方法
Class<Son> clazzSon = Son.class;
Method[] methods = clazzSon.getDeclaredMethods();
for (Method method : methods) {
   // Object invoke = method.invoke(, ); 为某个对象执行方法
--    
    String s = method.toString(); //获取方法的整个声明
--    
    System.out.println(method.getName()); //只获取方法名
--    
    Annotation[] declaredAnnotations = method.getDeclaredAnnotations(); //获取方法上的所有注解
--    
    Class<?>[] parameterTypes = method.getParameterTypes(); //获取方法的参数
--   
    int modifiers = method.getModifiers(); // 获取方法的所有修饰符
    System.out.println(Modifier.toString(modifiers));
--    
    Class<?>[] exceptionTypes = method.getExceptionTypes(); //获取方法声明中抛出的所有异常
--    
    Class<?> returnType = method.getReturnType(); //获取方法的返回值
3.2.1.6Constructor类的常用方法

3.3通过反射获取运行时类的完整结构

3.3.1获取类的父类接口以及泛型
Class<Son> clazzPerson = Son.class;
//获取类的父类
Class<? super Son> superclass = clazzPerson.getSuperclass(); 

String s = superclass.toString();// 输出 类型信息 :全类名

String simpleName = superclass.getSimpleName();// 只输出类名

String name = superclass.getName(); //全类名

-------------获取继承父类时的泛型
Type gSuperClass = sonClass.getGenericSuperclass();
ParameterizedType pgSuperClass = (ParameterizedType)gSuperClass; 
//获取泛型数组
Type[] actualTypeArguments = pgSuperClass.getActualTypeArguments();、

------获取实现接口时的数组
//Class类实现了Type接口 获取带泛型的接口 获取了泛型的接口的类对象
Type[] genericInterfaces = sonClass.getGenericInterfaces();
 for (Type type : genericInterfaces) {
  //ParameterizedType接口 继承Type接口 强转为ParameterizedType类型
ParameterizedType type1 = (ParameterizedType) type;
   //得到泛型 是一个数组
 Type[] actualTypeArguments1 = type1.getActualTypeArguments();
     for (Type argument : actualTypeArguments1) {
       System.out.println(argument.getTypeName());
     }
3.3.2获取类实现的所有接口
3.3.3获取类上的所有注解
3.3.4获取类的所有属性及所有定义
3.3.5获取类的所有构造器
3.3.6获取类的所有方法及方法上的所有注解

3.4通过反射创建运行时类的对象

3.3.1直接使用Class对象的newInstance()方法创建对象(最常用)

-- 要求
// 1.运行时类必须提供空参构造器
// 2.构造器满足权限 通常是public
Class clazzDog= Class.forName(全类名);
Dog dog = (Dog) clazzDog.newInstance();

3.3.2获取类的构造器创建对象

Class<Dog> clazzDog = Dog.class;

//空参默认获取空参的构造器
Constructor<Dog> noParConstructor = clazzDog.getDeclaredConstructor();

//写上类型 获取指定参数的构造器
Constructor<Dog> strParConstructor = clazzDog.getDeclaredConstructor(String.class);

//设置访问权限为true 即使是private修饰的构造器也能调用
strParConstructor.setAccessible(true);
noParConstructor.setAccessible(true);

//调用newInstance(parm ... xx)创建对象 
Dog zhangsan = strParConstructor.newInstance("zhangsan");
Dog dog = noParConstructor.newInstance();

3.5通过反射调用方法属性构造器

3.5.1反射使用属性

Class<Son> clazz = Son.class;
//反射创建对象
Son son = clazz.newInstance();
//获取某一个属性 私有的也能获取 但是要设置setAccessible为true才能set get
Field sonName = clazz.getDeclaredField("sonName");

//私有的属性必须要设置成true 不然无法get set
sonName.setAccessible(true);

//调用set方法 为son这个对象设置值
sonName.set(son, "sss");

//调用get方法,得到son对象的属性值
Object o = sonName.get(son);

3.5.2反射使用方法

Class<Son> clazz = Son.class;

//反射创建对象
Son son = clazz.newInstance();

//得到指定的方法
Method publicDadShow = clazz.getDeclaredMethod("publicDadShow");

//设置为可访问的
publicDadShow.setAccessible(true);

//result就是返回值 有参数在后面写参数 如果方法没有返回值 那么就返回null
Object result = publicDadShow.invoke(son);


//获取静态方法并调用
Method say = clazz.getDeclaredMethod("say");
Object invoke = say.invoke(clazz);

3.5.3反射使用构造器

Class<Son> clazz = Son.class;
//获取指定的构造器 可以传参数
Constructor<Son> constructor = clazz.getDeclaredConstructor();

//可访问设置为true
constructor.setAccessible(true);

//调用newInstance()方法创建对象,如果有参数传
Son son = constructor.newInstance();

3.5.4反射使用注解