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 浸染一个方法吸收的参数是固定例模之内的时候,那么即可利用列举类型
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类型的泛型父类的泛型实参<String,Integer> //(1)还是先获取Class工具 Class clazz = Son.class;//四种形式任意一种都可以 //(2)获取泛型父类 / getSuperclass()只能得到父类名,无法得到父类的泛型实参列表 / Type type = clazz.getGenericSuperclass(); // Father<String,Integer>属于ParameterizedType ParameterizedType pt = (ParameterizedType) type; //(3)获取泛型父类的泛型实参列表 Type[] typeArray = pt.getActualTypeArguments(); for (Type type2 : typeArray) { System.out.println(type2); } }}//泛型形参:<T,U>class Father<T,U>{}//泛型实参:<String,Integer>class Son extends Father <String,Integer>{}
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(Class<T>annotationType):得到指定类型的表明引用。没有返回null。boolean isAnnotationPresent(Class<?extends Annotation> annotationType):判断指定的表明有没有。Class、Method、Field、Constructor等实现了AnnotatedElement接口.Annotation[] getAnnotations():得到所有的表明,包含从父类继续下来的。Annotation[] getDeclaredAnnotations():得到自己身上的表明。public @interface Annotation01(){}@Annotation01class 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