本文適合對 Java 或 C 有一些了解的用戶閱讀,推薦閱讀時間15分鍾。
導言
寫這個系列的原因?
我曾經聽過一種說法,如果不了解Liunx的網絡通訊,就很難理解理解Java的IO;如果不知道Java的IO也很難理解之后的NIO,Netty。(理解是建立在對底層的理解之上的)
一門語言的設計是一項工程,是需要向很多其他編程語言、操作系統學習,站在巨人的肩膀上進行設計的;好的設計,好的語法也不是一蹴而就的,需要對之前版本的優點和缺點進行評估,再進行新語法的設想。
所以,本系列想采用Java從設計之初一直發展到Java8相關的新特性來講述語言為何如此設計,以及語言發展各個重大版本的變化這個角度進行解析如何編寫好“風格”的代碼。
注:Java已經發展了很多年,目前為止最新的LTS版本為Java11。由於Java8商用已經較為普及,所以采用此版本。
這一節,先從布爾,常量一些特性開始講起。
一、布爾類型
布爾類型在語法上類似於語言內置的常量,所以也做一些簡單的介紹。
在過去編寫C語言的代碼時,由於缺少布爾類型(C99加入了布爾類型!),默認任何 非零 的值(不為null)假定為 true,把 零 和 null 假定為 false。
#include<stdio.h> int main() { /* 輸出 if(null) is false */ if (null) print("if(null) is true"); else print("if(null) is false"); /* 輸出 if(0) is false */ if (0) print("if(0) is true"); else print("if(0) is false"); /* 輸出 if(1) is true */ if (1) print("if(1) is true"); else print("if(1) is false"); }
這在當時沒有問題,但這種寫法可讀性差,對程序員不太友好。
在這點上,C++往前走了一步,添加了布爾類型bool,但由於要兼容C語言,也可以同時使用C的語法(條件表達式接收int值),打印bool值真假實際為1和0。
#include <iostream> using namespace std; int main(){ bool flag = true; if(flag) cout<<"true"<<endl; else cout<<"false"<<endl; cout<<flag<<endl; flag = false; if(flag) cout<<"true"<<endl; else cout<<"false"<<endl; cout<<flag<<endl; return 0; } /* 輸出 true 1 false 0 */
Java也沿襲下來,增加了boolean類型,但條件表達式不接受int值,在語法上int值和boolean徹底無關。
package com.xx; public class Demo { public static void main(String[] args) { boolean b1 = true; System.out.println(b1); boolean b2 = false; System.out.println(b2); } } /*輸出 true false */
從C、C++一直到Java,可以看到語言設計者在一步步讓程序編寫者編出更直觀的、人類可讀的代碼。
引申:事實上,計算機是無法存儲true和false的,在JVM上,boolean的true和false同C++一樣存儲為int的1和0。
二、魔術數字(magic number)
編程初學者碰到一些特殊變量最開始可能會直接采用硬編碼的方式編寫。這種編碼被人們稱之為魔術數字(原因在於作者在若干月后,甚至自己也不知道這個數字是什么意義)
//100表示成功,其他值表示失敗 if (status == 100) { //成功處理 } else { //失敗處理 }
其中100就是一個典型的魔術數字
這種編碼習慣有以下兩個明顯的缺點
1.數值的意義難以了解
2.數值涉及變動時,可能要改不只一處
后來的編程人員使用了常量來代替魔術數字。常量名力求見名知意(某人說,好的代碼是自解釋(self-explanatory)的)。
常量的加入給程序帶來了這兩個好處。
1.增加可讀性。
2.方便修改。
//1.這樣的常量通常用相同的前綴表示同一類型 USER_SEX,且定義位置在最開頭 //2.從C沿襲至今的全大寫,下划線分隔 public static final int USER_SEX_MALE = 1; public static final int USER_SEX_FEMALE = 0;
但是,在越來越多的常量使用中,人們又發現了自定義的常量也有一些難以解決的問題
1.需編譯后才能生效
2.存在類型轉換的風險
p.s. 第二點正是和C++的布爾值bool有着同樣的問題。
再后來,常量的編寫方式越來越多,比如注解;
通過注解使用的常量值
//application.properties
# 默認使用dev的配置文件
spring.profiles.active=dev
//application-dev.properties
# foo在開發環境的默認次數
default.foo.numbers = 10
//application-test.properties
#foo在測試環境的默認次數
default.foo.numbers = 1000
//application-prod.properties
# foo在生產環境的默認次數
default.foo.numbers = 100
import org.springframework.beans.factory.annotation.Value;
@Service public class FooService { @Value("${default.foo.numbers}") private Integer fooNumbers; }
使用注解的好處在於用戶可以定義不同的application-xxx.properties等多個配置文件。每個文件可以定義同名不同值的常量, 在啟動項目時通過啟動參數 -Dspring.profiles.active=XXX 來選擇使用的配置文件,實現改變配置時改變常量值。
比如 dev配置文件的default.foo.numbers設置為10, test配置文件值設置為1000,根據 啟動項目調用 -Dspring.profiles.active=test 調用到的值就是1000。
Java設計之初並沒有考慮枚舉類型,而是沿用從C一直以來的傳統使用全局int/string值表示常量。為了讓用戶更好地使用常量,從1.5之后引入了枚舉類型
/** * 1.自動私有構造函數 * 2.強類型,不會因為類型轉換帶來問題 * 3.可以直接使用 == 比較,和String常量需要equals()對比,效率高 * 4.無需編譯生效 */ public enum UserSex { male, //男 female, //女 ; //error // UserSex(); public static UserSex get(String str) { for (UserSex t : UserSex.values()) { if (t.name().equals(str)) return t; } return null; } }
目前為止博主的最佳實踐
對於只有真假的值,在Java中推薦使用布爾類型表示,比如是否為會員(isVip: true, false)。
對於超過兩種以上或今后可能會增加新的類型的推薦用枚舉表示。比如會員類型(VipType: week, month, year),考慮之后可能會新加入quarter等時間。
對於不同服之間同一個常量值由於各種需要測試等原因不一致,可以在各個服的配置文件定義。