<p>Do any of you know of a tool that will search for .class files and then display their compiled versions?</p>

<p>I know you can look at them individually in a hex editor but I have a lot of class files to look over (something in my giant application is compiling to Java6 for some reason).</p>

[{'answer_id': 27123, 'author': 'McDowell', 'author_id': 304, 'author_profile': 'https://Stackoverflow.com/users/304', 'pm_score': 6, 'selected': False, 'text': '<p>It is easy enough to read the <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1" rel="noreferrer">class file signature</a> and get these values without a 3rd party API. All you need to do is read the first 8 bytes.</p>\n\n<pre><code>ClassFile {\n    u4 magic;\n    u2 minor_version;\n    u2 major_version;\n</code></pre>\n\n<p>For class file version 51.0 (Java 7), the opening bytes are:</p>\n\n<pre><code>CA FE BA BE 00 00 00 33\n</code></pre>\n\n<p>...where 0xCAFEBABE are the magic bytes, 0x0000 is the minor version and 0x0033 is the major version.</p>\n\n<pre><code>import java.io.*;\n\npublic class Demo {\n  public static void main(String[] args) throws IOException {\n    ClassLoader loader = Demo.class.getClassLoader();\n    try (InputStream in = loader.getResourceAsStream("Demo.class");\n        DataInputStream data = new DataInputStream(in)) {\n      if (0xCAFEBABE != data.readInt()) {\n        throw new IOException("invalid header");\n      }\n      int minor = data.readUnsignedShort();\n      int major = data.readUnsignedShort();\n      System.out.println(major + "." + minor);\n    }\n  }\n}\n</code></pre>\n\n<p>Walking directories (<a href="http://docs.oracle.com/javase/7/docs/api/java/io/File.html" rel="noreferrer">File</a>) and archives (<a href="http://docs.oracle.com/javase/7/docs/api/java/util/jar/JarFile.html" rel="noreferrer">JarFile</a>) looking for class files is trivial.</p>\n\n<p>Oracle\'s <a href="https://blogs.oracle.com/darcy/" rel="noreferrer">Joe Darcy\'s blog</a> lists the <a href="https://blogs.oracle.com/darcy/entry/source_target_class_file_version" rel="noreferrer">class version to JDK version mappings</a> up to Java 7:</p>\n\n<pre><code>Target   Major.minor Hex\n1.1      45.3        0x2D\n1.2      46.0        0x2E\n1.3      47.0        0x2F\n1.4      48.0        0x30\n5 (1.5)  49.0        0x31\n6 (1.6)  50.0        0x32\n7 (1.7)  51.0        0x33\n8 (1.8)  52.0        0x34\n9        53.0        0x35\n</code></pre>\n'}, {'answer_id': 27505, 'author': 'staffan', 'author_id': 988, 'author_profile': 'https://Stackoverflow.com/users/988', 'pm_score': 8, 'selected': True, 'text': '<p>Use the <a href="http://java.sun.com/javase/6/docs/technotes/tools/solaris/javap.html" rel="noreferrer">javap</a> tool that comes with the JDK. The <code>-verbose</code> option will print the version number of the class file.</p>\n\n<pre><code>&gt; javap -verbose MyClass\nCompiled from "MyClass.java"\npublic class MyClass\n  SourceFile: "MyClass.java"\n  minor version: 0\n  major version: 46\n...\n</code></pre>\n\n<p>To only show the version:</p>\n\n<pre><code>WINDOWS&gt; javap -verbose MyClass | find "version"\nLINUX  &gt; javap -verbose MyClass | grep version\n</code></pre>\n'}, {'answer_id': 27904, 'author': 'WMR', 'author_id': 2844, 'author_profile': 'https://Stackoverflow.com/users/2844', 'pm_score': 3, 'selected': False, 'text': '<p>If you are on a unix system you could just do a </p>\n\n<pre><code>find /target-folder -name \\*.class | xargs file | grep "version 50\\.0"\n</code></pre>\n\n<p>(my version of file says "compiled Java class data, version 50.0" for java6 classes).</p>\n'}, {'answer_id': 6609249, 'author': 'phunehehe', 'author_id': 168034, 'author_profile': 'https://Stackoverflow.com/users/168034', 'pm_score': 5, 'selected': False, 'text': '<p>On Unix-like</p>\n\n<pre>file /path/to/Thing.class</pre>\n\n<p>Will give the file type and version as well. Here is what the output looks like:</p>\n\n<blockquote>\n  <p>compiled Java class data, version 49.0</p>\n</blockquote>\n'}, {'answer_id': 25201428, 'author': 'igo', 'author_id': 1795220, 'author_profile': 'https://Stackoverflow.com/users/1795220', 'pm_score': 3, 'selected': False, 'text': '<p>Yet another java version check</p>\n\n<pre><code>od -t d -j 7 -N 1 ApplicationContextProvider.class | head -1 | awk \'{print "Java", $2 - 44}\'\n</code></pre>\n'}, {'answer_id': 35080304, 'author': 'PbxMan', 'author_id': 1652451, 'author_profile': 'https://Stackoverflow.com/users/1652451', 'pm_score': 3, 'selected': False, 'text': '<p>In eclipse if you don\'t have sources attached. Mind the first line after the attach source button.</p>\n\n<p>// Compiled from CDestinoLog.java (<strong>version 1.5 : 49.0, super bit</strong>)</p>\n\n<p><a href="https://i.stack.imgur.com/Mku2A.png" rel="noreferrer"><img src="https://i.stack.imgur.com/Mku2A.png" alt="enter image description here"></a></p>\n'}, {'answer_id': 37256401, 'author': 'Radoslav Kastiel', 'author_id': 6341080, 'author_profile': 'https://Stackoverflow.com/users/6341080', 'pm_score': 2, 'selected': False, 'text': '<p>Maybe this helps somebody, too. Looks there is more easy way to get JAVA version used to compile/build .class. This way is useful to application/class self check on JAVA version.</p>\n\n<p>I have gone through JDK library and found this useful constant:\n<em>com.sun.deploy.config.BuiltInProperties.CURRENT_VERSION</em>.\nI do not know since when it is in JAVA JDK.</p>\n\n<p>Trying this piece of code for several version constants I get result below:</p>\n\n<p><strong>src:</strong></p>\n\n<pre><code>System.out.println("JAVA DEV       ver.: " + com.sun.deploy.config.BuiltInProperties.CURRENT_VERSION);\nSystem.out.println("JAVA RUN     v. X.Y: " + System.getProperty("java.specification.version") );\nSystem.out.println("JAVA RUN v. W.X.Y.Z: " + com.sun.deploy.config.Config.getJavaVersion() ); //_javaVersionProperty\nSystem.out.println("JAVA RUN  full ver.: " + System.getProperty("java.runtime.version")  + " (may return unknown)" );\nSystem.out.println("JAVA RUN       type: " + com.sun.deploy.config.Config.getJavaRuntimeNameProperty() );\n</code></pre>\n\n<p><strong>output:</strong></p>\n\n<pre><code>JAVA DEV       ver.: 1.8.0_77\nJAVA RUN     v. X.Y: 1.8\nJAVA RUN v. W.X.Y.Z: 1.8.0_91\nJAVA RUN  full ver.: 1.8.0_91-b14 (may return unknown)\nJAVA RUN       type: Java(TM) SE Runtime Environment\n</code></pre>\n\n<p>In class bytecode there is really stored constant - see red marked part of Main.call - <a href="https://i.stack.imgur.com/u9ocW.png" rel="nofollow noreferrer">constant stored in .class bytecode</a></p>\n\n<p>Constant is in class used for checking if JAVA version is out of date (see <a href="https://stackoverflow.com/questions/20524800/how-java-checks-that-is-out-of-date">How Java checks that is out of date</a>)...</p>\n'}, {'answer_id': 58642950, 'author': 'Marinos An', 'author_id': 1555615, 'author_profile': 'https://Stackoverflow.com/users/1555615', 'pm_score': 2, 'selected': False, 'text': '<p>A java-based solution using version <a href="https://en.wikipedia.org/wiki/Java_class_file#General_layout" rel="nofollow noreferrer">magic numbers</a>. Below it is used by the program itself to detect its bytecode version.</p>\n\n<pre><code>import java.io.IOException;\nimport java.io.InputStream;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.apache.commons.codec.DecoderException;\nimport org.apache.commons.codec.binary.Hex;\nimport org.apache.commons.io.IOUtils;\npublic class Main {\n    public static void main(String[] args) throws DecoderException, IOException {\n        Class clazz = Main.class;\n        Map&lt;String,String&gt; versionMapping = new HashMap();\n        versionMapping.put("002D","1.1");\n        versionMapping.put("002E","1.2");\n        versionMapping.put("002F","1.3");\n        versionMapping.put("0030","1.4");\n        versionMapping.put("0031","5.0");\n        versionMapping.put("0032","6.0");\n        versionMapping.put("0033","7");\n        versionMapping.put("0034","8");\n        versionMapping.put("0035","9");\n        versionMapping.put("0036","10");\n        versionMapping.put("0037","11");\n        versionMapping.put("0038","12");\n        versionMapping.put("0039","13");\n        versionMapping.put("003A","14");\n\n        InputStream stream = clazz.getClassLoader()\n            .getResourceAsStream(clazz.getName().replace(".", "/") + ".class");\n        byte[] classBytes = IOUtils.toByteArray(stream);\n        String versionInHexString = \n            Hex.encodeHexString(new byte[]{classBytes[6],classBytes[7]});\n        System.out.println("bytecode version: "+versionMapping.get(versionInHexString));\n    }\n}\n</code></pre>\n'}, {'answer_id': 63204108, 'author': 'DuncG', 'author_id': 4712734, 'author_profile': 'https://Stackoverflow.com/users/4712734', 'pm_score': 0, 'selected': False, 'text': '<p>The simplest way is to scan a class file using many of the answers here which read the class file magic bytes.</p>\n<p>However some code is packaged in jars or other archive formats like WAR and EAR, some of which contain other archives or class files, plus you now have multi-release JAR files - see <a href="https://openjdk.java.net/jeps/238" rel="nofollow noreferrer">JEP-238</a> which use different JDK compilers per JAR.</p>\n<p>This program scans classes from a list of files + folders and prints summary of java class file versions for each component including each JAR within WAR/EARs:</p>\n<pre><code>public static void main(String[] args) throws IOException {\n    var files = Arrays.stream(args).map(Path::of).collect(Collectors.toList());\n    ShowClassVersions v = new ShowClassVersions();\n    for (var f : files) {\n        v.scan(f);\n    }\n    v.print();\n}\n</code></pre>\n<p>Example output from a scan:</p>\n<pre><code>Version: 49.0 ~ JDK-5\n   C:\\jars\\junit-platform-console-standalone-1.7.1.jar\nVersion: 50.0 ~ JDK-6\n   C:\\jars\\junit-platform-console-standalone-1.7.1.jar\nVersion: 52.0 ~ JDK-8\n   C:\\java\\apache-tomcat-10.0.12\\lib\\catalina.jar\n   C:\\jars\\junit-platform-console-standalone-1.7.1.jar\nVersion: 53.0 ~ JDK-9\n   C:\\java\\apache-tomcat-10.0.12\\lib\\catalina.jar\n   C:\\jars\\junit-platform-console-standalone-1.7.1.jar\n</code></pre>\n<p>The scanner:</p>\n<pre><code>public class ShowClassVersions {\n    private TreeMap&lt;String, ArrayList&lt;String&gt;&gt; vers = new TreeMap&lt;&gt;();\n    private static final byte[] CLASS_MAGIC = new byte[] { (byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe };\n    private final byte[] bytes = new byte[8];\n\n    private String versionOfClass(InputStream in) throws IOException  {\n        int c = in.readNBytes(bytes, 0, bytes.length);\n        if (c == bytes.length &amp;&amp; Arrays.mismatch(bytes, CLASS_MAGIC) == CLASS_MAGIC.length) {\n            int minorVersion = (bytes[4] &lt;&lt; 8) + (bytes[4] &lt;&lt; 0);\n            int majorVersion = (bytes[6] &lt;&lt; 8) + (bytes[7] &lt;&lt; 0);\n            return &quot;&quot;+ majorVersion + &quot;.&quot; + minorVersion;\n        }\n        return &quot;Unknown&quot;;\n    }\n\n    private Matcher classes = Pattern.compile(&quot;\\\\.(class|ear|war|jar)$&quot;).matcher(&quot;&quot;);\n\n    // This code scans any path (dir or file):\n    public void scan(Path f) throws IOException {\n        try (var stream = Files.find(f, Integer.MAX_VALUE,\n                (p, a) -&gt; a.isRegularFile() &amp;&amp; classes.reset(p.toString()).find())) {\n            stream.forEach(this::scanFile);\n        }\n    }\n\n    private void scanFile(Path f) {\n        String fn = f.getFileName().toString();\n        try {\n            if (fn.endsWith(&quot;.ear&quot;) || fn.endsWith(&quot;.war&quot;) || fn.endsWith(&quot;.jar&quot;))\n                scanArchive(f);\n            else if (fn.endsWith(&quot;.class&quot;))\n                store(f.toAbsolutePath().toString(), versionOfClass(f));\n        } catch (IOException e) {\n            throw new UncheckedIOException(e);\n        }\n    }\n\n    private void scanArchive(Path p) throws IOException {\n        try (InputStream in = Files.newInputStream(p)) {\n            scanArchive(p.toAbsolutePath().toString(), Files.newInputStream(p));\n        }\n    }\n\n    private void scanArchive(String desc, InputStream in) throws IOException {\n        HashSet&lt;String&gt; versions = new HashSet&lt;&gt;();\n        ZipInputStream zip = new ZipInputStream(in);\n        for (ZipEntry entry = null; (entry = zip.getNextEntry()) != null; ) {\n            String name = entry.getName();\n            // There could be different compiler versions per class in one jar\n            if (name.endsWith(&quot;.class&quot;)) {\n                versions.add(versionOfClass(zip));\n            } else if (name.endsWith(&quot;.jar&quot;) || name.endsWith(&quot;.war&quot;)) {\n                scanArchive(desc + &quot; =&gt; &quot; + name, zip);\n            }\n        }\n        if (versions.size() &gt; 1)\n            System.out.println(&quot;Warn: &quot;+desc+&quot; contains multiple versions: &quot;+versions);\n\n        for (String version : versions)\n            store(desc, version);\n    }\n\n    private String versionOfClass(Path p) throws IOException {\n        try (InputStream in = Files.newInputStream(p)) {\n            return versionOfClass(in);\n        }\n    }\n\n    private void store(String path, String jdkVer) {\n        vers.computeIfAbsent(jdkVer, k -&gt; new ArrayList&lt;&gt;()).add(path);\n    }\n\n    // Could add a mapping table for JDK names, this guesses based on (JDK17 = 61.0)\n    public void print() {\n        for (var ver : vers.keySet()) {\n            System.out.println(&quot;Version: &quot; + ver + &quot; ~ &quot; +jdkOf(ver));\n            for (var p : vers.get(ver)) {\n                System.out.println(&quot;   &quot; + p);\n            }\n        }\n    }\n\n    private static String jdkOf(String ver)  {\n        try {\n            return &quot;JDK-&quot;+((int)Float.parseFloat(ver)-44);\n        }\n        catch(NumberFormatException nfe)\n        {\n            return &quot;JDK-??&quot;;\n        }\n    }\n}\n</code></pre>\n'}, {'answer_id': 70299029, 'author': 'BaiJiFeiLong', 'author_id': 5254103, 'author_profile': 'https://Stackoverflow.com/users/5254103', 'pm_score': 2, 'selected': False, 'text': "<h1>Read the 8th byte to decimal:</h1>\n<p>Unix-like: <code>hexdump -s 7 -n 1 -e '&quot;%d&quot;' Main.class</code></p>\n<p>Windows: <code>busybox.exe hexdump -s 7 -n 1 -e '&quot;%d&quot;' Main.class</code></p>\n<h2>Output example:</h2>\n<p><code>55</code></p>\n<h2>Explain:</h2>\n<ul>\n<li><code>-s 7</code> Offset 7</li>\n<li><code>-n 1</code> Limit 1</li>\n<li><code>-e '&quot;%d&quot;'</code> Print as decimal</li>\n</ul>\n<h2>Version map:</h2>\n<pre><code>JDK 1.1 = 45 (0x2D hex)\nJDK 1.2 = 46 (0x2E hex)\nJDK 1.3 = 47 (0x2F hex)\nJDK 1.4 = 48 (0x30 hex)\nJava SE 5.0 = 49 (0x31 hex)\nJava SE 6.0 = 50 (0x32 hex)\nJava SE 7 = 51 (0x33 hex)\nJava SE 8 = 52 (0x34 hex)\nJava SE 9 = 53 (0x35 hex)\nJava SE 10 = 54 (0x36 hex)\nJava SE 11 = 55 (0x37 hex)\nJava SE 12 = 56 (0x38 hex)\nJava SE 13 = 57 (0x39 hex)\nJava SE 14 = 58 (0x3A hex)\nJava SE 15 = 59 (0x3B hex)\nJava SE 16 = 60 (0x3C hex)\nJava SE 17 = 61 (0x3D hex)\n</code></pre>\n"}]
['https://Stackoverflow.com/questions/27065', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/1666/']