1. 程式人生 > 其它 >Java程式設計基礎之面向物件

Java程式設計基礎之面向物件

技術標籤:Java基礎java多型封裝繼承面向物件程式設計

宣告

本人及以後標明的相關Java學習筆記為Java上課學習筆記,對自己學習的一個梳理記錄,僅供參考,有問題私聊

文章目錄


1、面向物件的基本認識

1.1、程式設計思想:

1.1.1、面向過程與面向物件

面向過程:例如c語言,需要關注每一步的細節。

問題: 每一個細節都要精通,核心功能不突出,開發大型複雜專案,不利於團隊分工合作。

優點:
思路直接
簡單的業務功能來說,面向過程更加簡潔
相對來說 程式執行效率要高

面向物件: 例如java,不再關注細節,重點在於找一個對應的物件來呼叫其功能。

優點:
每個物件的核心功能更加突出,在大型專案中,業務功能非常複雜,面向物件思想可以非常好的來實現團隊的分工合作。

缺點:
程式執行效率略低

1.2類和物件

類:

一類事物共有的特徵和行為抽取出來
特徵: 屬性變數
行為: 方法
抽象的概念,可以將它看作模板
物件: 類的具體化(例項化)

分析: 實在的物件 經過抽取得到類
實現: 先定義類 再基於類建立物件

類中可以定義哪些內容?
成員變數 成員方法 構造方法 構造程式碼塊

例子: 人的類

public class Person {
    //特徵--變數
    String name;//姓名
    int age;//年齡
    String gender;//性別
    //行為--方法
    //public 返回值資料型別 方法名(引數列表){}
    //吃飯
    public void eat()
{ System.out.println("吃飯~~"); } //睡覺 public void sleep(){ System.out.println("睡覺~~"); } }

建立物件(例項):
格式:
類名 物件名 = new 類名();
注意:物件名 變數名 例項名
例如:

Person p = new Person();

設定物件屬性
物件名.屬性名 = 屬性值;

 //設定姓名屬性值 field屬性 欄位
        p.name = "張大廚";
        //設定年齡  性別的值
        p.age = 35;
        p.gender = "boy";

檢視物件的屬性值:
物件名.屬性名

 System.out.println(p.name);
 System.out.println(p.age);
 System.out.println(p.gender);

呼叫物件的方法:
物件名.方法名([引數]);

p.eat();
p.sleep();

注意:
變數中儲存的是new出來的堆中物件的地址值
[email protected]
全路徑名稱@地址值

1.3物件的記憶體

方法、變數放在棧中,new出來的物件在堆中

1.4構造方法(建構函式 構造器) Constructor

概念: 構造方法名和類名完全相同且沒有返回值部分

作用: 給類的屬性進行初始化賦值

格式:
許可權修飾符 類名(){}–無參構造
許可權修飾符 類名(引數列表){}–有參構造

注意:
每個類如果沒有顯式的指定構造方法,則Java程式會自動生成一個無參構造;
如果在類中自定義了構造方法,則程式不會再提供預設的無參構造;
直接建立類的物件時,預設呼叫無參構造;
構造方法可以過載的;

public class A{
	int id;
	public A(){
		System.out.println("這是無參構造");
	}
	public A(int id){
		System.out.println("這是有參構造");
	}
}

1.5成員變數 區域性變數 生命週期

成員變數: 定義在類中方法外的類的屬性
區域性變數: 定義在方法中 方法上(形參) 語句程式碼塊中

成員變數和區域性變數可以同名;
在變數作用域相同的情況,使用哪個變數遵循-就近原則;
成員變數和區域性變數的區別:
1定義位置不同:
成員變數:類中方法外
區域性變數:方法中
2變數作用域不同:
成員變數:整個類
區域性變數:方法中
3記憶體儲存地址不同:
成員變數:堆中,必須給定預設值
區域性變數:棧中,沒有預設值
4生命週期:
成員變數:
隨著物件的建立而建立 隨著物件的銷燬而消亡
區域性變數:
隨著方法執行而建立,當方法執行完成,隨之消亡

1.6 this指標

this指標一般用在建構函式裡面指代當前物件,在其他位置使用也是表示的是當前物件

public class A{
	int id;
	public A(){
		System.out.println("這是無參構造");
	}
	public A(int id){
		this.id = id;
		System.out.println("這是有參構造");
	}
}

this表示當前物件,誰呼叫這行程式碼 this就是這個物件

this用法: this.屬性–呼叫當前物件的屬性
this.方法()–呼叫當前物件的方法
方法互相呼叫可以不寫this,程式預設會加this.
this語句
格式: this([引數]);
呼叫當前物件的其他構造方法
構造方法之間可以互相呼叫 但是不能形成環
this語句必須放在構造方法中首行

1.7構造程式碼塊

格式:
{
程式碼塊;
}
位置:
類中方法外

特點:
構造程式碼塊在構造方法之前執行
每建立物件構造程式碼塊都要在構造方法執行之前執行一次
不能手動呼叫
作用:
初始化成員屬性(相當於給每個物件的屬性都設定預設值)
將構造方法中重複的程式碼提取到構造程式碼塊-提高了程式碼的複用性

1.8匿名物件

new建立物件但是沒有賦值給變數
例如:

new Person();

用法:

new Person().eat();
new Person().name--獲取屬性
new Person().name = "張三";

注意:
new Person()建立新的物件 在堆中開闢一塊新的空間

特點:
匿名物件建立之後只能使用一次

優點:
可以儘早釋放記憶體空間 提高記憶體利用率,簡單方便

2、面向物件三大特徵

三大特徵:封裝 繼承 多型

2.1封裝

封裝是繼承和多型的基礎,隱藏細節,提供功能

封裝體現:
1類:

將屬性私有化(private修飾),只能在本類中訪問
提供屬性的公開的get set方法,可以通過get set方法對私有屬性進行訪問
(實際開發時可以通過idea直接生成get set方法)
2方法
注意:
許可權修飾符:public(公共) private(私有)

理解:
計算機封裝成了一個黑盒子,無法看到盒子中的具體細節,但是計算機提供了功能,即可以理解為使用計算器計算可以得到結果,但是無法看到計算器內部運算過程。封裝之後的類對於使用者來說是透明的。
優點:
使用更加簡單
保證資料的安全性

2.2繼承

當多個類中存在大量同樣的屬性和方法時,為了提高程式碼的複用性,可以將重複的程式碼提取到一個新的類中,將提取出來的類,稱之為父類(超類 基類);之前的類需要父類中的屬性或方法,則可以繼承父類,將這些類稱之為子類(派生類)。

2.1繼承的使用

繼承格式:
父類無需特殊指定
子類則需要通過extends關鍵表明繼承哪一個父類
class 子類類名 extends 父類類名{}
其中extends是關鍵字,表示繼承

特點:
1、子類繼承父類,可以將父類的屬性和方法繼承過來;如果子類有自己特有的屬性和方法,可以在子類中單獨定義;
2、父類中私有的屬性在子類中不可見,不可以直接使用,可以通過屬性get set方法來呼叫
3、父類的構造方法不能被繼承

java類是單繼承
1、子類只能繼承一個父類
單繼承:
優點:結構明確 不會出現混亂
缺點:程式碼複用性較低 不夠靈活
多繼承:
優點:程式碼複用性更強,更靈活
缺點:可能存在方法或者屬性衝突

A:m()
B:m() m1() 
C extends A,B

2、java支援多重繼承,以傳遞的方式實現多重繼承

C extends B{}
B extends A{}

2.2許可權修飾符

控制程式的訪問許可權

Java提供了四個許可權修飾符:
public:公共的 許可權範圍最大的
protected:受保護的
預設(default\friendly):不寫許可權修飾符
private:私有地 許可權範圍是最小的


也就是說public修飾的類誰都可以訪問,protected修飾的類,除了本類內部屬性方法可以訪問類中的其他屬性和方法,子類和同包類也可以,以此類推,預設修飾符和private同理規則如上圖

注意:
外部類只能用public和預設來修飾

2.3super

super是指父類物件的關鍵字
this–當前物件
super–父類物件

使用場景:
super.方法名();–呼叫父類的方法
super.屬性名–呼叫父類物件的屬性
super語句:
呼叫父類的構造方法,來給屬性賦值

格式:
super([引數]);
super語句必須放在構造方法的首行
super this語句不能一起使用

2.4方法重寫

方法重寫:當父類中的方法不滿足子類需求時,子類可以重寫父類的方法;基於繼承關係;
子類中方法名和引數列表和父類完全相同(方法簽名一致)

注意:
1、如果子類重寫了父類的方法,當通過子類物件呼叫該方法時,則呼叫的是子類重寫之後的;
2、 @Override 註解 用於標記當前方法是重寫方法
如果方法不是重寫方法,加了@Override這個註解會報錯
3、父類的私有方法子類不能重寫

**方法重寫遵循的五大原則:兩等兩小一大
(先記住,在學習多型之後才能理解)
**
兩等:
1、方法簽名完全相等(方法名 引數列表)
2、如果父類方法的返回值型別為基本資料型別、void等,則要求子類重寫的方法的返回值型別要和父類的保持一致
兩小:
1、如果父類方法的返回值型別為引用資料型別,則要求子類重寫的方法的返回值型別小於或者等於父類的返回值資料型別
2、子類重寫的方法丟擲的異常型別要小於或者等於父類方法丟擲的異常型別
一大:
子類重寫的方法的許可權修飾符要大於或者等於父類方法的許可權修飾符

方法過載和方法重寫的區別
1、方法過載在同一個類中
方法重寫在繼承的子父類間
2、方法過載方法名相同引數列表不同
方法重寫方法名相同引數列表相同
3、方法過載 OverLoad
方法重寫 Override
4、方法過載 為了提高程式可讀性
方法重寫 子類實現靈活性

3、多型

3.1對多型的初步認識

多種形態
多型基於封裝和繼承

分類:
1、編譯時多型:
編譯期間體現出來的
主要體現:方法過載

class A{
	//構造
	public A(){
		System.out.println("無參");
	}	
	public A(int id,int name){
		System.out.println("有參");
	}
}

2、執行時多型:
需要在執行期間才能確定的
主要體現:
方法重寫:
子類如果重寫了父類的方法,則呼叫的是子類重寫之後的;
如果沒有重寫,則呼叫從父類繼承過來的。
1)向上造型:
子類物件賦值給父類引用
父類 物件名 = new 子類();
例如:

Animal animal = new Dog();

2)向下造型–資料型別的強轉
子類引用 = (子類)父類引用;
例如:

Dog dog = (Dog)animal; 

當需要呼叫子類所特有的屬性或方法時,可以通過向下造型來實現

注意:
1、如果建立的物件是左側變數對應型別的子孫類,則構成了向上造型;
2、在向上造型中,將建立的子類物件賦值給了父類的引用,當呼叫方法時,編譯看左邊 執行看右邊(子類重寫了父類的方法時,在呼叫時實際呼叫的是子類重寫之後的方法)

應用場景:
1、父類型別作為類的屬性的資料型別,屬性值可以是子類物件

//Animal是Cat的父類
Cat cat = new Cat();
Animal animal = cat;

2、方法形參使用父類型別作為資料型別,實際傳入的是子類物件

p.setAnimal(cat);
...........

public Animal setAnimal(Animal animal){
	.......
	return animal;
}

3、方法的返回值使用父類型別作為資料型別,實際返回的是子類的物件

//Animal是Dog的父類
AnimalShop shop = new AnimalShop();
Dog dog = shop.sellDog();
System.out.println(dog.getName());

...........
public Dog sellDog(){
Dog dog = new Dog();
dog.setName("小花");
return dog;
}

4、使用父類型別作為陣列的元素資料型別 實際每個元素都是子類物件

Animal[] animals = {new Dog(),new Cat(),new Dog()};

3.2方法重寫的驗證:反證

五大原則: 為了保證在向上造型和方法重寫的背景下,讓程式正常執行
兩等兩小一大

1、父類中方法的返回值資料型別如果是基本資料型別或者void,則要求子類重寫的方法保持一致

public class MethodOverride {
    public static void main(String[] args) {
        //向上造型
        Father f = new Son();
        //編譯看左邊 執行看右邊
       int res =  f.method();
/*如果沒有要求必須一致,編譯時以父類的method方法為準,
返回int沒有問題,但是實際執行執行的時子類的method方法,
導致真正執行時的返回值時double型別,
將double型別的返回值賦值給int型別的變數 有問題*/
    }
}
class Father{
    public int method(){
        System.out.println("父類的方法");
        return 10;
    }
}
class Son extends Father{
    @Override
    public double method() {
        System.out.println("子類的方法");
        return 10;
    }
}

2、父類方法返回值資料型別是引用資料型別,子類方法的返回值型別要小於或者等於父類的型別

public class MethodOverride {
    public static void main(String[] args) {
        //向上造型
        Father f = new Son();
        //編譯看左邊 執行看右邊
        //A a = B;--向上造型
       B b = f.method();//B b = A;--有問題
    }
}
class Father{
    public B method(){
        System.out.println("父類的方法");
        return new B();
    }
}
class Son extends Father{
    @Override
    public A method() {
        System.out.println("子類的方法");
        return new B();
    }
}
class A{}
class B extends A{}

3、子類的許可權修飾符要大於或等於父類的許可權修飾符

public class MethodOverride {
    public static void main(String[] args) {
        //向上造型
        Father f = new Son();
        //編譯看左邊 執行看右邊
       f.method();
/*如果子類方法通過private修飾
當編譯時,會根據父類的方法許可權修飾符校驗能否訪問,
因為父類時public,所以編譯時認為許可權沒有問題可以正常訪問
但是當執行時實際呼叫子類的方法,而子類方法通過private修飾,
只能在本類中訪問,導致真正執行時無法呼叫改方法
所以子類應該為public 修飾符*/
    }
}
class Father{
    public void method(){
        System.out.println("父類的方法");
    }
}
class Son extends Father{
    @Override
    private void method() {
        System.out.println("子類的方法");
    }
}

3.3多型的優點

多型優點
提高了程式碼複用性
靈活性 和可擴充套件性
解耦(耦合性)

(不太理解啥是耦合解耦,借鑑了一下如下兄弟的解釋,明白不少,解耦:假設生產者和消費者分別是兩個類。如果讓生產者直接呼叫消費者的某個方法,那麼生產者對於消費者就會產生依賴(也就是耦合)。將來如果消費者的程式碼發生變化,可能會影響到生產者。而如果兩者都依賴於某個緩衝區,兩者之間不直接依賴,耦合也就相應降低了。生產者直接呼叫消費者的某個方法,還有另一個弊端。由於函式呼叫是同步的(或者叫阻塞的),在消費者的方法沒有返回之前,生產者只好一直等在那邊。萬一消費者處理資料很慢,生產者就會白白糟蹋大好時光。緩衝區還有另一個好處。如果製造資料的速度時快時慢,緩衝區的好處就體現出來了。當資料製造快的時候,消費者來不及處理,未處理的資料可以暫時存在緩衝區中。等生產者的製造速度慢下來,消費者再慢慢處理掉。

連結:https://www.jianshu.com/p/5543b2eee223)

4、面向物件常見異常

NullPointerException:
空指標異常

原因:當對null呼叫屬性或方法時,會丟擲當前異常

//arr陣列長度為5但是隻有三個數,在arr[4]是空的再執行if裡面的而語句會丟擲空異常
int[] arr = new int(5){2,5,1};
for(int i = 0;i<arr.length;i++){
	if(arr[i]%5==0){
	System.out.println(arr[i]+"是五的倍數")
	}	
}

解決:在對引用資料型別呼叫屬性或方法之前,為避免出現空指標異常,需要先進行非空判斷;

//arr陣列長度為5但是隻有三個數,在arr[4]是空的再執行if裡面的而語句會丟擲空異常
int[] arr = new int(5){2,5,1};
for(int i = 0;i<arr.length;i++){
	if(arr[i]!=null){
		if(arr[i]%5==0){
			System.out.println(arr[i]+"是五的倍數")
		}	
	}
}

ClassCastException:
型別轉換異常

原因:只有真實型別和強轉的引用型別之間存在繼承關係或者實現關係時才可以用向上造型或者向下造型
解決:
instanceof–判斷當前物件是否是某個型別
關鍵字 用來判斷引用的實際型別

a instanceof Person

判斷a是否是Person型別的物件
如果是返回true
否則返回false

5、final關鍵字

關鍵字,表示最終的

final可以修飾變數、方法、類
1、final修飾變數–常量
引用不可以發生變化的量
可以修飾區域性變數和成員變數

常量命名:
全部大寫 如果有多個單詞 通過_分隔

final 修飾區域性變數:
基本資料型別:值不可變
引用資料型別:引用地址不可變,但是地址指向的堆記憶體中的資料可變
作用域:區域性方法內生效
可以先宣告,在使用之前賦值即可

final修飾成員變數:
則要麼在宣告常量時就給出預設值
或者
先宣告,
在構造程式碼塊中給出初始值
在構造方法中給出初始值
總而言之,要求final在物件建立完成之前顯示的給出初始值;

2、final修飾方法
不能被重寫
向上造型+方法重寫

Person p = new Student();
p.getMoney();//編譯看左邊執行看右邊

3、final修飾類
final修飾的類不能被繼承
Java中String System都是使用final修飾
String字串,在程式中非常常用;因為字串的操作也非常豐富且必須,Java為了方便程式設計師使用字串,沒有將字串定義成基本資料型別,而是設計成了一個類,在String類中提供了大量的方法便於程式設計師操作字串;

String passwd = "123456";
class StringDemo extends String{
toString(){
return "hello";
}
}

為了保證字串類的安全以及穩定,將String通過final修飾