java 8中的ClassFormatError?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24580370/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 13:10:52  来源:igfitidea点击:

ClassFormatError in java 8?

javajava-8

提问by bcsb1001

I was making a class similar to java.util.LinkedList, and got a completely unexpected ClassFormatError. My IDE shows no warnings. FYI, I am using Java 8u20. Update: fixed in Java 8u60.

我正在制作一个类似的类java.util.LinkedList,并得到了一个完全出乎意料的ClassFormatError. 我的 IDE 没有显示任何警告。仅供参考,我使用的是 Java 8u20。更新:在 Java 8u60 中修复。

T?h?i?s? ?i?n?c?l?u?d?e?s? ?a?l?l? ?r?e?l?e?v?a?n?t? ?m?e?t?h?o?d?s?:? Updated example as fully compilable:

这个??i?n?c?l?u?d?e?s? ?全部??相关的??方法?:?将示例更新为完全可编译:

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;

public class Foo<E> implements Deque<E>, Serializable {
    private static final long serialVersionUID = 0L;

    private final Node sentinel = sentinelInit();
    private final Iterable<Node> nodes = (Iterable<Node> & Serializable) () -> new Iterator<Node>() {
        @SuppressWarnings("UnusedDeclaration")
        private static final long serialVersionUID = 0L;

        private Node next = sentinel.next;

        @Override
        public boolean hasNext() {
            return next != sentinel;
        }

        @Override
        public Node next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            Node old = next;
            next = next.next;
            return old;
        }

        @Override
        public void remove() {
             if (next.previous == sentinel) {
                throw new IllegalStateException();
            }
            removeNode(next.previous);
        }
    };

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return null;
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return false;
    }

    @Override
    public void clear() {

    }

    @Override
    public void addFirst(E e) {

    }

    @Override
    public void addLast(E e) {

    }

    @Override
    public boolean offerLast(E e) {
        return false;
    }

    @Override
    public E removeFirst() {
        return null;
    }

    @Override
    public E removeLast() {
        return null;
    }

    @Override
    public E pollFirst() {
        return null;
    }

    @Override
    public E getFirst() {
        return null;
    }

    @Override
    public E getLast() {
        return null;
    }

    @Override
    public E peekFirst() {
        return null;
    }

    @Override
    public boolean removeFirstOccurrence(Object o) {
        return false;
    }

    @Override
    public boolean removeLastOccurrence(Object o) {
        return false;
    }

    @Override
    public E remove() {
        return null;
    }

    @Override
    public E element() {
       return null;
    }

    @Override
    public void push(E e) {

    }

    @Override
    public E pop() {
        return null;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public boolean offerFirst(E e) {
        return false;
    }

    @Override
    public E pollLast() {
        return null;
    }

    @Override
    public E peekLast() {
        return null;
    }

    @Override
    public boolean offer(E e) {
        Node node = new Node(e);
        sentinel.previous.next = node;
        node.previous = sentinel.previous;
        sentinel.previous = node;
        node.next = sentinel;
        return true;
    }

    @Override
    public E poll() {
        return null;
    }

    @Override
    public E peek() {
        return null;
    }

    @Override
    public boolean remove(Object o) {
        for (Node node : nodes) {
            if (node.value.equals(o)) {
                removeNode(node);
                return true;
            }
        }
        return false;
    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public Iterator<E> descendingIterator() {
        return null;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            private final Iterator<Node> backingIter = nodes.iterator();

            @Override
            public boolean hasNext() {
                return backingIter.hasNext();
            }

            @Override
            public E next() {
                return backingIter.next().value;
            }

            @Override
            public void remove() {
                backingIter.remove();
            }
        };
    }

    private Node sentinelInit() {
        Node sentinel = new Node();
        sentinel.next = sentinel;
        sentinel.previous = sentinel;
        return sentinel;
    }

    private void removeNode(Node node) {
        node.previous.next = node.next;
        node.next.previous = node.previous;
    }

    private class Node implements Serializable {
        private static final long serialVersionUID = 0L;
        public E value;
        public Node next;
        public Node previous;

        public Node(E value) {
            this.value = value;
        }

        public Node() {
            this(null);
        }
    }

    public static <I, O> List<O> map(Function<? super I, O> function, Iterable<I> objects) {
        ArrayList<O> returned = new ArrayList<>();
        for (I obj : objects) {
            returned.add(function.apply(obj));
        }
        return returned;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        boolean ret = false;
        for (boolean changed : map(this::add, c)) {
            if (changed) {
                ret = true;
            }
        }
        return ret;
    }

    @Override
    public boolean add(E e) {
        if (!offer(e)) {
            throw new IllegalStateException();
        }
        return true;
    }

    public static void main(String[] args) {
        Foo<String> list = new Foo<>();
        System.out.println("Constructed list");
        list.addAll(Arrays.asList("a", "B", "c"));
        System.out.println("Added a, B and c.");
        list.forEach(System.out::println);
        list.remove("B");
        list.forEach(System.out::println);
    }
}

Here is the output:

这是输出:

Constructed list
Added a, B and c.
Exception in thread "main" java.lang.ClassFormatError: Duplicate field name&signature in class file uk/org/me/Foo
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:455)
    at java.net.URLClassLoader.access0(URLClassLoader.java:73)
    at java.net.URLClassLoader.run(URLClassLoader.java:367)
    at java.net.URLClassLoader.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at uk.org.me.Foo.lambda$new$c83cc381(Foo.java:18)
    at uk.org.me.Foo$$Lambda/1392838282.iterator(Unknown Source)
    at uk.org.me.Foo.<init>(Foo.java:222)
    at uk.org.me.Foo.iterator(Foo.java:221)
    at java.lang.Iterable.forEach(Iterable.java:74)
    at uk.org.me.Foo.main(Foo.java:300)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

回答by Edwin Buck

Try compiling the '.java' file on the command line with your jdk installation.

尝试使用 jdk 安装在命令行上编译“.java”文件。

After you compile using the JDK, see if you can run off the new '.class' files. If your class loader is working under those conditions, your IDE is not using the JDK copy of javac (or is configured to compile to a different --target level).

使用 JDK 编译后,看看是否可以运行新的“.class”文件。如果您的类加载器在这些条件下工作,则您的 IDE 没有使用 javac 的 JDK 副本(或配置为编译到不同的 --target 级别)。

Eclipse used to ship it's own compiler (and I believe it still does). Netbeans always defers to the JDK compiler. In the last couple of releases, the JDK compiler was worked on quite a bit for IDE integration; but, I don't think Eclipse has reworked their IDE to take advantage of that effort.

Eclipse 过去常常发布它自己的编译器(我相信它仍然如此)。Netbeans 始终遵循 JDK 编译器。在最近的几个版本中,JDK 编译器在 IDE 集成方面做了大量工作;但是,我认为 Eclipse 并没有重新设计他们的 IDE 来利用这一努力。

In any event, you will soon be able to tell if your issue is your JDK compiler or IDE compiler.

无论如何,您很快就能判断出问题是出在 JDK 编译器还是 IDE 编译器上。

回答by Marco Massenzio

Both variables inside the nodesiterable and the private class Nodecalled nextare of the same type (Node) and have the same name: inside that private class, they are both in scope, but cannot be disambiguated.

nodes可迭代变量和被private class Node调用变量的next类型相同 ( Node) 并具有相同的名称:在私有类中,它们都在范围内,但不能消除歧义。

So, the issue is notthe nextfield and next()method, but the nextnamed variables both in Iterable<Node> nodesand your private class Node.

所以,这个问题是不是next领域和next()方法,但是next命名变量都在Iterable<Node> nodes和你的private class Node

If you refactor sentinel.nextto some other name:

如果您重构sentinel.next为其他名称:

private final Iterable<Node> nodes = (Iterable<Node> & Serializable) () -> new Iterator<Node>() {
    @SuppressWarnings("UnusedDeclaration")
    private static final long serialVersionUID = 0L;

    private Node snext = sentinel.next;  // refactor `next` to `snext`
    ...

It will compile and run.

它将编译并运行。

As a personal note, I would discourage you writing code such as this: it's incredibly difficult to read and figure out what it does.

作为个人说明,我不鼓励您编写这样的代码:阅读和弄清楚它的作用非常困难。

Hope this helps!

希望这可以帮助!

回答by skomisa

Your code is valid, and the ClassFormatErroris due to a bug in the Java 8 compiler. I think you have uncovered an edge case: loading the class file generated for a lambda expression body which contains a variable with the same name as a method that is being overriden within that body may fail.

您的代码是有效的,这ClassFormatError是由于 Java 8 编译器中的错误造成的。我认为您已经发现了一个边缘情况:加载为 lambda 表达式主体生成的类文件,其中包含与在该主体中被覆盖的方法同名的变量可能会失败。

I pasted your code into the latest versions of Intellij Idea (13.1.5), NetBeans (8.0.1) and Eclipse (Kepler SR2). The code compiled in all cases. When run, it failed with that ClassFormatErrorin NetBeans and Idea, yet it ran fine in Eclipse.

我将您的代码粘贴到 Intellij Idea (13.1.5)、NetBeans (8.0.1) 和 Eclipse (Kepler SR2) 的最新版本中。在所有情况下编译的代码。运行时,它ClassFormatError在 NetBeans 和 Idea 中失败,但在 Eclipse 中运行良好。

The stack trace shows the Error is arising from a failed attempt to load the class generated by the lamba expression assigned to instance variable Foo.nodes. The root cause is that within the lambda body assigned to Foo.nodesyou have a variable nextwith the same name as the overriden method next().

堆栈跟踪显示错误是由于加载由分配给实例变量的 Lamba 表达式生成的类的尝试失败引起的Foo.nodes。根本原因是在分配给Foo.nodes您的 lambda 主体中有一个next与覆盖方法同名的变量next()

As you have already noted, if you rename the variable in the declaration private Node next = sentinel.next;to some unique value (e.g. next2) the problem goes away. Similarly, renaming it to a different overriden method (i.e. removeor hasNext) causes the problem to reappear. The message accompanying the ClassFormatError("Duplicate field name&signature") exactly describes the problem, but does not explain why this should be such a precisely defined runtime error at all; the code compiled, and having the same name for a method and a variable is legal (if unwise). Prefixing next with "this." doesn't solve the problem.

正如您已经注意到的,如果您将声明中的变量重命名private Node next = sentinel.next;为某个唯一值(例如next2),问题就会消失。同样,将其重命名为不同的覆盖方法(即removehasNext)会导致问题再次出现。伴随ClassFormatError("Duplicate field name&signature") 的消息准确地描述了这个问题,但并没有解释为什么这应该是一个如此精确定义的运行时错误;编译的代码,并且方法和变量具有相同的名称是合法的(如果不明智的话)。以“this”为前缀。不能解决问题。

Running javap on the failing Idea class file (foo/Foo$1in my case) shows this:

在失败的 Idea 类文件(foo/Foo$1在我的例子中)上运行 javap显示:

C:\Idea\Java8\Foo\out\production\Foo\foo>"C:\Program Files\Java\jdk1.8.0_20\bin\javap.exe" Foo.class
Compiled from "Foo.java"
class foo.Foo implements java.util.Iterator<foo.Foo<E>.Node> {
  final foo.Foo this
public java.lang.Object next();
; foo.Foo(foo.Foo); public boolean hasNext(); public foo.Foo<E>.Node next(); public void remove(); }

However, running javap on the corresponding Eclipse class which ran fine shows an extra entry:

但是,在运行良好的相应 Eclipse 类上运行 javap 会显示一个额外的条目:

##代码##

That extra method entry also magically appears in the Idea class file if the variable nextwithin the lambda body is renamed to something unique, allowing the code to run. I suggest you report this as a compiler bug to JetBrains.

如果nextlambda 主体中的变量被重命名为唯一的东西,那么这个额外的方法条目也会神奇地出现在 Idea 类文件中,从而允许代码运行。我建议您将此作为编译器错误报告给 JetBrains。

Also, as an unrelated issue, the Iteratorinterface implements remove()as a default method in Java 8. Since you never call Iterator.remove()you can delete your two implementations.

此外,作为一个不相关的问题,该Iterator接口remove()在 Java 8 中作为默认方法实现。由于您从不调用,Iterator.remove()您可以删除您的两个实现。

UPDATE: I raised a bug (JDK-8080842) for this issue with Oracle, and there will be a fix for it in release 1.8.0_60.

更新:我针对 Oracle 的这个问题提出了一个错误 (JDK-8080842),并将在 1.8.0_60 版中修复它。

UPDATE TO THE UPDATE (8/27/15): Oracle have fixed this issue in all releases from 8u60 onwards. I verified the fix against 8u60. Bug JDK-8080842 refers.

更新至更新 (8/27/15):Oracle 已在 8u60 以后的所有版本中修复了此问题。我验证了针对 8u60 的修复。错误 JDK-8080842 指的是。

回答by Roy Doron

As per java jdk documentation : Thrown when the Java Virtual Machine attempts to read a class file and determines that the file is malformed or otherwise cannot be interpreted as a class file.

根据 java jdk 文档:当 Java 虚拟机尝试读取类文件并确定该文件格式错误或无法解释为类文件时抛出。

You could try to delete the contents of the outcompiled classes folderthen have a full rebuild.

你可以尝试删除的内容进行编译的类文件夹,然后有一个完整的重建。

That's what worked for me.

这就是对我有用的东西。