Java总结篇系列:java.lang.Object

本文详细介绍了Java中java.lang.Object类的重要方法,包括构造器、registerNatives、clone、getClass、equals、hashCode、toString、wait/notify及finalize。讲解了它们的功能、使用场景和在多线程、垃圾回收中的作用,揭示了Java面向对象设计的底层思维。

从本篇开始,将对Java中各知识点进行一次具体总结,以便对以往的Java知识进行一次回顾,同时在总结的过程中加深对Java的理解。

Java作为一个庞大的知识体系,涉及到的知识点繁多,本文将从Java中最基本的类java.lang.Object开始谈起。

Object类是Java中其他所有类的祖先,没有Object类Java面向对象无从谈起。作为其他所有类的基类,Object具有哪些属性和行为,是Java语言设计背后的思维体现。

Object类位于java.lang包中,java.lang包包含着Java最基础和核心的类,在编译时会自动导入。Object类没有定义属性,一共有13个方法,具体的类定义结构如下图:

1.类构造器public Object();

大部分情况下,Java中通过形如 new A(args..)形式创建一个属于该类型的对象。其中A即是类名,A(args..)即此类定义中相对应的构造函数。通过此种形式创建的对象都是通过类中的构造函数完成。为体现此特性,Java中规定:在类定义过程中,对于未定义构造函数的类,默认会有一个无参数的构造函数,作为所有类的基类,Object类自然要反映出此特性,在源码中,未给出Object类构造函数定义,但实际上,此构造函数是存在的。

当然,并不是所有的类都是通过此种方式去构建,也自然的,并不是所有的类构造函数都是public。
 

2.private static native void registerNatives();

registerNatives函数前面有native关键字修饰,Java中,用native关键字修饰的函数表明该方法的实现并不是在Java中去完成,而是由C/C++去完成,并被编译成了.dll,由Java去调用。方法的具体实现体在dll文件中,对于不同平台,其具体实现应该有所不同。用native修饰,即表示操作系统,需要提供此方法,Java本身需要使用。具体到registerNatives()方法本身,其主要作用是将C/C++中的方法映射到Java中的native方法,实现方法命名的解耦。

既然如此,可能有人会问,registerNatives()修饰符为private,且并没有执行,作用何以达到?其实,在Java源码中,此方法的声明后有紧接着一段静态代码块:

1 private static native void registerNatives();
2 static {
3     registerNatives();
4 }

3.protected native Object clone() throws CloneNotSupportedException;

看,clone()方法又是一个被声明为native的方法,因此,我们知道了clone()方法并不是Java的原生方法,具体的实现是有C/C++完成的。clone英文翻译为"克隆",其目的是创建并返回此对象的一个副本。形象点理解,这有一辆科鲁兹,你看着不错,想要个一模一样的。你调用此方法即可像变魔术一样变出一辆一模一样的科鲁兹出来。配置一样,长相一样。但从此刻起,原来的那辆科鲁兹如果进行了新的装饰,与你克隆出来的这辆科鲁兹没有任何关系了。你克隆出来的对象变不变完全在于你对克隆出来的科鲁兹有没有进行过什么操作了。Java术语表述为:clone函数返回的是一个引用,指向的是新的clone出来的对象,此对象与原对象分别占用不同的堆空间。

明白了clone的含义后,接下来看看如果调用clone()函数对象进行此克隆操作。

首先看一下下面的这个例子:

 1 package com.corn.objectsummary;
 2 
 3 import com.corn.Person;
 4 
 5 public class ObjectTest {
 6 
 7     public static void main(String[] args) {
 8 
 9         Object o1 = new Object();
10         // The method clone() from the type Object is not visible
11         Object clone = o1.clone();
12     }
13 
14 }

例子很简单,在main()方法中,new一个Oject对象后,想直接调用此对象的clone方法克隆一个对象,但是出现错误提示:"The method clone() from the type Object is not visible"

why? 根据提示,第一反应是ObjectTest类中定义的Oject对象无法访问其clone()方法。回到Object类中clone()方法的定义,可以看到其被声明为protected,估计问题就在这上面了,protected修饰的属性或方法表示:在同一个包内或者不同包的子类可以访问。显然,Object类与ObjectTest类在不同的包中,但是ObjectTest继承自Object,是Object类的子类,于是,现在却出现子类中通过Object引用不能访问protected方法,原因在于对"不同包中的子类可以访问"没有正确理解。

"不同包中的子类可以访问",是指当两个类不在同一个包中的时候,继承自父类的子类内部且主调(调用者)为子类的引用时才能访问父类用protected修饰的成员(属性/方法)。 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员。!(super关键字除外)

于是,上例改成如下形式,我们发现,可以正常编

<think>嗯,用户需要解决Java程序中的"NoSuchMethodError"错误,具体是找不到ImmutableMap.of方法。用户提供了四个引用内容,但核心问题是版本冲突。 用户的问题描述得很清楚,但是引用中有干扰信息(比如Azkaban和Flume的报错)。用户真正需要的是解决Guava版本冲突的通用方法。 从报错信息看,用户的Java代码调用了ImmutableMap.of方法,但实际加载的Guava jar包中没有这个方法。这通常发生在: 1. 项目依赖了多个Guava版本(常见于Maven/Gradle项目) 2. 低版本Guava覆盖了高版本(ImmutableMap.of是Guava 21+才有的) 3. 容器环境自带低版本Guava(比如Tomcat/lib里有旧jar) 解决方案路径应该分三步走: 第一步确认版本:用find命令搜索所有guava*.jar,检查版本号。引用[3]提到的方法很实用,但需要补充版本检查细节。 第二步解决冲突:重点在依赖管理。如果是Maven项目,要分析依赖树,用mvn dependency:tree命令,然后在高版本依赖里显式声明Guava版本。用户可能不知道Maven的dependencyManagement技巧。 第三步验证方案:需要强调clean install的重要性,很多开发者会忽略这个步骤。对于容器环境,要特别提醒移除WEB-INF/lib下的旧jar包。 用户可能是中级Java开发者,知道排查依赖但不太熟悉冲突解决技巧。回复时要避免深入Maven机制,给出可操作的命令行示例就好。那个"Too many arguments"的错误示例特别重要,要保留在回复里。 最后生成的提问要引导用户反馈环境细节(比如构建工具),这对精准解决问题很关键。不过用户没提环境信息,可能需要后续追问。 </think>好的,这个问题通常是由于项目中存在多个不兼容的 **Guava** 库版本冲突导致的。`ImmutableMap.of()` 方法的重载形式(支持多个键值对)是在 Guava 库的较新版本(通常是 v21.0 及以上)中引入的。当你的代码编译时依赖了较新的 Guava 版本(包含所需的方法),但在运行时加载的却是旧版本的 Guava(不包含该方法),就会抛出 `java.lang.NoSuchMethodError: com.google.common.collect.ImmutableMap.of` 错误。 以下是解决此问题的详细步骤: 1. **确认 Guava 版本冲突:** * 查看错误栈信息:错误信息本身 (`java.lang.NoSuchMethodError: com.google.common.collect.ImmutableMap.of(...)`) 已经明确指出了缺少的方法签名。比较关键的点是它指出了需要几个参数的重载方法找不到(例如错误中的 `... (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;...)` 表示一个接收多个键值对参数的重载)。 * **查找项目中的 Guava Jar 包:** * 在你的应用程序部署环境(如 Tomcat 的 `WEB-INF/lib` 目录、Spark 的 `jars/` 目录、Flink 的 `lib/` 目录或应用程序的启动类路径中),使用命令查找所有 `guava-*.jar` 文件: ```bash find /path/to/your/app -name "guava-*.jar" ``` * 仔细检查找到的每一个 `guava-xx.xx.jar` 文件的文件名,确定其具体版本号 (xx.xx)。 2. **解决版本冲突:** * **统一依赖版本 (推荐且根治):** 这是最根本的解决方法。 * **Maven 项目:** 在你的 `pom.xml` 中,确保所有模块都明确声明并使用**同一个**且**足够新**的 Guava 版本(至少 >= v21.0,建议使用较新稳定版如 v31.1-jre 或 v32.1.3-jre)。使用 `<dependencyManagement>` 或直接在高层级模块中声明依赖来统一版本。 1. 添加或修改 Guava 依赖: ```xml <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.1.3-jre</version> <!-- 使用最新稳定版 --> </dependency> ``` 2. 运行 `mvn dependency:tree` 命令分析依赖关系: ```bash mvn dependency:tree -Dincludes=com.google.guava:guava ``` 3. 查看输出,找出哪些其他依赖也引入了 Guava(通常版本较旧)。对于这些依赖导致的冲突: * **排除旧版本:** 在你的项目的 `<dependency>` 节点中,对引入冲突 Guava 的依赖项添加 `<exclusions>`: ```xml <dependency> <groupId>some.group.id</groupId> <artifactId>conflicting-artifact</artifactId> <version>...</version> <exclusions> <exclusion> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> </exclusion> </exclusions> </dependency> ``` * **强制版本 (谨慎):** 在 Maven 的 `<properties>` 中定义 `guava.version` 并在所有相关依赖处引用,或者使用 Maven Enforcer 插件强制版本统一。 4. 执行 `mvn clean install` 确保依赖更新。 * **Gradle 项目:** 在 `build.gradle` 中做类似操作。 1. 声明需要的 Guava 版本: ```groovy dependencies { implementation 'com.google.guava:guava:32.1.3-jre' // 使用最新稳定版 } ``` 2. 运行 `gradle dependencies --configuration runtimeClasspath | grep guava` (或类似命令) 查看依赖树。 3. 排除冲突: ```groovy dependencies { implementation('some.group.id:conflicting-artifact:...') { exclude group: 'com.google.guava', module: 'guava' } implementation 'com.google.guava:guava:32.1.3-jre' } ``` 4. 使用 `gradle clean build`。 * **其他构建工具/手动管理 Jar 包:** 确保应用程序的类路径 (Classpath) 中**只存在一个**符合版本要求的 Guava Jar 包(>= v21.0)。手动删除所有旧版本(如 `guava-15.0.jar`, `guava-16.0.1.jar`, `guava-18.0.jar`, `guava-20.0.jar` 等)的副本。确保所需版本(如 `guava-32.1.3-jre.jar`)在类路径中且唯一。 * **降级代码 (不推荐):** 如果你的应用程序代码直接调用了需要过多参数的 `ImmutableMap.of(...)` 方法,并且升级依赖库版本困难,可以考虑修改代码,避免使用这个特定的重载方法。例如,使用 `ImmutableMap.builder()` 或拆分成多个 `.of(...)` 链式调用(每个 `.of` 支持最多5对参数)。**但是,这通常只是规避了表面问题,根本的版本冲突风险依然存在,强烈建议优先采用统一版本的方法。** 3. **清理和重启:** * 在进行依赖调整或 Jar 包替换后,**务必清理**构建输出目录(如 Maven 的 `target/`, Gradle 的 `build/`)和应用程序服务器的缓存目录(如 Tomcat 的 `work/` 和 `temp/`)。 * 重新构建你的应用程序。 * 重新启动你的应用程序服务器或进程。 4. **验证:** * 再次运行触发错误的操作,确认 `java.lang.NoSuchMethodError` 是否消失。 **关键要点总结:** 1. **核心原因:** 运行时加载的 Guava 版本低于编译时使用的版本,导致找不到新引入的 `ImmutableMap.of` 重载方法。 2. **根本解决:** 确保项目依赖树和运行时环境中的 **Guava 版本统一且足够新** (>= v21.0)。 3. **排查手段:** 使用 `find` 命令查找 Jar,使用构建工具(`mvn dependency:tree`, `gradle dependencies`)分析依赖冲突。 4. **解决方法:** 在构建工具中**声明所需版本**并对冲突依赖进行**排除** (`<exclusions>`/`exclude`),或手动管理 Jar 包**移除旧版本保留唯一新版本**。 5. **必要步骤:** **清理构建缓存和服务器缓存**后重启。 这种类路径上的 Jar 包版本冲突是 Java 开发中常见的问题,Guava 由于其广泛使用和接口变动,尤其容易出现这类问题[^1][^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值