java面向對象編程
面向對象的基本特征
面向對象的基本特征包括:抽象、封裝、繼承、多態,這三大特征后面細寫
抽象(個人理解):將一個具體的事物,根據他的特征和行為,將它抽離成一個不具體的一種概念,一種很大的東西,一種分類,就好比人 根據把它的特征抽離成哺乳動物,一說哺乳動物你並不知道這是一個什么東西,它具體是誰,是哪種動物,你可以通過哺乳動物知道人符合條件,老鼠也符合條件,這里面人和老鼠其實都是哺乳動物的一種具體實例。
沒寫太清除,看看再描述一遍老師寫的筆記:讓事物脫離具體化,只將它的類型、屬性和行為抽離出來形成一個類,例如針對貓、狗、牛、羊,根據它們的共同特征和共同行為,可以把它們抽象出一個動物類,這個從現實中的物體到程序的類設計就是一個抽象的過程,動物類本身就是泛指,它不指具體的某一種動物,這就是脫離具體化的體現。
-
構造函數
用途:在實例化的時候調用,常常用來初始化對象,給對象的屬性賦初始值
書寫規則:訪問修飾符 類名([形參]){
//內容
}
類里面其實有一個默認的無參構造函數,當你沒寫構造函數的時候會默認使用它!但是當你自己寫了構造函數時,這個構造函數就會消失。所以當你寫了有參構造函數的時候,在實例化時就要傳入參數(因為沒有無參的構造函數)這是你要是不需要傳入參數的話,你可以加上無參的構造函數,這里就涉及到了方法的重載
重載:在同類中不同的方法使用相同的方法名(同類中一種多態性的表現)(同名,不同參:包括順序不一樣,數量不一樣,類型不一樣,返回值可以不同,訪問修飾符也可以不一樣) -
static(靜態的)關鍵字: 當你使用static修飾方法或者屬性時,你可以把該方法或者屬性理解為類的專屬的方法和屬性,它不再是某個具體的對象的方法和屬性,它是所有對象所共有的,因為它是"類的屬性和方法",既然如此,那你也可以不用實例化對象,直接使用類名來使用static修飾的屬性或者方法
注意事項:
- 靜態變量只能寫在方法外面,不能寫在方法體中
- 靜態方法中不能使用this調用其他的非靜態方法或者非靜態屬性。因為就像我剛才所說,靜態方法的是屬於類中的,而非對象,不用實例化就可以使用,但是this指向當前對象。。。。。不知道怎么說,反正就是this指向的問題。
-
static修飾的代碼塊:
static{
內容
}寫在方法外邊的代碼塊,在類加載時執行該代碼塊且僅僅執行一次(比構造函數早,構造函數在實例化時才執行)。
用途:常用來做初始化操作
-
快捷鍵操作
- Eclipse中的生成構造函數的快捷鍵:Shift+Alt+S鍵+O鍵
- Idea中的快捷鍵:Alt+insert
封裝
概念:隱藏內部實現細節,對外提供接口
-
意義:
- 避免非法數據(可以對setter方法進行流程控制)使數據更加安全。
- 增加代碼的重用性
-
封裝是如何實現的:
- 改變修飾符,所以修飾符的種類有哪些呢(將public改為
private
或者protected
)- public(公有的)
- private(私有的)
- protected(受保護的)
- friendly(默認)
封裝的原理就涉及到了一個訪問權限的問題
訪問權限:public
>protected
>friendly
>private
訪問范圍 public protected private friendly 同一個類 √ √ √ √ 同一包中的其他類 √ √ × √ 不同包中的子類 √ √ × × 不同包中的非子類 √ x × × - 設置get/set方法(根據需求 對它加入條件判讀)(這個get&set方法就是對外提供的接口)
- 改變修飾符,所以修飾符的種類有哪些呢(將public改為
繼承
一、繼承
-
概念:繼承是類與類之間的關系,被繼承的那一方,可以使用基類的方法和屬性,Java中只能單繼承(即一個子類只能繼承自一個父類)繼承要滿足 is-a的關系,例如:熊貓是一只哺乳動物;那么熊貓不僅有哺乳動物的特性還有一些特有的屬性和行為;假如有多個類,它們有很多類似的地方,那我們就可以將這些共性抽離成一個父類,然后再讓他們繼承這個父類;
-
語法,如下面代碼所示,在需要繼承的類后面加
extends
關鍵字再在關鍵字后面加入需要繼承的類名
package cn.Sukura.www;
class Animals{
public String name;
public Animals(String name) {
this.name = name;
}
public void sayHi() {
System.out.printf("My name is %s\n",this.name);
}
}
class Pandas extends Animals{
public Pandas(String name) {
//super()必須寫在第一行
super(name);
}
}
public class Test {
public static void main(String[] args) {
Pandas p = new Pandas("榮榮");
p.sayHi();
}
}
- 哪些不能被繼承
- 被
final
(最終的)修飾的類 - 不能繼承父類的
構造方法
- 不能繼承被
private
修飾的方法或屬性 - 不能繼承在不同包下的父類中使用默認修飾符
friendly
的修飾的屬性和方法(上面那條和這條其實就是設計一個訪問權限的問題)
-
super關鍵字
super()
調用父類的構造方法,其實和this
的使用是一樣的,只不過super是調用關於父類的一切,super.Method()
調用父類方法,super.attribute
調用父類屬性,在子類構造中調用super()時,super()必須寫在第一行,不然不能通過編譯 -
實例化子類對象的執行過程:
父類的靜態成員,靜態代碼塊 --> 子類的靜態成員,靜態代碼塊 -->父類屬性 -->父類構造 -->子類屬性 -->子類構造 -->實例化完成
二、重寫
-
概念:是指子類繼承自父類,寫一個和父類中方法名和參數相同的方法,子類中的該方法會覆蓋父類的方法。(發生在繼承當中)
-
好處,可以子類根據不同的需要來實現不同的行為,父類只用來提供一個模板
-
注意事項:
-
繼承是重寫的大前提,沒有繼承就沒有重寫
-
方法名和參數列表完全一致才叫做重寫
-
子類方法的訪問修飾符必須大於或者等於父類方法的訪問修飾符
-
- 子類方法的返回值必須與父類的返回值相關(返回基本數據類型時子類必須與父類完全一樣,返回引用數據類型時,子類的返回值類型與父類的返回值類型相同,或是父類的返回值類型的子類)
- 注解:以@開頭的稱為注解,@Override是重寫的注解,表示該方法是重寫方法,如果該方法不滿足重寫規則,使用該注解時就會報錯。
三、抽象類
-
什么是抽象類:使用abstract修飾類是抽象類,語法如下:
public abstract class 類名{ public void sayHi(){ //這是一個正經的方法 } public abstract void sayLove();//這是一個抽象方法 }
-
注意事項:
- 抽象類不能被實例化,它主要的目的就是為了給子類做模板的!
abstract
關鍵字不能和final
或static
一起出現(他們有沖突)- 用
abstract
修飾的方法是抽象方法,它沒有方法體,抽象方法必須被子類實現,除非子類也是一個抽象類 - 抽象方法必須寫在抽象類中,但抽象類不止有抽象方法
-
abstract
關鍵字 和final
關鍵字的異同abstract
可以修飾方法和類 **final
可以修飾類,方法,屬性**abstract
修飾類時 該類不能被實例化,但是可以被子類繼承(設計出來就是用來繼承的)final
修飾類時 該類為最終類,不能被繼承abstract
修飾方法時 該方法必須被子類實現除非子類也是一個抽象類,該方法不能有方法體!final
修飾方法時為最終方法,不能被重寫final
修飾屬性時,表示常量,必須賦初始值,並且一旦賦值就不能在修改
多態
接口
-
接口的概念:Java中的
接口
是一個全部由抽象方法組成的集合,使用interface
關鍵字來聲明一個接口,接口里面只能有抽象方法和常量,實際上,它就是提供了一種規范,表示一種能力,增強了對於事物的擴展 -
對於面向接口編程的理解: 要盡可能的使代碼更加簡介,復用性更高,我們就可以考慮,我們需要的是什么,我們需要知道它是什么嗎?還是只需要知道它能做什么。可能我沒說明白,還是寫一段代碼看看吧
//首先根據業務需要 老師要教小朋友說普通話 class Teacher{ public void teachToTalk(){ //方法 } } class Kids{ String name; public void listenForClass{ //假裝里面有代碼 } public void readBookLoudly{ //假裝里面有代碼 } } //這時業務很簡單,很容易就實現了
//后來業務升級 老師的服務對象不僅僅只是小孩了 還有其他類型的學生 這時就會比較繁瑣了,這時我們可以把他們的共性抽離出來寫一個抽象類 也可以實現 class abstract Person{ String name; public abstract void listenForClass(); public abstract void readBookLoudly(); } class Kids extends Person{ public void listenForClass{ //假裝里面有代碼 } public void readBookLoudly{ //假裝里面有代碼 } } class Adults extends Person{ public void listenForClass{ //假裝里面有代碼 } public void readBookLoudly{ //假裝里面有代碼 } } class Teacher{ public void teachToTalk(){ //方法 } }
//現在你會發現只要服務對象是人都可以解決 業務繼續升級 這時老師又需要服務動物 但是動物的技能和前面的不匹配了 而且也不可能實現多種動物,這時你會想到你關注的到底是什么,是他們是誰?還是他們能做什么? public interface ListenAndRead{ public void listen(); public void recite(); } abstract class Person implements ListenAndRead{ } class Child extends Person{//兒童 public void listen(){}//聽課 public void recite(){}//朗讀 } class Student extends Person{ public void listen(){}//聽課 public void recite(){}//朗讀 } class Foreigner extends Person{ public void listen(){} public void recite(){} } class Parrot implements ListenAndRead{//鸚鵡 public void listen(){} public void recite(){} } class Teacher{ public void teach(ListenAndReciteAble lar){} }
引用: 要讓一個依賴方達到最大的兼容性或擴展性,就要讓依賴的對象類型盡量寬泛,當然(為什么不用Object)Object是最寬泛的,但是這就不明確了,而且不能保證依賴方的需要。這是就要跳出一些定式,我需要什么的是什么?還是我需要的是他們會什么?這就變成了面向接口編程。
-
接口的語法
定義接口:
訪問修飾符 interface關鍵字 接口的名字{ } 例如: public interface Call{ void CallByWechat(); }
實現接口:
- 父類和子類的關系叫做繼承
- 接口與實現類的關系叫做實現
- 使用
implements
關鍵字來實現接口,可以實現很多接口(實現多繼承)
class Phone implements Call,Play{ public void CallByWechat(){ //實現 } }
-
接口的特點
-
不能被實例化
-
接口中的方法都是抽象方法訪問修飾符默認為public 且必須是public
-
接口可以有屬性,但他們只能是公有的靜態常量(public static final)寫於不寫都如此
-
實現類必須重寫父接口的所有方法
-
接口可以繼承接口
-
-
接口與抽象類的異同點
接口 | 抽象類 |
不能被實例化 可以包含抽象方法 可以包含靜態常量 |
|
使用interface關鍵字聲明 | 使用abstract修飾的類 |
沒有構造方法 | 有構造方法 |
不能包含非抽象方法,且所有方法必須是public的,只能包含public的靜態常量。 | 可以包含非抽象方法並且可以是非public的,可以包含非public的普通變量。 |
滿足 has-a的關系 表示某有某一種能力 表示什么可以做什么。 | 滿足 is-a的關系 比如哈士奇是一條狗 這里就是繼承關系 |
異常
-
概念: 程序在運行中出現預料之外的錯誤
-
在Java中一般產生異常的情況有以下三種:
- Java內部發生錯誤產生異常,Java虛擬機產生的異常
- 編寫代碼錯誤產生的異常,如空指針異常,數組越界等異常,這種異常稱為未檢查異常
- 通過throw語句手動拋出的異常
-
異常的類型
graph TD A(Throwable)-->B1(Error錯誤) A -->B2(Exception異常) B2 -->C1(運行時異常即非檢查異常) B2 -->C2(非運行時異常即檢查異常)
運行時異常:都是RuntimeException 類
及其子類
異常,例如空指針異常、數組越界異常等,這些異常是不檢查異常,程序可以選擇捕獲處理,或者不處理,這些異常一般都是程序的邏輯引起的
檢查異常:指運行時異常以外的類型,在類型上都屬於Exception及其子類,該類異常必須處理,如果不處理就無法編譯通過,常見的有IOException
、ClassNotFoundException
等還有用戶自己拋出的異常,如果你不處理的話肯定是會報紅的對不對
-
常見的異常
異常類型 說明 Exception
異常類,它是所有異常類的頂級父類。 RuntimeException
運行時異常 ArithmeticException
除0異常,除數不能為0 ArrayIndexOutOfBoundsException
數組下標越界 NullPointerExceptio
空指針異常 ClassNotFoundException
不能加載所需要的類 ClassCastException
對象強制類型轉換錯誤 -
處理的方式,如下代碼演示
捕獲處理方式
try{ //可能出現異常的代碼塊}catch(異常類型 對象名字){ //出現異常時處理方式}catch(異常類型 對象名字){ //出現異常時處理方式}finally{ //最后執行這個 無論如何都會執行 除非System.exit()}
捕獲異常代碼一般根據異常類型從小到大捕獲 因為程序出現異常時只會進入第一個符合條件的代碼塊中
還可以通過拋出的方式處理異常
//將異常往外拋出,不再用本類中的try-catch來處理,讓調用該方法的那一方來處理public class Test { //throws 往外拋出異常 throw 人為制造一個異常 public void playing() throws Exception {//在方法名的小括號后面、大括號前面加上 throws 異常類名1,異常類名2 throw new Exception("作業沒寫完,還想玩?"); } public static void main(String[] args) { Test t = new Test(); try { t.playing(); } catch (Exception e) { e.printStackTrace(); } }}
- finally、finalize、final的區別
finally finalize final的區別 修飾類時,此類不能被繼承,修飾方法時,該方法不能被重寫,修飾屬性時,屬性的值不能被更改而且一定要賦值 java
中的垃圾回收方法,可以清理內存未使用的對象該方法不需要調用,因為java
會自動檢測並清理內存,簡稱為GC
異常處理的一部分 表示一定執行 除非遇到上面講的那個語句
集合框架
- 為什么要使用集合框架?
數組的弊端:類型必須相同、長度是固定的,如果一組數據在程序運行時並不能確定長度,那就不能再使用數組了
相對於數組,集合框架不限類型,並且長度可變,運用更加靈活。
現學部分的重點是標紅部分
Collection
和Map
是java.util
包下的兩個集合上級接口
-
Collection:存儲單個對象的集合 特點:無序(有序、無序是指在進行插入操作時,插入位置的順序性
先插的位置在前,后插的位置在后,則為有序,反之無序),不唯一如圖所示,它擁有三個子類型集合
List
、Set
和Queue
(重點是List)-
List :有序,不唯一
常用的實現類:
1. `ArrayList` :以`數組`的方式存儲,**查詢的效率高,插入和刪除的效率低** 2. `LinkedList`:以`鏈表`的方式,**插入和刪除的效率高,查詢的效率低**,和`ArrayList`相反
List常用方法 功能 add(obj) /add(int index,obj o) 添加對象/在指定位置插入 remove(obj (這是Collection提供的方法))/remove(int index)(這是List提供的方法) 按對象刪除/按下標刪除 get(int index) 獲取對象 size() 獲取集合的長度 contains() 查看集合中是否包含某對象 前三個方法
LinkedList
有擴展的方法如addFirst()
,addLast()
;(功能就不解釋了,顧名思義,其余兩個以此類推)- Set:無序、唯一,典型的實現類有:
HashSet
:以哈希表的形式存儲
TreeSet
:以樹形表的形式存儲
-
-
Map:存儲鍵(key,value)值對的集合 鍵唯一,值不唯一。
Map常用方法 | 功能 |
---|---|
put(key,value) | 往Map中添加鍵值對 |
remove(key) | 移除一對鍵值對 |
get(key) | 根據鍵獲取值 |
size() | 獲取集合的長度 |
values() | 獲取值集,返回Collection對象,因為Collection是不唯一的,所以值不唯一 |
keySet() | 獲取鍵集,返回Set對象,因為Set是唯一的,所以鍵唯一 |
containsKey(key) | 判斷集合中是否有某鍵 |
Map典型的實現類
- HashMap: 允許插入空值,效率高,線程不安全
- Hashtable :不允許插入空值,效率低,但線程安全
- TreeMap
集合的遍歷
- java中的foreach
for(類名 對象名:集合){ }//例如class Student{ String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } }public class Test { public static void main(String[] args) { List l = new ArrayList(); l.add(new Student("Yuan",19)); l.add(new Student("He",19)); for(Object s:l) { Student stu = (Student)s; System.out.println(stu.name); } }}
-
使用iterator迭代器
-
如何獲得iterator迭代器?
通過Collection提供的方法Iterator<e> Iterator()方法來得到一個迭代器
-
Iterator迭代器有哪些方法
方法 功能 boolean hasNext() 判斷是否還有下一個元素,如果有返回true,沒有返回false E next() 獲得下一個元素 -
List list = new ArrayList() {
{
this.add(new Student("張三",20));
this.add(new Student("李四",20));
}
};
Iterator iter= list.iterator();
while(iter.hasNext()) {
Student s = (Student)iter.next();
String name = s.name;
System.out.println(name);
}
- 使用for循環 + Iterator迭代器
List list = new ArrayList() {
{
this.add(new Student("張三",20));
this.add(new Student("李四",20));
}
};
for(Iterator iter = list.iterator();iter.hasNext();) {
Student s = (Student)iter.next();
String name = s.name;
System.out.println(name);
}
//另外 單純的用for循環也是可以遍歷的 因為List是有序的
泛型
-
為什么要用泛型:先注意集合的特點:集合長度可變,並且可以放任意的引用數據類型,存儲的數據類型也可以不一樣假如我有一個這樣的需求,我需要獲取全班所有人的姓名,但有個別人搗亂,把名字寫成電話號碼,導致我后面的代碼出錯,這種時候,就需要使用到泛型了,如果沒按照我的規定來存放數據就會報錯,等於是將運行時期會發生的異常提前到編譯時期了。所以可見泛型是一種安全機制,一種規范,它和接口一樣在制定規則。
-
如何理解泛型?大家上學的時候都寫過作文吧,考試的時候作文上面的要求經常是題材不限,但是一旦你確認題材了,你就不能再寫其他題材的作文了,泛型也是如此,本質上泛型是一個變量,它是需要我們賦值的。
//例如
List<student> names = new ArrayList<student> names();
//這時我如果給他存放其他的數據類型就會出錯 而且我們在取元素的時候不需要強制類型轉換
還有泛型類泛型方法之類的沒太多時間去補充,到時候補充和完善。