Detailed explanation of JAVA reflection concept and use
1, What is reflex?
Reflection: the soul of frame design
Framework: semi-finished software. Software development can be carried out on the basis of framework to simplify coding
Reflection: encapsulates the components of a class as other objects, which is the reflection mechanism
Benefits:
You can manipulate these objects while the program is running.
It can decouple and improve the scalability of the program.
Photo source: https://blog.csdn.net/qsbbl/article/details/85801571
Definition: java reflection mechanism is to know all the properties and methods of any class in the running state, and to call any method of any object. This function of dynamically obtaining and calling object methods is called the reflection mechanism of Java language.
Reflection is to know all the properties and methods of a Class through the Class object (bytecode file). That is to say, through reflection, we can get constructors, objects, properties and methods
Unlike the class we can see now, in the JAVA framework, many classes are invisible to us. We can't directly use the class name to get objects, but only through reflection.
Picture source: https://blog.csdn.net/sinat_/article/details/71799078
2, There are three ways to get a Class object:
To use reflection, you must first get a class object representing the bytecode, which is used to represent the. Class file (bytecode)
1. Get the corresponding Class object through the object of this Class (it is basically not used)
//The first way: Student > class object through getClass() method //Student is an empty class Student student = new Student(); //Here we'll leave out generics, class <? > Class stuClass = student.getClass(); //Corresponding Class object obtained System.out.println(stuClass); System.out.println(stuClass.getName()); //Get the name of the Class object
Output: class fanshe.Student fanshe.Student
But it should be noted that the first one is basically not used by us, which is obviously contrary to the reflection mechanism (why do you have a Class object student to use reflection to get a Class object
2. Get by class name. Class static attribute (relatively simple)
//The second method: after each class is created, there will be a default static class attribute to return the class object of the class //Note that any data type (including the basic data type) has a "static" class attribute Class stuClass2 = Student.class; System.out.println("Is it the same class object?"+(stuClass==stuClass2));
Result: true
It should be noted that although this method is relatively simple, it needs to import packages, or it will compile incorrectly (compare with the third method, fully qualified class name)
3. Obtained by the static method forName() method in the Class (the most common)
//The third method: Class.forName("fanshe.Student"); note that the parameter must be the fully qualified class name of the class try { Class stuClass3 = Class.forName("fanshe.Student"); //System.out.println(stuClass3); output is still class fanshe.Student System.out.println("Is it the same class object?"+(stuClass3==stuClass2)); } catch (ClassNotFoundException e) { e.printStackTrace(); }
Result: true
Conclusion: the same bytecode file (*. class) can only be loaded once in the process of running a program, and the class object obtained by either way is the same.
2, Get the constructor of the Class through the Class object:
After getting the Class object, we can get the constructor of the Class through the Class object. There are many methods, one by one:
First create a Class object
Class stuClass = Student.class;//We took the second approach
This is my Student class, which constructs multiple constructors for demonstration
public class Student { //List of parameters with different modifiers through multiple constructors Student(String name) { System.out.println("use default Decorated Student Of contains a String Constructor of parameter:"+name); } public Student() { System.out.println("use public Decorated Student Parameterless constructor of"); } public Student(String name,int age) { System.out.println("use public Decorated Student Constructor with two parameters for:"+name+age); } public Student(boolean sex) { System.out.println("use public Decorated Student Constructor with one parameter:"+sex); } protected Student(int age) { System.out.println("use protected Decorated Student Constructor with one parameter:"+age); } private Student(String name,int age,boolean sex) { System.out.println("use private Decorated Student Constructor with three parameters:"+name+age+sex); } }
1.getDeclaredConstructors()
Returns an array of Constructor objects that reflect all the Constructor methods declared by the Class represented by this Class object
Constructor[] conArray01 = stuClass.getDeclaredConstructors(); for (Constructor constructor : conArray01) { System.out.println(constructor); }
//Output result: output all constructors private fanshe.Student(java.lang.String,int,boolean) protected fanshe.Student(int) public fanshe.Student(boolean) public fanshe.Student(java.lang.String,int) public fanshe.Student() fanshe.Student(java.lang.String)
2.getConstructors()
Returns an array of Constructor objects that reflect all public Constructor methods of the Class represented by this Class object.
Constructor[] consArray02 = stuClass.getConstructors(); for (Constructor constructor : consArray02) { System.out.println(constructor); } //The output is all the public decorated construction methods: //public fanshe.Student(boolean) //public fanshe.Student(java.lang.String,int) //public fanshe.Student()
3.getConstructor(Class<?>... parameterTypes)
Returns a Constructor object that reflects the specified public Constructor of the Class represented by this Class object.
//Parameter is a variable length parameter: gets the class object corresponding to the parameter type of the specified constructor //Constructor decorated with public Constructor con0 = stuClass.getConstructor(null); //The parameterless constructor parameter is null or empty, which is equivalent to stuClass.getConstructor() Constructor con = stuClass.getConstructor(boolean.class); //boolean System.out.println(con0); System.out.println(con); //Output: //public fanshe.Student() //public fanshe.Student(boolean)
Note that the output is of public type and can only be of public type.
4.getDeclaredConstructor(Class<?>... parameterTypes)
Returns a Constructor object that reflects the specified Constructor of the Class or interface represented by this Class object.
Constructor con1 = stuClass.getDeclaredConstructor(int.class); System.out.println(con1); Constructor con2 = stuClass.getDeclaredConstructor(String.class,int.class,boolean.class); System.out.println(con2); //Output: //protected fanshe.Student(int) //private fanshe.Student(java.lang.String,int,boolean)
Conclusion:
① for 3 and 4 with parameters, the returned one is a Constructor (it's easy to understand that the Constructor overload requires different parameter lists). 1 and 2 without parameters return arrays of type Constructor. You can remember that if you add s to the method name, for example, getConstructors(), the return will be multiple constructors. If you do not have s, it will be one.
(2) the method name contains the constructor access modifier returned by Declared without restriction, and the constructor returned by Declared example getConstructors() is all public decorated constructors.
In short: there are more construction methods for adding s than for returning without adding s, more for adding Declared than for not adding, and the most for adding all (getDeclaredConstructors())
So how do we get the constructor to create the object?
3, Create an object from the constructor obtained:
//Here we need to catch an exception //Because our constructor is private and cannot be created in other classes, we use the setAccessible() method to create /* private Student(String name,int age,boolean sex){ System.out.println("The constructor with three parameters of the Student decorated with private: "+ name + age + sex"; }*/ try { con2.setAccessible(true); //Ignore the access modifier of the constructor and lift the private restriction Object object = con2.newInstance("Zhang San",10,true); //This sentence is equivalent to new Student("Zhang San", 10,true); Student student = (Student) object; //A parent reference to a subclass object is converted back to a subclass reference } catch (Exception e) { e.printStackTrace(); } //Output: constructor with three parameters of Student decorated with private: Zhang San 10true
Be careful:
xxx.setAccessible(true) is to remove the private restriction. It will appear frequently in the future. There will be no more explanation after that
4, Get member variable through Class object
There are many exceptions to be thrown in the following methods, so I will omit them
First define a Teacher class:
public class Teacher { public Teacher() { } public String name; protected int age; boolean sex; private String address; @Override public String toString() { return "Teacher [name=" + name + ", age=" + age + ", sex=" + sex + ", address=" + address + "]"; } }
Class teaClass = Class.forName("fanshe.Teacher"); //Create a Class object (the third way) //Get all the public fields in the Teacher class Field[] f = teaClass.getFields(); for (Field field : f) { System.out.println(field); } //Get all the fields in the Teacher class (including various access modifiers) System.out.println("===========All fields=======================>"); Field[] df = teaClass.getDeclaredFields(); for (Field field : df) { System.out.println(field); } System.out.println("===========Public specific fields=======================>"); // Get public specific fields Field field = teaClass.getField("name"); System.out.println(field); System.out.println("===========Specific fields=======================>"); // Get specific fields Field field2 = teaClass.getDeclaredField("age"); System.out.println(field2); /** * Set specific values for fields * Parameter 1: object of this class * Parameter 2: assign a value to a specific attribute */ Object object = teaClass.getConstructor().newInstance();//Equivalent to Object object = new Teacher(); field.set(object, "Zhang Sanfeng"); System.out.println("Name is:"+((Teacher)object).name);
Output results:
public java.lang.String fanshe.Teacher.name //All public fields ===========All fields=======================> public java.lang.String fanshe.Teacher.name protected int fanshe.Teacher.age boolean fanshe.Teacher.sex private java.lang.String fanshe.Teacher.address ===========Public specific fields=======================> public java.lang.String fanshe.Teacher.name ===========Specific fields=======================> protected int fanshe.Teacher.age //Name: Zhang Sanfeng
The memory method is the same as above, so I won't repeat it here.
5, Get the method of the Class through the Class object
First, an Employee class is created:
public class Employee { public Employee() {} public String name; protected double money; String address; private long number; protected void getDayMoney(String name) { System.out.println("I am Employee Protected way to earn daily salary,One parameter is:"+name); } public void getweekMoney(String name,double money) { System.out.println("I am Employee Public way to get weekly salary,No parameters.."); } void getMonthMoney() { System.out.println("I am Employee Default method of getting monthly salary,No parameters.."); } private void getYearMoney(int age) { System.out.println("I am Employee A private method of getting an annual salary and a monthly salary,One parameter is:"+age); } public static void main(String[] args) { System.out.println("Employee Medium main()Method executed..."); System.out.println(Arrays.toString(args)); } @Override public String toString() { return "Employee [name=" + name + ", money=" + money + ", address=" + address + ", number=" + number + "]"; } }
Obtain the method of this Class through the Class object:
Class<?> emplClass = Class.forName("fanshe.Employee");
//1. Get all the public methods (including the methods of the parent class) Method[] methods = emplClass.getMethods(); for (Method method : methods) { System.out.println(method); }
//Output: public java.lang.String fanshe.Employee.toString() public void fanshe.Employee.getweekMoney(java.lang.String,double) public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
Be careful:
1. The above methods are all public;
2. Public methods in the parent class (Object) will also be output.
//2. Get all the methods in this class and return them as an array: Method[] methods = emplClass.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); } //Output: public java.lang.String fanshe.Employee.toString() public void fanshe.Employee.getweekMoney(java.lang.String,double) private void fanshe.Employee.getYearMoney(int) void fanshe.Employee.getMonthMoney() protected void fanshe.Employee.getDayMoney(java.lang.String)
//3. Access to specific public: only public //Parameter 1: the name of the method to be obtained; parameter 2: the type of parameter list corresponding to the method Method method = emplClass.getMethod("getweekMoney",new Class[]{String.class,double.class}); System.out.println(method); //Output: public void fanche.employee.getweekmoney (Java. Lang.string, double) //4. Get specific methods: can be private Method method2 = emplClass.getDeclaredMethod("getYearMoney", int.class); System.out.println(method2); //Output: private void fanche.employee.getyearmoney (int)
Next, I'm going to get the private void getYearMoney(int age) {} method and execute the private method.
//The object of the class corresponding to emplClass Object obj = emplClass.getConstructor().newInstance(); //You can directly call non private methods through the Employee class object. Here I put the test class and Employee class under the same package, and all of them can access the default methods (package qualification methods) Employee employee = (Employee)obj; employee.getMonthMoney(); //Parameter 1: the object to be called parameter 2: the return value of the value required to be passed by the method: the object corresponding to the return value after the method is called. If there is no return value (void), the object is null method2.setAccessible(true); Object result = method2.invoke(obj, 20); //Execute the code corresponding to method2 method System.out.println(result); //Output: /* I am the Employee's default method of getting monthly salary. There is no parameter I am the Employee's private method of getting annual salary and monthly salary, with one parameter: 20 null */
Call the main method of the Employee class:
Class<?> class1 = Class.forName("fanshe.Employee"); Method methodMain = class1.getMethod("main", String[].class); //Parameter 1: object type null when the called method is static, the first parameter can be null //Method 1: Object object = methodMain.invoke(null, (Object)new String[]{"a","b","c","d"}); //Mode two: Object object = methodMain.invoke(null, new Object[] {new String[]{"a","b","c","d"}}); System.out.println(object);
//Output: Employee Medium main()Method executed... //Here is the output from the main method [a, b, c, d] //The output in the main method calls toString Employee Medium main()Method executed... [a, b, c, d] null //The return type of main method is void, so the return value here is null
6, Call the specified Method through the Method object
Let's go through the invoke() Method in Method.
public Object invoke(Object obj, Object... args)
Calls the underlying Method represented by this Method object on the specified object with the specified parameters.
If the underlying method is static, the specified obj parameter can be ignored. The parameter can be null.
If the shape parameter required by the underlying method is 0, the supplied args array length can be 0 or null.
If the underlying method is static and the class that declares it has not been initialized, it is initialized.
If the method completes normally, the value returned by the method is returned to the caller; if the value is of basic type, it is wrapped appropriately in the object first. However, if the type of the value is a set of primitive types, the array elements are not wrapped in the object; in other words, an array of primitive types is returned. If the underlying method return type is void, the call returns null.
Parameters:
obj - calls the object of the underlying method.
args - parameters used for method calls
Return:
The result of assigning the method represented by the object on obj with args
Throw: the exceptions thrown will not be listed. You can check the API document yourself
I created an InvokeTest class to explain the use of the InvokeTest method:
public class InvokeTest { public static void main(String[] args) throws Exception { Class classType = InvokeTest.class; Object invoketest = classType.getConstructor().newInstance(); //Get Method class object Method m = classType.getMethod("add", new Class[] {int.class,int.class}); /** * Method Invoke of (object obj, object [] args) the parameter received by this method must be an object * If the parameter is of basic data type, the result returned by using the corresponding wrapper class object is always an object (its result represents the return value after calling the method) */ //If the add method is a static method, it can be written as follows: Object object = m.invoke(null, new Integer(10), new Integer(20)); Object object = m.invoke(invoketest, new Integer(10), new Integer(20)); System.out.println(object); } public int add(int param1,int param2) { return param1+param2; } } //Output: 30
7, Use of reflections
1. Run the profile content through reflection:
In this way, when we need to access other classes, we don't need to change the source code. We can directly modify the configuration file using reflection.
public class FileDemo { public static void main(String[] args) throws Exception { Class aClass = Class.forName(getValue("className")); Method m = aClass.getMethod(getValue("methodName")); m.invoke(aClass.getConstructor().newInstance()); } //This method is used to receive the value value corresponding to the key in the configuration file public static String getValue(String key) throws Exception { Properties pro = new Properties(); //Get object for profile FileReader reader = new FileReader("prop.txt"); pro.load(reader); //Load the stream into the profile object //Read the list of attributes (key and element pairs) from the input stream. Get all key value pairs in the specified file by loading it, such as the prop.txt file above. For getproperty (string key) to search. reader.close(); return pro.getProperty(key);//Searches for properties in this property list with the specified key. That is to say, through the parameter key, get the value corresponding to the key. } }
//Contents of prop.txt file className=fanshe.file.Apple methodName=taste
//Class Apple public class Apple { public void taste() { System.out.println("How delicious the apples are..."); } }
/ / output: The apples are delicious
Summarize the steps:
1. Obtain the Class object of Apple Class through the forName static method in Class class;
2. Obtain the Method Class object through the Class class object, and pass in the parameter to the Method name to call the Method;
3. Call this Method through the invoke non static Method of the Method class. The parameters are passed into the class object and the parameter object of the called Method (the test Method here has no parameters, and all the invoke parameters are only Apple class objects).
There is an important class properties (Java.util.Properties) in Java, which is mainly used to read Java configuration files.
The above example is obtained from the text file, while the configuration file (. properties file) is read in a slightly different way:
public class ReflectTest { public static void main(String[] args) throws Exception { //1. Load configuration file //1.1 create Properties object Properties pro = new Properties(); //1.2 get the configuration file under the class directory ClassLoader classLoader = ReflectTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("pro.properties"); //1.3 loading configuration file pro.load(is); //2. Get the data defined in the configuration file String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); //3. With the fully qualified class name and the method name (parameter), the method can be called through reflection //3.1: get the Class object of this Class Class cls = Class.forName(className); //3.2: get the instance object of this class Object object = cls.getConstructor().newInstance(); //3.3: get the class specific method object Method method = cls.getMethod(methodName); //4. Implementation method method.invoke(object); } }
//The content of pro.properties file. This configuration file can be package - > right click - > New - > file - > File Name: pro.properties className=bean.Student methodName=study
The Student class and running results are not listed here, which is similar to the above
2. Cross generics check by reflection:
Our goal is to add an Integer object to the String generic collection
List<String> list = new ArrayList<>(); list.add("I love Java"); list.add("Successful landing"); //list.add(100); if you add directly, it will compile and report an error //1. Get the Class object of the List Class Class listClass = list.getClass(); Method method = listClass.getMethod("add", Object.class); method.invoke(list, 100); for (Object object : list) { System.out.println(object); } /*Output: I love Java. Successful landing 100 */
If there is any mistake, please give me some advice