如何纠正混乱的 java .class 文件集或从混乱的 .class 集生成正确的 .jar 存档? [英] How to correct a mess java .class file set or generate a proper .jar archive from a mess .class set?

查看:27
本文介绍了如何纠正混乱的 java .class 文件集或从混乱的 .class 集生成正确的 .jar 存档?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我必须接触不同类型的 Java 项目和各种构建系统.有时目录结构与包层次结构不同.所以很难打包.

即使像 Maven 和 Gradle 这样的构建系统有自己的打包 .jar 的功能,但它需要一个合格的互联网连接和一个巨大的本地存储库.因此,我通常在办公室的桌面上构建我需要的库.但是,我在笔记本电脑上花费了更多时间,因为它没有很好的连接和存储空间.

问题

是否有稳定、兼容和安全的方式来制作 .jar 包?或者将那些 .class 文件移动到适当的目录?(官方成熟的开源工具更好)

我是 Java 语言的初学者,所以任何一点都可能对我有帮助.

为了清楚起见,我在这里举了一些例子.

案例 1:如果有一个工具可以在正确的目录中移动(移动或复制).class 文件,它应该表现如下:

操作前:

from_root/├── a.class├── b.class├── c.class├── d.class├── e.class└── f.class

注意在.cl​​ass文件的每个源文件(.java)中,都有包声明(如package org.hello.world;package org.hello;)

在 shell 中输入 the_tool ./from_root ./to_root

它会变成:

to_root/└── 组织└── 你好├── a.class├── b.class└── 世界├── c.class├── d.class├── e.class└── f.class

案例 2:如果有一个工具可以从混乱的 .class 文件目录中打包正确的 .jar 文件,它应该表现如下:

操作前:

from_root/├── a.class├── b.class├── c.class├── d.class├── e.class└── f.class

在 shell 中输入 pack_tool ./from_root ./to_root/pack.jar

./to_root 中会生成一个 pack.jar.

在Eclipse的Java Build Path中添加时,应该正确导入和调用,而不是错误的命名空间层次结构.

我尝试了什么

对于一些著名的第三部分库(例如 apache tika)

它是由 maven 构建的,我输入了 cd/path/to/tika-1.18-src/tika-1.18/ 然后我输入了 mvn package.

不幸的是,它失败了.

然而,当我输入 mvn compile 时,一切正常.

然后为了构建 .jar 包,我在终端中输入 jar cvf ./build/tika-1.18.jar $(find ./-name org | grep target).

然而,.jar 包内部的结构是完全错误的.我无法在我的项目中使用它.

然后我尝试了 find ./-name org |grep 目标 |并行 cp {} ./build/-R -f 然后 cd ./build 最后 jar cvf ./tika-1.18.jar org.>

它奏效了.但是这种方法有一些缺点.

缺点:

  1. 如果某些源文件(.java 文件)的名称中包含目标",则会造成很大的麻烦.

  2. 如果不同的子项目包含相同的文件会导致冲突.例如这里是冲突信息:cp:无法创建目录./build/org/apache/tika/batch":文件存在cp:无法创建目录./build/org/apache/tika/language/translate":文件存在

  3. 这种方法只能处理那些.class文件的路径部分正确的情况.如果所有 .class 文件都平放在同一个目录下,将无法工作.

又一次尝试

我尝试根据二进制内容将 .class 文件放在正确的目录中.

例如,org.apache.tika.detect.AutoDetectReader

$ hexdump -C/path/to/here/EncodingDetector.class00000000 ca fe ba be 00 00 00 33 00 0e 07 00 0a 07 00 0b |.......3......|00000010 07 00 0c 01 00 06 64 65 74 65 63 74 01 00 54 28 |......检测..T(|00000020 4c 6a 61 76 61 2f 69 6f 2f 49 6e 70 75 74 53 74 |Ljava/io/InputSt|00000030 72 65 61 6d 3b 4c 6f 72 67 2f 61 70 61 63 68 65 |ream;Lorg/apache|00000040 2f 74 69 6b 61 2f 6d 65 74 61 64 61 74 61 2f 4d |/tika/metadata/M|00000050 65 74 61 64 61 74 61 3b 29 4c 6a 61 76 61 2f 6e |etadata;)Ljava/n|00000060 69 6f 2f 63 68 61 72 73 65 74 2f 43 68 61 72 73 |io/charset/Chars|00000070 65 74 3b 01 00 0a 45 78 63 65 70 74 69 6f 6e 73 |et;...例外|00000080 07 00 0d 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 |......源文件|00000090 01 00 15 45 6e 63 6f 64 69 6e 67 44 65 74 65 63 |...EncodingDetec|000000a0 74 6f 72 2e 6a 61 76 61 01 00 27 6f 72 67 2f 61 |tor.java..'org/a|000000b0 70 61 63 68 65 2f 74 69 6b 61 2f 64 65 74 65 63 |pache/tika/detec|000000c0 74 2f 45 6e 63 6f 64 69 6e 67 44 65 74 65 63 74 |t/EncodingDetect|000000d0 6f 72 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f |或...java/lang/O|000000e0 62 6a 65 63 74 01 00 14 6a 61 76 61 2f 69 6f 2f |bject...java/io/|000000f0 53 65 72 69 61 6c 69 7a 61 62 6c 65 01 00 13 6a |可序列化...j|00000100 61 76 61 2f 69 6f 2f 49 4f 45 78 63 65 70 74 69 |ava/io/IOExcepti|00000110 6f 6e 06 01 00 01 00 02 00 01 00 03 00 00 00 01 |开........|00000120 04 01 00 04 00 05 00 01 00 06 00 00 00 04 00 01 |................|00000130 00 07 00 01 00 08 00 00 00 02 00 09 |............|0000013c

为了比较,我使用了另一个 .class 文件 org.apache.tika.embedder.Embedder

$ hexdump -C ./Embedder.class00000000 ca fe ba be 00 00 00 33 00 14 07 00 0f 07 00 10 |.......3......|00000010 07 00 11 01 00 16 67 65 74 53 75 70 70 6f 72 74 |......getSupport|00000020 65 64 45 6d 62 65 64 54 79 70 65 73 01 00 36 28 |edEmbedTypes..6(|00000030 4c 6f 72 67 2f 61 70 61 63 68 65 2f 74 69 6b 61 |Lorg/apache/tika|00000040 2f 70 61 72 73 65 72 2f 50 61 72 73 65 43 6f 6e |/parser/ParseCon|00000050 74 65 78 74 3b 29 4c 6a 61 76 61 2f 75 74 69 6c |文本;)Ljava/util|00000060 2f 53 65 74 3b 01 00 09 53 69 67 6e 61 74 75 72 |/Set;...签名|00000070 65 01 00 58 28 4c 6f 72 67 2f 61 70 61 63 68 65 |e..X(Lorg/apache|00000080 2f 74 69 6b 61 2f 70 61 72 73 65 72 2f 50 61 72 |/tika/parser/Par|00000090 73 65 43 6f 6e 74 65 78 74 3b 29 4c 6a 61 76 61 |seContext;)Ljava|000000a0 2f 75 74 69 6c 2f 53 65 74 3c 4c 6f 72 67 2f 61 |/util/Set;...e|000000d0 6d 62 65 64 01 00 76 28 4c 6f 72 67 2f 61 70 61 |mbed..v(Lorg/apa|000000e0 63 68 65 2f 74 69 6b 61 2f 6d 65 74 61 64 61 74 |che/tika/metadat|000000f0 61 2f 4d 65 74 61 64 61 74 61 3b 4c 6a 61 76 61 |a/元数据;Ljava|00000100 2f 69 6f 2f 49 6e 70 75 74 53 74 72 65 61 6d 3b |/io/InputStream;|00000110 4c 6a 61 76 61 2f 69 6f 2f 4f 75 74 70 75 74 53 |Ljava/io/OutputS|00000120 74 72 65 61 6d 3b 4c 6f 72 67 2f 61 70 61 63 68 |tream;Lorg/apach|00000130 65 2f 74 69 6b 61 2f 70 61 72 73 65 72 2f 50 61 |e/tika/parser/Pa|00000140 72 73 65 43 6f 6e 74 65 78 74 3b 29 56 01 00 0a |rseContext;)V...|00000150 45 78 63 65 70 74 69 6f 6e 73 07 00 12 07 00 13 |例外......|00000160 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 0d |...源文件...|00000170 45 6d 62 65 64 64 65 72 2e 6a 61 76 61 01 00 21 |Embedder.java..!|00000180 6f 72 67 2f 61 70 61 63 68 65 2f 74 69 6b 61 2f |org/apache/tika/|00000190 65 6d 62 65 64 64 65 72 2f 45 6d 62 65 64 64 65 |嵌入器/嵌入器|000001a0 72 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 |r...java/lang/Ob|000001b0 6a 65 63 74 01 00 14 6a 61 76 61 2f 69 6f 2f 53 |ject...java/io/S|000001c0 65 72 69 61 6c 69 7a 61 62 6c 65 01 00 13 6a 61 |erializable...ja|000001d0 76 61 2f 69 6f 2f 49 4f 45 78 63 65 70 74 69 6f |va/io/IOExceptio|000001e0 6e 01 00 27 6f 72 67 2f 61 70 61 63 68 65 2f 74 |n..'org/apache/t|000001f0 69 6b 61 2f 65 78 63 65 70 74 69 6f 6e 2f 54 69 |ika/异常/Ti|00000200 6b 61 45 78 63 65 70 74 69 6f 6e 06 01 00 01 00 |kaException....|00000210 02 00 01 00 03 00 00 00 02 04 01 00 04 00 05 00 ||00000220 01 00 06 00 00 00 02 00 07 04 01 00 08 00 09 00 ||00000230 01 00 0a 00 00 00 06 00 02 00 0b 00 0c 00 01 00 ||00000240 0d 00 00 00 02 00 0e |.......|00000247

令人惊奇的是...SourceFile..."之后的内容.是这个类的包位置.

可以编写一个程序来扫描每个 .class 文件并确定它们在目录中的位置.但是,Java 9、Java 10 和 Java 11 即将推出.不同的java版本会导致.class文件的二进制内容不同.JDK 和 OpenJDK 之间可能有所不同.所以兼容性不够.但另一方面,它表明可以在没有其他信息的情况下确定某个 .class 文件的包位置.

希望有人能提供一些想法,衷心感谢!

解决方案

一般来说,最好首先修复构建系统问题,生成正确的目录结构,而不是事后尝试修复它.我看到的一个问题是,来自不同包的类可能具有相同的简单名称,因此如果将它们的类文件写入同一个平面目录,其中一个会覆盖另一个,并且此数据丢失之后无法修复.

一般情况下,类文件开头的常量池包含限定的类名,所以提取出来是可以的,但是需要了解类文件的结构才能选择正确的字符串.以下方法将解析类文件并提取名称(以其内部形式):

static String getClassName(ByteBuffer buf) {if(buf.order(ByteOrder.BIG_ENDIAN).getInt()!=0xCAFEBABE) {throw new IllegalArgumentException("不是一个有效的类文件");}int minor=buf.getChar(), ver=buf.getChar(), poolSize=buf.getChar();int[] pool = new int[poolSize];//System.out.println("version "+ver+'.'+minor);for(int ix=1; ix>1));而(buf.hasRemaining()){字节 b=buf.get();if(b>0) sb.append((char)b);别的 {int b2 = buf.get();if((b&0xf0)!=0xe0)sb.append((char)((b&0x1F)<<6 | b2&0x3F));别的 {int b3 = buf.get();sb.append((char)((b&0x0F)<<12 | (b2&0x3F)<<6 | b3&0x3F));}}}buf.limit(oldLimit);返回 sb.toString();}私有静态最终字节 CONSTANT_Utf8 = 1,CONSTANT_Integer = 3,CONSTANT_Float = 4, CONSTANT_Long = 5, CONSTANT_Double = 6,CONSTANT_Class = 7, CONSTANT_String = 8, CONSTANT_FieldRef = 9,CONSTANT_MethodRef = 10,CONSTANT_InterfaceMethodRef = 11,CONSTANT_NameAndType = 12, CONSTANT_MethodHandle = 15,CONSTANT_MethodType = 16,CONSTANT_Dynamic = 17,CONSTANT_InvokeDynamic = 18,CONSTANT_Module = 19, CONSTANT_Package = 20;

这可用于修复错误的文件位置,如下所示:

static void checkAndMoveClassFile(Path path) throws IOException {字节缓冲区 bb;try(FileChannel ch=FileChannel.open(path, StandardOpenOption.READ)) {bb=ByteBuffer.allocate((int)ch.size());while(bb.hasRemaining()) ch.read(bb);bb.flip();}字符串名称 = getClassName(bb);路径 newPath = path.resolveSibling(name+".class");if(!path.equals(newPath)) {System.out.println("移动"+path+"到"+newPath);Files.createDirectories(newPath.getParent());Files.move(path, newPath);}}

您可以轻松地在目录上运行

Files.list(dirPath).filter(p -> p.getFileName().toString().endsWith(".class")).forEach(p -> {尝试 { checkAndMoveClassFile(p);}catch (IOException ex) { throw new UncheckedIOException(ex);}});

Background

I have to contact different kind of Java project with various of build system. Sometimes the directory structure is different from the package hierarchy. So it is difficult to package.

Even if the build system like Maven and Gradle have its own function to pack .jar but it need a qualified internet connection and a giant size of local repository. Hence I usually build the library I need on the desktop in my office. However I spend more time on my laptop which doesn't have such good connection and storage.

Question

Is there a stable, compatible and secure way to make .jar package? Or move those .class file to appropriate directory? (An official and mature open source tool is better)

I am a beginner of Java Language, so any point may be helpful for me.

In order to make it clearly, here I put some examples.

Case 1: If there is a tool which can moving (moving or copying) the .class file in correct directory it should behave as below:

Before operation:

from_root/
├── a.class
├── b.class
├── c.class
├── d.class
├── e.class
└── f.class

Note in each source file(.java) of .class file, there is package announcement(like package org.hello.world; or package org.hello;)

when typing the_tool ./from_root ./to_root in shell

it will become:

to_root/
└── org
    └── hello
        ├── a.class
        ├── b.class
        └── world
            ├── c.class
            ├── d.class
            ├── e.class
            └── f.class

Case 2: If there is a tool which can packing correct .jar file from mess .class file directory it should behave as below:

Before operation:

from_root/
├── a.class
├── b.class
├── c.class
├── d.class
├── e.class
└── f.class

when typing pack_tool ./from_root ./to_root/pack.jar in shell

A pack.jar will be generate in ./to_root.

When add it in Java Build Path of Eclipse, it should be imported and called properly, instead of wrong name-space hierarchy .

What I have tried

For some famous 3rd part library (e.g. apache tika)

It is build by maven, and I typed cd /path/to/tika-1.18-src/tika-1.18/ then I typed mvn package.

Unfortunately, it was failed.

However when I typed mvn compile, everything went right.

Then in order to build .jar package, I typed jar cvf ./build/tika-1.18.jar $(find ./ -name org | grep target) in terminal.

However, the structure inside the .jar package was totally wrong. I couldn't use it in my project.

Then I tried find ./ -name org | grep target | parallel cp {} ./build/ -R -f then cd ./build and finally jar cvf ./tika-1.18.jar org.

It worked. However there is some disadvantage of this method.

Shortcoming:

  1. If the some source file(.java file)'s name include 'target', it will cause big trouble.

  2. If different subproject contain same file it will cause conflict. For example here is the conflict information: cp: cannot create directory './build/org/apache/tika/batch': File exists cp: cannot create directory './build/org/apache/tika/language/translate': File exists

  3. This method can only handle the situation which the path of those .class file is partly correct. If all the .class file are located in same directory flatly, it will failed to work.

Another attemption

I tried to put the .class file in correct directory according to its binary content.

For example, org.apache.tika.detect.AutoDetectReader

$ hexdump -C /path/to/here/EncodingDetector.class
00000000  ca fe ba be 00 00 00 33  00 0e 07 00 0a 07 00 0b  |.......3........|
00000010  07 00 0c 01 00 06 64 65  74 65 63 74 01 00 54 28  |......detect..T(|
00000020  4c 6a 61 76 61 2f 69 6f  2f 49 6e 70 75 74 53 74  |Ljava/io/InputSt|
00000030  72 65 61 6d 3b 4c 6f 72  67 2f 61 70 61 63 68 65  |ream;Lorg/apache|
00000040  2f 74 69 6b 61 2f 6d 65  74 61 64 61 74 61 2f 4d  |/tika/metadata/M|
00000050  65 74 61 64 61 74 61 3b  29 4c 6a 61 76 61 2f 6e  |etadata;)Ljava/n|
00000060  69 6f 2f 63 68 61 72 73  65 74 2f 43 68 61 72 73  |io/charset/Chars|
00000070  65 74 3b 01 00 0a 45 78  63 65 70 74 69 6f 6e 73  |et;...Exceptions|
00000080  07 00 0d 01 00 0a 53 6f  75 72 63 65 46 69 6c 65  |......SourceFile|
00000090  01 00 15 45 6e 63 6f 64  69 6e 67 44 65 74 65 63  |...EncodingDetec|
000000a0  74 6f 72 2e 6a 61 76 61  01 00 27 6f 72 67 2f 61  |tor.java..'org/a|
000000b0  70 61 63 68 65 2f 74 69  6b 61 2f 64 65 74 65 63  |pache/tika/detec|
000000c0  74 2f 45 6e 63 6f 64 69  6e 67 44 65 74 65 63 74  |t/EncodingDetect|
000000d0  6f 72 01 00 10 6a 61 76  61 2f 6c 61 6e 67 2f 4f  |or...java/lang/O|
000000e0  62 6a 65 63 74 01 00 14  6a 61 76 61 2f 69 6f 2f  |bject...java/io/|
000000f0  53 65 72 69 61 6c 69 7a  61 62 6c 65 01 00 13 6a  |Serializable...j|
00000100  61 76 61 2f 69 6f 2f 49  4f 45 78 63 65 70 74 69  |ava/io/IOExcepti|
00000110  6f 6e 06 01 00 01 00 02  00 01 00 03 00 00 00 01  |on..............|
00000120  04 01 00 04 00 05 00 01  00 06 00 00 00 04 00 01  |................|
00000130  00 07 00 01 00 08 00 00  00 02 00 09              |............|
0000013c

In order to compare I used another .class file org.apache.tika.embedder.Embedder

$ hexdump -C ./Embedder.class 
00000000  ca fe ba be 00 00 00 33  00 14 07 00 0f 07 00 10  |.......3........|
00000010  07 00 11 01 00 16 67 65  74 53 75 70 70 6f 72 74  |......getSupport|
00000020  65 64 45 6d 62 65 64 54  79 70 65 73 01 00 36 28  |edEmbedTypes..6(|
00000030  4c 6f 72 67 2f 61 70 61  63 68 65 2f 74 69 6b 61  |Lorg/apache/tika|
00000040  2f 70 61 72 73 65 72 2f  50 61 72 73 65 43 6f 6e  |/parser/ParseCon|
00000050  74 65 78 74 3b 29 4c 6a  61 76 61 2f 75 74 69 6c  |text;)Ljava/util|
00000060  2f 53 65 74 3b 01 00 09  53 69 67 6e 61 74 75 72  |/Set;...Signatur|
00000070  65 01 00 58 28 4c 6f 72  67 2f 61 70 61 63 68 65  |e..X(Lorg/apache|
00000080  2f 74 69 6b 61 2f 70 61  72 73 65 72 2f 50 61 72  |/tika/parser/Par|
00000090  73 65 43 6f 6e 74 65 78  74 3b 29 4c 6a 61 76 61  |seContext;)Ljava|
000000a0  2f 75 74 69 6c 2f 53 65  74 3c 4c 6f 72 67 2f 61  |/util/Set<Lorg/a|
000000b0  70 61 63 68 65 2f 74 69  6b 61 2f 6d 69 6d 65 2f  |pache/tika/mime/|
000000c0  4d 65 64 69 61 54 79 70  65 3b 3e 3b 01 00 05 65  |MediaType;>;...e|
000000d0  6d 62 65 64 01 00 76 28  4c 6f 72 67 2f 61 70 61  |mbed..v(Lorg/apa|
000000e0  63 68 65 2f 74 69 6b 61  2f 6d 65 74 61 64 61 74  |che/tika/metadat|
000000f0  61 2f 4d 65 74 61 64 61  74 61 3b 4c 6a 61 76 61  |a/Metadata;Ljava|
00000100  2f 69 6f 2f 49 6e 70 75  74 53 74 72 65 61 6d 3b  |/io/InputStream;|
00000110  4c 6a 61 76 61 2f 69 6f  2f 4f 75 74 70 75 74 53  |Ljava/io/OutputS|
00000120  74 72 65 61 6d 3b 4c 6f  72 67 2f 61 70 61 63 68  |tream;Lorg/apach|
00000130  65 2f 74 69 6b 61 2f 70  61 72 73 65 72 2f 50 61  |e/tika/parser/Pa|
00000140  72 73 65 43 6f 6e 74 65  78 74 3b 29 56 01 00 0a  |rseContext;)V...|
00000150  45 78 63 65 70 74 69 6f  6e 73 07 00 12 07 00 13  |Exceptions......|
00000160  01 00 0a 53 6f 75 72 63  65 46 69 6c 65 01 00 0d  |...SourceFile...|
00000170  45 6d 62 65 64 64 65 72  2e 6a 61 76 61 01 00 21  |Embedder.java..!|
00000180  6f 72 67 2f 61 70 61 63  68 65 2f 74 69 6b 61 2f  |org/apache/tika/|
00000190  65 6d 62 65 64 64 65 72  2f 45 6d 62 65 64 64 65  |embedder/Embedde|
000001a0  72 01 00 10 6a 61 76 61  2f 6c 61 6e 67 2f 4f 62  |r...java/lang/Ob|
000001b0  6a 65 63 74 01 00 14 6a  61 76 61 2f 69 6f 2f 53  |ject...java/io/S|
000001c0  65 72 69 61 6c 69 7a 61  62 6c 65 01 00 13 6a 61  |erializable...ja|
000001d0  76 61 2f 69 6f 2f 49 4f  45 78 63 65 70 74 69 6f  |va/io/IOExceptio|
000001e0  6e 01 00 27 6f 72 67 2f  61 70 61 63 68 65 2f 74  |n..'org/apache/t|
000001f0  69 6b 61 2f 65 78 63 65  70 74 69 6f 6e 2f 54 69  |ika/exception/Ti|
00000200  6b 61 45 78 63 65 70 74  69 6f 6e 06 01 00 01 00  |kaException.....|
00000210  02 00 01 00 03 00 00 00  02 04 01 00 04 00 05 00  |................|
00000220  01 00 06 00 00 00 02 00  07 04 01 00 08 00 09 00  |................|
00000230  01 00 0a 00 00 00 06 00  02 00 0b 00 0c 00 01 00  |................|
00000240  0d 00 00 00 02 00 0e                              |.......|
00000247

What's amazing is the content after "...SourceFile..." is the package location of this class.

It is possible to write a program which can scan every .class file and determine their location in the directory. However, there is Java 9, Java 10 and Java 11 will coming soon. different java version will cause the different binary content of .class files. And it might be different between JDK and OpenJDK. So it is not compatible enough. But on the other hand, it shows that it is possible to determine the package location of a certain .class file without other infomation.

Hope someone can provide some ideas, thanks sincerely!

解决方案

Generally, it is better to fix the build system issues, to generate the correct directory structure in the first place, rather than trying to fix it after the fact. One problem I see, is that classes from different packages may have the same simple name, so if their class files are written to the same flat directory, one of them will overwrite the other and this data loss can not be fixed afterwards.

Generally, the constant pool at the beginning of the class file contains the qualified class name, so it is possible to extract it, but you need to understand the class file structure to pick the right string. The following method will parse a class file and extract the name (in its internal form):

static String getClassName(ByteBuffer buf) {
    if(buf.order(ByteOrder.BIG_ENDIAN).getInt()!=0xCAFEBABE) {
        throw new IllegalArgumentException("not a valid class file");
    }
    int minor=buf.getChar(), ver=buf.getChar(), poolSize=buf.getChar();
    int[] pool = new int[poolSize];
    //System.out.println("version "+ver+'.'+minor);
    for(int ix=1; ix<poolSize; ix++) {
        String s; int index1=-1, index2=-1;
        byte tag = buf.get();
        switch(tag) {
            default: throw new UnsupportedOperationException(
                    "unknown pool item type "+buf.get(buf.position()-1));
            case CONSTANT_Utf8:
                buf.position((pool[ix]=buf.position())+buf.getChar()+2); continue;
            case CONSTANT_Module: case CONSTANT_Package: case CONSTANT_Class:
            case CONSTANT_String: case CONSTANT_MethodType:
                pool[ix]=buf.getChar(); break;
            case CONSTANT_FieldRef: case CONSTANT_MethodRef:
            case CONSTANT_InterfaceMethodRef: case CONSTANT_NameAndType:
            case CONSTANT_InvokeDynamic: case CONSTANT_Dynamic:
            case CONSTANT_Integer: case CONSTANT_Float:
                buf.position(buf.position()+4); break;
            case CONSTANT_Double: case CONSTANT_Long:
                buf.position(buf.position()+8); ix++; break;
            case CONSTANT_MethodHandle: buf.position(buf.position()+3); break;
        }
    }
    int access = buf.getChar(), thisClass = buf.getChar();
    buf.position(pool[pool[thisClass]]);
    return decodeString(buf);
}
private static String decodeString(ByteBuffer buf) {
    int size=buf.getChar(), oldLimit=buf.limit();
    buf.limit(buf.position()+size);
    StringBuilder sb=new StringBuilder(size+(size>>1));
    while(buf.hasRemaining()) {
        byte b=buf.get();
        if(b>0) sb.append((char)b);
        else {
            int b2 = buf.get();
            if((b&0xf0)!=0xe0)
                sb.append((char)((b&0x1F)<<6 | b2&0x3F));
            else {
                int b3 = buf.get();
                sb.append((char)((b&0x0F)<<12 | (b2&0x3F)<<6 | b3&0x3F));
            }
        }
    }
    buf.limit(oldLimit);
    return sb.toString();
}
private static final byte CONSTANT_Utf8 = 1, CONSTANT_Integer = 3,
    CONSTANT_Float = 4, CONSTANT_Long = 5, CONSTANT_Double = 6,
    CONSTANT_Class = 7, CONSTANT_String = 8, CONSTANT_FieldRef = 9,
    CONSTANT_MethodRef = 10, CONSTANT_InterfaceMethodRef = 11,
    CONSTANT_NameAndType = 12, CONSTANT_MethodHandle = 15,
    CONSTANT_MethodType = 16, CONSTANT_Dynamic = 17, CONSTANT_InvokeDynamic = 18,
    CONSTANT_Module = 19, CONSTANT_Package = 20;

This can be used to fix a wrong file location like this:

static void checkAndMoveClassFile(Path path) throws IOException {
    ByteBuffer bb;
    try(FileChannel ch=FileChannel.open(path, StandardOpenOption.READ)) {
        bb=ByteBuffer.allocate((int)ch.size());
        while(bb.hasRemaining()) ch.read(bb);
        bb.flip();
    }
    String name = getClassName(bb);
    Path newPath = path.resolveSibling(name+".class");
    if(!path.equals(newPath)) {
        System.out.println("moving "+path+" to "+newPath);
        Files.createDirectories(newPath.getParent());
        Files.move(path, newPath);
    }
}

which you can run over a directory easily

Files.list(dirPath)
     .filter(p -> p.getFileName().toString().endsWith(".class"))
     .forEach(p -> {
         try { checkAndMoveClassFile(p); }
         catch (IOException ex) { throw new UncheckedIOException(ex); }
     });

这篇关于如何纠正混乱的 java .class 文件集或从混乱的 .class 集生成正确的 .jar 存档?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆