# 【韩顺平讲Java】Chapter10 - 面向对象编程(高级部分) *** > **即使再小的帆也能遠航** > 參考自[【零基础 快速学Java】韩顺平 零基础30天学会Java](https://www.bilibili.com/video/BV1fh411y7R8/?vd_source=c5074574112ef27dae243d70aa2175b8) >###### tags: `韓順平講Java` *** # 類變量和類方法 - JDK1.7後,static變量產生在堆中  :::info 不管static變量在哪裡 (1) static 變量是同一個類中,所有對象共享的 (2) static 類變量,在類加載的時候就生成 ::: - 如果我們希望不創建實例,也可以調用某個方法(即當作工具類使用),這時,把方法做成靜態方法是非常合適 ## 注意事項   # 理解main方法語法 static - main方法是JVM調用  - IDEA 傳參  # 代碼塊   * 代碼塊的調用優先於構造器 ## 細節 - 普通代碼塊只跟new 創建對象有關,調用類變量時不會使用普通代碼塊 - 類什麼時候會被加載 - 創建實例(new) - 創建子類實例,父類會被加載 - 使用類的靜態成員或方法     # 單例設計模式    ## 餓漢式 - 類加載的同時,對象就創建,向餓漢一樣很急的感覺 - 餓漢式可能造成創建了對象,但是沒有使用 ```java= package com.hspedu.single_; import com.sun.imageio.plugins.gif.GIFImageReader; public class SingleTon01 { public static void main(String[] args) { // 通過方法可以獲取對象 GirlFriend instance = GirlFriend.getInstance(); GirlFriend instance2 = GirlFriend.getInstance(); System.out.println(instance2 == instance); //T } } // GirlFriend,只能有一個對象 class GirlFriend { private String name; // 為了能夠在靜態方法中,返回gf對象,需要將其修飾為static // 對象,通常是重量級對象 private static GirlFriend gf = new GirlFriend("Elsa"); // 如何保證只能創建一個對象 // 單例模式 -- 餓漢式 // 1. 將構造器私有化 // 2. 在類的內部直接創建 (該對象是static) // 3. 提供一個公共的static方法,返回gf對象 private GirlFriend(String name) { this.name = name; } public static GirlFriend getInstance(){ return gf; } } ``` ## 懶漢式 - 只有當用戶使用getInstance時,才返回對象,之後再次調用時,會返回過去創建的對象,從而保證了單例 ```java= package com.hspedu.single_; /** * 演示懶漢式 */ public class SingleTon02 { public static void main(String[] args) { } } class Cat { private String name; private static Cat cat; // 步驟 // 1. 構造器私有化 // 2. 定義一個靜態屬性對象 // 3. 提供一個public static 方法,可以返回一個Cat對象 private Cat(String name) { this.name = name; } public static Cat getInstance(){ if (cat == null){ cat = new Cat("Kitty"); } return cat; } } ``` # final 關鍵字  ## 注意事項 - final class無法被繼承 - final 成員可以被繼承,但無法被改寫  - final + static 不會觸發類加載  ```java= package com.hspedu.final_; public class FinalExercise01 { public static void main(String[] args) { Circle circle = new Circle(5.0); System.out.println(circle.calArea()); } } class Circle { private double radius; private final double PI = 3.14; public Circle(double radius) { this.radius = radius; // PI = 3.14 } /* { PI = 3.14 } */ public double calArea() { return PI * radius * radius; } } ``` - 形參可以加 final  # 抽象類  - 抽象類不能被實例化 - 抽象類不一定要包含抽象方法,但只要有聲明abstract方法,就必須要聲明成abstract類 - ==abstract **只能** 修飾類或方法==  - 抽象方法不能使用 **private, final, static**來修飾,因為這些關鍵字與重寫相違背 - 靜態(static)跟重寫是互斥的 !! ## 抽量類最佳實踐 -- 模板設計模式   (設計模板) ```java= package com.hspedu.abstract_; abstract public class Template { // 抽象類 -- 模板設計模式 public abstract void job(); // 抽象方法 public void calculateTime() { // Start Time long start = System.currentTimeMillis(); // Do Sth.. job(); // End Time long end = System.currentTimeMillis(); System.out.println("執行時間= " +(end - start)); } } ``` (實現) ```java= package com.hspedu.abstract_; public class AA extends Template { @Override public void job() { long num = 0; for (int i = 0; i < 100000000; i++) { num += i; } } } ``` # 接口    ## 接口 vs 繼承 - 當子類繼承了父類,就自動擁有父類的功能,如果子類需要擴展功能,可以通過實現接口的方式擴展,可以理解實現接口是對Java單繼承機制的一種補充   - 修正方法 - A.x - super.x # 內部類 - 類的五大成員 - 屬性、方法、構造器、代碼塊、內部類 - 內部類的最大特性 : 可以直接訪問私有屬性  ## 局部內部類   ```java= package com.hspedu.innerclass; /** * 演示局部內部類的使用 */ public class LocalInnerClass { public static void main(String[] args) { Outer outer = new Outer(); outer.m1(); } } class Outer { // 外部類 private int n1 = 100; private void m2(){// 私有方法 System.out.println("Outer m2()"); } public void m1() { // 方法 // 局部內部類是定義在外部類的局部位置,通常在方法 final class Inner { // 局部內部類 (本質仍然是一個類) private int n1 =800; public void f1() { System.out.println("內部的n1=" + n1); // 老韓解讀 : Outer.this本質就是外部類的對象,即哪個對象調用了m1, // Outer.this就是哪個對象 System.out.println("外部的n1=" + Outer.this.n1); m2(); } } Inner inner = new Inner(); inner.f1(); } } ``` ## 匿名內部類    ```java= package com.hspedu.innerclass; /** * 演示匿名內部類的使用 */ public class AnonymousInnerClass { public static void main(String[] args) { Outer04 outer04 = new Outer04(); outer04.method(); } } class Outer04{ // 外部類 private int n1 =10; // 屬性 public void method(){ // 方法 // 基於接口的匿名內部類 // 老韓解讀 // 1. 需求: 想使用IA接口 // 2. 傳統方式: 寫一個類實現該接口,並創建對象 // 3. 需求: 希望Tiger類只使用一次,後面不再使用 // 4. 解法: 使用匿名內部類簡化開發 IA tiger = new Tiger(); tiger.cry(); // 5. tiger的編譯類型 => IA // 6. 運行類型 => 匿名內部類 -> Outer04$1 /* 我們看底層 class Outer04$1 implements IA { @Override public void cry() { System.out.println("匿名老虎叫喚"); } } */ // 7. jdk底層在創建匿名內部類Outer04$1,馬上就創建了該類的實例, // 並將地址回傳給tiger // 8. 匿名內部類使用一次,就不能再使用 tiger = new IA() { @Override public void cry() { System.out.println("匿名老虎叫喚"); } }; System.out.println(tiger.getClass()); tiger.cry(); //=========================== // 演示基於類的匿名內部類 // 分析 // 1. father編譯類型 Father // 2. father運行類型 Outer04$2 /* class Outer04$2 extends Father { @Override public void test() { System.out.println("重寫的方法"); } } */ // 3. 同時也直接返回了 匿名內部類Outer04$2的的對象 // 4. 注意("jack")參數列表會傳遞給 構造器 Father father = new Father("jack") { @Override public void test() { System.out.println("重寫的方法"); } }; System.out.println(father.getClass()); // Outer04$2 father.test(); //====================================== Animal animal = new Animal() { @Override void eat() { System.out.println("動物吃東西"); } }; animal.eat(); } } interface IA{ public void cry(); } class Tiger implements IA{ @Override public void cry() { System.out.println("老虎叫喚"); } } class Father{ public Father(String name) { } public void test() {} } abstract class Animal{ // 抽象類 abstract void eat(); } ``` ```java= package com.hspedu.innerclass; public class AnonymousInnerClassDetail { public static void main(String[] args) { Outer05 outer = new Outer05(); outer.f1(); } } class Outer05 { private int n1 = 99; public void f1() { // 創建一個基於類的匿名內部類 Person person = new Person() { private int n1 = 88; @Override public void hi() { System.out.println("匿名內部類重寫了hi()方法"); System.out.println("匿名n1= " + n1); System.out.println("外部n1= " + Outer05.this.n1); } }; person.hi(); // 動態綁定,運行類型是 Outer05$1 // 也可以直接調用,匿名內部類本身也是返回對象 // class 匿名內部類 extend Person {} new Person() { @Override public void hi() { System.out.println("匿名內部類重寫了hi()方法,哈哈哈"); } @Override public void ok(String str) { super.ok(str); } }.ok("jack"); } } class Person { public void hi(){ System.out.println("Person hi()"); } public void ok(String str){ System.out.println("Person ok() "+ str); } } // 抽象類/接口... ``` ### 實踐 - 當作實參直接傳入 ```java= package com.hspedu.innerclass; import com.hspedu.abstract_.AA; public class InnerClassExercise01 { public static void main(String[] args) { f1(new IL() { @Override public void show() { System.out.println("This is a picture"); } }); } // 靜態方法,形參是接口類型 public static void f1(IL il){ il.show(); } } interface IL{ public void show(); } ```  ```java= package com.hspedu.innerclass; public class InnerClassExercise02 { public static void main(String[] args) { Cellphone cellphone = new Cellphone(); cellphone.alarmclock(new Bell() { @Override public void ring() { System.out.println("懶豬起床了"); } }); cellphone.alarmclock(new Bell() { @Override public void ring() { System.out.println("小夥伴上課了"); } }); } } interface Bell { void ring(); } class Cellphone{ public void alarmclock(Bell bell) { bell.ring(); } } ``` ## 成員內部類    ```java= package com.hspedu.innerclass; public class MemberInnerClass01 { public static void main(String[] args) { Outer01 outer = new Outer01(); outer.t1(); // 外部其他類,使用成員內部類的三種方式 // 1. // 相當於把 new Inner01()當作是outer的成員 Outer01.Inner01 inner = outer.new Inner01(); inner.say(); // 2. 在外部類編寫方法,返回Inner01對象 Outer01.Inner01 inner2 = outer.getInner01Instance(); inner2.say(); } } class Outer01{ private int n1 = 10; public String name = "張三"; private void hi() { System.out.println("外部類hi()方法"); } // 注意 : 成員內部類是定義在,外部類的成員位置上 // 因為它等同於成員變量,因此可以加訪問修飾服 public class Inner01{ // 成員內部類 private int n1 = 66; public void say() { System.out.println("Outer01 的 n1 = "+ Outer01.this.n1+ " Outer01 的 name = " + Outer01.this.name); System.out.println("n1 = "+ n1+ " name = " + name); hi(); } } public void t1() { Inner01 inner01 = new Inner01(); inner01.say(); } public Inner01 getInner01Instance(){ return new Inner01(); } } ``` ## 靜態內部類    - 因為靜態內部類只能訪問靜態成員,因此外部的靜態成員,不需要另外在打this  ```java= package com.hspedu.innerclass; public class StaticInnerClass01 { public static void main(String[] args) { Outer10 outer10 = new Outer10(); outer10.m1(); // 外部其他類,使用靜態內部類 // 1. 靜態內部類是可以通過類名直接訪問(前提是權限滿足) Outer10.Inner10 inner10 = new Outer10.Inner10(); inner10.say(); // 2. 編寫方法返回實例 Outer10.Inner10 inner101 = outer10.getInner10(); inner101.say(); Outer10.Inner10 inner102 = Outer10.getInner10_(); inner102.say(); } } class Outer10 { // 外部類 private int n1 = 10; private static String name = "張三"; private static void cry() {} // 靜態內部類 // 1. 放在外部類的成員位置 // 2. 使用 "static" 修飾 // 3. 不能直接訪問非靜態成員 public static class Inner10{ private static String name = "李四"; public void say() { System.out.println("Inner" + name); System.out.println("Outer" + Outer10.name); cry(); } } public void m1() { Inner10 inner10 = new Inner10(); inner10.say(); } public Inner10 getInner10() { return new Inner10(); } public static Inner10 getInner10_() { return new Inner10(); } } ``` ## 小結 1. 內部類有四種 : 局部內部類、匿名內部類、成員內部類、靜態內部類 2. ==重點還是掌握匿名內部類的使用== - new 類/接口(參數列表) {...}; 4. 成員內部類、靜態內部類是放在外部類的成員位置,本質就是一個成員 # 本章作業 ## 1  9.0 red 100.0 red ## 2  ```java= package com.hspedu.homework.hw02; /** * @author Jerry Wang * @version 1.0 */ public class Frock { private static int currentNum = 100000; private int serialNumber; public Frock() { this.serialNumber = getNextNum(); } public int getSerialNumber() { return serialNumber; } public static int getNextNum() { currentNum += 100; return currentNum; } } ``` ## 3  有寫,略 ## 4  ```java= package com.hspedu.homework.hw04; /** * @author Jerry Wang * @version 1.0 */ public class Homework04 { public static void main(String[] args) { Cellphone cellphone = new Cellphone(); cellphone.testWork(new Computer() { @Override public double work(double n1, double n2) { return n1+n2; } },50,30); } } interface Computer{ public abstract double work(double n1, double n2); } class Cellphone{ public void testWork(Computer cp, double n1, double n2){ double result = cp.work(n1, n2); System.out.println("The result is "+result); } } ``` ## 5  ```java= package com.hspedu.homework.hw05; /** * @author Jerry Wang * @version 1.0 */ public class Homework05 { public static void main(String[] args) { A a = new A(); a.bClass(); } } class A{ private final String NAME = "Jerry"; public void bClass(){ class B{ private final String NAME = "Elsa"; public void show() { System.out.println("B Class has NAME = " + NAME); System.out.println("A Class has NAME = " + A.this.NAME); } } B b = new B(); b.show(); } } ``` ## 6  ## 7  ```java= package com.hspedu.homework.hw07; /** * @author Jerry Wang * @version 1.0 */ public class Car { private double temperature; public Car(double temperature) { this.temperature = temperature; } class Air{ public void flow(){ if (temperature > 40){ System.out.println("吹冷氣"); } else if (temperature < 0){ System.out.println("吹暖氣"); } else { System.out.println("關空調"); } } } public Air getAir() { return new Air(); } } ``` ```java= package com.hspedu.homework.hw07; /** * @author Jerry Wang * @version 1.0 */ public class Homework07 { public static void main(String[] args) { Car car = new Car(50); Car car1 = new Car(-2); Car car2 = new Car(25); car.getAir().flow(); car1.getAir().flow(); car2.getAir().flow(); } } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up