Example tutorial on JAVA reflection mechanism

  • 2020-04-01 03:28:09
  • OfStack

This article details Java's reflection mechanism in the form of examples, which is an important technique in Java programming. Share with you for your reference. Specific analysis is as follows:

First, Reflection is one of the features of the Java programming language Allows a running Java program to examine itself, or "self-audit," and directly manipulate the program's internal properties . For example, you can use it to get the names of the members of a Java class and display them. This capability in Java may not be used much in practice, but it simply does not exist in other programming languages. For example, in Pascal, C, or C++ there is no way to get information about function definitions in a program.

Javabeans are one of the practical applications of reflection that allows some tools to visualize operating software components. These tools load and retrieve properties of Java components (classes) dynamically by reflection.

1. A simple example

Consider the following simple example to see how reflection works.


import java.lang.reflect.*; 
public class DumpMethods { 
  public static void main(String args[]) { 
   try { 
      Class c = Class.forName("java.util.Stack"); 
      Method m[] = c.getDeclaredMethods(); 
      
      for (int i = 0; i < m.length; i++) 
        System.out.println(m[i].toString()); 
   } 
   catch (Throwable e){ 
      System.err.println(e); 
   } 
  } 
}

Its output is:


public synchronized java.lang.Object java.util.Stack.pop()
public java.lang.Object java.util.Stack.push(java.lang.Object)
public boolean java.util.Stack.empty()
public synchronized java.lang.Object java.util.Stack.peek()
public synchronized int java.util.Stack.search(java.lang.Object)

This lists the names of the parties of the java.util.Stack class and their qualifiers and return types.

This program loads the specified Class using class.forname, and then calls getDeclaredMethods to get a list of methods defined in the Class. Java. Lang. Reflect the Methods is used to describe a class of a class of a single method.

2. Start using Reflection

Classes for reflection, such as Method, can be found in the java.lang.relfect package. There are three steps to follow when using these classes: the first step is to get the java.lang.class object of the Class you want to manipulate. In a running Java program, the java.lang.class Class is used to describe classes, interfaces, and so on.

Here's one way to get a Class object:


Class c = Class.forName("java.lang.String");

This statement gets a class object of the String class. There is another method, as follows:

Class c = int. Class; Or Class c = Integer.TYPE;

They obtain class information for basic types. In the latter method, the predefined TYPE field in the wrapper class of the primitive TYPE, such as Integer, is accessed.

The second step is to call methods such as getDeclaredMethods to get a list of all the methods defined in the class.

Once you have this information, you can proceed to step 3 -- use the reflection API to manipulate this information, as shown in the following code:


Class c = Class.forName("java.lang.String"); 
Method m[] = c.getDeclaredMethods(); 
System.out.println(m[0].toString()); 

It will print a prototype of the first method defined in the String as text.

In the following examples, these three steps will exemplify the use of reflection to process specific applications.

Simulate the instanceof operator

With Class information in hand, the next step is usually to solve some basic problems with Class objects. For example, the class.isinstance method can be used to simulate the instanceof operator:


class S { 
} 
public class IsInstance { 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("S"); 
      boolean b1 = cls.isInstance(new Integer(37)); 
      System.out.println(b1); 
      boolean b2 = cls.isInstance(new S()); 
      System.out.println(b2); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

In this example, a Class object of Class S is created, and then some objects are checked to see if they are instances of S. Integer(37) is not, but new S() is.

3. Find the method of the class

Finding out what methods are defined in a class is a very valuable and fundamental use of reflection. The following code implements this usage:


import java.lang.reflect.*; 
public class Method1 { 
  private int f1(Object p, int x) throws NullPointerException { 
  if (p == null) 
   throw new NullPointerException(); 
  return x; 
} 
  public static void main(String args[]) { 
    try { 
      Class cls = Class.forName("Method1"); 
      Method methlist[] = cls.getDeclaredMethods(); 
      for (int i = 0; i < methlist.length; i++) { 
        Method m = methlist[i]; 
        System.out.println("name = " + m.getName()); 
        System.out.println("decl class = " + m.getDeclaringClass()); 
        Class pvec[] = m.getParameterTypes(); 
        for (int j = 0; j < pvec.length; j++) 
          System.out.println("param #" + j + " " + pvec[j]); 
        Class evec[] = m.getExceptionTypes(); 
        for (int j = 0; j < evec.length; j++) 
          System.out.println("exc #" + j + " " + evec[j]); 
        System.out.println("return type = " + m.getReturnType()); 
        System.out.println("-----"); 
      } 
    } 
    catch (Throwable e) { 
      System.err.println(e); 
    } 
  } 
}

The program first gets a description of the method1 class, then calls getDeclaredMethods to get a list of Method objects that describe each Method defined in the class, including public, protected, package, and private methods. If you use getMethods instead of getDeclaredMethods in your program, you can also get information about the methods you inherited.

Once you have a list of Method objects, it is not difficult to display the parameter types, exception types, return value types, and so on for these methods. Whether these types are primitive or class types can be given in order by the objects describing the class.

The output results are as follows:


name = f1 
decl class = class method1 
param #0 class java.lang.Object 
param #1 int 
exc #0 class java.lang.NullPointerException 
return type = int
-----
name = main 
decl class = class method1 
param #0 class [Ljava.lang.String; 
return type = void

Get constructor information

The usage of the get class constructor is similar to that of the above get method, such as:


import java.lang.reflect.*;
public class Constructor1 { 
  public Constructor1() { 
  } 
  protected Constructor1(int i, double d) { 
  } 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("Constructor1"); 
      Constructor ctorlist[] = cls.getDeclaredConstructors(); 
      for (int i = 0; i < ctorlist.length; i++) { 
       Constructor ct = ctorlist[i]; 
       System.out.println("name = " + ct.getName()); 
       System.out.println("decl class = " + ct.getDeclaringClass()); 
       Class pvec[] = ct.getParameterTypes(); 
       for (int j = 0; j < pvec.length; j++) 
         System.out.println("param #" + j + " " + pvec[j]); 
       Class evec[] = ct.getExceptionTypes(); 
       for (int j = 0; j < evec.length; j++) 
         System.out.println("exc #" + j + " " + evec[j]); 
       System.out.println("-----"); 
      } 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

In this example, information about the return type was not obtained because the constructor did not have a return type.

The result of this program is:


name = Constructor1
decl class = class Constructor1
param #0 int
param #1 double
-----
name = Constructor1
decl class = class Constructor1
-----

5. Get the fields of the class (fields)

It's also possible to figure out which data fields are defined in a class, and the following code does just that:


import java.lang.reflect.*; 
public class Field1 { 
  private double d; 
  public static final int i = 37; 
  String s = "testing"; 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("Field1"); 
      Field fieldlist[] = cls.getDeclaredFields(); 
      for (int i = 0; i < fieldlist.length; i++) { 
       Field fld = fieldlist[i]; 
       System.out.println("name = " + fld.getName()); 
       System.out.println("decl class = " + fld.getDeclaringClass()); 
       System.out.println("type = " + fld.getType()); 
       int mod = fld.getModifiers(); 
       System.out.println("modifiers = " + Modifier.toString(mod)); 
       System.out.println("-----"); 
      } 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

This example is very similar to the previous one. The example USES a new Modifier, which is also a reflection class, to describe modifiers for field members, such as "private int." These modifiers are themselves described by integers, and Modifier. ToString is used to return string descriptions in "official" order (such as "static" before "final"). The output of this program is:


name = d
decl class = class Field1
type = double
modifiers = private
-----
name = i
decl class = class Field1
type = int
modifiers = public static final
-----
name = s
decl class = class Field1
type = class java.lang.String
modifiers = 
-----

In the case of getDeclaredFields and methods, you can also get only the fields declared in the current class (getDeclaredFields), or you can get the fields defined in the parent class (getFields).

Execute the method by its name

So far, the examples are all about how to get information about a class. We can also use reflection for other things, such as executing a named method. The following example demonstrates this:


import java.lang.reflect.*; 
public class Method2 { 
  public int add(int a, int b) { 
   return a + b; 
  } 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("Method2"); 
      Class partypes[] = new Class[2]; 
      partypes[0] = Integer.TYPE; 
      partypes[1] = Integer.TYPE; 
    
      Method meth = cls.getMethod("add", partypes); 
      Method2 methobj = new Method2(); 
      Object arglist[] = new Object[2]; 
      arglist[0] = new Integer(37); 
      arglist[1] = new Integer(47); 
      Object retobj = meth.invoke(methobj, arglist); 
      Integer retval = (Integer) retobj; 
      System.out.println(retval.intValue()); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

If a program doesn't know it needs to execute a method until somewhere along the way, and the name of the method is specified during the program's execution (for example, in the javabeans development environment), the above program demonstrates how to do it.

In the example above, getMethod is used to find a method with two integer parameters named add. Once the Method is found and the corresponding Method object is created, it is executed in the correct object instance. When you execute this method, you need to provide a list of arguments, which in the above example are two Integer objects that wrap the integers 37 and 47, respectively. The return of the execution method is also an Integer object that encapsulates the return value 84.

Create a new object

With constructors, you can't do it the same way as with execution methods, because executing a constructor means creating a new object (exactly, the process of creating an object involves allocating memory and constructing the object). So, the most similar example is as follows:


import java.lang.reflect.*; 
public class Constructor2 { 
  public Constructor2() { 
  } 
  public Constructor2(int a, int b) { 
   System.out.println("a = " + a + " b = " + b); 
  } 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("Constructor2"); 
      Class partypes[] = new Class[2]; 
      partypes[0] = Integer.TYPE; 
      partypes[1] = Integer.TYPE; 
      Constructor ct = cls.getConstructor(partypes); 
      Object arglist[] = new Object[2]; 
      arglist[0] = new Integer(37); 
      arglist[1] = new Integer(47); 
      Object retobj = ct.newInstance(arglist); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

Find the constructor for the specified parameter type and execute it to create a new object instance. It is valuable to use this method to create objects dynamically while the program is running, rather than at compile time.

8. Change the value of the field (field)

Another use of reflection is to change the value of an object's data field. Reflection can find an object's field by name from a running program and change it, as illustrated by the following example:


import java.lang.reflect.*; 
public class Field2 { 
  public double d; 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("Field2"); 
      Field fld = cls.getField("d"); 
      Field2 f2obj = new Field2(); 
      System.out.println("d = " + f2obj.d); 
      fld.setDouble(f2obj, 12.34); 
      System.out.println("d = " + f2obj.d); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

In this example, the value of field d is changed to 12.34.

9. Use arrays

The final use of reflection in this article is to create an array of operations. Arrays are a special class type in the Java language, and a reference to an array can be assigned to an Object reference. Look at the following example to see how arrays work:


import java.lang.reflect.*; 
public class Array1 { 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("java.lang.String"); 
      Object arr = Array.newInstance(cls, 10); 
      Array.set(arr, 5, "this is a test"); 
      String s = (String) Array.get(arr, 5); 
      System.out.println(s); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

The example creates a String array of 10 unit lengths, assigns a value to the String at the fifth position, and finally fetches the String from the array and prints it out.

The following code provides a more complex example:


import java.lang.reflect.*; 
public class Array2 { 
  public static void main(String args[]) { 
   int dims[] = new int[]{5, 10, 15}; 
   Object arr = Array.newInstance(Integer.TYPE, dims); 
   Object arrobj = Array.get(arr, 3); 
   Class cls = arrobj.getClass().getComponentType(); 
   System.out.println(cls); 
   arrobj = Array.get(arrobj, 5); 
   Array.setInt(arrobj, 10, 37); 
   int arrcast[][][] = (int[][][]) arr; 
   System.out.println(arrcast[3][5][10]); 
  } 
}

The example creates a 5 x 10 x 15 integer array and assigns a value of 37 to the element in [3][5][10]. Note that a multidimensional Array is actually an Array of arrays. For example, after the first array.get, arrobj is a 10 x 15 Array. It then takes one of the elements, an Array of length 15, and assigns its 10th element using array.setint.

Pay attention to The type of the array when created is dynamic and is not known at compile time .

I believe that this article has certain reference value to the study of Java programming.


Related articles: