第一章 多態(tài)
1.1 多態(tài)的形式
多態(tài)是繼封裝、繼承之后,面向對象的第三大特性。
多態(tài)是出現(xiàn)在繼承或者實現(xiàn)關系中的。
多態(tài)體現(xiàn)的格式:
父類類型 變量名 = new 子類/實現(xiàn)類構造器;
變量名.方法名();
多態(tài)的前提:有繼承關系,子類對象是可以賦值給父類類型的變量。例如Animal是一個動物類型,而Cat是一個貓類型。Cat繼承了Animal,Cat對象也是Animal類型,自然可以賦值給父類類型的變量。
1.2 多態(tài)的使用場景
如果沒有多態(tài),在下圖中register方法只能傳遞學生對象,其他的Teacher和administrator對象是無法傳遞給register方法方法的,在這種情況下,只能定義三個不同的register方法分別接收學生,老師和管理員。
有了多態(tài)之后,方法的形參就可以定義為共同的父類Person。
要注意的是:
- 當一個方法的形參是一個類,我們可以傳遞這個類所有的子類對象。
- 當一個方法的形參是一個接口,我們可以傳遞這個接口所有的實現(xiàn)類對象(后面會學)。
- 而且多態(tài)還可以根據(jù)傳遞的不同對象來調用不同類中的方法。
代碼示例:
父類:
public class Person {
private String name;
private int age;
空參構造
帶全部參數(shù)的構造
get和set方法
public void show(){
System.out.println(name + ", " + age);
}
}
子類1:
public class Administrator extends Person {
@Override
public void show() {
System.out.println("管理員的信息為:" + getName() + ", " + getAge());
}
}
子類2:
public class Student extends Person{
@Override
public void show() {
System.out.println("學生的信息為:" + getName() + ", " + getAge());
}
}
子類3:
public class Teacher extends Person{
@Override
public void show() {
System.out.println("老師的信息為:" + getName() + ", " + getAge());
}
}
測試類:
public class Test {
public static void main(String[] args) {
//創(chuàng)建三個對象,并調用register方法
Student s = new Student();
s.setName("張三");
s.setAge(18);
Teacher t = new Teacher();
t.setName("王建國");
t.setAge(30);
Administrator admin = new Administrator();
admin.setName("管理員");
admin.setAge(35);
register(s);
register(t);
register(admin);
}
//這個方法既能接收老師,又能接收學生,還能接收管理員
//只能把參數(shù)寫成這三個類型的父類
public static void register(Person p){
p.show();
}
}
1.3 多態(tài)的定義和前提
多態(tài): 是指同一行為,具有多個不同表現(xiàn)形式。
從上面案例可以看出,Cat和Dog都是動物,都是吃這一行為,但是出現(xiàn)的效果(表現(xiàn)形式)是不一樣的。
前提【重點】
-
有繼承或者實現(xiàn)關系
-
方法的重寫【意義體現(xiàn):不重寫,無意義】
-
父類引用指向子類對象【格式體現(xiàn)】
父類類型:指子類對象繼承的父類類型,或者實現(xiàn)的父接口類型。
1.4 多態(tài)的運行特點
調用成員變量時:編譯看左邊,運行看左邊
調用成員方法時:編譯看左邊,運行看右邊
代碼示例:
Fu f = new Zi();
//編譯看左邊的父類中有沒有name這個屬性,沒有就報錯
//在實際運行的時候,把父類name屬性的值打印出來
System.out.println(f.name);
//編譯看左邊的父類中有沒有show這個方法,沒有就報錯
//在實際運行的時候,運行的是子類中的show方法
f.show();
1.5 多態(tài)的弊端
我們已經(jīng)知道多態(tài)編譯階段是看左邊父類類型的,如果子類有些獨有的功能,此時多態(tài)的寫法就無法訪問子類獨有功能了。
class Animal{
public void eat(){
System.out.println("動物吃東西!")
}
}
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("吃骨頭");
}
}
class Test{
public static void main(String[] args){
Animal a = new Cat();
a.eat();
a.catchMouse();//編譯報錯,編譯看左邊,Animal沒有這個方法
}
}
1.6 引用類型轉換
1.6.1 為什么要轉型
多態(tài)的寫法就無法訪問子類獨有功能了。
當使用多態(tài)方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調用子類擁有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態(tài)給我們帶來的一點"小麻煩"。所以,想要調用子類特有的方法,必須做向下轉型。
回顧基本數(shù)據(jù)類型轉換
- 自動轉換: 范圍小的賦值給范圍大的.自動完成:double d = 5;
- 強制轉換: 范圍大的賦值給范圍小的,強制轉換:int i = (int)3.14
? 多態(tài)的轉型分為向上轉型(自動轉換)與向下轉型(強制轉換)兩種。
1.6.2 向上轉型(自動轉換)
-
向上轉型:多態(tài)本身是子類類型向父類類型向上轉換(自動轉換)的過程,這個過程是默認的。
當父類引用指向一個子類對象時,便是向上轉型。
使用格式:
父類類型 變量名 = new 子類類型();
如:Animal a = new Cat();
**原因是:父類類型相對與子類來說是大范圍的類型,Animal是動物類,是父類類型。Cat是貓類,是子類類型。Animal類型的范圍當然很大,包含一切動物。**所以子類范圍小可以直接自動轉型給父類類型的變量。
1.6.3 向下轉型(強制轉換)
-
向下轉型:父類類型向子類類型向下轉換的過程,這個過程是強制的。
一個已經(jīng)向上轉型的子類對象,將父類引用轉為子類引用,可以使用強制類型轉換的格式,便是向下轉型。
使用格式:
子類類型 變量名 = (子類類型) 父類變量名;
如:Aniaml a = new Cat();
Cat c =(Cat) a;
1.6.4 案例演示
當使用多態(tài)方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調用子類擁有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態(tài)給我們帶來的一點"小麻煩"。所以,想要調用子類特有的方法,必須做向下轉型。
轉型演示,代碼如下:
定義類:
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
}
}
1.6.5 轉型的異常
轉型的過程中,一不小心就會遇到這樣的問題,請看如下代碼:
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
,類型轉換異常!這是因為,明明創(chuàng)建了Cat類型對象,運行時,當然不能轉換成Dog對象的。
1.6.6 instanceof關鍵字
為了避免ClassCastException的發(fā)生,Java提供了 instanceof
關鍵字,給引用變量做類型的校驗,格式如下:
變量名 instanceof 數(shù)據(jù)類型
如果變量屬于該數(shù)據(jù)類型或者其子類類型,返回true。
如果變量不屬于該數(shù)據(jù)類型或者其子類類型,返回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
}
}
}
1.6.7 instanceof新特性
JDK14的時候提出了新特性,把判斷和強轉合并成了一行
//新特性
//先判斷a是否為Dog類型,如果是,則強轉成Dog類型,轉換之后變量名為d
//如果不是,則不強轉,結果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("沒有這個類型,無法轉換");
}
1.7 綜合練習
需求:根據(jù)需求完成代碼:
1.定義狗類
屬性:
年齡,顏色
行為:
eat(String something)(something表示吃的東西)
看家lookHome方法(無參數(shù))
2.定義貓類
屬性:
年齡,顏色
行為:
eat(String something)方法(something表示吃的東西)
逮老鼠catchMouse方法(無參數(shù))
3.定義Person類//飼養(yǎng)員
屬性:
姓名,年齡
行為:
keepPet(Dog dog,String something)方法
功能:喂養(yǎng)寵物狗,something表示喂養(yǎng)的東西
行為:
keepPet(Cat cat,String something)方法
功能:喂養(yǎng)寵物貓,something表示喂養(yǎng)的東西
生成空參有參構造,set和get方法
4.定義測試類(完成以下打印效果):
keepPet(Dog dog,String somethind)方法打印內容如下:
年齡為30歲的老王養(yǎng)了一只黑顏色的2歲的狗
2歲的黑顏色的狗兩只前腿死死的抱住骨頭猛吃
keepPet(Cat cat,String somethind)方法打印內容如下:
年齡為25歲的老李養(yǎng)了一只灰顏色的3歲的貓
3歲的灰顏色的貓瞇著眼睛側著頭吃魚
5.思考:
1.Dog和Cat都是Animal的子類,以上案例中針對不同的動物,定義了不同的keepPet方法,過于繁瑣,能否簡化,并體會簡化后的好處?
2.Dog和Cat雖然都是Animal的子類,但是都有其特有方法,能否想辦法在keepPet中調用特有方法?
畫圖分析:
代碼示例:
//動物類(父類)
public class Animal {
private int age;
private String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void eat(String something){
System.out.println("動物在吃" + something);
}
}
//貓類(子類)
public class Cat extends Animal {
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge() + "歲的" + getColor() + "顏色的貓瞇著眼睛側著頭吃" + something);
}
public void catchMouse(){
System.out.println("貓抓老鼠");
}
}
//狗類(子類)
public class Dog extends Animal {
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
//行為
//eat(String something)(something表示吃的東西)
//看家lookHome方法(無參數(shù))
@Override
public void eat(String something) {
System.out.println(getAge() + "歲的" + getColor() + "顏色的狗兩只前腿死死的抱住" + something + "猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
//飼養(yǎng)員類
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//飼養(yǎng)狗
/* public void keepPet(Dog dog, String something) {
System.out.println("年齡為" + age + "歲的" + name + "養(yǎng)了一只" + dog.getColor() + "顏色的" + dog.getAge() + "歲的狗");
dog.eat(something);
}
//飼養(yǎng)貓
public void keepPet(Cat cat, String something) {
System.out.println("年齡為" + age + "歲的" + name + "養(yǎng)了一只" + cat.getColor() + "顏色的" + cat.getAge() + "歲的貓");
cat.eat(something);
}*/
//想要一個方法,能接收所有的動物,包括貓,包括狗
//方法的形參:可以寫這些類的父類 Animal
public void keepPet(Animal a, String something) {
if(a instanceof Dog d){
System.out.println("年齡為" + age + "歲的" + name + "養(yǎng)了一只" + a.getColor() + "顏色的" + a.getAge() + "歲的狗");
d.eat(something);
}else if(a instanceof Cat c){
System.out.println("年齡為" + age + "歲的" + name + "養(yǎng)了一只" + c.getColor() + "顏色的" + c.getAge() + "歲的貓");
c.eat(something);
}else{
System.out.println("沒有這種動物");
}
}
}
//測試類
public class Test {
public static void main(String[] args) {
//創(chuàng)建對象并調用方法
/* Person p1 = new Person("老王",30);
Dog d = new Dog(2,"黑");
p1.keepPet(d,"骨頭");
Person p2 = new Person("老李",25);
Cat c = new Cat(3,"灰");
p2.keepPet(c,"魚");*/
//創(chuàng)建飼養(yǎng)員的對象
Person p = new Person("老王",30);
Dog d = new Dog(2,"黑");
Cat c = new Cat(3,"灰");
p.keepPet(d,"骨頭");
p.keepPet(c,"魚");
}
}
第二章 包
2.1 包
? 包在操作系統(tǒng)中其實就是一個文件夾。包是用來分門別類的管理技術,不同的技術類放在不同的包下,方便管理和維護。
在IDEA項目中,建包的操作如下:
包名的命名規(guī)范:
路徑名.路徑名.xxx.xxx
// 例如:com.itheima.oa
- 包名一般是公司域名的倒寫。例如:黑馬是www.itheima.com,包名就可以定義成com.itheima.技術名稱。
- 包名必須用”.“連接。
- 包名的每個路徑名必須是一個合法的標識符,而且不能是Java的關鍵字。
2.2 導包
什么時候需要導包?
? 情況一:在使用Java中提供的非核心包中的類時
? 情況二:使用自己寫的其他包中的類時
什么時候不需要導包?
? 情況一:在使用Java核心包(java.lang)中的類時
? 情況二:在使用自己寫的同一個包中的類時
2.3 使用不同包下的相同類怎么辦?
假設demo1和demo2中都有一個Student該如何使用?
代碼示例:
//使用全類名的形式即可。
//全類名:包名 + 類名
//拷貝全類名的快捷鍵:選中類名crtl + shift + alt + c 或者用鼠標點copy,再點擊copy Reference
com.itheima.homework.demo1.Student s1 = new com.itheima.homework.demo1.Student();
com.itheima.homework.demo2.Student s2 = new com.itheima.homework.demo2.Student();
第三章 權限修飾符
3.1 權限修飾符
? 在Java中提供了四種訪問權限,使用不同的訪問權限修飾符修飾時,被修飾的內容會有不同的訪問權限,我們之前已經(jīng)學習過了public 和 private,接下來我們研究一下protected和默認修飾符的作用。
-
public:公共的,所有地方都可以訪問。
-
protected:本類 ,本包,其他包中的子類都可以訪問。
-
默認(沒有修飾符):本類 ,本包可以訪問。
注意:默認是空著不寫,不是default
-
private:私有的,當前類可以訪問。
public > protected > 默認 > private
3.2 不同權限的訪問能力
public | protected | 默認 | private | |
---|---|---|---|---|
同一類中 | √ | √ | √ | √ |
同一包中的類 | √ | √ | √ | |
不同包的子類 | √ | √ | ||
不同包中的無關類 | √ |
可見,public具有最大權限。private則是最小權限。
編寫代碼時,如果沒有特殊的考慮,建議這樣使用權限:
- 成員變量使用
private
,隱藏細節(jié)。 - 構造方法使用
public
,方便創(chuàng)建對象。 - 成員方法使用
public
,方便調用方法。
小貼士:不加權限修飾符,就是默認權限
第四章 final關鍵字
4.1 概述
? 學習了繼承后,我們知道,子類可以在父類的基礎上改寫父類內容,比如,方法重寫。
如果有一個方法我不想別人去改寫里面內容,該怎么辦呢?
Java提供了final
關鍵字,表示修飾的內容不可變。
-
final: 不可改變,最終的含義。可以用于修飾類、方法和變量。
- 類:被修飾的類,不能被繼承。
- 方法:被修飾的方法,不能被重寫。
- 變量:被修飾的變量,有且僅能被賦值一次。
4.2 使用方式
4.2.1 修飾類
final修飾的類,不能被繼承。
格式如下:
final class 類名 {
}
代碼:
final class Fu {
}
// class Zi extends Fu {} // 報錯,不能繼承final的類
查詢API發(fā)現(xiàn)像 public final class String
、public final class Math
、public final class Scanner
等,很多我們學習過的類,都是被final修飾的,目的就是供我們使用,而不讓我們所以改變其內容。
4.2.2 修飾方法
final修飾的方法,不能被重寫。
格式如下:
修飾符 final 返回值類型 方法名(參數(shù)列表){
//方法體
}
代碼:
class Fu2 {
final public void show1() {
System.out.println("Fu2 show1");
}
public void show2() {
System.out.println("Fu2 show2");
}
}
class Zi2 extends Fu2 {
// @Override
// public void show1() {
// System.out.println("Zi2 show1");
// }
@Override
public void show2() {
System.out.println("Zi2 show2");
}
}
4.2.3 修飾變量-局部變量
-
局部變量——基本類型
基本類型的局部變量,被final修飾后,只能賦值一次,不能再更改。代碼如下:
public class FinalDemo1 {
public static void main(String[] args) {
// 聲明變量,使用final修飾
final int a;
// 第一次賦值
a = 10;
// 第二次賦值
a = 20; // 報錯,不可重新賦值
// 聲明變量,直接賦值,使用final修飾
final int b = 10;
// 第二次賦值
b = 20; // 報錯,不可重新賦值
}
}
思考,下面兩種寫法,哪種可以通過編譯?
寫法1:
final int c = 0;
for (int i = 0; i < 10; i++) {
c = i;
System.out.println(c);
}
寫法2:
for (int i = 0; i < 10; i++) {
final int c = i;
System.out.println(c);
}
根據(jù) final
的定義,寫法1報錯!寫法2,為什么通過編譯呢?因為每次循環(huán),都是一次新的變量c。這也是大家需要注意的地方。
4.2.4 修飾變量-成員變量
成員變量涉及到初始化的問題,初始化方式有顯示初始化和構造方法初始化,只能選擇其中一個:
- 顯示初始化(在定義成員變量的時候立馬賦值)(常用);
public class Student {
final int num = 10;
}
-
構造方法初始化(在構造方法中賦值一次)(不常用,了解即可)。
注意:每個構造方法中都要賦值一次!
public class Student {
final int num = 10;
final int num2;
public Student() {
this.num2 = 20;
// this.num2 = 20;
}
public Student(String name) {
this.num2 = 20;
// this.num2 = 20;
}
}
被final修飾的常量名稱,一般都有書寫規(guī)范,所有字母都大寫。文章來源:http://www.zghlxwxcb.cn/news/detail-652798.html
后記
????????美好的一天,到此結束,下次繼續(xù)努力!欲知后續(xù),請看下回分解,寫作不易,感謝大家的支持??! ??????文章來源地址http://www.zghlxwxcb.cn/news/detail-652798.html
到了這里,關于從零開始學習 Java:簡單易懂的入門指南之多態(tài)(十)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!