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";
}
Note that here we have recompiled the “class A” alone. Now if we execute the class B (class that contains main() method) then an error message like below will be thrown at run-time.
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";
}
Exception in thread “main” java.lang.VerifyError: (class: C, method: main signature: ([Ljava/lang/String;)V) Incompatible argument to function
Could not find the main class: C.
Program will exit.
原因 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,以确保一切正确。
尽可能使用最新版本的依赖项,而不是禁用验证。