Java 語言是一門強類型語言。強類型包含兩方面的含義: ① 所有的變量必須先聲明、后使用; ②指定類型的變量只能接受類型與之匹配的值。強類型語言可以在編譯過程中發現源代碼的錯誤,從而保證程序更加健壯。Java 語言提供了豐富的基本數據類型,例如整型、字符型、浮點型和布爾型等。基本類型大致上可以分為兩類: 數值類型和布爾類型, 其中數值類型包括整型、字符型和浮點型,所有數值類型之間可以進行類型轉換,這種類型轉換包括自動類型轉換和強制類型轉換。
Java 語言還提供了一系列功能豐富的運算符, 這些運算符包括所有的算術運算符,以及功能豐富的位運算符、比較運算符、邏輯運算符, 這些運算符是Java 編程的基礎。將運算符和操作數連接在一起就形成了表達式。
3.1 注釋
編寫程序時總需要為程序添加一些注釋,用以說明某段代碼的作用,或者說明某個類的用途、某個方法的功能,以及該方法的參數和返回值的數據類型及意義等。
程序注釋的作用非常大,很多初學者在開始學習Java 語言時,會很努力地寫程序,但不大會注意添加注釋,他們認為添加注釋是一件浪費時間,而且沒有意義的事情。經過一段時間的學習,他們寫出了一些不錯的小程序,如一些游戲、工具軟件等。再經過一段時間的學習,他們開始意識到當初寫的程序在結構上有很多不足,需要重構。於是打開源代碼,他們以為可以很輕松地改寫原有的代碼,但這時發現理解原來寫的代碼非常困難,很難理解原有的編程思路。
為什么要添加程序注釋?至少有如下三方面的考慮。
- 永遠不要過於相信自己的理解力! 當你思路通暢,進入編程境界時,你可以很流暢地實現某個功能,但這種流暢可能是因為你當時正處於這種開發思路中。為了在再次閱讀這段代碼時,還能找回當初編寫這段代碼的思路,建議添加注釋!
- 可讀性第一, 效率第二! 在那些"古老"的歲月里,編程是少數人的專利, 他們隨心所欲地寫程序,他們以追逐程序執行效率為目的。但隨着軟件行業的發展, 人們發現僅有少數技術極客編程滿足不了日益增長的軟件需求,越來越多的人加入了編程隊伍, 並引入了工程化的方式來管理軟件開發。這個時候, 軟件開發變成團隊協同作戰,團隊成員的溝通變得很重要,因此,一個人寫的代碼,需要被整個團隊的其他人所理解: 而且,隨着硬件設備的飛速發展, 程序的可讀性取代執行效率變成了第一考慮的要素。
- 代碼即文檔! 很多剛剛學完學校軟件工程課程的學生會以為: 文檔就是Word 文檔! 實際上,程序源代碼是程序文檔的重要組成部分, 在想着把各種軟件相關文檔寫規范的同時,不要忘了把軟件里最重要的文檔一一源代碼寫規范!
程序注釋是源代碼的一個重要部分,對於一份規范的程序源代碼而言, 注釋應該占到源代碼的1/3以上。幾乎所有的編程語言都提供了添加注釋的力法。一般的編程語言都提供了基本的單行注釋和多行注釋, Java 語言也不例外, 除此之外, Java 語言還提供了一種文檔注釋。Java 語言的注釋一共有三種類型。
- 單行注釋。
- 多行注釋。
- 文檔注釋。
3.1.1 單行注釋和多行注釋
單行注釋就是在程序中注釋一行代碼,在Java 語言中,將雙斜線(//)放在需要注釋的內容之前就可以了;多行注釋是指一次性地將程序中多行代碼注釋掉, 在Java 語言中,使用" /* "和" */ "將程序中需要注釋的內容包含起來, "/*"表示注釋開始,而" */ "表示注釋結束。
下面代碼中增加了單行注釋和多行注釋。
1 public class CommentTest 2 { 3 /* 4 這里面的內容全部是多行注釋 5 Java語言真的很有趣 6 */ 7 public static void main(String[] args) 8 { 9 //這是一行簡單的注釋 10 System.out.println("Hello World!"); 11 //System.out.println("這行代碼被注釋了") 12 } 13 }
除此之外,添加注釋也是調試程序的一個重要方法。如果覺得某段代碼可能有問題,可以先把這段代碼注釋起來,讓編譯器忽略這段代碼,再次編譯、運行,如果程序可以正常執行,則可以說明錯誤就是由這段代碼引起的,這樣就縮小了錯誤所在的范圍,有利於排錯; 如果依然出現相同的錯誤,則可以說明錯誤不是由這段代碼引起的,同樣也縮小了錯誤所在的范圍。
3.1.2 Java 9 增強文檔注釋
Java 語言還提供了一種功能更強大的注釋形式: 文檔注釋。如果編寫Java 源代碼時添加了合適的文檔注釋,然后通過JDK 提供的javadoc 工具可以直接將源代碼里的文檔注釋提取成一份系統的API文檔。
Java 提供了大量的基礎類,因此Oracle 也為這些基礎類提供了相應的API 文檔,用於告訴開發者如何使用這些類,以及這些類里包含的方法。
下載Java 9 的API 文檔很簡單, 登錄http ://www.oracle.comltechnetwork/java/javase/downloads/index.html 站點,將頁面上的滾動條向下滾動,找到" Additional Resources"部分,看到如圖3 .1所示的頁面。單擊如圖3 .1 所示的鏈接即可下載得到Java SE 9文檔,這份文檔里包含了JDK 的API 文檔。下載成功后得到一個j dk-9_doc-al l. zip 文件。
將jdk-9_ doc-al l. zip 文件解壓縮到任意路徑,將會得到一個docs 文件夾,這個文件夾下的內容就是JDK文檔, JDK 文檔不僅包含API 文檔,還包含JDK 的其他說明文檔。
進入docs/api 路徑下,打開index.html 文件,可以看到JDK 9 API 文檔首頁,單擊該頁面上方的"FRAMES" 鏈接,這個首頁就是一個典型的Java API文檔首頁,如圖3.2 所示。
從圖3.2 所示的首頁中可以看出, API 文檔頁面被分為三個部分,左上角部分是API 文檔"包列表區",在該區域內可以查看Java 類的所有包 ; 左下角是API文擋的"類列表區",用於查看Java 的所有類;右邊頁面是"詳細說明區",默認顯示的是各包空間的說明信息。
Java 9 對API 文檔進行了增強, Java 9 為API 文檔增加了一個搜索框,如圖3.2 中右上角所示,用戶可以通過該搜索框快速查找指定的Java 類。
Java 9 將API 文檔分成3 個子集。
- Java SE: 該子集的API 文檔主要包含Java SE 的各種類。
- JDK: 該子集的API 文檔主要包含JDK 的各種工具類。
- JavaFX: 該子集的API 文檔主要包含Java FX 的各種類。
如果單擊"類列表區"中列出的某個類,將看到右邊頁面變成了如圖3 .3所示的格局。
由於文檔注釋是用於生成API 文檔的,而API 文檔主要用於說明類、方法、成員變量的功能。因此, javadoc 工具只處理文檔源文件在類、接口、方法、成員變量、構造器和內部類之前的注釋, 忽略其他地方的文檔注釋。而且javadoc 工具默認只處理以public 或protected 修飾的類、接口、方法、成員變量、構造器和內部類之前的文檔注釋。
文檔注釋以斜線后緊跟兩個星號(/** )開始, 以星號后緊跟一個斜線( *!)結束,中間部分全部都是文檔注釋, 會被提取到API 文檔中。
Java 9 的API 文檔己經支持HTML 5 規范, 因此為了得到完全兼容HTML5 的API 文擋,必須保證文檔注釋中的內容完全兼容HTML5 規范。
下面先編寫一個JavadocTest 類,這個類里包含了對類、方法、成員變量的文檔注釋。
1 /** 2 * Description: 3 * Copyright @,2018-2019<br> 4 * This program is protected by copyright laws 5 * Date:<br> 6 * @version 1.0 7 */ 8 public class JavadocTest 9 { 10 /** 11 * 簡單測試成員變量 12 */ 13 protected String name; 14 15 /** 16 * 主方法,程序的入口 17 */ 18 public static void main(String[] args) 19 { 20 System.out.println("Hello World !"); 21 } 22 }
編寫好上面的Java 程序后,就可以使用javadoc工具提取這兩個程序中的文檔注釋來生成API 文檔了。javadoc 命令的基本用法如下:
javadoc 選項Java 源文件|包
javadoc 命令可對源文件、包生成API 文檔,在上面的語法格式中, Java 源文件可以支持通配符,例如,使用*.j ava 來代表當前路徑下所有的Java 源文件。javadoc 的常用選項有如下幾個。
- -d <directory> : 該選項指定一個路徑,用於將生成的API 文檔放到指定目錄下。
- -windowtitle <text> : 該選項指定一個宇符串,用於設置API 文檔的瀏覽器窗口標題。
- -doctitle <html-code> : 該選項指定一個HTML 格式的文本,用於指定概述頁面的標題。
- -header <html-code> : 該選項指定一個HTML 格式的文本,包含每個頁面的頁眉。
除此之外, javadoc 命令還包含了大量其他選項,讀者可以通過在命令行窗口執行javadoc -help 來查看javadoc 命令的所有選項。
在命令行窗口執行如下命令來為剛剛編寫的Java 程序生成API 文檔:
javadoc -d apidoc -windowtitle 測試 -doctitle 學習javadoc工具的測試API文檔 -header 我的類*Test .java
如果希望javadoc 工具生成更詳細的文檔信息,例如為方法參數、方法返回值等生成詳細的說明信息,則可利用javadoc 標記。常用的javadoc 標記如下。
- @author: 指定Java 程序的作者。
- @version: 指定源文件的版本。
- @deprecated: 不推薦使用的方法。
- @param: 方法的參數說明信息。
- @retum: 方法的返回值說明信息。
- @see: "參見",用於指定交叉參考的內容。
- @exception: 拋出異常的類型。
- @throws: 拋出的異常,和@exception 同義。
需要指出的是,這些標記的使用是有位置限制的。上面這些標記可以出現在類或者接口文檔注釋中的有@see 、@deprecated 、@author 、@version 等:可以出現在方法或構造器文檔注釋中的有@see 、@deprecated 、@param 、@return 、@throws 和@exception 等;可以出現在成員變量的文檔注釋中的有@see 和@deprecated 等。
下面的JavadocTagTest 程序包含了一個hello 方法, 該方法的文檔注釋使用了@param 和@return 等文檔標記。
1 package test; 2 3 /** 4 * Description: 5 * Copyright (C), 2001-2019<br> 6 * This program is protected by copyright laws.<br> 7 * Program Name:<br> 8 * Date:<br> 9 * @author abc@163.com 10 * @version 1.0 11 */ 12 public class JavadocTagTest 13 { 14 /** 15 * 一個得到打招呼字符串的方法。 16 * @param name 該參數指定向誰打招呼。 17 * @return 返回打招呼的字符串。 18 */ 19 public String hello(String name) 20 { 21 return name + ",你好!"; 22 } 23 }
上面程序中粗體宇標識出使用javadoc 標記的示范。再次使用javadoc 工具來生成API 文檔, 這次為了能提取到文檔中的@author 和@version 等標記信息,在使用javadoc 工具時增加-author 和-version兩個選項,即按如下格式來運行javadoc 命令:
javadoc -d apidoc - windowtitle 測試- doctitle 學習javadoc 工具的測試API 文檔- header 我的類 -version - author *Test . java
3.2 標識符和關鍵字
Java 語言也和其他編程語言一樣,使用標識符作為變量、對象的名字,也提供了系列的關鍵字用以實現特別的功能。本節詳細介紹Java 語言的標識符和關鍵字等內容。
3.2.1 分隔符
Java 語言里的分號(;)、花括號({})、方括號([])、圓括號(()) 、空格、圓點( . )都具有特殊的分隔作用,因此被統稱為分隔符。
1. 分號
Java 語言里對語句的分隔不是使用回車來完成的, Java 語言采用分號(;) 作為語句的分隔, 因此每個Java i吾句必須使用分號作為結尾。Java 程序允許一行書寫多個語句,每個語句之間以分號隔開即可: 一個語句也可以跨多行,只要在最后結束的地方使用分號結束即可。
例如,下面語句都是合法的Java 語句。
int age=25;String name="Peter"; String hello="Hello"+ "你好";
值得指出的是, Java 語句可以跨越多行書寫, 但一個字符串、變量名不能跨越多行。例如, 下面的Java 語句是錯誤的。
1 //字符串內容不能跨行 2 String name="ddddddd 3 assss"; 4 //變量名不能跨行 5 String s 6 ex="男";
不僅如此,雖然Java 語法允許一行書寫多個語旬,但從程序可讀性角度來看,應該避免在一行書寫多個語句。
2. 花括號
花括號的作用就是定義一個代碼塊, 一個代碼塊指的就是" {"和"}"所包含的一段代碼, 代碼塊在邏輯上是一個整體。對Java 語言而言, 類定義部分必須放在一個代碼塊里,方法體部分也必須放在一個代碼塊里。除此之外,條件語句中的條件執行體和循環語句中的循環體通常也放在代碼塊里。
花括號一般是成對出現的, 有一個"{"則必然有一個"}飛反之亦然。
3. 方括號
方括號的主要作用是用於訪問數組元素, 方括號通常緊跟數組變量名,而方括號里指定希望訪問的數組元素的索引。
例如,如下代碼:
1 //定義一個數組,設置其中某個元素數值 2 a[3]=5;
4. 圓括號
圓括號是一個功能非常豐富的分隔符: 定義方法時必須使用圓括號來包含所有的形參聲明, 調用方法時也必須使用圓括號來傳入實參值; 不僅如此,圓括號還可以將表達式中某個部分括成一個整體, 保證這個部分優先計算:除此之外,圓括號還可以作為強制類型轉換的運算符。
關於圓括號分隔符在后面還有更進一步的介紹, 此處不再贅述。
5. 空格
Java 語言使用空格分隔一條語句的不同部分。Java 語言是一門格式自由的語言,所以空格幾乎可以出現在Java 程序的任何地方,也可以出現任意多個空格,但不要使用空格把一個變量名隔開成兩個,這將導致程序出錯。
Java 語言中的空格包含空格符(Space) 、制表符(Tab) 和回車C(Enter ) 等。
除此之外, Java 源程序還會使用空格來合理縮進Java 代碼,從而提供更好的可讀性。
6. 圓點
圓點(.)通常用作類/對象和它的成員(包括成員變量、方法和內部類)之間的分隔符,表明調用某個類或某個實例的指定成員。關於圓點分隔符的用法,后面還會有更進一步的介紹,此處不再贅述。
3.2.2 Java 9 的標識符規則
標識符就是用於給程序中變量、類、方法命名的符號。Java 語言的標識符必須以字母、下畫線(_)、美元符( $)開頭,后面可以跟任意數目的字母、數字、下畫線( )和美元符($) 。此處的宇母並不局限於26 個英文字母,甚至可以包含中文字符、日文字符等。
由於Java 9 支持Unicode 8.0 字符集,因此Java 的標識符可以使用Unicode 8.0 所能表示的多種語言的字符。Java 語言是區分大小寫的,因此abc 和Abc 是兩個不同的標識符。
Java 9 規定: 不允許使用單獨的下畫線(_)作為標識符。也就是說,下畫線必須與其他字符組合在一起才能作為標識符。
使用標識符時,需要注意如下規則。
- 標識符可以由字母、數字、下畫線(_)和美元符($)組成,其中數字不能打頭。
- 標識符不能是Java 關鍵字和保留字,但可以包含關鍵字和保留字。
- 標識符不能包含空格。
- 標識符只能包含美元符($) ,不能包含@、#等其他特殊字符。
3.2.3 Java 關鍵字
Java 語言中有一些具有特殊用途的單詞被稱為關鍵宇( keyword ) , 當定義標識符時,不要讓標識符和關鍵字相同, 否則將引起錯誤。例如,下面代碼將無法通過編譯。
// 試圖定義一個名為boolea口的變量,但boolean 是關鍵字,不能作為標識符 int boolean;
Java 的所有關鍵宇都是小寫的, TRUE 、FALSE 和NULL 都不是Java 關鍵字。
Java 一共包含50 個關鍵字,如表3. 1 所示。
上面的50 個關鍵字中, enum 是從Java 5 新增的關鍵字,用於定義一個枚舉。而goto 和const 這兩個關鍵字也被稱為保留宇( reserved word) ,保留字的意思是, Java 現在還未使用這兩個關鍵字,但可能在未來的Java 版本中使用這兩個關鍵宇;不僅如此, Java 還提供了三個特殊的直接量(literal) : true 、false 和null; Java 語言的標識符也不能使用這三個特殊的直接量。
3.3 數據類型分類
Java 語言是強類型( strongly typed ) 語言,強類型包含兩方面的含義: ①所有的變量必須先聲明、后使用: ②指定類型的變量只能接受類型與之匹配的值。這意味着每個變量和每個表達式都有一個在編譯時就確定的類型。類型限制了一個變量能被賦的值, 限制了一個表達式可以產生的值,限制了在這些值上可以進行的操作, 並確定了這些操作的含義。
強類型語言可以在編譯時進行更嚴格的語法檢查,從而減少編程錯誤。
聲明變量的語法非常簡單,只要指定變量的類型和變量名即可,如下所示:
type varName [=初始值] ;
上面語法中,定義變量時既可指定初始值,也可不指定初始值。隨着變量的作用范圍的不同 ,變量還可能使用其他修飾符。但不管是哪種變量,定義變量至少需要指定變量類型和變量名兩個部分。定義變量時的變量類型可以是Java 語言支持的所有類型。
Java 語言支持的類型分為兩類:基本類型(Primitive Type ) 和引用類型( Reference Type ) 。
基本類型包括boolean 類型和數值類型。數值類型有整數類型和浮點類型。整數類型包括byte 、short 、int 、long 、char , 浮點類型包括float 和double 。
引用類型包括類、接口和數組類型,還有一種特殊的null 類型。所謂引用數據類型就是對一個對象的引用,對象包括實例和數組兩種。實際上,引用類型變量就是一個指針,只是Java 語言里不再使用指針這個說法。
空類型(null type ) 就是null 值的類型, 這種類型沒有名稱。因為null 類型沒有名稱,所以不可能聲明一個null 類型的變量或者轉換到null 類型。空引用(null )是null 類型變量唯一的值。空引用(null )可以轉換為任何引用類型。
在實際開發中, 程序員可以忽略null 類型,假定null 只是引用類型的一個特殊直接量。
3.4 基本數據類型
Java 的基本數據類型分為兩大類: boolean 類型和數值類型。而數值類型又可以分為整數類型和浮點類型,整數類型里的字符類型也可被單獨對待。因此常把Java 里的基本數據類型分為4 類,如圖3.8所示。
Java 只包含這8 種基本數據類型,值得指出的是,字符串不是基本數據類型, 字符串是一個類,也就是一個引用數據類型。
3.4 .1 整型
通常所說的整型,實際指的是如下4 種類型。
- byte: 一個byte 類型整數在內存里占8 位,表數范圍是: - 128(-27) - 127(27一1) 。
- short: 一個short 類型整數在內存里占16 位,表數范圍是: - 32768(- 215) - 32767(215 一1) 。
- int: 一個int 類型整數在內存里占32 位, 表數范圍是: 一2147483648(-231 ) - 2147483647(231 - 1) 。
- long: 一個long 類型整數在內存里占64 位,表數范圍是: (-263 ) -(263 - 1) 。
int 是最常用的整數類型,因此在通常情況下, 直接給出一個整數值默認就是int 類型。除此之外,有如下兩種情形必須指出。
- 如果直接將一個較小的整數值(在byte 或short 類型的表數范圍內〉賦給一個byte 或short 變量,系統會自動把這個整數值當成byte 或者short 類型來處理。
- 如果使用一個巨大的整數值(超出了int 類型的表數范圍)時, Java 不會自動把這個整數值當成long 類型來處理。如果希望系統把一個整數值當成long 類型來處理,應在這個整數值后增加l或者L 作為后綴。通常推薦使用L ,因為英文字母l 很容易跟數字1 搞混。
下面的代碼片段驗證了上面的結論。
1 // 下面代碼是正確的,系統會自動把56當成byte類型處理 2 byte a = 56; 3 /* 4 下面代碼是錯的,系統不會把9999999999999當成long類型處理, 5 所以超出int的表數范圍,從而引起錯誤 6 */ 7 // long bigValue = 9999999999999; 8 // 下面代碼是正確的,在巨大的整數值后使用L后綴,強制使用long類型 9 long bigValue2 = 9223372036854775807L;
Java 中整數值有4 種表示方式: 十進制、二進制、八進制和十六進制,其中二進制的整數以0b 或0B 開頭;八進制的整數以0開頭:十六進制的整數以0x 或者0X 開頭,其中10- 15 分別以a-f ( 此處的a-f 不區分大小寫)來表示。
下面的代碼片段分別使用八進制和十六進制的數。
1 // 以0開頭的整數值是8進制的整數 2 int octalValue = 013; 3 System.out.println(octalValue); 4 // 以0x或0X開頭的整數值是16進制的整數 5 int hexValue1 = 0x13; 6 int hexValue2 = 0XaF; 7 8 System.out.println(hexValue1); 9 System.out.println(hexValue2);
在某些時候,程序需要直接使用二進制整數, 二進制整數更"真實",更能表達整數在內存中的存在形式。不僅如此, 有些程序(尤其在開發一些游戲時)使用二進制整數會更便捷。
從Java 7 開始新增了對二進制整數的支持, 二進制的整數以0b 或者0B 開頭。程序片段如下。
1 // 定義二個8位的二進制數 2 int binVal1 = 0b11010100; 3 byte binVal2 = 0B01101001; 4 // 定義一個32位的二進制數,最高位是符號位。 5 int binVal3 = 0B10000000000000000000000000000011; 6 System.out.println(binVal1); // 輸出212 7 System.out.println(binVal2); // 輸出105 8 System.out.println(binVal3); // 輸出-2147483645
當定義32 位的二進制整數時,最高位其實是符號位,當符號位是1時,表明它是一個負數,負數在計算機里是以補碼的形式存在的,因此還需要換算成原碼。
所有數字在計算機底層都是以二進制形式存在的,原碼是直接將一個數值換算成二進制數。但計算機以補碼的形式保存所有的整數。補碼的計算規則: 正數的補碼和原碼完全相同,負數的補碼是其反碼加1 ;反碼是對原碼按住取反,只是最高位(符號位)保持不變。
將上面的二進制整數binVal3 轉換成十進制數的過程如圖3.9 所示。
正如前面所指出的,整數值默認就是int 類型,因此使用二進制形式定義整數時,二進制整數默認占32 位, 其中第32位是符號位:如果在二進制整數后添加l 或L 后綴,那么這個二進制整數默認占64 位,其中第64 位是符號位。
例如如下程序。
1 /* 2 定義一個8位的二進制,該數值默認占32位,因此它是一個正數。 3 只是強制類型轉換成byte時產生了溢出,最終導致binVal4變成了-23 4 */ 5 byte binVal4 = (byte)0b11101001; 6 /* 7 定義一個32位的二進制數,最高位是1。 8 但由於數值后添加了L后綴,因此該整數的實際占64位,第32位的1不是符號位。 9 因此binVal5的值等於2的31次方 + 2 + 1 10 */ 11 long binVal5 = 0B10000000000000000000000000000011L; 12 System.out.println(binVal4); // 輸出-23 13 System.out.println(binVal5); // 輸出2147483651
上面程序中粗體字代碼與前面程序片段的粗體字代碼基本相同,只是在定義二進制整數時添加了"L" 后綴,這就表明把它當成long 類型處理,因此該整數實際占64 位。此時的第32 位不再是符號位,因此它依然是一個正數。
至於程序中的byte bin Val4 = (byte )0b11101001;代碼,其中0bl1101001 依然是一個32位的正整數, 只是程序進行強制類型轉換時發生了溢出,導致它變成了負數。
3.4.2 字符型
字符型通常用於表示單個的宇符, 字符型值必須使用單引號( ')括起來。Java 語言使用16 位的Unicode 字符集作為編碼方式,而Unicode 被設計成支持世界上所有書面語言的字符,包括中文字符,因此Java 程序支持各種語言的字符。
字符型值有如下三種表示形式。
- 直接通過單個字符來指定字符型值,例如'A' 、'9'和'0'等。
- 通過轉義字符表示特殊字符型值,例如'\n'、'\t'等。
- 直接使用Unicode 值來表示字符型值,格式是'\uXXXX' ,其中xxxx 代表一個十六進制的整數。
Java 語言中常用的轉義字符如表3.2 所示。
字符型值也可以采用十六進制編碼方式來表示,范圍是'\u0000'~'\uFFFF' , 一共可以表示65536 個字符,其中前256 個(‘\u0000'~'\u00FF' ) 字符和ASCII 碼中的字符完全重舍。
由於計算機底層保存字符時,實際是保存該字符對應的編號,因此char 類型的值也可直接作為整型值來使用, 它相當於一個16位的無符號整數, 表數范圍是0~65535 。
如果把0-65535 范圍內的一個int 整數賦給char 類型變量,系統會自動把這個int 整數當成char 類型來處理。
下面程序簡單示范了字符型變量的用法。
1 public class CharTest 2 { 3 public static void main(String[] args) 4 { 5 // 直接指定單個字符作為字符值 6 char aChar = 'a'; 7 // 使用轉義字符來作為字符值 8 char enterChar = '\r'; 9 // 使用Unicode編碼值來指定字符值 10 char ch = '\u9999'; 11 // 將輸出一個'香'字符 12 System.out.println(ch); 13 // 定義一個'瘋'字符值 14 char zhong = '瘋'; 15 // 直接將一個char變量當成int類型變量使用 16 int zhongValue = zhong; 17 System.out.println(zhongValue); 18 // 直接把一個0~65535范圍內的int整數賦給一個char變量 19 char c = 97; 20 System.out.println(c); 21 } 22 }
Java 沒有提供表示字符串的基本數據類型,而是通過String 類來表示宇符串,由於字符串由多個字符組成,因此字符串要使用雙引號括起來。
//下面代碼定義了一個s 變量, 它是一個字符串實例的引用,它是一個引用類型的變量 String s = "曾經滄海難為水,除卻巫山不是雲" ;
3.4.3 浮點型
Java 的浮點類型有兩種: float 和double o Java 的浮點類型有固定的表數范圍和宇段長度, 宇段長度和表數范圍與機器無關。Java 的浮點數遵循IEEE 754 標准,采用二進制數據的科學計數法來表示浮點數, 對於float 型數值,第1 位是符號位, 接下來8 位表示指數, 再接下來的23 位表示尾數:對於double 類型數值,第1位也是符號位,接下來的11 位表示指數, 再接下來的52 位表示尾數。
double 類型代表雙精度浮點數, float 類型代表單精度浮點數。一個double 類型的數值占8 字節、64 位, 一個float 類型的數值占4 字節、32 位。
Java 語言的浮點數有兩種表示形式。
- 十進制數形式:這種形式就是簡單的浮點數,例如5.12 、512 . 0 、. 512 。浮點數必須包含一個小數點,否則會被當成int類型處理。
- 科學計數法形式:例如5.12e2 (5.12x102), 5.l2E2 (也是5 .12x 102)。
必須指出的是,只有浮點類型的數值才可以使用科學計數法形式表示。例如, 51200 是一個int 類型的值, 但512E2 則是浮點類型的值。
Java 語言的浮點類型默認是double 類型,如果希望Java 把一個浮點類型值當成float 類型處理,應該在這個浮點類型值后緊跟f 或F 。例如, 5.12 代表一個double 類型的值,占64 位的內存空間; 5.12f或者5.12F才表示一個float 類型的值, 占32 位的內存空間。當然,也可以在一個浮點數后添加d或D后綴, 強制指定是double 類型,但通常沒必要。
Java 還提供了三個特殊的浮點數值:正無窮大、負無窮大和非數,用於表示溢出和出錯。例如,使用一個正數除以0 將得到正無窮大,使用一個負數除以0 將得到負無窮大, 0.0 除以0.0 或對一個負數開方將得到一個非數。正無窮大通過Double 或Float 類的POSITIVE_FINITY 表示:負無窮大通過Double 或Float 類的NEGATIVE_INFINITY 表示,非數通過Double 或Float 類的NaN 表示。
必須指出的是,所有的正無窮大數值都是相等的,所有的負無窮大數值都是相等的: 而NaN 不與任何數值相等, 甚至和NaN 都不相等。
下面程序示范了上面介紹的關於浮點數的各個知識點。
1 public class FloatTest 2 { 3 public static void main(String[] args) 4 { 5 float af = 5.2345556f; 6 // 下面將看到af的值已經發生了改變 7 System.out.println(af); 8 double a = 0.0; 9 double c = Double.NEGATIVE_INFINITY; 10 float d = Float.NEGATIVE_INFINITY; 11 // 看到float和double的負無窮大是相等的。 12 System.out.println(c == d); 13 // 0.0除以0.0將出現非數 14 System.out.println(a / a); 15 // 兩個非數之間是不相等的 16 System.out.println(a / a == Float.NaN); 17 // 所有正無窮大都是相等的 18 System.out.println(6.0 / 0 == 555.0/0); 19 // 負數除以0.0得到負無窮大 20 System.out.println(-8 / a); 21 // 下面代碼將拋出除以0的異常 22 // System.out.println(0 / 0); 23 } 24 }
3.4.4 數值中使用下畫線分隔
正如前面程序中看到的,當程序中用到的數值位數特別多時,程序員眼睛"看花"了都看不清到底有多少位數。為了解決這種問題, Java 7 引入了一個新功能:程序員可以在數值中使用下畫線,不管是整型數值,還是浮點型數值,都可以自由地使用下畫線。通過使用下畫線分隔, 可以更直觀地分辨數值中到底包含多少位。如下面程序所示。
1 public class UnderscoreTest 2 { 3 public static void main(String[] args) 4 { 5 // 定義一個32位的二進制數,最高位是符號位。 6 int binVal = 0B1000_0000_0000_0000_0000_0000_0000_0011; 7 double pi = 3.14_15_92_65_36; 8 System.out.println(binVal); 9 System.out.println(pi); 10 double height = 8_8_4_8.23; 11 System.out.println(height); 12 } 13 }
3.4.5 布爾型
布爾型只有一個boolean 類型,用於表示邏輯上的"真"或"假"。在Java 語言中, boolean 類型的數值只能是true 或false ,不能用0或者非0 來代表。其他基本數據類型的值也不能轉換成boolean 類型。
例如,下面代碼定義了兩個boolean 類型的變量,並指定初始值。
1 public class BooleanTest 2 { 3 public static void main(String[] args) 4 { 5 boolean b1 = true; 6 boolean b2 = false; 7 // 下面代碼將出現錯誤:字符串不能直接變成boolean型的值 8 // boolean b3 = "true"; 9 // 使用boolean和字符串進行連接運算,boolean會自動轉換成字符串 10 String str = true + ""; 11 // 下面將輸出true 12 System.out.println(str); 13 } 14 }
字符串"true"和"false"不會直接轉換成boolean 類型,但如果使用一個boolean 類型的值和字符串進行連接運算,則boolean 類型的值將會自動轉換成字符串。
boolean 類型的值或變量主要用做旗標來進行流程控制, Java 語言中使用boolean 類型的變量或值控制的流程主要有如下幾種。
- if 條件控制語句
- while 循環控制語句
- do while 循環控制語句
- for 循環控制語句
除此之外, boolean 類型的變量和值還可在三目運算符(? :)中使用。
3.5 基本類型的類型轉換
在Java 程序中,不同的基本類型的值經常需要進行相互轉換。Java 語言所提供的7 種數值類型之間可以相互轉換,有兩種類型轉換方式:自動類型轉換和強制類型轉換。
3.5.1 自動類型轉換
Java 所有的數值型變量可以相互轉換,如果系統支持把某種基本類型的值直接賦給另一種基本類型的變量,則這種方式被稱為自動類型轉換。當把一個表數范圍小的數值或變量直接賦給另一個表數范圍大的變量時,系統將可以進行自動類型轉換:否則就需要強制轉換。
表數范圍小的可以向表數范圍大的進行自動類型轉換,就如同有兩瓶水,當把小瓶里的水倒入大瓶中時不會有任何問題。Java 支持自動類型轉換的類型如圖3 .1 0 所示。
圖3 . 10 中所示的箭頭左邊的數值類型可以自動類型轉換為箭頭右邊的數值類型。下面程序示范了自動類型轉換。
1 public class AutoConversion 2 { 3 public static void main(String[] args) 4 { 5 int a = 6; 6 // int可以自動轉換為float類型 7 float f = a; 8 // 下面將輸出6.0 9 System.out.println(f); 10 // 定義一個byte類型的整數變量 11 byte b = 9; 12 // 下面代碼將出錯,byte型不能自動類型轉換為char型 13 // char c = b; 14 // 下面是byte型變量可以自動類型轉換為double型 15 double d = b; 16 // 下面將輸出9.0 17 System.out.println(d); 18 } 19 }
不僅如此,當把任何基本類型的值和字符串值進行連接運算時,基本類型的值將自動類型轉換為字符串類型,雖然字符串類型不是基本類型,而是引用類型。因此,如果希望把基本類型的值轉換為對應的字符串時,可以把基本類型的值和一個空字符串進行連接。看如下代碼。
1 public class PrimitiveAndString 2 { 3 public static void main(String[] args) 4 { 5 // 下面代碼是錯的,因為5是一個整數,不能直接賦給一個字符串 6 // String str1 = 5; 7 // 一個基本類型值和字符串進行連接運算時,基本類型值自動轉換為字符串 8 String str2 = 3.5f + ""; 9 // 下面輸出3.5 10 System.out.println(str2); 11 // 下面語句輸出7Hello! 12 System.out.println(3 + 4 + "Hello!"); 13 // 下面語句輸出Hello!34,因為Hello! + 3會把3當成字符串處理, 14 // 而后再把4當成字符串處理 15 System.out.println("Hello!" + 3 + 4); 16 } 17 }
3.5.2 強制類型轉換
如果希望把圖3.10 中箭頭右邊的類型轉換為左邊的類型,則必須進行強制類型轉換,強制類型轉換的語法格式是: (targetType )value ,強制類型轉換的運算符是圓括號(()) 。當進行強制類型轉換時,類似於把一個大瓶子里的水倒入一個小瓶子,如果大瓶子里的水不多還好,但如果大瓶子里的水很多,將會引起溢出,從而造成數據丟失。這種轉換也被稱為"縮小轉換( Narrow Conversion) " 。
下面程序示范了強制類型轉換。
1 public class NarrowConversion 2 { 3 public static void main(String[] args) 4 { 5 int iValue = 233; 6 // 強制把一個int類型的值轉換為byte類型的值 7 byte bValue = (byte)iValue; 8 // 將輸出-23 9 System.out.println(bValue); 10 double dValue = 3.98; 11 // 強制把一個double類型的值轉換為int 12 int tol = (int)dValue; 13 // 將輸出3 14 System.out.println(tol); 15 } 16 }
經常上網的讀者可能會發現有些網頁上會包含臨時生成的驗證字符串,那么這個隨機字符串是如何生成的呢?可以先隨機生成一個在指定范圍內的mt 數字(如果希望生成小寫字母,就在97~122 之間),然后將其強制轉換成char 類型,再將多次生成的字符連綴起來即可。
下面程序示范了如何生成一個6 位的隨機字符串.
1 public class RandomStr 2 { 3 public static void main(String[] args) 4 { 5 // 定義一個空字符串 6 String result = ""; 7 // 進行6次循環 8 for(int i = 0 ; i < 6 ; i ++) 9 { 10 // 生成一個97~122的int型的整數 11 int intVal = (int)(Math.random() * 26 + 97); 12 // 將intValue強制轉換為char后連接到result后面 13 result = result + (char)intVal; 14 } 15 // 輸出隨機字符串 16 System.out.println(result); 17 } 18 }
還有下面一行容易出錯的代碼:
//直接把5.6 賦值給float 類型變量將出現錯誤, 因為5.6 默認是doub1e 類型 float a = 5.6
上面代碼中的5.6 默認是一個double 類型的浮點數,因此將5.6 賦值給一個float 類型變量將導致錯誤,必須使用強制類型轉換才可以,即將上面代碼改為如下形式:
float a = (float)5.6
在通常情況下,字符串不能直接轉換為基本類型,但通過基本類型對應的包裝類則可以實現把字符串轉換成基本類型。例如,把字符串轉換成int 類型,則可通過如下代碼實現:
String a = "45"; //使用Integer 的方法將一個字符串轉換成工川類型 int iVa1ue =Integer.parseInt (a);
Java 為8 種基本類型都提供了對應的包裝類: boolean 對應Boolean 、byte 對應Byte 、short 對應Short、int 對應Integer 、long 對應Long 、char 對應Character 、float 對應Float 、double 對應Double , 8 個包裝類都提供了一個parseXxx(String str)靜態方法用於將字符串轉換成基本類型。
3.5.3 表達式類型的自動提升
當一個算術表達式中包含多個基本類型的值時,整個算術表達式的數據類型將發生自動提升。Java定義了如下的自動提升規則。
- 所有的byte 類型、short 類型和char 類型將被提升到int 類型。
- 整個算術表達式的數據類型自動提升到與表達式中最高等級操作數同樣的類型。操作數的等級排列如圖3.10 所示,位於箭頭右邊類型的等級高於位於箭頭左邊類型的等級。
public class AutoPromote { public static void main(String[] args) { // 定義一個short類型變量 short sValue = 5; // 下面代碼將出錯:表達式中的sValue將自動提升到int類型, // 則右邊的表達式類型為int,將一個int類型賦給short類型的變量將發生錯誤。 // sValue = sValue - 2; byte b = 40; char c = 'a'; int i = 23; double d = .314; // 右邊表達式中在最高等級操作數為d(double型) // 則右邊表達式的類型為double型,故賦給一個double型變量 double result = b + c + i * d; // 將輸出144.222 System.out.println(result); int val = 3; // 右邊表達式中2個操作數都是int,故右邊表達式的類型為int // 因此,雖然23/3不能除盡,依然得到一個int整數 int intResult = 23 / val; System.out.println(intResult); // 將輸出7 // 輸出字符串Hello!a7 System.out.println("Hello!" + 'a' + 7); // 輸出字符串104Hello! System.out.println('a' + 7 + "Hello!"); } }
3.6 直接量
直接量是指在程序中通過源代碼直接給出的值, 例如在int a = 5 ;這行代碼中, 為變量a 所分配的初始值5 就是一個直接量。
3.6.1 直接量的類型
並不是所有的數據類型都可以指定直接量, 能指定直接量的通常只有三種類型: 基本類型、字符串類型和null 類型。具體而言, Java 支持如下8 種類型的直接量。
- int 類型的直接量: 在程序中直接給出的整型數值, 可分為二進制、十進制、八進制和十六進制4 種,其中二進制需要以0B 或0b 開頭, 八進制需要以0 開頭, 十六進制需要以0x 或0X 開頭。例如123 、012 (對應十進制的10 ) 、0x12 (對應十進制的18 ) 等。
- long 類型的直接量: 在整型數值后添加l 或L 后就變成了long 類型的直接量。例如3L 、0x12L(對應十進制的18 L) 。
- float 類型的直接量: 在一個浮點數后添加f 或F 就變成了float 類型的直接量,這個浮點數可以是標准小數形式,也可以是科學計數法形式。例如5.34F 、3. 14E5f。
- double 類型的直接量:直接給出一個標准小數形式或者科學計數法形式的浮點數就是double 類型的直接量。例如5.34 、3.14E5
- boolean 類型的直接量: 這個類型的直接量只有true 和false 。
- char 類型的直接量: char 類型的直接量有三種形式,分別是用單引號括起來的字符、轉義字符和Unicode 值表示的字符。例如'a' 、W和'\u0061 ' 。
- String 類型的直接量: 一個用雙引號括起來的字符序列就是String 類型的直接量。
- null 類型的直接量: 這個類型的直接量只有一個值,即null 。
在上面的8 種類型的直接量中, null 類型是一種特殊類型,它只有一個值: null ,而且這個直接量可以賦給任何引用類型的變量,用以表示這個引用類型變量中保存的地址為空,即還未指向任何有效對象。
3.7 運算符
運算符是一種特殊的符號,用以表示數據的運算、賦值和比較等。Java 語言使用運算符將一個或多個操作數連綴成執行性語句,用以實現特定功能。
Java 語言中的運算符可分為如下幾種。
- 算術運算符
- 賦值運算符
- 比較運算符
- 邏輯運算符
- 位運算符
- 類型相關運算符
3.7.1 算術運算符
Java 支持所有的基本算術運算符,這些算術運算符用於執行基本的數學運算:加、減、乘、除和求余等。下面是7個基本的算術運算符。
+ :加法運算符。例如如下代碼:
double a = 5.2 ; double b = 3.1; double sum = a + b ; // sum 的值為8 . 3 System.out.println(sum);
除此之外, +還可以作為字符串的連接運算符。
-: 減法運算符。例如如下代碼:
double a = 5.2 ; double b = 3.1; doub1e sub = a - b ; //sub 的值為2 . 1 System.out.println (sub) ;
*: 乘法運算符。例如如下代碼:
double a = 5.2 ; double b = 3.1 ; double multiply = a * b ; //multiply 的值為16.12 System. out.println(mu1tiply) ;
/ : 除法運算符。除法運算符有些特殊,如果除法運算符的兩個操作數都是整數類型,則計算結果也是整數, 就是將自然除法的結果截斷取整,例如19/4的結果是4 , 而不是5 。如果除法運算符的兩個操作數都是整數類型,則除數不可以是0 , 否則將引發除以零異常。
但如果除法運算符的兩個操作數有一個是浮點數,或者兩個都是浮點數,則計算結果也是浮點數,這個結果就是自然除法的結果。而且此時允許除數是0, 或者0.0,得到結果是正無窮大或負無窮大。看下面代碼。
1 public class DivTest 2 { 3 public static void main(String[] args) 4 { 5 double a = 5.2; 6 double b = 3.1; 7 double div = a / b; 8 // div的值將是1.6774193548387097 9 System.out.println(div); 10 // 輸出正無窮大:Infinity 11 System.out.println("5除以0.0的結果是:" + 5 / 0.0); 12 // 輸出負無窮大:-Infinity 13 System.out.println("-5除以0.0的結果是:" + - 5 / 0.0); 14 // 下面代碼將出現異常 15 // java.lang.ArithmeticException: / by zero 16 System.out.println("-5除以0的結果是::" + -5 / 0); 17 } 18 }
% : 求余運算符。求余運算的結果不一定總是整數, 它的計算結果是使用第一個操作數除以第二個操作數, 得到一個整除的結果后剩下的值就是余數。由於求余運算也需要進行除法運算,因此如果求余運算的兩個操作數都是整數類型,則求余運算的第二個操作數不能是0 ,否則將引發除以零異常。如果求余運算的兩個操作數中有一個或者兩個都是浮點數,則允許第二個操作數是0 或0.0 ,只是求余運算的結果是非數: NaN. 0 或0.0 對零以外的任何數求余都將得到0 或0.0 。看如下程序。
1 public class ModTest 2 { 3 public static void main(String[] args) 4 { 5 double a = 5.2; 6 double b = 3.1; 7 double mod = a % b; 8 9 System.out.println(mod); // mod的值為2.1 10 System.out.println("5對0.0求余的結果是:" + 5 % 0.0); // 輸出非數:NaN 11 System.out.println("-5.0對0求余的結果是:" + -5.0 % 0); // 輸出非數:NaN 12 System.out.println("0對5.0求余的結果是:" + 0 % 5.0); // 輸出0.0 13 System.out.println("0對0.0求余的結果是:" + 0 % 0.0); // 輸出非數:NaN 14 // 下面代碼將出現異常:java.lang.ArithmeticException: / by zero 15 System.out.println("-5對0求余的結果是:" + -5 % 0); 16 } 17 }
++ : 自加。該運算符有兩個要點: ①自加是單目運算符,只能操作一個操作數: ②自加運算符只能操作單個數值型( 整型、浮點型都行)的變量, 不能操作常量或表達式。運算符既可以出現在操作數的左邊,也可以出現在操作數的右邊。但出現在左邊和右邊的效果是不一樣的。如果把++放在左邊,則先把操作數加1,然后才把操作數放入表達式中運算:如果把++放在右邊,則先把操作數放入表達式中運算,然后才把操作數加1 。看如下代碼:
int a = 5 ; //讓a 先執行算術運算,然后自加 int b = a++ + 6; //輸出a 的值為6 , b 的值為11 System . out.print1n(a + " \ n " + b);
執行完后, a 的值為6 ,而b 的值為11 。當++在操作數右邊時,先執行a+6 的運算(此時a 的值為5) ,然后對a 加1 。對比下面代碼:
int a = 5 ; //讓a 先自加,然后執行算術運算 int b = ++a + 6; //輸出a 的值為6 , b 的值為12 Systern.out .print1n(a + " \ n " + b);
執行的結果是a 的值為6 , b 的值為12 。當++在操作數左邊時,先對a加1, 然后執行a+6 的運算(此時a 的值為6) ,因此b為12。
一一:自減。也是單目運算符,用法與++基本相似,只是將操作數的值減1。
Java 並沒有提供其他更復雜的運算符,如果需要完成乘方、開方等運算,則可借助於java.lang.Math類的工具方法完成復雜的數學運算,見如下代碼。
public class MathTest { public static void main(String[] args) { double a = 3.2; // 定義變量a為3.2 // 求a的5次方,並將計算結果賦為b。 double b = Math.pow(a , 5); System.out.println(b); // 輸出b的值。 // 求a的平方根,並將結果賦給c double c = Math.sqrt(a); System.out.println(c); // 輸出c的值。 // 計算隨機數,返回一個0~1之間的偽隨機數。 double d = Math.random(); System.out.println(d); // 輸出隨機數d的值 // 求1.57的sin函數值:1.57被當成弧度數 double e = Math.sin(1.57); System.out.println(e); // 輸出接近1 } }
3.7.2 賦值運算符
賦值運算符用於為變量指定變量值,與C 類似, Java 也使用=作為賦值運算符。通常,使用賦值運算符將一個直接量值賦給變量。例如如下代碼。
public class AssignOperatorTest { public static void main(String[] args) { String str = "Java"; // 為變量str賦值為Java double pi = 3.14; // 為變量pi賦值為3.14 boolean visited = true; // 為變量visited賦值為true String str2 = str; // 將變量str的值賦給str2 int a; int b; int c; // 通過為a, b , c賦值,三個變量的值都是7 a = b = c = 7; // 輸出三個變量的值。 System.out.println(a + "\n" + b + "\n" + c); double d1 = 12.34; double d2 = d1 + 5; // 將表達式的值賦給d2 System.out.println(d2); // 輸出d2的值,將輸出17.34 } }
3.7.3 位運算符
Java 支持的位運算符有如下7 個。
- & : 按位與。當兩位同時為1時才返回1。
- | : 按位或。只要有一位為1即可返回1。
- ~ : 按位非。單目運算符,將操作數的每個位(包括符號位〉全部取反。
- ^ :按位異或。當兩位相同時返回0, 不同時返回1。
- <<:左移運算符。
- >> : 右移運算符。
- >>> : 無符號右移運算符。
一般來說,位運算符只能操作整數類型的變量或值。位運算符的運算法則如表3. 3 所示。表3.3
按位非只需要一個操作數,這個運算符將把操作數在計算機底層的二進制碼按位(包括符號位)取反。如下代碼測試了按位與和按位或運算的運行結果。
public class BitOperatorTest { public static void main(String[] args) { // System.out.println(2.5 & 3.0); System.out.println(5 & 9); // 將輸出1 System.out.println(5 | 9); // 將輸出13 System.out.println(~-5); // 將輸出4 System.out.println(5 ^ 9); // 將輸出12 System.out.println(5 << 2); // 輸出20 System.out.println(-5 << 2); // 輸出-20 System.out.println(-5 >> 2); // 輸出-2 System.out.println(-5 >>> 2); // 輸出1073741822 } }
執行5 ^ 9 的結果是12 。下面通過圖3 .1 3 來介紹運算原理。
而5 ^ 9 的運算過程如圖3 . 14 所示。
左移運算符是將操作數的二進制碼整體左移指定位數,左移后右邊空出來的位以0 填充。例如如下代碼(程序清單同上) :
下面以-5 為例來介紹左移運算的運算過程,如圖3.15 所示。
在圖3.15 中, 上面的32 位數是-5 的補碼,左移兩位后得到一個二進制補碼,這個二進制補碼的最高位是L 表明是一個負數,換算成十進制數就是-20 。
Java 的右移運算符有兩個: >>和>>>,對於>>運算符而言,把第一個操作數的二進制碼右移指定位數后,左邊空出來的位以原來的符號位填充,即如果第一個操作數原來是正數,則左邊補0; 如果第一個操作數是負數,則左邊補1。>>>是無符號右移運算符, 它把第一個操作數的二進制碼右移指定位數后, 左邊空出來的位總是以0填充。
進行移位運算時還要遵循如下規則。
- 對於低於int 類型(如byte 、short 和char) 的操作數總是先自動類型轉換為int 類型后再移位。
- 對於int 類型的整數移位后>b , 當b>32 時,系統先用b 對32 求余(因為int 類型只有32 位),得到的結果才是真正移位的位數。例如, a>>33 和a>>1 的結果完全一樣,而a>>32 的結果和a 相同。
- 對於long 類型的整數移位a>>b ,當b>64 時,總是先用b 對64 求余(因為long 類型是64 位) ,得到的結果才是真正移位的位數。
當進行移位運算時,只要被移位的二進制碼沒有發生有效位的數字丟失(對於正數而言,通常指被移出的位全部都是0),不難發現左移n 位就相當於來以2的n次方,右移n
位則是除以2的n次方。不僅如此,進行移位運算不會改變操作數本身,只走得到了一個新的運算結果,而原來的操作數本身是不會改變的。
3.7.4 擴展后的賦值運算符
賦值運算符可與算術運算符、位移運算符結合,擴展成功能更加強大的運算符。擴展后的賦值運算符如下。
- +=;對於x+=y ,即對應於x=x+y 。
- -=;對於x 一=y ,即對應於x = x - y。
- *=;對於x *= y ,即對應於x=x*y 。
- /=;對於x /=y ,即對應於x =x / y 。
- %=;對於x%=y ,即對應於x=x %y 。
- &=;對於x &=y ,即對應於x=x &y 。
- |= ; 對於x I=y ,即對應於x=x |y 。
- ^=;對於x ^=y ,即對應於x=x ^ y 。
- <<=;對於x <<= y ,即對應於x= x << Y 。
- >>= ;對於x >>= y ,即對應於x = x>> Y 。
- >>>= ; 對於x >>>= y ,即對應於x = x >>> y 。
只要能使用這種擴展后的賦值運算符,通常都推薦使用它們。因為這種運算符不僅具有更好的性能,而且程序會更加健壯。下面程序示范了+=運算符的用法。
public class EnhanceAssignTest { public static void main(String[] args) { // 定義一個byte類型的變量 byte a = 5; // 下面語句出錯,因為5默認是int類型,a + 5就是int類型。 // 把int類型賦給byte類型的變量,所以出錯 // a = a + 5; // 定義一個byte類型的變量 byte b = 5; // 下面語句不會出現錯誤 b += 5; } }
3.7.5 比較運算符
比較運算符用於判斷兩個變量或常量的大小,比較運算的結果是一個布爾值( true 或false ) 。Java支持的比較運算符如下。
- > ; 大於,只支持左右兩邊操作數是數值類型。如果前面變量的值大於后面變量的值,則返回true 。
- >= ; 大於等於, 只支持左右兩邊操作數是數值類型。如果前面變量的值大於等於后面變量的值,則返回true 。
- <; 小於,只支持左右兩邊操作數是數值類型。如果前面變量的值小於后面變量的值, 則返回true 。
- <=;小於等於,只支持左右兩邊操作數是數值類型。如果前面變量的值小於等於后面變量的值,則返回true o
- ==; 等於,如果進行比較的兩個操作數都是數值類型,即使它們的數據類型不相同,只要它們的值相等,也都將返回true 。例如97 == 'a'返回true. 5.0 == 5 也返回true 。如果兩個操作數都是引用類型,那么只有當兩個引用變量的類型具有父子關系時才可以比較,而且這兩個引用必須指向同一個對象才會返回true 。Java 也支持兩個boolean 類型的值進行比較,例如. true = false將返回false 。
- !=:不等於,如果進行比較的兩個操作數都是數值類型,無論它們的數據類型是否相同, 只要它們的值不相等,也都將返回true 。如果兩個操作數都是引用類型,只有當兩個引用變量的類型具有父子關系時才可以比較,只要兩個引用指向的不是同一個對象就會返回true 。
下面程序示范了比較運算符的使用。
public class ComparableOperatorTest { public static void main(String[] args) { System.out.println("5是否大於 4.0:" + (5 > 4.0)); // 輸出true System.out.println("5和5.0是否相等:" + (5 == 5.0)); // 輸出true System.out.println("97和'a'是否相等:" + (97 == 'a')); // 輸出true System.out.println("true和false是否相等:" + (true == false)); // 輸出false // 創建2個ComparableOperatorTest對象,分別賦給t1和t2兩個引用 ComparableOperatorTest t1 = new ComparableOperatorTest(); ComparableOperatorTest t2 = new ComparableOperatorTest(); // t1和t2是同一個類的兩個實例的引用,所以可以比較, // 但t1和t2引用不同的對象,所以返回false System.out.println("t1是否等於t2:" + (t1 == t2)); // 直接將t1的值賦給t3,即讓t3指向t1指向的對象 ComparableOperatorTest t3 = t1; // t1和t3指向同一個對象,所以返回true System.out.println("t1是否等於t3:" + (t1 == t3)); } }
3.7.6 邏輯運算符
邏輯運算符用於操作兩個布爾型的變量或常量。邏輯運算符主要有如下6 個。
- &&: 與,前后兩個操作數必須都是true 才返回true , 否則返回false 。
- &:不短路與,作用與&&相同,但不會短路。
- || : 或,只要兩個操作數中有一個是true ,就可以返回true ,否則返回false 。
- |: 不短路或,作用與||相同,但不會短路。
- !: 非,只需要一個操作數,如果操作數為true ,則返回false ; 如果操作數為false ,則返回true 。
- ^ : 異或,當兩個操作數不同時才返回true ,如果兩個操作數相同則返回false 。
下面代碼示范了或、與、非、異或4 個邏輯運算符的執行示意。
public class LogicOperatorTest { public static void main(String[] args) { // 直接對false求非運算,將返回true System.out.println(!false); // 5>3返回true,'6'轉換為整數54,'6'>10返回true,求與后返回true System.out.println(5 > 3 && '6' > 10); // 4>=5返回false,'c'>'a'返回true。求或后返回true System.out.println(4 >= 5 || 'c' > 'a'); // 4>=5返回false,'c'>'a'返回true。兩個不同的操作數求異或返回true System.out.println(4 >= 5 ^ 'c' > 'a'); // 定義變量a,b,並為兩個變量賦值 int a = 5; int b = 10; // 對a > 4和b++ > 10求或運算 if (a > 4 | b++ > 10) { // 輸出a的值是5,b的值是11。 System.out.println("a的值是:" + a + ",b的值是:" + b); } // 定義變量c,d,並為兩個變量賦值 int c = 5; int d = 10; // c > 4 || d++ > 10求或運算 if (c > 4 || d++ > 10) { // 輸出c的值是5,d的值是10。 System.out.println("c的值是:" + c + ",d的值是:" + d); } } }
執行上面程序,看到輸出a 的值為5 , b 的值為11, 這表明b++ > 10 表達式得到了計算,但實際上沒有計算的必要,因為a> 4 己經返回了true ,則整個表達式一定返回true 。
&與&&的區別與此類似: &,總會計算前后兩個操作數,而&&先計算左邊的操作數,如果左邊的操作數為false ,則直接返回false ,根本不會計算右邊的操作數。
3.7.7 三目運算符
三目運算符只有一個: ?:, 三日運算符的語法格式如下:
(expression) ?if - true - statement : if- false - statement;
public class ThreeTest { public static void main(String[] args) { String str = 5 > 3 ? "5大於3" : "5不大於3"; System.out.println(str); // 輸出"5大於3" String str2 = null; if (5 > 3) { str2 = "5大於3"; } else { str2 = "5不大於3"; } int a = 11; int b = 12; // 三目運算符支持嵌套 System.out.println(a > b ? "a大於b" : (a < b ? "a小於b" : "a等於b")); } }
3.7.8 運算符的結合性和優先級
所有的數學運算都認為是從左向右運算的, Java 語言中大部分運算符也是從左向右結合的,只有單目運算符、賦值運算符和三目運算符例外,其中,單目運算符、賦值運算符和三日運算符是從右向左結合的,也就是從右向左運算。
乘法和加法是兩個可結合的運算,也就是說,這兩個運算符左右兩邊的操作數可以互換位置而不會影響結果。
運算符有不同的優先級,所謂優先級就是在表達式運算中的運算順序。表3 .4 列出了包括分隔符在內的所有運算符的優先級順序,上一行中的運算符總是優先於下一行的。