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类的主要方法
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,即只在编译器有效
@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反射使用注解