Java核心技术 卷1
Java核心技術 卷1
Java程序概述
關鍵術語
- 簡單性
- 面向對象:將重點放在數據(即對象)和對象的接口上
- 網絡技能
- 健壯性:能檢測許多在其他語言僅在運行時刻才能夠檢測出來的問題
- 安全性:能防范運行時堆棧溢出,在自己的處理空間之外破壞內存,未經授權讀寫文件
- 體系結構中立
- 可移植性
- 解釋型:java解釋器可以在任何移植了解釋器的機器上運行java字節碼
- 高性能
- 多線程:只要操作系統支持,Java中的線程就可以利用多個處理器
- 動態性
Java applet 與 Internet
在網頁上運行Java程序稱為applet
為了使用applet,需要啟動java的web瀏覽器執行字節碼。由于Sun公司負責發放Java源代碼的許可證,并堅持不允許對語言和基本類庫的結構做出任何修改。因此,Java的applet應該可以運行在任何啟動Java瀏覽器上,并且無論何時訪問包含applet的網頁,都會得到程序的最終版本。
配置環境
導航Java目錄
bin 編譯器和工具
docs HTML格式的類庫文件
include 用于編譯本地方法的文件
jre java運行環境文件
lib 類庫文件
src 類庫源文件
Java的基本程序設計結構
public class FirstSample{public static void main(String[] args){System.out.println("We will not use ‘Hello,World!’");} }關鍵字public稱為訪問修飾符,它用于控制程序的其他部分對這段代碼的訪問規則。
關鍵字class表明Java程序中的全部內容都包含在類中,這里只需要將類作為一個加載程序邏輯的容器。
數據類型
在Java中,一共有8種基本類型,其中有4種整型、2種浮點類型、1種用于表示Unicode編碼的字符單元的字符類型char和1種用于表示真值的boolean類型
整型
| int | 4字節 |
| short | 2字節 |
| long | 8字節 |
| byte | 1字節 |
長整型數值有一個后綴L,十六進制有一個前綴0x,從Java7開始,加上前綴0b就可以寫二進制數。同樣是從Java7開始,還可以為數字字面量加下劃線,這些下劃線只是為了讓人更易讀,Java編譯器會去除這些下劃線
浮點類型
浮點類型用于表示有小數部分的數值。在Java中有兩種浮點類型
| float | 4字節 |
| double | 8字節 |
double表示這種類型的數值精度是float類型的兩倍。絕大部分應用程序都采用double類型。在很多情況下,float類型的精度很難滿足需求。只有很少的情況適合使用float類型,float類型的數值有一個后綴F,沒有后綴F的默認為double類型,也可以在浮點數值后面加后綴D。
char類型
char類型用于表示單個字符,通常用來表示字符常量。
| \b | 退格 | \u0008 |
| \f | 制表 | \u0009 |
| \n | 換行 | \u000a |
| \f | 回車 | \u000d |
| \ " | 雙引號 | \n0022 |
| \ ’ | 單引號 | \u0027 |
| \ \ | 反斜杠 | \u005c |
Unicode打破了傳統字符編碼方法的限制。
Unicode-16采用不同長度的編碼表示所有Unicode代碼點(是指與一個編碼表中的某個字符對應的代碼值)。在Java中,char類型用UTF-16編碼描述一個代碼單元
boolean類型
整型值和布爾值之間不能相互轉換
變量
在Java中,每一個變量屬于一種類型。在聲明變量時,變量所屬的類位于變量名之前。
在Java中,利用關鍵字final指示常量、關鍵字final表示這個變量只能被賦值一次,一旦被賦值之后,就不能再更改了。習慣上,變量名使用全大寫。
在Java中,經常希望某個常量可以在一個類中的多個方法中使用,通常將這些變量稱為類常量。可以使用關鍵字static final設置一個類常量。
運算符
當參與除運算的兩個操作數都是整數時,表示整數除法,否則表示浮點除法。
位運算符
在處理整型數值時,可以直接對組成整型數值的各個位進行操作。這意味著可以使用屏蔽技術獲得整數中的各個位。
>> 和 << 運算符將二進制進行右移和左移操作,當需要建立位模式屏蔽某些位時,使用這兩個運算符十分方便。 >>>運算符將用0填充高位,>>運算符用符號位填充高位,沒有<<<運算符數值類型之間的轉換
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OZr3nm1Y-1619158617756)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210405155256593.png)]
圖中有6個實心箭頭,表示無消息丟失的轉換。有3個虛箭頭,表示可能有精度損失的轉換。
枚舉類型
有時候,變量的取值只在一個有限的集合內。針對這種情況,可以自定義枚舉類型。枚舉類型包括有限個命名的值。
字符串
當將一個字符串與一個非字符串的值進行拼接時,后者被轉換成字符串。
各種字符串放在公共的存儲池中,字符串變量指向存儲池中的相應為止,如果復制一個字符串變量,原始字符串與復制的字符串共享相同的字符‘、
//構建字符串 StringBuilder builder = new StringBuilder();//構造一個空字符串 //每次需要添加一部分的內容,就調用append方法 builder.append(ch);//appends a single character bulider.append(str);//append a string輸入輸出
//構造一個Scanner對象,并于標準輸入流 System.in 關聯 Scanner in = new Scanner(System.in); String name = in.nextLine();//使用nextLine()方法是因為在輸入行中可能包含空格,如果讀取單詞就用next()方法,讀取整數用nextInt()方法用于printf的轉換符
| d | 十進制整數 |
| x | 十六進制整數 |
| o | 八進制整數 |
| f | 定點浮點數 |
| e | 指數浮點數 |
| g | 通用浮點數 |
| a | 十六進制浮點數 |
| s | 字符串 |
| c | 字符 |
| b | 布爾 |
| h | 散列碼 |
| tx | 日期時間 |
| % | 百分號 |
| n | 與平臺有關的行分割符 |
用于printf的標志
| + | 打印正數和負數的符號 |
| 空格 | 在正數之前添加空格 |
| 0 | 數字前面補0 |
| - | 左對齊 |
| ( | 將負數括在括號里 |
| . | 添加分組分隔符 |
| #(對于f格式) | 包含小數點 |
| #(對于x或0格式) | 添加前綴0x或0 |
| $ | 給定被格式化的參數索引 |
| < | 格式化前面說明的數值 |
文件輸入與輸出
//需要用File對象構造一個Scanner對象 Scanner in = new Scanner.get(Paths.get("myfile.txt")); //寫入文件,如果文件不存在,則創建該文件 PrintWrite out = new PrintWriter("myfile.txt")帶標簽的break語句
標簽必須放在希望跳出的最外層循環之前,并且必須緊跟一個冒號
read_data(); {{{break read_data;} }}控制流程
塊作用域:塊是指一對花括號括起來的若干條簡單的Java語句。塊確定了變量的作用域。一個塊可以嵌套在另一個塊中
大數值
如果基本的整數和浮點數精度不能夠滿足需求,那么可以使用java.math包中的兩個很有用的類:BigInteger和BigDecimal
這兩個類可以處理包含任意長度數字序列的數值。BigInteger類實現了任意精度的整數運算,BigDecimal實現了任意精度的浮點數運算。
數組
for each循環:Java有一種功能很強的循環結構,可以用來依次處理數組中的每個元素(其他類型的元素集合亦可)而不必為指定下標值而分心。
語句格式為:
for (variable : collection) statement
打印數組中所有值可以利用Arrays類的toString方法,返回一個包含數組元素的字符串,這些元素被放置在括號內,并用逗號分隔。
快速打印二維數組的數據元素列表可以調用:
System.out.println(Arrays.deepToString(a));
對象與類
面向對象的程序是由對象組成的,每個對象包含對用戶公開的特定功能部分和隱藏的實現部分。
類
類是構造對象的模板或藍圖。由類構造對象的過程稱為創建類的實例
封裝從形式上看,封裝不過是將數據和行為組合在一個包中,并對對象的使用者隱藏了數據的實現方法。對象中的數據稱為實例域,操縱數據的過程稱為方法。對于每個特定的類實例都有一組特定的實例閾值。這些值的集合就是這個對象的當前狀態。
實現封裝的關鍵在于絕對不能讓類中的方法直接地訪問其他類的實例域。
對象
對象的三個特性:
- 對象的行為——可以對對象施加哪些操作,或可以對對象施加哪些方法
- 對象的狀態——當施加那些方法時,對象如何響應
- 對象標識——如何辨別具有相同行為與狀態的不同對象
類之間的關系:
- 依賴:一個類的方法操縱另一個類的對象,我們就說一個類依賴于另一個類
- 聚合:類A的對象包含類B的對象
- 繼承
javac EmployeeTest.java
當Java編譯器發現EmplyeeTest.java使用了Employee類時會查找名為Employee.class的文件。如果沒有找到這個文件,就會自動地搜索Employee.java,然后對它進行編譯。更重要的是:如果Employee.java版本較已有的Employee.class文件版本新,Java編譯器就會自動地重新編譯這個文件。
構造器
構造器與類同名,在構造類的對象時,構造器會運行,以便將實例域初始化為所希望的狀態。
構造器與其他的方法有一個重要的不同,構造器總是伴隨著new操作符的執行被調用,而不能對一個已經存在的對象調用構造器來達到重新設置實例域的目的。
隱式參數與顯式參數
方法用于操作對象以及存取它們的實例域
public void raiseSalary(double byPercent){double raise = salary * byPercent / 100;salary += raise; }將調用這個方法的對象的salary實例域設置為新值。看看下面這個調用:
number007.raiseSalary(5);它的結果將number007.salary域的值增加5%,具體地說,這個調用將執行下列指令:
double raise = number007.salary * 5 / 100; number007.salary += raise;raiseSalary方法有兩個參數。第一個參數稱為隱式參數,是出現在方法名前的Employee類對象。第二個參數位于方法名后面括號中的數值,這是一個顯式參數。
在每一個方法中,關鍵字this表示隱式參數,采用下面的方式可以將實例域與局部變量明顯地區分開來。
public void raiseSalary(double byPercent){double raise = this.salary * byPercent / 100;this.salary += raise; }在Java程序設計語言中,所有的方法都必須在類的內部定義,但并不表示它們是內聯方法。是否將某個方法設置為內聯方法是Java虛擬機的任務。即時編譯器會監視調用那些簡潔、經常被調用、沒有被重載以及可優化的方法。
封裝的優點:
getxxx方法是典型的訪問器方法,由于它們只返回實例域值,因此又稱為域訪問器。一旦在構造器中設置完成,就沒有任何一個方法可以對它進行修改,這樣確保域不會受到外界的破壞。
在有時候需要獲得或設置實例域的值,因此,應該提供下面三項內容:
- 一個私有的數據域
- 一個共有的域訪問器方法
- 一個公用的域更改器方法
基于類的訪問權限
一個方法可以訪問所屬類的所有對象的私有數據
私有方法
只要方法是私有的,類的設計者就可以確信,它不會被外部的其他類操作調用,可以將其刪去。如果方法是公有的,就不能將其刪去,因為其他的代碼很可能依賴它。
final實例域
可以將實例域定位為final。構建對象時必須初始化這樣的域。final修飾符大都應用于基本類型域或不可變域
靜態域與靜態方法
靜態域
如果將域定義為static,每個類中只有一個這樣的域,而每一個對象對所有的實例域卻都有自己的一份拷貝。
靜態常量
靜態變量使用得比較少,但靜態常量卻使用得比較多。如果關鍵字static被省略,PI就變成了Math類的一個實例域,需要通過Math類的對象訪問PI,并且每一個Math對象都有它自己的一份PI拷貝。
靜態方法
靜態方法是一種不能向對象實施操作的方法。可以認為靜態方法是沒有this參數的方法
因為靜態方法不能操作對象,所以不能在靜態方法中訪問實例域,但是,靜態方法可以訪問自身類中的靜態域。
工廠方法
靜態方法還有一種常見的用途。NumberFormat類使用工程方法產生不同風格的格式對象。
方法參數
按值調用表示方法接收的是調用者提供的值。
而按引用調用表示方法接收的是調用者提供的變量地址。
方法參數共有兩種類型:基本數據類型、對象引用
對象構造
重載
如果多個方法有相同的名字,不同的參數,便產生了重載。編譯器必須挑選除具體執行哪個方法,它通過用各個方法給出的參數類型與特定方法調用所實驗的值類型進行匹配來挑選出相應的方法。如果編譯器找不到匹配的參數,或者找出多個可能的匹配,就會產生編譯時錯誤。(這個過程被稱為重載解析)
如果在構造器沒有顯式地給域賦予初值,那么就會被自動地賦為默認值:數值為0、布爾值為false、對象引用為null
如果在編寫一個類的時候沒有編寫構造器,那么系統就會提供一個無參數構造器。這個構造器將所有的實例域設置為默認值。
顯式域初始化
由于類的構造器方法可以重載,所有可以采用多種形式設置類的實例域的初始狀態。確保不管怎么樣調用構造器,每個實例域都可以被設置為一個有意義的初值。
在執行構造器之前,先執行賦值操作。當一個類的所有構造器都希望把相同的值賦予某個特定的實例域時,這種方法特別有用。
參數名
還有一種常用的技巧,它基于這樣的事實:參數變量用同樣的名字將實例域屏蔽起來。
public Employee(String name, double salary){this.name = name;this.salary = salary; }如果將參數命名為salary,salary將引用這個參數,而不是實例域。但是,可以采用this.salary的形式訪問實例域。
調用另一個構造器
public Employee(double s){this("Employee #" + nextId, s);nextId++; }如果構造器的第一個語句為this(…),這個構造器將調用同一個類的另一個構造器。
初始化塊
調用構造器的具體處理步驟:
可以通過一個初始化值,或者使用一個靜態的初始化塊對靜態域進行初始化。
對象析構與finalize方法
可以為任何一個方法添加finalize方法。finalize方法將在垃圾回收器清楚對象之前調用,在實際應用中,不要依賴于使用finalize方法回收任何短缺的資源,這是因為很難知道這個方法什么時候調用。
如果某個資源需要在使用完畢后立即被關閉,那么就需要人工來管理。對象用完時,可以應用一個close方法來完成相應的清理操作。
包
Java允許使用包(package)將類組織起來。使用包的主要原因是確保類名的唯一性。
一個類可以使用所屬的包中的所有類,以及其他包中的公有類。
靜態導入
import可以導入靜態方法和靜態域的功能
包作用域
如果沒有指定Public和private,這個部分可以被同一個包的所有方法訪問。
類設計技巧
- 一定要保證數據私有
- 一定要對數據初始化
- 不要在類中使用過多的基本類型
- 不是所有的域都需要獨立的域訪問器和域更改器
- 將職責過多的類進行分解
- 類名和方法名要能夠體現它們的職責
繼承
類、超類和子類
關鍵字extends表明正在構造的新類派生于一個已存在的類。已存在的類稱為超類、基類或父類;新類稱為子類、派生類或孩子類。
在通過擴展超類定義子類的時候,僅需要指出子類與超類的不同之處。因此在設計類的時候,應該將通用的方法放在超類中,而將具有特殊用途的方法放在子類中。
繼承層次
由一個公共超類派生出來的所有類的集合被稱為繼承層次。在繼承層次中,從某個特定的類到其祖先的路徑被稱為該類的繼承鏈。
多態
有一個用來判斷是否應該設計為繼承關系的簡單規則,這就是"is-a"規則,它表明子類的每個對象也是超類的對象。另一種表述法是置換規則,它表明程序中出現超類對象的任何地方都可以用子類對象置換。
例如:可以將一個子類的對象賦給超類變量
Employee e; e = new Employee(...)//Employee object expected e = new Manager(...)//OK, Manager can be used as well在JAVA中,對象變量是多態的。一個Employee變量既可以引用以惡搞Employee類對象,也可以引入一個Employee類的任何一個子類的對象
動態綁定
每次調用方法都需要進行搜索,時間開銷相當大,因此虛擬機預先為每個類創建了一個方法表,其中列出了所有方法的簽名和實際調用的方法,這樣一來,在真正調用方法的時候,虛擬機僅查找這個表就行了。
阻止繼承
不允許擴展的類被稱為final類,如果在定義類的時候使用了final修飾符就表明這個類是final類。
將方法或類聲明為final主要目的是:確保它們不會在子類中改變語義。
強制類型轉換
進行類型轉換的唯一原因是:在暫時忽略對象的實際類型之后,使用對象的全部功能
抽象類
如果自下而上在類的繼承層次結構中上移,位于上層的類更具有通用性,甚至可能更加抽象。從某種角度看,祖先類更加通用,人們只將它作為派生其他類的基類,而不作為想使用的特定的實例類。
抽象類充當著占位的角色,它們的具體體現在子類中。擴展抽象類可以有兩種選擇,一種是在子類中定義部分抽象方法或抽象方法也不定義,這樣就必須將子類也標記為抽象類;另一種是定義全部的方法,這樣一來,子類就不是抽象的了。
類即使不含抽象方法,也可以將類聲明為抽象類
抽象類不能被實例化,也就是說,如果將一個類聲明為abstract,就不能創建這個類的對象。可以定義一個抽象類的對象變量,但是它只能引用非抽象子類的對象
Person p = new Student("Yutou", "Econmoics");這里的p是一個抽象類的Person的變量,Person引用了一個非抽象子類Student的實例。
受保護訪問
有些時候,人們希望超類中的某些方法允許被子類訪問,或允許子類的方法訪問超類的某個域,需要將這些方法聲明為protected
受保護的方法更具有實際意義。如果需要限制某個方法的使用,就可以將它聲明為protected
java用于控制可見性的4個訪問修飾符:
Object
Object類是Java中所有類的始祖。
可以使用Object類型的變量引用任何類型的對象,在java中,只有基本類型不是對象
equals方法
Java語言規范要求equals方法具有下面的特性:
如果子類能夠擁有自己的相等概念,則對稱性需求將強制采用getClass進行檢測
如果由超類決定相等的概念,那么就可以使用Instanceof進行檢測,這樣可以在不同子類的對象之間進行相等的比較。
hasCode方法
散列碼(hash code)是由對象導出的一個整型值。散列碼是沒有規律的。如果x和y是兩個不同的對象,x.hashCode()與y.hashCode()基本上不會相同
toString方法
在Object中還有一個重要的方法,就說toString方法,它用于返回表示對象值的字符串。
Object類定義了toString方法,用來打印輸出對象所屬的類名和散列碼,數組繼承了object類的toString方法,數組類型將按照舊的格式打印,修正的方式是調用靜態方法Arrays.toString
泛型數組列表
ArrayList是一個采用類型參數的泛型類,為了指定數組列表保存的元素對象類型,需要用一對尖括號將類名括起來加在后面。
下面聲明和構造一個保存Employee對象的數組列表:
ArrayList<Employee> staff = new ArrayList<Employee>();兩邊都使用類型參數Employee,有些繁瑣,在Java7中,可以省去右邊的類型參數:
ArrayList<Employee> staff = new ArrayList<>();這被稱為菱形語法,因為<>是菱形,可以結合New操作符使用菱形語法。編譯器會檢查新值是什么。如果賦值給一個變量,或傳遞到某個方法,或者從某個方法返回,編譯器會檢查這個變量、參數或方法的泛型類型,然后將這個類型放在<>中。
訪問數組列表元素
既可以靈活地擴展數組,又可以方便地訪問數組元素
ArrayList<X> list = new ArrayList<>; while(...){x=...;list.add(x); } X[] a = new X[list.size()]; list.toArray(a);類型化與原始數組列表的兼容性
編譯器對類型轉換進行檢查之后,如果沒有發現違反規則的現象,就將所有的類型化數組列表轉換成原始ArrayList對象。在程序運行時,所有的數組列表都是一樣的,即沒有虛擬機的類型參數。
對象包裝器與自動裝箱
有時,需要將int這樣的基本類型轉換為對象。所有的基本類型都有一個與之對應的類。例如,Integer類對應基本類型int。通常,這些類稱為包裝器(wrapper)
這些對象包裝器類擁有很鮮明的名字:Integer、Long、Flaot、Double、Short、Byte、Character、Void和Boolean(前6個類派生于公共的超類Number)對象包裝器類是不可變的,即一旦構造了包裝器,就不允許更改包裝在其中的值。同樣,對象包裝器類還是final,因此不能定義它們的子類。
假設想定義一個整型數組列表,而尖括號的數據參數不允許是基本類型。這里就用到了Integer對象包裝類
JavaSE 5.0 的另一個改進之處是更加便于添加或獲得數組元素,下面這個調用
list.add(3); //將自動變換成 list.add(Integer.valueOf(3));相反地,當將一個Integer對象賦給一個Int值時,將會自動地拆箱,也就是說,編譯器將下列語句:
int n = list.get(i); //翻譯成 int n = list.get(i).intValue();裝箱和拆箱是編譯器認可的,而不是虛擬機。編譯器在生成類的字節碼時,插入必要的方法調用。虛擬機只是執行這些字節碼。
參數數量可變的方法
現在的版本提供了可以用可變的參數數量調用的方法(有時稱為"變參"方法)
public class PrintStream{public PrintStream printf(String fmt,Object ... args){return format(fmt, args);} }這里的省略號…是java代碼的一部分,它表明這個方法可以接收任意數量的對象(除fmt參數之外)
用戶自己也可以定義可變參數的方法,并將參數指定為任意類型,甚至是基本類型
枚舉類
比較兩個枚舉類型的值時,永遠不需要調用equals,而直接使用"=="就可以了
public enum Size{SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");private String abbreviation;private Size(String abbreviation){this.abbreviation = abbreviation}public String getAbbreviation(){return abbreviation;} }所有枚舉類型都是Enum類的子類,它們繼承了這個類的許多方法,其中最有用的一個是toString,這個方法能夠返回枚舉常量名。
toString的逆方法是靜態方法valueOf
每個枚舉類型都有一個靜態的values方法,它將返回一個包含全部枚舉值的數組。
反射
能夠分析類的能力的程序稱為反射。
Class類
在程序運行期間,Java運行時系統始終為所有的對象維護一個被稱為運行時的類型標識。這個消息跟蹤著每個對象所屬的類。
可以通過專門的Java類訪問這些信息,保存這些信息的類被稱為Class。Object類中的getClass()方法將會返回一個Class類型的實例
如果類在一個包里,包的名字也作為類名的一部分,還可以調用靜態方法forName獲得類名對應的Class對象
虛擬機為每個類型管理一個Class對象,因此,可以利用==運算符實現兩個類對象比較的操作
捕獲異常
當程序運行過程中發送錯誤時,就會拋出異常。
異常有兩種類型:未檢查異常和已檢查異常。對于已檢查異常,編譯器將會檢查是否提供了處理器,然而,有很多常見的異常。編譯器不會查看是否為這些錯誤提供了處理器。
利用反射分析類的能力
在java.lang.reflect包中有三個類Field、Method和Constructor分別用于描述類的域、方法和構造器。這三個類都有一個叫做getName的方法,用來返回項目的名稱。Filed類有一個getType方法,用來返回描述域所屬類型的Class對象。Method和Constructor類有能夠報告參數類型的方,Method類還有一個可以報告返回類型的方法。
這三個類還有一個叫做getModifiers的方法,它將返回一個整型數值,用不同的位開關描述public和static這樣的修飾符使用狀況。
另外還可以利用java.lang.reflect包中的Modifier類靜態方法分析getModifiers返回的整型數值。
在運行時使用反射分析對象
查看對象域的關鍵方法是Filed類中的get方法。如果f是一個Field類型的對象,obj是某個包含f域的類的對象,f.get(obj)將返回一個對象,其值為obj域的當前值。
反射機制的默認行為受限于java的訪問控制。然而,如果一個Java程序沒有受到安全管理器的控制,就可以覆蓋訪問控制。
繼承設計的技巧
接口與內部類
接口技術,主要用來描述類具有功能,而并不給出每個功能的具體實現。一個類可以實現一個或多個接口,并且需要接口的地方,隨時使用實現了相應接口的對象。
內部類定義在另一個類的內部,其中的方法可以訪問包含它們的外部類的域,這是一項比較復雜的技術。內部類技術主要用于設計具有相互協作關系的類集合。
接口
在java程序設計語言中,接口不是類,而是對類的一組需求描述,這個類要遵從接口描述的統一格式進行定義。
接口中的所有方法自動地屬于public,因此,在接口中聲明方法時,不必提供關鍵字public。
接口絕不能含有實例域,也不能在接口中實現方法。提供實例域和方法實現的任務應該由實現接口那個類來完成。因此,可以將接口看成是沒有實例域的抽象類。但是這個兩個概念還是有一定區別的。
為了讓類實現一個接口,通常需要下面兩個步驟:
接口的特性
接口不是類,尤其不能用new運算符實例化一個接口
然而,盡管不能構造接口的對象,卻能聲明接口的變量
接口變量必須引用實現了接口的類對象
接口與抽象類
使用抽象類表示通用屬性存在這樣一個問題:每個類只能擴展于一個類。
對象克隆
當拷貝一個變量時,原始變量與拷貝變量引用同一個對象,這就是說,改變一個變量所引用的對象將會對另一個變量產生影響。
如果在對象中包含了子對象的引用,拷貝的結構會使得兩個域引用同一個子對象,因此原始對象與克隆對象共享這部分信息。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-CVZEfKTs-1619158617760)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210417212230194.png)]
接口與回調
回調是一種常見的程序設計模式。在這種模式,可以指出某個特定事件發送時應該采取動作。
內部類
內部類是定義在另一個類中的類。使用內部類的原因:
- 內部類方法可以訪問該類定義所在的作用域中的數據,包括私有的數據。
- 內部類可以對同一個包中的其他類隱藏起來
- 當想要定義一個回調函數且不想編寫大量代碼時,使用匿名內部類比較便捷
從傳統意義上講,一個方法可以引用調用這個方法的對象數據域。內部類既可以訪問自身的數據域,也可以訪問創建它的外圍類對象的數據域。
局部內部類
public void start(){class TimePrinter implements ActionListner{public void actionPerformed(ActionEvent event){Date now = new Date();System.out.println("At the tone, the time is " + now);if(beep) Tookit.getDefaultToolkit().beep();}}ActionListener listener = new TimePrinter();Timer t = new Timer(interval, listner);t.start(); }局部類不能用public或private訪問說明符進行聲明。它的作用域被限定在聲明這個局部類的塊中。
局部類有一個優勢,即對外部世界可以完全地隱藏起來。即使TakingClock類中的其他代碼也不能訪問它。除start方法之外,沒有任何方法知道TImePrinter類的存在。
由外部方法訪問final變量
與其他內部類相比較,局部類還有一個優點。它們不僅能夠訪問包含它們的外部類,還可以訪問局部變量。不過,那些局部變量必須被聲明為final。
匿名內部類
將局部內部類的使用再深入一步。假如只創建這個類的一個對象,就不必命名了。這種類被稱為匿名內部類。
靜態內部類
使用內部類只是為了把一個類隱藏在另外一個類的內部,并不需要內部類引用外圍類對象。為此,可以將內部類聲明為static,以便取消產生的引用。
代理
利用代理可以在運行時創建一個實現了一組給定接口的新類。這種功能只有在編譯時無法確定需要實現哪個接口時才有必要使用。
假設有一個表示接口的Class對象,它的確切類型在編譯時無法知道。
異常、斷言、日志和調試
Java使用一種稱為異常處理的錯誤捕獲機制處理。
使用斷言
在一個具有自我保護能力的程序中,斷言很常用。假設確信某個屬性符合要求,并且代碼的執行依賴于這個屬性。
斷言機制運行在測試期間向代碼中插入一些檢查語句,當代碼發布時,這些插入的檢測語句將會自動地移走。
assert 條件; 和assert 條件:表達式這兩種形式都會對條件進行檢測,如果結構為false,則拋出一個AssertionError異常。在第二種形式中,表達式將被傳入AssertionError的構造器,并轉換一個消息字符串。
啟動和禁用斷言
在默認情況下,斷言被禁用。可以在運行程序時用下面代碼啟用它:
java -enableassertions MyApp需要注意的是,在啟用或禁用斷言時不必重新編譯程序。啟用或禁用是類加載器的功能。當斷言被禁用時,類加載器將跳過斷言代碼。因此,不會降低程序運行的速度。
使用斷言完成參數檢查
在java語言中,給出了3種處理系統錯誤的機制:
- 拋出一個異常
- 日志
- 使用斷言
什么時候應該選擇使用斷言?
- 斷言失敗是致命的,不可恢復的錯誤
- 斷言檢查只用于開發和測試階段
記錄日志
記錄日志API優點:
- 可以很容易地取消全部日志記錄,或者僅僅取消某個級別的日志,而且打開和關閉這個操作也很容易
- 可以很簡單地禁止日志記錄的輸出,因此,將這些日志代碼留在程序中的開銷很小。
- 日志記錄可以被定向到不同的處理器,用于在控制臺中顯示,用于存儲在文件中等。
- 日志記錄器和處理器都可以對記錄進行過濾。過濾器可以根據過濾實現器指定的標準丟棄那些無用的記錄項。
- 日志記錄可以采用不同的方式格式化
- 應用程序可以使用多個日志記錄器,它們使用類似包名的這種具有層次結構的名字。
- 在默認情況下,日志系統的配置由配置文件控制。如果需要的話,應用程序可以替換這個配置。
基本日志
可以使用System.out替換它,并調用Info方法記錄日志信息
泛型程序設計
為什么要使用泛型程序設計
泛型程序設計意味著編寫的代碼可以被很多不同類型的對象所重用。
定義簡單泛型類
一個泛型類就是具有一個或多個類型變量的類
public class Pair<T>{private T first;private T second;public Pair(){first = null;second = null;}public T getFirst(){return first;}public T getSecond(){return second;}public void setFirst(T newValue){first = newValue;}public void setSecond(T newValue){second = newValue;} }Pair類引入了一個類型變量T,用尖括號括起來,放在類名的后面。泛型類可以有多個類型變量。
類型變量使用大寫形式,且比較短。在Java庫中,使用變量E表示集合的元素類型,K和V分別表示表的關鍵字與直的類型。T(需要時話可以用臨近的U和S)表示"任意類型"
泛型方法
clasS ArrayAlg{public static <T> T getMiddle(T... a){return a[a.length/2];} }這個方法在普通類中定義的,而不是在泛型類中定義的。然而,這是一個泛型方法。泛型方法可以定義在普通類中,也可以定義在泛型類中。
當調用一個泛型方法時,在方法名前的尖括號中放入具體的類型:
String middle = ArrayAlg.<String>getMiddle("John","Q.","Public");在這種情況下,方法調用中可以省略類型參數
泛型代碼和虛擬機
虛擬機沒有泛型類型對象——所有對象都屬于普通類。
無論何時定義一個泛型類型,都自動提供了一個相應的原始類型。原始類型的名字就是刪去類型參數后的泛型類型名,擦除類型變量,并轉換為限定類型(無限定的變量用Object)
約束和局限性
不能用類型參數代替基本類型。
因此沒有Pair,只有Pair。
運行時類型查詢只適用于原始類型
虛擬機的對象總有一個特定的非泛型類型。因此,所有的類型查詢只產生原始類型。
不能創建參數化類型的數組
不能實例化參數化類型的數組
不能實例化類型變量
泛型類的靜態上下文中類型變量無效
不能在靜態域或方法中引用類型變量
不能拋出或捕獲泛型類的實例
通配符類型
Pair<? extends Employee>表示任何泛型Pair類型,它的類型參數是Employee的子類
通配符的超類型限定
超類型限定:? super Manager 這個通配符限制為Manager的所有超類型
無限定通配符
Pair<?>和Pair的本質不同在于:可以用任意Object對象調用原始的Pair類的setObject方法
通配符捕獲
通配符捕獲只有在有許多限制的情況下才是合法的。編譯器必須要能夠確信通配符表達的是單個、確定的類型。
反射和泛型
Class類是泛型的。例如,String.class實際上是一個Class類的對象
虛擬機中的泛型類型信息、
Java泛型的卓越特性之一是在虛擬機中泛型類型的擦除。
集合
集合接口
將集合的接口與實現分離
Java集合類庫將接口與實現(implementation)分離。
Java類庫中的集合接口和迭代器接口
在java類庫中,集合類的基本接口是Collector接口,這個接口有兩個基本方法:
public interface Collection<E>{boolean add(E element);Iterator<E> iteractor(); }add方法用于向集合中增加元素。如果添加元素確實改變了集合就返回true,如果集合沒有發生變化就返回false。
iterator方法用于放回一個實現了Iterator接口的對象,可以使用這個迭代器對象依次訪問集合中的元素。
Collection接口擴展了Iterator接口。因此,對于標準類庫中的任何集合都可以使用for each循環。
元素被訪問的順序取決集合類型。如果對ArrayList進行迭代,迭代器將從索引0開始,每迭代一次,索引值加1.然而如果訪問HashSet中的元素,每個元素將會按照某種隨機的次序出現。
由于Collection與Iterator都是泛型接口,可以編寫操作任何集合類型的實用方法。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-eiA54w82-1619158617763)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210420171035312.png)]
散列表
散列表為每個對象計算一個整數,稱為散列碼。散列碼是由對象的實例域產生的一個整數。在Java中,散列表用鏈表數組實現。每個列表被稱為桶。要想查找表中對象的位置,就要先計算它的散列表,然后與桶的總數取余,所得到的結果就是保存這個元素的桶的索引。
如果散列表太滿,就需要再散列。如果要對散列表再散列,就需要創建一個桶數更多的表,并把所有元素插入這個新表中,然后丟棄原來的表。裝填因子決定何時對散列表進行再散列。
散列表可以用于實現幾個重要的數據結構。其中最簡單的是set類型。set是沒有重復元素的元素集合。
樹集
樹集是一個有序集合。可以以任何順序將元素插入到集合中。在對集合進行遍歷時,每個值將自動地按照排序后的順序呈現。將一個元素添加到樹中要比添加到散列表中慢,但是,與將元素添加到數組或鏈表的正確位置上相比還是快很多的。
映射表
映射表是用來存放鍵/值對。如果提供了鍵,就能夠找到值。
java類庫為映射表提供了兩個通用的實現:HashMap和TreeMap,這兩個類都實現了Map接口。
散列映射表對鍵進行散列,樹映射表用鍵的整體順序對元素進行排序,并將其組織成搜索樹。散列或比較函數只能作用于鍵,與鍵關聯的值不能進行散列或比較。
多線程
每一個任務稱為一個線程,它是線程控制的簡稱。可以同時運行一個以上線程的程序稱為多線程程序。
中斷線程
當對一個線程調用interrupt方法時,線程的終端狀態將被置位。這是每一個線程都具有的Boolean標志。每個線程都應該不時地檢查這個標志,以判斷線程是否被中斷。
線程狀態
線程可以有如下6種狀態:
- New 新創建
- Runable 可運行
- Blocked 被阻塞
- Waiting 等待
- Timed waiting 計時等待
- Terminated 被終止
線程屬性
線程優先級
每個線程都有一個優先級,WINDOWS有7個優先級別,在Linux提供的Java虛擬機,線程的優先級被忽略——所有線程具有相同的優先級。
守護線程的唯一用途是為其他線程提供服務。
執行器
構建一個新的線程是有一定代價的,因為涉及與操作系統的交互。如果程序中創建了大量的生命期很短的線程,應該使用線程池。一個線程池中包含許多準備運行的空閑線程,將Runnable對象交給線程池,就會有一個線程調用run方法。當run方法退出時,線程不會死亡,而是在池中準備為下一個請求提供服務。
執行器類有許多靜態工廠方法用來構建線程池。
交換器
當兩個線程在同一個數據緩沖區的兩個實例上工作的時候,就可以使用交換器。
總結
以上是生活随笔為你收集整理的Java核心技术 卷1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 禁止sethc.exe运行 防止3389
- 下一篇: java美元兑换,(Java实现) 美元