1. 程式人生 > 其它 >Java學習筆記-面向物件

Java學習筆記-面向物件

面向物件部分

  • Java的核心思想就是OOP
    • 面向過程思想:步驟清晰簡單,面向過程適合處理一些較為簡單的問題。
    • 面向物件思想:分類的思維模式,面向物件適合處理一些複雜的問題,適合處理需要多人協作的問題。

Java面向物件01:什麼是面向物件

  • 面向物件程式設計(Object-Oriented Programming , OOP)
  • 面向物件的本質就是:以類的方式組織程式碼,以物件的方式組織(封裝)資料。
  • 抽象
  • 三大特性:封裝、繼承、多型。
  • 從認識論的角度考慮是先有物件後有類。
  • 從程式碼執行的角度是先有類後有物件。

Java面向物件02:回顧方法的定義和呼叫

  • 方法的定義:
    • 修飾符
    • 返回型別
    • break和return的區別
    • 方法名
    • 引數列表
    • 異常丟擲
  • 方法的呼叫:
    • 靜態方法
    • 非靜態方法
    • 形參和實參
    • 值傳遞和引用傳遞
    • this關鍵字

靜態與非靜態,程式碼如下:

package com.oop;

public class Demo01 {
    //Main 方法
    public static void main(String[] args) {
//        Student.say();
        Student student = new Student();
        student.say();
    }
    //和類一起載入的
    public static void a(){
        b();
    }
    //類例項化之後才存在
    public void b(){
        a();
    }
}
package com.oop;

public class Student {
//    public static void say(){
//        System.out.println("學生說話了");
//    }
    public void say(){
        System.out.println("學生說話了");
    }

}

實參和形參,程式碼如下:

package com.oop;

public class Demo03 {
    public static void main(String[] args) {
        //實際引數和形式引數的名字要一一對應
        System.out.println(add(1, 2));
    }
    public static int add(int a,int b){
        return a + b;
    }
}

值傳遞,程式碼如下:

package com.oop;

//值傳遞
//以下的兩種change方法都是值傳遞
public class Demo04 {
    public static void main(String[] args) {
        int a = 1;
        System.out.println(a);
        
        Demo04.change01(a);
//        Demo04.change02(a);

        System.out.println(a);
    }

//    private static void change02(int a) {
//        a = 10;
//        return a;
//    }

    private static int change01(int a) {
        a = 10;
        return a;
    }

}

引用傳遞(和值傳遞),程式碼如下:

package com.oop;

//引用傳遞:物件,本質還是值傳遞
public class Demo05 {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.name);
        Demo05.change(person);
        System.out.println(person.name);
    }
    public static void change(Person person){//因為是引用型別,都是對同一個記憶體地址的內容進行修改,所以本質還是值傳遞
        person.name = "maynerd";
    }
}
//定義了一個Person類,有一個屬性:name
class Person{
    String name;
}

Java面向物件03:回顧方法的呼叫

Java面向物件04:類與物件的建立

使用new關鍵字建立物件

使用new關鍵字建立的時候,除了分配記憶體空間以外,還會給 建立好的物件 進行預設的初始化以及 對類中構造器的呼叫。

類中構造器也稱為構造方法,是在進行建立物件的時候必須要呼叫的。並且構造器有以下兩個特點:

  1. 必須和類的名字相同。
  2. 必須沒有返回型別,也不能寫void。

構造器必須掌握。

tip:好習慣:不要在每一個類裡面加上main方法,這樣是不好的,因為類裡面不應該有main方法,他就是一個單純的類,而一個程式只有一個主啟動類,所以我們可以定義一個大的主啟動類或者測試類。所以我們建造一個Application類。

程式碼如下:

package com.oop.Demo02;

//一個類應該只有一個main方法
public class Application {
    public static void main(String[] args) {
        Student xiaoming = new Student();
        Student xiaohong = new Student();

        xiaoming.name = "小明";
        xiaoming.age = 3;
        //預設初始化
        System.out.println(xiaoming.name);
        System.out.println(xiaoming.age);
        xiaohong.name = "小紅";
        xiaohong.age = 3;
        System.out.println(xiaohong.name);
        System.out.println(xiaohong.age);

    }
}
package com.oop.Demo02;

//學生類
public class Student {
    //屬性:欄位
    String name;
    int age;
    //方法
    public void study(){
        System.out.println(this.name + "在學習");
    }
}

Java面向物件05:構造器詳解

檢視一下class檔案,為什麼類裡邊是空的也能new?

IDEA看class檔案步驟:點開專案結構——>點開modules——>把out檔案加入進來——>點選OK再Apply。

一個類即使什麼都不寫,它也會存在一個方法。

可以顯式的定義構造器。

tip:一旦定義有參構造,無參構造就必須顯式定義
alt+insert生成構造器,生成有參/無參構造器

new物件的過程和有參無參構造,程式碼如下:


package com.oop.Demo02;

public class Person {
    //一個類即使什麼都不寫,他也會存在一個方法
    //顯式的定義一個構造器
    String name;
    int age;

    //例項化初始值
    //無參構造
    //1.使用new關鍵字,本質是在呼叫構造器
    //2.用來初始化值
    public Person(){
        this.name = "maynerd";
    }
    //有參構造:一旦定義有參構造,無參構造就必須顯式定義
    public person(String name){
        this.name = name;
    }

    //快捷鍵構造alt+insert
    public Person(int age) {
        this.age = age;
    }
}
package com.oop.Demo02;

//一個類應該只有一個main方法
public class Application {
    public static void main(String[] args) {
        //new例項化了一個物件
        Person person = new Person();
        System.out.println(person.name);//meynerd
    }
}

Java面向物件06:建立物件記憶體分析

建立物件程式碼:

package com.oop.Demo03;

public class Pet {
    public String name;
    public int age;
    public void shout(){
        System.out.println("叫了一聲");
    }
}
package com.oop.Demo03;

public class Application {
    public static void main(String[] args) {

        Pet dog = new Pet();
        dog.name = "旺財";
        dog.age = 3;
        dog.shout();
        System.out.println(dog.name);
        System.out.println(dog.age);

        Pet cat = new Pet();

    }
}

分析圖:

https://www.notion.so/791a81450ab644faa4bbe924a0db566e#18c4b8d8670c48e98213219e8a1c47f7

參考陣列部分:三種初始化及記憶體分析

Java面向物件07:簡單小結:類與物件

Java面向物件08:封裝詳解

高內聚,低耦合:高內聚就是類的內部資料操作細節自己完成,不允許外部干涉;低耦合就是僅暴露少量方法給外部使用。

封裝(資料的隱藏):通常應禁止直接訪問一個物件中資料的實際表示,而應通過操作介面來訪問,這稱為資訊隱藏。

tip:

  • 屬性私有,get/set。
  • alt+insert快捷鍵生成get/set方法
  • 封裝:
    • 提高程式的安全性,保護資料
    • 隱藏程式碼實現細節
    • 統一介面
    • 系統可維護性增加
package com.oop.Demo04;

public class Student {
    //屬性私有
    private String name;
    private int id;
    private char sex;
    private int age;
    /*
    提供一些可以操作這個屬性的方法
    提供一些public的get/set方法
     */

    //get獲得這個資料
    public String getName() {
        return name;
    }
    //set給這個資料設定值
    public void setName(String name) {
        this.name = name;
    }
    //alt+insert快捷鍵生成get/set方法

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age > 120 || age < 0) {
            System.out.println("年齡不合法!已自動設定為3歲!");
            this.age = 3;
        }
        else this.age = age;
    }
}
package com.oop.Demo04;

/*
    1.提高程式的安全性,保護資料
    2.隱藏程式碼實現細節
    3.統一介面
    4.系統可維護性增加
 */

public class Application {
    public static void main(String[] args) {
        Student s1 = new Student();
        s1.setName("maynerd");
        System.out.println(s1.getName());
        s1.setAge(130);//不合法資料
        s1.setAge(-1);//不合法資料
        System.out.println(s1.getAge());
    }
}

Java面向物件09:什麼是繼承

  • 繼承的本質是對某一批類的抽象,從而實現對現實世界更好的建模。

  • Java類中只有單繼承沒有多繼承!

  • 繼承是類和類之間的一種關係。除此以外,類和類之間的關係還有依賴、組合、聚合等。

  • 繼承關係的兩個類,一個為子類(派生類)一個為父類(基類)。子類繼承父類,使用關鍵字extends來表示。

    • Object類
    • super-this
    • 方法重寫

tip:ctrl+H開啟繼承樹。

Java中所有的類都預設直接或者間接繼承Object類。

Object類,程式碼如下:

package com.oop.Demo05;

public class Application {
    public static void main(String[] args) {
        Student student = new Student();
        student.say();
        System.out.println(student.money);

//        Person person = new Person();
//        person.
    }
}
package com.oop.Demo05;

/*
    基類
 */
public class Person /* extends Object */ {
    public int money = 100000;
//    private int money = 100000;//私有的是無法繼承的

    public void say(){
        System.out.println("說了一句話");
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}
package com.oop.Demo05;

/*
    派生類
 */
public class Student extends Person{

}
package com.oop.Demo05;

/*
    派生類
 */
public class Teacher extends Person{

}

繼承樹,如圖:

Java面向物件10:super詳解

父類子類屬性的關係測試,程式碼如下:

父類子類方法測試,程式碼如下:

父類子類構造器測試,程式碼如下:

tip:

  • super注意點:
    1. super呼叫父類的構造方法,必須在構造方法的第一個。
    2. super必須只能出現在子類的方法或者構造方法中!
    3. super和this不嫩惡搞同時呼叫構造方法!
  • super 跟this 的區別:
    • 代表物件不同:

      this:本身呼叫者這個物件

      super:代表父類物件的引用

    • 前提:

      this:沒有繼承也可以使用

      super:只能在繼承條件下才可以使用

    • 構造方法:

      this():本類的構造

      super():父類的構造

程式碼如下:

package com.oop.Demo05;

public class Application {
    public static void main(String[] args) {
        Student student = new Student();
        student.test("lm");
        System.out.println("==============");
        student.test02();
    }
}
package com.oop.Demo05;

/*
    派生類
 */
public class Student extends Person02{

    //構造器測試
    public Student() {
        //隱藏程式碼:先呼叫了父類的無參構造
        //super();//且必須放在下面語句的上面,不可調換位置
        System.out.println("Student(子類)無參構造器執行了!");
    }

    //屬性測試
    private String name = "lianming";
    public void test(String name){
        System.out.println(name);//傳進來的lm
        System.out.println(this.name);//本類的lianming
        System.out.println(super.name);//父類的maynerd
    }

    //方法測試
    public void test02(){
        print();//Student
        this.print();//Student
        super.print();//Person02
    }
    public void print(){
        System.out.println("Student");
    }

}
package com.oop.Demo05;

/*
    基類
 */
public class Person02 /* extends Object */ {

    //構造器測試
    public Person02() {
        System.out.println("Person(父類)無參構造器執行了!");
    }

    //屬性測試
    protected String name = "maynerd";

    //方法測試
    //私有的方法或者變數無法被繼承
    public void print(){
        System.out.println("Person");
    }
}

Java面向物件11:方法重寫

package com.oop.Demo055;

public class Application {
    public static void main(String[] args) {

        A a = new A();
        a.test();//A

        //父類的引用指向子類
        //方法呼叫只和左邊,定義的資料型別有關
        B b = new A();
        b.test();//B
    }
}
package com.oop.Demo055;

public class A extends B{
    public static void test(){
        System.out.println("A的test()");
    }
}
package com.oop.Demo055;

//重寫都是方法的重寫,和屬性無關
public class B {
    public static void test(){
        System.out.println("B的test()");
    }
}

把關鍵字static刪掉會出現如圖示記嗎,表明方法重寫

把A中的方法test刪了,alt+insert重寫該方法:

再點選執行,可以發現,都是B的test。可見靜態方法和非靜態方法的區別:

tip:

重寫:需要有繼承關係,子類重寫父類的方法!

  1. 方法名必須相同
  2. 引數列表必須相同
  3. 修飾符:範圍可以擴大但不能縮小 Public>Protected>Default>Private
  4. 丟擲的異常:範圍可以被縮小,但不能擴大 ClassNotFoundException →Exception

重寫,子類的方法和父類必須一致,方法體不同。

為什麼要重寫?父類的功能,子類不一定需要或者不一定滿足。

Java面向物件12:什麼是多型

  • 即同一方法可以根據傳送物件的不同而採取多種不同的行為方式。
  • 一個物件的實際型別是確定的,但可以指向物件的引用的型別有很多。
  • 多型存在條件:
    • 有繼承關係
    • 子類重寫父類方法
    • 父類引用指向子類物件
  • 注意:多型是方法的多型,屬性沒有多型。
  • instanceof
  1. 雖然new的是Student,但是用的是父類的方法,因為子類繼承父類。
  1. 子類重寫一下父類方法,輸出,可以發現,子類物件s1照常輸出自己方法,輸出son,父類s2因為方法重寫也輸出son。
  1. 子類增加eat,發現父類無法使用,只能強制轉換後可以使用。
package com.oop.Demo06;

public class Application {
    public static void main(String[] args) {
        //一個物件的實際型別是確定的
        //new Strdent();
        //mew Person();

        //可以指向的引用型別就不確定了:父類引用指向子類
        //Student 能呼叫的方式都是自己的或者繼承父類的
        Student s1 = new Student();
        //Person 父類,可以指向子類,但是不能呼叫子類獨有的方法
        Person s2 = new Student();
        Object s3 = new Student();

        s2.run();//子類重寫了父類方法,執行子類方法
        s1.run();

        ((Student)s2).eat();//只能強制型別轉換才能執行
    }
}
package com.oop.Demo06;

public class Person {
    public void run(){
        System.out.println("run");
    }
}
package com.oop.Demo06;

public class Student extends Person {
    @Override
    public void run() {
        System.out.println("son");
    }
    public void eat(){
        System.out.println("eat");
    }
}

多型注意事項:

  1. 多型是方法的多型,屬性沒有多型。
  2. 父類和子類,有繼承關係,才能進行強制型別轉換。型別轉換異常ClassCastException!
  3. 存在條件:繼承關係,方法需要重寫,父類引用指向子類物件。Father f1 = new Son();
  4. 哪些方法不能重寫?
    1. static 方法,屬於類,它不屬於例項
    2. final常量
    3. private方法

Java面向物件13:instaceof和型別轉換

A instanceof B用來判斷A是否與B(或者B的左邊定義的引用資料型別)有直接或者間接的繼承關係。

總結:有關係的才能編譯通過,毫無關係的,不能編譯通過。有父子關係的是true,沒有父子關係的是false

程式碼如下:

package com.oop.Demo06;

public class Application02 {
    public static void main(String[] args) {
//        Object > String
//        Object > Person > Teacher
//        Object > Person > Student
        Object object = new Student();
        System.out.println(object instanceof Student);//true
        System.out.println(object instanceof Person);//true
        System.out.println(object instanceof Object);//true
        System.out.println(object instanceof Teacher);//false
        System.out.println(object instanceof String);//true
        System.out.println("==================================================");
        Person person = new Student();
        System.out.println(person instanceof Student);//true
        System.out.println(person instanceof Person);//true
        System.out.println(person instanceof Object);//true
        System.out.println(person instanceof Teacher);//false
//        System.out.println(person instanceof String);//false
        System.out.println("==================================================");
        Student student = new Student();
        System.out.println(student instanceof Student);
        System.out.println(student instanceof Person);
        System.out.println(student instanceof Object);
//        System.out.println(student instanceof Teacher);
//        System.out.println(student instanceof String);
    }
}

強制型別轉換例項:

向下轉:強制轉換,可以使用子類方法,和父類方法。

package com.oop.Demo06;

public class Application03 {
    public static void main(String[] args) {
        //型別之間的轉換:高轉低
        //高              低
        Person obj = new Student();
        //student將這個物件轉換為Student型別,我們就可以使用Student型別的方法了!
        ((Student)obj).go();//子類方法
				((Student)obj).run()//父類方法
    }
}

向上轉:會丟失一些子類自己的一些方法。

package com.oop.Demo06;

public class Application03 {
    public static void main(String[] args) {
        //型別之間的轉換:高轉低
        Student student = new Student();
        student.go();
        Person person = student;
        //子類轉換為父類,可能丟失自己本來的一些方法!
//        person.go();
    }
}

Java面向物件14:static關鍵字詳解

tip:static修飾的變數和方法是隨類一起載入的。呼叫非靜態方法只能new一個物件,通過物件呼叫該方法。非靜態方法可以訪問類中的靜態方法。靜態方法也可以呼叫類中靜態方法,但不能呼叫非靜態方法。

new物件時,關於static的一些執行順序:

package com.oop.Demo07;

public class Person {
    //2   賦初始值
    {
        System.out.println("匿名程式碼塊");
    }
    //1   只執行一次
    static {
        System.out.println("靜態程式碼塊");
    }
    //3
    public Person(){
        System.out.println("構造方法");
    }

    public static void main(String[] args) {
        Person person = new Person();
        System.out.println("==================================");
        Person person1 = new Person();
    }
}

關於靜態匯入包:

package com.oop.Demo07;

//靜態匯入包:就可以直接使用Math中的方法,不需要加Math字首
import static java.lang.Math.random;
import static java.lang.Math.PI;

public class Test {
    public static void main(String[] args) {
        System.out.println(random());
        System.out.println(PI);
    }
}

tip:另外,被final修飾的類無法被繼承!

Java面向物件15:抽象類

abstract修飾符可以用來修飾方法也可以修飾類,如果修飾方法,那麼該方法就是抽象方法;如果修飾類,那麼該類就是抽象類。

package com.oop.Demo08;

//抽象類
public abstract class Action {
    //約束,有人幫我們實現
    //abstract,抽象方法,只有方法名字,沒有方法的實現!
    public abstract void doSomething();

}
package com.oop.Demo08;

public class ActionSon extends Action {

    @Override
    public void doSomething() {

    }
}

tip:

  • 抽象類中可以沒有抽象的方法,但是有抽象方法的類一定要宣告為抽象類。
  • 抽象類,不能使用new關鍵字來建立物件,它是用來讓子類繼承的。
  • 抽象方法,只有方法的宣告,沒有方法的實現,它是用來讓子類實現的,
  • 子類繼承抽象類,那麼就必須要實現抽象類沒有實現的抽象方法,否則該子類也要宣告為抽象類。
  • Java的類為單繼承,介面可以多繼承。

特點:

  1. 抽象類不能new出來,只能靠子類去實現它:他就是一個約束!
  2. 抽象類中可以寫普通方法。
  3. 一旦有抽象方法,必須宣告為抽象類。抽象方法必須在抽象類中!
  4. 抽象的抽象。

思考題:不能new,那存在構造器嗎?存在的意義是什麼?(提高開發效率

答:

Java面向物件16:介面的定義與實現

普通類:只有具體實現。

抽象類:具體實現和規範(抽象方法)都有!

介面:只有規範!(自己無法寫方法,(專業的約束!)約束和實現分離

tip:介面不能被例項化,介面中沒有構造方法。

作用:

  1. 約束
  2. 定一些方法,讓不同的人實現。
  3. public abstract
  4. public static final
  5. 介面不能被例項化,介面中沒有構造方法。
  6. implements可以實現多個介面。
  7. 必須要重寫介面中的方法。
package com.oop.Demo09;

//interface 定義的關鍵字,將class替換掉了
public interface UserService {
    //介面中的所有定義的變數 都是全域性靜態常量
    /*public static final */int AGE = 99;

    //介面中的所有定義的方法其實都是抽象的 public abstract
    /*public abstract */void add(String name);

    void delete(String name);
    void update(String name);
    void query(String name);
}
package com.oop.Demo09;

public interface TimeService {
    void timer();
}
package com.oop.Demo09;

//抽象類:通過extends
//類可以實現介面 implements 介面
//實現了介面的類就需要重寫介面中的方法!
public class UserServiceImp implements UserService,TimeService{

    @Override
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void update(String name) {

    }

    @Override
    public void query(String name) {

    }

    //多繼承,利用介面實現多繼承
    @Override
    public void timer() {

    }
}

Java面向物件17:N種內部類

內部類就是在一個類的內部在定義一個類,比如,A類中定義一個B類,那麼B類相對A類來說就稱為內部類,而A類相對B類來說就是外部類。

  1. 成員內部類

  2. 靜態內部類

  3. 區域性內部類

  4. 匿名內部類

  5. 成員內部類

package com.oop.Demo10;

public class Outer {
    private int id;
    public void out(){
        System.out.println("這是外部類的方法");
    }

    class Inner{
        public void in(){
            System.out.println("這是內部類的方法");
        }

        //獲得外部類的私有屬性
        public void getId(){
            System.out.println(id);
        }
    }
}
package com.oop.Demo10;

public class Application {
    public static void main(String[] args) {
        Outer outer = new Outer();
        //通過這個外部類來例項化內部類
        Outer.Inner inner = outer.new Inner();
        inner.in();

        inner.getId();
    }
}

如果Inner加上public static,那麼就成為靜態內部類,靜態內部類無法訪問非靜態屬性id。

  1. tip:一個.java檔案裡面可以有多個類,但是隻能有一個public的類,測試類就可以直接寫在下面。
  2. 物件的區域性內部類:
package com.oop.Demo10;

public class Outer02 {
    //區域性內部類
    public void method(){
        class Inner{
            public void in(){

            }
        }
    }
}
  1. 匿名內部類
package com.oop.Demo10;

public class Test {
    public static void main(String[] args) {
        //沒有名字就初始化類,匿名物件的使用,不用將例項儲存到變數中
        new Apple().eat();
    }
}

class Apple{
    public void eat(){
        System.out.println("eat");
    }
}
  1. 介面的匿名內部類