前言
今天去了一家國內(nèi)領(lǐng)先的可視化智能硬件公司面試。面試的我是技術(shù)總監(jiān)畸颅。為人和藹没炒,和他交談中犯戏,我還有一股緊張笛丙。面試中骨稿,能感覺他功力深厚,同時(shí)也學(xué)到了很多東西形耗。個(gè)人感覺激涤,我對自己的面試結(jié)果不是很滿意倦踢。技術(shù)總監(jiān)問的問題比較深入辱挥,也是我平時(shí)比較疏忽的知識點(diǎn)。
關(guān)于Int類型的理解
面試官問我int類型占幾個(gè)字節(jié)晤碘。我是這樣說的: "占4
個(gè)字節(jié),在內(nèi)存中占32
位功蜓≡耙可能不同的操作系統(tǒng)占的字節(jié)不一樣。" 我真的是強(qiáng)行裝逼式撼,給自己挖坑童社。面試官說:"為什么不一樣"。 然后我說:"我記得博客上面是這樣說的端衰。"
可能是面試官說的意思是在Java
語言中int
類型占幾個(gè)字節(jié)叠洗。而我印象中的那篇博客說的是int
類型跟OS
有關(guān),所以面試的要老老實(shí)實(shí)回答問題旅东。
操作系統(tǒng)是
32
位/64
位灭抑,尋址空間不同。尋址空間一般指的是CPU對于內(nèi)存尋址的能力抵代。也就是最多用到多少內(nèi)存的一個(gè)問題荤牍。32
位的CPU
一次就能處理4
個(gè)字節(jié),64位的CPU
一次可以處理8
個(gè)字節(jié)同辣。int
類型在32
位和64
位系統(tǒng)中都是4
個(gè)字節(jié)。Java
程序不是直接運(yùn)行在OS
上,而是運(yùn)行在JVM
上伏穆,JVM
將class
程序在不同OS
上的基礎(chǔ)類型長度固定涛酗,也就是int
類型就是4
個(gè)字節(jié)。所有平臺上的JVM
向上提供給Java
字節(jié)碼程序的接口完全相同,但向下適應(yīng)不同平臺的接口則互不相同。
Int類型的使用
忘記是我主動拋出Int類型的使用聚至,還是面試官給我拋出的贷币。請?jiān)徫业挠浶蕴盍讼就佟K圆灰獜?qiáng)行顯擺自己的實(shí)力亡呵,你在技術(shù)總監(jiān)面前什么都不是。我舉的是二分法,尋找中間元素下標(biāo)的例子。" int mid = (a + b) / 2
铸题。int類型的范圍是-2^31 ~ 2^31 - 1
驹针。我是這樣說的:a
如果是一個(gè)足夠大的int
類型數(shù)據(jù),b
如果也是一個(gè)足夠大的int
類型數(shù)據(jù)。那么a + b
數(shù)據(jù)的范圍肯定超過了int
類型的范圍,會造成內(nèi)存泄露。我的做法是改成 int mid = a + (b - a) / 2
。這樣可以避免造成內(nèi)存的泄露七蜘,同時(shí)減少了內(nèi)存的開銷损搬。"
說出這個(gè)答案,我心中一陣竊喜。然后面試官又給我拋出了這樣的問題,“那你為什么不用int mid = a /2 + b/2
"吠架。但是我覺得這個(gè)問題還好,不是特別難。我就說: "這種做法的性能沒有我的好,因?yàn)?a / 2
做了一次運(yùn)算赶诊,然后 b / 2
又做了一次運(yùn)算寓调,然后把他們加在一起又做了一次運(yùn)算痛悯,內(nèi)存開銷比較大驯耻。"
然后面試官說可缚,“這種回答并不能說服我,可能你的做法性能上確實(shí)比較好描姚,但是根本原因是內(nèi)存開銷的問題嗎澄步?”武氓。當(dāng)時(shí)我就懵了弱睦,不知道說什么了况木。最后面試官告訴了答案:“計(jì)算機(jī)不擅長做除法運(yùn)算尸疆!”
所有數(shù)字在計(jì)算機(jī)底層都是以二進(jìn)制存在的筐咧。計(jì)算機(jī)以補(bǔ)碼的形式保存所有的整數(shù)。計(jì)算機(jī)不擅長做除法。除法一般都是減法和移位的綜合體碉怔。
-
Java
支持的位運(yùn)算符有7
個(gè):-
&
:按位與烘贴。當(dāng)兩位同時(shí)為1
時(shí)才返回1
。 -
|
:按位或撮胧。只要有一位為1
即可返回1
桨踪。 -
~
:按位非。單目運(yùn)算符芹啥,將操作數(shù)的每個(gè)位(包括符號位)全部取反锻离。 -
^
:按位異或铺峭。當(dāng)兩位相同時(shí)返回0
,不同時(shí)返回1
汽纠。 -
<<
:左移運(yùn)算符卫键。 -
>>
:右移運(yùn)算符。 -
>>>
:無符號位右移運(yùn)算符虱朵。比如-5>>>2
莉炉,-5
無符號右移動2
位后,左邊空出2
位后碴犬,空出來的2
位用0
去補(bǔ)充絮宁。
-
突然想到
int mid = a / 2 + b / 2
這種做法還有其他的問題,就是如果a
和b
都是一個(gè)偶數(shù)服协,這樣得出的結(jié)果不會有影響绍昂,這樣假設(shè)a = 2
,b = 2
蚯涮,那么算式1
治专,算式2
得出的結(jié)論mid
都是等于2
。 那么假如a = 1
,b = 3
遭顶,算式1
得出的結(jié)論是2
张峰,算式2
得出的結(jié)論是1
。顯而易見棒旗,算式2
會造成精度缺失喘批,最后就會導(dǎo)致二分法錯(cuò)誤。
字符型
- 字符型通常用于表示單個(gè)的字符铣揉,字符型值必須使用單引號括起來饶深,
Java
語言使用16
位的Unicode
字符集作為編碼方式,而Unicode
被設(shè)計(jì)成支持世界上任何書面語言的字符逛拱,包括中文字符敌厘,因此Java
程序支持各種語言的字符。
表單重復(fù)提交
后面朽合,我就不具體講和面試官的細(xì)節(jié)了俱两。直接概要出面試官拋出的問題和復(fù)盤分析。
我們在添加數(shù)據(jù)的時(shí)候曹步,如果表單重復(fù)提交宪彩,肯定會造成數(shù)據(jù)庫表的數(shù)據(jù)重復(fù)。
客戶端生成
token
讲婚,存在于表單的hidden
域尿孔,接著把token
存入session
中。表單提交后,后臺比較表單的token
和session
中的token
活合,如果相等的話雏婶,就表示表單沒有重復(fù)提交,如果不相等的話芜辕,證明表單重復(fù)提交尚骄。如果表單沒有重復(fù)提交的話,就把session
中的token
清空侵续。假如發(fā)送100
條請求倔丈,第一條請求肯定是會通過的,后面的99
條請求會因?yàn)?code>session中的token
為null
状蜗,造成請求失敗需五,從而防止了表單重復(fù)提交。當(dāng)客戶端輸入的數(shù)據(jù)提交到后臺轧坎,后臺添加的數(shù)據(jù)會與數(shù)據(jù)庫表中的數(shù)據(jù)進(jìn)行比較宏邮,如果不重復(fù)則寫入,可以防止表單重復(fù)提交缸血。如果
2
個(gè)對象相同的話蜜氨,那么它們的hashCode
肯定相同。如果2個(gè)對象的hashCode
相同捎泻,那么它們不一定是相同的對象飒炎。重寫equals()
方法,必須要重寫hashCode()
方法笆豁。數(shù)據(jù)庫中郎汪,增加一個(gè)
int
類型的hash
值字段,加上索引闯狱。將所有的業(yè)務(wù)信息(排除主鍵id
煞赢,create_time
之類的字段)計(jì)算hash
值。往表里面添加數(shù)據(jù)的時(shí)候哄孤,先計(jì)算hash
值照筑,然后用hash
值去數(shù)據(jù)庫中查詢,查詢結(jié)果為多個(gè)的時(shí)候瘦陈,可以去做精確比較朦肘。
用hashCode()
解決數(shù)據(jù)重復(fù),是一個(gè)不錯(cuò)的選擇双饥。
MySQL數(shù)據(jù)庫
基本概念
DML
(Data Manipulation Language
): 數(shù)據(jù)操作語言,主要由insert
,delete
,update
三個(gè)關(guān)鍵字完成弟断。DDL
(Data Definition Language
): 數(shù)據(jù)定義語言咏花,主要由create
,alter, drop
,truncate
這四個(gè)關(guān)鍵字完成。DCL
(Data Control Language
):數(shù)據(jù)控制語言,主要由grant
(準(zhǔn)許)和revoke
(撤銷)兩個(gè)關(guān)鍵字組成昏翰。primary key
:PRIMARY KEY(id)
, 有2
個(gè)作用苍匆,一是約束作用(constraint
),用來規(guī)范一個(gè)存儲主鍵和唯一性棚菊,但是同時(shí)也在該key
上建立了一個(gè)索引浸踩。unique key
:UNIQUE KEY deal_id_uk(deal_id)
,CONSTRAINT deal_id_uk UNIQUE(deal_id)
兩種寫法都可以,有2
個(gè)作用统求。一是規(guī)范數(shù)據(jù)的唯一性检碗,建立索引。key
: 建立索引码邻。index
:create index deal_id_idx on employees(deal_id)
折剃。index
是數(shù)據(jù)庫的物理結(jié)構(gòu),索引總是屬于數(shù)據(jù)表像屋,當(dāng)它和數(shù)據(jù)表一樣都是屬于數(shù)據(jù)庫對象怕犁。創(chuàng)建索引的唯一作用是加速對表的查詢,索引通過使用快速路徑訪問方法來快速定位數(shù)據(jù)己莺,從而減少了磁盤的I/O
奏甫。
基礎(chǔ)概念說完了,怎么去規(guī)范的創(chuàng)建數(shù)據(jù)表呢凌受。再此部分引用阿里巴巴Java
開發(fā)手冊的的建表規(guī)約索引規(guī)約阵子。
表達(dá)是與否的概率的字段,必須使用
is_xxx
的方式命名胁艰,數(shù)據(jù)類型是unsigned
tinyint
(0-255
)款筑,1
代表刪除,0
表示未刪除腾么。任何字段如果是非負(fù)數(shù)奈梳,必須是unsigned
。char
的長度是固定的解虱,而varchar
的長度是可變的攘须,比如我存一個(gè)字符串,叫“cmazxiaoma”
,對于char(20)
來說表示你存儲的字符將占20
個(gè)字節(jié)殴泰,其中包括10
個(gè)空閑字符于宙,而同樣varchar(20)
只會占用10
個(gè)字節(jié),20
個(gè)字節(jié)只是最大值而已悍汛。varchar
是可變長字符串捞魁,不預(yù)先分配空間,長度不要超過5000
离咐,如果存儲長度大于此值谱俭,定義字段類型為text
奉件,獨(dú)立出來一張表。用主鍵去對應(yīng)昆著,避免影響其他字段的索引效率县貌。字段允許冗余尤勋,以提高查詢性能锐极,但必須考慮數(shù)據(jù)的一致,冗余字段應(yīng)該遵循:不是頻繁修改的字段橡类,不是
varchar
超長字段接谨,更不是text
字段摆碉。業(yè)務(wù)上具有唯一特性的字段,即使是多個(gè)字段的組合疤坝,也必須建成唯一索引兆解。不要以為唯一索引影響
insert
的速度,這個(gè)速度的損耗可以忽略跑揉,但是提高查詢速度是明顯的锅睛,另外,即使在應(yīng)用層做了非常完善的校驗(yàn)控制历谍,只要沒有唯一索引现拒,根據(jù)墨菲定律,必然有臟數(shù)據(jù)的產(chǎn)生望侈。區(qū)分度與索引長度的權(quán)衡印蔬。索引長度越低,索引在內(nèi)存中占的長度越小脱衙,排序越快侥猬。然而區(qū)分度就很低了,不利于查找捐韩。索引長度越長退唠,區(qū)分度越高,雖然利于查找荤胁,但是索引在內(nèi)存占的空間就越多了瞧预。
數(shù)據(jù)庫三大范式
- 列名不可再分,保持原子性仅政。
- 每一個(gè)非主屬性必須依賴于主鍵垢油。消除部分函數(shù)依賴
- 除了主鍵之外,其他屬性之間不能相互依賴圆丹。消除傳遞依賴滩愁。
尾言
心之所向,素履以往辫封。生如逆旅惊楼,一葦以航玖瘸。