java基础(面试必会)
一、Object类
1.核心了解
Object类是最顶层,万物之父,提供了通用基础方法,供所有子类重写或直接使用
2.常用核心方法
(1)equals
默认实现:本质是利用==来比较对象内存地址
作用:判断两个对象逻辑是否相等
重写实现:有一些子类会重写该方法,例如String:会先用==比较地址是否相同,如果地址不同,再比较值是否相同
(2)hashCode
默认实现:根据对象内存地址生成整数哈希值,本质是将生成的整数存到哈希结构里面
作用:配合哈希集合(HashSet / HashMap),提升查询,去重效率
官方规定:若要重写equals,必须重写hashCode
equals() == true→ 两个对象hashCode必须相同hashCode相同 →equals可不同(哈希冲突)
(3)toString
默认实现:类名@十六进制哈希码 例:User@15db9742
作用:打印对象信息
重写实现:一般都会重写,返回对象属性内容
二、为什么重写equals方法必须要重写hashCode方法
1.官方规定
Java 官方在 Object 类的文档里明确规定了 3 条规则:
- 如果两个对象调用
equals()相等 - 那么它们调用
hashCode()必须返回相同的值 - 反过来不要求:hashCode 相同,equals 不一定相等(因为会有哈希冲突,同一个映射地址到哈希结构里面的可能是多个)
不遵守这个规则,会出现bug
2.为什么会出现bug(核心原理)
HashSet / HashMap 这类哈希集合判断两个对象是否重复的逻辑是:
- 先比较
hashCode() - 如果 hashCode 不同 → 直接判定不是同一个对象
- 如果 hashCode 相同 → 再用
equals()比较
如果不理解,这里举个实际场景:你想要通过id来判断是否是一个用户
前提:只重写了equals,不重写hashCode
假设你有一个 User 类,只重写了 equals(按 id 判断相等),没重写 hashCode
User u1 = new User(1);
User u2 = new User(1);
这时调用equals方法:u1.equals(u2) 会返回 true(因为你重写了)
那调用hashCode方法:u1.hashCode() 和 u2.hashCode() 会发现两者不同(默认是内存地址)
若将两个对象存进去:set.add(u1); set.add(u2); 发现两个对象都存进去了!
但是我们要的效果是id相同就是同一个用户,那么存到hashSet,不应该只存一个吗?
原因就是:HashSet 会先看 hashCode ,发现不一样,直接认为是两个不同对象,根本不会调用你的 equals。
这就违背了你的业务逻辑:你认为 id 相同就是同一个人,但集合不认。
因此,我们在重写了equals之后,一定要重写hashCode
3.默认的hashCode / equals是什么?
Java 所有对象默认继承 Object 的方法:
equals():默认比较 内存地址hashCode():默认返回 内存地址计算的数字
所以默认情况下:
equals相等 → 地址相同 →hashCode一定相等
但一旦你重写 equals 改变了相等规则(比如按属性相等),默认的 hashCode 就不再遵守约定了
更形象点的说法:本来我判断两个东西是否长得一样,是通过看整体一不一样equals--->那么就只有一个东西可以存进hashSet,但是当你改了equals,改成通过看这两个东西是不是都有个尾巴来判断是不是一样的--->那么本来逻辑上是只能存一个,但是hashSet通过hashCode发现这两东西其实并不一一样,就会把两个东西都存到hashSet里面
三、包装类
1.为什么会有包装类的存在?
- java说好了万物皆对象,但是基本数据类型不是
- 基本数据类型不能进行运算
2.定义
java为八种基本数据类型提供对应的引用类型,就是包装类(基本类型:简单、没有方法;包装类:对象,有方法,可存集合)
| 基本类型 | 包装类(java.lang) |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer(高频) |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
3.自动装箱 & 自动拆箱
(1)装箱
基本类型--->包装类
Integer a = 10; // 自动装箱,等价于 Integer.valueOf(10)
(2)拆箱
包装类--->基本类型
Integer a=20;
int b = a; // 自动拆箱,等价于 a.intValue()
4.128陷阱(重点)
讲128陷阱之前先来说一下:Integer常量池
Integer常量池:Java为了减少对象创建,节省内存,对Integer类型的部分数值做了缓存,缓存范围是-128~127之间的Integer对象
若创建Integer对象在这个范围内,直接会返回缓存里面已有的对象(若数值本身一致,多个对象会指向同一个地址),若超出这个范围就会创建新对象

基于常量池的知识来说一下128陷阱:

i==j 为 true:因为 127 在缓存范围内,i 和 j 指向同一个缓存对象,内存地址相同。
k==l 为 false:因为 128 超出缓存范围,k 和 l 是两个不同的 new Integer(128) 对象,内存地址不同。
但是如果用equals来判断的话,无论是否在缓存范围里,只要数值相同,equals就会返回true
5.拆装箱练习
public class Main {
public static void main(String[] args) {
int a = 10;
int b = 10;
Integer a1 = 10;
Integer b1 = 10;
Integer a2 = new Integer(10);
Integer b2 = new Integer(10);
System.out.println(a==b);
System.out.println(a1==b1);
System.out.println(a2==b2);
System.out.println(a1 == a);
System.out.println(a1.equals(a));
System.out.println(a1 == a2);
System.out.println(a == a2);
}
}
输出结果+分析:
(1)==在基本数据类型里面比较的是数值,两者数值一样因此是true
(2)==在引用数据类型里面比较的是地址,但是在常量池里面,两者指向同一个地址,因此返回true
(3)==在引用数据类型里面比较的是地址,a2和b2都是通过new来创建的,每次new都会创建一个全新的对象,和缓存无关,两个对象的地址不同,所以结果返回false
(4)==两边类型不一致时,Integer会进行自动拆箱,变成int类型,因此变成了比较基本数据类型,因此返回true
(5)equals方法是Integer重写过的,用来比较数值是否相同,Integer会进行自动拆箱,变成int类型,因此比较两者数值。所以返回true
(6)==在引用数据类型里面比较的是地址值,a1是缓存中的Integer对象(堆里共享区域),a2是new出来的新对象(堆里面普通区域),两者内存地址不一样,因此返回false
(7)==两边类型不一致时,Integer会进行自动拆箱,变成int类型,比较后数值相等,因此返回true
更多推荐

所有评论(0)