1 reflection
1.1 reflection overview
Java reflection mechanism is to know all the properties and methods of any class in the running state; For any object, you can call any of its methods and properties; This function of dynamically obtaining information and dynamically calling object methods is called the reflection mechanism of Java language.
The concept of reflection was first proposed by Smith in 1982. It mainly refers to the ability of a program to access, detect and modify its own state or behavior. The proposal of this concept soon triggered the research on applied reflexivity in the field of computer science. It was first adopted in the field of programming language design, and made achievements in Lisp and object-oriented. Of course, reflection itself is not a new concept. It may remind us of the concept of reflection in optics. Although computer science gives the concept of reflection a new meaning, phenomenally speaking, they do have some similarities, which are helpful for our understanding
Java reflection mechanism mainly provides the following functions: judge the class of any object at runtime; Construct the object of any class at runtime; Judge the member variables and methods of any class at run time; Call the method of any object at run time; Generate dynamic proxy
1.2 three methods to obtain class objects
When Java is running, no matter how many objects a Class generates, they will correspond to the same Class object, which represents the classes and interfaces in the running program. There are three common ways to obtain the Class object of an operation Class:
- Call the static method forName of Class, such as Class.forName("java.lang.String");
- Use the. Class syntax of the class, such as class <? > cls = String.class;
- Call the getClass method of the object, such as String str = "abc"; Class<?> cls = str .getClass();
1.3 reflection example analysis
1.3.1 reflection acquisition method
First look at a simple example to understand how Java's reflection mechanism works.
package com.wanggc.reflection; import java.lang.reflect.Method; /** * Java Reflection practice. * * @author Wanggc */ public class ForNameTest { /** * Entry function. * * @param args * parameter * @throws Exception * error message */ public static void main(String[] args) throws Exception { // Get Class Class<?> cls = Class.forName(args[0]); // Method to obtain the corresponding object through Class Method[] methods = cls.getMethods(); // Output each method name for (Method method : methods) { System.out.println(method); } } }
When the passed in parameter is java.lang.String, the following results will be output
public boolean java.lang.String.equals(java.lang.Object) public java.lang.String java.lang.String.toString() public int java.lang.String.hashCode() public int java.lang.String.compareTo(java.lang.String) public int java.lang.String.compareTo(java.lang.Object) public int java.lang.String.indexOf(int) public int java.lang.String.indexOf(int,int) public int java.lang.String.indexOf(java.lang.String) public int java.lang.String.indexOf(java.lang.String,int) public static java.lang.String java.lang.String.valueOf(int) public static java.lang.String java.lang.String.valueOf(char) public static java.lang.String java.lang.String.valueOf(boolean) public static java.lang.String java.lang.String.valueOf(float) public static java.lang.String java.lang.String.valueOf(char[],int,int) public static java.lang.String java.lang.String.valueOf(double) public static java.lang.String java.lang.String.valueOf(char[]) public static java.lang.String java.lang.String.valueOf(java.lang.Object) public static java.lang.String java.lang.String.valueOf(long) public char java.lang.String.charAt(int) public int java.lang.String.codePointAt(int) public int java.lang.String.codePointBefore(int) public int java.lang.String.codePointCount(int,int) public int java.lang.String.compareToIgnoreCase(java.lang.String) public java.lang.String java.lang.String.concat(java.lang.String) public boolean java.lang.String.contains(java.lang.CharSequence) public boolean java.lang.String.contentEquals(java.lang.CharSequence) public boolean java.lang.String.contentEquals(java.lang.StringBuffer) public static java.lang.String java.lang.String.copyValueOf(char[]) public static java.lang.String java.lang.String.copyValueOf(char[],int,int) public boolean java.lang.String.endsWith(java.lang.String) public boolean java.lang.String.equalsIgnoreCase(java.lang.String) public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]) public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]) public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException public void java.lang.String.getBytes(int,int,byte[],int) public byte[] java.lang.String.getBytes() public byte[] java.lang.String.getBytes(java.nio.charset.Charset) public void java.lang.String.getChars(int,int,char[],int) public native java.lang.String java.lang.String.intern() public boolean java.lang.String.isEmpty() public int java.lang.String.lastIndexOf(java.lang.String) public int java.lang.String.lastIndexOf(int,int) public int java.lang.String.lastIndexOf(int) public int java.lang.String.lastIndexOf(java.lang.String,int) public int java.lang.String.length() public boolean java.lang.String.matches(java.lang.String) public int java.lang.String.offsetByCodePoints(int,int) public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int) public boolean java.lang.String.regionMatches(int,java.lang.String,int,int) public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence) public java.lang.String java.lang.String.replace(char,char) public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String) public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String) public java.lang.String[] java.lang.String.split(java.lang.String) public java.lang.String[] java.lang.String.split(java.lang.String,int) public boolean java.lang.String.startsWith(java.lang.String) public boolean java.lang.String.startsWith(java.lang.String,int) public java.lang.CharSequence java.lang.String.subSequence(int,int) public java.lang.String java.lang.String.substring(int) public java.lang.String java.lang.String.substring(int,int) public char[] java.lang.String.toCharArray() public java.lang.String java.lang.String.toLowerCase() public java.lang.String java.lang.String.toLowerCase(java.util.Locale) public java.lang.String java.lang.String.toUpperCase() public java.lang.String java.lang.String.toUpperCase(java.util.Locale) public java.lang.String java.lang.String.trim() public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException 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 java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
This lists all the method names, their qualifiers, return types and exceptions thrown of the java.lang.String class. This program loads the specified class using the Class class forName method, and then calls the getMethods method to return the list of methods for the specified class. java.lang.reflect.Method is used to express a single method in a class.
To use java's reflection mechanism, you generally need to follow three steps:
- Get the Class object of the Class you want to operate on
- Obtain the method or property name of the operation Class through the Class object obtained in the first step
- The method or property obtained in the second step of the operation
When obtaining the methods, properties and constructors of the class, there will be two corresponding methods: getXXX and getDeclaredXXX. The difference is that the former returns methods and properties with access permission of public, including those in the parent class; However, the latter returns the methods and properties of all access permissions, excluding those of the parent class
1.3.2 application method of obtaining class object
The following will describe how to execute a method of an object through the three steps described above through an example:·
package com.wanggc.reflection; import java.lang.reflect.Method; /** * Java Reflection practice. * * @author Wanggc */ public class ReflectionTest { public static void main(String[] args) throws Exception { DisPlay disPlay = new DisPlay(); // Get Class Class<?> cls = disPlay.getClass(); // Get the show method of the play Class through Class Method method = cls.getMethod("show", String.class); // Call the show method method.invoke(disPlay, "Wanggc"); } } class DisPlay { public void show(String name) { System.out.println("Hello :" + name); } }
As mentioned earlier, each Class of a Java program will have a Class object corresponding to it. The first step in Java reflection is to get the Class object. Of course, the Method of each Class must also have a Method object corresponding to it. To call this Method through reflection, you must first obtain the Method object of this Method, and then call this Method in turn with the Method object. Note that the first parameter of the getMethod Method is the Method name and the second is the parameter type of the Method. If there are multiple parameters, then add the parameters, because getMethod is a variable parameter Method. The invoke Method actually executes the show Method. Note that the first parameter of invoke is an object of the play Class, that is, the show Method of which object of the play Class is called. The second parameter is the parameter passed to the show Method. The type and number must be consistent with the getMethod Method.
1.3.3 reflect the attribute value of a class
The above example describes how to call a class's method through reflection, and the following example describes how to assign a value to a class's attribute through reflection:
package com.wanggc.reflection; import java.lang.reflect.Field; /** * Java Reflection attribute exercise */ public class ReflectionTest { public static void main(String[] args) throws Exception { // Establish student object Student student = new Student(); // Assign values to student objects student.setStuName("Wanggc"); student.setStuAge(24); // Create copy target object Student destStudent = new Student(); // Copy Student object copyBean(student, destStudent); // Output copy results System.out.println(destStudent.getStuName() + ":" + destStudent.getStuAge()); } /** * Copy Student object information. * * @param from * Copy source object * @param dest * Copy target object * @throws Exception * exception */ private static void copyBean(Object from, Object dest) throws Exception { // Gets the Class object of the copy source object Class<?> fromClass = from.getClass(); // Gets the attribute list of the copy source object Field[] fromFields = fromClass.getDeclaredFields(); // Gets the Class object of the copy target object Class<?> destClass = dest.getClass(); Field destField = null; for (Field fromField : fromFields) { // Gets the property name of the copy source object String name = fromField.getName(); // Gets the property of the same name as the copy target object destField = destClass.getDeclaredField(name); // Set accessibility of properties fromField.setAccessible(true); destField.setAccessible(true); // Assign the value of the attribute of the copy source object to the corresponding attribute of the copy target object destField.set(dest, fromField.get(from)); } } } /** * Students. */ class Student { /** full name */ private String stuName; /** Age */ private int stuAge; /** * Get student name. * * @return Student name */ public String getStuName() { return stuName; } /** * Set student name * * @param stuName * Student name */ public void setStuName(String stuName) { this.stuName = stuName; } /** * Get student age * * @return Student age */ public int getStuAge() { return stuAge; } /** * Set student age * * @param stuAge * Student age */ public void setStuAge(int stuAge) { this.stuAge = stuAge; } }
In Java's emission mechanism, classes have Class correspondence, Class methods have Method correspondence, and of course, attributes also have Field correspondence. Detailed comments have been made in the code and will not be repeated here. Note that Field provides get and set methods to obtain and set the value of the property. However, since the property is a private type, you need to set the accessibility of the property to true. You can also set accessibility for the entire fields by using the static Method setAccessible of AccessibleObject, such as AccessibleObject. setAccessible (from fields, true);
1.3.4 create reflection class objects
Previously, we described how to use Java reflection mechanism to operate the methods and properties of a class. Next, we describe how to create an object of a class at runtime through an example:
package com.wanggc.reflection; import java.lang.reflect.Field; /** * Java Reflection attribute exercise. * * @author Wanggc */ public class ReflectionTest { public static void main(String[] args) throws Exception { // Establish student object Student student = new Student(); // Assign values to student objects student.setStuName("Wanggc"); student.setStuAge(24); // Create copy target object Student destStudent = (Student) copyBean(student); // Output copy results System.out.println(destStudent.getStuName() + ":" + destStudent.getStuAge()); } /** * Copy Student object information. * * @param from * Copy source object * @param dest * Copy target object * @throws Exception * exception */ private static Object copyBean(Object from) throws Exception { // Gets the Class object of the copy source object Class<?> fromClass = from.getClass(); // Gets the attribute list of the copy source object Field[] fromFields = fromClass.getDeclaredFields(); // Gets the Class object of the copy target object Object ints = fromClass.newInstance(); for (Field fromField : fromFields) { // Set accessibility of properties fromField.setAccessible(true); // Assign the value of the attribute of the copy source object to the corresponding attribute of the copy target object fromField.set(ints, fromField.get(from)); } return ints; } } /** * Students. */ class Student { /** full name */ private String stuName; /** Age */ private int stuAge; /** * Get student name. * * @return Student name */ public String getStuName() { return stuName; } /** * Set student name * * @param stuName * Student name */ public void setStuName(String stuName) { this.stuName = stuName; } /** * Get student age * * @return Student age */ public int getStuAge() { return stuAge; } /** * Set student age * * @param stuAge * Student age */ public void setStuAge(int stuAge) { this.stuAge = stuAge; } }
The results of this example and the previous example are the same. However, the object returned by the copyBean method is no longer passed in from the outside, but generated inside the method. Note: the newInstance method of Class can only create classes that contain only parameterless constructors. If a Class has only parameterless constructors, another method must be used
fromClass.getDeclaredConstructor(int.class,String.class) .newInstance(24,"wanggc");
1.3.5 reflection operation array
The getComponentType() method in reflection can get an array of Class objects
import java.lang.reflect.*; class hello{ public static void main(String[] args) { int[] temp={1,2,3,4,5}; Class<?>demo=temp.getClass().getComponentType(); System.out.println("Array type: "+demo.getName()); System.out.println("Array length "+Array.getLength(temp)); System.out.println("The first element of the array: "+Array.get(temp, 0)); Array.set(temp, 0, 100); System.out.println("After modification, the first element of the array is: "+Array.get(temp, 0)); } }
1.4 difference between newinstance() and new()
Class.forName() is often used in java development, especially in database development. By querying Java Documentation, we can find that the purpose of using the Class.forName() static method is to dynamically load classes. After loading, it is generally necessary to call the newInstance() static method under class to instantiate the object for operation. Therefore, it is useless to use Class.forName() to load classes dynamically. The ultimate goal is to instantiate objects
What is the difference between newInstance() and new under Class?
Firstly, newInstance() is a method and new is a keyword. Secondly, the use of newInstance() under Class is limited, because it can only call parameterless constructors to generate objects, but there is no such restriction when using new keyword to generate objects.
That is, Class.forName("") returns a class, and Class.forName("").newInstance() returns an object
Friends with database development experience will find that why do we not call the newInstance() method when we load the database driver package? Some jdbc connection databases are written as Class.forName(xxx.xx.xx); Why are some Class.forName(xxx.xx.xx).newInstance(), written in these two ways?
Class.forName(""); The function of is to require the JVM to find and load the specified class. If there is a static initializer in the class, the JVM will inevitably execute the static code segment of the class. The JDBC specification explicitly requires that the Driver class must register itself with the DriverManager, that is, the Driver class code of any JDBC Driver must be similar to the following:
public class MyJDBCDriver implements Driver { static { DriverManager.registerDriver(new MyJDBCDriver()); } }
Since it has been registered in the of the static initializer, we only need Class.forName(XXX.XXX) when using JDBC; That's it
The factory pattern in Java often uses the newInstance() method to create objects, so you can find the specific answer from why you want to use the factory pattern. For example:
class c = Class.forName("Example"); factory = (ExampleInterface)c.newInstance(); among ExampleInterface yes Example The interface can be written as follows: String className = "Example"; class c = Class.forName(className); factory = (ExampleInterface)c.newInstance(); It can be further written as follows: String className = readfromXMlConfig;//Get string from xml configuration file class c = Class.forName(className); factory = (ExampleInterface)c.newInstance();
The above code no longer has the class name of example. Its advantage is that no matter how the example class changes, the above code remains unchanged. It can even replace the brother classes of example, example2, Example3, example4... As long as they inherit the ExampleInterface.
From the perspective of the JVM, when we use the keyword new to create a class, the class may not be loaded. However, when using the newInstance() method, you must ensure that:
- This class has been loaded;
- This class is already connected.
The static method forName() of Class completes the above two steps. This static method calls the loader that starts the Class, that is, the loader that loads the java API.
Now we can see that newInstance() actually decomposes the new method into two steps, that is, first call the class loading method to load a class, and then instantiate it. The benefits of this step-by-step are obvious. We can get better flexibility when calling the static loading method forName of class, which provides a means of decoupling.
Finally, use the simplest description to distinguish the new keyword from the newInstance() method:
- newInstance: weak type. poor efficiency. Only parameterless constructs can be called.
- new: strongly typed. Relatively efficient. Any public construct can be called