2.1 變量
我們先來看下面的示例:
Dog dog1 = new Dog(); // 給他們狀態 dog1.name = "Lucy"; dog1.color = "Black";
dog1是Dog類的實例(對象)名稱,name和color是dog1字段的名稱(字段存儲了對象的狀態),這些名稱都代表了某種類型的值,在編程語言中被稱為“變量”。通過變量,可以方便地找到內存中所存儲的值。
Java里面的變量包含如下類型:
· 實例變量/非靜態字段(Instance Variables/Non-Static Fields):從技術上講,對象存儲它們的個人狀態在“非靜態字段”,也就是沒有static關鍵字聲明的字段。非靜態字段也被稱為實例變量,因為它們的值對于類的每個實例來說是唯一的(換句話說,就是每個對象)。名字叫作Lucy的狗獨立于另一條名字叫作Lily的狗。
· 類變量/靜態字段(Class Variables/Static Fields):類變量是用static修飾符聲明的字段,也就是告訴編譯器無論類被實例化多少次,這個變量的存在只有一個副本。特定種類自行車的齒輪數目的字段可以被標記為static,因為相同的齒輪數量將適用于所有情況。代碼“static intnumGears = 6;”將創建一個這樣的靜態字段。此外,關鍵字final也可以加入,以指示齒輪的數量不會改變。
· 局部變量(Local Variables):類似于對象存儲狀態在字段里,方法通常會存放臨時狀態在局部變量里。語法與局部變量的聲明類似(例如,int count = 0;)。沒有特殊的關鍵字來指定一個變量是否是局部變量,是由該變量聲明的位置決定的。局部變量是類的方法中的變量。
· 參數(Parameters):在前文的例子中經常可以看到public static void main(String[] args),這里的args變量就是這個方法參數。需要記住的重要一點是,參數都歸類為“變量(variable)”而不是“字段(field)”。
如果我們談論的是“一般的字段”(不包括局部變量和參數),那么我們可以簡單地說“字段”。如果討論適用于上述所有情況,那么我們可以簡單地說“變量”。如果上下文要求一個區別,我們將使用特定的術語(靜態字段、局部變量等),也偶爾會使用術語“成員(member)”。類型的字段、方法和嵌套類型統稱為它的成員。
2.1.1 命名
每一個編程語言都有它自己的一套規則和慣例的各種名目,Java編程語言對于命名變量的規則和慣例可以概括如下:
· 變量名稱是區分大小寫的。變量名可以是任何合法的標識符(無限長度的Unicode字母和數字),以字母、美元符號$或下畫線_開頭,但是推薦按照慣例以字母開頭,而不是$或_。此外,按照慣例,美元符號從未使用過。在某些情況下,某些軟件自動生成的名稱會包含美元符號,但在實際編程中變量名應該始終避免使用美元符號。類似的約定還有下畫線,不鼓勵用“_”作為變量名開頭。空格是不允許的。
· 隨后的字符可以是字母、數字、美元符號或下畫線字符。慣例同樣適用于這一規則。為變量命名,盡量是完整的單詞,而不是神秘的縮寫。這樣做會使你的代碼更容易閱讀和理解,比如name和color會比縮寫n和c更直觀。同時請記住,選擇的名稱不能是關鍵字或保留字。
· 如果選擇的名稱只包含一個單詞,那么拼寫單詞全部為小寫字母。如果它由一個以上的單詞組成,那么每個后續單詞的第一個字母大寫,如gearRatio和currentGear。如果你的變量存儲一個常量,如static final int NUM_GEARS = 6,那么每個字母大寫,并用下畫線分隔后續字符。按照慣例,下畫線字符永遠不會在其他地方使用。
詳細的命名規范,可以參考筆者所著的《Java編碼規范》(https://github.com/waylau/java-code-conventions)。
2.1.2 基本數據類型
Java是靜態類型的語言,必須先聲明再使用。基本數據類型之間不會共享。主要有8種基本數據類型:byte、short、int、long、char、float、double、boolean。其中,byte、short、int、long是整數類型,float、double是浮點數類型。
1.byte
byte由1個字節8位表示,是最小的整數類型。當操作來自網絡、文件或者其他I/O的數據流時,byte類型特別有用。byte取值范圍是[-128, 127],默認值為(byte)0。如果我們試圖將取值范圍外的值賦給byte類型的變量,則會出現編譯錯誤,例如:
byte b = 128; // 編譯錯誤
上面這個語句是無法通過編譯的。
還有一個有趣的問題,如果有這樣一個方法:

試圖通過test(0)來調用這個方法是錯誤的,編譯器會報錯,因為類型不兼容!但是像下面這樣賦值就完全沒有問題:
byte b = 0; // 正常
這里涉及一個叫字面值(literal)的問題。字面值就是表面上的值,例如整型字面值在源代碼中就是諸如5、0、-200這樣的。如果整型字面值后面加上L或者l,那么這個字面值就是long類型,比如1000L代表一個long類型的值。
注意
l和1長得很像,所以表示一個long型時,以免肉眼看錯,建議以L結尾。
若不加L或者l,則為int類型。基本類型當中的byte、short、int、long都可以通過不加L的整型字面值來創建,例如:
byte b = 100; short s = 5;
對于long類型,如果大小超出int所能表示的范圍(32 bits),則必須使用L結尾來表示。整型字面值可以有不同的表示方式:十六進制(0X或者0x)、十進制、八進制(0)、二進制(0B或者0b)等。二進制字面值是JDK 7以后才有的功能。在賦值操作中,int字面值可以賦給byte、short、int、long等,Java語言會自動處理好這個過程。如果方法調用時不一樣,比如調用test(0)的時候,它能匹配的方法是test(int),當然不能匹配test(byte)方法。
注意區別包裝器與原始類型的自動轉換,比如下面的賦值是允許的:
byte d = 'A'; // 正常
上面例子中的字符字面值可以自動轉換成16位的整數。
對byte類型進行數學運算時,會自動提升為int類型。如果表達式中有double或者float等類型,也是會自動提升的。所以下面的代碼是錯誤的:
byte t s1 = 100; byte s2 = 'a'; byte sum = s1 + s2; // 錯誤!不能將int轉為byte
2.short
short用16位表示,取值范圍為[- 2^15, 2^15 - 1]。short可能是最不常用的類型,可以通過整型字面值或者字符字面值賦值,前提是不超出范圍。short類型參與運算的時候,一樣會被提升為int或者更高的類型。
3.int
int用32位表示,取值范圍為[- 2^31, 2^31 - 1]。Java 8以后,可以使用int類型表示無符號32位整數,數據范圍為[0, 2^31 - 1]。
4.long
long用64位表示,取值范圍為[- 2^63, 2^63 - 1],默認值為0L。當需要計算非常大的數時,如果int不足以容納大小,可以使用long類型。如果long也不夠,可以使用BigInteger類。
5.char
char用16位表示,其取值范圍可以是[0, 65535]、[0, 2^16 -1]或者是從\u0000到\uffff。Java使用Unicode字符集表示字符,Unicode是完全國際化的字符集,可以表示全部人類語言中的字符。Unicode需要16位寬,所以Java中的char類型也使用16位表示。賦值可能是這樣的:
char ch1 = 88; char ch2 = 'A';
ASCII字符集占用了Unicode的前127個值。之所以把char歸入整型,是因為Java為char提供算術運算支持,例如運行“ch2++;”之后ch2就變成Y。當char進行加減乘除運算的時候,會被轉換成int類型,必須顯式轉化回來。
6.float
float使用32位表示,對應單精度浮點數,遵循IEEE 754規范。運行速度相比double更快,占內存更小,但是當數值非常大或者非常小的時候會變得不精確。精度要求不高的時候可以使用float類型,聲明賦值示例:
float f1 =10; f1 = 10L; f1 = 10.0f;
可以將byte、short、int、long、char賦給float類型,Java自動完成轉換。
7.double
double使用64位表示,將浮點字面值賦給某個變量時,如果不顯示在字面值后面加f或者F,則默認為double類型。比如下面的例子:
float f1 =10; f1 = 10.0; //為double類型
java.lang.Math中的函數都采用double類型。如果double和float都無法達到想要的精度,可以使用BigDecimal類。
8.boolean
boolean類型只有兩個值true和false,默認為false。boolean與是否為0沒有任何關系,但是可以根據想要的邏輯進行轉換。許多地方都需要用到boolean類型。
除了上面列出的8種原始數據類型,Java編程語言還提供了java.lang.String,用于字符串的特殊支持。雙引號包圍的字符串會自動創建一個新的String對象,例如:
String s = "this is a string";
String對象是不可變的,這意味著一旦創建,它們的值不能改變。String類型不是技術上的原始數據類型,但考慮到語言所賦予的特殊支持,你可能會錯誤地傾向于認為它是這樣的。
2.1.3 基本數據類型的默認值
在字段聲明時,有時并不必要分配一個值。字段被聲明但尚未初始化時,將會由編譯器設置一個合理的默認值。一般而言,根據數據類型的不同,默認將為零或為null。良好的編程風格不應該依賴于這樣的默認值。表2-1總結了上述數據類型的默認值。
表2-1 基本數據類型的默認值

局部變量(Local Variable)略有不同,編譯器不會指定一個默認值未初始化的局部變量。如果你不能初始化你聲明的局部變量,那么請確保使用之前給它分配一個值。訪問一個未初始化的局部變量會導致編譯時錯誤。
2.1.4 字面值
在Java源代碼中,字面值(Literal)用于表示固定的值,直接展示在代碼里,而不需要計算。數值型的字面值是最常見的,字符串字面值可以算是一種,當然也可以把特殊的null當作字面值。字面值大體上可以分為整型字面值、浮點字面值、字符和字符串字面值、特殊字面值。
1.整型字面值
從形式上看是整數的字面值歸類為整型字面值。例如,10、100000L、'B'、0XFF這些都可以稱為字面值。整型字面值可以用十進制、十六進制、八進制、二進制來表示。十進制很簡單,二進制、八進制、十六進制的表示分別在最前面加上0B(0b)、0、0X(0x)即可。
當然基數不能超出進制的范圍,比如在八進制里面09是不合法的,八進制的基數只能到7。一般情況下,字面值創建的是int類型,但是int字面值可以賦值給byte、short、int、long、char,只要字面值在目標范圍以內,Java就會自動完成轉換。如果試圖將超出范圍的字面值賦給某一類型(比如把128賦給byte類型),編譯會通不過。如果想創建一個int類型無法表示的long類型,則需要在字面值最后面加上L或者l,通常建議使用容易區分的L。所以整型字面值包括int字面值和long字面值兩種。
· 十進制:其位數由數字0~9組成,這是你每天使用的數字系統。
· 十六進制:其位數由數字0到9和字母A至F組成。
· 二進制:其位數由數字0和1組成。
下面是使用的語法:
// 十進制 int decVal = 26; // 十六進制 int hexVal = 0x1a; // 二進制 int binVal = 0b11010;
2.浮點字面值
浮點字面值可以簡單理解為小數,分為float字面值和double字面值兩種。如果在小數后面加上F或者f,就表示這是一個float字面值,如11.8F。如果小數后面不加F(f),如10.4,或者小數后面加上D(d),則表示這是一個double字面值。另外,浮點字面值支持科學記數法(E或e)表示。下面是一些例子:
double d1 = 123.4; // 科學記數法 double d2 = 1.234e2; float f1 = 123.4f;
3.字符和字符串字面值
在Java中,字符字面值用單引號括起來,如'@'、'1'。所有的UTF-16字符集都包含在字符字面值中。不能直接輸入的字符可以使用轉義字符,如\n為換行字符。也可以使用八進制或者十六進制表示字符,八進制使用反斜杠加3位數字表示,例如'\141'表示字母a。十六進制使用'\u'加上4為十六進制的數表示,如'\u0061'表示字符a。也就是說,通過使用轉義字符,可以表示鍵盤上有的或者沒有的所有字符。常見的轉義字符序列有:
· \ddd(八進制)
· \uxxxx(十六進制Unicode字符)
· \'(單引號)
· \"(雙引號)
· \\(反斜杠)
· \r(回車符)
· \n(換行符)
· \f(換頁符)
· \t(制表符)
· \b(回格符)
字符串字面值使用雙引號。字符串字面值中同樣可以包含字符字面值中的轉義字符序列。字符串必須位于同一行或者使用+運算符,因為Java沒有續行轉義序列。
4.特殊字面值
從Java SE 7開始,可以在數值型字面值中使用下畫線,但是下畫線只能用于分隔數字,不能分隔字符與字符,也不能分隔字符與數字。例如:
int x = 123_456_789;
在編譯上面的代碼時,下畫線會自動去掉。
可以連續使用下畫線,比如:
float f = 1.22___33__44
二進制或者十六進制的字面值也可以使用下畫線。
切記,下畫線只能用于數字與數字之間,除此以外都是非法的。例如,1._23是非法的,_123、11000_L都是非法的。
下面列出一些正確的用法:
long creditCardNumber = 1234_5678_9012_3456L; long socialSecurityNumber = 999_99_9999L; float pi = 3.14_15F; long hexBytes = 0xFF_EC_DE_5E; long hexWords = 0xCAFE_BABE; long maxLong = 0x7fff_ffff_ffff_ffffL; byte nybbles = 0b0010_0101; long bytes = 0b11010010_01101001_10010100_10010010;
下面列出一些非法的用法:
float pi1 = 3_.1415F; float pi2 = 3._1415F; long socialSecurityNumber1 = 999_99_9999_L; int x2 = 52_; int x4 = 0_x52; int x5 = 0x_52; int x7 = 0x52_;
2.1.5 基本數據類型之間的轉換
在Java中,將一種類型的值賦給另一種類型是很常見的。同時要注意,boolean類型與其他7種類型不能進行轉換,這一點很明確。對于其他7種數據類型,它們之間都可以進行轉換,但是可能會存在精度損失或者其他一些變化。
轉換分為自動轉換和強制轉換。對于自動轉換(隱式),無須任何操作;而強制類型轉換需要顯式轉換,即使用轉換操作符“(類型)”。以下是一個示例:
int i =97; char c = (char)i; // int強制轉換為char
首先將7種類型按下面的順序排列一下:
byte <(short=char)< int < long < float < double
從小轉換到大,可以自動完成;而從大到小,則必須強制轉換。即使short和char類型相同,也必須強制轉換。
圖2-1形象地展示了類型轉換之間的關系。小杯子的物品可以順利倒入大杯子中(自動轉換),但大杯子里面的物品則不能簡單地倒入小杯子中(強制轉化,可能會導致物品丟失)。

圖2-1 基本數據類型之間的轉換
1.自動轉換
自動轉換時發生擴寬轉換(widening conversion)。因為較大的類型(如int)要保存較小的類型(如byte),內存總是足夠的,不需要強制轉換。將字面值保存到byte、short、char、long的時候,也會自動進行類型轉換。注意,此時從int(沒有帶L的整型字面值為int)到byte、short、char也是自動完成的,雖然它們都比int小。在自動類型轉化中,除了以下幾種情況可能會導致精度損失以外,其他的轉換都不能出現精度損失。
· int–> float
· long–> float
· long–> double
· float –>無符號double
除了可能的精度損失外,自動轉換不會出現任何運行時異常。
2.強制類型轉換
如果要把大的轉成小的,或者在short與char之間進行轉換,就必須強制轉換,也被稱作縮小轉換(narrowing conversion),因為必須顯式地使數值更小以適應目標類型。嚴格地說,將byte轉為char不屬于縮小轉換,因為從byte到char的過程其實是byte→int→char,所以擴寬轉換和縮小轉換都有。
強制轉換除了可能的精度損失外,還可能使模(overall magnitude)發生變化。強制轉換示例如下:
int a = 257; byte b; b = (byte)a; // 1
如果整數的值超出了byte所能表示的范圍,結果將對byte類型的范圍取余數。例如,a=257超出了byte [-128,127]的范圍,所以將257除以byte的范圍(256)取余數得到b=1。需要注意的是,當a=200時,除以256取余數應該為-56,而不是200。
將浮點類型賦給整數類型的時候會發生截尾(truncation),也就是把小數的部分去掉,只留下整數部分。此時如果整數超出目標類型范圍,一樣將對目標類型的范圍取余數。
7種基本類型轉換總結如圖2-2所示。

圖2-2 7種基本類型轉換總結
3.字面值賦值
在使用字面值對整數賦值的過程中,可以將int字面值賦給byte、short、char、int,只要不超出范圍即可。這個過程中的類型轉換是自動完成的,但是如果你試圖將long字面值賦給byte,即使沒有超出范圍,也必須進行強制類型轉換。例如,下面的例子是非法的:
byte b = 10L; // 錯誤!
如果想將long型轉為byte,則需要進行強制轉換。
4.表達式中的自動類型提升
除了賦值以外,表達式計算過程中也可能發生一些類型轉換。在表達式中,類型提升規則如下:
· 所有byte、short、char都被提升為int。
· 如果有一個操作數為long,整個表達式提升為long。float和double情況也一樣。
2.1.6 數組
數組(Array)是一個容器對象,保存一個固定數量的單一類型的值。當數組創建時,數組的長度就確定了。創建后,其長度是固定的。數據里面的每個項稱為元素(element),每個元素都用一個數組下標(index)關聯。下標從0開始,如圖2-3所示,第9個元素的下標是8。

圖2-3 數組示例
以下是一個數組的示例:

輸出為:
Element at index 0: 100 Element at index 1: 200 Element at index 2: 300 Element at index 3: 400 Element at index 4: 500 Element at index 5: 600 Element at index 6: 700 Element at index 7: 800 Element at index 8: 900 Element at index 9: 1000
1.聲明引用數組的變量
聲明數組的類型:
byte[] anArrayOfBytes; short[] anArrayOfShorts; long[] anArrayOfLongs; float[] anArrayOfFloats; double[] anArrayOfDoubles; boolean[] anArrayOfBooleans; char[] anArrayOfChars; String[] anArrayOfStrings;
也可以將中括號放在數組名稱后面(但不推薦):
// 合法,但不推薦使用 float anArrayOfFloats[];
2.創建、初始化和訪問數組
ArrayDemo的示例說明了創建、初始化和訪問數組的過程。可以用下面的方式簡化創建、初始化數組:
int[] anArray = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 };
數組里面可以聲明數組,即多維數組(multidimensional array)。如下面的例子就是一個多維數組MultiDimArrayDemo:

輸出為:
Mr. Way Ms. Lau
最后,可以通過內建的length屬性來確認數組的大小:
System.out.println(anArray.length);
3.復制數組
System類有一個arraycopy方法,用于數組的有效復制:

下面是一個例子(ArrayCopyDemo):

程序輸出為:
way
4.數組操作
Java提供了一些數組有用的操作。觀察下面的例子ArrayCopyOfDemo:

可以看到,相比于ArrayCopyDemo的例子,使用java.util.Arrays.copyOfRange方法,代碼量減少了很多。
其他常用操作還包括:
· binarySearch:用于搜索。
· equals:比較兩個數組是否相等。
· fill:填充數組。
· sort:數組排序,在Java 8以后,可以使用parallelSort方法,在多處理器系統的大數組并行排序比連續數組排序更快。
- Python Game Programming By Example
- Magento 2 Development Cookbook
- Django:Web Development with Python
- INSTANT Django 1.5 Application Development Starter
- 計算機應用基礎案例教程
- iOS自動化測試實戰:基于Appium、Python與Pytest
- Access 2010中文版項目教程
- R Data Science Essentials
- Elasticsearch Essentials
- 從零開始學Android開發
- SQL Server 入門很輕松(微課超值版)
- Magento 2 Beginners Guide
- Jakarta EE Cookbook
- 測試工程師Python開發實戰
- H5頁面設計與制作(全彩慕課版·第2版)