JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射是比较重要的一个知识,现在的大多数框架都使用得到了反射+注解的形式,对我们的程序进行封装,让我们开箱即用,大大的减少了我们的开发时间,提升了我们的开发效率,使用反射+注解使得我们的程序变得更加的灵活多变
反射就是把java类中的属性和方法映射成与之对应的Java对象,一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把这些组成一个完整类的成员,拆分为一个一个的java对象。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是说jvm中每个实例都应该属于某个Class对象与之对应。(包括基本数据类型)
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
**我们来看下Class类的一些方法:**当然太多,等下挑常用的讲
Modifier and Type | Method and Description |
---|---|
类 extends U> |
asSubclass(类 clazz) 类 这个 类 对象来表示由指定的类对象表示的类的子类。 |
T |
cast(Object obj) 施放一个目的是通过本表示的类或接口 类 对象。 |
boolean |
desiredAssertionStatus() 如果要在调用此方法时初始化该类,则返回将分配给此类的断言状态。 |
static 类> |
forName(String className) 返回与给定字符串名称的类或接口相关联的 类 对象。 |
static 类> |
forName(String name, boolean initialize, ClassLoader loader) 使用给定的类加载器返回与给定字符串名称的类或接口相关联的 类 对象。 |
AnnotatedType[] |
getAnnotatedInterfaces() 返回一个 AnnotatedType 对象的数组, AnnotatedType 使用类型指定由此 AnnotatedType 对象表示的实体的超级 类 。 |
AnnotatedType |
getAnnotatedSuperclass() 返回一个 AnnotatedType 对象,该对象表示使用类型来指定由此 类 对象表示的实体的 类 类。 |
A |
getAnnotation(类 annotationClass) 返回该元素的,如果这样的注释 *,*否则返回null指定类型的注释。 |
Annotation[] |
getAnnotations() 返回此元素上 存在的注释。 |
A[] |
getAnnotationsByType(类 annotationClass) 返回与此元素相关 联的注释 。 |
String |
getCanonicalName() 返回由Java语言规范定义的基础类的规范名称。 |
类>[] |
getClasses() 返回包含一个数组 类 表示所有的公共类和由此表示的类的成员接口的对象 类 对象。 |
ClassLoader |
getClassLoader() 返回类的类加载器。 |
类> |
getComponentType() 返回 类 数组的组件类型的Class。 |
Constructor |
getConstructor(类>... parameterTypes) 返回一个 Constructor 对象,该对象反映 Constructor 对象表示的类的指定的公共 类 函数。 |
Constructor>[] |
getConstructors() 返回包含一个数组 Constructor 对象反射由此表示的类的所有公共构造 类 对象。 |
A |
getDeclaredAnnotation(类 annotationClass) 如果这样的注释 直接存在 ,则返回指定类型的元素注释,否则返回null。 |
Annotation[] |
getDeclaredAnnotations() 返回 直接存在于此元素上的注释。 |
A[] |
getDeclaredAnnotationsByType(类 annotationClass) 如果此类注释 直接存在或 *间接存在,*则返回该元素的注释(指定类型)。 |
类>[] |
getDeclaredClasses() 返回一个反映所有被这个 类 对象表示的类的成员声明的类和 类 对象的数组。 |
Constructor |
getDeclaredConstructor(类>... parameterTypes) 返回一个 Constructor 对象,该对象反映 Constructor 对象表示的类或接口的指定 类 函数。 |
Constructor>[] |
getDeclaredConstructors() 返回一个反映 Constructor 对象表示的类声明的所有 Constructor 对象的数组 类 。 |
Field |
getDeclaredField(String name) 返回一个 Field 对象,它反映此表示的类或接口的指定已声明字段 类 对象。 |
Field[] |
getDeclaredFields() 返回的数组 Field 对象反映此表示的类或接口声明的所有字段 类 对象。 |
方法 |
getDeclaredMethod(String name, 类>... parameterTypes) 返回一个 方法 对象,它反映此表示的类或接口的指定声明的方法 类 对象。 |
方法[] |
getDeclaredMethods() 返回包含一个数组 方法 对象反射的类或接口的所有声明的方法,通过此表示 类 对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。 |
类> |
getDeclaringClass() 如果由此 类 对象表示的类或接口是另一个类的成员,则返回表示其声明的类的 类 对象。 |
类> |
getEnclosingClass() 返回底层类的即时封闭类。 |
Constructor> |
getEnclosingConstructor() 如果此类 对象表示构造函数中的本地或匿名类,则返回表示底层类的立即封闭构造函数的Constructor 对象。 |
方法 |
getEnclosingMethod() 如果此类 对象表示方法中的本地或匿名类,则返回表示基础类的即时封闭方法的方法 对象。 |
T[] |
getEnumConstants() 返回此枚举类的元素,如果此Class对象不表示枚举类型,则返回null。 |
Field |
getField(String name) 返回一个 Field 对象,它反映此表示的类或接口的指定公共成员字段 类 对象。 |
Field[] |
getFields() 返回包含一个数组 Field 对象反射由此表示的类或接口的所有可访问的公共字段 类 对象。 |
Type[] |
getGenericInterfaces() 返回 Type 表示通过由该对象所表示的类或接口直接实现的接口秒。 |
Type |
getGenericSuperclass() 返回 Type 表示此所表示的实体(类,接口,基本类型或void)的直接超类 类 。 |
类>[] |
getInterfaces() 确定由该对象表示的类或接口实现的接口。 |
方法 |
getMethod(String name, 类>... parameterTypes) 返回一个 方法 对象,它反映此表示的类或接口的指定公共成员方法 类 对象。 |
方法[] |
getMethods() 返回包含一个数组 方法 对象反射由此表示的类或接口的所有公共方法 类 对象,包括那些由类或接口和那些从超类和超接口继承的声明。 |
int |
getModifiers() 返回此类或接口的Java语言修饰符,以整数编码。 |
String |
getName() 返回由 类 对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为 String 。 |
软件包 |
getPackage() 获取此类的包。 |
ProtectionDomain |
getProtectionDomain() 返回 ProtectionDomain 。 |
URL |
getResource(String name) 查找具有给定名称的资源。 |
InputStream |
getResourceAsStream(String name) 查找具有给定名称的资源。 |
Object[] |
getSigners() 获得这个类的签名者。 |
String |
getSimpleName() 返回源代码中给出的基础类的简单名称。 |
类 super T> |
getSuperclass() 返回 类 表示此所表示的实体(类,接口,基本类型或void)的超类 类 。 |
String |
getTypeName() 为此类型的名称返回一个内容丰富的字符串。 |
TypeVariable<类 |
getTypeParameters() 返回一个 TypeVariable 对象的数组,它们以声明顺序表示由此 GenericDeclaration 对象表示的通用声明声明的类型变量。 |
boolean |
isAnnotation() 如果此 类 对象表示注释类型,则返回true。 |
boolean |
isAnnotationPresent(类 extends Annotation> annotationClass) 如果此元素上 存在指定类型的注释,则返回true,否则返回false。 |
boolean |
isAnonymousClass() 返回 true 当且仅当基础类是匿名类时。 |
boolean |
isArray() 确定此 类 对象是否表示数组类。 |
boolean |
isAssignableFrom(类> cls) 确定由此 类 对象表示的类或接口是否与由指定的Class 类 表示的类或接口相同或是超类或 类 接口。 |
boolean |
isEnum() 当且仅当该类在源代码中被声明为枚举时才返回true。 |
boolean |
isInstance(Object obj) 确定指定的Object是否与此 Object 表示的对象分配 类 。 |
boolean |
isInterface() 确定指定 类 对象表示接口类型。 |
boolean |
isLocalClass() 返回 true 当且仅当基础类是本地类时。 |
boolean |
isMemberClass() 返回 true 当且仅当基础类是成员类时。 |
boolean |
isPrimitive() 确定指定 类 对象表示一个基本类型。 |
boolean |
isSynthetic() 如果这个类是一个合成类,返回true ; 返回false 其他。 |
T |
newInstance() 创建由此 类 对象表示的类的新实例。 |
String |
toGenericString() 返回描述此 类 的字符串,包括有关修饰符和类型参数的信息。 |
String |
toString() 将对象转换为字符串。 |
User:案例类
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Created with IntelliJ IDEA.
*
* @Author: compass
* @Date: 2021-10-23-0:00
* @Version:1.0
* @Description:
*/
public class User {
private int id;
private int age;
private String name;
private String sex;
public static List<User> getUserList(){
List<User> userList = new ArrayList<>();
userList.add(new User(1,29,"小明","男"));
userList.add(new User(2,21,"小王","男"));
userList.add(new User(3,18,"小李","女"));
userList.add(new User(4,23,"小华","女"));
userList.add(new User(7,50,"小花","女"));
userList.add(new User(5,50,"小k","男"));
userList.add(new User(6,60,"小浪","女"));
return userList;
}
public User(int id, int age, String name, String sex) {
this.id = id;
this.age = age;
this.name = name;
this.sex=sex;
}
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public User( String name) {
this.name = name;
}
public User(int id, String name) {
this.id=id;
this.name = name;
}
public User() {
}
public String getSex() {
return sex;
}
public void setSex(int id) {
this.sex = sex;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
/**
* 获取一个User对象
* @return
*/
public User getUser(){
return new User(1,29,"小明","男");
}
}
// 1.使用Object的方法getClass(),Object是所有类的父类,那么任何一个对象,都应该有getClass()方法
User u = new User();
Class<? extends User> uClass = u.getClass();
//2.任何数据类型都包含了一个class属性,类型.class的方式
Class<User> aClass = User.class;
//3.常用的 Class.classFrom(String arg),传递一个String类型的从类路径到该类的具体路径
// Class.forName("test.User") : 这个代码会导致我们的静态代码块加载,如果只是想执行某个类的静态代码块,那么可以使用该方法
Class<?> bClass = Class.forName("test.User");
获取到某个类的Class对象有什么用?
我们可以通过该类的class对象,通过newInstance() 方法对该类进行实例化,不用我们手动去new,注意该方法,默认调用的是无参构造方法,如果一个类没有任何构造方法,默认有一个无参构造,如果你重写了有参构造,记得无参构造一定要带上,否则newInstance() 方法将创建对象失败。
User user = (User)bClass.newInstance();
@Test
void test1() throws Exception{
// 获取共有的构造方法(public进行修饰的方法),并且返回一个 Constructor数组
Constructor<?>[] constructors = User.class.getConstructors();
for (Constructor constructor: constructors){
System.out.println(constructor);
}
System.out.println("----------------------------------------------");
// 获取指定某个构造方法,不传递任何参数,默认获取无参构造,如果要获取有参的需要跟上参数类型,必须和构造方法中参数顺序一致
Constructor<User> constructor1 = User.class.getConstructor(); // 获取无参构造
System.out.println("无参构造="+constructor1);
System.out.println("----------------------------------------------");
Constructor<User> constructor2 = User.class.getConstructor(String.class); // 获取有参构造 User(String name)
System.out.println("有参构造="+constructor2);
System.out.println("----------------------------------------------");
// 获取私有、受保护、默认、公有的构造方法,并且返回一个 Constructor数组
Constructor<?>[] dc1 = User.class.getDeclaredConstructors();
for (int i = 0; i < dc1.length; i++) {
System.out.println("private="+dc1[i]);
}
System.out.println("----------------------------------------------");
// 获取私有、受保护、默认、公有的构造方法,获取单个的(自定义)
Constructor dc2 = User.class.getDeclaredConstructor(int.class,String.class);
System.out.println(dc2);
// 如果该构造方法是私有的,不能直接进行访问,需要先设置为可访问的
if (!dc2.isAccessible()){ // 先判断该方法是否可以进行访问,如果不可以,进行修改,设置为可访问的
dc2.setAccessible(true); // 将private修饰的方法修饰为可访问的
}
// 调用 获取到的构造方法,可以跟参数(不过要和获取到的构造方法中的参数对应,否则抛出:IllegalArgumentException)
User user1 = (User)dc2.newInstance(1, "张三");
System.out.println("name="+user1.getName());
}
@Test // 通过class对象获取到属性
void test2() throws Exception{
// 获取到User类中的所有(公共)属性 ,返回一个 Field[]数组
Field[] fields = User.class.getFields();
for (Field field : fields) {
System.out.println("filed = "+field);
}
System.out.println("----------------------------------------");
// 获取到User类中单个属性,返回一个Field对象
Field emailFiled = User.class.getField("email");
System.out.println("emailFiled = "+emailFiled);
System.out.println("----------------------------------------");
// 获取到User类中的所有属性(私有、受保护、默认)
Field[] df1 = User.class.getDeclaredFields();
for (Field field : df1) {
System.out.println("field = "+field);
}
System.out.println("----------------------------------------");
// 获取到User类中的单个的属性(私有、受保护、默认)
Field emailAttributes = User.class.getDeclaredField("email");
System.out.println("emailAttributes ="+emailAttributes);
System.out.println("----------------------------------------");
// 通过反射实例化一个对象,并且获取到该类中的字段(email),并且给其赋值
Class<User> userClass = User.class; // 获得一个User.class对象
User user = userClass.newInstance();
Field[] declaredFields = userClass.getDeclaredFields(); // 获取到user类中所有的属性
for (int i = 0; i < declaredFields.length; i++) { // 遍历User类中所有的属性,找到email属性,为其赋值
if (declaredFields[i].toString().contains("email")){
declaredFields[i].setAccessible(true);//设置为可访问的,以免程序出错
declaredFields[i].set(user,"[email protected]");
}
}
System.out.println(user.email);
}
@Test // 同过class对象获取到类中的所有方法,以及调用方法
void test3() throws Exception{
// 获取到User类中所有的public方法,包括父类的
Method[] methods = User.class.getMethods();
for (Method method : methods) {
System.out.println(" method = "+method);
}
System.out.println("----------------------------------------");
// 获取到User类中单个的public方法,包括父类的
Method equals = User.class.getMethod("equals", Object.class);
System.out.println("equals ="+equals);
System.out.println("----------------------------------------");
// 获取到User类中所有的(私有、受保护、默认)方法
Method[] methods2 = User.class.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(" method = "+method);
}
System.out.println("----------------------------------------");
// 获取到User类中单个的(私有、受保护、默认)方法
Method select = User.class.getDeclaredMethod("select", int.class);
System.out.println("select ="+select);
// 获取到某个类中的方法,并且查询到select方法,然后执行
// invoke(调用对象,参数) :注意参数一定要匹配
Class<User> userClass = User.class;
User user = userClass.newInstance();
Method[] dm = userClass.getDeclaredMethods();
for (int i = 0; i < dm.length; i++) { // 这里怎么多条件只是练习下api而已
if (dm[i].getName().equals("select")&&dm[i].getParameterCount()==1&& dm[i].getReturnType().getName().equals("java.lang.String")){
//if (dm[i].toString().contains("select(int)")) // 这样写也是ok的
dm[i].setAccessible(true); //设置为可访问的
// executeResult:就是我们方法调用完后的执行结果
Object executeResult = dm[i].invoke(user, 123456);
System.out.println("执行结果 = "+executeResult);
}
}
}
public class MyTest {
public static void main(String[] args) {
System.out.println("hello world");
}
}
@Test // 反射main方法
void test4() throws Exception{
Class<MyTest> testClass = MyTest.class;
Method main = testClass.getDeclaredMethod("main",String[].class);
main.setAccessible(true);
Object args = new String[]{"1"};
main.invoke(testClass.newInstance(),args);
}
info.properties:内容 注意idea项目中的当前位置是,当前过程路径下。
user=test.User
method=select
@Test // 读取配置文件,创建对象,并且调用方法
void test5() throws Exception{
Properties properties = new Properties();
// 获取到配置文件流
FileReader reader = new FileReader("src/main/resources/info.properties");
// 将配置文件流加载到 Properties 中
properties.load(reader);
// 从info.properties中获取到user的信息,并且得到User的class对象
Class<?> aClass = Class.forName(properties.get("user").toString());
// 利用反射实例化User对象
Object object = aClass.newInstance();
// 获取到User类中的select方法
Method select = aClass.getDeclaredMethod(properties.get("method").toString(), int.class);
// 设置为可访问的
select.setAccessible(true);
// 调用select方法,并且获取返回结果
Object executeResult = select.invoke(object,1);
System.out.println("executeResult = "+executeResult);
}
@Test // 使用反射跳过泛型机制检测
void test6() throws Exception{
ArrayList<String> list = new ArrayList<>();
list.add("admin");
list.add("root");
//list.add(new User(10,20,"admin")); //因为泛型机制的原因,此处不能添加User类型的数据,只能添加String类型的数据
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = list.getClass();
//获取add()方法
Method method = listClass.getMethod("add", Object.class);
//调用add()方法
method.invoke(list, new User(10,20,"admin"));
for(Object value : list){
System.out.println("value = "+value);
}
}
注解(注释,标注,Annotation)的作用 ?
如果要对于注解的作用进行分类,我们可以根据它所起的作用,大致可分为三类:
编写文档:通过代码里标识的元数据生成文档。
代码分析:通过代码里标识的元数据对代码进行分析。
编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。
注解出现的位置
Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。从某些方面看,Annotation 就像修饰符一样被使用,并应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在Annotation的“name=value”结构对中。
注解成员属性
Annotation的成员在Annotation类型中以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。在此有一个特定的默认 语法:允许声明任何Annotation成员的默认值。一个Annotation可以将name=value对作为没有定义默认值的Annotation 成员的值,当然也可以使用name=value对来覆盖其它成员默认值。这一点有些近似类的继承特性,父类的构造函数可以作为子类的默认构造函数,但是也 可以被子类覆盖。
注解生命周期分类
源码注解:注解只在源码中存在,编译成.class文件就不存在了。
编译时注解:注解在源码和.class文件中都存在。(例如:JDK的三个注解)
运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。
注解不会影响程序代码的执行
Annotation 能被用来为某个程序元素(类、方法、成员变量等)关联任何的信息。需要注意的是,这里存在着一个基本的规则:Annotation不能影响程序代码的执行,无论增加、删除 Annotation,代码都始终如一的执行。另外,尽管一些Annotation通过java的反射api方法在运行时被访问,而java语言解释器在工作时忽略了这些Annotation。正是由于java虚拟机忽略了Annotation,导致了Annotation类型在代码中是“不起作用”的; 只有通过某种配套的工具才会对Annotation类型中的信息进行访问和处理
@Override:表示子类覆盖了父类的方法
@Deprecation:表示方法已经过时,方法上有横线,使用时会有警告。
@author: 标明开发该类模块的作者
@version: 标明该类模块的版本
@see: 参考转向,也就是相关主题
@param: 对方法中某参数的说明
@return: 对方法返回值的说明
@exception: 对方法可能抛出的异常进行说明
@throws: 抛出的异常
@since:描述文本
@FunctionalInterface: 表示该接口是一个函数式接口,并且可以作为Lambda表达式参数传入
@SuppviseWarnings: 表示关闭一些警告信息(通知java编译器忽略特定的编译警告)
SuppviseWarnings:详细参数如下:
属性值 | 描述 |
---|---|
all | 抑制所有警告 |
boxing | 抑制装箱、拆箱操作时候的警告 |
cast | 抑制映射相关的警告 |
dep-ann | 抑制启用注释的警告 |
deprecation | 抑制过期方法警告 |
fallthrough | 抑制确在switch中缺失breaks的警告 |
finally | 抑制finally模块没有返回的警告 |
hiding | 抑制相对于隐藏变量的局部变量的警告的步骤 |
incomplete-switch | 忽略没有完整的switch语句 |
nls | 忽略非nls格式的字符 |
null | 忽略对null的操作 |
rawtypes | 使用generics时忽略没有指定相应的类型 |
restriction | 抑制与不鼓励或禁止引用的使用相关的警告 |
serial | 忽略在serializable类中没有声明serialVersionUID变量 |
static-access | 抑制不正确的静态访问方式警告 |
ic-access | 抑制子类没有按最优方法访问内部类的警告 |
unchecked | 抑制没有进行类型检查操作的警告 |
unqualified-field-access | 抑制没有权限访问的域的警告 |
unused | 抑制没被使用过的代码的警告 |
自定义注解的语法规则
自定义注解初识
// 自定义一个MyAnnotation注解,该注解中有一个className属性成员
public @interface MyAnnotation {
String className();
}
在其他类中使用该注解(后面会说到如何去获取注解,以及注解中的属性)
@MyAnnotation(className = "java.lang.reflect.Field")
public class MyTest2 {
注解中的 default关键字:
// 自定义一个MyAnnotation注解
public @interface MyAnnotation {
String className();
int userId() default 1004; // 增加一个 userId属性,并且设置默认值为:1024
}
@MyAnnotation(className = "java.util.UUID") // 不写userId也可以,使用我们设置的默认值:1024
class MyAnnotationTest{
}
成员属性至有一个,且名称为 value的特性
// 自定义一个MyAnnotation注解
public @interface MyAnnotation {
String value();
}
//注解中只有一个成员属性,且名称为value,可以不用谢属性名,当然也可以写为:value = "simpleType"
@MyAnnotation(value = "simpleType")
class MyAnnotationTest{
}
当注解的成员属性是数组的时候
// 自定义一个MyAnnotation注解
public @interface MyAnnotation {
String[] dataType(); //成员属性使用数组的形式
}
// 当注解成员属性是数组时:dataType = {属性值1,属性值2,属性值3,......}
@MyAnnotation(dataType = {"java","php","javaScript"})
class MyAnnotationTest{
}
注解中使用枚举类
// 自定义一个MyAnnotation注解
public @interface MyAnnotation {
Color[] colorType();
}
// 当注解成员属性是数组时:dataType = {属性值1,属性值2,属性值3,......}
@MyAnnotation(colorType={Color.BLACK,Color.GREEN})
class MyAnnotationTest{
}
// 定义颜色枚举类型
enum Color{
READ,GREEN,BLACK,WHITE;
}
什么是元注解?简单的来说,就是注解的注解,也就是用来描述我们自定义的注解的注解,听起来有点像套娃的感觉,其实注解,就是一个标签,就像是商品一样,贴上了标签,对这个商品进行描述,比如说:价格呀,生产日期啊等等,我们的注解也是如此,可以对一个类,一个方法,一个属性等等进行描述,我们可以获取到他们上面是否包含有该注解,或者是获取到他们上面的注解来指向某些逻辑,就像是我们的spring框架一样,使用 @Autowired注解一样,让我们的对象进行自动装配,使用注解+反射的开发形式,大大的提高了开发效率,代码的灵活性,可重复性。
1.@Target
@Target说明了Annotation所修饰的对象范围:即注解的作用域,用于说明注解的使用范围(即注解可以用在什么地方,比如类的注解,方法注解,成员变量注解等等)简单的来说,就是你自己定义的注解可以出现在什么位置都是由@Target注解来决定的如果没有使用@Target进行描述:那么自定义的的注解可以应用于程序的任何位置
java.lang.annotation.ElementType这个枚举中规定@Target的取值范围以及作用
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
// 类,接口,注解,枚举
TYPE,
/** Field declaration (includes enum constants) */
// 成员属性,枚举型常量
FIELD,
/** Method declaration */
// 只能出现在方法上
METHOD,
/** Formal parameter declaration */
// 只能用于形参声明
PARAMETER,
/** Constructor declaration */
// 只能用于构造方法上
CONSTRUCTOR,
/** Local variable declaration */
// 局部变量声明
LOCAL_VARIABLE,
/** Annotation type declaration */
// 注解类型声明
ANNOTATION_TYPE,
/** Package declaration */
// 用于包描述
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
// 参数类型声明
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
// 类型使用
TYPE_USE
}
2.@Retention
@Retention定义了该Annotation被保留的时间长短:
@Retention的取值是在RetentionPoicy这个枚举中规定的
3.@Inherited
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的Annotation类型被用于一个class,则这个Annotation将被用于该class的子类。
注意:@Inherited Annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承Annotation,方法并不从它所重载的方法继承Annotation。当@Inherited Annotation类型标注的Annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继 承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的Annotation类型被发现, 或者到达类继承结构的顶层。
@Inherited:也就是说,子类可以继承父类的类上的注解
// 自定义注解
// 自定义一个MyAnnotation注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited // 表示 MyAnnotation 这个注解可以被子类锁继承
public @interface MyAnnotation {
String name();
}
//测试代码
@MyAnnotation(name = "admin")
public class MyTest2 {
@Test
void test1() throws Exception{
// 通过反射获取到 MyTest3的class对象
Class<MyTest3> aClass = MyTest3.class;
Annotation[] annotations = aClass.getAnnotations();// 获取自身和父亲的注解
// 得到注解的类型
System.out.println(annotations[0].annotationType()); // interface test.MyAnnotation
}
}
// 此处默认继承父类的 @MyAnnotation(name = "admin")
class MyTest3 extends MyTest2{
}
此类非常重要,我们需要通过反射获取注解,那么久离不开这个接口,我们的Class类是实现了该接口的,那就说明,我们可以通过class对象来获取到class成员上的注解。
来我们看下API:
Modifier and Type | Method and Description |
---|---|
|
getAnnotation(类 返回该元素的,如果这样的注释 *,*否则返回null指定类型的注释。 |
Annotation[] |
getAnnotations() 返回此元素上 存在的注释。 |
default |
getAnnotationsByType(类 返回与此元素相关 联的注释 。 |
default |
getDeclaredAnnotation(类 如果这样的注释 直接存在 ,则返回指定类型的元素注释,否则返回null。 |
Annotation[] |
getDeclaredAnnotations() 返回 直接存在于此元素上的注释。 |
default |
getDeclaredAnnotationsByType(类 如果此类注释 直接存在或 *间接存在,*则返回该元素的注释(指定类型)。 |
default boolean |
isAnnotationPresent(类 extends Annotation> annotationClass) 如果此元素上 存在指定类型的注释,则返回true,否则返回false。 |
MyAnnotation :自定义注解
import java.lang.annotation.*;
/**
* Created with IntelliJ IDEA.
*
* @Author: compass
* @Date: 2021-10-24-19:53
* @Version:1.0
* @Description: 自定义注解
*/
// 自定义一个MyAnnotation注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.TYPE_USE,ElementType.FIELD})
public @interface MyAnnotation {
String className()default "";
Color[] coloType()default Color.RED; //默认红色
String tableName()default "";
String attribute()default "";
String Autowired()default "yes"; // yes表示自动装配,no表示不自动装备
}
enum Color{
RED,GREEN,BLACK,BLUE;
}
2.Student:实体类
/**
* Created with IntelliJ IDEA.
*
* @Author: compass
* @Date: 2021-10-24-21:48
* @Version:1.0
* @Description:
*/
public class Student {
private int id;
private String name;
private String email;
private int status;
private static Student student;
public String show(String message){
System.out.println("Student类的 show(String message) 方法被调用");
System.out.println("message = "+message);
return " call success";
}
public Student(int id, String name, String email, int status) {
this.id = id;
this.name = name;
this.email = email;
this.status = status;
} public Student() {
}
/**
* 单例模式,双重检测机制(多线程下安全)返回一个student对象
* @return 返回一个单例的student对象
*/
public static Student getInstance(){
if (student==null){
synchronized (Student.class){
if (student==null){
student= new Student();
}
}
}
return student;
}
public int getId() {
return id;
}
public void setStudent(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", status=" + status +
'}';
}
}
MyTest2:核心方法init()
import org.junit.jupiter.api.Test;
import java.io.FileReader;
import java.lang.reflect.Field;
import java.util.Properties;
/**
* Created with IntelliJ IDEA.
*
* @Author: compass
* @Date: 2021-10-24-20:02
* @Version:1.0
* @Description:
*/
@MyAnnotation(Autowired = "yes")
public class MyTest2 {
@MyAnnotation(Autowired = "yes") // 如果把属性值改为 no,将调用失败
private static Student student;
public static void init(){
// 虽然这种方式比较复杂,也许我没有设计的那么好,但是可以联合注解和反射做一个小练习,
// 对注解和反射有个理解(当然可以把这段代码写在静态代码块中)
try {
Class<MyTest2> aClass = MyTest2.class;
// 获取到 MyTest2 中的student属性
Field studentFiled = aClass.getDeclaredField("student");
studentFiled.setAccessible(true); // 将该字段先设置为可访问,以免后面使用到出错
// 判断 MyTest2 中的student属性中是否有MyAnnotation注解
if (studentFiled.isAnnotationPresent(MyAnnotation.class)){
// 获取到注解中的属性值
String flag = studentFiled.getAnnotation(MyAnnotation.class).Autowired();
// 如果注解中的属性值==yes,那么久创建一个Student的实例对象,赋值给student
if (flag.equals("yes")){
// 读取config.properties配置文件中的studentClassName属性值
Properties p = new Properties();
FileReader configFile = new FileReader("src/main/resources/config.properties");
p.load(configFile);
String className = p.get("studentClassName").toString();
// 使用反射机制创建一个对象 赋值给student
Class<?> forName = Class.forName(className.trim());
student = (Student)forName.newInstance();
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("初始化异常");
}
}
@Test
void test1() throws Exception{
init(); // 只要是调用成功我们可以直接执行以下代码的
// 如果断言失败,请在idea中的 jvm option中设置:-ea
assert student!=null; // 断言判断student是否为空,如果为空抛出 java.lang.AssertionError 异常
String result = student.show("hello world!");
System.out.println("call result = "+result);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbRIdAaR-1635165117962)(C:\Users\14823\AppData\Roaming\Typora\typora-user-images\image-20211024230433211.png)]
当然还有别的更好的办法,话几个小时看了下反射和注解,如果有总结不太到位的地方请多多谅解。
import java.lang.annotation.*;
/**
* Created with IntelliJ IDEA.
*
* @Author: compass
* @Date: 2021-10-24-19:53
* @Version:1.0
* @Description: 自定义注解
*/
// 自定义一个MyAnnotation注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
public @interface MyAnnotation {
String[] name()default "";
String tableName()default "";
String attribute()default "";
String Autowired()default "yes"; // yes表示自动装配,no表示不自动装备
}
// 枚举类
enum Color{
RED,GREEN,BLACK,BLUE;
}
// 自定义一个MyAnnotation2注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@interface MyAnnotation2 {
Color[] coloType() default Color.RED; //默认红色
}
package test;
import org.junit.jupiter.api.Test;
import java.io.FileReader;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Properties;
public class MyTest2 {
@MyAnnotation(Autowired = "yes") // 单个属性
private static Student student;
// 使用自定义注解中带有枚举类型数组的属性
@MyAnnotation2(coloType = {Color.RED,Color.BLACK,Color.GREEN,Color.BLUE}) //枚举类型数组
String colorType;
@MyAnnotation(name = {"admin","root","jack"}) //String类型数组
String name;
}
class MyTest3{
// 获取字段属性上枚举类型数组的属性值
@Test
void test2() throws Exception {
Class<MyTest2> aClass = MyTest2.class;
Field field = aClass.getDeclaredField("colorType");
MyAnnotation2 annotation = field.getAnnotation(MyAnnotation2.class);
Color[] colors = annotation.coloType();
for (Color color : colors) {
System.out.println(color);
}
}
// 获取字段上String类型数组的属性值
@Test
void test3() throws Exception {
Class<MyTest2> aClass = MyTest2.class;
Field field = aClass.getDeclaredField("name");
boolean b = field.isAnnotationPresent(MyAnnotation.class);
System.out.println(Arrays.toString(field.getDeclaredAnnotation(MyAnnotation.class).name()));
}
// 获取字段上单个属性值
@Test
void test4() throws Exception{
Class<MyTest2> aClass = MyTest2.class;
Field student = aClass.getDeclaredField("student");
if ( student.isAnnotationPresent(MyAnnotation.class)){
String value = student.getDeclaredAnnotation(MyAnnotation.class).Autowired();
System.out.println("value="+value);
}
}
}
枚举是一种特殊的常量类,构造方法默认是强制私有化的。(感觉枚举也有点忘了,大概的记录一下,不需要的可以直接跳过)
枚举类型的每个自定义必须采用常量的命名方式,每个常量类型的字段属性写好注释,明确数据的用途【枚举代码编写规范】
public enum Color {
RED, GREEN, BLANK, YELLOW
}
enum Color{
RED,GREEN,BLACK,BLUE;
}
@Test
void test4(){
Color color = Color.RED;
switch (color){
case RED:
System.out.println("read");break;
case BLACK:
System.out.println("black");break;
case BLUE:
System.out.println("blue");break;
default:
System.out.println("green");
}
}
public enum Color {
READ(1,"红色"),GREEN(2,"绿色"),BLACK(3,"黑色"),BLUE(4,"蓝色");
private String name;
private int index;
// 构造方法
Color(int index, String name) {
this.index=index;
this.name=name;
}
/**
* 根据索引获取名称
* @param index
* @return
*/
public static String getName(int index){
for (Color value : Color.values()) {
if (value.index==index){
return value.name;
}
}
return null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
@Override
public String toString() {
return "Color{" +
"name='" + name + '\'' +
", index=" + index +
'}';
}
}
class MyTest{
public static void main(String[] args) {
// 根据枚举类型字段获取一个枚举类型对象
Color color = Color.valueOf("READ");
System.out.println("index="+color.getIndex());
System.out.println("name="+color.getName());
// 根据获取到枚举类中的所有属性
Color[] values = Color.values();
for (Color value : values) {
System.out.println("item="+value);
}
Color black1 = Color.BLACK;
Color black2 = Color.BLACK;
// 是常量,所有无论获取多少次都是相同的,比较可以使用 == ,不必使用 equals
System.out.println(black1==black2);
System.out.println(black1.equals(black2));
}
}
interface Food {
// 水果
enum FRUITS implements Food{
APPLE,BANANA,GRAPE,PEAR
}
//喝的
enum DRINK implements Food{
WATER, COKE, COFFEE
}
}
//实现枚举类型接口
class FoodImpl implements Food{
}
class MyTest2{
public static void main(String[] args) {
// 可以获取到接口中定义的枚举类型组,以及组中定义的枚举属性值
Food[] drinks = FoodImpl.DRINK.values();
System.out.println(Arrays.toString(drinks));
// 可以获取到接口中定义的枚举类型组,以及组中定义的枚举属性值
Food[] fruits = FoodImpl.FRUITS.values();
System.out.println(Arrays.toString(fruits));
}
}