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>

InformationWeek Staff, Contributor

June 25, 2008

13 Min Read

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 

Never Miss a Beat: Get a snapshot of the issues affecting the IT industry straight to your inbox.

You May Also Like


More Insights