面试 Java 基础八股文十问十答第十四期
作者:程序员小白条,个人博客
相信看了本文后,对你的面试是有一定帮助的!关注专栏后就能收到持续更新!
⭐点赞⭐收藏⭐不迷路!⭐
1)为什么要有 hashCode?
在 Java 中,HashSet是基于哈希表实现的集合,它的添加、删除和查找操作的时间复杂度都是 O(1)。为了实现高效的查找和去重,HashSet使用了哈希表来存储元素。哈希表通过将元素的键(即对象的哈希码)映射到表中的一个位置来存储和检索元素。
在HashSet中,当需要添加一个新元素时,首先会调用该元素的hashCode()方法来获取其哈希码,然后根据哈希码确定其在哈希表中的存储位置。如果该位置上已经存在元素,则会根据equals()方法来判断是否为相同元素,如果相同则不添加,如果不同则处理哈希冲突(通常采用链表或红黑树解决),最终将元素添加到哈希表中。因此,hashCode()方法的作用是确定对象在哈希表中的存储位置,从而加速元素的查找和去重。
2)hashCode() 与 equals() 的相关规定
在 Java 中,hashCode()方法和equals()方法是密切相关的,它们共同决定了对象在集合中的行为。以下是它们之间的相关规定:
- 如果两个对象根据
equals()方法比较是相等的,则它们的哈希码必须相等。换句话说,如果a.equals(b)返回true,那么a.hashCode()和b.hashCode()应该相等。 - 如果两个对象根据
equals()方法比较是不相等的,它们的哈希码不一定要不相等。但是,为了提高哈希表的效率,不同的对象应该尽可能映射到不同的哈希码,以减少哈希冲突的概率。
3)因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
由于hashCode()方法和equals()方法之间的密切关系,如果一个类覆盖了equals()方法,那么它必须同时覆盖hashCode()方法,以保证对象在集合中的行为一致性。如果不遵循这个规定,那么在使用HashSet、HashMap等基于哈希表实现的集合时,就会导致一些意想不到的问题,例如无法正确判断对象是否存在、重复添加相同对象等。因此,为了避免这些问题,覆盖equals()方法的同时,必须确保覆盖hashCode()方法,以维护哈希表的正确性和性能。
4)对象的相等与指向它们的引用相等,两者有什么不同?
- 对象的相等(Equality of Objects)是指对象在逻辑上是否相等,通常由对象的内容决定。例如,两个具有相同属性值的对象被认为是相等的,即使它们在内存中的位置不同。
- 指向对象的引用相等(Reference Equality)是指两个引用变量是否指向内存中的同一个对象。如果两个引用变量指向的是同一个对象,则它们是引用相等的。
简而言之,对象的相等性是根据对象的内容进行比较的,而引用的相等性则是比较引用变量指向的内存地址是否相同。
5)当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
在 Java 中,参数传递是值传递。但需要理解的是,当参数是对象类型时,传递的是对象的引用的副本,而不是对象本身。因此,虽然传递的是引用的副本,但仍然可以通过这个引用来修改对象的属性。这是因为引用指向的对象在内存中是可变的,因此可以在方法内部修改其状态。
当方法修改了对象的属性并返回修改后的结果时,返回的是对象引用的副本,指向的仍然是同一个对象,因此在调用方看来,对象的内容已经改变。这种情况下,虽然传递的是引用的副本,但实际上是通过引用对对象进行了修改。
6)为什么 Java 中只有值传递?
Java 中只有值传递是因为 Java 中的所有参数传递都是按值传递的。这意味着在方法调用时,实际上传递给方法的是参数的副本,而不是参数本身。对于基本数据类型,传递的是它们的值;对于对象类型,传递的是对象引用的副本。
这种设计有助于保证程序的可靠性和安全性。如果 Java 中支持引用传递,那么方法内部对参数进行的修改可能会影响调用方的对象,从而导致意想不到的结果和不确定性。通过值传递,可以确保方法对参数的修改不会影响调用方的对象,保证了程序的可靠性和稳定性。
7)值传递和引用传递有什么区别?
- 值传递(Pass by Value):在值传递中,方法参数传递的是实际数据的副本。这意味着对参数的任何修改都不会影响到原始数据。
- 引用传递(Pass by Reference):在引用传递中,方法参数传递的是实际数据的引用或地址,而不是数据本身。这意味着对参数的修改会影响到原始数据。
Java 中只支持值传递,无论是基本数据类型还是对象类型的参数传递,实际上都是按值传递的。对于对象类型的参数,传递的是对象引用的副本,但这并不是引用传递。因为虽然可以通过这个引用修改对象的属性,但对引用本身的修改并不会影响原始引用,也不会影响方法外部的其他引用。
8)JDK 中常用的包有哪些?
在 Java 开发中,常用的 JDK 包包括但不限于:
java.lang:Java 语言的核心类,提供基本的数据类型包装类、字符串、异常处理等。java.util:提供集合框架、日期时间处理、随机数生成等实用工具类。java.io:提供输入输出流操作,用于文件操作、网络通信等。java.net:提供网络编程相关的类和接口,用于实现网络通信。java.awt和javax.swing:提供图形用户界面(GUI)开发所需的类和接口。
还有其他一些用于特定功能的包,如java.sql用于数据库操作、java.nio用于新的 I/O 操作、java.math用于数学运算等。
9)import java 和 javax 有什么区别?
java包:java包中包含了 Java 标准库中的核心类和接口,提供了 Java 语言的基础功能,例如集合、I/O、网络通信等。这些类通常被认为是 Java 的基础部分。javax包:javax包则包含了 Java 标准库的一些扩展,提供了一些 Java 标准库之外的功能,例如图形用户界面(GUI)、XML 处理、网络安全等。它们通常被认为是 Java 的扩展部分。
虽然javax包提供了一些额外的功能,但它们通常比java包的类更具有特定性,需要额外的库来支持。因此,javax包的使用通常要求额外的配置和依赖,而java包的类则是 Java 核心库的一部分,可以直接使用而无需额外的配置。
10)Java 中 IO 流分为几种?
在 Java 中,IO 流主要分为两种:
- 字节流(Byte Streams):以字节为单位进行读写数据。
InputStream和OutputStream是所有字节流的抽象父类。常见的字节流包括FileInputStream、FileOutputStream用于文件操作,ByteArrayInputStream、ByteArrayOutputStream用于内存操作等。 - 字符流(Character Streams):以字符为单位进行读写数据,通常与字符编码相关联。
Reader和Writer是所有字符流的抽象父类。常见的字符流包括FileReader、FileWriter用于文件操作,BufferedReader、BufferedWriter提供缓冲功能等。
此外,IO 流还可以按照数据的流向分为输入流和输出流,以及按照功能和特性分为节点流和处理流。节点流直接与数据源或目标进行交互,而处理流则包装了其他流,提供了更高级别的功能,如缓冲、数据转换等。
前后端项目 Gitee & Github 累计 3000+ Star,10W+浏览量!⭐点赞⭐收藏⭐不迷路!⭐
智能 AI 旅游推荐平台:https://github.com/luoye6/vue3_tourism_frontend
智能 AI 校园二手交易平台:https://github.com/luoye6/vue3_trade_frontend
GPT 智能图书馆:https://github.com/luoye6/Vue_BookManageSystem