Java 複習筆記4 - 面向對象

本文是複習筆記,初學者若是不理解面向對象的概念能夠看看個人另外一篇博客:http://www.javashuo.com/article/p-zjxmduaw-ht.htmlhtml

對象的內存

image-20191118185432821

變量定義在棧中,而具體的對象數據則在堆區java

構造方法

構造函數,構造器python

語法要求設計模式

1.函數名稱與類名一致
2.不容許有返回值類型 
3.不容許使用return語句返回數據

特色:安全

new 對象時會自動執行 
1.能夠重載多個構造方法
2.當咱們沒有定義任何構造方法時,會自動生成無參的空構造方法
3.一旦咱們定義類構造方法,系統將不會自動生成

構造方法只能經過兩種方式來調用:ide

1.在構造方法中可經過this去調用其餘的構造方法
2.經過new 關鍵字  
//在普通方法中也不能去掉用構造方法 
public class Constructer {
    public Constructer(){
    }
    public Constructer(String name){
        this();  //調用空參數構造方法
    }
}

注意注意:調用其餘構造方法只能位於方法的第一行語句;

方法參數查找採起就近原則

當對象建立時對象的全部屬性都會被自動初始化

================================

封裝

特色:

1.只能經過規定的接口(方法)訪問內部數據函數

2.隱藏了內部實現細節,爲了隔離複雜度,和提升安全性,提升了類內部的維護性性能

使用:

修改方法或屬性的權限爲private 私有化測試

爲私有的屬性提供對外開放(public)的setter 和getter方法;this

public class Test {

    private String name;//該屬性被私有化 此時該屬性盡在該類內部能夠訪問

    public Test(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

概念:

包的本質是一個文件夾

當一個項目中出現大量的類是,維護性會下降,咱們能夠採起分包的方式來管理 ,將相關的類放到同一個包中,方便管理維護代碼,

命名規範:

採用域名倒敘,+模塊名稱+功能名稱

全小寫

image-20191119101847961

注意:不一樣包中能夠存在相同名稱的類

import

import用於導入某個類

語法:

1.導入某個包下的某個類 import com.yh.package1.Cat;
2.導入某個包下的全部類 import com.yh.package2.*;

注意:
1.當上述兩條語句出現了相同的類名稱時(package2中也有Cat),優先使用的是語義更清晰的即import com.yh.package1.Cat
2.第二種語法,僅能導入包下的類,不能導入包下的子包

咱們也能夠在代碼中使用徹底限定類名來避免衝突:

com.yh.package1.Cat c1 = new com.yh.package1.Cat();
c1.show();

static

static修飾的成員稱之爲靜態成員

特色:

1.可使用類名直接調用(固然對象也能夠)

2.而且static修飾的成員屬於類名稱空間中,也就是說全部該類對象,共享該資源,相反的若是非static修飾的成員是每一個對象獨有的,例如每一個人的name屬性不一樣;

3.靜態成員的生命週期跟隨的類,類加載時被加載,虛擬機結束時被銷燬;

4.在靜態方法中不可使用this關鍵字,由於static修飾的成員比對象先被加載

5.靜態方法中只能訪問靜態成員

代碼塊

使用一對大括號產生一個代碼塊,固然了方法也是代碼塊

1.構造代碼塊

咱們還能夠在類中直接使用{}來定義出一個構造代碼快 像下面這樣

class Person(){
  {
    //構造代碼塊,會在建立類的時候先於構造函數執行,沒建立一個對象就執行一次
  }  
}

2.普通代碼塊

class Person{
  void show(){
    //方法體    
    
    {
      // 普通代碼塊
    }
    
    //方法體
  }
}

3.靜態代碼塊

在類中使用static來修飾的代碼塊就是靜態代碼塊,其執行時機是在類被加載時,執行一次,後續不在執行,

何時使用?

當咱們須要在使用類以前作一些初始化操做時

問題?

1.代碼塊是不是局部做用域 ? 是的每一個代碼塊都是局部的

2.靜態代碼塊中是否能夠存在方法? 不能夠局部代碼塊建立方法沒有意義

代碼塊的執行順序

靜態代碼塊 -> 構造代碼塊 -> 構造方法

===============================================================

繼承

一個類和另外一個類之間的關係,是一種什麼是什麼的關係(A is aB)

如豬是動物,

好處:子類繼承父類能夠直接使用父類開放(非private)的已存在成員

image-20191119142825009

使用

extends關鍵字創建繼承關係,一個類只能有一個父類

class Animal{
  String name;
}
class Person extends Animal{
 public void show(){
   System.out.println(this.name);//直接訪問父類成員
 }
}

重寫/覆蓋

override

什麼是覆蓋:

​ 當子類出現了與父類徹底一致(相同返回值類型,相同名稱,相同參數列表)的方法時將產生覆蓋

什麼時候使用覆蓋:

​ 當父類的方法 沒法直接知足需求時子類能夠覆蓋父類已有方法

注意:

1.子類覆蓋時方法的返回值類型必須與原始方法返回值類型一致,或者是原始方法返回值類型的子類
2.子類覆蓋方法時要求方法的權限必須大於等於原始方法的權限
3.父類的靜態成員不會被覆蓋,當子類定義了與父類徹底一致的方法時,這個方法實際是屬於子類的與父類沒有關係
4.當子類出現方法名稱與參數列表與父類方法一致時則認爲是覆蓋,要求返回值類型必須一致或是其子類

也就是說若是子類想定義新的方法要麼名稱不一樣,要麼參數列表不一樣

補充:子類也能夠定義與父類相同的屬性,將覆蓋父類相同的屬性

權限修飾符

修飾符 本類 同包 子類 其餘包
public yes yes yes yes
protected yes yes yes no
default yes yes no no
private yes no no no

繼承注意事項:構造方法沒法被繼承

super

當子類覆蓋了父類的方法時,按照順序將優先執行子類中的方法,當咱們須要執行父類方法時

能夠在子類中使用super關鍵字來調用父類的方法

class A{
  public A(){
           
  }
    public show(){
    System.out.println("hello java!");
  }
}

class B extends B{
  public B(){
           super().show();
  }
}
注意:在子類的構造方法中系統會默認添加super(); 若是父類不存在空參構造函數將編譯錯誤,咱們也能夠手動的使用super來調用其餘的構造函數;
強調:super()必須放在第一行

對象的建立過程

1.加載類(僅發生一次)

2.加載父類靜態資源

3.加載子類靜態資源

4.執行父類構造代碼塊

5.執行父類構造函數

6.執行子類構造代碼塊

7.執行子類構造函數

8.完成

this與super

this:

​ 本類的,屬性,方法,構造器

super:

​ 父類的,屬性,方法,構造器

注意:this和super不能同時出如今構造函數中

image-20191119212436052

final

翻譯爲最終,能夠修飾類,變量,方法

1.在繼承關係中,被final修飾的方法沒法被覆蓋

2.final修飾變量時,就變成了常量,一旦初始化值不容許修改

3.final修飾類時,表示該類不能被繼承

註解

JDK1.5 推出

用於對方法或變量進行說明

image-20191120001010853

源碼註解盡在編譯完成時自動去除,用於對元素進行標記,方便編譯器識別,例如@override

編譯時註解指的是編譯成class後依然存在的註解

運行時註解指的是,會對程序邏輯產生影響的註解,例如autowrite,transaction等..

=============================================

單例模式

常見23中設計模式,及其分類;

image-20191120005925881

什麼是單例:

某個類有且僅有一個實例,那ta就是單例類

image-20191120010309895

java中的實現方式:

1.將構造函數私有化 以禁止外界本身初始化

2.利用static僅加載一次的特性,建立一個靜態的對象做爲類的私有屬性

3.提供訪問這個靜態對象的方法

按照建立對象時機不一樣能夠分爲兩種

1.餓漢式: 直接在聲明static屬性時建立對象 這種方法是線程安全

class SingleInstance{
  private static SingleInstance obj = new SingleInstance();
  private SingleInstance(){
    //私有化的構造函數 
  }
  public SingleInstance getInstance(){
    return obj;
  }
}

2.懶漢式:在獲取對象是若是發現對象爲空才建立 這種方法是非線程安全

public class Emperor {
     //定義私有構造方法
    private Emperor(){
        System.out.println("create instance!");
    }
     //定義私有靜態類對象
    private static Emperor obj = null;
     //定義公有靜態方法返回類內的私有靜態對象
     public static Emperor get_instance(){
         if(obj == null){
             obj = new Emperor();
         }
         return obj;
     }
}

優缺點:

優勢:

​ 節省空間,提升性能

缺點:

​ 擴展困難

​ 當對象長期不使用,困難會被回收致使異常

使用場景:

當程序須要共享同一份對象的數據時

當每一個對象數據都相同時,則沒有必要建立對個對象

當須要保證某些數據的一致性時,例如: 要生成惟一的id,若是每一個對象都有一份本身的生成方式則可能形成數據錯誤

image-20191120094929672

================================

多態

定義:

​ 多個不一樣對象能夠響應同一個方法產生各自不一樣的行爲

分類:

1.編譯時多態

​ 指的是在編譯階段就能識別具體要執行的方法是哪個,一般是方法重載,經過參數不一樣決定調用哪一個方法

2.運行時多態

​ 只有在運行時才能肯定到底調用哪一個方法

image-20191120095446497

注意下面的內容都是針對運行時多態,是任何OOP語言的共同特性

特色:

出現多態的兩個必要條件

1.必須具有繼承關係

2.父類指針指向子類對象

ps:在python中沒有這兩個限制,python是動態類型,編譯期間不會檢查對象具體類型,更加靈活,可是也增長了風險

向上轉型

指的是用父類指針引用子類實例,會自動將子類轉換爲父類,轉換後將隱藏子類特有的成員,只能使用父類中定義的方法和屬性,

向下轉型

當明確某個對象就是某個子類對象時能夠強制轉換爲子類,如此就能夠從新訪問子類中獨有的成員

當不能明確對象是不是某個類型時可使用 instanceof

if(obj instanceof Objcet){
  System.out.println("obj is  ObjectClass instance");
}else{
  System.out.println("obj not  ObjectClass instance");
}
注意:因爲靜態方法不能被重寫,當子類和父類存在徹底相同的靜態方法時,向上轉型後對象默認調用的是父類中的靜態方法,也就是說不存在多態,調用的方法是明確的,跟隨類型的

abstract 抽象類

什麼是抽象類:

抽象類指的是類中包含抽象方法的類,

抽象方法指的是沒有任何方法體的方法

使用abstract關鍵字類表示抽象方法 或抽象類

何時使用抽象類:

當父類須要規定子類應該具有某些方法,但父類自己不清楚方法具體如何實現時使用抽象類,

用於提早告訴子類,你應該具有哪些方法

特色:

存在抽象方法的類沒法直接實例化產生對象,必須由子類繼承並實現全部抽象方法才能實例化子類

如子類沒有實現全部抽象方法,那麼子類也只能做爲抽象類

實例:

abstract class Person{
         abstract public void show();
}

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

注意:

1.若是設計子類的人清楚的知道本身應該作的事情時(方法),能夠不用抽象類,抽象類本質就是限制子類必須怎麼怎麼滴

2.須要注意final 不能與 abstract 同時出現

interface 接口

接口是一組功能(方法)的定義

接口的做用:

定義一組協議方法,讓子類遵循協議中的要求去實現具體的操做,對於使用者而言,只須要掌握接口中定義的方法的使用便可,無需關心,具體是哪個類實現的,更不用關心是如何實現的, 這將類的設計者和類的使用者之間的耦合度大大的下降了,

定義語法:

interface Name {
  
}

特色:

接口本質是一個類,可是與普通類有着衆多區別

1.接口中的方法默認都是抽象的不能有方法體

2.接口中定義的變量默認都是靜態常量

3.接口中的成員必然是public修飾的,即時沒有明確聲明

4.接口沒法直接實例化

5.接口能夠繼承一個或多個其餘接口

6,一個類能夠同時實現多個接口

JDK1.8以後

某些狀況下,接口中聲明瞭不少抽象方法,然而子類不須要實現所有,而是想僅實現部分須要的,這種狀況在1.8以前是不容許的,除非把這個子類變成抽象的,這不夠靈活;

  • 1.8以後,增長了新的方法修飾符,default,能夠在接口中直接編寫方法體,被default修飾的方法,子類能夠選擇性的重寫
  • 新增特性,能夠爲方法加上static修飾符

新特性的測試

案例:

package com.yh.src;

interface HDMI {
    static int a = 0;
    // 上述代碼等價於public static final int a = 0;

    //抽象方法
    void generalMethod();
    //上述代碼等價於public void test();

    //默認方法 子類可選重寫
    default void defaultMethod(){
        System.out.println("默認的方法體");
    }
    //靜態方法 子類不可重寫
    static void staticMethod(){
        System.out.println("靜態方法體");
    }
}

class AA implements HDMI {
    @Override
    public void generalMethod() {
        System.out.println("AA Implements generalMethod!");
        HDMI.staticMethod();//靜態方法只能有接口名稱調用
    }

    @Override
    public void defaultMethod() {
        //super.test1();//沒法經過super調用接口中的默認方法
        HDMI.super.defaultMethod();//須要使用接口名稱.super來調用
    }
}

public class InterfaceRunner {
    public static void main(String[] args) {
        AA a = new AA();
        a.generalMethod();
        a.defaultMethod();//調用未被重寫的默認方法
        //a.staticMethod();//沒法直接調用接口中的靜態方法
        HDMI.staticMethod();//靜態方法只能由接口名稱調用

    }
}
default方法總結:
  • default方法子類能夠選擇是否重寫,調用順序與普通繼承相同,優先調用對象重寫的方法,
  • 當須要在重寫方法中調用接口中的默認實現時使用接口名稱.super.方法名稱HDMI.super.defaultMethod();
static方法總結:
  • static方法沒法被重寫,也不能使用super調用,不管在什麼位置只能使用接口名稱來調用

PS:其實取消抽象方法和接口同樣能夠實現設計-使用鬆耦合,直接使用普通類做爲父類,子類本身實現該實現的方法,固然了java爲何佔領企業開發也正是由於其嚴謹,標準規範,

你會發現,爲了提升靈活性接口好像變得和抽象類很是類似,.......乾脆像python同樣別整這麼多約束,,,

接口多實現

與普通類不一樣的是一個類能夠同時實現多個接口,增長了子類的擴展性

可是也帶來了訪問不肯定性,

  • 狀況1:多個接口存在相同的方法(名稱與參數列表相同)
interface IA{
    default void test(){
        System.out.println("IA test!");
    }
}

interface IB{
    default void test(){
        System.out.println("IB test!");
    }
}
//實現類直接編譯失敗,
class IMA implements IA,IB{

}
解決方案:
//在子類中重寫衝突的方法,便可 當須要調用默認方法時,使用`接口名稱.super.xx`來調用
class IMA implements IA,IB{

    @Override
    public void test() {
        IA.super.test();
        IB.super.test();
    }
}
  • 狀況2:父類中存在相同的方法
interface IA{
      default void test(){
          System.out.println("IA test!");
      }
  }
  
  interface IB{
      default void test(){
          System.out.println("IB test!");
      }
  }
  
  class P{
      public void test(){
          System.out.println("IB test!");
      }
  }
  
  class IMA extends P implements IA,IB{
  }

​ 這種狀況下不會出現問題,將直接調用父類中的方法

  • 狀況3:多個接口中出現了重複的常量
interface IA{
    int a =10;
    int b =100;
}

interface IB{
    int a =20;
}

class P{
    int a = 20000;
}

class IMA extends P implements IA,IB{
    public void  func(){
        System.out.println(a);//編譯錯誤
        System.out.println(IA.a);//使用接口名稱來明確
        System.out.println(super.a);//使super來指定訪問父類
        System.out.println(b);//不存在重複的時直接能夠訪問
        
    }
}

此時子類沒法明確選擇一個要訪問的常量,必須使用接口名稱來明確

要訪問父類時使用super來明確

補充:

子類對象能夠直接訪問接口中沒有衝突的靜態變量,可是沒法直接訪問靜態方法須要使用接口名稱調用

===================================

內部類

什麼是內部類:

當一個類定義在一個類的範圍中,則這個類稱之爲內部類,與之對應的是包含這個內部類的外部類

普通內部類(較少用)

實例:

class A{//外部類
  class B{ //內部類
    }
}

實例化內部類

public class InnerClass {
    public static void main(String[] args) {
        //實例化內部類方法1
        A.B obj; //定義
        obj = new A().new B();
        //實例化內部類方法2
        A aobj = new A();
        obj = aobj.getInner();
        //實例化內部類方法3
        obj = aobj.new B();
    }
}
class A{
    public B getInner() {
        return new B();
    }
    class B{
    }
}

內部類訪問外部類的成員

public class InnerClass {
    public static void main(String[] args) {
        //實例化內部類方法1
        A.B obj; //定義
        obj = new A().new B();//實例化
        obj.test();//調用方法訪問外部類的成員
    }
}
class A{
    int inta = 100;
    int intb = 200;
    public B getInner() {
        return new B();
    }
    class B{
        int intb = 300;
        public void test(){
            System.out.println(inta);//100 能夠直接訪問外部類成員
            System.out.println(intb);//300 衝突時優先訪問內部
            System.out.println(A.this.intb);//200 衝突時指定訪問外部
        }
    }
}

外部類訪問內部類成員

class A{
    int inta = 100;
    int intb = 200;
    public void accessInner() {
        B b = new B();//實例化
        b.test();//訪問
    }

    class B{
        int intb = 300;
        public void test(){
            System.out.println(inta);
            System.out.println(intb);
            System.out.println(A.this.intb);
        }
    }
}

外部類訪問內部類成員時須要先實例化內部類的對象,經過對象訪問便可

注意:普通內部類中不能包含任何static修飾的成員

靜態內部類 (不經常使用)

能夠將內部類使用static修飾,此時他就是靜態內部類

特色:

  • 與普通內部類的不一樣之處,與static修飾的其餘成員有着相同的特徵:

  • 在類被加載時就一塊兒加載,因此,能夠直接實例化,不須要先實例化外部類;

  • 一樣的在靜態內部類中不能夠直接訪問非靜態修飾的外部類成員,也不能夠訪問外部類的this;
  • 靜態內部類中的能夠包含靜態成員而且也能夠直接調用(外部類.內部類.屬性);普通類中不能夠有靜態成員;

實例:

public class InnerClass2 {
    public static void main(String[] args) {
        System.out.println(OUT.IN.a);//直接訪問靜態內部類的靜態成員
        OUT.IN.test();//訪問靜態內部類方法
    }
}

class OUT{
    static int a = 10;
    String name = "jack";
    static class IN{
        static int a = 100;
        static public void test(){
            System.out.println(a);//優先訪問內部類
            System.out.println(OUT.a);//指定訪問外部類
            System.out.println(new OUT().name);//訪問外部非靜態成員
        }
    }
}

局部內部類 (不經常使用)

定義在方法中的類稱爲局部內部類

意義不大..沒啥用..略過了

class A{
    public void test(){
    class B{
      //局部內部類
        }
  }
}

特色:不容許使用static修飾局部內部類,和其中的任何成員,要使用該類必須做爲返回值return出去

匿名內部類 (很經常使用)

什麼是匿名內部類

字面意思就是沒有名字的類

經過new 實例化接口或是抽象類,同時提供相應的方法實現

實例:

interface USB{
  void open();
}

class PC {
    public void working(){
        //此處須要一個實現了USB接口的對象來完成操做
    //咱們能夠定義一個類實現USB接口先下面的A類同樣
    //而後實例化A對象來完成OPEN操做
    new A().open();
        //咱們也可使用匿名內部類的方式來簡化操做 
    new USB(){
              public void open(){
            System.out.println("OPEN......");
        }
    }.open;
  }
}
class A implements USB{
  public void open(){
    System.out.println("OPEN......");
  }
}

在上面的例子中咱們能夠看出匿名內部類有如下優勢:

  • 代碼更加緊湊

  • 使用更便捷

固然匿名內部類的缺點也很明顯:

  • 這個類產生的對象也是沒有名字的,只能在定義的地方使用一次
  • 語法格式看起來比較亂

何時使用匿名內部類

1.臨時須要某個接口或是抽象類的獨享完成某個功能

2.當整個任務的部分代碼已經完成,可是剩下一部分關鍵代碼須要由使用這個功能的人來完成

案例:

PC類的working方法須要傳入一個USB接口對象才能完成整個任務

interface USB{
    void open();
}
public class PC {
    public static void main(String[] args) {
        new PC().working(new AP());//之前的方式:定義類實現方法而後實例化
        new PC().working(new USB() {//匿名內部類的方式:在當前位置完成上述所有操做
            @Override
            public void open() {
                System.out.println("open on NonNameClass");
            }
        });
    }
    public void working(USB usb){
        usb.open();
    }
}
class AP implements USB{
    public void open(){
        System.out.println("OPEN......on AP");
    }
}

PS:我的以爲匿名內部類+匿名對象其實 與OC中的block,python中的函數對象要完成的事情是同樣的,即方法的使用者實現部分代碼,而後做爲參數傳給某個方法(本質就是回調機制),可是JAVA中定義方法必須藉助類,因此纔有了匿名內部類這麼一說,歸根結底是由於JAVA強制面向對象致使的;

補充:匿名內部類中可使用構造代碼塊

代碼設計規範----單一功能職責:

有且只有一個引發功能變化的緣由
若是在一個類當中,承擔的職責越多,耦合度越高,這意味着這個類的重用性越低
相關文章
相關標籤/搜索