java枚举反射以及注解,看这一篇就够了
1.1 枚举的概述
枚举是 Java 中一种特殊的类,它可以定义固定数量的枚举实例,例如: 性别、交通信号灯、季节等 1.2 为什么要使用枚举
假设我们要定义一个人类,人类中包含姓名和性别。通常会将性别定义成字符串类型,效果如下: public class Person { private String name; private String sex; public Person() { } public Person(String name, String sex) { this.name = name; this.sex = sex; } // 省略get/set/toString方法 }public class Demo01 { public static void main(String[] args) { Person p1 = new Person("张三", "男"); Person p2 = new Person("张三", "abc"); // 因为性别是字符串,所以我们可以传入任意字符串 } }
不使用枚举存在的问题:可以给性别传入任意的字符串,导致性别是非法的数据,不安全。 1.3 作用
一个方法接收的参数是固定范围之内的时候,那么即可使用枚举类型 1.4 格式enum 枚举名 { 第一行都是罗列枚举实例,这些枚举实例直接写大写名字即可。 }1.5 案例定义枚举:MALE表示男,FEMALE表示女 enum Gender { MALE, FEMALE; // 男,女 }Perosn中的性别有String类型改为Sex枚举类型 public class Person { private String name; private Gender gender; public Person() { } public Person(String name, Gender gender) { this.name = name; this.gender = gender; } // 省略get/set/toString方法 }使用是只能传入枚举中的固定值 public class Demo02 { public static void main(String[] args) { Person p1 = new Person("张三", Gender.MALE); Person p2 = new Person("张三", Gender.FEMALE); Person p3 = new Person("张三", "abc");//报错 } }1.6 枚举中添加成员变量和成员方法
枚举的本质是一个类,所以枚举中还可以有成员变量,成员方法等。 public enum Gender { MALE("male"), MALE("female"); public String tag; Sex(String tag) { this.tag = tag; } public void showTag() { System.out.println("它的性别标志是: " + tag); } }public class Demo03 { public static void main(String[] args) { Person p1 = new Person("张三", Sex.MALE); Person p2 = new Person("李四", Sex.MALE); Sex.BOY.showTag();//它的性别标志是male } }二 反射
2.1 反射的概念
反射是一种机制/功能,利用该机制/功能可以在 程序运行 过程中对类进行解剖并操作类中的构造方法,成员方法,成员属性。 2.2反射的应用场景
各种框架的设计(主要场景)
各大框架的内部实现也大量使用到了反射机制,所以要想学好这些框架,则必须要求了解反射机制 2.3 反射的应用2.3.1 获取类型的详细信息
可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的) 2.3.1.1 获取包信息Package pkg = clazz.getPackage();2.3.1.2 获取修饰符int mod = clazz.getModifiers();
修饰符定义在Modifier类中,该类里面有很多常量值,每一个常量对应一种修饰符 2.3.1.3 获取类名String name = clazz.getName();2.3.1.4 获取父类的字节码对象Class superclass = clazz.getSuperclass();2.3.1.5 获取该类实现的所有接口Class[] interfaces = clazz.getInterfaces();2.3.1.6 获取该类的所有属性Field[] declaredFields = clazz.getDeclaredFields();2.3.1.7 获取该类的所有构造函数Method[] declaredMethods = clazz.getDeclaredMethods();2.3.1.8 获取该类的所有方法Method[] declaredMethods = clazz.getDeclaredMethods();
例子 //Person类 package reflect; public class Person implements Hello{ private String name="张三"; private int age; public String color; @Override public String toString() { return "Person{" + "name="" + name + """ + ", age=" + age + "}"; } public Person() { System.out.println("执行了无参构造"); } public Person(String name) { this.name = name; } public Person(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } @Override public void sayHello() { System.out.println("你好"); } private void study(String course,int day){ System.out.println("努力学习"+course+""+day+"天"); } } //测试类 package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class TestReflect { public static void main(String[] args) throws ClassNotFoundException { // 第一种 // Class clazz=Person.class; // 第二种 // Person person=new Person(); // Class clazz=person.getClass(); // 第三种 Class clazz = Class.forName("reflect.Person"); // 1.使用反射获取包 Package pac = clazz.getPackage(); String pacName = pac.getName(); System.out.println(pacName); // 2.获取修饰符,获取类的修饰符,1代表public,2代表private int modifiers = clazz.getModifiers(); System.out.println(modifiers); // 3.获取类名 String clazzName = clazz.getName(); System.out.println(clazzName); String simpleName = clazz.getSimpleName(); System.out.println(simpleName); // 4.获取父类的字节码对象,默认继承object Class superName=clazz.getSuperclass(); // 5.获取该类实现的接口的字节码对象 Class[] interfaces = clazz.getInterfaces(); for (Class anInterface : interfaces) { System.out.println(anInterface); } // 6.获取该类的所有属性(成员变量),getDeclaredFields获取自己的公有和私有,getFields获取自己何父类的共有属性 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } System.out.println("============="); Field[] fields1 = clazz.getFields(); for (Field field : fields1) { System.out.println(field); } System.out.println("=============="); // 7.获取类的所有构造函数 Constructor[] constructors = clazz.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("-------------------"); Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor.getParameterCount()); } // 参数个数 // 8.获取类的所有方法 Method[] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); } System.out.println("-----------"); Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } } } 2.4.2 创建任意引用类型的对象(重点)
两种方式:
1、直接通过Class对象来实例化(要求必须有无参构造)
2、通过获取构造器对象来进行实例化
方式一的步骤 :
(1)获取该类型的Class对象(2)创建对象 Class clazz = Person.class; // 使用第一种方式创建Person类对象 //强转一定是建立在父子关系的前提下 Person person = (Person) clazz.newInstance(); System.out.println(person);
方式二的步骤:
(1)获取该类型的Class对象(2)获取构造器对象(3)创建对象
如果构造器的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)
示例代码: public static void main(String[] args) throws Exception { //获取Person类的Class对象 Class clazz = Person.class; //使用第二种方式创建Person类的对象 // 使用构造函数创建对象 //获取无参的构造函数 Constructor declaredConstructor = clazz.getDeclaredConstructor(); Person person1 = (Person) declaredConstructor.newInstance(); System.out.println(person1); // 获取有参数的构造函数创建对象 Constructor declaredConstructor1 = clazz.getDeclaredConstructor(String.class, int.class); Person person2 = (Person) declaredConstructor1.newInstance("李四", 15); System.out.println(person2); }2.3.3 操作任意类型的属性(重点)
(1)获取该类型的Class对象 Class clazz = Class.forName("reflect.Person");
(2)获取属性对象 Field field = clazz.getDeclaredField("username");
(3)设置属性可访问(暴力反射) field.setAccessible(true);
(4)创建实例对象:如果操作的是非静态属性,需要创建实例对象 Object obj = clazz.newInstance();
(4)设置属性值 field.set(obj,"chai");
(5)获取属性值 Object value = field.get(obj);
如果操作静态变量,那么实例对象可以省略,用null表示,当然一般不会使用反射操作静态变量
示例代码: public static void main(String[] args) throws Exception { //1. 获取Person的字节码对象 Class clazz = Person.class; Object obj = clazz.newInstance(); //2.1 获取Person的所有属性(只能获取自己的,包含公有的和私有的) /*Field[] declaredFields = clazz.getDeclaredFields(); for (Field declaredField : declaredFields) { //获取每个属性的属性名和属性值 //获取属性名 String name = declaredField.getName(); //获取属性的类型 Class<?> type = declaredField.getType(); //获取属性的修饰符 int modifiers = declaredField.getModifiers(); //暴力反射: 通过反射可以访问类的私有成员 declaredField.setAccessible(true); //获取属性的值 Object value = declaredField.get(obj); //等值于 对象.属性名 System.out.println(name + "," + value + "," + type + "," + modifiers); }*/ //2.2 单独获取某一个属性,比如获取name Field filed = clazz.getDeclaredField("name"); // 设置其属性值为王五,如果是私有属性,则需要暴力获取 filed.setAccessible(true); filed.set(obj,"王五"); String str = (String) filed.get(obj); System.out.println(str); }2.3.4 调用任意类型的方法
(1)获取该类型的Class对象 Class clazz = Class.forName("reflect.Person");
(2)获取方法对象 Method method = clazz.getDeclaredMethod("login",String.class,String.class);
(3)创建实例对象 Object obj = clazz.newInstance();
(4)调用方法 Object result = method.invoke(obj,"chai","123);如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)
如果方法是静态方法,实例对象也可以省略,用null代替
示例代码: public static void main(String[] args) throws Exception { //使用反射操作类的方法: 获取方法、调用方法 //1. 获取类的字节码对象 Class clazz= Person.class; Object obj = clazz.newInstance(); //2. 获取某一个方法,例如: getName() //获取无参的getName方法 Method getNameMethod = clazz.getDeclaredMethod("getName"); //获取带一个String类型参数的study方法 Method studyMethod = clazz.getDeclaredMethod("study", String.class, int.class); //调用方法 String name = (String) getNameMethod.invoke(obj);//invoke先当与执行方法 System.out.println("获取到的name:" + name); //暴力反射 studyMethod.setAccessible(true); studyMethod.invoke(obj,"Java",180); }2.3.5 Type接口的介绍(了解)
java.lang.reflect.Type 接口及其相关接口用于描述java中用到的所有类型,是Java的反射中很重要的组成部分。Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。 2.3.5.1 使用反射获取Type
有很多场景下我们可以获得Type,比如: 当我们拿到一个Class,用 Class.getGenericInterfaces() 方法得到Type[],也就是这个类实现接口的Type类型列表。 当我们拿到一个Class,用 Class.getDeclaredFields() 方法得到Field[],也就是类的属性列表,然后用 Field. getGenericType() 方法得到这个属性的Type类型。 当我们拿到一个Method,用 Method.getGenericParameterTypes() 方法获得Type[],也就是方法的参数类型列表。 当我们拿到一个Class,用 clazz.getGenericSuperclass() 这样就可以获取父类的泛型实参列表 2.3.5.2 Type的分类
Type接口包含了一个实现类(Class)和四个实现接口(TypeVariable, ParameterizedType, GenericArrayType, WildcardType),这四个接口都有自己的实现类,但这些实现类开发都不能直接使用,只能用接口。 Class: 当需要描述的类型是普通Java类、数组、自定义类、 8种java基本类型 的时候, java会选择Class来作为这个Type的实现类,我们甚至可以直接把这个Type强行转换类型为Class。这些类基本都有一个特点: 基本和泛型无关 ,其他4种Type的类型,基本都是泛型的各种形态。 ParameterizedType: 当需要描述的类是 泛型类 时,比如List,Map等,不论代码里写没写具体的泛型,java会选择ParameterizedType接口做为Type的实现。ParameterizedType接口有getActualTypeArguments()方法,用于得到泛型的Type类型数组。 GenericArrayType: 当需要描述的类型是 泛型类的数组 时,比如比如List[],Map[],type用GenericArrayType接口作为Type的实现。GenericArrayType接口有getGenericComponentType()方法,得到数组的组件类型的Type对象。 WildcardType: 当需要描述的类型是泛型类,而且泛型类中的泛型被定义为(? extends xxx)或者(? super xxx)这种类型,比如List<? extends TestReflect>,这个类型首先将由ParameterizedType实现,当调用ParameterizedType的getActualTypeArguments()方法后得到的Type就由WildcardType实现。 2.3.6 获取泛型父类信息
示例代码获取泛型父类信息: public class TestGeneric { public static void main(String[] args) { //需求:在运行时,获取Son类型的泛型父类的泛型实参 //(1)还是先获取Class对象 Class clazz = Son.class;//四种形式任意一种都可以 //(2)获取泛型父类 /* * getSuperclass()只能得到父类名,无法得到父类的泛型实参列表 */ Type type = clazz.getGenericSuperclass(); // Father属于ParameterizedType ParameterizedType pt = (ParameterizedType) type; //(3)获取泛型父类的泛型实参列表 Type[] typeArray = pt.getActualTypeArguments(); for (Type type2 : typeArray) { System.out.println(type2); } } } //泛型形参: class Father{ } //泛型实参: class Son extends Father { }2.3.7 动态创建和操作任意类型的数组
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array类来动态的创建数组,操作数组元素等。
Array类提供了如下几个方法:
public static Object newInstance(Class<?> componentType, int... dimensions):创建一个具有指定的组件类型和维度的新数组。
public static void setXxx(Object array,int index,xxx value):将array数组中[index]元素的值修改为value。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用set(Object array,int index, Object value)方法。
public static xxx getXxx(Object array,int index,xxx value):将array数组中[index]元素的值返回。此处的Xxx对应8种基本数据类型,如果该属性的类型是引用数据类型,则直接使用get(Object array,int index)方法。 public static void main(String[] args) { //使用反射操作数组 //1. 使用反射创建一个String类型的数组,长度是5 Object array = Array.newInstance(String.class, 5); //2. 往数组中存入数据 for (int i=0;i<5;i++){ Array.set(array,i,"value"+i); } //使用Array获取数组中的元素 for (int i=0;i<5;i++){ System.out.println(Array.get(array, i)); } }三 注解3.1 注解概述3.1.1 什么是注解
注解英文是annotation,是一种代码级别的说明,和类 接口平级关系。相当于一种标记,在程序中加入注解就等于为程序打上某种标记,以后,javac编译器、开发工具和其他程序可以通过反射来了解你的类及各种元素上有无标记,看你的程序有什么标记,就去干相应的事,标记可以加在包、类,属性、方法,方法的参数以及局部变量上定义 3.1.2 注解的作用
执行编译期的检查 例如:@Override
分析代码(主要用途:替代配置文件); 用在框架里面, 注解开发 3.2 JDK提供的三个基本的注解@Override :描述方法的重写. @SuppressWarnings :压制警告. @Deprecated :标记过时 3.3 自定义注解(重点)3.3.1 自定义注解语法
语法 : home.php?mod=space&uid=593715 注解名{}
示例 /** * 定义了注解 * */ public @interface Annotation01 { }3.3.2 注解属性3.3.2.1 注解属性的作用
注解属性可以让注解具备携带存储数据的功能 3.3.2.2 注解属性的类型基本类型
2.String
3.枚举类型
4.注解类型
5.Class类型
6.以上类型的一维数组类型
注意:
一旦注解有属性了,使用注解的时候,属性必须有值 示例代码 /** *注解的属性; 格式和接口的方法很类似 * 1.基本类型 2.String 3.枚举类型 4.注解类型 5.Class类型 6.以上类型的一维数组类型 */ public @interface Annotation02 { int a();//基本类型 String b();//String Color c();//枚举类型 Annotation01 d();//注解类型 Class e();//Class类型 String[] f();//一维数组类型 }3.3.2.3 使用注解时给属性赋值格式 @注解名(属性名=值,属性名 2 =值2 ) 实例:@MyAnnotation3(i = 0,s="23") 3.3.2.4 属性赋值的特殊情况若属性类型的一维数组的时候,当数组的值只有一个的时候可以省略{} @MyAnnotation4(ss = { "a" }) @MyAnnotation4(ss = "a") 注解属性可以有默认值 属性类型 属性名() default 默认值;若属性名为value的时候,且只有这一个属性需要赋值的时候可以省略value 【重点】
实例: //注解1类 package myAnnotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME)//在运行时解析 public @interface MyAnnotation { // 注解的作用 // 1.作为标记 // 2.用于代替配置文件,存储一些配置信息 // 3.注解但凡定义了属性,那么在使用注解的时候,就要给每一个注解的属性进行赋值 // 4.注解属性可以使用default进行赋值()默认值,被赋予默认值的属性,我们在使用该注解的时候可以不进行赋值 // 5.如果注解属性是字符属性,但赋值的时候该数组只有一个元素,那么可以省略这个数组的大括号 // 6.如果只有一个注解属性必须赋值,并且这个注解属性的属性名是value,那么赋值的时候可以省略 // 元注解 // 1.Target表示注解只能用在哪些位置 // 2.Retention 表示该注解保留到哪个阶段,自定义注解一般都需要保存在RUNTIME时 String str() default "小高"; int num() default 12; Color color() default Color.YRLLOW; Class clas() default UseAnnotation.class; MyAnnotation2 anno() default @MyAnnotation2; String[] value(); } //注解2类 package myAnnotation; public @interface MyAnnotation2 { } //枚举类 package myAnnotation; public enum Color { RED,GREEN,YRLLOW; } //测试类 package myAnnotation; // 在这个类中使用自定义注解 public class UseAnnotation { private int age; //使用默认赋值 @MyAnnotation(num = 0, clas = UseAnnotation.class, anno = @MyAnnotation2(), value = "省略大括号") public void say(String name) { System.out.println(name); } // @MyAnnotation({"你好"})//省略value= public void study() { System.out.println("努力学习..."); } } 3.4 元注解3.4.1 元注解的作用
元注解是使用在自定义的注解上,为自定义的注解提供支持的 3.4.2 常用的元注解
@Target :定义该注解作用在什么上面(位置),默认注解可以在任何位置. 值为: ElementType 的枚举值
METHOD :方法
TYPE :类 接口
FIELD :字段
CONSTRUCTOR :构造方法声明
@Retention :定义该注解保留到那个代码阶段, 值为: RetentionPolicy 类型, 默认只在源码阶段保留
SOURCE :只在源码上保留(默认)
CLASS :在源码和字节码上保留
RUNTIME :在所有的阶段都保留
java (源码阶段) ----编译---> .class(字节码阶段) ----加载内存--> 运行(RUNTIME)
eg: @Target(value = {ElementType.METHOD,ElementType.TYPE }) @Retention(value = RetentionPolicy.RUNTIME) public @interface MyAnnotation03 { int a(); String b(); }3.5 注解解析
java.lang.reflect.AnnotatedElement T getAnnotation(ClassannotationType):得到指定类型的注解引用。没有返回null。 boolean isAnnotationPresent(Class<?extends Annotation> annotationType) :判断指定的注解有没有。 Class、Method、Field、Constructor等实现了AnnotatedElement接口. Annotation[] getAnnotations():得到所有的注解,包含从父类继承下来的。 Annotation[] getDeclaredAnnotations():得到自己身上的注解。 public @interface Annotation01(){ } @Annotation01 class Demo01(){ @Annotation01 public void fun01(){ } public void fun02(){ } } //1.获得Demo01字节码对象 Class clazz = Demo01.class; //2. 获得Demo01上面的注解对象 Annotation01 annotation01 = clazz.getAnnotation(Annotation01.class); //3.反射获得fun01()方法对象 Method method = clazz.getMethod("fun01"); //4.判断fun01()方法上面是否有@Annotation01注解 boolean flag = method.isAnnotationPresent(Annotation01.class);
文章来源:https://www.52pojie.cn/forum.php?mod=viewthread&tid=1553183&extra=page%3D1%26filter%3Dtypeid%26typeid%3D192
北京发布指引正面回应大数据杀熟互联网反垄断风险还有哪些?21世纪经济报道记者王俊黄婉仪实习生龙情北京报道国家对平台经济的反垄断监管决心已定。对广大互联网企业来说,如何参与市场公平竞争是一个迫切问题,一份反垄断合规指引显得意义重大。12月
2021搜狐畅游数据分析笔试题哈喽,大家好,我是可乐很多同学留言想看数据分析类岗位的笔试面试题目,今天这篇文章就是了。从牛客网上找到的搜狐畅游数据分析岗位的笔试题,精选了14道题目来给大家解析下,内容涵盖统计学
以先进技术解决方案助力数据中心绿色发展近日,国家发展改革委中央网信办工业和信息化部国家能源局联合发布贯彻落实碳达峰碳中和目标要求推动数据中心和5G等新型基础设施绿色高质量发展实施方案的通知指出,在交通能源工业和市政等基
AV功放和HiFi功放区别与特点1。AV功放是专门为家庭影院用途而设计的放大器AV是英文AODIOVIDIO即音频,视频的打头字母缩写。AV功放从诞生到现在,经历了杜比环绕,杜比定向逻辑,AC3,DTS,以及现在
电气设备板块之双一科技(300693)的投资价值分析一公司隶属非金属矿物制品业,通俗地讲就是复合材料设备制造行业,主要业务涵盖复合材料研发设计生产销售和服务。主要产品涉及大型非金属模具加工风电工程和农用机械游艇及船舶轨道交通及公共交
新的时代开始了慢生意时代中国互联网激荡昂扬了二十年之后,终于迎来了向下的拐点。过去,互联网行业年年都有风口,年年都有新词汇,今年却异常安静乖巧。除了热度从去年延续到今年的社区团购,整个行业已经看不到任何抓
苹果新品曝光新款iPhoneSE来了4。7寸3GB内存12月8日,天风国际分析师郭明錤发布最新研报,曝光了数款苹果即将发布的新品,包括想新款iPhoneSE新一代AppleWatch等等。郭明錤在研报中指出,苹果将于2022年上半年发
乐视视频乐视旗下华影时光APP升级中证网讯(记者董添)12月8日,乐视视频官微发文,乐视旗下华影时光APP升级,升级后的华影时光APP将承载乐视在大屏端的独家版权内容,公司在大屏生态上的布局将进一步加强。据公司介绍
嫌员工摸鱼太严重,软银注资的独角兽怒裁900人软银注资的美国数字抵押贷款公司Better。com近日召开一次视频会议,其CEO维沙尔加格(VishalGarg)当场向参加会议的900多名员工宣布,他们将被裁员,即刻生效。这些员
每人发120万!这些大厂的年终奖把我看呆了打工一年都比不过点击右上方关注,第一时间获取科技资讯技能攻略产品体验,私信我回复01,送你一份玩机技能大礼包。12月8日消息,大厂的年终奖有多疯狂话题登上微博热搜,引来大量网友的围观和讨论。有内部
持证上岗,带货主播成正式工种!李佳琦薇娅能评几级?近日,人社部中央网信办国家广播电视总局共同发布了互联网营销师国家职业技能标准。在互联网营销师职业下增设直播销售员工种,带货主播成为正式工种。对此,网友热议称李佳琦薇娅们正式转正,要