In "The dynamics of Java programming, part 1th," I introduce you to Java programming classes and class loading. This article describes some of the Java binary class format related information. This month I'll explain the basics of using the Java Reflection API to access and use some of the same information at run time. To focus on this article for developers who already know the reflection basics, I'll include how reflection performance compares to direct access in the article.
Using reflection is different from regular Java programming, where it collaborates with metadata -The data that describes other data. The special type of data that the Java language reflects is the description of classes and objects in the JVM. Reflection enables you to access a wide range of class information at run time. It even enables you to read the Write field and invoke the method of the class selected at run time.
Reflection is a powerful tool. It enables you to create flexible code that can be assembled at run time without the need for source-representative links between components. But there are some aspects of reflection that have some doubts. In this article, I'll delve into why you might not want to use reflection in your program, and why you should do so. After you understand the tradeoff analysis, you can decide for yourself whether the benefits outweigh the disadvantages.
Beginner's Class
The start point using reflection is always an java.lang.Class
instance. If you want to collaborate with pre-defined classes, the Java language provides a quick and easy way to get an Class
instance directly:
Class clas = Myclass.class;
When you use this technique, all the work involved in loading a class is performed behind the scenes. However, this method is not appropriate if you need to read the class name from some external sources at run time. In fact, you need to use a class loader to find class information. Here's a way to do this:
"Name" is the class name to LoadClass Clas = null;try { clas = class.forname (name);} catch (ClassNotFoundException Ex) { //Handle exception case}//use the loaded class
If the class is already loaded, you will get the existing Class
information. If the class is not loaded, the class loader will now load and return the newly created class instance.
Back to top of page
Class-based reflection
Class
The object provides you with all the basic hooks for the reflection of the access class metadata. Such metadata includes information about the class itself, such as the parent class of the package and class, and the interfaces that the class implements. It also includes details about the constructors, fields, and methods defined by the class. These final projects are the most frequently used projects in programming, so I'll give you some examples of how to collaborate with them later in this section.
For any of the following three categories of components-constructors, fields, and methods- java.lang.Class
provides four separate reflection calls to obtain information in different ways. Calls follow a standard format. The following is a set of reflection calls used to find a constructor:
Constructor getConstructor(Class[] params)
--Get a public constructor that uses a special parameter type,
Constructor[] getConstructors()
--Get all public constructors for the class
Constructor getDeclaredConstructor(Class[] params)
--Get constructors that use specific parameter types (regardless of access level)
Constructor[] getDeclaredConstructors()
--Get all constructors for the class (regardless of access level)
Each class of these calls returns one or more java.lang.reflect.Constructor
functions. This Constructor
class defines newInstance
a method that takes a set of objects as its only parameter, and then returns the newly created instance of the original class. The group object is the parameter value used to construct the function call. As an example of explaining this workflow, suppose you have a TwoString
class and a constructor that uses a pair String
of S, as shown in Listing 1:
Listing 1: Classes created from a pair of strings & #160
public class Twostring { private String m_s1, M_s2; Public twostring (String s1, string s2) { m_s1 = S1; M_S2 = s2; }}
The code in Listing 2 obtains the constructor and uses it to create String
"a"
an instance of "b"
the TwoString
class that uses S and:
Listing 2: Reflection invocation of the constructor
class[] types = new class[] {string.class, string.class}; Constructor cons = TwoString.class.getConstructor (types); object[] args = new object[] {"A", "B"}; twostring ts = cons.newinstance (args);
The code in Listing 2 ignores a number of possible exception types that are thrown by different reflection methods. Exceptions are documented in detail in the Javadoc API description, so I will ignore them in all program instances for brevity.
Although I am discussing the topic of a constructor, the Java programming language defines a special shortcut that you can use to create an instance of a class using the parameterless (or default) constructor. This shortcut is embedded Class
in the definition as follows:
Object newInstance()
--Create a new instance using the default function
Even if this method only allows you to use a special constructor, if that's what you need, it will provide a very handy shortcut. This technique is especially useful when collaborating with JavaBeans, and JavaBeans requires defining public, parameterless constructors.
Add a field by reflection
The reflection call that obtains the field information Class
differs from those used to access the constructor, and the field name is used in the parameter type array:
Field getField(String name)
--Get a named public field
Field[] getFields()
--Get all the public fields of the class
Field getDeclaredField(String name)
--Get the named field of the class declaration
Field[] getDeclaredFields()
--Get all the fields of the class declaration
Although similar to a constructor call, there is an important difference in the field: the first two variables return information about the public fields that can be accessed through the class-even if they come from an ancestor class. The latter two variables return information about the fields declared directly by the class--regardless of the access type of the field.
Calling the returned java.lang.reflect.Field
instance defines the and methods of all the main types getXXX
setXXX
, as well as the common get
and methods of collaborating with object references set
. You can choose an appropriate method based on the actual field type, and getXXX
the method will automatically handle the extended transformation (such as using getInt
a method to retrieve a byte value).
Listing 3 shows an instance of using the field reflection method that increments the field of the object by name in the format of the method int
:
Listing 3: Adding a field by reflection
public int Incrementfield (String name, Object obj) throws ... { Field field = Obj.getclass (). Getdeclaredfield (name); int value = Field.getint (obj) + 1; Field.setint (obj, value); return value;}
This approach begins to demonstrate some of the flexibility that reflection brings. Unlike a specific class collaboration, incrementField
you use the method of an incoming object getClass
to find the class information, and then find the named field directly in the class.
Add a method by reflection
The reflection call that obtains the method information Class
is very similar to the call used to construct the function and the field:
Method getMethod(String name, Class[] params)
--Use a specific parameter type to get a named public method
Method[] getMethods()
--Get all public methods of the class
Method getDeclaredMethod(String name, Class[] params)
--Use the close-up parameter type to get the naming method of the class declaration
Method[] getDeclaredMethods()
--all methods of obtaining class declarations
As with field calls, the first two variables return information about the public methods that can be accessed through the class-even if they come from an ancestor class. The latter two variables return information about the method declared by the class, regardless of the access type of the method.
Call the returned java.lang.reflect.Method
instance to define a invoke
method that you can use to invoke a method on an instance of the class you are defining. This invoke
method uses two parameters to provide an array of class instances and parameter values for the call.
Listing 4 further illustrates a field instance that shows an instance of a method that reflects a run. This method adds a JavaBean attribute that defines both get
set
the and method int
. For example, if the object defines a and method for an integer count
value, getCount
setCount
you can name
increase the value by passing "Count" as a parameter to the method in a single call.
Listing 4: Adding a JavaBean property by reflection
public int Incrementproperty (string name, Object obj) { string prop = Character.touppercase (Name.charat (0)) + Name.substring (1); String mname = "get" + prop; class[] types = new class[] {}; method = Obj.getclass (). GetMethod (Mname, types); Object result = Method.invoke (obj, new object[0]); int value = ((Integer) result). Intvalue () + 1; Mname = "Set" + prop; types = new class[] {int.class}; method = Obj.getclass (). GetMethod (Mname, types); Method.invoke (obj, new object[] {new Integer (value)}); return value;}
To follow the JavaBeans convention, I changed the first letter of the property name to uppercase, and then I thought about get
creating the Read method name to set
create the Write method name. The JavaBeans Read method returns only the value, and the Write method uses the value as the unique parameter, so I specify the parameter type of the method to match. Finally, the Convention requires the method to be public, so I use the Find format to find public methods that can be called on the class.
This is the first instance where I pass the primary value using reflection, so let's look at how it works. The basic principle is simple: whenever you need to pass the primary value, you simply replace the class's primary value with an instance of the enclosing class (defined in the java.lang
package). This can be applied to calls and returns. So, when I call a method in an instance get
, I expect the result to be an int
encapsulation of the actual property value java.lang.Integer
.
Reflected array
An array is an object in the Java programming language. As with all objects, they all have classes. If you have an array, using the standard getClass
method, you can get the class of the array, just like any other object. However, you do not use an existing instance to obtain a class that differs from other types of objects. Even if you have an array class, you cannot do too much of it directly--reflection provides a constructor for a standard class that Access cannot be used in an array, and the array does not have any accessible fields, only the basic java.lang.Object
method definition is used for the array object.
The special handling of arrays uses a java.lang.reflect.Array
collection of static methods provided by the class. The methods in this class enable you to create a new array, get the length of the array object, and read and write the indexed values of the array object.
Listing 5 shows an efficient way to resize an existing array. It uses reflection to create a new array of the same type, and then copies all the data in the old array before returning the new array.
Listing 5: Extending an array by reflection
public object Growarray (object array, int size) { Class type = Array.getclass (). Getcomponenttype (); Object grown = array.newinstance (type, size); System.arraycopy (array, 0, grown, 0, math.min (array.getlength (array), size)); return grown;}
Back to top of page
Security and reflection
Security is a more complex issue when dealing with reflection. Reflection is often used by frame-type code, and because of this, you may want the framework to fully access your code without regard to general access restrictions. However, in other cases, uncontrolled access poses a serious security risk, such as when a contemporary code is running in an environment that is not trusted for code sharing.
Because of these conflicting requirements, the Java programming language defines a multi-level approach to handling the security of reflection. The basic pattern is that the reflection is implemented with the same restrictions applied to source code access:
- Access from any location to a class-common component
- No access to private components outside of the class itself
- Limited access to protected and packaged (default access) components
However-at least some times, there is an easy way around these restrictions. The, and classes I used in the previous instance Constructor
Field
Method
extend a common basic class--& #160 java.lang.reflect.AccessibleObject
class. This class defines a setAccessible
method that enables you to start or turn off access detection for an instance of one of these classes. The only problem is that if you use the Security Manager, it detects whether the code that is shutting down access detection is licensed to do so. If not licensed, the Security manager throws an exception.
Listing 6 shows a program that TwoString
uses reflection on an instance of the listing 1 class to show that security is running:
Listing 6: Reflection security is running
public class Reflectsecurity {public static void Main (string[] args) { try { twostring ts = new Twostring ("a" , "B"); Field field = Clas.getdeclaredfield ("m_s1");// field.setaccessible (true); SYSTEM.OUT.PRINTLN ("Retrieved value is" + Field.get (inst)); } catch (Exception ex) { ex.printstacktrace (System.out);}} }
If you compile this program and run it directly from the command line without using any specific parameters, it will field.get(inst)
throw one in the call IllegalAccessException
. If you do not comment on the field.setAccessible(true)
line of code, recompile and rerun the code, it will succeed. Finally, if you add a JVM parameter to the command line -Djava.security.manager
to implement the security manager, it will fail again unless you have defined the ReflectSecurity
permissions for the class.
Back to top of page
Reflective performance
Reflection is a powerful tool, but there are some deficiencies. One of the major drawbacks is the impact on performance. Using reflection is basically an interpretation operation, you can tell the JVM what you want to do and it meets your requirements. This type of operation is always slower than just performing the same operation directly. To illustrate the performance cost of using reflection, I have prepared a set of benchmark programs for this article (see Resources, full code links).
Listing 7 is a summary of the field access performance test, including the basic test method. Each method tests a form of field access-working accessSame
with member fields of the same object, accessOther
using fields of another object that can be directly accessed, accessReflection
using fields from another object that can be accessed through reflection. In each case, the method performs the same calculation-the simple addition/multiplication order in the loop.
Listing 7: Field access performance test code
public int accesssame (int loops) {m_value = 0; for (int index = 0; index < loops; index++) {m_value = (m_value + additive_value) * Multiplier_value ; } return m_value;} public int accessreference (int loops) {Timingclass timing = new Timingclass (); for (int index = 0; index < loops; index++) {Timing.m_value = (timing.m_value + additive_value) * MU Ltiplier_value; } return timing.m_value;} public int accessreflection (int loops) throws Exception {Timingclass timing = new Timingclass (); try {Field field = Timingclass.class. Getdeclaredfield ("M_value"); for (int index = 0; index < loops; index++) {int value = (Field.getint (timing) + Additive_va LUE) * Multiplier_value; Field.setint (timing, value); } return timing.m_value; } catch (Exception ex) {System.out.println ("Error using reflection"); Throw ex; }}
The test program repeatedly calls each method, using a cycle number, thus averaging the time of the multiple calls to measure the result. The average value does not include the time of the first invocation of each method, so the initialization time is not a factor in the result. In the test for this article, I used 10 million cycles per call to run on a 1GHz piiim system. Three timing results for different Linux JVMs are shown in 1. All tests use the default settings for each JVM.
Figure 1: Field access time
The logarithmic scale of the above table can show all time, but reduces the difference visible effect. In the first two graphs (Sun JVM), the execution time using reflection is more than 1000 times times that of direct access. By comparison, the IBM JVM may be slightly better, but the reflection method still needs to be 700 times times longer than other methods. There is no significant difference in time between the other two methods on any JVM, but the IBM JVM is almost one-fold faster than the sun JVM. Most likely, this discrepancy reflects the professional optimizations of the sun Hot Spot JVM, which is poorly represented in simple benchmarks.
In addition to the field access time test, I made the same method call time test. In the method call, I tried the same three access variables as the field access and increased the use of the parameterless method variable instead of passing in and returning a value in the method call. Listing 8 shows the code for testing the three methods in the form of call passing and return values.
Listing 8: Method access performance Test code
public int calldirectargs (int loops) {int value = 0; for (int index = 0; index < loops; index++) {value = step (value); } return value; public int Callreferenceargs (int loops) {Timingclass timing = new Timingclass (); int value = 0; for (int index = 0; index < loops; index++) {value = Timing.step (value); } return value; public int Callreflectargs (int loops) throws Exception {Timingclass timing = new Timingclass (); try {method = TimingClass.class.getMethod ("Step", new class [] {int.class}); object[] args = new object[1]; Object value = new Integer (0); for (int index = 0; index < loops; index++) {args[0] = value; Value = Method.invoke (timing, args); } return ((Integer) value). Intvalue (); } catch (Exception ex) {System.out.println ("Error using reflection"); Throw ex; }}
Figure 2 shows the timing results I got from the method call. Reflection is much slower than direct access. The difference is not as large as the field entry, but without the use of parameters, the range is hundreds of times times the size of the sun 1.3.1 JVM and less than 30 times times the IBM JVM. On all JVMs, the test performance of reflection method calls using parameters is slower than calls that do not use parameters. int
This may be local because of the encapsulation required for the delivery and return values java.lang.Integer
. Since Integer
S is immutable, each method return presents a new requirement, which will add a lot of overhead.
Figure 2: Method call time
Reflection performance is an aspect of Sun's development of the 1.4 JVM, which is displayed in the reflection method invocation results. In terms of the performance of such operations, the Sun 1.4.1 JVM shows a significant improvement over the 1.3.1 version, which runs at about 1.3 in my test. 1 version of the Open. In such a simple test, the IBM 1.4.0 JVM was again getting better results, but only two to three times times faster than the Sun 1.4.1 JVM.
I have also written a similar timing tester for creating objects that use reflection, but the differences in this case are not as significant as in the case of field and method invocations. Using a newInstance()
call to create a simple java.lang.Object
instance consumes approximately 12 times times the time it was used on the Sun 1.3.1 JVM new Object()
, four times times the IBM 1.4.0 JVM, and only two of the Sun 1.4.1 JVM. The Array.newInstance(type, size)
time used to create an array is twice times the amount used on any test JVM new type[size]
, and as the array size increases, the differences are gradually reduced.
Back to top of page
Conclusion
Java language Reflection provides a versatile way to dynamically link program components. It allows programs to create and control objects of any class (based on security restrictions) without having to hardcode the target class in advance. These features make reflection especially useful for creating libraries that collaborate with objects in a very common way. For example, reflection is often used in a framework that continuously stores objects as databases, XML, or other external formats.
Reflection has two drawbacks. The first is a performance issue. Reflection is much slower than direct code when used for fields and method access. The degree of performance issues depends on how the reflection is used in the program. Slow performance will not be a problem if it is part of a program that is relatively rarely involved in running. Even if the worst-case timing chart in the test shows a reflection operation that only consumes a few microseconds. Performance issues become critical only when reflection is used in the core logic of performance-critical applications.
One of the more serious drawbacks of many applications is that using reflection blurs what is actually going on inside the program. The program staff wants to see the logic of the program in the source code, and the technology that bypasses the source code, such as reflection, can cause maintenance problems. The reflection code is more complex than the corresponding direct code, as seen in the code instance of performance comparison. The best solution to these problems is to use reflection conservatively-only where it can really add flexibility-to record its use in the target class.
In the next section, I'll provide a more detailed example of how to use reflection. This example provides an API to handle Java application command-line parameters, a tool that you might find appropriate for your application. It is also created based on the advantages of reflection, while avoiding its weaknesses. Can reflection simplify your command-line processing? You can find the answer in the 3rd part of the dynamics of Java programming .
Original: http://www.ibm.com/developerworks/cn/java/j-dyn0603/
The dynamics of Java programming, part 2nd: Introduction of Reflection--reprint