1. 程式人生 > >Java面向物件詳解-上

Java面向物件詳解-上

### 一、類及物件 #### 1. 類的組成成分 - 屬性(成員變數,Field) - 方法(成員方法,函式,Method) #### 2. 屬性 成員變數 vs 區域性變數 - 相同點: - 遵循變數宣告的格式: 資料型別 變數名 = 初始化值 - 都有作用域 - 不同點: - 宣告的位置的不同 **:成員變數:宣告在類裡,方法外, 區域性變數:宣告在方法內,方法的形參部分,程式碼塊內** - 成員變數的修飾符有四個:**public private protected 預設,區域性變數沒有修飾符,與所在的方法修飾符相同** - 初始化值:一定會有初始化值,成員變數:如果在宣告的時候,不顯式的賦值,那麼不同資料型別會有不同的預設初始化值。 **區域性變數:一定要顯式的賦值。(區域性變數沒有預設初始化值**) - byte short int long ==>0 - float double ==>0.0 - char ==>空格 - boolean ==>false - 引用型別變數==>null - 二者在記憶體中存放的位置不同**:成員變數存在於堆空間中;區域性變數:棧空間中** - 總結: - 按照資料型別的不同:基本資料型別(8種) & 引用資料型別 - 按照宣告的位置的不同:成員變數 & 區域性變數 #### 3. 方法 提供某種功能的實現 ```java public void eat(){//方法體} public String getName(){} public void setName(String n){} //格式:許可權修飾符 返回值型別(void:無返回值/具體的返回值) 方法名(形參){} ``` - 關於返回值型別 - void:表明此方法不需要返回值 - 有返回值的方法:在方法的最後一定有return + 返回值型別對應的變數 - 方法內可以呼叫本類的其他方法或屬性,但是不能在方法內再定義方法! #### 4. 面向物件程式設計的思想的落地法則一: - 設計並建立類及類的成分 - 例項化類的物件 - 通過“物件.屬性”或"物件.方法"的形式完成某項功能 #### 5. 類的初始化記憶體解析:記憶體劃分的結構 - 棧(stack):區域性變數 、物件的引用名、陣列的引用名 - 堆(heap):new 出來的“東西”(如:物件的實體,陣列的實體),含成員變數 - 方法區:含字串常量 - 靜態域:宣告為static的變數 #### 6. 萬事萬物皆物件 - 在Java語言範疇中,我們都能將功能、結構等封裝到類中,通過類的實體化,來呼叫具體的功能結構 - Scanner、String - 檔案:File - 設計到Java語言與前端Html、後端的資料庫互動時,都體現為類、物件 #### 7. 例題 ```java /* * 4. 物件陣列題目: 定義類Student,包含三個屬性:學號number(int),年級state(int),成績score(int)。 建立20個學生物件,學號為1到20,年級和成績都由隨機數確定。 問題一:打印出3年級(state值為3)的學生資訊。 問題二:使用氣泡排序按學生成績排序,並遍歷所有學生資訊 提示: 1) 生成隨機數:Math.random(),返回值型別double; 2) 四捨五入取整:Math.round(double d),返回值型別long。 * * * // 兩位數的,隨機數 10 - 99 公式:【a,b】 : Math.random()*(b-a+1)+a 再強轉資料型別。 * */ public class StudentTest { public static void main(String[] args) { // 宣告Student型別的陣列 Student[] stu = new Student[20]; for (int i = 0; i < stu.length; i++) { // 給陣列元素賦值 stu[i] = new Student(); stu[i].number = i + 1; // [1,6] stu[i].state = (int) (Math.random() * (6 - 1 + 1) + 1); // [0,100] stu[i].score = (int) (Math.random() * (100 - 0 + 1)); } StudentTest test = new StudentTest(); // 問題1 test.searchState(stu,3); System.out.println("------------------------"); // // 問題2 test.sort(stu); test.print(stu); } // 遍歷學生陣列 public void print(Student[] stu) { for (int i = 0; i < stu.length; i++) { System.out.println(stu[i].info()); } } /** * * @Description 查詢指定年紀的學生 * @author MD * @date 2020年7月6日下午12:02:56 * @param stu 查詢的陣列 * @param state 指定的年紀 */ public void searchState(Student[] stu, int state) { for (int i = 0; i < stu.length; i++) { if (stu[i].state == 3) System.out.println(stu[i].info()); } } public void sort(Student[] stu) { for (int i = 0; i < stu.length - 1; i++) { for (int j = 0; j < stu.length - i - 1; j++) { if (stu[j].score <= stu[j + 1].score) { // 注意,這裡交換的不是成績而是物件 Student temp = stu[j]; stu[j] = stu[j + 1]; stu[j + 1] = temp; } } } } } class Student { int number; int state; int score; public String info() { return "學號:" + number + " 年級:" + state + " 分數:" + score; } } ``` ### 二、方法的過載(overload) 要求: * 同一個類中 * 方法名必須相同 * 方法的引數列表不同(**①引數的個數不同②引數型別不同**) 補充:方法的過載與方法的返回值型別沒有關係! ```java //如下的四個方法構成過載 //定義兩個int型變數的和 public int getSum(int i,int j){ return i + j; } //定義三個int型變數的和 public int getSum(int i,int j,int k){ return i + j + k; } //定義兩個double型資料的和 public double getSum(double d1,double d2){ return d1 + d2; } //定義三個double型陣列的和 public void getSum(double d1,double d2,double d3){ System.out.println(d1 + d2 + d3); } //不能與如上的幾個方法構成過載 // public int getSum1(int i,int j,int k){ // return i + j + k; // } // public void getSum(int i,int j,int k){ // System.out.println(i + j + k); // } //以下的兩個方法構成過載。 public void method1(int i,String str){ } public void method1(String str1,int j){ } ``` ### 三、可變個數的形參的方法 - .格式:對於方法的形參: 資料型別 ... 形參名 - 可變個數的形參的方法與同名的方法之間構成過載 - 可變個數的形參在呼叫時,個數從0開始,到無窮多個都可以 - **使用可變多個形參的方法與方法的形參使用陣列是一致** - 若方法中存在可變個數的形參,那麼一定要宣告在方法形參的最後 - 在一個方法中,最多宣告一個可變個數的形參 ```java //如下四個方法構成過載 //在類中一旦定義了過載的可變個數的形參的方法以後,如下的兩個方法可以省略 // public void sayHello(){ // System.out.println("hello world!"); // } // public void sayHello(String str1){ // System.out.println("hello " + str1); // } //可變個數的形參的方法 public void sayHello(String ... args){ for(int i = 0;i < args.length;i++){ System.out.println(args[i] + "$"); } //System.out.println("====="); } public void sayHello(int i,String ... args){ //public void sayHello(String ... args,int i){ System.out.println(i); for(int j = 0;j < args.length;j++){ System.out.println(args[j] + "$"); } } public void sayHello1(String[] args){ for(int i = 0;i < args.length;i++){ System.out.println(args[i]); } } ``` ### 四、 Java的值傳遞 - 方法的引數傳遞(重點、難點) - 形參:方法宣告時,方法小括號內的引數 - 實參:呼叫方法時,實際傳入的引數的值 - java中的引數傳遞機制:值傳遞機制 - 形參是基本資料型別的:將實參的值傳遞給形參的基本資料型別的變數 - 形參是引用資料型別的:將實參的引用型別變數的值(對應的堆空間的物件實體的首地址值)傳遞給形參的引用型別變數 - 關於變數的賦值 - **如果變數是基本資料型別,此時賦值的是變數所儲存的資料值** - **如果變數是引用資料型別,此時賦值的變數是所儲存的地址值** #### 1. 例一 ```java public static void main(String[] args) { TestArgsTransfer tt = new TestArgsTransfer(); int i = 10; int j = 5; System.out.println("i:" + i + " j:" + j);//i : 10 j : 5 // //交換變數i與j的值 // int temp = i; // i = j; // j = temp; tt.swap(i, j);//將i的值傳遞給m,j的值傳遞給n System.out.println("i:" + i + " j:" + j);//i : 10 j : 5 } //定義一個方法,交換兩個變數的值 public void swap(int m,int n){ int temp = m; m = n; n = temp; System.out.println("m:" + m + " n:" + n); } ``` #### 2. 例二 ```java public class TestArgsTransfer1 { public static void main(String[] args) { TestArgsTransfer1 tt = new TestArgsTransfer1(); DataSwap ds = new DataSwap(); System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j); tt.swap(ds); System.out.println(ds); System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j); } //交換元素的值 public void swap(DataSwap d){ int temp = d.i; d.i = d.j; d.j = temp; System.out.println(d);//列印引用變數d的值 } } class DataSwap{ int i = 10; int j = 5; } ``` #### 3. 例3 ![](https://img2020.cnblogs.com/blog/1212924/202007/1212924-20200708220713330-1590073045.png) ```java package com.atguigu.exer; import java.io.PrintStream; public class Test { public static void main(String[] args) { int a = 10; int b = 10; method(a,b); System.out.println("a="+a); System.out.println("b="+b); } // public static void method(int a , int b) { // a = a * 10; // b = b * 20; // System.out.println(a); // System.out.println(b); // System.exit(0); // } public static void method(int a, int b) { PrintStream ps = new PrintStream(System.out) { public void println(String x){ if("a=10".equals(x)) { x = "a=100"; }else if("b=10".equals(x)) { x = "b=200"; } super.println(x); } }; System.setOut(ps); } } ``` ![](https://img2020.cnblogs.com/blog/1212924/202007/1212924-20200708220722024-472013125.png) #### 4. 例4 輸出的什麼? ```java public class Test1 { public static void main(String[] args) { int[] arr = new int[] {1,2,3}; System.out.println(arr); //地址值 char[] arr1 = new char[] {'a','b','c'}; System.out.println(arr1); //abc } } ``` ### 五、面向物件的特徵一:封裝 - 問題:當建立了類的物件以後,如果直接通過"物件.屬性"的方式對相應的物件屬性賦值的話,可能會出現不滿足實際情況的意外,我們考慮不讓物件來直接作用屬性,而是通過"物件.方法"的形式,來控制物件對屬性的訪問。實際情況中,對屬性的要求就可以通過方法來體現 - 高內聚,低耦合 - **面向物件思想的落地法則二:** - 將類的屬性私有化 - 提供公共的方法(setter & getter)來實現呼叫 - **四種許可權修飾符** - 許可權從大到小為:public protected 預設 private - 四種許可權都可以用來修飾屬性、方法、構造器 - 修飾類的話:public 預設 ![](https://img2020.cnblogs.com/blog/1212924/202007/1212924-20200708220734617-693008718.png) - 封裝性的體現 - 將類的屬性私有化,提供公共的方法來呼叫 - 不對外暴露的私有化方法 - 單例模式 #### 1. 構造器 構造器的作用**:①建立物件 ②給建立的物件的屬性賦值** - 設計類時,若不顯式宣告類的構造器的話,程式會預設提供一個空參的構造器 - **一旦顯式的定義類的構造器,那麼預設的構造器就不再提供** - 如何宣告類的構造器。格式:許可權修飾符 類名(形參){ } - 類的多個構造器之間構成過載 - **類物件的屬性賦值的先後順序:** - 屬性的預設初始化 - 屬性的顯式初始化 - 通過構造器給屬性初始化 - 通過"物件.方法"的方式給屬性賦值 - 一個類中至少會有一個構造器,一般都提供一個空參的構造器 #### 2. this關鍵字 - 使用在類中,可以用來修飾屬性、方法、構造器 - 表示當前物件或者是當前正在建立的物件 - **當形參與成員變數重名時,如果在方法內部需要使用成員變數,必須新增this來表明該變數時類成員** - 在任意方法內,如果使用當前類的成員變數或成員方法可以在其前面新增this,增強程式的閱讀性 - **在構造器中使用“this(形參列表)”顯式的呼叫本類中過載的其它的構造器,要求“this(形參列表)”要宣告在構造器的首行!** ```java public class TestPerson { public static void main(String[] args) { Person p1 = new Person(); System.out.println(p1.getName() + ":" + p1.getAge()); Person p2 = new Person("BB",23); int temp = p2.compare(p1); System.out.println(temp); } } class Person{ private String name; private int age; public Person(){ this.name = "AA"; this.age = 1; } public Person(String name){ this(); // 先呼叫空引數的 this.name = name; } public Person(String name,int age){ this(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; } public void eat(){ System.out.println("eating"); } public void sleep(){ System.out.println("sleeping"); this.eat(); } //比較當前物件與形參的物件的age誰大。 public int compare(Person p){ if(this.age >
p.age) return 1; else if(this.age < p.age) return -1; else return 0; } } ``` #### 3. package/import package: 宣告原始檔所在的包,寫在程式的第一行 import: - 顯式匯入指定包下的類或介面 - 寫在包的宣告和原始檔之間 - 如果需要引入多個類或介面,那麼就並列寫出 - 如果匯入的類是java.lang包下的,如:System String Math等,就不需要顯式的宣告 - 理解.\*的概念。比如java.util. *; - 匯入java.lang.*只能匯入lang包下的所有類或介面,不能匯入lang的子包下的類或介面 - import static 表示匯入指定類的static的屬性或方法 ```java /import java.util.Scanner; //import java.util.Date; //import java.util.List; //import java.util.ArrayList; import java.lang.reflect.Field; import java.util.*; import static java.lang.System.*; public class TestPackageImport { public static void main(String[] args) { out.println("helloworld"); Scanner s = new Scanner(System.in); s.next(); Date d = new Date(); List list = new ArrayList(); java.sql.Date d1 = new java.sql.Date(522535114234L); Field f = null; } } ``` ### 六、面向物件的特徵二:繼承 1. 繼承的格式 - 通過"class A extends B"類實現類的繼承 2. 子類繼承父類以後,父類中宣告的屬性、方法,子類就可以獲取到 - 當父類中有私有的屬性或方法時,子類同樣可以獲取得到,只是由於封裝性的設計,使得子類不可以直接呼叫罷了 - 子類除了通過繼承,獲取父類的結構之外,還可以定義自己的特有的成分 3. java中類的繼承性只支援單繼承:一個類只能繼承一個父類。反之,一個父類可以有多個子類 4. 如果沒有顯示宣告一個類的父類的話,則此類繼承於java.lang.Object類 #### 1. 方法的重寫(override orverwrite) vs 過載(overload) 1. 過載:“兩同一不同”:**同一個類,同一個方法名,不同的引數列表注**:方法的過載與方法的返回值無關!**構造器是可以過載的** 2. 重寫:(前提:在繼承的基礎之上,子類在獲取了父類的結構以後,可以對父類中同名的方法進行“重構”)**方法的返回值,方法名,形參列表形同;許可權修飾符不小於父類的同名方法;子類方法的異常型別不大於父類的;兩個方法要同為static或同為非static** - 注:不能重寫父類的私有的方法 - 父類被重寫的返回值型別是A型別,那麼子類重寫方法的返回值型別可以是A類或者A類的子類 ```java class Cirlce{ //求圓的面積 public double findArea(){ } } class Cylinder extends Circle{ //求圓柱的表面積 public double findArea(){ } } ``` #### 2. 關鍵字 super 1. super,相較於關鍵字this,可以修飾屬性、方法、構造器 2. super修飾屬性、方法:在子類的方法、構造器中,通過super.屬性或者super.方法的形式,**顯式的呼叫父類的指定屬性或方法。尤其是,當子類與父類有同名的屬性、或方法時,呼叫父類中的結構的話,一定要用“super.”** 3. 通過“super(形參列表)”,顯式的在子類的構造器中,呼叫父類指定的構造器! 4. 任何一個類(除Object類)的構造器的首行,要麼顯式的呼叫本類中過載的其它的構造器“this(形參列表)”或顯式的呼叫父類中指定的構造器“super(形參列表)”,要麼預設的呼叫父類空參的構造器"super()",只能二選一 5. 建議在設計類時,提供一個空參的構造器 #### 3. 子類物件例項化的全過程 無論通過那個構造器建立子類物件,需要保證先初始化父類 目的:當子類繼承父類後,繼承父類中所有的屬性和方法,因此子類必須知道父類如何為物件進行初始化 ```java public class TestDog { public static void main(String[] args) { Dog d = new Dog(); d.setAge(10); d.setName("小明"); d.setHostName("花花"); System.out.println("name:" + d.getName() + " age:" + d.getAge() + "hostName:" + d.getHostName()); System.out.println(d.toString()); } } // 生物 class Creator { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Creator() { super(); System.out.println("this is Creator's constructor"); } } // 動物類 class Animal extends Creator { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Animal() { super(); System.out.println("this is Animal's constructor"); } } // 狗 class Dog extends Animal { private String hostName; public String getHostName() { return hostName; } public void setHostName(String hostName) { this.hostName = hostName; } public Dog() { super(); System.out.println("this is Dog's constructor"); } } ``` ### 七、 面向物件的特徵三:多型 #### 1. 多型性的表現: **①方法的過載與重寫 ②子類物件的多型性** #### 2. 使用的前提: **①要有繼承關係 ②要有方法的重寫** #### 3. 格式 - **Person p = new Man();//向上轉型** - **通過父類的引用指向子類的物件實體,當呼叫方法時**,**實際執行的是子類重寫父類的方法** **編譯時,認為p是Person型別的,故只能執行Person裡才有的結構,即Man裡特有的結構不能夠呼叫,子類物件的多型性,並不使用於屬性。** **呼叫方法:編譯看左邊,執行看右邊** **屬性:編譯和執行都看左邊** ```java package com.atguigu.java; public class AnimalTest { public static void main(String[] args) { AnimalTest test = new AnimalTest(); // 多型性的體現 test.func(new Dag()); test.func(new Cat()); } public void func(Animal an) { // Animal an = new Dag(); an.eat(); an.shot(); } // public void func(Dag dag) { // dag.eat(); // dag.shot(); // } } class Animal{ public void eat() { System.out.println("動物:吃食物"); } public void shot() { System.out.println("動物:叫"); } } class Dag extends Animal{ public void eat() { System.out.println("狗吃肉"); } public void shot() { System.out.println("汪!汪"); } } class Cat extends Animal{ public void eat() { System.out.println("貓吃魚"); } public void shot() { System.out.println("喵!喵"); } } ``` #### 4. 關於向下轉型 有了物件的多型性之後,記憶體中實際上是載入了子類特有的屬性和方法,但是由於變數宣告為父類型別,導致了編譯時只能呼叫父類中宣告的屬性和方法,子類中特有的屬性和方法調用不了,所以有了向下轉型 - **向下轉型,使用強轉符:()** - **為了保證不報ClassCastException,最好在向下轉型前,進行判斷: instanceof** ```java if (p1 instanceof Woman) { System.out.println("hello!"); Woman w1 = (Woman) p1; w1.shopping(); } if (p1 instanceof Man) { Man m1 = (Man) p1; m1.entertainment(); } ``` #### 5. 多型是編譯性行為還是執行時行為 **執行時行為** ```java package com.atguigu.java5; import java.util.Random; //面試題:多型是編譯時行為還是執行時行為? //證明如下: class Animal { protected void eat() { System.out.println("animal eat food"); } } class Cat extends Animal { protected void eat() { System.out.println("cat eat fish"); } } class Dog extends Animal { public void eat() { System.out.println("Dog eat bone"); } } class Sheep extends Animal { public void eat() { System.out.println("Sheep eat grass"); } } public class InterviewTest { public static Animal getInstance(int key) { switch (key) { case 0: return new Cat (); case 1: return new Dog (); default: return new Sheep (); } } public static void main(String[] args) { int key = new Random().nextInt(3); System.out.println(key); Animal animal = getInstance(key); animal.eat(); } } ``` #### 6. 問題1 ```java package com.atguigu.exer; /* * 練習: * 1.若子類重寫了父類方法,就意味著子類裡定義的方法徹底覆蓋了父類裡的同名方法, * 系統將不可能把父類裡的方法轉移到子類中:編譯看左邊,執行看右邊 * * 2.對於例項變數則不存在這樣的現象,即使子類裡定義了與父類完全相同的例項變數, * 這個例項變數依然不可能覆蓋父類中定義的例項變數:編譯執行都看左邊 */ class Base { int count = 10; public void display() { System.out.println(this.count); } } class Sub extends Base { int count = 20; public void display() { System.out.println(this.count); } } public class FieldMethodTest { public static void main(String[] args) { Sub s = new Sub(); System.out.println(s.count);//20 s.display();//20 Base b = s;//多型性 //==:對於引用資料型別來講,比較的是兩個引用資料型別變數的地址值是否相同 System.out.println(b == s);//true System.out.println(b.count);//10 b.display();//20 } } ``` #### 7. 問題2 ```java package com.atguigu.exer; //考查多型的筆試題目: public class InterviewTest1 { public static void main(String[] args) { Base1 base = new Sub1(); base.add(1, 2, 3); //sub_1 Sub1 s = (Sub1)base; s.add(1,2,3); //sub_2 } } class Base1 { public void add(int a, int... arr) { System.out.println("base1"); } } class Sub1 extends Base1 { public void add(int a, int[] arr) { System.out.println("sub_1"); } public void add(int a, int b, int c) { System.out.println("sub_2"); }