原文連接:https://foochane.cn/article/2...html
字節是計算機中最小存儲單元。計算機存儲任何的數據,都是以字節的形式存儲。8個bit(二進制位) 0000-0000表示爲1個字節,寫成1 byte或者1 B。java
虛擬機是一種抽象化的計算機,經過在實際的計算機上仿真模擬各類計算機功能來實現的。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都被甲骨文公司收入旗下,大有整合的趨勢。segmentfault
任何軟件的運行,都必需要運行在操做系統之上,而咱們用Java編寫的軟件能夠運行在任何的操做系
統上,這個特性稱爲Java語言的跨平臺特性。該特性是由JVM實現的,咱們編寫的程序運行在JVM上,而JVM運行在操做系統上。數組
咱們想要運行一個已有的Java程序,那麼只需安裝 JRE 便可。咱們想要開發一個全新的Java程序,那麼必須安裝 JDK 。安全
常量分類:架構
類型 | 含義 | 舉例 |
---|---|---|
整數常量 | 全部的整數 | 0,1, 567, -9 |
小數常量 | 全部的小數 | 0.0, -0.1, 2.55 |
字符常量 | 單引號引發來,只能寫一個字符,必須有內容 | 'a' , ' ', '好' |
字符串常量 | 雙引號引發來,能夠寫多個字符,也能夠不寫 | "A" ,"Hello" ,"你好" ,"" |
布爾常量 | 只有兩個值 | true , false |
空常量 | 只有一個值 | null |
變量分類:編程語言
Java的數據類型分爲兩大類:ide
數據類型 | 關鍵字 | 佔用內存 | 取值範圍 |
---|---|---|---|
字節型 | 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
表示
自動類型轉換(隱式)函數
強制類型轉換(顯式)
注意事項:
byte/short/char
這三種類型均可以發生數學運算,例如加法「+
」.byte/short/char
這三種類型在運算的時候,都會被首先提高成爲int類型,而後再計算。boolean
類型不能發生數據類型轉換符號 | 說明 |
---|---|
+ | 加法運算,字符串鏈接運算 |
- | 減法運算 |
* | 乘法運算 |
/ | 除法運算 |
% | 取模運算,兩個數字相除取餘數 |
++ 、 -- | 自增自減運算 |
前++和後++的區別
public static void main(String[] args) { int a = 1; int b = ++a; System.out.println(a);//計算結果是2 System.out.println(b);//計算結果是2 }
public static void main(String[] args) { int a = 1; int b = 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 |
三元運算符格式:
數據類型 變量名 = 布爾類型表達式?結果1:結果2
示例:
public static void main(String[] args) { int i = (1==2 ? 100 : 200); System.out.println(i);//200 int j = (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循環變量數組 |
語句格式:
if (判斷條件1) { 執行語句1; } else if (判斷條件2) { 執行語句2; } ... }else if (判斷條件n) { 執行語句n; } else { 執行語句n+1; }
語句格式:
switch(表達式) { case 常量值1: 語句體1; break; case 常量值2: 語句體2; break; ... default: 語句體n+1; break; }
語句格式:
for(初始化表達式①; 布爾表達式②; 步進表達式④){ 循環體③ }
執行流程
執行順序:①②③④ >②③④>②③④…②不知足爲止。
①負責完成循環變量初始化
②負責判斷是否知足循環條件,不知足則跳出循環
③具體執行的語句
④循環後,循環條件所涉及變量的變化狀況
語句格式1:
初始化表達式① while(布爾表達式②){ 循環體③ 步進表達式④ }
執行流程
執行順序:①②③④ >②③④>②③④…②不知足爲止。
①負責完成循環變量初始化。
②負責判斷是否知足循環條件,不知足則跳出循環。
③具體執行的語句。
④循環後,循環變量的變化狀況。
語句格式2:
初始化表達式① do{ 循環體③ 步進表達式④ }while(布爾表達式②);
執行流程
執行順序:①③④ >②③④>②③④…②不知足爲止。
①負責完成循環變量初始化。
②負責判斷是否知足循環條件,不知足則跳出循環。
③具體執行的語句
④循環後,循環變量的變化狀況
使用場景:終止 switch或者循環
示例:
public static void main(String[] args) { for (int i = 1; i<=10; i++) { //需求:打印完兩次HelloWorld以後結束循環 if(i == 3){ break; } System.out.println("HelloWorld"+i); } }
使用場景:結束本次循環,繼續下一次的循環
示例:
public static void main(String[] args) { for (int i = 1; i <= 10; i++) { //需求:不打印第三次HelloWorld if(i == 3){ continue; } System.out.println("HelloWorld"+i); } }
容器: 是將多個數據存儲到一塊兒,每一個數據稱爲該容器的元素。
數組概念: 數組就是存儲數據長度固定的容器,保證多個數據的數據類型要一致。
格式:
數組存儲的數據類型[] 數組名字 = new 數組存儲的數據類型[長度];
示例:
int[] arr = new int[3];
格式:
數據類型[] 數組名 = new 數據類型[]{元素1,元素2,元素3...};
示例:
int[] arr = new int[]{1,2,3,4,5};
格式:
數據類型[] 數組名 = {元素1,元素2,元素3...};
示例:
int[] arr = {1,2,3,4,5};
【注意】:
方式三
一樣也進行了new
操做數組名 .length
,屬性length的執行結果是數組的長度,int類型結果。由次能夠推斷出,數組的最大索引值爲 數組名 .length-1
。數組名[索引]
示例:
public static void main(String[] args) { //定義存儲int類型數組,賦值元素1,2,3,4,5 int[] arr = {1,2,3,4,5}; //爲0索引元素賦值爲6 arr[0] = 6; //獲取數組0索引上的元素 int i = arr[0]; System.out.println(i); //直接輸出數組0索引元素 System.out.println(arr[0]); }
代碼以下:
public static void main(String[] args) { int[] arr = { 5, 15, 2000, 10000, 100, 4000 }; //定義變量,保存數組中0索引的元素 int max = arr[0]; //遍歷數組,取出每一個元素 for (int i = 0; i < arr.length; i++) { //遍歷到的元素和變量max比較 //若是數組元素大於max if (arr[i] > max) { //max記錄住大值 max = arr[i]; } } System.out.println("數組最大值是: " + max); }
代碼以下:
public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; /* 循環中定義變量min=0最小索引 max=arr.length‐1最大索引 min++,max‐‐ */ for (int min = 0, max = arr.length ‐ 1; min <= max; min++, max‐‐) { //利用第三方變量完成數組中的元素交換 int temp = arr[min]; arr[min] = arr[max]; arr[max] = temp; } // 反轉後,遍歷數組 for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } }
內存是計算機中的重要原件,臨時存儲區域,做用是運行程序。咱們編寫的程序是存放在硬盤中的,在硬盤中的程序是不會運行的,必須放進內存中才能運行,運行完畢後會清空內存。
Java虛擬機要運行程序,必需要對內存進行空間的分配和管理。
Java的內存須要劃分紅爲5個部分:
棧(Stack) :存放的都是方法中的局部變量。方法的運行必定要在棧當中運行。
堆(Heap):凡是new出來的東西,都在堆當中。
堆內存裏面的數據,都有默認值。規則:
0
0.0
'\u0000'
false
null
示例:
public static void main(String[] args) { // 定義數組,存儲3個元素 int[] arr = new int[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]); }
代碼執行流程:
兩個變量指向同一個數據:
public static void main(String[] args) { // 定義數組,存儲3個元素 int[] arr = new int[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語言去設計、開發計算機程序。 這裏的對象泛指現實中一切事物,每種事物都具有本身的屬性和行爲。面向對象思想就是在計算機程序設計過程當中,參照現實中事物,將事物的屬性特徵、行爲特徵抽象出來,描述成計算機事件的設計思想。 它區別於面向過程思想,強調的是經過調用對象的行爲來實現功能,而不是本身一步一步的去操做實現。
面向對象思想是一種更符合咱們思考習慣的思想,它能夠將複雜的事情簡單化,並將咱們從執行者變成了指揮者。面向對象的語言中,包含了三大基本特徵,即封裝、繼承和多態。
類 :是一組相關屬性和行爲的集合。能夠當作是一類事物的模板,使用事物的屬性特徵和行爲特徵來描述該
類事物。
類與對象的關係
定義格式:
public class ClassName { //成員變量 //成員方法 }
示例:
public class Student { //成員變量 String name;//姓名 int age;//年齡 //成員方法 //學習的方法 public void study() { System.out.println("好好學習,每天向上"); } //吃飯的方法 publicvoid eat() { System.out.println("學習餓了要吃飯"); } }
對象的使用格式
建立對象:
類名 對象名 = new 類名();
使用對象訪問類中的成員:
對象名.成員變量; 對象名.成員方法();
示例:
public class Test01_Student { public static void main(String[] args) { //建立對象格式:類名 對象名 = new 類名(); Student s = new Student(); 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(); // "學習餓了要吃飯" } }
成員變量的默認值
基本類型:
0
0.0
'\u0000'
布爾(boolean):false
null
public class Car{ String color; //成員變量 public void drive(){ int speed = 80; //局部變量 //...... } }
在類中的位置不一樣
做用範圍不同
初始化值的不一樣
存中的位置不一樣
生命週期不一樣
面向對象編程語言是對客觀世界的模擬,客觀世界裏成員變量都是隱藏在對象內部的,外界沒法直接操做和修改。封裝能夠被認爲是一個保護屏障,防止該類的代碼和數據被其餘類隨意訪問。要訪問該類的數據,必須經過指定的方式。適當的封裝可讓代碼更容易理解與維護,也增強了代碼的安全性。
封裝的步驟
private的含義
private
是一個權限修飾符,表明最小權限。private
修飾後的成員變量和成員方法,只在本類中才能訪問。private的使用格式:
private 數據類型 變量名;
public class Student { private String name; private int age; }
public class Student { private String name; private int age; public void setName(String n) { name = n; } public String getName() { return name; } public void setAge(int a) { age = a; } public int getAge() { return age; } }
this的含義this表明所在類的當前對象的引用(地址值),即對象本身的引用。
方法被哪一個對象調用,方法中的this
就表明那個對象。即誰在調用,this就表明誰。
this使用格式:
this.成員變量名;
使用 this 修飾方法中的變量,解決成員變量被隱藏的問題,代碼以下:
因爲形參變量名與成員變量名重名,致使成員變量名被隱藏,方法中的變量名,沒法訪問到成員變量,從而賦值失敗。因此,咱們只能使用this關鍵字,來解決這個重名問題。
public class Student { private String name; private int age; public void setName(String name) { //name = name; this.name = name; } public String getName() { return name; } public void setAge(int age) { //age = age; this.age = age; } public int getAge() { return age; }
當一個對象被建立時候,構造方法用來初始化該對象,給對象的成員變量賦初始值。
不管你與否自定義構造方法,全部的類都有構造方法,由於Java自動提供了一個無參數構造方法,一旦本身定義了構造方法,Java自動提供的默認無參數構造方法就會失效。
構造方法的定義格式:
修飾符 構造方法名(參數列表){ // 方法體 }
構造方法的寫法上,方法名與它所在的類名相同。它沒有返回值,因此不須要返回值類型,甚至不須要void。使用構造方法後,代碼以下:
public class Student { private String name; private int age; // 無參數構造方法 public Student() {} // 有參數構造方法 public Student(String name,int age) { this.name = name; this.age = age; } }
注意事項
JavaBean 是 Java語言編寫類的一種標準規範。符合 JavaBean 的類,要求類必須是具體的和公共的,而且具備無參數的構造方法,提供用來操做成員變量的 set 和 get 方法。
public class ClassName{ //成員變量 //構造方法 //無參構造方法【必須】 //有參構造方法【建議】 //成員方法 //getXxx() //setXxx() }
編寫符合 JavaBean 規範的類,以學生類爲例,標準代碼以下:
public class Student { //成員變量 private String name; private int age; //構造方法 public Student() {} public Student(String name,int age) { this.name = name; this.age = age; } //成員方法 publicvoid setName(String name) { this.name = name; } public String getName() { return name; } publicvoid setAge(int age) { this.age = age; } publicint getAge() { return age; } }
多個類中存在相同屬性和行爲時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行爲,只要繼承那一個類便可。
其中,多個類能夠稱爲子類,單獨那一個類稱爲父類、超類(superclass)或者基類。
父類更通用,子類更具體。咱們經過繼承,可使多種事物之間造成一種關係體系。
繼承 :就是子類繼承父類的屬性和行爲,使得子類對象具備與父類相同的屬性、相同的行爲。子類能夠直接訪問父類中的非私有的屬性和行爲。
好處:
格式:
經過 extends
關鍵字,能夠聲明一個子類繼承另一個父類,定義格式以下:
class 父類 { ... } class 子類 extends 父類 { ... }
示例:
/* * 定義員工類Employee,作爲父類 */ class Employee { String name; // 定義name屬性 // 定義員工的工做方法 public void work() { System.out.println("全力以赴地工做"); } } /* * 定義講師類Teacher 繼承 員工類Employee */ class Teacher extends Employee { // 定義一個打印name的方法 public void printName() { System.out.println("name=" + name); } } /* * 定義測試類 */ public class ExtendDemo01 { public static void main(String[] args) { // 建立一個講師類對象 Teacher t = new Teacher(); // 爲該員工類的name屬性進行賦值 t.name = "小明"; // 調用該員工的printName()方法 t.printName(); // name = 小明 // 調用Teacher類繼承來的work()方法 t.work(); // 全力以赴地工做 } }
成員變量不重名
成員變量重名
super
關鍵字,修飾父類成員變量,相似於以前學過的 this
。成員方法不重名
成員方法重名 ——重寫(Override)
class Fu { private int n; Fu(){ System.out.println("Fu()"); } } class Zi extends Fu { Zi(){ // super(),調用父類構造方法 super(); System.out.println("Zi()"); } } public class ExtendsDemo07{ public static void main (String args[]){ Zi zi = new Zi(); } } 輸出結果: Fu() Zi()
Java只支持單繼承,不支持多繼承。
方法重寫 :子類中出現與父類如出一轍的方法時(返回值類型,方法名和參數列表都相同),會出現覆蓋效果,也稱爲重寫或者複寫。聲明不變,從新實現。
示例:
子類能夠根據須要,定義特定於本身的行爲。既沿襲了父類的功能名稱,又根據子類的須要從新實現父類方法,從而進行擴展加強。好比新的手機增長來電顯示頭像的功能,代碼以下:
class Phone { public void sendMessage(){ System.out.println("發短信"); } public void call(){ System.out.println("打電話"); } public void showNum(){ System.out.println("來電顯示號碼"); } } //智能手機類 class NewPhone extends Phone { //重寫父類的來電顯示號碼功能,並增長本身的顯示姓名和圖片功能 public void showNum(){ //調用父類已經存在的功能使用super super.showNum(); //增長本身特有顯示姓名和圖片功能 System.out.println("顯示來電姓名"); System.out.println("顯示頭像"); } } public class ExtendsDemo06 { public static void main(String[] args) { // 建立子類對象 NewPhone np = new NewPhone(); // 調用父類繼承而來的方法 np.call(); // 調用子類重寫的方法 np.showNum(); } }
注意事項
父類空間優先於子類對象產生
在每次建立子類對象時,先初始化父類空間,再建立其子類對象自己。目的在於子類對象中包含了其對應的父類空間,即可以包含其父類的成員,若是父類成員非private修飾,則子類能夠隨意使用父類成員。代碼體如今子類的構造方法調用時,必定先調用父類的構造方法。
super和this的含義
super和this的用法
this.成員變量 ‐‐ 本類的 super.成員變量 ‐‐ 父類的 this.成員方法名() ‐‐ 本類的 super.成員方法名() ‐‐ 父類的
示例:
class Animal { public void eat() { System.out.println("animal : eat"); } } class Cat extends Animal { public void eat() { System.out.println("cat : eat"); } public void eatTest() { this.eat(); // this 調用本類的方法 super.eat(); // super 調用父類的方法 } } public class ExtendsDemo { public static void main(String[] args) { Animal a = new Animal(); a.eat(); Cat c = new Cat(); c.eatTest(); } } 輸出結果爲: animal : eat cat : eat animal : eat
this(...) ‐‐ 本類的構造方法 super(...) ‐‐ 父類的構造方法
父類中的方法,被它的子類們重寫,子類各自的實現都不盡相同。那麼父類的方法聲明和方法主體,只有聲明還有意義,而方法主體則沒有存在的意義了。咱們把沒有方法主體的方法稱爲抽象方法。Java語法規定,包含抽象方法的類就是抽象類。
使用 abstract 關鍵字修飾方法,該方法就成了抽象方法,抽象方法只包含一個方法名,而沒有方法體
格式:
修飾符 abstract 返回值類型 方法名 (參數列表);
示例:
public abstract void run();
格式:
abstract class 類名字 { }
示例:
public abstract class Animal { public abstract void run(); }
抽象的使用:
繼承抽象類的子類必須重寫父類全部的抽象方法。不然,該子類也必須聲明爲抽象類。最終,必須有子類實現該父類的抽象方法,不然,從最初的父類到最終的子類都不能建立對象,失去意義。
public class Cat extends Animal { public void run (){ System.out.println("小貓在牆頭走~~~"); } } public class CatTest { public static void main(String[] args) { // 建立子類對象 Cat c = new Cat(); // 調用run方法 c.run(); } } 輸出結果: 小貓在牆頭走~~~
此時的方法重寫,是子類對父類抽象方法的完成實現,咱們將這種方法重寫的操做,也叫作實現方法。
多態 : 是指同一行爲,具備多個不一樣表現形式。
多態體現的格式:
父類類型 變量名 = new 子類對象; //父類類型:指子類對象繼承的父類類型,或者實現的父接口類型。 變量名.方法名();
當使用多態方式調用方法時,首先檢查父類中是否有該方法,若是沒有,則編譯錯誤;若是有,執行的是子類重寫後方法。
定義父類:
public abstract class Animal { public abstract void eat(); }
定義子類:
class Cat extends Animal { public void eat() { System.out.println("吃魚"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨頭"); } }
定義測試類:
public class Test { public static void main(String[] args) { // 多態形式,建立對象 Animal a1 = new Cat(); // 調用的是 Cat 的 eat a1.eat(); // 多態形式,建立對象 Animal a2 = new Dog(); // 調用的是 Dog 的 eat a2.eat(); } }
實際開發的過程當中,父類類型做爲方法形式參數,傳遞子類對象給方法,進行方法的調用,更能體現出多態的擴展性與便利。
定義父類:
public abstract class Animal { public abstract void eat(); }
定義子類:
class Cat extends Animal { public void eat() { System.out.println("吃魚"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨頭"); } }
定義測試類:
public class Test { public static void main(String[] args) { // 多態形式,建立對象 Cat c = new Cat(); Dog d = new Dog(); // 調用showCatEat showCatEat(c); // 調用showDogEat showDogEat(d); /* 以上兩個方法, 都可以被showAnimalEat(Animal a)方法所替代 而執行效果一致 */ showAnimalEat(c); showAnimalEat(d); } public static void showCatEat (Cat c){ c.eat(); } public static void showDogEat (Dog d){ d.eat(); } public static void showAnimalEat (Animal a){ a.eat(); } }
因爲多態特性的支持, showAnimalEat方法的Animal類型,是Cat和Dog的父類類型,父類類型接收子類對象,固然能夠把Cat對象和Dog對象,傳遞給方法。
當eat方法執行時,多態規定,執行的是子類重寫的方法,那麼效果天然與showCatEat、showDogEat方法一致,因此showAnimalEat徹底能夠替代以上兩方法。
不只僅是替代,在擴展性方面,不管以後再多的子類出現,咱們都不須要編寫showXxxEat方法了,直接使用showAnimalEat均可以完成。
因此,多態的好處,體如今,可使程序編寫的更簡單,並有良好的擴展。
多態的轉型分爲向上轉型與向下轉型兩種
向上轉型 :多態自己是子類類型向父類類型向上轉換的過程,這個過程是默認的。當父類引用指向一個子類對象時,即是向上轉型。
使用格式:
父類類型 變量名 = new 子類類型(); 如:Animal a = new Cat();
向下轉型 :父類類型向子類類型向下轉換的過程,這個過程是強制的。一個已經向上轉型的子類對象,將父類引用轉爲子類引用,可使用強制類型轉換的格式,即是向下轉型。
使用格式:
子類類型 變量名 = (子類類型) 父類變量名; 如:Cat c =(Cat) a;
當使用多態方式調用方法時,首先檢查父類中是否有該方法,若是沒有,則編譯錯誤。也就是說,不能調用子類擁有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態給咱們帶來的一點"小麻煩"。因此,想要調用子類特有的方法,必須作向下轉型。
轉型演示,代碼以下:
定義類:
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃魚"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨頭"); } public void watchHouse() { System.out.println("看家"); } }
定義測試類:
public class Test { public static void main(String[] args) { // 向上轉型 Animal a = new Cat(); a.eat(); // 調用的是 Cat 的 eat // 向下轉型 Cat c = (Cat)a; c.catchMouse(); // 調用的是 Cat 的 catchMouse } }
轉型的過程當中,一不當心就會遇到這樣的問題,請看以下代碼:
public class Test { public static void main(String[] args) { // 向上轉型 Animal a = new Cat(); a.eat(); // 調用的是 Cat 的 eat // 向下轉型 Dog d = (Dog)a; d.watchHouse(); // 調用的是 Dog 的 watchHouse 【運行報錯】 } }
這段代碼能夠經過編譯,可是運行時,卻報出了 ClassCastException
,類型轉換異常!這是由於,明明建立了Cat類型對象,運行時,固然不能轉換成Dog對象的。這兩個類型並無任何繼承關係,不符合類型轉換的定義。
爲了不ClassCastException
的發生,Java提供了 instanceof
關鍵字,給引用變量作類型的校驗,格式以下:
變量名 instanceof 數據類型 若是變量屬於該數據類型,返回true。 若是變量不屬於該數據類型,返回false。
因此,轉換前,咱們最好先作一個判斷,代碼以下:
public class Test { public static void main(String[] args) { // 向上轉型 Animal a = new Cat(); a.eat(); // 調用的是 Cat 的 eat // 向下轉型 if (a instanceof Cat){ Cat c = (Cat)a; c.catchMouse(); // 調用的是 Cat 的 catchMouse } else if (a instanceof Dog){ Dog d = (Dog)a; d.watchHouse(); // 調用的是 Dog 的 watchHouse } } }