Skip to content

Commit da1052e

Browse files
committed
[docs improve] Java基础常见知识&面试题总结(中)&Java 内存区域详解内容完善
1 parent 114a705 commit da1052e

12 files changed

+483
-542
lines changed

docs/cs-basics/network/osi&tcp-ip-model.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础
7474

7575
**网络层负责为分组交换网上的不同主机提供通信服务。** 在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组和包进行传送。在 TCP/IP 体系结构中,由于网络层使用 IP 协议,因此分组也叫 IP 数据报,简称数据报。
7676

77-
注意 ⚠️**不要把运输层的“用户数据报 UDP”和网络层的“IP 数据报”弄混**
77+
⚠️注意 **不要把运输层的“用户数据报 UDP”和网络层的“IP 数据报”弄混**
7878

7979
**网络层的还有一个任务就是选择合适的路由,使源主机运输层所传下来的分株,能通过网络层中的路由器找到目的主机。**
8080

docs/java/basis/java-basic-questions-01.md

Lines changed: 12 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ Java 中的注释有三种:
190190
> - 在类,方法和变量修饰符中,从 JDK8 开始引入了默认方法,可以使用 `default` 关键字来定义一个方法的默认实现。
191191
> - 在访问控制中,如果一个方法前没有任何修饰符,则默认会有一个修饰符 `default`,但是这个修饰符加上了就会报错。
192192
193-
注意 ⚠️:虽然 `true`, `false`, 和 `null` 看起来像关键字但实际上他们是字面值,同时你也不可以作为标识符来使用。
193+
⚠️注意 :虽然 `true`, `false`, 和 `null` 看起来像关键字但实际上他们是字面值,同时你也不可以作为标识符来使用。
194194
195195
官方文档:[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html)
196196
@@ -423,138 +423,6 @@ public class SuperSuperMan extends SuperMan {
423423
}
424424
```
425425
426-
### == 和 equals() 的区别
427-
428-
**`==`** 对于基本类型和引用类型的作用效果是不同的:
429-
430-
- 对于基本数据类型来说,`==` 比较的是值。
431-
- 对于引用数据类型来说,`==` 比较的是对象的内存地址。
432-
433-
> 因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
434-
435-
**`equals()`** 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。`equals()`方法存在于`Object`类中,而`Object`类是所有类的直接或间接父类,因此所有的类都有`equals()`方法。
436-
437-
`Object` 类 `equals()` 方法:
438-
439-
```java
440-
public boolean equals(Object obj) {
441-
return (this == obj);
442-
}
443-
```
444-
445-
`equals()` 方法存在两种使用情况:
446-
447-
- **类没有重写 `equals()`方法** :通过`equals()`比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 `Object`类`equals()`方法。
448-
- **类重写了 `equals()`方法** :一般我们都重写 `equals()`方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。
449-
450-
举个例子(这里只是为了举例。实际上,你按照下面这种写法的话,像 IDEA 这种比较智能的 IDE 都会提示你将 `==` 换成 `equals()` ):
451-
452-
```java
453-
String a = new String("ab"); // a 为一个引用
454-
String b = new String("ab"); // b为另一个引用,对象的内容一样
455-
String aa = "ab"; // 放在常量池中
456-
String bb = "ab"; // 从常量池中查找
457-
System.out.println(aa == bb);// true
458-
System.out.println(a == b);// false
459-
System.out.println(a.equals(b));// true
460-
System.out.println(42 == 42.0);// true
461-
```
462-
463-
`String` 中的 `equals` 方法是被重写过的,因为 `Object` 的 `equals` 方法是比较的对象的内存地址,而 `String` 的 `equals` 方法比较的是对象的值。
464-
465-
当创建 `String` 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 `String` 对象。
466-
467-
`String`类`equals()`方法:
468-
469-
```java
470-
public boolean equals(Object anObject) {
471-
if (this == anObject) {
472-
return true;
473-
}
474-
if (anObject instanceof String) {
475-
String anotherString = (String)anObject;
476-
int n = value.length;
477-
if (n == anotherString.value.length) {
478-
char v1[] = value;
479-
char v2[] = anotherString.value;
480-
int i = 0;
481-
while (n-- != 0) {
482-
if (v1[i] != v2[i])
483-
return false;
484-
i++;
485-
}
486-
return true;
487-
}
488-
}
489-
return false;
490-
}
491-
```
492-
493-
### hashCode() 与 equals()
494-
495-
面试官可能会问你:“你重写过 `hashCode()` 和 `equals()`么?为什么重写 `equals()` 时必须重写 `hashCode()` 方法?”
496-
497-
一个非常基础的问题,面试中的重中之重,然而,很多求职者还是会回答不到点子上去。
498-
499-
#### hashCode() 有什么用?
500-
501-
`hashCode()` 的作用是获取哈希码(`int` 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。
502-
503-
`hashCode()`定义在 JDK 的 `Object` 类中,这就意味着 Java 中的任何类都包含有 `hashCode()` 函数。另外需要注意的是: `Object` 的 `hashCode()` 方法是本地方法,也就是用 C 语言或 C++ 实现的,该方法通常用来将对象的内存地址转换为整数之后返回。
504-
505-
```java
506-
public native int hashCode();
507-
```
508-
509-
散列表存储的是键值对(key-value),它的特点是:**能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)**
510-
511-
#### 为什么要有 hashCode?
512-
513-
我们以“`HashSet` 如何检查重复”为例子来说明为什么要有 `hashCode`?
514-
515-
下面这段内容摘自我的 Java 启蒙书《Head First Java:
516-
517-
> 当你把对象加入 `HashSet` 时,`HashSet` 会先计算对象的 `hashCode` 值来判断对象加入的位置,同时也会与其他已经加入的对象的 `hashCode` 值作比较,如果没有相符的 `hashCode`,`HashSet` 会假设对象没有重复出现。但是如果发现有相同 `hashCode` 值的对象,这时会调用 `equals()` 方法来检查 `hashCode` 相等的对象是否真的相同。如果两者相同,`HashSet` 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 `equals` 的次数,相应就大大提高了执行速度。
518-
519-
其实, `hashCode()` 和 `equals()`都是用于比较两个对象是否相等。
520-
521-
**那为什么 JDK 还要同时提供这两个方法呢?**
522-
523-
这是因为在一些容器(比如 `HashMap`、`HashSet`)中,有了 `hashCode()` 之后,判断元素是否在对应容器中的效率会更高(参考添加元素进`HastSet`的过程)!
524-
525-
我们在前面也提到了添加元素进`HastSet`的过程,如果 `HashSet` 在对比的时候,同样的 `hashCode` 有多个对象,它会继续使用 `equals()` 来判断是否真的相同。也就是说 `hashCode` 帮助我们大大缩小了查找成本。
526-
527-
**那为什么不只提供 `hashCode()` 方法呢?**
528-
529-
这是因为两个对象的`hashCode` 值相等并不代表两个对象就相等。
530-
531-
**那为什么两个对象有相同的 `hashCode` 值,它们也不一定是相等的?**
532-
533-
因为 `hashCode()` 所使用的哈希算法也许刚好会让多个对象传回相同的哈希值。越糟糕的哈希算法越容易碰撞,但这也与数据值域分布的特性有关(所谓哈希碰撞也就是指的是不同的对象得到相同的 `hashCode` )。
534-
535-
总结下来就是 :
536-
537-
- 如果两个对象的`hashCode` 值相等,那这两个对象不一定相等(哈希碰撞)。
538-
- 如果两个对象的`hashCode` 值相等并且`equals()`方法也返回 `true`,我们才认为这两个对象相等。
539-
- 如果两个对象的`hashCode` 值不相等,我们就可以直接认为这两个对象不相等。
540-
541-
相信大家看了我前面对 `hashCode()` 和 `equals()` 的介绍之后,下面这个问题已经难不倒你们了。
542-
543-
#### 为什么重写 equals() 时必须重写 hashCode() 方法?
544-
545-
因为两个相等的对象的 `hashCode` 值必须是相等。也就是说如果 `equals` 方法判断两个对象是相等的,那这两个对象的 `hashCode` 值也要相等。
546-
547-
如果重写 `equals()` 时没有重写 `hashCode()` 方法的话就可能会导致 `equals` 方法判断是相等的两个对象,`hashCode` 值却不相等。
548-
549-
**思考** :重写 `equals()` 时没有重写 `hashCode()` 方法的话,使用 `HashMap` 可能会出现什么问题。
550-
551-
**总结**
552-
553-
- `equals` 方法判断两个对象是相等的,那这两个对象的 `hashCode` 值也要相等。
554-
- 两个对象有相同的 `hashCode` 值,他们也不一定是相等的(哈希碰撞)。
555-
556-
更多关于 `hashCode()` 和 `equals()` 的内容可以查看:[Java hashCode() 和 equals()的若干问题解答](https://www.cnblogs.com/skywang12345/p/3324958.html)
557-
558426
### 什么是可变长参数?
559427
560428
Java5 开始,Java 支持定义可变长参数,所谓可变长参数就是允许在调用方法时传入不定长度的参数。就比如下面的这个 `printVariable` 方法就可以接受 0 个或者多个参数。
@@ -670,13 +538,20 @@ Java 中有 8 种基本数据类型,分别为:
670538
671539
这八种基本类型都有对应的包装类分别为:`Byte`、`Short`、`Integer`、`Long`、`Float`、`Double`、`Character`、`Boolean` 。
672540
673-
包装类型不赋值就是 `Null` ,而基本类型有默认值且不是 `Null`。
541+
### 局部类型和包装类型的区别?
674542
675-
另外,这个问题建议还可以先从 JVM 层面来分析。
543+
- 包装类型不赋值就是 `null` ,而基本类型有默认值且不是 `null`。
544+
- 包装类型可用于泛型,而基本类型不可以。
545+
- 基本数据类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 `static` 修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,我们知道对象实例都存在于堆中。
546+
- 相比于对象类型, 基本数据类型占用的空间非常小。
676547
677-
基本数据类型直接存放在 Java 虚拟机栈中的局部变量表中,而包装类型属于对象类型,我们知道对象实例都存在于堆中。相比于对象类型, 基本数据类型占用的空间非常小
548+
⚠️注意 : **基本数据类型存放在栈中是一个常见的误区!** 基本数据类型的成员变量如果没有被 `static` 修饰的话,就存放在堆中
678549
679-
> 《深入理解 Java 虚拟机》 :局部变量表主要存放了编译期可知的基本数据类型 **booleanbytecharshortintfloatlongdouble****对象引用**(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
550+
```java
551+
class BasicTypeVar{
552+
private int x;
553+
}
554+
```
680555
681556
### 包装类型的常量池技术了解么?
682557

0 commit comments

Comments
 (0)