Targeting Specific Java Versions (and using javap)
I recently worked on a project where it became necessary to determine the version of the compiler that individual class files were compiled with. More specifically, I needed to determine the target version of Java the code was compiled to run on. You need to be specific about that since even the Java SE 6 compiler can be made to compile classes specifically for other versions of Java.</p>
I recently worked on a project where it became necessary to determine the version of the compiler that individual class files were compiled with. More specifically, I needed to determine the target version of Java the code was compiled to run on. You need to be specific about that since even the Java SE 6 compiler can be made to compile classes specifically for other versions of Java.
For instance, you can execute the following command to target the 1.4.2 compiler:
javac -target 1.4 -source 1.4 AccountClient.java
You can target Java SE 5 the same way by substituting 1.5 for 1.4 in the previous command. However, given that the code may have been compiled by someone else, how can you determine the target version? You can use javap, which comes with the JDK.
javap - The Java Disassembler
The javap tool contains a bunch of useful commands that will tell you all about a .class file. For instance, you can see the .class file's source filename, its full package name, its public and protected methods and variables, all of the actual bytecode (that the VM uses to compile and execute the class), and other information such as the target Java version. The following is a list of command-line parameters for javap:
-help (Prints out help message for javap)
-l ( Prints out line and local variable tables)
-b (Ensures backward compatibility with javap in JDK 1.1)
-public (Shows only public classes and members)
-protected (Shows only protected and public classes and members)
-package (Shows only package, protected, and public classes and members. This is the default)
-private (Shows all classes and members)
-Jflag (Pass flag directly to the runtime system. Some examples:
javap -J-version
javap -J-Djava.security.manager -J-Djava.security.policy=MyPolicy MyClassName)
-s (Prints internal type signatures)
-c (Prints out disassembled code, i.e., the instructions that comprise the
Java bytecodes, for each of the methods in the class. These are documented in the Java Virtual Machine Specification.)
-verbose (Prints stack size, number of locals and args for methods)
-classpath (Specifies the path javap uses to look up classes)
-bootclasspath (Specifies path from which to load bootstrap classes)
-extdirs (Overrides location at which installed extensions are searched for)
To determine the version of Java a class was compiled for, use the -verbose option. Running javap -verbose on the AccountClient.class file I'm using as a sample, I get the following output:
Compiled from "AccountClient.java"
public class AccountClient extends java.lang.Object
SourceFile: "AccountClient.java"
minor version: 0
major version: 48
Constant pool:
const #1 = Method #32.#41; // java/lang/Object."":()V
const #2 = class #42; // java/rmi/RMISecurityManager
const #3 = Method #2.#41; // java/rmi/RMISecurityManager."":()V
const #4 = Method #43.#44; // java/lang/System.setSecurityManager:(Ljava/lang/SecurityManager;)V
const #5 = String #45; // rmi://localhost/Account
const #6 = Method #46.#47; // java/rmi/Naming.lookup:(Ljava/lang/String;)Ljava/rmi/Remote;
const #7 = class #48; // Account
const #8 = float 12000.0f;
const #9 = InterfaceMethod #7.#49; // Account.deposit:(F)V
const #10 = Field #43.#50; // java/lang/System.out:Ljava/io/PrintStream;
const #11 = class #51; // java/lang/StringBuffer
const #12 = Method #11.#41; // java/lang/StringBuffer."":()V
const #13 = String #52; // Deposited 12,000 into account owned by
const #14 = Method #11.#53; // java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
const #15 = InterfaceMethod #7.#54; // Account.getName:()Ljava/lang/String;
const #16 = Method #11.#55; // java/lang/StringBuffer.toString:()Ljava/lang/String;
const #17 = Method #56.#57; // java/io/PrintStream.println:(Ljava/lang/String;)V
const #18 = String #58; // Balance now totals:
const #19 = InterfaceMethod #7.#59; // Account.getBalance:()F
const #20 = Method #11.#60; // java/lang/StringBuffer.append:(F)Ljava/lang/StringBuffer;
const #21 = class #61; // java/rmi/RemoteException
const #22 = Field #43.#62; // java/lang/System.err:Ljava/io/PrintStream;
const #23 = String #63; // Remote exception while looking up account:
const #24 = Method #21.#64; // java/rmi/RemoteException.getMessage:()Ljava/lang/String;
const #25 = class #65; // java/rmi/NotBoundException
const #26 = String #66; // Server object not found at expected URL:
const #27 = Method #25.#64; // java/rmi/NotBoundException.getMessage:()Ljava/lang/String;
const #28 = class #67; // java/net/MalformedURLException
const #29 = String #68; // Bad RMI URL given for server object:
const #30 = Method #28.#64; // java/net/MalformedURLException.getMessage:()Ljava/lang/String;
const #31 = class #69; // AccountClient
const #32 = class #70; // java/lang/Object
const #33 = Asciz ;
const #34 = Asciz ()V;
const #35 = Asciz Code;
const #36 = Asciz LineNumberTable;
const #37 = Asciz main;
const #38 = Asciz ([Ljava/lang/String;)V;
const #39 = Asciz SourceFile;
const #40 = Asciz AccountClient.java;
const #41 = NameAndType #33:#34;// "":()V
const #42 = Asciz java/rmi/RMISecurityManager;
const #43 = class #71; // java/lang/System
const #44 = NameAndType #72:#73;// setSecurityManager:(Ljava/lang/SecurityManager;)V
const #45 = Asciz rmi://localhost/Account;
const #46 = class #74; // java/rmi/Naming
const #47 = NameAndType #75:#76;// lookup:(Ljava/lang/String;)Ljava/rmi/Remote;
const #48 = Asciz Account;
const #49 = NameAndType #77:#78;// deposit:(F)V
const #50 = NameAndType #79:#80;// out:Ljava/io/PrintStream;
const #51 = Asciz java/lang/StringBuffer;
const #52 = Asciz Deposited 12,000 into account owned by ;
const #53 = NameAndType #81:#82;// append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
const #54 = NameAndType #83:#84;// getName:()Ljava/lang/String;
const #55 = NameAndType #85:#84;// toString:()Ljava/lang/String;
const #56 = class #86; // java/io/PrintStream
const #57 = NameAndType #87:#88;// println:(Ljava/lang/String;)V
const #58 = Asciz Balance now totals: ;
const #59 = NameAndType #89:#90;// getBalance:()F
const #60 = NameAndType #81:#91;// append:(F)Ljava/lang/StringBuffer;
const #61 = Asciz java/rmi/RemoteException;
const #62 = NameAndType #92:#80;// err:Ljava/io/PrintStream;
const #63 = Asciz Remote exception while looking up account: ;
const #64 = NameAndType #93:#84;// getMessage:()Ljava/lang/String;
const #65 = Asciz java/rmi/NotBoundException;
const #66 = Asciz Server object not found at expected URL: ;
const #67 = Asciz java/net/MalformedURLException;
const #68 = Asciz Bad RMI URL given for server object: ;
const #69 = Asciz AccountClient;
const #70 = Asciz java/lang/Object;
const #71 = Asciz java/lang/System;
const #72 = Asciz setSecurityManager;
const #73 = Asciz (Ljava/lang/SecurityManager;)V;
const #74 = Asciz java/rmi/Naming;
const #75 = Asciz lookup;
const #76 = Asciz (Ljava/lang/String;)Ljava/rmi/Remote;;
const #77 = Asciz deposit;
const #78 = Asciz (F)V;
const #79 = Asciz out;
const #80 = Asciz Ljava/io/PrintStream;;
const #81 = Asciz append;
const #82 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuffer;;
const #83 = Asciz getName;
const #84 = Asciz ()Ljava/lang/String;;
const #85 = Asciz toString;
const #86 = Asciz java/io/PrintStream;
const #87 = Asciz println;
const #88 = Asciz (Ljava/lang/String;)V;
const #89 = Asciz getBalance;
const #90 = Asciz ()F;
const #91 = Asciz (F)Ljava/lang/StringBuffer;;
const #92 = Asciz err;
const #93 = Asciz getMessage;
{
public AccountClient();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: return
LineNumberTable:
line 11: 0
public static void main(java.lang.String[]);
Code:
Stack=3, Locals=2, Args_size=1
0: new #2; //class java/rmi/RMISecurityManager
3: dup
4: invokespecial #3; //Method java/rmi/RMISecurityManager."":()V
7: invokestatic #4; //Method java/lang/System.setSecurityManager:(Ljava/lang/SecurityManager;)V
10: ldc #5; //String rmi://localhost/Account
12: invokestatic #6; //Method java/rmi/Naming.lookup:(Ljava/lang/String;)Ljava/rmi/Remote;
15: checkcast #7; //class Account
18: astore_1
19: aload_1
20: ldc #8; //float 12000.0f
22: invokeinterface #9, 2; //InterfaceMethod Account.deposit:(F)V
27: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream;
30: new #11; //class java/lang/StringBuffer
33: dup
34: invokespecial #12; //Method java/lang/StringBuffer."":()V
37: ldc #13; //String Deposited 12,000 into account owned by
39: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
42: aload_1
43: invokeinterface #15, 1; //InterfaceMethod Account.getName:()Ljava/lang/String;
48: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
51: invokevirtual #16; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
54: invokevirtual #17; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
57: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream;
60: new #11; //class java/lang/StringBuffer
63: dup
64: invokespecial #12; //Method java/lang/StringBuffer."":()V
67: ldc #18; //String Balance now totals:
69: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
72: aload_1
73: invokeinterface #19, 1; //InterfaceMethod Account.getBalance:()F
78: invokevirtual #20; //Method java/lang/StringBuffer.append:(F)Ljava/lang/StringBuffer;
81: invokevirtual #16; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
84: invokevirtual #17; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
87: goto 183
90: astore_1
91: getstatic #22; //Field java/lang/System.err:Ljava/io/PrintStream;
94: new #11; //class java/lang/StringBuffer
97: dup
98: invokespecial #12; //Method java/lang/StringBuffer."":()V
101: ldc #23; //String Remote exception while looking up account:
103: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
106: aload_1
107: invokevirtual #24; //Method java/rmi/RemoteException.getMessage:()Ljava/lang/String;
110: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
113: invokevirtual #16; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
116: invokevirtual #17; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
119: goto 183
122: astore_1
123: getstatic #22; //Field java/lang/System.err:Ljava/io/PrintStream;
126: new #11; //class java/lang/StringBuffer
129: dup
130: invokespecial #12; //Method java/lang/StringBuffer."":()V
133: ldc #26; //String Server object not found at expected URL:
135: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
138: aload_1
139: invokevirtual #27; //Method java/rmi/NotBoundException.getMessage:()Ljava/lang/String;
142: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
145: invokevirtual #16; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
148: invokevirtual #17; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
151: goto 183
154: astore_1
155: getstatic #22; //Field java/lang/System.err:Ljava/io/PrintStream;
158: new #11; //class java/lang/StringBuffer
161: dup
162: invokespecial #12; //Method java/lang/StringBuffer."":()V
165: ldc #29; //String Bad RMI URL given for server object:
167: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
170: aload_1
171: invokevirtual #30; //Method java/net/MalformedURLException.getMessage:()Ljava/lang/String;
174: invokevirtual #14; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
177: invokevirtual #16; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
180: invokevirtual #17; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
183: return
Exception table:
from to target type
0 87 90 Class java/rmi/RemoteException
0 87 122 Class java/rmi/NotBoundException
0 87 154 Class java/net/MalformedURLException
LineNumberTable:
line 15: 0
line 18: 10
line 21: 19
line 24: 27
line 26: 57
line 39: 87
line 28: 90
line 29: 91
line 39: 119
line 32: 122
line 33: 123
line 39: 151
line 36: 154
line 37: 155
line 40: 183
}
There's lots of output, since this includes stack snapshots, line number tables, and some of the bytecode for this simple class. To see all of the bytecode for this class, execute the same command with the -c option. The version number we're interested in, however, is at the top listed as:
"major version: 48"
What does 48 Mean?
No this isn't the answer to life, the universe ,and everything (that answer was 42, remember?). This is the Java version number, where this number maps to javap's output for major version as such:
major version: 48 = Java SE 1.4.2
major version: 49 = Java SE 5
major version: 50 = Java SE 6
You can get more information on javap in the Java documentation here: http://java.sun.com/javase/6/docs/technotes/tools/windows/javap.html
Happy Coding!
-EJB
About the Author
You May Also Like