1. 程式人生 > 實用技巧 >Java基礎(7)— 面向物件

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();
    }
    
  • 思想很重要!!!