Chapter 17. Reflection, or “A Class Named Class”

17.0 Introduction

The class java.lang.Class, and the reflection package java.lang.reflect, provide a number of mechanisms for gathering information from the Java Virtual Machine. Known collectively as reflection, these facilities allow you to load classes on the fly, to find methods and fields in classes, to generate listings of them, and to invoke methods on dynamically loaded classes. There is even a mechanism to let you construct a class from scratch (well, actually, from an array of bytes) while your program is running. This is about as close as Java lets you get to the magic, secret internals of the Java machine.

The JVM itself is a large program, normally written in C and/or C++, that implements the Java Virtual Machine abstraction. You can get the source for OpenJDK and other JVMs via the Internet, which you could study for months. Here we concentrate on just a few aspects, and only from the point of view of a programmer using the JVM’s facilities, not how it works internally; that is an implementation detail that could vary from one vendor’s JVM to another.

I’ll start with loading an existing class dynamically, move on to listing the fields and methods of a class and invoking methods, and end by creating a class on the fly using a ClassLoader. One of the more interesting aspects of Java, and one that accounts for both its flexibility (applets in days of your, servlets, web services, and other dynamic APIs) and was once part of its perceived speed problem, is the notion of dynamic loading. For example, even the simplest “Hello Java” program has to load the class file for your HelloJava class, the class file for its parent (usually java.lang.Object), the class for PrintStream (because you used System.out), the class for PrintStream’s parent, and IOException, and its parent, and so on. To see this in action, try something like:

java -verbose HelloJava | more

To take another example, when applets were popular, a browser would download an applet’s bytecode file over the Internet and run it on your desktop. How does it load the class file into the running JVM? We discuss this little bit of Java magic in Recipe 17.4. The chapter ends with replacement versions of the JDK tools javap, and a cross-reference tool that you can use to become a famous Java author by publishing your very own reference to the complete Java API.

17.1 Getting a Class Descriptor

Problem

You want to get a Class object from a class name or instance.

Solution

If the type name is known at compile time, you can get the class instance using the compiler keyword .class, which works on any type that is known at compile time, even the eight primitive types.

Otherwise, if you have an object (an instance of a class), you can call the java.lang.Object method getClass(), which returns the Class object for the object’s class (now that was a mouthful!):

        System.out.println("Trying the ClassName.class keyword:");
        System.out.println("Object class: " + Object.class);
        System.out.println("String class: " + String.class);
        System.out.println("String[] class: " + String[].class);
        System.out.println("Calendar class: " + Calendar.class);
        System.out.println("Current class: " + ClassKeyword.class);
        System.out.println("Class for int: " + int.class);
        System.out.println();

        System.out.println("Trying the instance.getClass() method:");
        System.out.println("Sir Robin the Brave".getClass());
        System.out.println(Calendar.getInstance().getClass());

When we run it, we see:

C:javasrc
eflect>java  ClassKeyword 
Trying the ClassName.class keyword:
Object class: class java.lang.Object
String class: class java.lang.String
String[] class: class [Ljava.lang.String;
Calendar class: class java.util.Calendar
Current class: class ClassKeyword
Class for int: int

Trying the instance.getClass( ) method:
class java.lang.String
class java.util.GregorianCalendar

C:javasrc
eflect>

Nothing fancy, but as you can see, you can get the Class object for almost anything known at compile time, whether it’s part of a package or not.

17.2 Finding and Using Methods and Fields

Problem

You need to find arbitrary method or field names in arbitrary classes.

Solution

Use the reflection package java.lang.reflect.

Discussion

If you just wanted to find fields and methods in one particular class, you wouldn’t need this recipe; you could simply create an instance of the class using new and refer to its fields and methods directly. But this allows you to find methods and fields in any class, even classes that have not yet been written! Given a class object created as in Recipe 17.1, you can obtain a list of constructors, a list of methods, or a list of fields. The method getMethods() lists the methods available for a given class as an array of Method objects. Similarly, getFields() returns a list of Field objects. Because constructor methods are treated specially by Java, there is also a getConstructors() method, which returns an array of Constructor objects. Even though Class is in the package java.lang, the Constructor, Method, and Field objects it returns are in java.lang.reflect, so you need an import of this package. The ListMethods class (see Example 17-1) shows how get a list of methods in a class whose name is known at runtime.

Example 17-1. main/src/main/java/reflection/ListMethods.java
public class ListMethods {
    public static void main(String[] argv) throws ClassNotFoundException {
        if (argv.length == 0) {
            System.err.println("Usage: ListMethods className");
            return;
        }
        Class<?> c = Class.forName(argv[0]);
        Constructor<?>[] cons = c.getConstructors();
        printList("Constructors", cons);
        Method[] meths = c.getMethods();
        printList("Methods", meths);
    }
    static void printList(String s, Object[] o) {
        System.out.println("*** " + s + " ***");
        for (int i=0; i<o.length; i++)
            System.out.println(o[i].toString());
    }
}

For example, you could run Example 17-1 on a class like java.lang.String and get a fairly lengthy list of methods; I’ll only show part of the output so you can see what it looks like:

> java reflection.ListMethods java.lang.String
*** Constructors ***
public java.lang.String( )
public java.lang.String(java.lang.String)
public java.lang.String(java.lang.StringBuffer)
public java.lang.String(byte[])
// and many more...
*** Methods ***
public static java.lang.String java.lang.String.copyValueOf(char[])
public static java.lang.String java.lang.String.copyValueOf(char[],int,int)
public static java.lang.String java.lang.String.valueOf(char)
// and more valueOf( ) forms...
public boolean java.lang.String.equals(java.lang.Object)
public final native java.lang.Class java.lang.Object.getClass( )
// and more java.lang.Object methods...
public char java.lang.String.charAt(int)
public int java.lang.String.compareTo(java.lang.Object)
public int java.lang.String.compareTo(java.lang.String)

You can see that this could be extended (almost literally) to write a BeanMethods class that would list only the set/get methods defined in a JavaBean (see Recipe 15.4).

Alternatively, you can find a particular method and invoke it, or find a particular field and refer to its value. Let’s start by finding a given field, because that’s the easiest. Example 17-2 is code that, given an Object and the name of a field, finds the field (gets a Field object) and then retrieves and prints the value of that Field as an int.

Example 17-2. main/src/main/java/reflection/FindField.java
public class FindField {

    public static void main(String[] unused)
    throws NoSuchFieldException, IllegalAccessException {

        // Create instance of FindField
        FindField gf = new FindField();

        // Create instance of target class (YearHolder defined below).
        Object o = new YearHolder();

        // Use gf to extract a field from o.
        System.out.println("The value of 'currentYear' is: " +
            gf.intFieldValue(o, "currentYear"));
    }

    int intFieldValue(Object o, String name)
    throws NoSuchFieldException, IllegalAccessException {
        Class<?> c = o.getClass();
        Field fld = c.getField(name);
        int value = fld.getInt(o);
        return value;
    }
}

/** This is just a class that we want to get a field from */
class YearHolder {
    /** Just a field that is used to show getting a field's value. */
    public int currentYear = Calendar.getInstance().get(Calendar.YEAR);
}

What if we need to find a method? The simplest way is to use the methods getMethod() and invoke(). But this is not altogether trivial. Suppose that somebody gives us a reference to an object. We don’t know its class but have been told that it should have this method:

public void work(String s) { }

We wish to invoke work(). To find the method, we must make an array of Class objects, one per item in the parameter list. So, in this case, we make an array containing only a reference to the class object for String. Because we know the name of the class at compile time, we’ll use the shorter invocation String.class instead of Class.forName(). This, plus the name of the method as a string, gets us entry into the getMethod() method of the Class object. If this succeeds, we have a Method object. But guess what? In order to invoke the method, we have to construct yet another array, this time an array of Object references actually containing the data to be passed to the invocation. We also, of course, need an instance of the class in whose context the method is to be run. For this demonstration class, we need to pass only a single string, because our array consists only of the string. Example 17-3 is the code that finds the method and invokes it.

Example 17-3. main/src/main/java/reflection/GetAndInvokeMethod.java
/**
 * Get a given method, and invoke it.
 * @author Ian F. Darwin, http://www.darwinsys.com/
 */
public class GetAndInvokeMethod {

    /** This class is just here to give us something to work on,
     * with a println() call that will prove we got into it.
     */
    static class X {
        public void work(int i, String s) {
            System.out.printf("Called: i=%d, s=%s%n", i, s);
        }
        // The main code does not use this overload.
        public void work(int i) {
            System.out.println("Unexpected call!");
        }
    }
    public static void main(String[] argv) {
        try {
            Class<?> clX = X.class; // or Class.forName("X");

            // To find a method we need the array of matching Class types.
            Class<?>[] argTypes = {
                int.class,
                String.class
            };

            // Now find a Method object for the given method.
            Method worker = clX.getMethod("work", argTypes);

            // To INVOKE the method, we need the invocation
            // arguments, as an Object array.
            Object[] theData = {
                42,
                "Chocolate Chips"
            };

            // The obvious last step: invoke the method.
            // First arg is an instance, null if static method
            worker.invoke(new X(), theData);

        } catch (Exception e) {
            System.err.println("Invoke() failed: " + e);
        }
    }
}

Not tiny, but it’s still not bad. In most programming languages, you couldn’t do that in the 40 lines it took us here.

A word of caution: when the arguments to a method are of a primitive type, such as int, you do not pass Integer.class into getMethod(). Instead, you must use the class object representing the primitive type int. The easiest way to find this class is in the Integer class, as a public constant named TYPE, so you’d pass Integer.TYPE. The same is true for all the primitive types; for each, the corresponding wrapper class has the primitive class referred to as TYPE.

Java also includes a mechanism called a MethodHandle which was intended both to simplify and to generalize use of Reflection to invoke methods; we do not cover it here because in practice it has not shown to be a significant improvement over using the Reflection API.

17.3 Accessing Private Methods and Fields via Reflection

Problem

You want to access private fields and have heard you can do so using the Reflection API.

Solution

You bad kid, you! You’re not supposed to go after other classes’ private parts. But if you have to, and the SecurityManager allows you to use Reflection, you can.

Discussion

There is occasionally a need to access private fields in other classes. For example, I did so recently in writing a JUnit test case that needed to see all the fields of a target class. The secret is to call the Field or Method descriptor’s setAccessible() method passing the value true before trying to get the value or invoke the method. It really is that easy, as shown in Example 17-4.

Example 17-4. main/src/main/java/reflection/DefeatPrivacy.java
class X {
    @SuppressWarnings("unused") // Used surreptitiously below.
    private int p = 42;
    int q = 3;
}

/**
 * Demonstrate that it is, in fact, all too easy to access private members
 * of an object using Reflection, using the default SecurityManager
 */
public class DefeatPrivacy {

    public static void main(String[] args) throws Exception {
        new DefeatPrivacy().process();
    }

    private void process() throws Exception {
        X x = new X();
        System.out.println(x);
        // System.out.println(x.p); // Won't compile
        System.out.println(x.q);
        Class<? extends X> class1 = x.getClass();
        Field[] flds = class1.getDeclaredFields();
        for (Field f : flds) {
            f.setAccessible(true);    // bye-bye "private"
            System.out.println(f + "==" + f.get(x));
            f.setAccessible(false);    // reset to "correct" state
        }
    }
}
Warning

Use this with extreme care, because it can defeat some of the most cherished principles of Java programming.

17.4 Loading and Instantiating a Class Dynamically

Problem

You want to load classes dynamically, just like web servers load your servlets.

Solution

Use class.forName("ClassName"); and the class’s newInstance( ) method.

Discussion

Suppose you are writing a Java application and want other developers to be able to extend your application by writing Java classes that run in the context of your application. In other words, these developers are, in essence, using Java as an extension language, in the same way that applets are an extension of a web browser. You would probably want to define a small set of methods that these extension programs would have and that you could call for such purposes as initialization, operation, and termination. The best way to do this is, of course, to publish a given, possibly abstract, class that provides those methods and get the developers to subclass from it. Sound familiar? It should. This is just how web browsers such as Netscape allow the deployment of applets.

We’ll leave the thornier issues of security and of loading a class file over a network socket for now, and assume that the user can install the classes into the application directory or into a directory that appears in CLASSPATH at the time the program is run. First, let’s define our class. We’ll call it Cooklet (see Example 17-5) to avoid infringing on the overused word applet. And we’ll initially take the easiest path from ingredients to cookies before we complicate it.

Example 17-5. Cooklet.java
/** A simple class, just to provide the list of methods that
 * users need to provide to be usable in our application.
 * Note that the class is abstract so you must subclass it,
 * but the methods are non-abstract so you don't have to provide
 * dummy versions if you don't need a particular functionality.
 */
public abstract class Cooklet {

    /** The initialization method. The Cookie application will
     * call you here (AFTER calling your no-argument constructor)
     * to allow you to initialize your code
     */
    public void initialize( ) {
    }

    /** The work method. The cookie application will call you
     * here when it is time for you to start cooking.
     */
    public void work( ) {
    }

    /** The termination method. The cookie application will call you
     * here when it is time for you to stop cooking and shut down
     * in an orderly fashion.
     */
    public void terminate( ) {
    }
}

Now, because we’ll be baking, err, making this available to other people, we’ll probably want to cook up a demonstration version too; see Example 17-6.

Example 17-6. main/src/main/java/reflection/DemoCooklet.java
public class DemoCooklet extends Cooklet {
    public void work() {
        System.out.println("I am busy baking cookies.");
    }
    public void terminate() {
        System.out.println("I am shutting down my ovens now.");
    }
}

But how does our application use it? Once we have the name of the user’s class, we need to create a Class object for that class. This can be done easily using the static method Class.forName() . Then we can create an instance of it using the Class object’s newInstance() method; this calls the class’s no-argument constructor. Then we simply cast the newly constructed object to our Cooklet class, and we can call its methods! It actually takes longer to describe this code than to look at the code, so let’s do that now; see Example 17-7.

Example 17-7. main/src/main/java/reflection/Cookies.java
public class Cookies {
    public static void main(String[] argv) {
        System.out.println("Cookies Application Version 0.0");
        Cooklet cooklet = null;
        String cookletClassName = argv[0];
        try {
            Class<Cooklet> cookletClass =
                (Class<Cooklet>) Class.forName(cookletClassName);
            cooklet = cookletClass.newInstance();
        } catch (Exception e) {
            System.err.println("Error " + cookletClassName + e);
        }
        cooklet.initialize();
        cooklet.work();
        cooklet.terminate();
    }
}

And if we run it?

$ java Cookies DemoCooklet
Cookies Application Version 0.0
I am busy baking cookies.
I am shutting down my ovens now.
$

Of course, this version has rather limited error handling. But you already know how to fix that. Your ClassLoader can also place classes into a package by constructing a Package object; you should do this if loading any medium-sized set of application classes.

17.5 Constructing a Class from Scratch with a ClassLoader

Problem

You need to load a class from a nonstandard location and run its methods.

Solution

Examine the existing loaders such as java.net.URLClassLoader. If none is suitable, write and use your own ClassLoader.

Discussion

A ClassLoader, of course, is a program that loads classes. One class loader is built into the Java Virtual Machine, but your application can create others as needed. Learning to write and run a working class loader and using it to load a class and run its methods is a nontrivial exercise. In fact, you rarely need to write a class loader, but knowing how is helpful in understanding how the JVM finds classes, creates objects, and calls methods.

ClassLoader itself is abstract; you must subclass it, presumably providing a loadClass() method that loads classes as you wish. It can load the bytes from a network connection, a local disk, RAM, a serial port, or anywhere else. Or you can construct the class file in memory yourself, if you have access to a compiler.

There is a general-purpose loader called java.net.URLClassLoader that can be used if all you need is to load classes via the Web protocol (or, more generally, from one or more URLs).

You must call the class loader’s loadClass() method for any classes you wish to explicitly load from it. Note that this method is called to load all classes required for classes you load (superclasses that aren’t already loaded, for example). However, the JVM still loads classes that you instantiate with the new operator “normally” via CLASSPATH.

When writing a class loader, your loadClass() method needs to get the class file into a byte array (typically by reading it), convert the array into a Class object, and return the result.

What? That sounds a bit like “And Then a Miracle Occurs…” And it is. The miracle of class creation, however, happens down inside the JVM, where you don’t have access to it. Instead, your ClassLoader has to call the protected defineClass() method in your superclass (which is java.lang.ClassLoader). This is illustrated in Figure 17-1, where a stream of bytes containing a hypothetical Chicken class is converted into a ready-to-run Chicken class in the JVM by calling the defineClass() method.

jcb4 1701
Figure 17-1. ClassLoader in action

What next?

To use your ClassLoader subclass, you need to instantiate it and call its loadClass() method with the name of the class you want to load. This gives you a Class object for the named class; the Class object in turn lets you construct instances, find and call methods, etc. Refer back to Recipe 17.2.

17.6 Constructing a Class from Scratch with JavaCompiler

Problem

You’d rather construct a class dynamically by generating source code and compiling it.

Solution

Use the JavaCompiler from javax.tools.

Discussion

There are many cases where you might need to generate code on the fly. If you’re writing a framework, you might want to introspect on a model class to find its fields, and generate accessors for them on the fly. As we’ve seen in Recipe 17.2 above you could do this with the Field class. However, for a high-volume operation it may well be more efficient to generate direct access code.

The Java Compiler API has been around since Java 1.6 and is fairly easy to use for simple cases. The basic steps are:

  • Get the JavaCompiler object for your current Java Runtime. If it’s not available, either give up altogether, or fall back to using reflection.

  • Get a CompilerTask (which is also a Callable) to run the comilation, passing input and outputs.

  • Invoke the Callable, either directly or by using an ExecutorService.

  • Check the results. If true, invoke the class.

This is demonstrated in Example 17-8.

Example 17-8. main/src/main/java/reflection/JavaCompilerDemo.java
package reflection;

import java.lang.reflect.Method;
import java.net.URI;
import java.util.List;
import java.util.concurrent.Callable;

// tag::main[]
import javax.tools.JavaCompiler;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

/** Demo the Java Compiler API: Create a class, compile, load, and run it.
 * N.B. Will not run under Eclipse due to classpath settings;
 * best run it standalone using "java JavaCompiler.java"
 * @author Ian Darwin
 */
public class JavaCompilerDemo {
    private final static String PACKAGE = "reflection";
    private final static String CLASS = "AnotherDemo";
    private static boolean verbose;
    public static void main(String[] args) throws Exception {
        String source = "package " + PACKAGE + ";
" +                  1
            "public class " + CLASS + " {
" +
            "	public static void main(String[] args) {
" +
            "		String message = (args.length > 0 ? args[0] : "Hi")" + ";
" +
            "		System.out.println(message + " from AnotherDemo");
" +
            "	}
}
";
        if (verbose)
            System.out.print("Source to be compiled:
" + source);

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();   2
        if (compiler == null) {
            throw new IllegalStateException("No default compiler, giving up.");
        }
        Callable<Boolean> compilation =
            compiler.getTask(null, null, null, List.of("-d","."), null, 3
            List.of(new MySource(CLASS, source)));
        boolean result = compilation.call();                            4
        if (result) {
            System.out.println("Compiled OK");
            Class<?> c = Class.forName(PACKAGE + "." + CLASS);          5
            System.out.println("Class = " + c);
            Method m = c.getMethod("main", args.getClass());            6
            System.out.println("Method descriptor = " + m);
            Object[] passedArgs = { args };
            m.invoke(null, passedArgs);                                 7
        } else {
            System.out.println("Compilation failed");
        }
    }
}
// end::main[]

class MySource extends SimpleJavaFileObject {
    final String source;
    MySource(String fileName, String source) {
        super(URI.create("string:///" + fileName.replace('.', '/') +
                Kind.SOURCE.extension), Kind.SOURCE);
        this.source = source;
    }
    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return source;
    }
}
1

The source code that we want to compile. In real life would probably be dynamically generated, maybe using a StringBuffer.

2

Get a reference to the default JavaCompiler object.

3

Ask the compiler to create a CompilerTask to do the compilation. CompilerTask is also Callable and we save it under that type. The “-d .” are standard javac arguments. MySource extends the compiler-provided API class SimpleJavaFileObject to give access to a file by creating a “file://” URL.

4

A Callable can be put into an Executor (see Recipe 16.1); we don’t need this capability but the Compiler API returns it. We invoke the Callable directly.

5

Assuming the result was true indicating success, we load the class with Class.forName()

6

We have to find the main() method in the generated class. We re-use the String[].class type from args, since all main methods have the same argument.

7

Finally, we can invoke the main method, re-using the incoming args array to pass any welcome message along.

Running this program with and without an argument shows that the argument passed to the JavaCompilerDemo is being passed correctly to the generated AnotherDemo class.

$ java src/main/java/reflection/JavaCompilerDemo.java
Compiled OK
Class = class reflection.AnotherDemo
Method descriptor = public static void reflection.AnotherDemo.main(java.lang.String[])
Hi from AnotherDemo
$ java src/main/java/reflection/JavaCompilerDemo.java Welcome
Compiled OK
Class = class reflection.AnotherDemo
Method descriptor = public static void reflection.AnotherDemo.main(java.lang.String[])
Welcome from AnotherDemo
$

There is a lot to explore in the Compiler API, including the JavaFileManager that lets you control the placement of class files (other than by using -d as we did here), listeners to monitor compilation, control of output and error streams, and more. Consult the javax.tools.JavaCompiler documentation for details.

17.7 Performance Timing

Problem

Slow performance?

Solution

Use a profiler, or, time individual methods using System.currentTimeMillis() before and after invoking the target method; the difference is the time that method took.

Discussion

Profilers

Profiling tools—profilers—have a long history as one of the important tools in a programmer’s toolkit. A commercial profiling tool will help find bottlenecks in your program by showing both the number of times each method was called, and the amount of time in each.

Quite a bit of useful information can be obtained from a Java application by use of the VisualVM tool, which was part of the Oracle JDK up until Java 8. With Java 9 this tool was open-sourced, and is now available from the VisualVM project.

Another tool that is part of the JDK is Java Flight Recorder, which is now open-sourced and built into the JDK. Its data are meant to be analyzed by Java Mission Control. There are also third-party profilers that will give more detailed information; a web search will find current commercial offerings.

Measuring a single method

The simplest technique is to save the JVM’s accumulated time before and after dynamically loading a main program, and calculate the difference between those times. Code to do just this is presented in Example 17-11; for now, just remember that we have a way of timing a given Java class.

One way of measuring the efficiency of a particular operation is to run it many times in isolation. The overall time the program takes to run thus approximates the total time of many invocations of the same operation. Gross numbers like this can be compared if you want to know which of two ways of doing something is more efficient. Consider the case of string concatenation versus println(). The code:

println("Time is " + n.toString( ) + " seconds");

will probably work by creating a StringBuilder, appending the string "Time is ", the value of n as a string, and " seconds", and finally converting the finished StringBuilder to a String and passing that to println(). Suppose you have a program that does a lot of this, such as a Java servlet that creates a lot of HTML this way, and you expect (or at least hope) your web site to be sufficiently busy so that doing this efficiently will make a difference. There are two ways of thinking about this:

  • Theory A: This string concatenation is inefficient.

  • Theory B: String concatenation doesn’t matter; println() is inefficient, too.

A proponent of Theory A might say that because println() just puts stuff into a buffer, it is very fast and that string concatenation is the expensive part.

How to decide between Theory A and Theory B? Assume you are willing to write a simple test program that tests both theories. Let’s just write a simple program both ways and time it. Example 17-9 is the timing program for Theory A.

Example 17-9. main/src/main/java/performance/StringPrintA.java
public class StringPrintA {
    public static void main(String[] argv) {
        Object o = "Hello World";
        for (int i=0; i<100000; i++) {
            System.out.println("<p><b>" + o.toString() + "</b></p>");
        }
    }
}

StringPrintAA (in the javasrc repo but not printed here) is the same but explicitly uses a StringBuilder for the string concatenation. Example 17-10 is the tester for Theory B.

Example 17-10. main/src/main/java/performance/StringPrintB.java
public class StringPrintB {
    public static void main(String[] argv) {
        Object o = "Hello World";
        for (int i=0; i<100000; i++) {
            System.out.print("<p><b>");
            System.out.print(o.toString());
            System.out.print("</b></p>");
            System.out.println();
        }
    }
}

Timing results

I ran StringPrintA, StringPrintAA, and StringPrintB twice each on the same computer. To eliminate JVM startup times, I ran them from a program called TimeNoArgs, which takes a class name and invokes its main() method, using the Reflection API. TimeNoArgs and a shell script to run it, stringprinttimer.sh, are in the performance folder of the javasrc source repository. Here are the results:

2004 Results

StringPrintA

17.23, 17.20 seconds

StringPrintAA

17.23, 17.23 seconds

StringPrintB

27.59, 27.60 seconds

2014 Results

StringPrintA

0.714, 0.525 seconds

StringPrintAA

0.616, 0.561 seconds

StringPrintB

Although the times went down by a factor of roughly 20 over a decade, the ratios remain remarkably consistent: StringPrintB, which calls print() and println() multiple times, takes roughly twice as long.

Moral: Don’t guess. If it matters, time it.

Another moral: Multiple calls to System.out.print() cost more than the same number of calls to a StringBuilder’s append() method, by a factor of roughly 1.5 (or 150%). Theory B wins; the extra println calls appear to save a string concatenation but make the program take substantially longer.

Other aspects of performance: GC

There are many other aspects of software performance. One that is fundamental to Java is garbage collection behavior. Sun/Oracle usually talk about this at JavaOne. See, for example, the JavaOne 2003 paper “Garbage Collection in the Java HotSpot Virtual Machine”. You should also see the JavaOne 2007 talk by the same GC Development team, “Garbage-Collection-Friendly Programming”, TS-2906. Unfortunately it seems to have gotten lost from Oracle’s website, but is still available online. JavaOne 2010 featured an updated presentation entitled “The Garbage Collection MythBusters.”

A timing program

It’s pretty easy to build a simplified time command in Java, given that you have System.currentTimeMillis() to start with. Run my Time program, and, on the command line, specify the name of the class to be timed, followed by the arguments (if any) that class needs for running. The program is shown in Example 17-11. The time that the class took is displayed. But remember that System.currentTimeMillis() returns clock time, not necessarily CPU time. So you must run it on a machine that isn’t running a lot of background processes. And note also that I use dynamic loading (see Recipe 17.4) to let you put the Java class name on the command line.

Example 17-11. main/src/main/java/performance/Time.java
public class Time {
    public static void main(String[] argv) throws Exception {
        // Instantiate target class, from argv[0]
        Class<?> c = Class.forName(argv[0]);

        // Find its static main method (use our own argv as the signature).
        Class<?>[] classes = { argv.getClass() };
        Method main = c.getMethod("main", classes);

        // Make new argv array, dropping class name from front.
        // (Normally Java doesn't get the class name, but in
        // this case the user puts the name of the class to time
        // as well as all its arguments...
        String nargv[] = new String[argv.length - 1];
        System.arraycopy(argv, 1, nargv, 0, nargv.length);

        Object[] nargs = { nargv };

        System.err.println("Starting class " + c);

        // About to start timing run. Important to not do anything
        // (even a println) that would be attributed to the program
        // being timed, from here until we've gotten ending time.

        // Get current (i.e., starting) time
        long t0 = System.currentTimeMillis();

        // Run the main program
        main.invoke(null, nargs);

        // Get ending time, and compute usage
        long t1 = System.currentTimeMillis();

        long runTime = t1 - t0;

        System.err.println(
             "runTime="  + Double.toString(runTime/1000D));
    }
}

Of course, you can’t directly compare the results from the operating system time command with results from running this program. There is a rather large, but fairly constant, initialization overhead—the JVM startup and the initialization of Object and System.out, for example—that is included in the former and excluded from the latter. One could even argue that my Time program is more accurate because it excludes this constant overhead. But, as noted, it must be run on a single-user machine to yield repeatable results. And no fair running an editor in another window while waiting for your timed program to complete!

See Also

Java Performance: The Definitive Guide by Scott Oaks (O’Reilly) provides information on tuning Java performance.

17.8 Printing Class Information

Problem

You want to print all the information about a class, similar to the way javap does.

Solution

Get a Class object, call its getFields() and getMethods(), and print the results.

Discussion

The JDK includes a program called javap, the Java Printer. Sun’s JDK version normally prints the outline of a class file—a list of its methods and fields—but can also print out the Java bytecodes or machine instructions. The Kaffe package did not include a version of javap, so I wrote one and contributed it (see Example 17-12). The Kaffe folks have expanded it somewhat, but it still works basically the same. My version doesn’t print the bytecodes; it behaves rather like Sun’s behaves when you don’t give theirs any command-line options.

The getFields() and getMethods() methods return arrays of Field and Method, respectively; these are both in package java.lang.reflect. I use a Modifiers object to get details on the permissions and storage attributes of the fields and methods. In many Java implementations, you can bypass this and simply call toString() in each Field and Method object (as I do here for Constructors). Doing it this way gives me a bit more control over the formatting.

Example 17-12. main/src/main/java/reflection/MyJavaP.java
public class MyJavaP {

    /** Simple main program, construct self, process each class name
     * found in argv.
     */
    public static void main(String[] argv) {
        MyJavaP pp = new MyJavaP();

        if (argv.length == 0) {
            System.err.println("Usage: MyJavaP className [...]");
            System.exit(1);
        } else for (int i=0; i<argv.length; i++)
            pp.doClass(argv[i]);
    }

    /** Format the fields and methods of one class, given its name.
     */
    protected void doClass(String className) {
        try {
            Class<? extends Object> c = Class.forName(className);

            final Annotation[] annotations = c.getAnnotations();
            for (Annotation a : annotations) {
                System.out.println(a);
            }

            System.out.println(c + " {");

            Field fields[] = c.getDeclaredFields();
            for (Field f : fields) {
                final Annotation[] fldAnnotations = f.getAnnotations();
                for (Annotation a : fldAnnotations) {
                    System.out.println(a);
                }
                if (!Modifier.isPrivate(f.getModifiers()))
                    System.out.println("	" + f + ";");
            }

            Constructor<? extends Object>[] constructors = c.getConstructors();
            for (Constructor<? extends Object> con : constructors) {
                System.out.println("	" + con + ";");
            }

            Method methods[] = c.getDeclaredMethods();
            for (Method m : methods) {
                final Annotation[] methodAnnotations = m.getAnnotations();
                for (Annotation a : methodAnnotations) {
                    System.out.println(a);
                }
                if (!Modifier.isPrivate(m.getModifiers())) {
                    System.out.println("	" + m + ";");
                }
            }
            System.out.println("}");
        } catch (ClassNotFoundException e) {
            System.err.println("Error: Class " +
                className + " not found!");
        } catch (Exception e) {
            System.err.println("JavaP Error: " + e);
        }
    }
}

17.9 Listing Classes in a Package

Problem

You want to get a list of all the classes in a package.

Solution

You can’t, in the general case. There are some limited approaches, most involving “classpath scanning.”

Discussion

There is no way to find out all the classes in a package, in part because, as we just saw in Recipe 17.5, you can add classes to a package at any time! And, for better or for worse, the JVM and standard classes such as java.lang.Package do not even allow you to enumerate the classes currently in a given package.

The nearest you can come is to look through the classpath. And this will surely work only for local directories and JAR files; if you have locally defined or network-loaded classes, this is not going to help. In other words, it will find compiled classes, but not dynamically loaded ones. There are several libraries that can automate this for you, and you’re welcome to use them. The code to scan the classpath is fairly simple at heart, though, so classy developers with heart will want to examine it. Example 17-13 shows my ClassesInPackage class with its one static method. The code works but is rather short on error handling, and will crash on non-existent packages and other failures.

The code goes through a few gyrations to get the classpath as an enumeration of URLs, then looks at each element. “file:” URLs will contain the pathname of the file containing the .class file, so we can just list it. “jar:” URLs contain the filename as “file:/path_to_jar_file!package/name,” so we have to pull this apart; the “package name” suffix is slightly redundant in this case because it’s the package we asked the ClassLoader to give us.

Example 17-13. main/src/main/java/reflection/ClassesInPackage.java
public class ClassesInPackage {

    /** This approach began as a contribution by Paul Kuit at
     * http://stackoverflow.com/questions/1456930/, but his only
     * handled single files in a directory in classpath, not in Jar files.
     * N.B. Does NOT handle system classes!
     * @param packageName
     * @return
     * @throws IOException
     */
    public static String[] getPackageContent(String packageName)
        throws IOException {

        final String packageAsDirName = packageName.replace(".", "/");
        final List<String> list = new ArrayList<>();
        final Enumeration<URL> urls =
                Thread.currentThread().
                getContextClassLoader().
                getResources(packageAsDirName);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            // System.out.println("URL = " + url);
            String file = url.getFile();
            switch (url.getProtocol()) {
            case "file":
                // This is the easy case: "file" is
                // the full path to the classpath directory
                File dir = new File(file);
                for (File f : dir.listFiles()) {
                    list.add(packageAsDirName + "/" + f.getName());
                }
                break;
            case "jar":
                // This is the harder case; "file" is of the form
                // "jar:/home/ian/bleah/darwinsys.jar!com/darwinsys/io"
                // for some jar file that contains at least one class from
                // the given package.
                int colon = file.indexOf(':');
                int bang = file.indexOf('!');
                String jarFileName = file.substring(colon + 1, bang);
                JarFile jarFile = new JarFile(jarFileName);
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry e = entries.nextElement();
                    String jarEntryName = e.getName();
                    if (!jarEntryName.endsWith("/") &&
                        jarEntryName.startsWith(packageAsDirName)) {
                        list.add(jarEntryName);
                    }
                }
                break;
            default:
                throw new IllegalStateException(
                "Dunno what to do with URL " + url);
            }
        }
        return list.toArray(new String[] {});
    }

    public static void main(String[] args) throws IOException {
        String[] names = getPackageContent("com.darwinsys.io");
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("Done");
    }
}

Note that if you run this application in the “javasrc” project, it will list the members of the demonstration package (com.darwinsys.io) twice, because it will find them both in the build directory and in the JAR file. If this is an issue, change the List to a Set (see Recipe 7.3).

17.10 Using and Defining Annotations

Problem

You need to know how to use annotations in code or to define your own annotations.

Solution

Apply annotations in your code using @AnnotationName before a class, method, field, etc. Define annotations with @interface at the same level as class, interface, etc.

Discussion

Annotations are a way of adding additional information beyond what the source code conveys. Annotations may be directed at the compiler or at runtime examination. Their syntax was somewhat patterned after javadoc annotations (such as @author, @version inside “doc comments”). Annotations are what I call class-like things (so they have initial-cap names), but are prefixed by @ sign where used (e.g., @Override). You can place them on classes, methods, fields, and a few other places; they must appear immediately before what they annotate (ignoring space and comments). A given annotation may only appear once in a given position (this is relaxed in Java 8 or 9).

As an example of the benefits of a compile-time annotation, consider the common error made when overriding: as shown in Example 17-14, a small error in the method signature can result it an overload when an override was intended.

Example 17-14. MyClass.java: Why annotations?
public class MyClass {

    public boolean equals(MyClass object2) {
        // compare, return boolean
    }
}

The code will compile just fine on any release of Java, but it is incorrect. The standard contract of the equals() method (see Recipe 8.1) requires a method whose solitary argument is of type java.lang.Object. The preceding version creates an accidental overload. Because the main use of equals() (and its buddy method hashCode(), see Recipe 8.1) is in the Collections classes (see Chapter 7), this overloaded method will never get called, resulting both in dead code and in incorrect operation of your class within +Set+s and +Map+s.

The solution is very simple: using the annotation java.lang.Override, as in Example 17-15, informs the compiler that the annotated method is required to be overriding a method inherited from a supertype (such as a superclass or an interface). If not, the code will not compile.

Example 17-15. MyClass.java: @Override Annotation
public class MyClass {

    @Override
    public boolean equals(MyClass object2) {
        // compare, return boolean
    }
}

This version of equals(), while still incorrect, will be flagged as erroneous at compile time, potentially avoiding a lot of debugging time. This annotation, on your own classes, will help both at the time you write new code and as you maintain your codebase; if a method is removed from a superclass, all the subclasses that still attempt to override it and have the @Override annotation, will cause an error message, allowing you to remove a bunch of dead code.

The second major use of annotations is to provide metatdata at runtime. For example, the Java Persistence API (JPA, see https://darwinsys.com/db_in_java) uses its own set of annotations from the package javax.persistence to “mark up” entity classes to be loaded and/or persisted. A JPA Entity Class might look like Example 17-16:

Example 17-16. main/src/main/java/domain/Person.java - JPA annotations
@Entity
public class Person {

    int id;
    protected String firstName;
    protected String lastName;

    public Person() {
        // required by JPA; must code it since we need 2-arg form.
    }

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Id @GeneratedValue(strategy=GenerationType.AUTO, generator="my_poid_gen")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @Column(name="surname")
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return getFullName();
    }

    @Transient /* synthetic: cannot be used in JPA queries. */
    public String getFullName() {
        StringBuilder sb = new StringBuilder();
        if (firstName != null)
            sb.append(firstName).append(' ');
        if (lastName != null)
            sb.append(lastName);
        if (sb.length() == 0)
            sb.append("NO NAME");
        return sb.toString();
    }
}

The @Entity annotation at class level directs JPA to treat this as a data object to be mapped into the database. The @Id informs JPA that this id is the primary key property, and the @GeneratedValue tells it how to assign the primary key values for newly created objects. The @Column annotation is only needed when the column name in the relational database differs from the expected name based on the property; in this case, the SQL database designer has used surname, whereas the Java developer wants to use lastName.

I said that annotations are class-like things, and so, you can define your own. The syntax here is a bit funky; you use @interface. It is rumored that the team developing this feature was either told not to, or was afraid to, introduce a new keyword into the language, due to the trouble that doing so had caused when the enum keyword was introduced in Java SE 1.4. Or, maybe they just wanted to use a syntax that was more reminiscent of the annotation’s usage. At any rate, Example 17-17 is a trivial example of a custom annotation.

Example 17-17. Trivial annotation defined
package lang;

public @interface MyToyAnnotation {
}

Annotations are “class-like things” so they should be named the same way—that is, names that begin with a capital letter and, if public, stored in a source file of the same name (e.g, MyToyAnnotation.java).

Compile the Example 17-17 with javac and you’ll see there’s a new MyToyAnnotation.class file. In Example 17-18, we examine this with javap, the standard JDK class inspection tool.

Example 17-18. Running javap on trivial annotation
$ javap lang.MyToyAnnotation
Compiled from "MyToyAnnotation.java"
public interface lang.MyToyAnnotation extends java.lang.annotation.Annotation {
}
$

As it says, an Annotation is represented in the class file format as just an interface that extends Annotation (to answer the obvious question, you could write simple interfaces this way, but it would be a truly terrible idea). Let’s have a quick look at Annotation itself:

Example 17-19.
$ javap java.lang.annotation.Annotation
Compiled from "Annotation.java"
public interface java.lang.annotation.Annotation {
  public abstract boolean equals(java.lang.Object);
  public abstract int hashCode();
  public abstract java.lang.String toString();
  public abstract java.lang.Class<? extends java.lang.annotation.Annotation>
    annotationType();
}
$

Annotations can be made such that the compiler will only allow them in certain points in your code. Here is one that can only go on classes or interfaces:

Example 17-20.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

The @Target specifies where the annotation can be used: ElementType.TYPE makes it usable on classes, interfaces, class-like things such as enums, even annotations! To restrict it to use just on annotations, there is ElementType.ANNOTATION_TYPE. Other types include METHOD, FIELD, CONSTRUCTOR, LOCAL_VARIABLE, PACKAGE, and PARAMETER. So, this annotation is itself annotated with two @ANNOTATION_TYPE-targeted annotations.

Usage of annotations with an existing framework requires consulting their documentation. Using annotations for your own purpose at runtime requires use of the Reflection API, as shown in Example 17-21.

One more thing to note about annotations is that they may have attributes. These are defined as methods in the annotation source code, but used as attributes where the annotation is used. Example 17-21 is an annotated annotation with one such attribute:

Example 17-21. main/src/main/java/lang/AnnotationDemo.java
/**
 * A sample annotation for types (classes, interfaces);
 * it will be available at run time.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationDemo {
    public boolean fancy() default false;
    public int order() default 42;
}

/** A simple example of using the annotation */
@AnnotationDemo(fancy=true)
@Resource(name="Dumbledore")
class FancyClassJustToShowAnnotation {

    /** Print out the annotations attached to this class */
    public static void main(String[] args) {
        Class<?> c = FancyClassJustToShowAnnotation.class;
        System.out.println("Class " + c.getName() + " has these annotations:");
        for (Annotation a : c.getAnnotations()) {
            if (a instanceof AnnotationDemo) {
                AnnotationDemo ad = (AnnotationDemo)a;
                System.out.println("	" +a +
                    " with fancy=" + ad.fancy() +
                    " and order " + ad.order());
            } else {
                System.out.println("	Somebody else's annotation: " + a);
            }
        }
    }
}

AnnotationDemo has the meta-annotation @Target(ElementType.TYPE) to indicate that it can annotate user-defined types (such as classes). Other ElementType choices include METHOD, FIELD, PARAMETER and a few more. If more than one is needed, use array initializer syntax.

AnnotationDemo also has the @Retention(RetentionPolicy.RUNTIME) annotation to request that it be preserved until runtime. This is obviously required for any annotation that will be examined by a framework at runtime.

These two meta-annotations are common on user-defined annotations that will be examined at runtime.

The class FancyClassJustToShowAnnotation shows using the AnnotationDemo annotation, along with a standard Java one (the @Resource annotation).

Refer to Recipe 17.11 for a full example of using this mechanism.

17.11 Finding Plug-in-like Classes via Annotations

Problem

You want to do plug-in-like things without using an explicit plug-in API.

Solution

Define an annotation for the purpose, and use it to mark the plug-in classes.

Discussion

Suppose we want to model how the Java EE standard javax.annotations.Named or javax.faces.ManagedBean annotations work; for each class that is so annotated, convert the class name to an instance-like name (e.g, lowercase the first letter), and do something special with it. You’d want to do something like the following:

  1. Get the list of classes in the given package(s) (see Recipe 17.9).

  2. Check if the class is annotated.

  3. If so, save the name and Class descriptor for later use.

This is implemented in Example 17-22.

Example 17-22. main/src/main/java/reflection/PluginsViaAnnotations::findAnnotatedClasses
/** Discover "plugins" or other add-in classes via Reflection using Annotations */
public class PluginsViaAnnotations {

    /**
     * Find all classes in the given package which have the given
     * class-level annotation class.
     */
    public static List<Class<?>> findAnnotatedClasses(String packageName,
        Class<? extends Annotation> annotationClass) throws Exception {

        List<Class<?>> ret = new ArrayList<>();
        String[] clazzNames = ClassesInPackage.getPackageContent(packageName);
        for (String clazzName : clazzNames) {
            if (!clazzName.endsWith(".class")) {
                continue;
            }
            clazzName = clazzName.replace('/', '.').replace(".class", "");
            Class<?> c = null;
            try {
                c = Class.forName(clazzName);
            } catch (ClassNotFoundException ex) {
                System.err.println("Weird: class " + clazzName +
                    " reported in package but gave CNFE: " + ex);
                continue;
            }
            if (c.isAnnotationPresent(annotationClass) &&
                    !ret.contains(c))
                    ret.add(c);

        }
        return ret;
    }

We can take this one step further, and support particular method annotations, similar to javax.annotations.PostCreate, which is meant to decorate a method that is to be called after an instance of the bean has been instantiated by the framework. Our flow is now something like this, and the code is shown in Example 17-23:

  1. Get the list of classes in the given package(s) (again, see Recipe 17.9).

  2. If you are using a class-level annotation, check if the class is annotated.

  3. If this class is still of interest, get a list of its methods.

  4. For each method, see if it contains a given method-specific annotation.

  5. If so, add the class and method to a list of invocable methods.

Example 17-23. main/src/main/java/reflection/PluginsViaAnnotations - Find Annotated Methods
    /**
     * Find all classes in the given package which have the given
     * method-level annotation class on at least one method.
     */
    public static List<Class<?>> findClassesWithAnnotatedMethods(String packageName,
            Class<? extends Annotation> methodAnnotationClass) throws Exception {
        List<Class<?>> ret = new ArrayList<>();
        String[] clazzNames = ClassesInPackage.getPackageContent(packageName);
        for (String clazzName : clazzNames) {
            if (!clazzName.endsWith(".class")) {
                continue;
            }
            clazzName = clazzName.replace('/', '.').replace(".class", "");
            Class<?> c = null;
            try {
                c = Class.forName(clazzName);
                // System.out.println("Loaded " + c);
            } catch (ClassNotFoundException ex) {
                System.err.println("Weird: class " + clazzName +
                    " reported in package but gave CNFE: " + ex);
                continue;
            }
            for (Method m : c.getDeclaredMethods()) {
                // System.out.printf("Class %s Method: %s
",
                //     c.getSimpleName(), m.getName());
                if (m.isAnnotationPresent(methodAnnotationClass) &&
                        !ret.contains(c)) {
                    ret.add(c);
                }
            }
        }
        return ret;
    }

See Also

Recipe 17.10, and the rest of this chapter.

17.12 Program: CrossRef

You’ve probably seen those other Java books that consist entirely of listings of the Java API for version thus-and-such of the JDK. I don’t suppose you thought the authors of these works sat down and typed the entire contents from scratch. As a programmer, you would have realized, I hope, that there must be a way to obtain that information from Java. But you might not have realized how easy it is! If you’ve read this chapter faithfully, you now know that there is one true way: make the computer do the walking. Example 17-24 is a program that puts most of the techniques together. This version generates a cross-reference listing, but by overriding the last few methods, you could easily convert it to print the information in any format you like, including an API Reference book. You’d need to deal with the details of this or that publishing software—FrameMaker, troff, TEX, or whatever—but that’s the easy part.

This program makes fuller use of the Reflection API than did MyJavaP in Recipe 17.8. It also uses the java.util.zip classes (see Recipe 10.15) to crack the JAR archive containing the class files of the API. Each class file found in the archive is loaded and listed; the listing part is similar to MyJavaP.

Example 17-24. main/src/main/java/reflection/CrossRef.java
public class CrossRef extends APIFormatter {

    /** Simple main program, construct self, process each .ZIP file
     * found in CLASSPATH or in argv.
     */
    public static void main(String[] argv) throws IOException {
        CrossRef xref = new CrossRef();
        xref.doArgs(argv);
    }

    /**
     * Print the fields and methods of one class.
     */
    protected void doClass(Class<?> c) {
        startClass(c);
        try {
            Field[] fields = c.getDeclaredFields();
            Arrays.sort(fields, new Comparator<Field>() {
                public int compare(Field o1, Field o2) {
                    return o1.getName().compareTo(o2.getName());
                }
            });
            for (int i = 0; i < fields.length; i++) {
                Field field = (Field)fields[i];
                if (!Modifier.isPrivate(field.getModifiers()))
                    putField(field, c);
                // else System.err.println("private field ignored: " + field);
            }

            Method methods[] = c.getDeclaredMethods();
            Arrays.sort(methods, new Comparator<Method>() {
                public int compare(Method o1, Method o2) {
                    return o1.getName().compareTo(o2.getName());
                }
            });
            for (int i = 0; i < methods.length; i++) {
                if (!Modifier.isPrivate(methods[i].getModifiers()))
                    putMethod(methods[i], c);
                // else System.err.println("pvt: " + methods[i]);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        endClass();
    }

    /** put a Field's information to the standard output.  */
    protected void putField(Field fld, Class<?> c) {
        println(fld.getName() + " field " + c.getName() + " ");
    }

    /** put a Method's information to the standard output.  */
    protected void putMethod(Method method, Class<?> c) {
        String methName = method.getName();
        println(methName + " method " + c.getName() + " ");
    }

    /** Print the start of a class. Unused in this version,
     * designed to be overridden */
    protected void startClass(Class<?> c) {
    }

    /** Print the end of a class. Unused in this version,
     * designed to be overridden */
    protected void endClass() {
    }

    /** Convenience routine, short for System.out.println */
    protected final void println(String s) {
        System.out.println(s);
    }
}

You probably noticed the methods startClass() and endClass(), which are null. These methods are placeholders designed to make subclassing easy for when you need to write something at the start and end of each class. One example might be a fancy text formatting application in which you need to output a bold header at the beginning of each class. Another would be XML, where you’d want to write a tag like <class> at the front of each class, and </class> at the end. Example 17-25 is an XML-specific subclass that generates (limited) XML for each field and method.

Example 17-25. main/src/main/java/reflection/CrossRefXML.java
public class CrossRefXML extends CrossRef {

    public static void main(String[] argv) throws IOException {
        CrossRef xref = new CrossRefXML();
        xref.doArgs(argv);
    }

    /** Print the start of a class.
     */
    protected void startClass(Class<?> c) {
        println("<class><classname>" + c.getName() + "</classname>");
    }

    protected void putField(Field fld, Class<?> c) {
        println("<field>" + fld + "</field>");
    }

    /** put a Method's information to the standard output.
     * Marked protected so you can override it (hint, hint).
     */
    protected void putMethod(Method method, Class<?> c) {
        println("<method>" + method + "</method>");
    }

    /** Print the end of a class.
     */
    protected void endClass() {
        println("</class>");
    }
}

By the way, if you publish a book using either of these and get rich, “Remember, remember me!”

See Also

We have not investigated all the ins and outs of reflection or the ClassLoader mechanism, but by now you should have a basic idea of how it works.

Perhaps the most important omissions are SecurityManager and ProtectionDomain. Only one SecurityManager can be installed in a given instance of the JVM (e.g., to prevent malicious code from providing its own!). A browser running the old Java Applet API, for example, provides a SecurityManager that is far more restrictive than the standard one. Writing such a SecurityManager is left as an exercise for the reader—an important exercise for anyone planning to load classes over the Internet! (For more information about security managers and the Java Security APIs, see Java Security by Scott Oaks (O’Reilly). A ProtectionDomain can be provided with a ClassLoader to specify all the permissions needed for the class to run.

I’ve also left unexplored many topics in the JVM; see the (somewhat dated) O’Reilly books Java Virtual Machine and Java Language. You can also read the Sun/Oracle Java Language Specification and JVM Specification documents (both updated with new releases, available online), for a lifetime of reading enjoyment and edification!

The Apache Software Foundation maintains a vast array of useful software packages that are free to get and use. Source code is always available without charge from its website. Two packages you might want to investigate include the Commons BeanUtils and the Byte Code Engineering Library (BCEL). The Commons BeanUtils claims to provide easier-to-use wrappers around some of the Reflection API. BCEL is a third-party toolkit for building and manipulating “bytecode” class files. Written by Markus Dahm, BCEL has become part of the Apache Commons Project.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.182.66