📜  java中为什么会出现Java Java以及如何解决?

📅  最后修改于: 2022-05-13 01:55:50.174000             🧑  作者: Mango

java中为什么会出现Java Java以及如何解决?

Java虚拟机 (JVM) 不信任所有加载的字节码作为Java安全模型的核心原则。在运行时,JVM 将加载 .class 文件并尝试将它们链接在一起以形成可执行文件——但这些加载的 .class 文件的有效性是未知的。为确保加载的 .class 文件不会对最终可执行文件构成威胁,JVM 会对 .class 文件进行验证过程。

此外,JVM 确保二进制文件格式良好。例如,JVM 将验证类不会对 final 类进行子类型化。在许多情况下,对有效、非恶意字节码的验证会失败,因为较新版本的Java具有比旧版本更严格的验证过程。例如,JDK 13 可能添加了一个未在 JDK 7 中强制执行的验证步骤。因此,如果我们使用 JVM 13 运行应用程序并包含使用旧版本Java编译器 (javac) 编译的依赖项,JVM 可能会考虑过时的依赖项无效。

因此,当将较旧的 .class 文件与较新的 JVM 链接时,JVM 可能会抛出Java.lang.VerifyError。

自Java 1.0 版本以来,VerifyError 就存在了。

VerifyError 的结构:

构造函数

VerifyError()

这个构造函数创建了一个 VerifyError 类的实例,将 null 设置为它的消息。

VerifyError(String s)

此构造函数使用指定的字符串作为消息创建 VerifyError 类的实例。这里抛出错误的类通过字符串参数表示。

可能发生此错误的三个最常见原因如下:

原因 1: “只要扩展了声明为 final 的类,就会抛出此错误。”

程序:

Java
// Java program to show the occurence 
// of  java.lang.VerifyError
  
class B extends A {
  
    public static void main(String args[])
  
    {
  
        System.out.println("my super class name:-"
                           + myname);
    }
}
  
public class A
  
{
  
    static String myname = "A";
}


Java
// Java program to show the occurence 
// of  java.lang.VerifyError
  
class C extends B {
    public static void main(String args[])
    {
        B b = new B();
        display(b);
    }
    public static void display(A a)
    {
        System.out.println(a.supername);
    }
}
  
class B extends A {
    String subname = "B";
}
  
public class A {
    String supername = "A";
}


Java
// Java program to show the occurence 
// of  java.lang.VerifyError
  
class B extends A
{
  
    public static void main(String args[])
  
    {
  
        A a = new A();
  
        a.display();
    }
  
    void display() { super.display(); }
}
  
public class A
{
    String supername = "A";
  
    void display()
  
    {
  
        System.out.println("My name is " + supername);
    }
}


正如您所看到的,如果您编译并执行这两个程序,它必须能够正常工作而不会显示任何错误。现在按如下方式更改A类并单独编译它。

final public class A
{
  static String myname="A";
}
Exception in thread "main" java.lang.VerifyError: Cannot inherit from final class
       at java.lang.ClassLoader.defineClass1(Native Method)
       at java.lang.ClassLoader.defineClassCond(Unknown Source)
       at java.lang.ClassLoader.defineClass(Unknown Source)
       at java.security.SecureClassLoader.defineClass(Unknown Source)
       at java.net.URLClassLoader.defineClass(Unknown Source)
       at java.net.URLClassLoader.access$000(Unknown Source)
       at java.net.URLClassLoader$1.run(Unknown Source)
       at java.security.AccessController.doPrivileged(Native Method)
       at java.net.URLClassLoader.findClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
       at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: B.  

这个错误是因为我们更改了类 TestClassA 的定义,但是类 TestClassB 是使用类 TestClassA 的旧版本编译的。

原因 2: “考虑一个以前扩展另一个类的类,如果它现在不再扩展该类,那么可能会在运行时抛出此错误。”

程序:

Java

// Java program to show the occurence 
// of  java.lang.VerifyError
  
class C extends B {
    public static void main(String args[])
    {
        B b = new B();
        display(b);
    }
    public static void display(A a)
    {
        System.out.println(a.supername);
    }
}
  
class B extends A {
    String subname = "B";
}
  
public class A {
    String supername = "A";
}
输出
A

上面的程序也可以正常工作,但是如果将 B 类更改为不再扩展 A 类,则可能会引发错误。现在,如果我们按如下方式更改 B 类,并“单独重新编译它”,那么 C 类将不知道 B 类中所做的更改,从而导致此错误。

class B {
String subname="B";
}

原因 3: “如果我们尝试覆盖声明为 final 的方法,那么也会抛出此错误”。让我们有类 A 和 B 如下:

程序:

Java

// Java program to show the occurence 
// of  java.lang.VerifyError
  
class B extends A
{
  
    public static void main(String args[])
  
    {
  
        A a = new A();
  
        a.display();
    }
  
    void display() { super.display(); }
}
  
public class A
{
    String supername = "A";
  
    void display()
  
    {
  
        System.out.println("My name is " + supername);
    }
}

在类 A 中,如果方法 display() 更改为 final 并“单独重新编译”,则如果执行类 B,则将抛出此验证错误,因为没有其他类可以覆盖此方法。

输出:

Exception in thread "main" java.lang.VerifyError: class B overrides final method
display.()V
       at java.lang.ClassLoader.defineClass1(Native Method)
       at java.lang.ClassLoader.defineClassCond(Unknown Source)
       at java.lang.ClassLoader.defineClass(Unknown Source)
       at java.security.SecureClassLoader.defineClass(Unknown Source)
       at java.net.URLClassLoader.defineClass(Unknown Source)
       at java.net.URLClassLoader.access$000(Unknown Source)
       at java.net.URLClassLoader$1.run(Unknown Source)
       at java.security.AccessController.doPrivileged(Native Method)
       at java.net.URLClassLoader.findClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
       at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: B.  Program will exit.

在这里你可能已经注意到,这验证了错误被抛出,因为我们只重新编译了编辑过的类”,而不是整个类。因此,您可能认为如果通过重新编译包含 main() 方法的类,将所有类作为一个整体重新编译,则可以轻松识别此错误。当然,确实如此,但在某些情况下,您在编译时无法识别此错误,这主要是因为您的应用程序中使用了两个不同版本的第三方库。

如何处理VerifyError?

为了避免 VerifyError,您必须使用相同版本的Java编译所有类。此外,一旦对类进行了更改,请确保从头开始重新编译项目。最后,如果您的应用程序使用外部库,请确认您使用每个库的适当版本,当然,请查阅相应的 javadoc,以确保一切正确。

尽可能使用最新版本的依赖项,而不是禁用验证。