舊時(shí)王謝堂前燕户誓,
飛入尋常百姓家嫁赏。
小編將帶你們一起分析阿里巴巴Java開發(fā)手冊(cè)磨隘!
背景
阿里巴巴Java開發(fā)手冊(cè)是阿里巴巴集團(tuán)技術(shù)團(tuán)隊(duì)的集體智慧結(jié)晶和經(jīng)驗(yàn)總結(jié),以Java開發(fā)者為中心視角蹋订,劃分為編程規(guī)約率挣、異常日志、單元測(cè)試露戒、安全規(guī)約椒功、工程結(jié)構(gòu)、MySQL數(shù)據(jù)庫(kù)六個(gè)維度智什。手冊(cè)的愿景是碼出高效动漾、碼出質(zhì)量、效率優(yōu)先荠锭、質(zhì)量為本旱眯。
目的
之所以要寫這個(gè)系列的文章,首先是學(xué)習(xí)與總結(jié)证九,其次是思考與理解删豺,更是分享與交流,手冊(cè)中的每一條每一項(xiàng)都有其背后隱藏的原理與經(jīng)驗(yàn)愧怜,我們看到的只是冰山一角呀页,深入挖掘其背后的知識(shí)有益于更深刻的理解,并在自己實(shí)際編程中提高自己的基本技術(shù)素養(yǎng)拥坛。
主題
- 【推薦】表達(dá)異常的分支時(shí)蓬蝶,少用 if-else 方式尘分,這種方式可以改寫成:
if (condition) {
...
return obj;
}
// 接著寫 else 的業(yè)務(wù)邏輯代碼;
說(shuō)明:如果非得使用 if()...else if()...else...方式表達(dá)邏輯,【強(qiáng)制】避免后續(xù)代碼維
護(hù)困難丸氛,請(qǐng)勿超過(guò) 3 層培愁。
正例:超過(guò) 3 層的 if-else 的邏輯判斷代碼可以使用衛(wèi)語(yǔ)句、策略模式缓窜、狀態(tài)模式等來(lái)實(shí)現(xiàn)定续, 其中衛(wèi)語(yǔ)句示例如下:
public void today() {
if (isBusy()) {
System.out.println(“change time.”); return;
}
if (isFree()) {
System.out.println(“go to travel.”);
return;
}
System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);
return;
}
我們?cè)噲D通過(guò)例子來(lái)分析下上面的規(guī)則,在分析之前雹洗,我們先明確在Java里香罐,默認(rèn)的equals和hashCode方法的實(shí)現(xiàn)卧波,以及把一個(gè)元素放入散列集合(HashSet时肿、HashMap等)時(shí),散列集合對(duì)equals和hashCode方法的判定規(guī)則港粱。
Java對(duì)象中默認(rèn)的equals(Object obj)方法用來(lái)判斷兩個(gè)對(duì)象是否“相同”螃成,如果“相同”則返回true,否則返回false查坪。hashCode()方法返回一個(gè)int數(shù)寸宏,這個(gè)整數(shù)值是將該對(duì)象的內(nèi)部地址轉(zhuǎn)換成一個(gè)整數(shù)返回的 。
在散列集合中存儲(chǔ)一個(gè)對(duì)象時(shí)偿曙,先進(jìn)行hashCode的比較氮凝,如果hashCode不想等,則直接放入望忆,否則繼續(xù)進(jìn)行equals的比較罩阵,equals不相等才放入,equals不相等就直接丟棄了启摄。
理解
- 如果只重寫equals而不重寫hashCode會(huì)導(dǎo)致什么問(wèn)題稿壁?
package com.test;
import java.util.HashSet;
import java.util.Set;
public class OverrideEqualsTest {
public static void main(String[] args) {
Set<Point> set = new HashSet<Point>();
Point p1 = new Point(1, 1);
Point p2 = new Point(1, 1);
System.out.println("p1.equals(p2):" + p1.equals(p2));
set.add(p1);
set.add(p2);
set.add(p1);
System.out.println("set.size():" + set.size());
for (Point p : set) {
System.out.println(p);
}
}
static class Point {
private int x;
private int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Point other = (Point) obj;
if (x != other.x) {
return false;
}
if (y != other.y) {
return false;
}
return true;
}
@Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
}
}
運(yùn)行結(jié)果:
p1.equals(p2):true
set.size():2
Point[x=1,y=1]
Point[x=1,y=1]
分析:由于沒有重寫hashCode方法,p1和p2對(duì)象默認(rèn)的hashCode方法返回的兩個(gè)對(duì)象地址轉(zhuǎn)換的整數(shù)肯定不同歉备,所以p1和p2都可以放入set中傅是,所以這并不是我們期望的結(jié)果。
- ?如果只重寫hashCode而不重寫equals又會(huì)導(dǎo)致什么問(wèn)題蕾羊?
package com.test;
import java.util.HashSet;
import java.util.Set;
public class OverrideHashCodeTest {
public static void main(String[] args) {
Set<Point> set = new HashSet<Point>();
Point p1 = new Point(1, 1);
Point p2 = new Point(1, 1);
System.out.println("p1.equals(p2):" + p1.equals(p2));
set.add(p1);
set.add(p2);
set.add(p1);
System.out.println("set.size():" + set.size());
for (Point p : set) {
System.out.println(p);
}
}
static class Point {
private int x;
private int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
final int prime = 3L;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
}
}
運(yùn)行結(jié)果:
p1.equals(p2):false
set.size():2
Point[x=1,y=1]
Point[x=1,y=1]
分析:由于沒有重寫equals方法喧笔,p1和p2對(duì)象的默認(rèn)的equals方法通過(guò)“==”來(lái)比較,而p1和p2是兩個(gè)不同的對(duì)象龟再,所以p1和p2都可以放入set中书闸,所以這也不是我們期望的結(jié)果。
所以綜上吸申,當(dāng)我們同時(shí)重寫equals和hashCode方法后梗劫,才能在散列集合操作中得到一致性的結(jié)果享甸。
- 對(duì)象放入散列集合后,又修改了影響hashCode的值梳侨,后果蛉威?
package com.test;
public class Test {
public static void main(String[] args) {
Point p1 = new Point(1, 1);
Point p2 = new Point(2, 2);
set.add(p1);
set.add(p2);
System.out.println("set.size():" + set.size());
p2.setY(3);
set.remove(p1);
set.remove(p2);
System.out.println("set.size():" + set.size());
}
}
運(yùn)行結(jié)果:
set.size():2
set.size():1
分析:由于在執(zhí)行期間,修改了p2對(duì)象的y值走哺,導(dǎo)致p2對(duì)象的hashCode返回值有變化蚯嫌,所以hashset的remove方法將找不到新的hashCode所映射的對(duì)象,導(dǎo)致內(nèi)存泄漏丙躏。