面向物件的特徵(封裝、繼承、多型、抽象)
面向物件(思想)
許可權修飾:
- public: 公開的,所有不同的包,不同的類全都可以訪問得到。
- private: 私有的,只有自己的類(同一個class)中可以訪問,其他的都訪問不到。
- protected: 受保護的,除了不同包的其他類訪問不到,其他都可以訪問到,不同包的子類也可以 訪問的到。
- 預設:只有同一個包中的類可以訪問到,不同包訪問不到。
封裝:
-
實際含義是:該隱藏的隱藏,該暴露的暴露
-
全域性變數:定義在類中的,所有方法都可以訪問到,叫全域性變數
-
區域性變數:定義在一個方法裡的,只有本方法可以訪問的叫區域性變數
定義變數:
許可權修飾符(public) + 型別(void、int、string、array[]...) + 變數名稱
注:變數名稱要用合法的JAVA識別符號,首單詞的第一個字母小寫,後面的每一個單詞首字母大寫
可變引數:
public static void test(int...a){
for(int i=0;i<a.length;i++){
System.out.println(a[i]);
}
}
-
當不確定引數內要傳多少個時,用int... 在呼叫test方法時,可以任意傳幾個引數,同時會把a當作陣列進行處理
-
注:
-
如果一個引數裡有可變引數,並且有多個引數,那麼可變引數一定要放在最後一個
-
public static void test(int b,int...a){ for(int i=0;i<a.length;i++){ System.out.println(a[i]); } }
-
方法封裝:
- -定義格式: 修飾符(private) + 返回值型別(void、int、string....) + 方法名(自定義)+
( 引數型別 引數名稱 + , + 引數型別 引數名稱) + { 程式碼體 + return 表示式 }
-
形式引數:在引數名稱中,方法本身起的名字叫形式引數,只起一個代號的作用。
-
實際引數:在通過 . 呼叫方法時,依據形式引數傳進的東西是實際引數。
-
不需要new物件就可以訪問的方法封裝:
public static Person getPerson(){
return new Person();
}
-
方法封裝的好處:1.方便維護 2.減少冗餘程式碼的出現
構造方法:
-
作用:用於該類的例項物件的初始化
-
格式: public Person(){ }
-
示例: pubilc int size;
pubilec Person(){
size=18;
}
- 這就是給物件做了初始化,只要在另一個類中呼叫了Person的方法,new出了Person,那麼當輸出size時,預設輸出18.
- 構造方法中也可以傳引數,當傳入了引數後,再呼叫此方法是就必須傳進引數對應的型別,如果想既可以傳引數,也可以不傳引數,那麼就再構造一個同名的方法,該方法不加任何引數,這就叫構造方法的過載
This關鍵字
-
this關鍵字總是指向該方法的物件。
- 當this出現在構造方法中,this指的是該構造器正在初始化的物件,(也就是最外層的)
- 在普通方法中引用,this指的是呼叫該方法的物件
-
作用:this關鍵字最大的作用就是讓類中一個方法,訪問該類裡的另一個方法或Field
final關鍵字
-
final關鍵字可以修飾類,可以修飾變數,可以修飾方法:
-
修飾類,類不能被繼承
-
修飾變數,變數就變成了常量,只能被賦值一次 當修飾的變數是一個物件時,該物件不能改變(即不能將變數更改為另一個物件),但是物件中的內容可以改變
-
如:final StringBuffer a=new StringBuffer("abc");
執行 a=new StringBuffer(""); 會報錯,因為又重新New了一個物件
但是執行 a.append("123"); 不會報錯,因為物件沒有改變,物件的內容發生變化是不影響的
-
-
修飾方法,方法不能被重寫
-
通過set方法對物件進行初始化:
程式碼: public void setSex(String sex){
this.sex=sex;
}
小結:
- 變數—————— 類對應的屬性
- 構造方法——————new物件的時候對物件初始化
- 方法(函式)——————功能性的方法
封裝打包學生類的程式碼:
Demo demo=new Demo();
Student student=new Student("tom",18,6,"3333","男");
demo.save(student);
public void save(Student student){
System.out.println(student.getName());
System.out.println(student.getAge());
}
匿名物件
如上:Demo demo=new Demo();
demo.save(new Student());
- new Student只用到一次,沒有給他起名字,這就是匿名物件
值傳遞
-
JAVA的引數傳遞方式只有一種,那就是值傳遞
-
值傳遞含義:就是將實際引數的複製品傳進方法,也就是重新建立一個一模一樣的引數,對這個複製品進行方法操作,而實際引數的本身並沒有受到影響。
如:int a=5,b=5;
public void change(int a, int b){
a=100; b=100;
}
public void change(int a, int b){ a=100; b=100; } public static void main(){ int a=10; change(a); System.out.println(a); } //列印後a的值依舊是10,呼叫方法並沒有改變,因為傳進去的是基本型別的副本,只有引用型別才會傳進地址,最後的值會被改變,如果用change方法改變a的值的話,需要在後面return
傳進的是引用型別時:
-
public void change(StringBuffer buffer){ buffer.append("123"); } public static void main(){ StringBuffer buffer=new StringBuffer("ABC"); change(buffer); System.out.println(buffer); } //此時列印後結果為ABC123,因為傳進去的是地址,原本的值會被修改
在此次方法操作中,a和b的值依舊是5,並沒有變成100,就是對a與b的複製品進行了操作,實際引數本身沒有改變
如果想要將ab變成100的話,就需要返回a與b,在後面加return ,並將void改為Int
-
-
只有基本型別的值傳遞才是將複製品傳進方法,其他型別的值傳遞是傳進地址值的複製品,所以是可以對實際引數進行修改的
過載
-
方法名不可以重名,但是方法名相同時,引數列表不同時,如:方法一有兩個引數,方法二有三個引數,儘管兩個方法名字相同,但是可以同時存在,這叫方法的過載
-
過載只與方法名和引數名稱有關,與許可權修飾符,返回值型別等無關
-
方法名相同,引數名稱不同或引數數量不同時,才叫過載
遞迴
-
含義:自己呼叫自己
-
斐波那契數列:
-
1 1 2 3 5 8 13 21 34
-
假設要求第n位的值:
public int fb(int n){
if(n1 || n2){
return 1;
}else{
return fb(n-1)+fb(n-2);
}
}
-
-
遞迴最重要的是找到第一個遞迴的出口
Static
-
特點:
-
static是跟隨類的出現而出現,類名出現一次,那麼static後面的內容就會執行一次
-
被類的所有物件(new一次就是一個物件)共享:在呼叫static修飾的變數時,不同物件呼叫的是同一個變數,物件一對變數b進行改變時,物件二中的變數b也會改變而不同的物件呼叫不被static修飾的變數時,不同的物件使用的是不同的變數,物件一修改變數a時,物件二的變數a是不會被改變的
-
例如:
-
static int a=1; public static void main(){ Num num1=new Num(); Num num2=new Num(); num1.a=2; System.out.println(num2.a) } //此時列印的num2的a,它的值為2,因為兩個物件共用的一個變數,num1做了修改,num2也會有改變
-
-
可以通過類名直接呼叫
-
-
注意事項
- 在靜態方法中不可以使用this關鍵字 因為this關鍵字總是指向該方法的物件,靜態方法是隨著類的出現而出現,不需要New出物件,所以在靜態方法中,沒有物件是無法使用this的
- 靜態方法裡不能訪問成員變數或成員方法 因為靜態方法是隨著類的出現而出現,成員變數需要先New出物件,才能訪問得到
-
靜態變數和成員變數的區別
- 所屬不同
- 靜態變數屬於類,所以也稱為為類變數
成員變數屬於物件,所以也稱為例項變數(物件變數) - 記憶體中位置不同
靜態變數儲存於方法區的靜態區
成員變數儲存於堆記憶體 - 記憶體出現時間不同
靜態變數隨著類的載入而載入,隨著類的消失而消失
成員變數隨著物件的建立而存在,隨著物件的消失而消失 - 呼叫不同
靜態變數可以通過類名呼叫,也可以通過物件呼叫
- 靜態變數屬於類,所以也稱為為類變數
- 所屬不同
-
用武之地
- 使用工具方法時,可以加static,因為每次使用工具都需要New出物件,過於麻煩,而加上static後,只需要使用類的名字直接用點就可以呼叫,不需要new物件
- 只需載入或執行一次
程式碼塊:
-
區域性程式碼塊:
- 出現在方法的內部,用大括號括起來的就是區域性程式碼塊
- 定義在區域性程式碼塊內的變數,出了區域性程式碼塊後,外面的訪問不到
- 限定變數生命週期,及早釋放,提高記憶體利用率
-
構造程式碼塊
- 在類中方法外出現;多個構造方法中相同的程式碼存放到一起,每次呼叫構造都執行時就可以用構造程式碼塊,並且在構造方法前執行
- 運用:當所有的構造方法中都有相同的部分時,可以把他放進構造程式碼塊裡,這樣可以減少構造方法中相同的部分出現
-
靜態程式碼塊
- 在構造程式碼塊前加上static,構造程式碼塊是New一次,程式碼塊的內容出現一次,靜態程式碼塊是類出現一次,就執行一次
繼承:
概述:
-
多個類存在相同的屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為(也叫方法),只要繼承這個類就可以了,這個類就是父類
-
父類與子類:
- 父類又叫超類或者基類
- 子類又叫派生類
-
通過extends關鍵字實現繼承
- class 子類名 extends 父類名 {}
繼承的好處:
-
提高了程式碼的複用性
- 多個類相同的成員可以放到同一個類中
- 如:老師和學生同屬於人,那麼人這個類中定義的姓名,年齡,身高體重這些屬性老師和學生都可以使用
-
提高了程式碼的維護性
- 如果功能的程式碼需要修改,只需修改父類的程式碼即可
-
讓類與類之間產生了關係,是多型的前提
- 其實這也是繼承的弊端,類的耦合性很強
- 耦合性:在A類中new一個B類,一旦B類出現問題,那麼A類中使用的B類也不能用,或者想要將B類換成C類,要將A類中所有B類的部分全都替換
特點:
-
JAVA只支援單繼承,不支援多繼承
- 一個類只能有一個父類
-
JAVA支援多層繼承(爺爺類)
- A繼承B B繼承C
注意事項:
-
子類只能繼承父類非私有的成員(成員方法和成員變數)
- 這也是繼承的另一個弊端:打破了封裝性
-
子類不能繼承父類的構造方法,但是可以通過super關鍵字去訪問父類構造方法
-
要訪問父類的構造就只能通過子類的構造方法去訪問
-
//父類的空參構造: public Pet(){ } //通過子類訪問父類構造: public Dog(){ super(); } //注:儘管在子類的構造方法中沒有寫super(); 但是已經預設寫上了父類的空參構造 //父類的有參構造: public name; publci Pet(String name){ this.name=name; } //通過子類訪問父類的有參構造: public Dog(String name){ super(name); }
-
-
子類中所有的構造方法預設都會訪問父類空參的構造方法
- 因為要實現子類的初始化,首先要實現父類的初始化,子類使用的是父類的變數,父類如果沒有構造,子類不能使用
-
不能通過 子類=new 父類
-
可以通過 父類=new 子類
-
子類中訪問變數的順序
- 首先在子類區域性範圍找
- 然後在子類成員範圍找
- 最後在父類成員範圍找 不能訪問父類區域性範圍
- 最後還沒有就會報錯 是不會找爺爺類的
super關鍵字
-
super的用法和this很像
- this代表本類物件的引用
- super代表父類物件的引用
-
super用法:
- super.變數名 (父類的變數) super.name;
- super.方法名 (父類的方法) super.eat();
重寫(Override):
-
子類中出現了和父類一模一樣的方法宣告,也被稱為方法覆蓋,方法複寫
-
規則:
-
方法名相同,形參列表相同
-
子類方法返回值型別應該比父類方法返回值相等或者更小。
- 返回值型別比父類的要小,這兩個返回值一定要有父子關係才行
- 基本型別之間沒有父子關係
-
子類方法宣告丟擲的異常應該比父類方法更小或者相等
-
子類許可權比父類許可權大或者相等
-
-
補充:
- 子類重寫了父類的方法後,子類物件無法訪問父類中被覆蓋的方法,但可以在子類方法中呼叫父類被重寫的方法,需要使用super關鍵字
- 如果父類的方法啊具有private許可權修飾符,則該方法是對子類隱藏的,也就是無法重寫該方法
- 如果子類中定義了一個與父類private方法 相同的方法名、形參列表、返回值型別,這依然不是重寫,只是在子類重新定義了一個新方法而已
- 子類重寫父類方法時,訪問許可權不能更低
- 如:父類方法用 protcted修飾,那麼子類的修飾符只能更大或者一致,也就是隻可以用public 或者protcted修飾
- 父類靜態方法,子類也必須經過靜態方法進行重寫
- 這個不算重寫,但是表現出的現象確實如此
- 因為靜態不屬於繼承的範圍,靜態與類掛鉤,與物件無關
常量的許可權修飾:
- 任何一個常量都是被public static final修飾
- public 保證可以訪問到
- static 表示是跟著類出來的 (通過類來選)
- final 表示不能更改
一個類的初始化順序:
- 先初始化靜態變數,靜態方法
- 執行靜態程式碼塊
- 執行構造程式碼塊
- 執行構造方法
多型
- 一個事物在不同時刻表現出來的不同狀態
多型的前提和體現:
-
有繼承關係
-
有方法的重寫
-
有父類引用指向子類物件
-
舉例:
-
Pet pet=new Dog(); pet.eat(); Pet pet=new Cat(); pet.eat();
-
new Dog時打印出狗吃骨頭,new Cat時打印出貓吃魚
-
多型的好處:
-
對外管理的是同樣的pet物件,具體New哪一個類,在後面改就可以
- 在大型專案中,後面用父類具體new哪一個子類,是不用在程式碼裡寫的,用配置檔案配置就好了
- 這叫做解耦,程式碼與程式碼之間的耦合性降低
-
提高了程式的維護性
-
提高了程式的擴充套件性
- 在定義方法時,傳進的引數只需傳一個父類就代表隨意傳子類了,具體想要傳哪一個子類,只需在父類new子類的時候,改成想要new的那個子類就可以,形參是不需要動的
多型的弊端:
- 不能訪問子類特有功能
- 想要訪問子類特有功能只能轉型
instanceof關鍵字
-
A類 instanceof B類
- A類是否是B類的子類
-
A類 instanceof A類
- 這也是可以的
成員訪問的特點:
-
訪問變數時,訪問的是父類的變數
-
訪問方法時,訪問的是子類的方法
-
但是父類中要有和子類方法一樣的方法名
-
如: 子類Dog類中有play這個方法,但是父類Pet類中沒有該方法,所以是無法使用Dog類的play方法的
-
這就是多型的弊端:無法訪問子類特有的功能
- play方法是Dog類特有的功能,父類Pet類沒有,所以訪問不到
-
解決方法:轉型
-
public static void change(Pet pet){ if(pet instanceof Dog ){ pet=(Dog)pet; ((Dog)pet).play(); } if(pet instanceof Cat){ pet=(Cat)pet; ((Cat)pet).play(); } }
-
-
-
-
訪問靜態方法時,訪問的是父類的
抽象類
-
抽象就是找出事物的相似和共性之處,然後將然後將這些事物歸為一個類,這個類只考慮這些
事物的相似和共性之處,並且會忽略與當前主題和目標無關的那些方面,將注意力集中在與
當前目標有關的方面 -
當一個類沒有具體資訊,只是定義一些功能(就是要幹嘛能幹嘛),那麼就把這個類定義為抽象類
定義格式:
- 抽象類和抽象方法必須用abstract關鍵字修飾
- abstract class 類名 {}
- public abstract void eat();
特點:
-
抽象類不一定有抽象方法,有抽象方法的類一定是抽象類
- 抽象類中有具體方法,是為了提高程式碼的複用性
- 抽象類可以有一些共有的工具方法,這些方法對所繼承的所有子類來說都是一樣的,或者是不變的、唯一的,那麼就可以加在抽象類中大家一起去用
- 子類所特有的一些東西,在抽象類中只定義一個抽象方法,具體的實現由子類自己去重寫
- 抽象類中有具體方法,是為了提高程式碼的複用性
-
抽象類不能夠被例項化
- 抽象類的例項化是通過多型來例項化
- 這也是多型的一種,抽象類多型
-
抽象類的子類:
- 抽象類的子類必須重寫抽象類的所有抽象方法
- 如果子類沒有實現抽象父類的所有抽象方法,那麼子類也必須定義為抽象類
成員特點:
-
成員變數:
- 可以是變數
- 可以是常量
-
構造方法:
- 抽象類可以有構造方法,但是不能夠被例項化
- 構造方法的作用就是用於子類訪問父類資料的初始化
- 抽象類的抽象方法是要被子類重寫的,是要有繼承關係的,所以抽象類的構造方法的作用就是讓子類去訪問的,但是抽象類本身是不能夠被例項化,不能new出抽象類這個物件,但是它的構造方法會被執行一下
- 抽象類不能有抽象構造方法和抽象靜態方法
-
成員方法:
- 抽象方法:限定子類必須完成某些行為
- 非抽象方法:提高程式碼的複用性
abstract關鍵字不能與哪些關鍵字共存:
-
private 衝突
- 定義為抽象方法一定是要被子類重寫的,那麼如果將抽象方法定義為private子類都訪問不到,重寫個錘子
-
final 衝突
- 與上同,final修飾的方法不可以被重寫
-
static 無意義
- 抽象類要被繼承,繼承與物件掛鉤,物件是後出現的,而static是隨著類的出現而出現,繼承不到static方法
介面
- 介面是在抽象類的基礎上出現的,介面限定的是行為,方便程式碼的擴充套件
格式:
-
定義一個類為介面:
-
public interface UserService{ public void insert(); public void delete(); }
-
-
用一個類實現一個介面:用 implements
-
public class UserService implements UserService{ public void insert(){ } public void delete(){ } }
-
特點:
-
對於變數來說:
- 只能是常量
- 且常量的預設修飾符:public static final
- 介面是一個規範,是不能被改變的,所以一定是常量,被final修飾,如果可以更改的話,這個規範沒有意義,就不叫規範
-
對於方法來說:
- 只能是抽象方法
- 且抽象方法的預設修飾符:public abstract
-
對於構造方法:
- 介面沒有構造方法
- 介面主要做功能拓展的,沒有具體實現
-
介面不能被例項化
- 要例項化介面,就要按照多型的方式
- 這也是多型的一種,叫介面多型
-
介面的子類
- 必須重寫介面中的所有抽象方法
介面和抽象類的差異
-
抽象類是一個類,子類要繼承(extends)它, 介面是要被子類實現(implements),關鍵字不同
-
對於變數:
- 抽象類可以有普通成員變數,介面沒有普通成員變數
- 介面的變數只能為常量,預設被public static final修飾
- 抽象類可以有普通成員變數,介面沒有普通成員變數
-
對於成員方法:
- 抽象類裡可以有非抽象的方法,但是接口裡必須全都是抽象方法
- 抽象類的方法可以包含靜態方法,介面不能包含靜態方法
- 介面的抽象方法只能是,並且預設是:public abstract
-
對於構造方法:
- 抽象類可以有構造方法,介面不能有構造方法
-
一個類可以實現多個介面,但只能繼承一個抽象類