foochane : https://foochane.cn/article/2019121501.html
字節是計算機中最小存儲單元。計算機存儲任何的數據,都是以字節的形式存儲。8個bit(二進制位) 0000-0000表示爲1個字節,寫成1 byte或者1 B。html
- 8 bit = 1 Bjava
- 1024 B =1 KBgit
- 1024 KB =1 MBgithub
- 1024 MB =1 GB算法
- 1024 GB = 1 TB編程
虛擬機是一種抽象化的計算機,經過在實際的計算機上仿真模擬各類計算機功能來實現的。Java虛擬機(Java Virtual Machine,JVM )有本身完善的硬體架構,如處理器、堆棧、寄存器等,還具備相應的指令系統。Java虛擬機屏蔽了與具體操做系統平臺相關的信息,使得Java程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就能夠在多種平臺上不加修改地運行。數組
所謂的java虛擬機,就是一臺虛擬的機器。它是一款軟件,用來執行一系列虛擬計算機指令,大致上虛擬機能夠分爲系統虛擬機和程序虛擬機。Visual Box、VMWare就屬於系統虛擬機。他們徹底是對物理計算機的仿真,提供一個可運行完整操做系統的軟件平臺。而java虛擬機就是典型程序虛擬機,它專門爲執行單個計算機程序而設計,在java虛擬機中執行的指令咱們稱之爲java字節碼指令。java發展到今天,出現了不少虛擬機,最初sun使用的叫Classic的java虛擬機,到如今使用最普遍的是HotSpot虛擬機,除了sun之外還有BEA的JRockit,目前JRockit和HotSpot都被甲骨文公司收入旗下,大有整合的趨勢。安全
任何軟件的運行,都必需要運行在操做系統之上,而咱們用Java編寫的軟件能夠運行在任何的操做系架構
統上,這個特性稱爲Java語言的**跨平臺特性**。該特性是由JVM實現的,咱們編寫的程序運行在JVM上,而JVM運行在操做系統上。編程語言
- JRE(Java Runtime Environment):是Java程序的運行時環境,包含 JVM 和運行時所須要的 核心類庫 。
- JDK(Java Development Kit):是Java程序開發工具包,包含 JRE 和開發人員使用的工具。
咱們想要運行一個已有的Java程序,那麼只需安裝 JRE 便可。咱們想要開發一個全新的Java程序,那麼必須安裝 JDK 。
- 常量:程序運行中固定不變的量
- 變量:程序中運行能夠變化的量
常量分類:
| 類型 | 含義 | 舉例 |
| ---------- | -------------------------------------- | --------------------------- |
| 整數常量 | 全部的整數 | 0,1, 567, -9 |
| 小數常量 | 全部的小數 | 0.0, -0.1, 2.55 |
| 字符常量 | 單引號引發來,只能寫一個字符,必須有內容 | 'a' , ' ', '好' |
| 字符串常量 | 雙引號引發來,能夠寫多個字符,也能夠不寫 | "A" ,"Hello" ,"你好" ,"" |
| 布爾常量 | 只有兩個值 | true , false |
| 空常量 | 只有一個值 | null |
變量分類:
Java的數據類型分爲兩大類:
- 基本數據類型 :整數 、 浮點數 、 字符 、 布爾 。
- 引用數據類型 :類 、 數組 、 接口 。
| 數據類型 | 關鍵字 | 佔用內存 | 取值範圍 |
| ------------ | -------------- | -------- | --------------------- |
| 字節型 | byte | 1個字節 | -128~127 |
| 短整型 | short | 2個字節 | -32768~32767 |
| 整型 | int(默認) | 4個字節 | $-2^{31}$~$2^{31}-1$ |
| 長整型 | long | 8個字節 | $-2^{63}$~$2^{63}-1$ |
| 單精度浮點數 | float | 4個字節 | 1.4013E-45~3.4028E+38 |
| 雙精度浮點數 | double(默認) | 8個字節 | 4.9E-324~1.7977E+308 |
| 字符型 | char | 2個字節 | 0-65535 |
| 布爾類型 | boolean | 1個字節 | true,false |
>`long`類型:建議數據後加`L`表示
>`float`類型:建議數據後加`F`表示
自動類型轉換(隱式)
- 特色:代碼不須要進行特殊處理,自動完成。
- 規則:數據範圍從小到大。
強制類型轉換(顯式)
- 特色:代碼須要進行特殊的格式處理,不能自動完成。
- 格式:範圍小的類型 範圍小的變量名 =(範圍小的類型) 本來範圍大的數據;
注意事項:
1. 強制類型轉換通常不推薦使用,由於有可能發生精度損失、數據溢出。
2.`byte/short/char`這三種類型均可以發生數學運算,例如加法「`+`」.
3.`byte/short/char`這三種類型在運算的時候,都會被首先**提高成爲int類型**,而後再計算。
4.`boolean`類型不能發生數據類型轉換
| 符號 | 說明 |
| -------- | ---------------------------- |
| + | 加法運算,字符串鏈接運算 |
| - | 減法運算 |
| * | 乘法運算 |
| / | 除法運算 |
| % | 取模運算,兩個數字相除取餘數 |
| ++ 、 -- | 自增自減運算 |
**前++和後++的區別**
```java
publicstaticvoidmain(String[] args) {
inta = 1;
intb = ++a;
System.out.println(a);//計算結果是2
System.out.println(b);//計算結果是2
}
```
```java
publicstaticvoidmain(String[] args) {
inta = 1;
intb = a++;
System.out.println(a);//計算結果是2
System.out.println(b);//計算結果是1
}
```
| 符號 | 說明 |
| ---- | ------ |
| = | 等於號 |
| += | 加等於 |
| - = | 減等於 |
| *= | 乘等於 |
| /= | 除等於 |
| %= | 取模等 |
| 符號 | 說明 |
| ---- | ------------------------------------------------------------ |
| == | 比較符號兩邊數據是否相等,相等結果是true。 |
| < | 比較符號左邊的數據是否小於右邊的數據,若是小於結果是true。 |
| > | 比較符號左邊的數據是否大於右邊的數據,若是大於結果是true。 |
| <= | 比較符號左邊的數據是否小於或者等於右邊的數據,若是小於結果是true。 |
| >= | 比較符號左邊的數據是否大於或者等於右邊的數據,若是小於結果是true。 |
| ! = | 不等於符號 ,若是符號兩邊的數據不相等,結果是true。 |
| 符號 | 說明 |
| ---------- | ------------------------------------------------------------ |
| && 短路與 | 1. 兩邊都是true,結果是true<br/>2. 一邊是false,結果是false<br/>**短路特色:符號左邊是false,右邊再也不運算** |
| \|\| 短路或 | 1. 兩邊都是false,結果是false<br/>2. 一邊是true,結果是true<br/>**短路特色: 符號左邊是true,右邊再也不運算** |
| ! 取反 | 1. ! true 結果是false<br/>2. ! false結果是true |
三元運算符格式:
```java
數據類型 變量名 = 布爾類型表達式?結果1:結果2;
```
示例:
```java
publicstaticvoidmain(String[] args) {
inti = (1==2?100:200);
System.out.println(i);//200
intj = (3<=4?500:600);
System.out.println(j);//500
}
```
JShell腳本工具是JDK9的新特性,當咱們編寫的代碼很是少的時候,而又不肯意編寫類,main方法,也不肯意去編譯和運行,這個時候可使用JShell工具。
啓動JShell工具,在DOS命令行直接輸入JShell命令。
| 快捷鍵 | 功能 |
| ------------------------ | -------------------------------------- |
| Alt + Enter | 導入包,自動代碼修正 |
| Ctrl+Y | 刪除光標所在行 |
| Ctrl+D | 複製光標所在行的內容,插入光標位置下面 |
| Ctrl+Alt+L | 格式化代碼 |
| Ctrl+/ | 單行註釋 |
| Ctrl+Shift+/ | 選中代碼註釋,多行註釋,再按取消註釋 |
| Alt+Ins | 自動生成代碼,toString,get,set等方法 |
| Alt+Shift+ 上下箭頭 | 移動當前代碼行 |
| Shift+F6 | 同時修改不一樣地方的同一個量 |
| 輸入sout | System.out.println(); |
| 輸入psvm | public static void main(String[] args) |
| 輸入5.fori | for(int i = 0; i < 5; i++) |
| 輸入arr.fori或者arr.forr | for循環變量數組 |
語句格式:
```java
if (判斷條件1) {
執行語句1;
} elseif (判斷條件2) {
執行語句2;
}
...
}elseif (判斷條件n) {
執行語句n;
} else {
執行語句n+1;
}
```
語句格式:
```java
switch(表達式) {
case 常量值1:
語句體1;
break;
case 常量值2:
語句體2;
break;
...
default:
語句體n+1;
break;
}
```
語句格式:
```java
for(初始化表達式①; 布爾表達式②; 步進表達式④){
循環體③
}
```
執行流程
執行順序:①②③④ >②③④>②③④…②不知足爲止。
①負責完成循環變量初始化
②負責判斷是否知足循環條件,不知足則跳出循環
③具體執行的語句
④循環後,循環條件所涉及變量的變化狀況
語句格式1:
```java
初始化表達式①
while(布爾表達式②){
循環體③
步進表達式④
}
```
執行流程
執行順序:①②③④ >②③④>②③④…②不知足爲止。
①負責完成循環變量初始化。
②負責判斷是否知足循環條件,不知足則跳出循環。
③具體執行的語句。
④循環後,循環變量的變化狀況。
語句格式2:
```java
初始化表達式①
do{
循環體③
步進表達式④
}while(布爾表達式②);
```
執行流程
執行順序:①③④ >②③④>②③④…②不知足爲止。
①負責完成循環變量初始化。
②負責判斷是否知足循環條件,不知足則跳出循環。
③具體執行的語句
④循環後,循環變量的變化狀況
使用場景:終止 switch或者循環
- 在選擇結構 switch語句中
- 在循環語句中
- 離開使用場景的存在是沒有意義的
示例:
```java
publicstaticvoidmain(String[] args) {
for (inti = 1; i<=10; i++) {
//需求:打印完兩次HelloWorld以後結束循環
if(i == 3){
break;
}
System.out.println("HelloWorld"+i);
}
}
```
使用場景:結束本次循環,繼續下一次的循環
示例:
```java
publicstaticvoidmain(String[] args) {
for (inti = 1; i <= 10; i++) {
//需求:不打印第三次HelloWorld
if(i == 3){
continue;
}
System.out.println("HelloWorld"+i);
}
}
```
容器: 是將多個數據存儲到一塊兒,每一個數據稱爲該容器的元素。
數組概念: 數組就是存儲數據長度固定的容器,保證多個數據的數據類型要一致。
格式:
```java
數組存儲的數據類型[] 數組名字 = new 數組存儲的數據類型[長度];
```
示例:
```java
int[] arr = newint[3];
```
格式:
```java
數據類型[] 數組名 = new 數據類型[]{元素1,元素2,元素3...};
```
示例:
```java
int[] arr = newint[]{1,2,3,4,5};
```
格式:
```java
數據類型[] 數組名 = {元素1,元素2,元素3...};
```
示例:
```java
int[] arr = {1,2,3,4,5};
```
【注意】:
1. 數組有定長特性,長度一旦指定,不可更改
2.`方式三`一樣也進行了`new`操做
- 索引: 每個存儲到數組的元素,都會自動的擁有一個編號,從0開始,這個自動編號稱爲數組索引(index),能夠經過數組的索引訪問到數組中的元素。
- 數組的長度: 每一個數組都具備長度,並且是固定的,Java中賦予了數組的一個屬性,能夠獲取到數組的長度,語句爲: `數組名 .length` ,屬性length的執行結果是數組的長度,int類型結果。由次能夠推斷出,數組的最大索引值爲` 數組名 .length-1 `。
- 索引訪問數組中的元素: `數組名[索引]`
示例:
``` java
publicstaticvoidmain(String[] args) {
//定義存儲int類型數組,賦值元素1,2,3,4,5
int[] arr = {1,2,3,4,5};
//爲0索引元素賦值爲6
arr[0] = 6;
//獲取數組0索引上的元素
inti = arr[0];
System.out.println(i);
//直接輸出數組0索引元素
System.out.println(arr[0]);
}
```
代碼以下:
```java
publicstaticvoidmain(String[] args) {
int[] arr = { 5, 15, 2000, 10000, 100, 4000 };
//定義變量,保存數組中0索引的元素
intmax = arr[0];
//遍歷數組,取出每一個元素
for (inti = 0; i < arr.length; i++) {
//遍歷到的元素和變量max比較
//若是數組元素大於max
if (arr[i] > max) {
//max記錄住大值
max = arr[i];
}
}
System.out.println("數組最大值是: " + max);
}
```
代碼以下:
```java
publicstaticvoidmain(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
/*
循環中定義變量min=0最小索引
max=arr.length‐1最大索引
min++,max‐‐
*/
for (intmin = 0, max = arr.length ‐ 1; min <= max; min++, max‐‐) {
//利用第三方變量完成數組中的元素交換
inttemp = arr[min];
arr[min] = arr[max];
arr[max] = temp;
}
// 反轉後,遍歷數組
for (inti = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
```
內存是計算機中的重要原件,臨時存儲區域,做用是運行程序。咱們編寫的程序是存放在硬盤中的,在硬盤中的程序是不會運行的,必須放進內存中才能運行,運行完畢後會清空內存。
Java虛擬機要運行程序,必需要對內存進行空間的分配和管理。
**Java的內存須要劃分紅爲5個部分:**
1. 棧(Stack): 存放的都是方法中的局部變量。**方法的運行必定要在棧當中運行。**
- 局部變量:方法的參數,或者是方法{}內部的變量
- 做用域:一旦超出做用域,馬上從棧內存當中消失。
2. 堆(Heap): 凡是new出來的東西,都在堆當中。
- 堆內存裏面的東西都有一一個地址值: 16進制
- 堆內存裏面的數據,都有默認值。規則:
- 若是是整數: 默認爲`0`
- 若是是浮點數: 默認爲`0.0`
- 若是是字符: 默認爲`'\u0000'`
- 若是是布爾: 默認爲`false`
- 若是是引用類型: 默認爲`null`
3. 方法區(Method Area): 存儲class相關信息,包含方法的信息。
4. 本地方法棧(Native Method Stack): 與操做系統相關。
5. 寄存器(PC Register): 與CPU相關。
示例:
```java
publicstaticvoidmain(String[] args) {
// 定義數組,存儲3個元素
int[] arr = newint[3];
//數組索引進行賦值
arr[0] = 5;
arr[1] = 6;
arr[2] = 7;
//輸出3個索引上的元素值
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
}
```
代碼執行流程:
1. main方法進入方法棧。程序運行前main方法存儲在方法區,程序運行時,main方法進入棧
2. 建立數組。JVM在堆內存中開闢一個內存空間存儲數組(new int[3]),數組中的三個元素默認值爲0。內存地址以一個十六進制數表示(0xff343)。
3. JVM將內存地址賦值給變量 arr。**變量arr保存的是數組內存中的地址,而不是一個具體的數值,所以數組爲引用數據類型。**
4. 根據數組索引給數組的3個元素賦值,分佈賦值爲5,6,7。而後進行打印。
![數組內存圖](https://foochane.cn/images/20...
兩個變量指向同一個數據:
```java
publicstaticvoidmain(String[] args) {
// 定義數組,存儲3個元素
int[] arr = newint[3];
//數組索引進行賦值
arr[0] = 5;
arr[1] = 6;
arr[2] = 7;
//輸出3個索引上的元素值
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
//定義數組變量arr2,將arr的地址賦值給arr2
int[] arr2 = arr;
arr2[1] = 9;
System.out.println(arr[1]);
}
```
上述代碼中,arr和arr2都指向同一個內存地址,`arr2[1] = 9`執行後,`arr[1]`也會跟着改變。
Java語言是一種面向對象的程序設計語言,而面向對象思想是一種程序設計思想,咱們在面向對象思想的指引下,
使用Java語言去設計、開發計算機程序。 這裏的對象泛指現實中一切事物,每種事物都具有本身的屬性和行爲。面向對象思想就是在計算機程序設計過程當中,參照現實中事物,將事物的屬性特徵、行爲特徵抽象出來,描述成計算機事件的設計思想。 它區別於面向過程思想,強調的是經過調用對象的行爲來實現功能,而不是本身一步一步的去操做實現。
面向對象思想是一種更符合咱們思考習慣的思想,它能夠將複雜的事情簡單化,並將咱們從執行者變成了指揮者。**面向對象的語言中,包含了三大基本特徵,即封裝、繼承和多態。**
- 類 :是一組相關**屬性**和**行爲**的集合。能夠當作是一類事物的模板,使用事物的屬性特徵和行爲特徵來描述該
類事物。
- 屬性:事物的狀態信息。
- 行爲:事物可以作什麼。
- 對象 :是一類事物的具體體現。對象是類的一個實例,必然具有該類事物的屬性和行爲。
- 類與對象的關係
- 類是對一類事物的描述,是**抽象的**。
- 對象是一類事物的實例,是**具體的**。
- 類是對象的模板,對象是類的實體 。
定義格式:
```java
publicclassClassName {
//成員變量
//成員方法
}
```
示例:
```java
publicclassStudent {
//成員變量
String name;//姓名
int age;//年齡
//成員方法
//學習的方法
publicvoidstudy() {
System.out.println("好好學習,每天向上");
}
//吃飯的方法
publicvoid eat() {
System.out.println("學習餓了要吃飯");
}
}
```
對象的使用格式
建立對象:
```java
類名 對象名 = new 類名();
```
使用對象訪問類中的成員:
```java
對象名.成員變量;
對象名.成員方法();
```
示例:
```java
publicclassTest01_Student {
publicstaticvoidmain(String[] args) {
//建立對象格式:類名 對象名 = new 類名();
Students = newStudent();
System.out.println("s:"+s); //cn.itcast.Student@100363
//直接輸出成員變量值
System.out.println("姓名:"+s.name); //null
System.out.println("年齡:"+s.age); //0
System.out.println("‐‐‐‐‐‐‐‐‐‐");
//給成員變量賦值
s.name = "趙麗穎";
s.age = 18;
//再次輸出成員變量的值
System.out.println("姓名:"+s.name); //趙麗穎
System.out.println("年齡:"+s.age); //18
System.out.println("‐‐‐‐‐‐‐‐‐‐");
//調用成員方法
s.study(); // "好好學習,每天向上"
s.eat(); // "學習餓了要吃飯"
}
}
```
成員變量的默認值
- 基本類型:
- 整數(byte,short,int,long):`0`
- 浮點數(float,double):`0.0`
- 字符(char): `'\u0000'`
- 布爾(boolean):`false`
- 引用類型:數組,類,接口 `null`
```java
publicclassCar{
Stringcolor; //成員變量
publicvoiddrive(){
intspeed = 80; //局部變量
//......
}
}
```
- 在類中的位置不一樣
- 成員變量:類中,方法外
- 局部變量:方法中或者方法聲明上(形式參數)
- 做用範圍不同
- 成員變量:類中
- 局部變量:方法中
- 初始化值的不一樣
- 成員變量:有默認值
- 局部變量:沒有默認值。必須先定義,賦值,最後使用在內
- 存中的位置不一樣
- 成員變量:堆內存
- 局部變量:棧內存
- 生命週期不一樣
- 成員變量:隨着對象的建立而存在,隨着對象的消失而消失
- 局部變量:隨着方法的調用而存在,隨着方法的調用完畢而消失
面向對象編程語言是對客觀世界的模擬,客觀世界裏成員變量都是隱藏在對象內部的,外界沒法直接操做和修改。封裝能夠被認爲是一個保護屏障,防止該類的代碼和數據被其餘類隨意訪問。要訪問該類的數據,必須經過指定的方式。適當的封裝可讓代碼更容易理解與維護,也增強了代碼的安全性。
封裝的步驟
1. 使用 private 關鍵字來修飾成員變量。
2. 對須要訪問的成員變量,提供對應的一對 getXxx 方法 、 setXxx 方法。
private的含義
1.`private`是一個權限修飾符,表明最小權限。
2. 能夠修飾成員變量和成員方法。
3. 被`private`修飾後的成員變量和成員方法,只在本類中才能訪問。
private的使用格式:
```java
private 數據類型 變量名;
```
1. 使用 private 修飾成員變量,代碼以下:
```java
publicclassStudent {
privateStringname;
privateintage;
}
```
2. 提供 getXxx 方法 / setXxx 方法,能夠訪問成員變量,代碼以下:
```java
publicclassStudent {
privateStringname;
privateintage;
publicvoidsetName(Stringn) {
name = n;
}
publicStringgetName() {
return name;
}
publicvoidsetAge(inta) {
age = a;
}
publicintgetAge() {
return age;
}
}
```
this的含義this表明所在類的當前對象的引用(地址值),即對象本身的引用。
**方法被哪一個對象調用,方法中的`this`就表明那個對象。即誰在調用,this就表明誰。**
this使用格式:
```java
this.成員變量名;
```
使用 this 修飾方法中的變量,解決成員變量被隱藏的問題,代碼以下:
> 因爲形參變量名與成員變量名重名,致使成員變量名被隱藏,方法中的變量名,沒法訪問到成員變量,從而賦值失敗。因此,咱們只能使用this關鍵字,來解決這個重名問題。
```java
publicclassStudent {
privateStringname;
privateintage;
publicvoidsetName(Stringname) {
//name = name;
this.name = name;
}
publicStringgetName() {
return name;
}
publicvoidsetAge(intage) {
//age = age;
this.age = age;
}
publicintgetAge() {
return age;
}
```
當一個對象被建立時候,構造方法用來初始化該對象,給對象的成員變量賦初始值。
**不管你與否自定義構造方法,全部的類都有構造方法,由於Java自動提供了一個無參數構造方法,一旦本身定義了構造方法,Java自動提供的默認無參數構造方法就會失效。**
構造方法的定義格式:
```java
修飾符 構造方法名(參數列表){
// 方法體
}
```
構造方法的寫法上,方法名與它所在的類名相同。它沒有返回值,因此不須要返回值類型,甚至不須要void。使用構造方法後,代碼以下:
```java
publicclassStudent {
privateStringname;
privateintage;
// 無參數構造方法
publicStudent() {}
// 有參數構造方法
publicStudent(Stringname,intage) {
this.name = name;
this.age = age;
}
}
```
注意事項
1. 若是你不提供構造方法,系統會給出無參數構造方法。
2. 若是你提供了構造方法,系統將再也不提供無參數構造方法。
3. 構造方法是能夠重載的,既能夠定義參數,也能夠不定義參數。
JavaBean 是 Java語言編寫類的一種標準規範。符合 JavaBean 的類,要求類必須是具體的和公共的,而且具備無參數的構造方法,提供用來操做成員變量的 set 和 get 方法。
```java
publicclassClassName{
//成員變量
//構造方法
//無參構造方法【必須】
//有參構造方法【建議】
//成員方法
//getXxx()
//setXxx()
}
```
編寫符合 JavaBean 規範的類,以學生類爲例,標準代碼以下:
```java
publicclassStudent {
//成員變量
privateStringname;
privateintage;
//構造方法
publicStudent() {}
publicStudent(Stringname,intage) {
this.name = name;
this.age = age;
}
//成員方法
publicvoidsetName(Stringname) {
this.name = name;
}
publicStringgetName() {
return name;
}
publicvoidsetAge(intage) {
this.age = age;
}
publicintgetAge() {
return age;
}
}
```
多個類中存在相同屬性和行爲時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行爲,只要繼承那一個類便可。
其中,多個類能夠稱爲子類,單獨那一個類稱爲父類、超類(superclass)或者基類。
父類更通用,子類更具體。咱們經過繼承,可使多種事物之間造成一種關係體系。
**繼承** :就是子類繼承父類的屬性和行爲,使得子類對象具備與父類相同的屬性、相同的行爲。子類能夠直接訪問父類中的非私有的屬性和行爲。
好處:
1. 提升代碼的複用性。
2. 類與類之間產生了關係,是多態的前提。
格式:
經過 `extends` 關鍵字,能夠聲明一個子類繼承另一個父類,定義格式以下:
```java
class父類 {
...
}
class子類extends 父類 {
...
}
```
示例:
```java
/*
* 定義員工類Employee,作爲父類
*/
classEmployee {
Stringname; // 定義name屬性
// 定義員工的工做方法
publicvoidwork() {
System.out.println("全力以赴地工做");
}
}
/*
* 定義講師類Teacher 繼承 員工類Employee
*/
classTeacherextendsEmployee {
// 定義一個打印name的方法
publicvoidprintName() {
System.out.println("name=" + name);
}
}
/*
* 定義測試類
*/
publicclassExtendDemo01 {
publicstaticvoidmain(String[] args) {
// 建立一個講師類對象
Teachert = newTeacher();
// 爲該員工類的name屬性進行賦值
t.name = "小明";
// 調用該員工的printName()方法
t.printName(); // name = 小明
// 調用Teacher類繼承來的work()方法
t.work(); // 全力以赴地工做
}
}
```
- 成員變量不重名
- 若是子類父類中出現不重名的成員變量,這時的訪問是沒有影響的。
- 成員變量重名
- 若是子類父類中出現重名的成員變量,這時的訪問是有影響的。
- 子父類中出現了同名的成員變量時,在子類中須要訪問父類中非私有成員變量時,須要使用 `super` 關鍵字,修飾父類成員變量,相似於以前學過的 ` this` 。
- 成員方法不重名
- 若是子類父類中出現不重名的成員方法,這時的調用是沒有影響的。
- 對象調用方法時,會先在子類中查找有沒有對應的方法,若子類中存在就會執行子類中的方法,若子類中不存在就會執行父類中相應的方法。
- 成員方法重名 ——**重寫(Override)**
- 若是子類父類中出現重名的成員方法,這時的訪問是一種特殊狀況,叫作方法重寫(Override)。
- 構造方法的名字是與類名一致的。因此子類是沒法繼承父類構造方法的。
- 構造方法的做用是初始化成員變量的。因此子類的初始化過程當中,必須先執行父類的初始化動做。子類的構造方法中默認有一個 super() ,表示調用父類的構造方法,父類成員變量初始化後,才能夠給子類使用。
```java
classFu {
privateintn;
Fu(){
System.out.println("Fu()");
}
}
classZiextendsFu {
Zi(){
// super(),調用父類構造方法
super();
System.out.println("Zi()");
}
}
publicclassExtendsDemo07{
publicstaticvoidmain (Stringargs[]){
Zizi = newZi();
}
}
輸出結果:
Fu()
Zi()
```
Java只支持單繼承,不支持多繼承。
方法重寫 :子類中出現與父類如出一轍的方法時(返回值類型,方法名和參數列表都相同),會出現覆蓋效果,也稱爲重寫或者複寫。聲明不變,從新實現。
示例:
子類能夠根據須要,定義特定於本身的行爲。既沿襲了父類的功能名稱,又根據子類的須要從新實現父類方法,從而進行擴展加強。好比新的手機增長來電顯示頭像的功能,代碼以下:
```java
classPhone {
publicvoidsendMessage(){
System.out.println("發短信");
}
publicvoidcall(){
System.out.println("打電話");
}
publicvoidshowNum(){
System.out.println("來電顯示號碼");
}
}
//智能手機類
classNewPhoneextendsPhone {
//重寫父類的來電顯示號碼功能,並增長本身的顯示姓名和圖片功能
publicvoidshowNum(){
//調用父類已經存在的功能使用super
super.showNum();
//增長本身特有顯示姓名和圖片功能
System.out.println("顯示來電姓名");
System.out.println("顯示頭像");
}
}
publicclassExtendsDemo06 {
publicstaticvoidmain(String[] args) {
// 建立子類對象
NewPhonenp = newNewPhone();
// 調用父類繼承而來的方法
np.call();
// 調用子類重寫的方法
np.showNum();
}
}
```
**注意事項**
1. 子類方法覆蓋父類方法,必需要保證權限大於等於父類權限。
2. 子類方法覆蓋父類方法,返回值類型、函數名和參數列表都要如出一轍。
父類空間優先於子類對象產生
在每次建立子類對象時,先初始化父類空間,再建立其子類對象自己。目的在於子類對象中包含了其對應的父類空間,即可以包含其父類的成員,若是父類成員非private修飾,則子類能夠隨意使用父類成員。代碼體如今子類的構造方法調用時,必定先調用父類的構造方法。
super和this的含義
- super :表明父類的存儲空間標識(能夠理解爲父親的引用)。
- this :表明當前對象的引用(誰調用就表明誰)。
super和this的用法
1. 訪問成員
```java
this.成員變量 ‐‐ 本類的
super.成員變量 ‐‐ 父類的
this.成員方法名() ‐‐ 本類的
super.成員方法名() ‐‐ 父類的
```
示例:
```java
classAnimal {
publicvoideat() {
System.out.println("animal : eat");
}
}
classCatextendsAnimal {
publicvoideat() {
System.out.println("cat : eat");
}
publicvoideatTest() {
this.eat(); // this 調用本類的方法
super.eat(); // super 調用父類的方法
}
}
publicclassExtendsDemo {
publicstaticvoidmain(String[] args) {
Animala = newAnimal();
a.eat();
Catc = newCat();
c.eatTest();
}
}
輸出結果爲:
animal : eat
cat : eat
animal : eat
```
2. 訪問構造方法
```java
this(...) ‐‐ 本類的構造方法
super(...) ‐‐ 父類的構造方法
```
- 子類的每一個構造方法中均有默認的super(),調用父類的空參構造。手動調用父類構造會覆蓋默認的super()。
- super() 和 this() 都必須是在構造方法的第一行,因此不能同時出現。
父類中的方法,被它的子類們重寫,子類各自的實現都不盡相同。那麼父類的方法聲明和方法主體,只有聲明還有意義,而方法主體則沒有存在的意義了。咱們把沒有方法主體的方法稱爲**抽象方法**。**Java語法規定,包含抽象方法的類就是抽象類**。
使用 abstract 關鍵字修飾方法,該方法就成了抽象方法,抽象方法只包含一個方法名,而沒有方法體
格式:
```java
修飾符 abstract 返回值類型 方法名 (參數列表);
```
示例:
```java
publicabstractvoidrun();
```
格式:
```java
abstractclass類名字 {
}
```
示例:
```java
publicabstractclassAnimal {
publicabstractvoidrun();
}
```
抽象的使用:
繼承抽象類的子類必須重寫父類全部的抽象方法。不然,該子類也必須聲明爲抽象類。最終,必須有子類實現該父類的抽象方法,不然,從最初的父類到最終的子類都不能建立對象,失去意義。
```java
publicclassCatextendsAnimal {
publicvoidrun (){
System.out.println("小貓在牆頭走~~~");
}
}
publicclassCatTest {
publicstaticvoidmain(String[] args) {
// 建立子類對象
Catc = newCat();
// 調用run方法
c.run();
}
}
輸出結果:
小貓在牆頭走~~~
```
此時的方法重寫,是子類對父類抽象方法的完成實現,咱們將這種方法重寫的操做,也叫作實現方法。
多態 : 是指同一行爲,具備多個不一樣表現形式。
多態體現的格式:
```java
父類類型 變量名 = new 子類對象; //父類類型:指子類對象繼承的父類類型,或者實現的父接口類型。
變量名.方法名();
```
當使用多態方式調用方法時,首先檢查父類中是否有該方法,若是沒有,則編譯錯誤;若是有,執行的是子類重寫後方法。
定義父類:
```java
publicabstractclassAnimal {
publicabstractvoideat();
}
```
定義子類:
```java
classCatextendsAnimal {
publicvoideat() {
System.out.println("吃魚");
}
}
classDogextendsAnimal {
publicvoideat() {
System.out.println("吃骨頭");
}
}
```
定義測試類:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
// 多態形式,建立對象
Animala1 = newCat();
// 調用的是 Cat 的 eat
a1.eat();
// 多態形式,建立對象
Animala2 = newDog();
// 調用的是 Dog 的 eat
a2.eat();
}
}
```
實際開發的過程當中,父類類型做爲方法形式參數,傳遞子類對象給方法,進行方法的調用,更能體現出多態的擴展性與便利。
定義父類:
```java
publicabstractclassAnimal {
publicabstractvoideat();
}
```
定義子類:
```java
classCatextendsAnimal {
publicvoideat() {
System.out.println("吃魚");
}
}
classDogextendsAnimal {
publicvoideat() {
System.out.println("吃骨頭");
}
}
```
定義測試類:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
// 多態形式,建立對象
Catc = newCat();
Dogd = newDog();
// 調用showCatEat
showCatEat(c);
// 調用showDogEat
showDogEat(d);
/*
以上兩個方法, 都可以被showAnimalEat(Animal a)方法所替代
而執行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
publicstaticvoidshowCatEat (Catc){
c.eat();
}
publicstaticvoidshowDogEat (Dogd){
d.eat();
}
publicstaticvoidshowAnimalEat (Animala){
a.eat();
}
}
```
因爲多態特性的支持, showAnimalEat方法的Animal類型,是Cat和Dog的父類類型,父類類型接收子類對象,固然能夠把Cat對象和Dog對象,傳遞給方法。
當eat方法執行時,多態規定,執行的是子類重寫的方法,那麼效果天然與showCatEat、showDogEat方法一致,因此showAnimalEat徹底能夠替代以上兩方法。
不只僅是替代,在擴展性方面,不管以後再多的子類出現,咱們都不須要編寫showXxxEat方法了,直接使用showAnimalEat均可以完成。
因此,多態的好處,體如今,可使程序編寫的更簡單,並有良好的擴展。
多態的轉型分爲向上轉型與向下轉型兩種
向上轉型 :多態自己是子類類型向父類類型向上轉換的過程,這個過程是默認的。當父類引用指向一個子類對象時,即是向上轉型。
使用格式:
```java
父類類型 變量名 = new 子類類型();
如:Animala = newCat();
```
向下轉型 :父類類型向子類類型向下轉換的過程,這個過程是強制的。一個已經向上轉型的子類對象,將父類引用轉爲子類引用,可使用強制類型轉換的格式,即是向下轉型。
使用格式:
```java
子類類型 變量名 = (子類類型) 父類變量名;
如:Catc =(Cat) a;
```
當使用多態方式調用方法時,首先檢查父類中是否有該方法,若是沒有,則編譯錯誤。也就是說,不能調用子類擁有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態給咱們帶來的一點"小麻煩"。因此,想要調用子類特有的方法,必須作向下轉型。
轉型演示,代碼以下:
定義類:
```java
abstractclassAnimal {
abstractvoideat();
}
classCatextendsAnimal {
publicvoideat() {
System.out.println("吃魚");
}
publicvoidcatchMouse() {
System.out.println("抓老鼠");
}
}
classDogextendsAnimal {
publicvoideat() {
System.out.println("吃骨頭");
}
publicvoidwatchHouse() {
System.out.println("看家");
}
}
```
定義測試類:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
// 向上轉型
Animala = newCat();
a.eat(); // 調用的是 Cat 的 eat
// 向下轉型
Catc = (Cat)a;
c.catchMouse(); // 調用的是 Cat 的 catchMouse
}
}
```
轉型的過程當中,一不當心就會遇到這樣的問題,請看以下代碼:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
// 向上轉型
Animala = newCat();
a.eat(); // 調用的是 Cat 的 eat
// 向下轉型
Dogd = (Dog)a;
d.watchHouse(); // 調用的是 Dog 的 watchHouse 【運行報錯】
}
}
```
這段代碼能夠經過編譯,可是運行時,卻報出了 `ClassCastException `,類型轉換異常!這是由於,明明建立了Cat類型對象,運行時,固然不能轉換成Dog對象的。這兩個類型並無任何繼承關係,不符合類型轉換的定義。
爲了不`ClassCastException`的發生,Java提供了 ` instanceof `關鍵字,給引用變量作類型的校驗,格式以下:
```java
變量名 instanceof 數據類型
若是變量屬於該數據類型,返回true。
若是變量不屬於該數據類型,返回false。
```
因此,轉換前,咱們最好先作一個判斷,代碼以下:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
// 向上轉型
Animala = newCat();
a.eat(); // 調用的是 Cat 的 eat
// 向下轉型
if (a instanceof Cat){
Catc = (Cat)a;
c.catchMouse(); // 調用的是 Cat 的 catchMouse
} elseif (a instanceof Dog){
Dogd = (Dog)a;
d.watchHouse(); // 調用的是 Dog 的 watchHouse
}
}
}
```
接口,是Java語言中一種引用類型,是方法的集合,若是說類的內部封裝了成員變量、構造方法和成員方法,那麼接口的內部主要就是封裝了方法,包含**抽象方法**(JDK 7及之前),**默認方法**和**靜態方法**(JDK 8),**私有方法**(JDK 9)。
接口的定義,它與定義類方式類似,可是使用 interface 關鍵字。它也會被編譯成.class文件,但必定要明確它並非類,而是另一種引用數據類型。
接口的使用,它不能建立對象,可是能夠被實現( implements ,相似於被繼承)。一個實現接口的類(能夠看作是接口的子類),須要實現接口中全部的抽象方法,建立該類對象,就能夠調用方法了,不然它必須是一個抽象類。
定義格式
```java
publicinterface接口名稱 {
// 抽象方法
// 默認方法
// 靜態方法
// 私有方法
}
```
類與接口的關係爲實現關係,即類實現接口,該類能夠稱爲接口的實現類,也能夠稱爲接口的子類。實現的動做相似繼承,格式相仿,只是關鍵字不一樣,實現使用 ` implements` 關鍵字。
非抽象子類實現接口:
1. 必須重寫接口中全部抽象方法。
2. 繼承了接口的默認方法,便可以直接調用,也能夠重寫。
實現格式:
```java
class類名implements 接口名 {
// 重寫接口中抽象方法【必須】
// 重寫接口中默認方法【可選】
}
```
抽象方法:使用 `abstract` 關鍵字修飾,能夠省略,沒有方法體。該方法供子類實現使用。
**抽象方法必須實現。**
定義接口:
```java
publicinterfaceLiveAble {
// 定義抽象方法
publicabstractvoideat();
publicabstractvoidsleep();
}
```
接口實現類:
```java
publicclassAnimalimplementsLiveAble {
@Override
publicvoideat() {
System.out.println("吃東西");
}
@Override
publicvoidsleep() {
System.out.println("晚上睡");
}
}
```
測試類:
```java
publicclassInterfaceDemo {
publicstaticvoidmain(String[] args) {
// 建立子類對象
Animala = newAnimal();
// 調用實現後的方法
a.eat();
a.sleep();
}
}
輸出結果:
吃東西
晚上睡
```
使用 `default `修飾,不可省略,供子類調用或者子類重寫。
能夠**繼承**,能夠**重寫**,二選一,可是隻能經過實現類的對象來調用。
定義接口:
```java
publicinterfaceLiveAble {
publicdefaultvoidfly(){
System.out.println("天上飛");
}
}
```
定義實現類:
```java
publicclassAnimalimplementsLiveAble {
// 繼承,什麼都不用寫,直接調用
}
```
定義測試類:
```java
publicclassInterfaceDemo {
publicstaticvoidmain(String[] args) {
// 建立子類對象
Animala = newAnimal();
// 調用默認方法
a.fly();
}
}
輸出結果:
天上飛
```
定義接口:
```java
publicinterfaceLiveAble {
publicdefaultvoidfly(){
System.out.println("天上飛");
}
}
```
定義實現類:
```java
publicclassAnimalimplementsLiveAble {
@Override
publicvoidfly() {
System.out.println("自由自在的飛");
}
}
```
定義測試類:
```java
publicclassInterfaceDemo {
publicstaticvoidmain(String[] args) {
// 建立子類對象
Animala = newAnimal();
// 調用重寫方法
a.fly();
}
}
輸出結果:
自由自在的飛
```
靜態方法:使用 static 修飾,供接口直接調用。
靜態方法與.class 文件相關,**只能使用接口名調用**,不能夠經過實現類的類名或者實現類的對象調用
定義接口:
```java
publicinterfaceLiveAble {
publicstaticvoidrun(){
System.out.println("跑起來~~~");
}
}
```
定義實現類:
```java
publicclassAnimalimplementsLiveAble {
// 沒法重寫靜態方法
}
```
測試類:
```java
publicclassInterfaceDemo {
publicstaticvoidmain(String[] args) {
// Animal.run(); // 【錯誤】沒法繼承方法,也沒法調用
LiveAble.run(); //
}
}
輸出結果:
跑起來~~~
```
私有方法:使用 `private` 修飾,供接口中的默認方法或者靜態方法調用。
- 私有方法:只有默認方法能夠調用。
- 私有靜態方法:默認方法和靜態方法能夠調用。
若是一個接口中有多個默認方法,而且方法中有重複的內容,那麼能夠抽取出來,封裝到私有方法中,供默認方法去調用。從設計的角度講,私有的方法是對默認方法和靜態方法的輔助。
示例:
```java
publicinterfaceLiveAble {
defaultvoidfunc(){
func1();
func2();
}
privatevoidfunc1(){
System.out.println("跑起來~~~");
}
privatevoidfunc2(){
System.out.println("跑起來~~~");
}
}
```
在繼承體系中,一個類只能繼承一個父類。而對於接口而言,一個類是能夠實現多個接口的,這叫作接口的多實現。而且,一個類能繼承一個父類,同時實現多個接口。
實現格式:
```java
class類名 [extends 父類名] implements 接口名1,接口名2,接口名3... {
// 重寫接口中抽象方法【必須】
// 重寫接口中默認方法【不重名時可選】
}
```
接口中,有多個抽象方法時,實現類必須重寫全部抽象方法。**若是抽象方法有重名的,只須要重寫一次。**
定義多個接口:
```java
interfaceA {
publicabstractvoidshowA();
publicabstractvoidshow();
}
interfaceB {
publicabstractvoidshowB();
publicabstractvoidshow();
}
```
實現:
```java
publicclassCimplementsA,B{
@Override
publicvoidshowA() {
System.out.println("showA");
}
@Override
publicvoidshowB() {
System.out.println("showB");
}
@Override
publicvoidshow() {
System.out.println("show");
}
}
```
接口中,有多個默認方法時,實現類均可繼承使用。**若是默認方法有重名的,必須重寫一次。**
定義多個接口:
```java
interfaceA {
publicdefaultvoidmethodA(){}
publicdefaultvoidmethod(){}
}
interfaceB {
publicdefaultvoidmethodB(){}
publicdefaultvoidmethod(){}
}
```
實現:
```java
publicclassCimplementsA,B{
@Override
publicvoidmethod() {
System.out.println("method");
}
}
```
接口中,存在同名的靜態方法並不會衝突,緣由是隻能經過各自接口名訪問靜態方法。
當一個類,既繼承一個父類,又實現若干個接口時,父類中的成員方法與接口中的默認方法重名,子類就近選擇執行父類的成員方法。
定義接口:
```java
interfaceA {
publicdefaultvoidmethodA(){
System.out.println("AAAAAAAAAAAA");
}
}
```
定義父類:
```java
classD {
publicvoidmethodA(){
System.out.println("DDDDDDDDDDDD");
}
}
```
定義子類:
```java
classCextendsDimplementsA {
// 未重寫methodA方法
}
```
定義測試類:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
Cc = newC();
c.methodA();
}
}
輸出結果:
DDDDDDDDDDDD
```
一個接口能繼承另外一個或者多個接口,這和類之間的繼承比較類似。接口的繼承使用 extends 關鍵字,子接口繼承父接口的方法。若是父接口中的默認方法有重名的,那麼子接口須要重寫一次。
定義父接口:
```java
interfaceA {
publicdefaultvoidmethod(){
System.out.println("AAAAAAAAAAAAAAAAAAA");
}
}
interfaceB {
publicdefaultvoidmethod(){
System.out.println("BBBBBBBBBBBBBBBBBBB");
}
}
```
定義子接口:
```java
interfaceDextendsA,B{
@Override
publicdefaultvoidmethod() {
System.out.println("DDDDDDDDDDDDDD");
}
}
```
1. 子接口重寫默認方法時,default關鍵字能夠保留。
2. 子類重寫默認方法時,default關鍵字不能夠保留。
3.**接口中,沒法定義成員變量,可是能夠定義常量,其值不能夠改變,默認使用 `public static final`修飾。**
4. 接口中,沒有構造方法,不能建立對象。
5. 接口中,沒有靜態代碼塊。
關於 static 關鍵字的使用,它能夠用來修飾的**成員變量**和**成員方法**,被修飾的成員是屬於類的,而不是單單是屬
於某個對象的。也就是說,既然屬於類,就能夠不靠建立對象來調用了。
當 static 修飾**成員變量**時,該變量稱爲**類變量**。該類的每一個對象都共享同一個類變量的值。任何對象均可以更改該類變量的值,但也能夠在不建立該類的對象的狀況下對類變量進行操做。
格式:
```java
static 數據類型 變量名;
```
示例:
建立Student類
```java
publicclassStudent {
privateintid;
privateStringname;
privateintage;
staticStringroom;
privatestaticintidCounter = 0; //學號計數器,每當new了一個新對象的時候,計數器++
publicStudent(){
this.id = ++idCounter;
}
publicStudent(Stringname,intage) {
this.id = ++idCounter;
this.name = name;
this.age = age;
}
publicintgetId() {
return id;
}
publicvoidsetId(intid) {
this.id = id;
}
publicStringgetName() {
return name;
}
publicvoidsetName(Stringname) {
this.name = name;
}
publicintgetAge() {
return age;
}
publicvoidsetAge(intage) {
this.age = age;
}
}
```
調用:
```java
publicclassStaticDemo {
publicstaticvoidmain(String[] args) {
// 首先設置一下教室,這是靜態的東西,應該經過類名稱進行調用
Student.room = "101教室";
Studentstu1 = newStudent("xiaoming",18);
System.out.println("id:"+ stu1.getId()+",姓名:"+ stu1.getName()+",年齡:"+stu1.getAge()+",教室:"+Student.room);
System.out.println("=====================");
Studentstu2 = newStudent("xiaohong",19);
System.out.println("id:"+ stu2.getId()+",姓名:"+ stu2.getName()+",年齡:"+stu2.getAge()+",教室:"+Student.room);
System.out.println("=====================");
Studentstu3 = newStudent("xiaowang",22);
System.out.println("id:"+ stu3.getId()+",姓名:"+ stu3.getName()+",年齡:"+stu3.getAge()+",教室:"+Student.room);
System.out.println("=====================");
}
}
```
**類變量的修改還調用通常直接使用類名**
當 static 修飾成員方法時,該方法稱爲**類方法** 。**靜態方法在聲明中有 static ,建議使用類名來調用,而不須要建立類的對象。**調用方式很是簡單。
使用 static關鍵字修飾的成員方法,習慣稱爲**靜態方法**。
格式:
```java
修飾符 static 返回值類型 方法名 (參數列表){
// 執行語句
}
```
示例: 在Student類中定義靜態方法
```java
publicstaticvoidshowNum() {
System.out.println("num:" + numberOfStudent);
}
```
調用格式:
被static修飾的成員能夠而且**建議經過類名直接訪問**。雖然也能夠經過對象名訪問靜態成員,緣由即多個對象均屬於一個類,共享使用同一個靜態成員,可是不建議,會出現警告信息。
格式:
```java
// 訪問類變量
類名.類變量名;
// 調用靜態方法
類名.靜態方法名(參數);
```
示例:
```java
//訪問靜態變量(類變量)
System.out.println(Student.room);
//訪問靜態方法
Student.showNum();
```
**靜態方法調用的注意事項:**
- 靜態方法能夠直接訪問類變量和靜態方法。
- 靜態方法 不能直接訪問普通成員變量或成員方法。反之成員方法能夠直接訪問類變量或靜態方法。
- 靜態方法中,不能使用 this關鍵字。
static 修飾的內容:
- 是隨着類的加載而加載的,且只加載一次。
- 存儲於一塊固定的內存區域(靜態區),因此,能夠直接被類名調用。
- 它優先於對象存在,因此,能夠被全部對象共享。
![靜態的內存圖](https://foochane.cn/images/20...
靜態代碼塊 :定義在成員位置,使用static修飾的代碼塊{ }。
- 位置:類中方法外。
- 執行:**隨着類的加載而執行且執行一次,優先於 main方法和構造方法的執行**。
格式:
```java
publicclassClassName{
static {
// 執行語句
}
}
```
示例:
```java
publicclassPerson {
static {
System.out.println("靜態代碼塊執行!");
}
publicPerson() {
System.out.println("構造方法執行!");
}
}
```
調用Person類
```java
/*
靜態代碼塊特色:當第一次用到本類時,靜態代碼塊執行惟一的一次。
靜態內容老是優先於非靜態,因此靜態代碼塊比構造方法先執行。
靜態代碼塊的典型用途:用來一次性地對靜態成員變量進行賦值。
*/
publicclassDemo04Static {
publicstaticvoidmain(String[] args) {
//靜態代碼塊先執行,且只執行一次,構造方法執行了兩次
Personone = newPerson();
Persontwo = newPerson();
}
}
```
學習了繼承後,咱們知道,子類能夠在父類的基礎上改寫父類內容,好比,方法重寫。那麼咱們能不能隨意的繼承API中提供的類,改寫其內容呢?顯然這是不合適的。爲了不這種隨意改寫的狀況,Java提供了`final `關鍵字,用於修飾不可改變內容。
**`final `: 不可改變。能夠用於修飾類、方法和變量。**
- 類:被修飾的類,不能被繼承。
- 方法:被修飾的方法,不能被重寫。
- 變量:被修飾的變量,不能被從新賦值。
格式以下:
```java
finalclass類名 {
}
```
查詢 API發現像 public final class String 、 public final class Math 、 public final class Scanner等,不少咱們學習過的類,都是被final修飾的,目的就是供咱們使用,而不讓咱們因此改變其內容。
格式以下:
```java
修飾符 final 返回值類型 方法名(參數列表){
//方法體
}
```
**重寫被 final 修飾的方法,編譯時就會報錯。**
基本類型的局部變量,被final修飾後,只能賦值一次,不能再更改。代碼以下:
```java
publicclassFinalDemo1 {
publicstaticvoidmain(String[] args) {
// 聲明變量,使用final修飾
finalinta;
// 第一次賦值
a = 10;
// 第二次賦值
a = 20; // 報錯,不可從新賦值
// 聲明變量,直接賦值,使用final修飾
finalintb = 10;
// 第二次賦值
b = 20; // 報錯,不可從新賦值
}
}
```
引用類型的局部變量,被final修飾後,只能指向一個對象,地址不能再更改。可是不影響對象內部的成員變量值的修改,代碼以下:
```java
publicclassFinalDemo2 {
publicstaticvoidmain(String[] args) {
// 建立 User 對象
finalUseru = newUser();
// 建立 另外一個 User對象
u = newUser(); // 報錯,指向了新的對象,地址值改變。
// 調用setName方法
u.setName("張三"); // 能夠修改
}
}
```
成員變量涉及到初始化的問題,初始化方式有兩種,只能二選一:
- 顯示初始化:
```java
publicclassUser {
finalStringUSERNAME = "張三";
privateintage;
}
```
- 構造方法初始化:
```java
publicclassUser {
finalStringUSERNAME ;
privateintage;
publicUser(Stringusername, intage) {
this.USERNAME = username;
this.age = age;
}
}
```
**被final修飾的常量名稱,通常都有書寫規範,全部字母都大寫。**
在Java中提供了四種訪問權限,使用不一樣的訪問權限修飾符修飾時,被修飾的內容會有不一樣的訪問權限,
- public :公共的。
- protected :受保護的
- default :默認的
- private :私有的
不一樣權限的訪問能力:
| 類 | public | protected | default(空的) | private |
| ---------------------- | ------ | --------- | --------------- | ------- |
| 同一類中 | YES | YES | YES | YES |
| 同一包中(子類與無關類) | YES | YES | YES | NO |
| 不一樣包的子類 | YES | YES | NO | NO |
| 不一樣包中的無關類 | YES | NO | NO | NO |
可見,public具備最大權限。private則是最小權限。
編寫代碼時,若是沒有特殊的考慮,建議這樣使用權限:
- 成員變量使用 private ,隱藏細節。
- 構造方法使用 public ,方便建立對象。
- 成員方法使用 public ,方便調用方法。
**注意:不加權限修飾符,其訪問能力與default修飾符相同**
將一個類A定義在另外一個類B裏面,裏面的那個類A就稱爲內部類,B則稱爲外部類。
成員內部類 :定義在類中方法外的類。
定義格式:
```java
class外部類 {
class內部類{
}
}
```
在描述事物時,若一個事物內部還包含其餘事物,就可使用內部類這種結構。好比,汽車類 Car 中包含發動機類 Engine ,這時, Engine 就可使用內部類來描述,定義在成員位置。
代碼舉例:
```java
classCar { //外部類
classEngine { //內部類
}
}
```
訪問特色
- 內部類能夠直接訪問外部類的成員,包括私有成員。
- 外部類要訪問內部類的成員,必需要創建內部類的對象。
建立內部類對象格式:
```java
外部類名.內部類名 對象名 = new 外部類型().new 內部類型();
```
訪問演示,代碼以下:
定義類:
```java
publicclassPerson {
privatebooleanlive = true;
classHeart {
publicvoidjump() {
// 直接訪問外部類成員
if (live) {
System.out.println("心臟在跳動");
} else {
System.out.println("心臟不跳了");
}
}
}
publicbooleanisLive() {
return live;
}
publicvoidsetLive(booleanlive) {
this.live = live;
}
}
```
定義測試類:
```java
publicclassInnerDemo {
publicstaticvoidmain(String[] args) {
// 建立外部類對象
Personp = newPerson();
// 建立內部類對象
Heartheart = p.newHeart();
// 調用內部類方法
heart.jump();
// 調用外部類方法
p.setLive(false);
// 調用內部類方法
heart.jump();
}
}
輸出結果:
心臟在跳動
心臟不跳了
```
內部類仍然是一個獨立的類,在編譯以後會內部類會被編譯成獨立的 .class文件,可是前面冠之外部類的類名和`$`符號 。
好比,`Person$Heart.class`
內部類重名變量訪問:
```java
publicclassOuter {
intnum = 10;
publicclassInner{
intnum = 20;
publicvoidmethodInner(){
intnum = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(Outer.this.num);
}
}
publicstaticvoidmain(String[] args) {
Outer.Innerinner = newOuter().new Inner();
inner.methodInner();
}
}
輸出:
30
20
10
```
匿名內部類 :是內部類的簡化寫法。它的本質是一個 帶具體實現的 父類或者父接口的 匿名的 子類對象。開發中,最經常使用到的內部類就是匿名內部類了。
匿名內部類必須繼承一個父類或者實現一個父接口。
格式:
```java
new 父類名或者接口名(){
// 方法重寫
@Override
publicvoidmethod() {
// 執行語句
}
};
```
使用方式
以接口爲例,匿名內部類的使用,代碼以下:
定義接口:
```java
publicabstractclassFlyAble{
publicabstractvoidfly();
}
```
建立匿名內部類,並調用:
```java
publicclassInnerDemo {
publicstaticvoidmain(String[] args) {
/*
1.等號右邊:是匿名內部類,定義並建立該接口的子類對象
2.等號左邊:是多態賦值,接口類型引用指向子類對象
*/
FlyAblef = newFlyAble(){
publicvoidfly() {
System.out.println("我飛了~~~");
}
};
//調用 fly方法,執行重寫後的方法
f.fly();
}
}
```
一般在方法的形式參數是接口或者抽象類時,也能夠將匿名內部類做爲參數傳遞。代碼以下:
```java
publicclassInnerDemo2 {
publicstaticvoidmain(String[] args) {
/*
1.等號右邊:定義並建立該接口的子類對象
2.等號左邊:是多態,接口類型引用指向子類對象
*/
FlyAblef = newFlyAble(){
publicvoidfly() {
System.out.println("我飛了~~~");
}
};
// 將f傳遞給showFly方法中
showFly(f);
}
publicstaticvoidshowFly(FlyAblef) {
f.fly();
}
}
```
以上兩步,也能夠簡化爲一步,代碼以下:
```java
publicclassInnerDemo3 {
publicstaticvoidmain(String[] args) {
/*
建立匿名內部類,直接傳遞給showFly(FlyAble f)
*/
showFly( newFlyAble(){
publicvoidfly() {
System.out.println("我飛了~~~");
}
});
}
publicstaticvoidshowFly(FlyAblef) {
f.fly();
}
}
```
實際的開發中,引用類型的使用很是重要,也是很是廣泛的。咱們能夠在理解基本類型的使用方式基礎上,進一步去掌握引用類型的使用方式。基本類型能夠做爲成員變量、做爲方法的參數、做爲方法的返回值,那麼固然引用類型也是能夠的。
在定義一個類Role(遊戲角色)時,代碼以下:
```java
classRole {
intid; // 角色id
intblood; // 生命值
Stringname; // 角色名稱
}
```
使用 int 類型表示 角色id和生命值,使用 String 類型表示姓名。此時, String 自己就是引用類型,因爲使用的方式相似常量,因此每每忽略了它是引用類型的存在。若是咱們繼續豐富這個類的定義,給 Role 增長武器,穿戴裝備等屬性,咱們將如何編寫呢?
定義武器類,將增長攻擊能力:
```java
classWeapon {
String name; // 武器名稱
int hurt; // 傷害值
}
```
定義穿戴盔甲類,將增長防護能力,也就是提高生命值:
```java
classArmour {
String name;// 裝備名稱
int protect;// 防護值
}
```
定義角色類:
```java
classRole {
int id;
int blood;
String name;
// 添加武器屬性
Weapon wp;
// 添加盔甲屬性
Armour ar;
// 提供get/set方法
publicWeapongetWp() {
return wp;
}
publicvoidsetWeapon(Weaponwp) {
this.wp = wp;
}
publicArmourgetArmour() {
return ar;
}
publicvoidsetArmour(Armourar) {
this.ar = ar;
}
// 攻擊方法
publicvoidattack(){
System.out.println("使用"+ wp.getName() +", 形成"+wp.getHurt()+"點傷害");
}
// 穿戴盔甲
publicvoidwear(){
// 增長防護,就是增長blood值
this.blood += ar.getProtect();
System.out.println("穿上"+ar.getName()+", 生命值增長"+ar.getProtect());
}
}
```
測試類:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
// 建立Weapon 對象
Weaponwp = newWeapon("屠龍刀" , 999999);
// 建立Armour 對象
Armourar = newArmour("麒麟甲",10000);
// 建立Role 對象
Roler = newRole();
// 設置武器屬性
r.setWeapon(wp);
// 設置盔甲屬性
r.setArmour(ar);
// 攻擊
r.attack();
// 穿戴盔甲
r.wear();
}
}
輸出結果:
使用屠龍刀,形成999999點傷害
穿上麒麟甲 ,生命值增長10000
```
類做爲成員變量時,對它進行賦值的操做,實際上,是賦給它該類的一個對象。
接口是對方法的封裝,對應遊戲當中,能夠看做是擴展遊戲角色的技能。因此,若是想擴展更強大技能,咱們在Role 中,能夠增長接口做爲成員變量,來設置不一樣的技能。
定義接口:
```java
// 法術攻擊
publicinterfaceFaShuSkill {
publicabstractvoidfaShuAttack();
}
```
定義角色類:
```java
publicclassRole {
FaShuSkillfs;
publicvoidsetFaShuSkill(FaShuSkillfs) {
this.fs = fs;
}
// 法術攻擊
publicvoidfaShuSkillAttack(){
System.out.print("發動法術攻擊:");
fs.faShuAttack();
System.out.println("攻擊完畢");
}
}
```
定義測試類:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
// 建立遊戲角色
Rolerole = newRole();
// 設置角色法術技能
role.setFaShuSkill(newFaShuSkill() {
@Override
publicvoidfaShuAttack() {
System.out.println("縱橫天下");
}
});
// 發動法術攻擊
role.faShuSkillAttack();
// 更換技能
role.setFaShuSkill(newFaShuSkill() {
@Override
publicvoidfaShuAttack() {
System.out.println("逆轉乾坤");
}
});
// 發動法術攻擊
role.faShuSkillAttack();
}
}
輸出結果:
發動法術攻擊:縱橫天下
攻擊完畢
發動法術攻擊:逆轉乾坤
攻擊完畢
```
咱們使用一個接口,做爲成員變量,以便隨時更換技能,這樣的設計更爲靈活,加強了程序的擴展性。
接口做爲成員變量時,對它進行賦值的操做,實際上,是賦給它該接口的一個子類對象。
當接口做爲方法的參數時,須要傳遞什麼呢?當接口做爲方法的返回值類型時,須要返回什麼呢?對,其實都是它的子類對象。 ArrayList 類咱們並不陌生,查看API咱們發現,實際上,它是 java.util.List 接口的實類。因此,當咱們看見 List 接口做爲參數或者返回值類型時,固然能夠將 ArrayList 的對象進行傳遞或返回。
請觀察以下方法:獲取某集合中全部的偶數。
定義方法:
```java
publicstaticList<Integer> getEvenNum(List<Integer> list) {
// 建立保存偶數的集合
ArrayList<Integer> evenList = newArrayList<>();
// 遍歷集合list,判斷元素爲偶數,就添加到evenList中
for (inti = 0; i < list.size(); i++) {
Integerinteger = list.get(i);
if (integer % 2 == 0) {
evenList.add(integer);
}
}
/*
返回偶數集合
由於getEvenNum方法的返回值類型是List,而ArrayList是List的子類,
因此evenList能夠返回
*/
return evenList;
}
```
調用方法:
```java
publicclassTest {
publicstaticvoidmain(String[] args) {
// 建立ArrayList集合,並添加數字
ArrayList<Integer> srcList = newArrayList<>();
for (inti = 0; i < 10; i++) {
srcList.add(i);
}
/*
獲取偶數集合
由於getEvenNum方法的參數是List,而ArrayList是List的子類,
因此srcList能夠傳遞
*/
Listlist = getEvenNum(srcList);
System.out.println(list);
}
}
```
接口做爲參數時,傳遞它的子類對象。
接口做爲返回值類型時,返回它的子類對象。
***遞歸**:指在當前方法內調用本身的這種現象。
***遞歸的分類:**
- 遞歸分爲兩種,直接遞歸和間接遞歸。
- 直接遞歸稱爲方法自身調用本身。
- 間接遞歸能夠A方法調用B方法,B方法調用C方法,C方法調用A方法。
***注意事項**:
- 遞歸必定要有條件限定,保證遞歸可以中止下來,不然會發生棧內存溢出。
- 在遞歸中雖然有限定條件,可是遞歸次數不能太多。不然也會發生棧內存溢出。
- 構造方法,禁止遞歸
```java
publicclassDemo01DiGui {
publicstaticvoidmain(String[] args) {
// a();
b(1);
}
/*
* 3.構造方法,禁止遞歸
* 編譯報錯:構造方法是建立對象使用的,不能讓對象一直建立下去
*/
publicDemo01DiGui() {
//Demo01DiGui();
}
/*
* 2.在遞歸中雖然有限定條件,可是遞歸次數不能太多。不然也會發生棧內存溢出。
* 4993
* Exception in thread "main" java.lang.StackOverflowError
*/
privatestaticvoidb(inti) {
System.out.println(i);
//添加一個遞歸結束的條件,i==5000的時候結束
if(i==5000){
return;//結束方法
}
b(++i);
}
/*
* 1.遞歸必定要有條件限定,保證遞歸可以中止下來,不然會發生棧內存溢出。 Exception in thread "main"
* java.lang.StackOverflowError
*/
privatestaticvoida() {
System.out.println("a方法");
a();
}
}
```
**分析**:num的累和 = num + (num-1)的累和,因此能夠把累和的操做定義成一個方法,遞歸調用。
**實現代碼**:
```java
publicclassDiGuiDemo {
publicstaticvoidmain(String[] args) {
//計算1~num的和,使用遞歸完成
intnum = 5;
// 調用求和的方法
intsum = getSum(num);
// 輸出結果
System.out.println(sum);
}
/*
經過遞歸算法實現.
參數列表:int
返回值類型: int
*/
publicstaticintgetSum(intnum) {
/*
num爲1時,方法返回1,
至關因而方法的出口,num總有是1的狀況
*/
if(num == 1){
return1;
}
/*
num不爲1時,方法返回 num +(num-1)的累和
遞歸調用getSum方法
*/
return num + getSum(num-1);
}
}
```
![遞歸累和](https://raw.githubusercontent...
> 小貼士:遞歸必定要有條件限定,保證遞歸可以中止下來,次數不要太多,不然會發生棧內存溢出。
***階乘**:全部小於及等於該數的正整數的積。
```java
n的階乘:n! = n * (n-1) *...* 3 * 2 * 1
```
**分析**:這與累和相似,只不過換成了乘法運算,學員能夠本身練習,須要注意階乘值符合int類型的範圍。
```Java
推理得出:n! = n * (n-1)!
```
**代碼實現**:
```java
publicclassDiGuiDemo {
//計算n的階乘,使用遞歸完成
publicstaticvoidmain(String[] args) {
intn = 3;
// 調用求階乘的方法
intvalue = getValue(n);
// 輸出結果
System.out.println("階乘爲:"+ value);
}
/*
經過遞歸算法實現.
參數列表:int
返回值類型: int
*/
publicstaticintgetValue(intn) {
// 1的階乘爲1
if (n == 1) {
return1;
}
/*
n不爲1時,方法返回 n! = n*(n-1)!
遞歸調用getValue方法
*/
return n * getValue(n - 1);
}
}
```
**分析**:多級目錄的打印,就是當目錄的嵌套。遍歷以前,無從知道到底有多少級目錄,因此咱們仍是要使用遞歸實現。
**代碼實現**:
```java
publicclassDiGuiDemo2 {
publicstaticvoidmain(String[] args) {
// 建立File對象
Filedir = newFile("D:\\aaa");
// 調用打印目錄方法
printDir(dir);
}
publicstaticvoidprintDir(Filedir) {
// 獲取子文件和目錄
File[] files = dir.listFiles();
// 循環打印
/*
判斷:
當是文件時,打印絕對路徑.
當是目錄時,繼續調用打印目錄的方法,造成遞歸調用.
*/
for (Filefile: files) {
// 判斷
if (file.isFile()) {
// 是文件,輸出文件絕對路徑
System.out.println("文件名:"+ file.getAbsolutePath());
} else {
// 是目錄,輸出目錄絕對路徑
System.out.println("目錄:"+file.getAbsolutePath());
// 繼續遍歷,調用printDir,造成遞歸
printDir(file);
}
}
}
}
```
搜索`D:\aaa` 目錄中的`.java` 文件。
**分析**:
1. 目錄搜索,沒法判斷多少級目錄,因此使用遞歸,遍歷全部目錄。
2. 遍歷目錄時,獲取的子文件,經過文件名稱,判斷是否符合條件。
**代碼實現**:
```java
publicclassDiGuiDemo3 {
publicstaticvoidmain(String[] args) {
// 建立File對象
Filedir = newFile("D:\\aaa");
// 調用打印目錄方法
printDir(dir);
}
publicstaticvoidprintDir(Filedir) {
// 獲取子文件和目錄
File[] files = dir.listFiles();
// 循環打印
for (Filefile: files) {
if (file.isFile()) {
// 是文件,判斷文件名並輸出文件絕對路徑
if (file.getName().endsWith(".java")) {
System.out.println("文件名:" + file.getAbsolutePath());
}
} else {
// 是目錄,繼續遍歷,造成遞歸
printDir(file);
}
}
}
}
```
`java.io.FileFilter`是一個接口,是File的過濾器。 該接口的對象能夠傳遞給File類的`listFiles(FileFilter)` 做爲參數, 接口中只有一個方法。
`boolean accept(File pathname) ` :測試pathname是否應該包含在當前File目錄中,符合則返回true。
**分析**:
1. 接口做爲參數,須要傳遞子類對象,重寫其中方法。咱們選擇匿名內部類方式,比較簡單。
2.`accept`方法,參數爲File,表示當前File下全部的子文件和子目錄。保留住則返回true,過濾掉則返回false。保留規則:
1. 要麼是.java文件。
2. 要麼是目錄,用於繼續遍歷。
3. 經過過濾器的做用,`listFiles(FileFilter)`返回的數組元素中,子文件對象都是符合條件的,能夠直接打印。
**代碼實現:**
```java
publicclassDiGuiDemo4 {
publicstaticvoidmain(String[] args) {
Filedir = newFile("D:\\aaa");
printDir2(dir);
}
publicstaticvoidprintDir2(Filedir) {
// 匿名內部類方式,建立過濾器子類對象
File[] files = dir.listFiles(newFileFilter() {
@Override
publicbooleanaccept(Filepathname) {
returnpathname.getName().endsWith(".java")||pathname.isDirectory();
}
});
// 循環打印
for (Filefile: files) {
if (file.isFile()) {
System.out.println("文件名:" + file.getAbsolutePath());
} else {
printDir2(file);
}
}
}
}
```
**分析:**`FileFilter`是隻有一個方法的接口,所以能夠用lambda表達式簡寫。
lambda格式:
```java
()->{ }
```
**代碼實現:**
```java
publicstaticvoidprintDir3(File dir) {
// lambda的改寫
File[] files = dir.listFiles(f ->{
returnf.getName().endsWith(".java") || f.isDirectory();
});
// 循環打印
for (Filefile: files) {
if (file.isFile()) {
System.out.println("文件名:" + file.getAbsolutePath());
} else {
printDir3(file);
}
}
}
```