【问题标题】:What is the best way to calculate a checksum for a Java class?为 Java 类计算校验和的最佳方法是什么?
【发布时间】:2013-11-15 04:59:14
【问题描述】:

我有一个应用程序,我在其中生成基于 Java“源”类的“目标文件”。我想在源更改时重新生成目标。我决定最好的方法是获取类内容的字节[]并计算字节[]的校验和。

我正在寻找获取类的 byte[] 的最佳方法。这个 byte[] 相当于编译的 .class 文件的内容。使用 ObjectOutputStream 不 工作。下面的代码生成一个比类文件的字节内容小得多的字节[]。

// Incorrect function to calculate the byte[] contents of a Java class
public static final byte[] getClassContents(Class<?> myClass) throws IOException {
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    try( ObjectOutputStream stream = new ObjectOutputStream(buffer) ) {
        stream.writeObject(myClass);
    }
    // This byte array is much smaller than the contents of the *.class file!!!
    byte[] contents = buffer.toByteArray();
    return contents;
}

有没有办法让 byte[] 与 *.class 文件的内容相同?计算校验和是容易的部分,困难的部分是获取用于计算 MD5 或 CRC32 校验和的 byte[] 内容。

【问题讨论】:

  • 这听起来像是你真的不应该为之滚动的东西。为什么要原始 .class 文件?
  • 我不明白你为什么要检查 .class 文件的 byte[] 而不是 src 文件。您也可以尝试查看上次修改时间。
  • @Keerthi,我想计算 *.class 而不是 *.java 文件的内容,因为即使 cmets 改变,后者也会改变。我不在乎 cmets 的内容是否改变,只要实际指令改变。
  • @torquestomp,我想要原始的 *.class 文件,因为它代表了实现和签名的全部内容。我希望仅当自上次生成源后 *.class 文件的校验和发生更改时才重新生成目标文件。
  • MakeFile 概念也是一种使用的技术。试着从中得到一些想法。但它再次通过比较生成的二进制/目标文件的时间戳和其内部数据库中源文件的最后修改类型来检查源文件的任何更改。 pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html

标签: java class reflection checksum


【解决方案1】:

这是我最终使用的解决方案。我不知道它是否是最有效的实现,但是下面的代码使用类加载器来获取 *.class 文件的位置并读取其内容。为简单起见,我跳过了读取的缓冲。

// Function to obtain the byte[] contents of a Java class
public static final byte[] getClassContents(Class<?> myClass) throws IOException {
    String path = myClass.getName().replace('.', '/');
    String fileName = new StringBuffer(path).append(".class").toString();
    URL url = myClass.getClassLoader().getResource(fileName);
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    try (InputStream stream = url.openConnection().getInputStream()) {
        int datum = stream.read();
        while( datum != -1) {
            buffer.write(datum);
            datum = stream.read();
        }
    }   
    return buffer.toByteArray();
}

【讨论】:

  • 您可以使用getResourceAsStream(fileName) 来保护网址内容。
【解决方案2】:

我不明白你的意思,但我认为你正在寻找这个,MD5。

要检查文件的 MD5,您可以使用此代码

public String getMd5(File file)
{
    DigestInputStream stream = null;
    try
    {
        stream = new DigestInputStream(new FileInputStream(file), MessageDigest.getInstance("MD5"));
        byte[] buffer = new byte[65536];

        read = stream.read(buffer);
        while (read >= 1) {
        read = stream.read(buffer);
        }
    }
    catch (Exception ignored)
    {
        int read;
        return null;
    }
    return String.format("%1$032x", new Object[] { new BigInteger(1, stream.getMessageDigest().digest()) });
}

然后,您可以以任何方式存储文件的 md5,例如 XML。 MD5 的一个例子是49e6d7e2967d1a471341335c49f46c6c,所以一旦文件名和大小改变,md5 就会改变。您可以将每个文件的 md5 存储为 XML 格式,下次运行代码检查 md5 并比较 xml 文件中每个文件的 md5。

【讨论】:

【解决方案3】:

如果你真的想要 .class 文件的内容,你应该读取 .class 文件的内容,而不是内存中的 byte[] 表示。所以像

import java.io.*;

public class ReadSelf {
    public static void main(String args[]) throws Exception  {
        Class classInstance = ReadSelf.class;
        byte[] bytes = readClass(classInstance);
    }
    public static byte[] readClass(Class classInstance) throws Exception {
        String name = classInstance.getName();
        name = name.replaceAll("[.]", "/") + ".class";
        System.out.println("Reading this: " + name);
        File file = new File(name);
        System.out.println("exists: " + file.exists());
        return read(file);
    }
    public static byte[] read(File file) throws Exception {
        byte[] data = new byte[(int)file.length()]; // can only read a file of size INT_MAX
        DataInputStream inputStream =
        new DataInputStream(
            new BufferedInputStream(
                new FileInputStream(file)));

        int total = 0;
        int nRead = 0;
        try {
            while((nRead = inputStream.read(data)) != -1) {
                total += nRead;
            }
        }
        finally {
            inputStream.close();
        }
        System.out.println("Read " + total
            + " characters, which should match file length of "
            + file.length() + " characters");
        return data;
    }
}

【讨论】:

  • 你说得对,我想要 *.class 文件的直接内容并从文件系统中读取它是最好的方法。我也在发布我找到的解决方案。
猜你喜欢
  • 2010-10-03
  • 1970-01-01
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
  • 2020-09-26
  • 2015-08-21
  • 1970-01-01
  • 2011-09-13
相关资源
最近更新 更多