程序員新人上午使用 isXxx 形式定義布爾類(lèi)型,下午就被勸退?
二哥編程知識(shí)星球 (戳鏈接加入)正式上線了,來(lái)和 260 多名 小伙伴一起打怪升級(jí)吧!這是一個(gè) Java 學(xué)習(xí)指南 + 編程實(shí)戰(zhàn)的私密圈子,你可以向二哥提問(wèn)、幫你制定學(xué)習(xí)計(jì)劃、跟著二哥一起做實(shí)戰(zhàn)項(xiàng)目,沖沖沖。
Java 程序員進(jìn)階之路網(wǎng)址:https://tobebetterjavaer.com
小二是新來(lái)的背鍋俠,哦,不不不,新來(lái)的實(shí)習(xí)生。面試過(guò)程中表現(xiàn)得非常不錯(cuò),各種問(wèn)題對(duì)答如流,老板和我都倍感欣慰。
這么優(yōu)秀的人,絕不能讓他浪費(fèi)一分一秒,于是很快,我就給他安排了一個(gè)非常簡(jiǎn)單的任務(wù)。
大家都知道,在日常開(kāi)發(fā)中,我們會(huì)經(jīng)常要在類(lèi)中定義布爾類(lèi)型的變量,比如在給外部系統(tǒng)提供一個(gè) RPC 接口的時(shí)候,我們一般會(huì)定義一個(gè)字段表示本次請(qǐng)求是否成功的。
關(guān)于這個(gè)”本次請(qǐng)求是否成功”的字段的定義,我見(jiàn)過(guò)很多不同的開(kāi)發(fā)者,定義的方式都不同,尤其是在屬性的命名上,有人用 success,有人用 isSuccess 表示。
從語(yǔ)義上面來(lái)講,兩種命名方式都可以講的通,并且也都沒(méi)有歧義。
這不,小二就采用了 isSuccess 的形式來(lái)定義布爾類(lèi)型的變量,于是下午 review 的時(shí)候就被眼尖的老板揪了出來(lái)。老板畢竟是在阿里待過(guò)的,對(duì)這些細(xì)節(jié)扣的很細(xì)。
于是大發(fā)雷霆,準(zhǔn)備當(dāng)場(chǎng)勸退小二,被我攔了下來(lái)。畢竟自己面試的人,不看僧面看佛面,是吧?于是老板答應(yīng)我說(shuō)再試用一個(gè)月看看。
isSuccess 會(huì)有什么問(wèn)題呢?小二很是不解。
那根據(jù) JavaBeans Specification 的規(guī)定,如果是普通的參數(shù) propertyName,要以以下方式定義其 setter/getter:
public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);
但是,布爾類(lèi)型的變量 propertyName 則是單獨(dú)定義的:
public boolean is<PropertyName>();
public void set<PropertyName>(boolean m);
success 方法的 getter 應(yīng)該是 isSuccess/getSuccess,而 isSuccess 的 getter 應(yīng)該是 isIsSuccess/getIsSuccess。
但是很多人,在使用 isSuccess 作為屬性名的時(shí)候,還是會(huì)采用 isSuccess/getSuccess 作為 getter 方法名,尤其是現(xiàn)在的很多 IDE 在默認(rèn)生成 getter 的時(shí)候也是會(huì)生成 isSuccess。
在一般情況下,其實(shí)是沒(méi)有影響的。但是有一種特殊情況就會(huì)有問(wèn)題,那就是發(fā)生序列化的時(shí)候可能會(huì)導(dǎo)致參數(shù)轉(zhuǎn)換異常。
我們先來(lái)定義一個(gè) JavaBean:
class Model implements Serializable {
private static final long serialVersionUID = 1836697963736227954L;
private boolean isSuccess;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean success) {
isSuccess = success;
}
public String getWanger(){
return "hollis";
}
}
在這個(gè) JavaBean 中,有一個(gè)成員變量 isSuccess,三個(gè)方法,分別是 IDE 幫我們自動(dòng)生成的 isSuccess 和 setSuccess,另外一個(gè)是作者自己增加的一個(gè)符合 getter 命名規(guī)范的方法。
我們分別使用不同的 JSON 序列化工具來(lái)對(duì)這個(gè)類(lèi)的對(duì)象進(jìn)行序列化和反序列化:
public class BooleanMainTest {
public static void main(String[] args) throws IOException {
//定一個(gè)Model類(lèi)型
Model model = new Model();
model.setSuccess(true);
//使用fastjson(1.2.16)序列化model成字符串并輸出
System.out.println("Serializable Result With fastjson :" + JSON.toJSONString(model));
//使用Gson(2.8.5)序列化model成字符串并輸出
Gson gson =new Gson();
System.out.println("Serializable Result With Gson :" +gson.toJson(model));
//使用jackson(2.9.7)序列化model成字符串并輸出
ObjectMapper om = new ObjectMapper();
System.out.println("Serializable Result With jackson :" +om.writeValueAsString(model));
}
}
以上代碼輸出結(jié)果:
Serializable Result With fastjson :{"wanger":"hollis","success":true}
Serializable Result With Gson :{"isSuccess":true}
Serializable Result With jackson :{"success":true,"wanger":"hollis"}
在 fastjson 和 jackson 的結(jié)果中,原來(lái)類(lèi)中的 isSuccess 字段被序列化成 success,并且其中還包含 wanger 值。而 Gson 中只有 isSuccess 字段。
我們可以得出結(jié)論:fastjson 和 jackson 在把對(duì)象序列化成 json 字符串的時(shí)候,是通過(guò)反射遍歷出該類(lèi)中的所有 getter 方法,得到 getHollis 和 isSuccess,然后根據(jù) JavaBeans 規(guī)則,他會(huì)認(rèn)為這是兩個(gè)屬性 hollis 和 success 的值。直接序列化成 json:
{“wanger”:”沉默王二”,”success”:true}
但是 Gson 并不是這么做的,他是通過(guò)反射遍歷該類(lèi)中的所有屬性,并把其值序列化成 json:
{“isSuccess”:true}
可以看到,由于不同的序列化工具,在進(jìn)行序列化的時(shí)候使用到的策略是不一樣的,所以,對(duì)于同一個(gè)類(lèi)的同一個(gè)對(duì)象的序列化結(jié)果可能是不同的。那么,如果我們把一個(gè)對(duì)象使用 fastjson 進(jìn)行序列化,再使用 Gson 反序列化會(huì)發(fā)生什么呢?
public class BooleanMainTest {
public static void main(String[] args) throws IOException {
Model model = new Model();
model.setSuccess(true);
Gson gson =new Gson();
System.out.println(gson.fromJson(JSON.toJSONString(model),Model.class));
}
}
以上代碼,輸出結(jié)果:
Model[isSuccess=false]
這和我們預(yù)期的結(jié)果完全相反,原因是因?yàn)?JSON 框架通過(guò)掃描所有的 getter 后發(fā)現(xiàn)有一個(gè) isSuccess 方法,然后根據(jù) JavaBeans 的規(guī)范,解析出變量名為 success,把 model 對(duì)象序列化成字符串后內(nèi)容為{"success":true}。
根據(jù){"success":true}這個(gè) json 串,Gson 框架在通過(guò)解析后,通過(guò)反射尋找 Model 類(lèi)中的 success 屬性,但是 Model 類(lèi)中只有 isSuccess 屬性,所以,最終反序列化后的 Model 類(lèi)的對(duì)象中,isSuccess 則會(huì)使用默認(rèn)值 false。
但是,一旦以上代碼發(fā)生在生產(chǎn)環(huán)境,這絕對(duì)是一個(gè)致命的問(wèn)題。
所以,作為開(kāi)發(fā)者,我們應(yīng)該想辦法盡量避免這種問(wèn)題的發(fā)生。
所以,建議大家使用 success 而不是 isSuccess 這種形式。 這樣,該類(lèi)里面的成員變量時(shí) success,getter 方法是 isSuccess,這是完全符合 JavaBeans 規(guī)范的。無(wú)論哪種序列化框架,執(zhí)行結(jié)果都一樣。就從源頭避免了這個(gè)問(wèn)題。
沒(méi)有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧?kù)o的港灣,我是不系之舟。
推薦閱讀:
一鍵部署 Spring Boot 項(xiàng)目 離開(kāi)北京? 編程喵實(shí)戰(zhàn)項(xiàng)目可以在本地跑起來(lái)辣! 再見(jiàn) Spring Task,這款定時(shí)任務(wù)老而彌堅(jiān)!

