java no such method_no_such_method: 用于研究项目运行时抛出NoSuchMethodError,类似的还有NoClassDefFoundError等...

运行期间抛出NoSuchMethodError

背景

在开发中常遇到抛出NoSuchMethodError的情景,但想不通找不到方法的问题为什么可以通过编译阶段,所以创建了三个工程来模拟出NoSuchMethod的场景进行研究。

模拟NoSuchMethodError

这里简单的复现了NoSuchMethodError出现的场景:outer有1.0和2.0两个不兼容的版本,caller依赖于outer的2.0版本,而main中又显式指出了outer的1.0版本,执行main中的主方法即报错。

下面是main的依赖树:

[INFO] com.example:main:jar:1.0-SNAPSHOT

[INFO] +- com.example:caller:jar:1.0:compile

[INFO] \- com.example:outer:jar:1.0:compile

步骤

1. 生成1.0版本的outer

1.1 创建Painter类,其中包含一个接收String类型的print方法。

package org.example;

public class Painter {

// 1.0

public static void print(String name) {

System.out.println("Hello " + name);

}

}

1.2 设置pom中的version为1.0

1.3 运行mvn clean install安装到本地仓库

2. 生成2.0版本的outer

2.1 更新Painter类,更新print方法,改为接收一个Integer类型的参数。

package org.example;

public class Painter {

// 1.0

//public static void print(String name) {

// System.out.println("Hello " + name);

//}

// 2.0

public static void print(Integer i) {

System.out.println("Hello NO." + i);

}

}

2.2 设置pom中的version为2.0

2.3 运行mvn clean install安装到本地仓库

3. 创建caller

3.1 caller依赖于2.0版本的outer,pom片段如下

com.example

outer

2.0

3.2 创建Caller类,调用2.0版本outer中的Painter

package com.example;

import org.example.Painter;

public class Caller {

public static void call() {

Painter.print(1);

}

}

3.3 运行mvn clean install安装到本地仓库

4. 创建main

4.1 设置pom依赖如下

com.example

caller

1.0

com.example

outer

1.0

4.2 新建Main类,其中有main方法来调用Caller中的静态方法

package com.example;

public class Main {

public static void main(String[] args) {

Caller.call();

}

}

4.3 编译mvn compile -X,我们可以看到最终使用的outer版本为1.0,并且并没有报错

[DEBUG] com.example:main:jar:1.0-SNAPSHOT

[DEBUG] com.example:caller:jar:1.0:compile

[DEBUG] com.example:outer:jar:1.0:compile

[INFO] BUILD SUCCESS

4.4 运行main中的主函数,抛出NoSuchMethodError,命令为mvn exec:java -Dexec.mainClass="com.example.Main" -X,结果如下

java.lang.NoSuchMethodError: org.example.Painter.print(Ljava/lang/Integer;)V

at com.example.Caller.call (Caller.java:7)

at com.example.Main.main (Main.java:5)

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:498)

at org.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:282)

at java.lang.Thread.run (Thread.java:748)

原因分析

为什么编译时不报错,因为依赖的jar已经编译过,无需再编译,所以不报错。或者说,编译过程中只会检查当前java文件的正确性,不会关心依赖的class的正确性。而在运行时则动态加载类,才发现找不到方法,故抛出NoSuchMethodError。

验证

将Main反编译,使用命令javap -c com.example.Main,结果如下

Compiled from "Main.java"

public class com.example.Main {

public com.example.Main();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

public static void main(java.lang.String[]);

Code:

0: invokestatic #2 // Method com/example/Caller.call:()V

3: return

}

可以看到main的编译过程中并没有涉及到对outer工程中Painter#paint方法的引用,所以不会在编译期间发现这个问题。

而如果使用1.0版本的outer作为caller的依赖,则在编译时就会报异常,编译命令:mvn clean compile,异常片段如下

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project caller: Compilation failure

[ERROR] /no_such_method/caller/src/main/java/com/example/Caller.java:[7,23] 不兼容的类型: int无法转换为java.lang.String

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值