1. Reflection
1. Reflection overview
This is described in the java.lang.reflect package:
Provides classes and interfaces to obtain reflective information about classes and objects. Within security constraints, reflection allows programmatic access to fields about loaded classes, method and constructor information, and allows the use of reflection fields, Methods and constructors operate on their underlying counterparts.
Reflection refers to allowing programmatic access to components of a loaded class (member variables, methods, constructors, etc.).
After knowing what reflection is, let's learn what reflection is specifically about.
Because reflection obtains class information, the first step of reflection first obtains the class. Since the design principle of Java is that everything is an object, the obtained class is actually embodied in the form of an object, called a bytecode object, and represented by the Class class. After the bytecode object is obtained, the components of the class can be obtained through the bytecode object. These components are actually objects. Each member variable is represented by an object of the Field class, and each member method is represented by a Method Each constructor is represented by an object of the Constructor class. Right now:
- Load the class to get the bytecode of the class: Class object
- Get the constructor of the class: Constructor object
- Get the member variables of the class: Field object
- Get the member method of the class: Method object
Let's start learning one by one! ! !
2. Obtain the bytecode of the class (important)
The first step of reflection is to load the bytecode into memory, and we need to obtain the bytecode object.
For example, if there is a Student class, there are three ways to write the bytecode code of the Student class. No matter which method is used, the obtained bytecode object is actually the same.
public class Test1Class{ public static void main(String[] args){ //(Method 1) Obtain the parameter through the static method forName of the Class class as the fully qualified class name Class c2 = Class.forName("com.itheima.d2_reflect.Student"); System.out.println(c1 == c2); //true //(Method 2) Obtain the Class object through the class name.class Class c1 = Student.class; System.out.println(c1.getName()); //Get the full class name System.out.println(c1.getSimpleName()); //get simple class name // (Method 3) Get it through the getClass method in Object Student s = new Student(); Class c3 = s.getClass(); System.out.println(c2 == c3); //true } }
By comparing the addresses of the Class objects obtained by the three methods, we can see that the three of them have the same address, so obtaining the Class objects through these three methods is equivalent. In fact, this is the object obtained in three different stages of the class, which are the compilation, loading and running stages.
After knowing how to get the bytecode object, it is very clear when we look at the equals method that we always used before;
The first step; first judge whether the two objects are the same address. If it is the same address, there is no need to compare and return directly. (compare addresses)
The second step; first judge whether it is null, if it is null, there is no need to compare and return false directly. Then judge whether the bytecode objects of the two objects are the same. Because there is only one copy of the bytecode file of the same class in the memory, if it is different, return it directly (compare the bytecode), if it is the same, it means that it must be the implementation class of the current class. Just cast
Finally: comparing the contents of it. Primitive data types are compared directly, and reference data types are compared by methods. return the result of the comparison
@Override public boolean equals(Object o) { // this o if (this == o) return true; if (o == null || this.getClass() != o.getClass()) return false; Student student = (Student) o; if (age != student.age) return false; return name != null ? name.equals(student.name) : student.name == null; }
3. Get the constructor of the class
The first step of reflection is to get the class object first, and then get the component objects of the class from the class object.
The method used to obtain the constructor in the Class class
method | illustrate |
---|---|
Constructor<?>[] getConstructors() | Returns an array of all constructor objects (only public ones) |
Constructor<?>[] getDeclaredConstructors() | Returns an array of all constructor objects, you can get them if they exist |
Constructor getConstructor(Class<?>... parameterTypes) | Return a single constructor object (only public ones) |
Constructor getDeclaredConstructor(Class<?>... parameterTypes) | Return a single constructor object, you can get it if it exists |
There is a little trick when remembering methods. First of all, we must know the word of the Constructor. If you want to get a multi-band Constructor, then use the long name. That is, the longer the more the more methods. The short one is to get a constructor that is only modified by public
Sample code:
public class Cat{ private String name; private int age; // public parameterless constructor public Cat(){ } // private full parameter constructor private Cat(String name, int age){ } }
get all constructors
public class Test2Constructor(){ @Test public void testGetConstructors(){ //1. The first step of reflection: you must first get the Class object of this class Class c = Cat.class; //2. Get all the constructors of the class Constructor[] constructors = c.getDeclaredConstructors(); //3. Traverse each constructor object in the array. for(Constructor constructor: constructors){ System.out.println(constructor.getName()+"---> Number of parameters:"+constructor.getParameterCount()); } } }
Console result:
com.yfs1024.test.Cat---> Number of parameters: 0 com.yfs1024.test.Cat---> Number of parameters: 2
Get a single constructor
The parameter needs to pass in the class of the corresponding parameter. If it is a packaging type, use packaging class.TYPE
public class Test2Constructor(){ @Test public void testGetConstructor(){ //1. The first step of reflection: you must first get the Class object of this class Class c = Cat.class; //2. Obtain the empty parameter constructor modified by the public class Constructor constructor1 = c.getConstructor(); System.out.println(constructor1.getName()+"---> Number of parameters:"+constructor1.getParameterCount()); //3. Obtain a private modified constructor with two parameters, the first parameter is of type String, and the second parameter is of type int Constructor constructor2 = c.getDeclaredConstructor(String.class,int.class); System.out.println(constructor2.getName()+"---> Number of parameters:"+constructor1.getParameterCount()); } }
Console result:
com.yfs1024.test.Cat---> Number of parameters: 0 com.yfs1024.test.Cat---> Number of parameters: 2
4. The role of reflection acquisition constructor
After getting the constructor, what is the function? It's a good idea, in fact, the role of the constructor is to initialize the object and return it.
Here we need to use the following two methods. Note: these two methods belong to the Constructor and need to be called by the Constructor object.
symbol | illustrate |
---|---|
T newInstance(Object... initargs) | Create an object according to the specified constructor |
public void setAccessible(boolean flag) | Set to true to cancel the access check and perform violent reflection |
As shown in the figure below, constructor1 and constructor2 respectively represent two constructors in the Cat class. Now I'm going to implement these two constructors
Since the constructor is private ly modified, it is necessary to call setAccessible(true) to indicate that checking access control is prohibited, and then call newInstance (actual parameter list) to execute the constructor and complete the initialization of the object.
@Test public void getConstructor() throws Exception { Class<Cat> catClass = Cat.class; Constructor<Cat> constructor = catClass.getDeclaredConstructor(String.class,int.class); constructor.setAccessible(true); // Close Check Access ---> Violent Reflection Cat cat = constructor.newInstance("zs",12); System.out.println(cat); }
After learning the above method, in fact, we generally only use one constructor to obtain objects in development. We never said, come on, traverse a constructor and create a bunch of objects, so we only need to master how to obtain an object. And we generally use no-argument construction, and our common method of obtaining no-argument construction is usually to use the current bytecode Class object to create. As follows: The premise is that the constructor is not private, if so, then the above method can be used
@Test public void getConstructor() throws Exception { Class<Cat> catClass = Cat.class; Cat cat = catClass.newInstance(); System.out.println(cat); }
5. Member variables obtained by reflection and their use
In fact, the routine is the same. The method of obtaining member variables is provided in the Class class, as shown in the figure below.
method | illustrate |
---|---|
Field[] getFields() | Returns an array of all member variable objects (only public ones) |
Field[] getDeclaredFields() | Returns an array of all member variable objects, which can be obtained if they exist |
Field getField(String name) | Return a single member variable object (only public ones) |
Field getDeclaredField(String name) | Return a single member variable object, you can get it if it exists |
Suppose there is a Cat class which has several member variables, use the methods provided by the Class class to get the objects of the member variables.
After executing the above code, we can see the output on the console, the name and type of each member variable.
How to use it after getting the object of the member variable?
The methods for assigning and obtaining values to member variables are provided in the Filed class, as shown in the figure below.
symbol | illustrate |
---|---|
void set(Object obj, Object value): | assignment |
Object get(Object obj) | Get the value. |
@Test public void getFiles() throws Exception { // Member variables are private, constructors are public Class<Cat> catClass = Cat.class; Cat cat = new Cat("little flower",23); Field name = catClass.getDeclaredField("name"); name.setAccessible(true); name.set(cat,"Xiaohua's father"); System.out.println(name.get(cat)); // Xiaohua's father }
Emphasize that the method of setting and obtaining the value of the Filed class needs to be called by the object of the Filed class, and whether it is setting the value or obtaining the value, it needs to depend on the object to which the variable belongs.
6. Reflection to obtain member methods (important)
In the reflection package in Java, each member method is represented by a Method object, and the member method object in the class can be obtained through the methods provided by the Class class. as follows:
method | illustrate |
---|---|
Method[] getMethods() | Returns an array of all member method objects (only public ones can be obtained), which can also be obtained from the public method of the parent class |
Method[] getDeclaredMethods() | Returns an array of all member method objects, you can get it if it exists |
Method getMethod(String name, Class<?>... parameterTypes) | Return a single member method object (only public ones) |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | Return a single member method object, you can get it if it exists |
Next, let's demonstrate it with code: Suppose there is a Cat class, and there are several member methods in the Cat class
public class Cat{ private String name; private int age; public Cat(){ System.out.println("The empty parameter constructor executes"); } private Cat(String name, int age){ System.out.println("There is a parameter construction method executed"); this.name=name; this.age=age; } private void run(){ System.out.println("(>^ω^<)Meow runs so fast~~"); } public void eat(){ System.out.println("(>^ω^<)Meow loves cat food~"); } private String eat(String name){ return "(>^ω^<)Meow loves to eat:"+name; } public void setName(String name){ this.name=name; } public String getName(){ return name; } public void setAge(int age){ this.age=age; } public int getAge(){ return age; } }
Next, get all the member methods in the Cat class through reflection, each member method is a Method object
public class Test3Method{ public static void main(String[] args){ //1. The first step of reflection: first obtain the Class object Class c = Cat.class; //2. Get all member methods in the class Method[] methods = c.getDecalaredMethods(); //3. Traverse each method object in this array for(Method method : methods){ System.out.println(method.getName()+"-->"+method.getParameterCount()+"-->"+method.getReturnType()); } } }
Method name Number of parameters Return value type
getName-->0-->class java.lang.String run-->0-->void setName-->1-->void eat-->1-->class java.lang.String eat-->0-->void setAge-->1-->void getAge-->0-->int
Get a single specified member method:
public static void main(String[] args) throws Exception { //1. The first step of reflection: first obtain the Class object Class c = Cat.class; //2. Get the eat method in the class Method eat = c.getDeclaredMethod("eat"); System.out.println("Number of parameters:"+eat.getParameterCount()); // get eat(String name) Method eat1 = c.getDeclaredMethod("eat", String.class); System.out.println("Number of parameters:"+eat1.getParameterCount()); }
Console:
Number of parameters: 0 Number of parameters: 1
After obtaining the member method, what is the function?
The method is provided in the Method class, and the method can be executed by itself.
symbol | illustrate |
---|---|
Object invoke(Object obj, Object... args) | Run the method Parameter 1: call the method with the obj object Parameter 2: the passed parameters of the calling method (if not written) Return value: the return value of the method (if not written) |
Execute the run() method and the eat(String name) method. Look at the code below the dividing line
//1. The first step of reflection: first obtain the Class object Class c = Cat.class; //4. Get the private modified run method and get the Method object Method run = c.getDeclaredMethod("run"); //To execute the run method, the permission check needs to be canceled before execution Cat cat = new Cat(); run.setAccessible(true); Object rs1 = run.invoke(cat); // No return value returns null System.out.println(rs1); //5. Obtain the private modified eat(String name) method and get the Method object Method eat = c.getDeclaredMethod("eat",String.class); eat.setAccessible(true); Object rs2 = eat.invoke(cat,"the fish"); System.out.println(rs2);
The empty parameter constructor executes (>^ω^<)Meow runs so fast~~ null (>^ω^<)Meow loves to eat:the fish
7. Application cases of reflection
Case 1
Below we will make a comparison between using and not using reflection to appreciate the beauty of reflection.
Requirements: There is a fruit interface, there are some implementation classes such as apples and oranges, and the parent class has a method for obtaining juice. There is also a filtering class for processing the extracted fruit juice.
Instead of using reflection, we need to implement the following method: Fruit interface
// Fruit interface, there is a method to get juice public interface Fruit { public void getJuice(); }
Apples
public class Apple implements Fruit{ @Override public void getJuice() { System.out.println("squeeze apple juice"); } }
Orange Juice:
public class Orange implements Fruit{ @Override public void getJuice() { System.out.println("get a glass of orange juice"); } }
Filter class:
public class MainGuoLv { public static void main(String[] args) throws Exception { // guoLv(new Apple()); guoLv(new Orange()); } // filter method public static void guoLv(Fruit fruit){ // get juice fruit.getJuice(); System.out.println("filter complete"); } }
When we learn the advantage of polymorphism, we only need to use the parent class to receive the subclass object, and we can use one method to process different water methods.
But there is a problem, if I filter the orange juice now, I need to create an orange juice object. Then if I want to filter the apple juice, I need to pass in an apple juice object. Seems simple enough, right? But there is a problem, that is, if this function is launched, the code after the launch is a compiled class file, that is, a bytecode file. Then if we want to change the requirements again, filter apple juice for a while and orange juice for a while, then it's over, we need to modify the java file, and then compile it into a class file. This is a local modification. Then if you go to a configuration place, you have to recompile every time, isn't it too much trouble.
Then let's see how to solve it with reflection? We can manipulate bytecode files
We don’t move the Fruit interface, apple class, and orange class. We only need to modify the filter class by reflection: the usual way is this, we configure a file, because txt files will not be compiled (properties files also Yes), we have learned to get the bytecode object of the class through the class name. With the bytecode object, we can operate the methods in this class. The implementation is as follows:
Prepare a Fruit.txt, the content is as follows
fruitName=com.yfs1024.reflectTest02.Apple
The code after modifying the filter class to reflection is as follows:
public class MainGuoLv { public static void main(String[] args) throws Exception { // (1) Get the fully qualified class name of the file Properties properties = new Properties(); // (2) Load the file, properties.load(new FileInputStream("javaEE-day14\\fruit.txt")); // (3) Get the value by key String fruitName = properties.getProperty("fruitName"); // (4) After getting the fully qualified class name, you can get the bytecode object through the static method in Class Class<?> aClass = Class.forName(fruitName); // (5) Create objects through bytecode files Fruit fruit = (Fruit)aClass.newInstance(); // This is commonly used to create objects. Of course, it can also be created by obtaining the constructor. // (6) Call method guoLv(fruit); } // filter method public static void guoLv(Fruit fruit) { fruit.getJuice(); System.out.println("filter complete"); } }
It may look like a lot of code, but it has been unsealed by the java file. At this time, if we want to filter the orange juice, we can directly modify it in the Fruit.txt file. No need to change the source code
fruitName=com.yfs1024.reflectTest02.Orange
Case 2:
When we learned the method just now, we also saw that we can get all the methods through the bytecode object (Class object). So we can make some articles in the method? certainly.
There is such a tool class: I want to input specific data through the console, and implement the method to call specific functions, so let’s look at the benefits of reflection by not using reflection and using reflection
public class MyUtils { public static int getSum(int a,int b){ return a + b; } public static int minus(int a,int b){ return a - b; } public static int multiply(int a,int b){ return a * b; } public static int by(int a,int b){ return a / b; } }
Code without reflection:
Scanner scanner = new Scanner(System.in); System.out.println("Please enter the first number"); int a = scanner.nextInt(); System.out.println("Please enter the second number"); int b = scanner.nextInt(); System.out.println("Please enter the method name to execute"); String methodName = scanner.next(); // Judgment, pass in parameters and execute Here you will find that if one or two methods are needed, if ten or twenty, is it necessary to write if(methodName.equals("getSum")){ int sum = Utils.getSum(a, b); System.out.println(sum); }else if(methodName.equals("minus")){ int sum = Utils.minus(a, b); System.out.println(sum); }else if(methodName.equals("multiply")){ int sum = Utils.multiply(a, b); System.out.println(sum); }else if(methodName.equals("by")){ int sum = Utils.by(a, b); System.out.println(sum); }
Use reflection to accomplish the requirement:
Scanner scanner = new Scanner(System.in); System.out.println("Please enter the first number"); int a = scanner.nextInt(); System.out.println("Please enter the second number"); int b = scanner.nextInt(); System.out.println("Please enter the method name to execute"); String methodName = scanner.next(); Class<Utils> utilsClass = MyUtils.class; Method method = utilsClass.getMethod(methodName, int.class, int.class);//getSum // I thought it was a static method, so there is no need to pass in an object, just pass in null Object result = method.invoke(null,a,b); System.out.println(result);
Moreover, no matter how many similar methods there are, of course, because this is an example, the parameters are all the same. If they are different, we can pass in an array when getMethod and invoke by obtaining some judgments. , because the second parameter is a variable parameter.
2. Notes
1. Understanding annotations & defining annotations
Annotations, like reflections, are used as frameworks. The purpose of learning annotations here is actually to pave the way for future learning of frameworks or frameworks.
How to learn the annotation? Like the reflection learning routine, we first fully understand annotations, master the definition and usage format of annotations, and then learn its application scenarios.
Let's first understand what is an annotation?
Java annotations are special tags in the code, such as @Override, @Test, etc., whose function is to let other programs decide how to execute the program based on the annotation information.
For example: the @Test annotation of the Junit framework can be used on a method to mark the method as a test method, and the method marked by @Test can be executed by the Junit framework.
Another example: the @Override annotation can be used on a method to mark the method as an overridden method, and the method marked by the @Override annotation can be recognized by IDEA for syntax checking.
- Annotations can be used not only on methods, but also on classes, variables, constructors, etc.
The @Test annotations and @Overide annotations we mentioned above are defined by others for us to use. If we need to develop the framework ourselves in the future, we need to define the annotations ourselves. So if you can know how to use it, if you don’t know how to use it
Then we learn custom annotations
The format of the custom annotation is shown in the figure below
The following notes and considerations are important
in annotation:The return value type of the abstract method is limited to the following basic type string type bytecode type enumerated type Annotation type One-dimensional arrays of the above types Abstract methods cannot have parameters Precautions: (1)When the method named value when, Use annotations, if only right value when assigning, value can be omitted (2)When the return value of the method is an array, But when your value is only one,{}can be omitted!!!! (3)Our abstract method can be accessed via default Given a default return value!!!,If the default return value is given, the user does not need to rewrite!!!!!!!!!!!!
Annotations can be defined in the following ways:
public @interface A { //define constant int age = 10; //define abstract method int value() default 18; String method1() default "jack"; String[] method2() default {}; Class method3(); Week method4(); // enumerate Override method5(); String[] method6(); }
For example: Now we customize a MyTest annotation
public @interface MyTest{ String aaa(); boolean bbb() default true; //default true means the default value is true, and it is not necessary to assign a value when using it. String[] ccc(); }
After defining the MyTest annotation, we can use the MyTest annotation to mark on the class, method, etc. Note that the @ symbol needs to be added when using annotations, as follows
@MyTest1(aaa="bull devil",ccc={"HTML","Java"}) public class AnnotationTest1{ @MyTest(aaa="iron fan princess",bbb=false, ccc={"Python","front end","Java"}) public void test1(){ } }
Note: If the attribute name of the annotation is value, and only value has no default value, the value name can be omitted when using annotations. For example, now redefine a MyTest2 annotation
public @interface MyTest2{ String value(); //special attribute int age() default 10; }
After defining the MyTest2 annotation, mark @MyTest2 on the class. At this time, the value attribute name can be omitted. The code is as follows
@MyTest2("Sun Wukong") //Equivalent to @MyTest2(value="Monkey King") @MyTest1(aaa="bull devil",ccc={"HTML","Java"}) public class AnnotationTest1{ @MyTest(aaa="iron fan princess",bbb=false, ccc={"Python","front end","Java"}) public void test1(){ } }
What is the essence of annotation?
To find out what the essence of the annotation is, we can decompile the bytecode of the annotation and use the XJad tool to decompile it. After decompiling the bytecode of MyTest1 annotation, we will find:
1.MyTest1 Annotations are essentially interfaces, and each annotation interface inherits children Annotation interface 2.MyTest1 Properties in annotations are essentially abstract methods 3.@MyTest1 actually as MyTest The implementation class object of the interface 4.@MyTest1(aaa="Sun Wukong",bbb=false,ccc={"Python","front end","Java"})Inside the attribute value, you can call aaa(),bbb(),ccc()method is obtained. [Don't worry, keep reading, we will use it when parsing annotations]
2. Meta annotations
Next, we need to learn several special annotations, called meta-annotations.
What are meta annotations?
That is: the annotation of the modification annotation haha, it is a bit convoluted, but it is easy to understand
This is the case, we roughly know from the previous reflections that there are many stages in the middle from the java source code to the actual operation, so we need to use an annotation to tell the jvm when this class or method is what we need. Secondly, we can use some annotations to restrict the location of this annotation, whether it is in a class, a method, or a member variable.
These two annotations are @Target, @Retetion
@Target It is used to declare that annotations can only be used in those positions, such as: on classes, on methods, on member variables, etc. @Retetion It is used to declare the annotation retention period, such as: source code period, bytecode period, runtime period
Note: Our usual cycle for keeping annotations is RUNTIME
Example:
@Target The use of meta annotations: such as defining a MyTest3 Annotate, and add@Target Annotations are used to declare MyTest3 where to use
Next, we use @MyTest3 on the class to observe whether there is an error, and then use @MyTest3 on the method and variable to observe whether there is an error
If we define the MyTest3 annotation, use the @Target annotation attribute value to write as follows
//Declare that the @MyTest3 annotation can only be used on classes and methods @Target({ElementType.TYPE,ElementType.METHOD}) public @interface MyTest3{ }
At this point, the use of @Target meta-annotations may be more than enough, let’s learn @Retetion meta-annotations
The use of @Retetion meta-annotation: When defining the MyTest3 annotation, add the @Retetion annotation to the MyTest3 annotation to declare the retention period of the MyTest3 annotation
@Retetion It is used to declare the annotation retention period, such as: source code period, bytecode period, runtime period @Retetion(RetetionPloicy.SOURCE): Annotations are retained until the source code period, and there will be no bytecode @Retetion(RetetionPloicy.CLASS): Annotations are retained in the bytecode, runtime annotations are gone @Retetion(RetetionPloicy.RUNTIME): Annotations persist until runtime
[When writing code by yourself, it is more common to keep it until the runtime]
//Declare that the @MyTest3 annotation can only be used on classes and methods @Target({ElementType.TYPE,ElementType.METHOD}) //In the code that controls the use of @MyTest3 annotations, @MyTest3 is reserved until runtime @Retetion(RetetionPloicy.RUNTIME) public @interface MyTest3{ }
3. Analyzing annotations (combined cases of annotations and reflections)
Through the previous study, we can define annotations by ourselves, and we can also mark the annotations we define on the class or method, but it always feels a bit awkward. After adding annotations to classes, methods, variables, etc., we have nothing to do. ! ! !
Next, we're going to do something. We can get the annotation objects on the class, method, and variable through reflection technology, and then get the attribute value on the annotation by calling the method. We call the process of obtaining position annotations on classes, methods, variables, etc. and annotation attribute values as parsing annotations.
The analysis annotation routine is as follows
1.If the annotation is on the class, first get the bytecode object of the class, and then get the annotation on the class 2.If the annotation is on the method, first get the method object, and then get the annotation on the method 3.If the annotation is on the member variable, first get the member variable object, and then get the annotation on the variable In short: whoever the annotation is on, get the first person, and then use whoever gets the annotation
The operation of annotations often needs to be parsed. The parsing of annotations is to judge whether there are annotations, and if there are annotations, the content is parsed out.
Interfaces related to annotation parsing
Annotation: the top-level interface of annotations, all annotations are objects of type Annotation
AnnotatedElement: This interface defines the parsing methods related to annotation parsing
method | illustrate |
---|---|
Annotation[] getDeclaredAnnotations() | Get all annotations used on the current object, returning an array of annotations. |
T getDeclaredAnnotation(Class annotationClass) | Obtain the corresponding annotation object according to the annotation type |
boolean isAnnotationPresent(Class annotationClass) | Determine whether the current object uses the specified annotation, if used, return true, otherwise false |
All class components Class, Method, Field, and Constructor implement the AnnotatedElement interface and they all have the ability to parse annotations:
Let’s take a look at a case to demonstrate the code writing of parsing annotations
(1) Define a comment for MyTest4
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) public @interface MyTest4 { String value(); double aaa() default 100; String[] bbb(); }
(2) Define a class Demo
@MyTest4(value = "class value Schrödinger's cat",aaa = 100,bbb = {"on the class bbb Laplacemon","on the class bbb Laplacemon 2"}) public class Demo { public static void main(String[] args) { } @MyTest4(value = "method on zeno's tortoise",aaa = 800,bbb = {"Method on Maxwell's Demon 1","Method on Maxwell's Demon 2"}) private void test1() { } }
③ Write a test class AnnotationTest3 to parse the MyTest4 annotation on the Demo class
public class AnnotationTest3 { public static void main(String[] args) throws NoSuchMethodException { // Get the bytecode object of the annotated class Class<Demo> demoClass = Demo.class; // Get the annotations in the class MyTest4 annotation = demoClass.getAnnotation(MyTest4.class); // Get the value attribute of the MyTest4 annotation in the class String value = annotation.value(); System.out.println("in class value the value of:" + value); // Get the value attribute of the MyTest4 annotation in the class double aaa = annotation.aaa(); System.out.println("in class aaa the value of:" + aaa); // Get the bbb attribute in the MyTest4 annotation in the class String[] bbb = annotation.bbb(); // Traverse the output for (String s : bbb) { System.out.println("in class bbb[]the value of:" + s); } //(2) in the acquisition method Method test1 = demoClass.getDeclaredMethod("test1"); MyTest4 annotation1 = test1.getAnnotation(MyTest4.class); // Get the value attribute of the MyTest4 annotation in the method String value1 = annotation1.value(); System.out.println("method value the value of:" + value1); // Get the value attribute of the MyTest4 annotation in the method double aaa1 = annotation1.aaa(); System.out.println("method in aaa the value of:" + aaa1); // Get the bbb attribute in the MyTest4 annotation in the method String[] bbb1 = annotation1.bbb(); // Traverse the output for (String s : bbb1) { System.out.println("method bbb[]the value of:" + s); } } }
in class value the value of:class value Schrödinger's cat in class aaa the value of:100.0 in class bbb[]the value of:on the class bbb Laplacemon in class bbb[]the value of:on the class bbb Laplacemon 2 method value the value of:method on zeno's tortoise method in aaa the value of:800.0 method bbb[]the value of:Method on Maxwell's Demon 1 method bbb[]the value of:Method on Maxwell's Demon 2
4. Application scenarios of annotations
Next, let’s learn about the application scenarios of annotations. Annotations are used to write frameworks. For example, now we want to simulate Junit to write a test framework, which requires methods with @MyTest annotations to be executed by the framework, but methods without @MyTest annotations cannot implemented by the framework.
The first step: first define a MyTest annotation
@Target(ElementType.METHOD) @Retetion(RetetionPloicy.RUNTIME) public @interface MyTest{ }
Step 2: Write a test class AnnotationTest4, and define several methods marked by @MyTest annotation in the class
public class AnnotationTest4{ @MyTest public void test1(){ System.out.println("=====test1===="); } @MyTest public void test2(){ System.out.println("=====test2===="); } public void test3(){ System.out.println("=====test2===="); } public static void main(String[] args){ AnnotationTest4 a = new AnnotationTest4(); //1. Get the Class object first Class c = AnnotationTest4.class; //2. Parse all method objects in the AnnotationTest4 class Method[] methods = c.getDeclaredMethods(); for(Method m: methods){ //3. Determine whether there is a MyTest annotation on the method, and execute the method if there is one if(m.isAnnotationPresent(MyTest.class)){ m.invoke(a); } } } }