In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces "how to understand reflection". In daily operation, I believe many people have doubts about how to understand reflection. The editor consulted all kinds of data and sorted out simple and easy-to-use operation methods. I hope it will be helpful for you to answer the question of "how to understand reflection"! Next, please follow the editor to study!
The thought and function of reflection
There are opposites, there are positives, just like yin and yang in the world, and zeros and ones in computers. There is reincarnation in the way of heaven, heaven. (Jing will be blind bibi here)
Before you learn about reflex, let's understand what orthophoto is. The most commonly used new way to instantiate an object is a kind of orthophoto. If I needed to instantiate a HashMap, the code would look like this.
Map map = new HashMap (); map.put (1,1)
One day, it was found that this program is not suitable to store key-value pairs with HashMap, but is more likely to be stored with LinkedHashMap. When you rewrite the code, it looks like this.
Map map = new LinkedHashMap (); map.put (1,1)
If one day, it is found that the data is still suitable for HashMap storage, do you want to modify the source code again?
"did you find a problem? every time we change a requirement, we have to modify the source code, then compile, package, and restart the project on JVM. After these steps, the efficiency is very low.
For such scenarios where the requirements change frequently but not much, changing the source code frequently must be a disallowed operation, and we can use a switch to determine when and which data structure to use.
Public Map getMap (String param) {Map map = null; if (param.equals ("HashMap")) {map = new HashMap ();} else if (param.equals ("LinkedHashMap")) {map = new LinkedHashMap ();} else if (param.equals ("WeakHashMap")) {map = new WeakHashMap ();} return map;}
You decide which data structure to use by passing in the parameter param, and you can decide which data structure to use by passing in parameters dynamically while the project is running.
If you still want to use TreeMap one day, you still can't avoid the malpractice of modifying the source code and recompiling and executing. At this point, reflection comes in handy.
Before the code runs, we are not sure which data structure will be used in the future, and we only decide which data class to use when the program is running, and reflection can dynamically obtain class information and call class methods during the program running. By reflecting the construction of a class instance, the code evolves like this.
Public Map getMap (String className) {Class clazz = Class.forName (className); Consructor con = clazz.getConstructor (); return (Map) con.newInstance ();}
No matter what Map you use, as long as you implement the Map interface, you can pass it into the method using the full class name path to get the corresponding Map instance. For example, java.util.HashMap / java.util.LinkedHashMap if I want to create other classes such as WeakHashMap, I don't need to modify the above source code.
Let's review how to derive an object from new that uses reflection.
When reflection is not used, the construction object is implemented in the new way, which determines the type of the object at compile time.
If the requirement changes and another object needs to be constructed, the source code needs to be modified, which is very inelegant, so we use switches to determine which object needs to be constructed while the program is running. at run time, you can change the switch to instantiate different data structures.
If there are other extended classes that may be used, a lot of branches will be created, and you don't know what other classes are used when coding. If in the future, a collection class under the Map interface is xxxHashMap, and you have to create a branch, this leads to reflection: you can determine which data class to use at run time, and you don't need to modify the source code and compile the program when switching classes.
The first chapter summarizes:
The idea of reflection: determine and parse the types of data classes while the program is running.
The role of reflection: for scenarios where it is impossible to determine which data class to use at compile time, different data class instances can be constructed while the program is running.
Basic use of reflection
There are four main components of Java reflection:
Class: any class that runs in memory is an instance object of the Class class, and each Class class object contains all the original information. Remember, if you do anything through reflection, you must go to Class first!
Field: describes the property of a class, which contains all the information about the attribute, such as data type, property name, and access modifier.
Constructor: describes the constructor of a class, which contains all the information about the constructor, such as parameter type, parameter name, and access modifier.
Method: describes all the methods (including abstract methods) of a class and contains all the information about the method internally, similar to Constructor, except that Method has the return type information because the constructor does not return a value.
I summed up a brain map and put it below. If reflection is used, it is inseparable from the four core classes. It is only to understand what information is provided inside them and what they can do, so that they can be easily used.
When we learn the basic use of reflection, I will use a SmallPineapple class as a template. First, let's familiarize ourselves with the basic components of this class: properties, constructors, and methods.
Public class SmallPineapple {public String name; public int age; private double weight; / / weight only you know public SmallPineapple () {} public SmallPineapple (String name, int age) {this.name = name; this.age = age;} public void getInfo () {System.out.print (the age of "[" + name + "is" + age + "]")) }}
There are many uses in reflection, and the common functions are as follows:
Get the Class object of a class at run time
Construct an instantiated object of a class at run time
Get all the information about a class at run time: variables, methods, constructors, comments
Get the Class object of the class
In Java, each class will have its own Class object. When we write the .java file and compile it with javac, we will produce a bytecode file .class, which contains all the information about the class, such as properties, constructors, and methods. When the bytecode file is loaded into the virtual machine for execution, the Class object is generated in memory, which contains all the information within the class. This information can be obtained while the program is running.
There are three ways to get a Class object:
Class name .class: this method can get the Class object only if the type of the class has been declared before compilation
Class clazz = SmallPineapple.class
Instance .getClass (): get the Class object of the instance by instantiating the object
SmallPineapple sp = new SmallPineapple (); Class clazz = sp.getClass ()
Class.forName (className): gets the Class object of a class by its fully qualified name
Class clazz = Class.forName ("com.bean.smallpineapple")
Get the Class object and you can do whatever you want with it: peel it (get class information), instruct it to do things (call its methods), see through everything about it (get properties), and it has no privacy.
However, in the program, there is only one Class object for each class, which means you have only one slave. We tested it in the above three ways, and printing each Class object in three ways is the same.
Class clazz1 = Class.forName ("com.bean.SmallPineapple"); Class clazz2 = SmallPineapple.class; SmallPineapple instance = new SmallPineapple (); Class clazz3 = instance.getClass (); System.out.println ("Class.forName () = = SmallPineapple.class:" + (clazz1 = = clazz2)); System.out.println ("Class.forName () = = instance.getClass ():" (clazz1 = = clazz3)); System.out.println ("instance.getClass () = SmallPineapple.class:" + (clazz2 = clazz3)
"the reason why there is only one Class object in memory involves the parent delegate model of the JVM class loading mechanism, which ensures that when the program runs, each class produces only one Class object in memory when the class is loaded. I'm not going to expand it here, it can be simply understood that JVM ensures that a class has at most one Class object in memory.
Construct an instantiated object of a class
There are two ways to construct an instance of a class through reflection:
The Class object calls the newInstance () method
Class clazz = Class.forName ("com.bean.SmallPineapple"); SmallPineapple smallPineapple = (SmallPineapple) clazz.newInstance (); smallPineapple.getInfo (); / / [age of null is: 0]
Even though SmallPineapple has explicitly defined the constructor, all attribute values in the instance created by newInstance () are the initial values of the corresponding type, because the newInstance () constructor instance invokes the default no-parameter constructor.
The Constructor constructor calls the newInstance () method
Class clazz = Class.forName ("com.bean.SmallPineapple"); Constructor constructor = clazz.getConstructor (String.class, int.class); constructor.setAccessible (true); SmallPineapple smallPineapple2 = (SmallPineapple) constructor.newInstance ("Little Pineapple", 21); smallPineapple2.getInfo (); / / [Age of Little Pineapple is 21]
Through getConstructor (Object...) The paramTypes) method specifies the Constructor that gets the specified parameter type, and Constructor calls newInstance (Object...) If you pass the value of the constructor parameter when paramValues, you can also construct an instance, and the internal property has been assigned.
Calling newInstance () through the Class object will take the default no-parameter construction method. If you want to construct an instance through an explicit construction method, you need to call the getConstructor () method from Class in advance to get the corresponding constructor, and then use the constructor to de-instantiate the object.
"these API are the most commonly encountered in development, and of course there are many overloaded methods. In this article, because of the space, and if each method is explained one by one, we can't remember it, so it is enough to look it up in the class when we use it.
Get all the information about a class
The Class object contains all the information of the class, and the information we can see at compile time is the variables, methods, and constructors of the class, which are most often obtained at run time.
Get the variable in the class (Field)
Field [] getFields (): gets all the variables in the class that are decorated by public
Field getField (String name): gets a variable in the class based on the variable name, which must be modified by public
Field [] getDeclaredFields (): gets all variables in the class, but cannot get inherited variables
Field getDeclaredField (String name): gets a variable in the class by name, but cannot get the inherited variable
Get the method in the class (Method)
Method [] getMethods (): gets all the methods in the class that are decorated by public
Method getMethod (String name, Class... ParamTypes): get the corresponding method based on the name and parameter type, which must be modified by public
Method [] getDeclaredMethods (): gets all methods, but cannot get inherited methods
Method getDeclaredMethod (String name, Class... ParamTypes): get the corresponding method based on the name and parameter type, but cannot get the inherited method
Get the constructor of the class (Constructor)
Constuctor [] getConstructors (): gets all the public-decorated constructors in the class
Constructor getConstructor (Class... ParamTypes): gets a constructor in the class based on the parameter type, which must be modified by public
Constructor [] getDeclaredConstructors (): gets all the constructors in the class
Constructor getDeclaredConstructor (class... ParamTypes): get the corresponding constructor based on the parameter type
Each function is subdivided into 2 categories by Declared:
"methods with Declared modification: you can get all variables, methods, and constructors contained in this class, but cannot get inherited information. Methods without Declared modification: you can get variables, methods, and constructors modified by public in this class, and you can get inherited information.
If you want to get all the (including inheritance) * * variables, methods, and constructors in the class, you need to call both the getXXXs () and getDeclaredXXXs () methods, and use the Set collection to store the variables, constructors, and methods they get, in case both methods get the same thing.
For example, to get SmallPineapple to get all the variables in the class, the code should be written as follows.
Class clazz = Class.forName ("com.bean.SmallPineapple"); / / get public attributes, including inheriting Field [] fields1 = clazz.getFields (); / / get all attributes, excluding inheriting Field [] fields2 = clazz.getDeclaredFields (); / / summarize all attributes to set Set allFields = new HashSet (); allFields.addAll (Arrays.asList (fields1)); allFields.addAll (Arrays.asList (fields2))
"I don't know if you've found an interesting thing, but if the properties of the parent class are modified with protected, you can't get it with reflection. The scope of the protected modifier: it can only be accessed under the same package or subclass, and can be inherited to subclass. GetFields () can only get the variable value of the public attribute of this class; getDeclaredFields () can only get all the properties of this class, not including inherited; in any case, it can not get the variable modified by the protected attribute of the parent class, but it does exist in the subclass.
Get comments
Get annotations are unscrewed separately, because it is not a kind of information specific to Class objects, and every variable, method, and constructor can be modified by annotations, so in reflection, both Field,Constructor and Method class objects can call the following methods to get annotations marked on them.
Annotation [] getAnnotations (): get all comments on this object
Annotation getAnnotation (Class annotaionClass): pass in the annotation type to get a specific comment on the object
Annotation [] getDeclaredAnnotations (): get all comments of explicit annotations on this object, unable to get inherited comments
Annotation getDeclaredAnnotation (Class annotationClass): get a specific comment on the object based on the annotation type, but cannot get the inherited comment
Only when the @ Retension of the annotation is marked as RUNTIME, the annotation can be obtained through reflection. @ Retension has three save strategies:
SOURCE: save only in * * source file (.java) * *, that is, the comment will only be retained in the source file, and the compiler will ignore it at compile time, such as @ Override annotation
CLASS: saved in a bytecode file (.class), the comment follows the bytecode file as it is compiled, but the annotation is not parsed at run time
RUNTIME: the most frequently used save strategy all the way up to run time, where you can get all the information about the annotation
Like the following example, the SmallPineapple class inherits the @ Override annotation marked on the abstract class Pineapple,getInfo () method and marks the @ Transient annotation in the subclass. When you get all the annotations on the subclass rewriting method at run time, you can only get the information of @ Transient.
Public abstract class Pineapple {public abstract void getInfo ();} public class SmallPineapple extends Pineapple {@ Transient @ Override public void getInfo () {System.out.print ("the height and age of a small pineapple is: + height +" cm; "+ age +" year ");}}
Start the class Bootstrap to get the annotation information on the getInfo () method in the SmallPineapple class:
Public class Bootstrap {/ * determines the full class name path of a specific class object * @ param path class based on the full class name path passed in at run time * / public static void execute (String path) throws Exception {Class obj = Class.forName (path); Method method = obj.getMethod ("getInfo"); Annotation [] annotations = method.getAnnotations () For (Annotation annotation: annotations) {System.out.println (annotation.toString ());}} public static void main (String [] args) throws Exception {execute ("com.pineapple.SmallPineapple");}} / / @ java.beans.Transient (value=true)
Call a method through reflection
After a Method class object is obtained by reflection, it can be executed by calling the invoke method.
Invoke (Oject obj, Object... Args): parameter ``1 specifies the * * object * * to call the method, and parameter 2` is the parameter list value of the method.
If the method called is static, parameter 1 only needs to be passed in null, because static methods are not related to an object, only to a class.
You can instantiate an object through reflection, then get the Method method object, and call the getInfo () method of invoke () that specifies the SmallPineapple, as follows.
Class clazz = Class.forName ("com.bean.SmallPineapple"); Constructor constructor = clazz.getConstructor (String.class, int.class); constructor.setAccessible (true); SmallPineapple sp = (SmallPineapple) constructor.newInstance ("little pineapple", 21); Method method = clazz.getMethod ("getInfo"); if (method! = null) {method.invoke (sp, null);} / [the age of the little pineapple is 21]
Application scenarios of reflection
Here are 3 common application scenarios of reflection:
Spring instantiation object: when the program starts, Spring reads the configuration file applicationContext.xml and parses out all the tags instantiated into the IOC container.
Reflection + factory mode: eliminate multiple branches of the factory through reflection. If you need to produce new classes, you do not need to pay attention to the factory class, the factory class can deal with a variety of new classes, reflection can make the program more robust.
JDBC connection to the database: when using JDBC to connect to the database, the reflection load driver class is used to specify the driver class to connect to the database
IOC container for Spring
In Spring, a context configuration file applicationContext.xml is often written, which is about the configuration of bean. When the program starts, it reads the xml file, parses out all the tags, and instantiates the object into the IOC container.
After the above file is defined, the configuration file is loaded through ClassPathXmlApplicationContext. When the program starts, Spring instantiates all the bean in the configuration file and puts it into the IOC container. The IOC container is essentially a factory, and the corresponding instance is obtained by passing the id attribute of the tag in the factory.
Public class Main {public static void main (String [] args) {ApplicationContext ac = new ClassPathXmlApplicationContext ("applicationContext.xml"); SmallPineapple smallPineapple = (SmallPineapple) ac.getBean ("smallpineapple"); smallPineapple.getInfo (); / / [the age of the little pineapple is 21]}
After Spring simplifies the process of instantiating objects, it can be understood as reflecting the steps of instantiating objects:
Get the constructor of the Class object
Instantiate the object by calling newInstance () through the constructor
Of course, Spring does a lot of extra work when instantiating objects to make current development convenient and stable.
"later in the article will be devoted to an article on how to use reflection to achieve a simple version of the IOC container, the principle of IOC container is very simple, as long as you master the idea of reflection, understand the reflection of the common API can be achieved, I can provide a simple idea: use HashMap to store all instances, key represents the tag id,value storage corresponding instance, which corresponds to the Spring IOC container management object is singleton by default.
Reflection + Abstract Factory pattern
In the traditional factory model, if you need to produce new subclasses, you need to modify the factory class and add a new branch to the factory class.
Public class MapFactory {public Map produceMap (String name) {if ("HashMap" .equals (name)) {return new HashMap ();} else if ("TreeMap" .equals (name)) {return new TreeMap ();} / /}
With the combination of reflection and factory pattern, when generating a new subclass, the factory class does not have to modify anything and can focus on the implementation of the subclass, and when the subclass is determined, the factory can produce the subclass.
The core idea of reflection + Abstract Factory is:
At run time, different Class objects are obtained by passing in the fully qualified names of different subclasses, and the newInstance () method is called to return different subclasses. Careful readers will find that the concept of subclasses is mentioned, so the reflection + abstract factory pattern is generally used to have inheritance or interface implementation relationships.
For example, to determine which Map structure to use at run time, we can instantiate a particular subclass using reflection passing in the fully qualified name of a specific Map.
Fully qualified name of the public class MapFactory {/ * @ param className class * / public Map produceMap (String className) {Class clazz = Class.forName (className); Map map = clazz.newInstance (); return map;}}
ClassName can be specified as java.util.HashMap, or java.util.TreeMap, and so on, depending on the business scenario.
JDBC loads database driver classes
When importing a third-party library, JVM will not take the initiative to load the externally imported class, but will not load the required class until it is actually used. Just like this, we can pass in the fully qualified name of the driver class when obtaining the database connection and give it to JVM to load the class.
Public class DBConnectionUtil {/ * * specifies the driver class of the database * / private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver"; public static Connection getConnection () {Connection conn = null; / / load the driver class Class.forName (DRIVER_CLASS_NAME) / / get database connection object conn = DriverManager.getConnection ("jdbc:mysql://", "root", "root"); return conn;}}
We often encounter this class when we develop SpringBoot projects, but maybe we get used to it and don't care much about it. I'm here to show you the common database configuration in application.yml. I think you'll suddenly realize it.
The driver-class-name here is similar to the class we loaded at the beginning, because different versions of MySQL cause different driver classes, which reflects the benefits of using reflection: you don't need to modify the source code, only load the configuration file to complete the replacement of the driver class.
"later articles will be devoted to a detailed introduction to the application scenarios of reflection, the implementation of a simple IOC container, and the benefits of implementing the factory pattern through reflection. Here, you only need to master the basic usage of reflection and its ideas, and understand its main usage scenarios.
Advantages and disadvantages of reflection
Advantages of reflection:
Increase program flexibility: in the face of requirements changes, you can flexibly instantiate different objects
However, there are gains and losses, a technology can not only have advantages and disadvantages, reflection also has two more obscure disadvantages:
Breaking the encapsulation of a class: you can force access to private-decorated information
Performance attrition: reflection requires so many checking and parsing steps that JVM cannot optimize them compared to directly instantiating objects, calling methods, and accessing variables.
Increase program flexibility
Instead of using SmallPineapple as an example, let's look at an example that is closer to development:
Using reflection to connect to the database, involving the data source of the database. In SpringBoot, all conventions are greater than configuration. When you want to customize the configuration, use application.properties configuration file to specify the data source.
Designer of role 1-Java: we have designed the DataSource interface, and if your other database vendors want developers to use your data source to monitor the database, they have to implement mine!
Role 2-Database vendor:
MySQL database vendor: we provide com.mysql.cj.jdbc.MysqlDataSource data sources that developers can use to connect to MySQL.
Alibaba manufacturer: we provide com.alibaba.druid.pool.DruidDataSource data source, my data source is more powerful, with page monitoring, slow SQL logging and other functions, developers quickly use it to monitor MySQL!
SQLServer manufacturer: we provide com.microsoft.sqlserver.jdbc.SQLServerDataSource data source. If you want to use SQLServer as a database, use our data source connection.
Role 3-developer: we can specify the use of DruidDataSource data sources with configuration files
Spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
Requirements change: one day, the boss came to us and said that the Druid data source is not quite in line with our current project, let's use MysqlDataSource, and then the programmer will modify the configuration file, reload the configuration file, and restart the project to complete the data source switch.
Spring.datasource.type=com.mysql.cj.jdbc.MysqlDataSource
When you change the data source that connects to the database, you only need to change the configuration file, without changing any code, because:
The bottom layer of Spring Boot encapsulates the data source configuration to connect to the database, and adapts to each data source by reflection.
The following is a brief source code analysis. We use the ctrl+ left-click spring.datasource.type to enter the DataSourceProperties class and find that we use setType () to convert the full class name to a Class object and inject it into the type member variable. When connecting to and monitoring the database, the specified data source operation is used.
Private Class
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.