Java基礎(7)— 面向物件
面向物件
初識面向物件
面向過程&面向物件
- 面向過程
- 線性思維,步驟清晰簡單,第一步做什麼,第二步做什麼
- 面對過程適合處理一些較為簡單的問題
- 面向物件
- 物以類聚,分類的思想模式,思考問題首先會解決問題需要哪些分類,然後對這些分類進行單獨思考。最後才對某個分類下的細節進行面向過程的思索
- 面向物件適合處理複雜的問題,適合處理需要多人協作的問題
- 對於描述負責的事物,為了從巨集觀上把握,從整體上合理分析,我們需要使用面向物件的思路來分析整個系統。但是具體到微觀操作,仍需要面向過程的思路去處理
什麼是面向物件?
- 面向物件程式設計(Object-Oriented Programming, 00P)
- 面向物件程式設計的本質就是:以類的方式組織程式碼,以物件的組織(封裝)資料
- 抽象:程式設計思想
- 三大特性
- 封裝
- 繼承
- 多型
- 從認識論角度考慮是先有物件後有類。物件,是具體的事物。類,是抽象的,是對物件的抽象
- 從程式碼執行角度考慮是先有類後有物件。類是物件的模板
方法回顧和加深
方法的定義
-
修飾符
-
返回型別
/* 修飾符 返回值型別 方法名(...){ 方法體 return 返回值 } */ public String sayHello(){ return "hello,world!"; } public int max(int a, int b){ return a > b ? a : b; //三元運算子 }
-
break 和 return 的區別
- break:跳出switch,結束迴圈
- return:結束方法,返回結果
-
方法名:注意命名規範就OK,見明知意
-
引數列表:(引數型別 引數名)...
-
異常丟擲:疑問後面講解
public void readFile(String file) throws IOException{ }
方法的呼叫
-
靜態方法
-
非靜態方法
//學生類 public class Student { //靜態方法 public static void say(){ System.out.println("學生在說話...."); } //非靜態方法 public void eat(){ System.out.println("學生在吃飯...."); } } public static void main(String[] args) { //靜態方法呼叫 Student.say(); /* 非靜態方法呼叫 例項化這個類 new 物件型別 物件名 = 物件值; */ Student stu = new Student(); stu.eat(); }
-
形參和實參
public static void main(String[] args) { //形參和實參型別要對映 //int add = new Demo03().add(2,3); int add = add(2,3); System.out.println(add); } public static int add(int a,int b){ return a+b; }
-
值傳遞和引用傳遞
//值傳遞 public static void change(int a){ a = 10; } public static void main(String[] args) { int a = 1; System.out.println(a); //1 change(1); System.out.println(a); } //引用傳遞:物件,本質還是值傳遞 //物件,記憶體! //定義一個Person類,有個屬性是name class Person{ String name; } public static void change(Person person){ //person是一個物件,指向的Person person = new Person();這是具體的人,可以改變屬性。 person.name = "葉凡"; } public static void main(String[] args) { Person person = new Person(); System.out.println(person.name);//null change(person); System.out.println(person.name);//??? }
-
this 關鍵字(繼承和多型的時候學)
物件的建立分析
類與物件的關係
-
類是一種抽象的資料型別,它是對某一類事物整體描述定義,但是並不能代表某一個具體的事物
- 動物、植物、手機、電腦....
- Person類、Pet類、 Car類等,這些類都是用來描述/定義某類具體的事物應該具備的特點和行為
-
物件是抽象概念的具體例項
- 張三就是人的一個具體例項,張三家裡的旺財就是狗的一個具體例項
- 能夠體現出特點,展現出功能的是具體的例項,而不是一個抽象的概念
-
構造器必須要掌握
建立與初始化
-
使用new關鍵字建立物件的時候,除了分配記憶體空間之外,還會給建立好的物件進行預設的初始化以及對類中構造器的呼叫
//學生類 public class Student { //屬性/欄位 String name; int age; //方法 public void study(){ System.out.println(this.name + "在學習"); } } //一個專案應該只存在一個main方法 public class Application { public static void main(String[] args) { /* 類:抽象的,例項化 類例項化後會返回一個自己的物件 jack/tom 物件就是Student類的具體例項 */ Student jack = new Student(); Student tom = new Student(); jack.name = "jack"; jack.age = 3; System.out.println(jack.name); System.out.println(jack.age); tom.name = "tom"; tom.age = 3; System.out.println(tom.name); System.out.println(tom.age); } }
構造器詳解
-
類中的構造器也稱為構造方法,是在進行建立物件的時候必須要呼叫的,並且構造器有以下倆個特點
- 必須和類的名字相同
- 必須沒有返回型別,也不能寫void
-
IDEA生成構造器快捷鍵:Alt+Insert
public class Person { /* 一個類即使什麼都不寫,也會存在一個方法(空參構造) public Person() {} */ String name; //例項化初始值 /* 1.使用new關鍵字,本質是再呼叫構造器 */ public Person(){ } /* 2.有參構造,一旦定義了有參構造,無參必須顯示定義 */ public Person(String name){ this.name = name; } } public static void main(String[] args) { //例項化一個物件 Person person = new Person("葉凡"); System.out.println(person.name); }
建立物件記憶體分析
-
程式碼
public class Pet { public String name; public int age; public void shout(){ System.out.println("叫了一聲!"); } } public class Application { public static void main(String[] args) { Pet dog = new Pet(); dog.name = "旺財"; dog.age = 3; dog.shout(); Pet cat = new Pet(); cat.name = "湯姆"; cat.age = 2; cat.shout(); } }
-
示意圖
-
小結:
-
類與物件
- 類是一個模板,抽象
- 物件是一個具體的例項
-
方法
- 定義,呼叫!
-
物件的引用
- 引用型別:基本型別(8)之外的,物件是通過引用來操作的:棧 ---> 堆
-
屬性:欄位Field/成員變數
- 預設初始化(char-u0000/boolean-false/引用-null)
- 修飾符 : 屬性型別 屬性名 = 屬性值、
-
物件的建立和使用
- 必須使用new關鍵字創造物件,構造器 Person Jack = new Person();
- 物件的屬性 jack.name/jack.age
- 物件的方法 jack.sleep();
-
類
- 靜態的屬性
- 動態的行為
-
面向物件三大特性
封裝
-
該露的露,該藏得藏
- 我們程式設計要追求 "高內聚,低耦合"。高內聚就是類的內部資料操作細節自己完成,不允許外部干涉;低耦合:僅暴露少量的方法給外部使用。
-
封裝(資料的隱藏)
- 通常,應禁止直接訪問一個物件中資料的實際表示,而應通過操作介面來訪問,這稱為資訊隱藏。
- 記住一句話:屬性私有,set/get (快捷鍵 Alt+Insert)
public class Student { /* private 屬性私有 */ private String name;//名字 private int id; //學號 private char sex; //性別 private int age; //年齡 public int getAge() { return age; } public void setAge(int age) { //可以加一些校驗 if(age > 120 || age < 0){ age = 3; } this.age = age; } /* 提供一些可以操作這個屬性得方法 提供一個public的get/set方法 */ public String getName() { return name; } public void setName(String name) { this.name = name; } } public static void main(String[] args) { Student stu = new Student(); stu.setName("葉凡"); System.out.println(stu.getName()); stu.setAge(999); System.out.println(stu.getAge()); }
-
好處
- 提高程式的安全性,保護資料
- 隱藏程式碼的實現細節
- 統一介面,形成規範
- .系統可維護性增加了
繼承
-
繼承的本質是對某一批類的抽象,從而實現對現實世界更好的建模
-
extends的意思是“擴充套件”,子類是父類的擴充套件
-
JAVA中類只有單繼承,沒有多繼承
-
被final修飾的類不能被繼承
-
繼承是類和類之間的一種關係。除此之外,類和類之間的關係還有依賴、組合、聚合等
- 繼承關係的兩個類,一個為子類(派生類),一個為父類(基類),子類繼承父類,使用關鍵字extends來表示
- 子類和父類之間,從意義上講應該具有"is a"的關係
/* Person 人 父類/基類 java中,所有類都直接或間接繼承Object類 */ public class Person { /* 四個級別: public protected default private */ private int money = 10_0000_0000; public void say(){ System.out.println("說了一句話...."); } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } public static void main(String[] args) { Student stu = new Student(); stu.say(); System.out.println(stu.getMoney()); }
-
super注意點
- super呼叫父類的構造方法,必須在構造方法的第一行
- super 必須只能出現在子類的方法或者構造方法中
- super 和 this 不能同時呼叫構造方法(因為都要在第一行)
-
VS this
-
代表物件不同
this:代表呼叫者這個物件
suoer:代表父類物件的應用
-
前提
this:沒有繼承也可以用
super:只能在繼承條件才可以使用
-
構造方法
this:本類的構造
super:父類的構造
-
-
Ctrl + H:顯示繼承關係
//父 public class Person { protected String name = "葉凡"; public void print(){ System.out.println("Person"); } public Person(){ System.out.println("Person無參構造執行了"); } } //子 public class Student extends Person { private String name = "葉依水"; public void print(){ System.out.println("Student"); } public Student(){ //隱藏程式碼super():呼叫了父類的無參構造 System.out.println("Student無參構造執行了"); } public void test1(String name){ System.out.println(name); //石昊 System.out.println(this.name); //葉依水 System.out.println(super.name);//葉凡 } public void test2(){ print(); this.print(); super.print(); } } //效果 public static void main(String[] args) { Student stu = new Student(); stu.test1("石昊"); stu.test2(); }
-
方法重寫
-
前提:需要有繼承關係,子類重寫父類的方法!
- 方法名必須相同
- 引數列表必須相同
- 修飾符:範圍可以擴大,但不能縮小 (public > protected > default > private)
- 丟擲異常:範圍,可以被縮小,不能擴大 ClassNotFoundException --> Exception
-
子類的方法和父類的方法必須要一致,只方法體不同
-
為什麼需要重寫?
- 父類的功能子類不需要或者不滿足!
- Alt + Insert ---> override
//重寫都是方法的重寫,與屬性無關 public class A{ public void test(){ System.out.println("A-->test()"); } } public class B extends A{ @Override //重寫,註解:有功能的註釋 public void test() { super.test(); } } //執行 public class Application { /* 靜態方法和非靜態方法區別很大: 靜:方法呼叫只和左邊型別有關 非靜:才可以重寫,子類重寫了父類的方法 */ public static void main(String[] args) { B b = new B(); b.test(); //父類指向子類 A a = new B(); a.test(); } }
-
多型
-
即同一方法可以根據傳送物件的不同而採用多種不同的行為方式
-
一個物件的實際型別是確定的,但可以指向物件的引用的型別有很多
-
多型存在的條件
- 有繼承關係
- 子類重寫父類方法
- 父類引用指向子類物件
-
注意事項
- 多型是方法的多型,屬性沒有多型性
- 父類和子類,有聯絡,型別轉換異常! ClassCastException !
- 存在的條件:繼承關係,方法需要重寫,父類指向子類物件!Fathor f = new Son();
public static void main(String[] args) { //一個物件的實際型別是確定的 Student student = new Student(); Person person = new Person(); /* 可以指向引用型別就不確定了: 父類的引用指向子類,這就是多型 不能呼叫子類的獨有方法 */ Person stu = new Student(); Object os = new Student(); stu.run(); }
-
instanceof (型別轉換)引用型別,判斷要給物件是什麼型別
public static void main(String[] args) { //Object > Person > Student Object obj = new Student(); System.out.println(obj instanceof Student); //true System.out.println(obj instanceof Person); //true System.out.println(obj instanceof Object); //true System.out.println(obj instanceof Teacher); //false System.out.println(obj instanceof String); //false System.out.println("=========="); Person per = new Student(); System.out.println(per instanceof Student); //true System.out.println(per instanceof Person); //true System.out.println(per instanceof Object); //true System.out.println(per instanceof Teacher); //false //System.out.println(per instanceof String); //編譯都報錯了! System.out.println("=========="); Student stu = new Student(); System.out.println(stu instanceof Student); //true System.out.println(stu instanceof Person); //true System.out.println(stu instanceof Object); //true //System.out.println(stu instanceof Teacher); //編譯報錯! //System.out.println(per instanceof String); //編譯都報錯了! }
-
型別轉換
- 父類引用指向子類的物件
- 把子類轉為父類,向上轉型
- 把父類轉為子類,向下轉型,強制轉換
- 方便方法的呼叫,減少重複的程式碼!
//父轉子 public static void main(String[] args) { //型別之間轉換:父 子 //高 低 Person per = new Student(); per.run(); //per.go();不可使用 //將這個物件轉換成Student型別,就可以用student類的方法了 Student stu = (Student) per; stu.go(); } //子轉父,會丟失子類的方法 public static void main(String[] args) { Student stu = new Student(); stu.go(); Person per = stu; //per.go();//不可使用 }
-
static
public class Student{ private static int age; //靜態變數 多執行緒會多用! private double score; //非靜態變數 public void run(){ System.out.println("run..."); } public static void go(){ System.out.println("go..."); } public static void main(String[] args) { Student stu = new Student(); System.out.println(stu.score); System.out.println(stu.age); //System.out.println(Student.score);//不可使用 System.out.println(Student.age); //run()://不可呼叫 new Student().run(); go();//可以用 } } public class Person { { /* 程式碼塊(匿名程式碼塊)建立物件時候建立 附初始值 */ System.out.println("匿名程式碼塊"); } static{ /* 靜態程式碼塊(載入初始化內容) 類載入就執行了,永久執行一次~ */ System.out.println("靜態程式碼塊"); } public Person(){ System.out.println("構造器"); } public static void main(String[] args) { Person person = new Person(); /* 執行順序 1.靜態程式碼塊 2.匿名程式碼塊 3.構造器 */ } } /* 靜態匯入包 */ import static java.lang.Math.random; import static java.lang.Math.PI; public class Test { public static void main(String[] args) { //System.out.println(Math.random()); System.out.println(random()); System.out.println(PI); } } /* 被final修飾的類就斷子絕孫了... 被final修飾的基本變數必須有初始值,且不可更改 被final修飾的引用變數不能在指向其他物件 */
抽象類和介面
抽象類
-
abstract修飾符可以用來修飾方法也可以修飾類,如果修飾方法,那麼該方法就是抽象方;如果修飾類,那麼該類就是抽象類
//abstract 抽象類: 類,extends 單繼承~ 介面可以多實現 public abstract class Action { /* abstract 抽象方法,只有方法名,沒有方法的實現! 約束,幫我們實現 */ public abstract void doSomething(); /* 1.不能new抽象類,只能開子類去實現:約束! new的話編譯報錯:'Action' 是抽象的;無法例項化 2.抽象類中可以寫普通方法 3.抽象方法必須在抽象類中 抽象的抽象:約束 思考題? 存在構造器嗎? 存在的意義 抽象出來~ 提高開發效率 */ } /* 抽象類的所有方法,除非子類也是抽象類,否則繼承了他的子類都要實現他的方法~ */ public class A extends Action{ @Override public void doSomething() { } }
介面
-
比較
- 普通類,只有具體實現
- 抽象類,具體實現和規範(抽象方法)都有!
- 介面,只有規範!自己無法寫方法~專業的約束!約束和實現分離:面對介面程式設計
-
介面就是規範,定義的是一組規則,體現了現實世界中 "如果你...則必須能...的思想"
- 如果你是天使,則必須能飛。如果你是汽車,則必須能跑。如果你好人,則必須幹掉壞人;如果你是壞人,則必須欺負好人
-
介面的本質是契約,就像我們人間的法律一樣。制定好後大家都遵守
-
oop的精髓,是對物件的抽象,最能體現這一點的就是介面。為什麼我們討論設計模式都只針對具備了抽象能力的語言(比如c++、java、 c# 等),就是因為設計模式所研究的,實際上就是如何合理的去抽象
//抽象的思維~ Java //關鍵字 interface public interface UserService { //常量~ public static final int AGE = 99; //介面中的所有定義的方法都是抽象的 public abstract void add(Long id); void delete(Long id); void update(Long id); void query(Long id); } public interface TimeService { void timer(); } /* 類可以實現介面 implements 介面 實現了介面的類,就需要重寫介面中的方法~ 多實現~利用介面實現多繼承 */ public class UserServiceImpl implements UserService,TimeService{ @Override public void add(Long id) { } @Override public void delete(Long id) { } @Override public void update(Long id) { } @Override public void query(Long id) { } @Override public void timer() { } }
-
作用
- 約束
- 定義一些方法,讓不同的人實現
- 介面都是public abstract
- 常量都是public static final
- 介面不能被例項化,介面中沒有構造方法
- 介面可implements多個,必須重寫介面中所有的方法
內部類及OOP實戰
-
內部類就是在一個類的內部在定義一個類。比如A類中定義一個B類,那麼B類相對A類來說就稱為內部類,而A類相對B類來說就是外部類了
public class Outer { private int age = 10; public void out(){ System.out.println("這是外部類方法"); } public class Inner{ public void in(){ System.out.println("這是內部類方法"); } public void getAge(){ System.out.println(age); } } } public static void main(String[] args) { Outer outer = new Outer(); //通過這個外部類來例項化內部類~ Outer.Inner inner = outer.new Inner(); inner.in(); inner.getAge();//10 } //如果內部類加修飾詞static成為靜態內部類 public static class Inner{ public void getAge(){ //System.out.println(age);編譯都出錯了,static例項化在age之前所以拿不到age了。除非age也成為靜態變數 } } /* 一個java類中可以有多個class類,但是隻能由一個 public 修飾的 class */ public class Outer { } class A{ } //寫在外部類方法中的類就是區域性內部類 public class Outer { public void method(){ class Inner{ public void in(){ System.out.println("區域性內部類"); } } } } /* 匿名內部類的更多實現 */ public class Test { public static void main(String[] args) { //沒有名字初始化類,不用將例項儲存到變數中 new Apple().eat(); //匿名內部類實現介面 UserService userService = new UserService() { @Override public void hello() { } }; } } class Apple{ public void eat(){ System.out.println("1"); } } interface UserService{ void hello(); }
-
思想很重要!!!