static、接口、多態、內部類

第一章 static關鍵字

知識點-- static關鍵字

目標:

  • 以前咋們寫main方法的時候,使用過了一個static關鍵字,接下來咱們來學習一下static關鍵字java

路徑:

  • static關鍵字概述程序員

  • static關鍵字的使用數組

講解:

1.1 概述

static是靜態修飾符,表示靜態的意思,能夠修飾成員變量和成員方法以及代碼塊。ide

1.2 定義和使用格式

static修飾成員變量

static 修飾成員變量時,該變量稱爲類變量。該類的每一個對象都共享同一個類變量的值。任何對象均可以更改該類變量的值,但也能夠在不建立該類的對象的狀況下對類變量進行操做。工具

定義格式:學習

static 數據類型 變量名; 

舉例:測試

static String room;

好比說,同窗們來黑馬程序員學校學習,那麼咱們全部同窗的學校都是黑馬程序員, 不因每一個同窗不一樣而不一樣。this

因此,咱們能夠這樣定義一個靜態變量school,代碼以下:atom

public class Student {
 private String name;
 private int age;
 // 類變量,記錄學生學習的學校
 public static String school = "黑馬程序員學校";

 public Student(String name, int age){
   this.name = name;
   this.age = age;    
}

 // 打印屬性值
 public void show() {
   System.out.println("name=" + name + ", age=" + age + ", shool=" + shool );
}
}

public class StuDemo {
 public static void main(String[] args) {
   Student s1 = new Student("張三", 23);
   Student s2 = new Student("李四", 24);
   Student s3 = new Student("王五", 25);
   Student s4 = new Student("趙六", 26);

   s1.show(); // Student : name=張三, age=23, shool=學校
   s2.show(); // Student : name=李四, age=24, shool=學校
   s3.show(); // Student : name=王五, age=25, shool=學校
   s4.show(); // Student : name=趙六, age=26, shool=學校
}
}
static修飾成員方法

static 修飾成員方法時,該方法稱爲類方法 。靜態方法在聲明中有static ,建議使用類名來調用,而不須要建立類的對象。調用方式很是簡單。spa

  • 類方法:使用 static關鍵字修飾的成員方法,習慣稱爲靜態方法

定義格式:

修飾符 static 返回值類型 方法名 (參數列表){ 
// 執行語句
}

舉例:在Student類中定義靜態方法

public static void showNum() {
 System.out.println("num:" +  numberOfStudent);
}
  • 靜態方法調用的注意事項:

    • 靜態方法能夠直接訪問類變量和靜態方法。

    • 靜態方法不能直接訪問普通成員變量或成員方法。成員方法能夠直接訪問類變量或靜態方法。

    • 靜態方法中,不能使用this關鍵字。

小貼士:靜態方法只能訪問靜態成員。

類變量和類方法的使用

被static修飾的成員能夠而且建議經過類名直接訪問。雖然也能夠經過對象名訪問靜態成員,緣由即多個對象均屬於一個類,共享使用同一個靜態成員,可是不建議,會出現警告信息。

格式:

// 訪問類變量
類名.類變量名;

// 調用靜態方法
類名.靜態方法名(參數)

調用演示,代碼以下:

public class StuDemo2 {
 public static void main(String[] args) {      
   // 訪問類變量
   System.out.println(Student.numberOfStudent);
   // 調用靜態方法
   Student.showNum();
}
}

小結:static修飾的內容是屬於類的,能夠經過類名直接訪問

static修飾代碼塊

概述 : 被static修飾的代碼塊,就叫作靜態代碼塊

靜態代碼塊:定義在成員位置,使用static修飾的代碼塊{ }。

  • 位置:類中方法外。

  • 執行:隨着類的加載而執行且執行一次,優先於main方法和構造方法的執行。

格式:

static {
// 靜態代碼塊
}

例如:


public class Student {
   static {
       // 靜態代碼塊
       System.out.println("靜態代碼塊執行了...");
  }

   public Student() {
       System.out.println("Student類的空參構造方法執行了...");
  }
}

public class StaticDemo {

   static {
       // 靜態代碼塊
       System.out.println("main方法所在的類中的靜態代碼塊執行了...");
  }

   public static void main(String[] args) {
       /*
           靜態代碼塊:定義在成員位置,使用static修飾的代碼塊{ }。
               - 位置:類中方法外。
               - 執行:隨着類的加載而執行且執行一次,優先於main方法和構造方法的執行。

        */
       System.out.println("main方法中的代碼...");
       // new Student();
       // new Student();
  }
}
之後開發中static的應用

之後的項目中,一般會須要一些「全局變量」或者「全局的工具方法」,這些全局變量和方法,能夠單獨定義在一個類中,並聲明爲static(靜態)的,能夠很方便的經過類名訪問

例如:

public class Utils{
   public static double pi = 3.14159;
   public static int getMax(int[] arr) {
       int max = arr[0];
       for (int i = 0; i < arr.length; i++) {
           max = arr[i] > max ? arr[i] : max;

      }
       return max;
  }
}

public class Demo {
   public static void main(String[] args) {
       System.out.println("全局的PI值:" + Utils.pi);
       int[] arr = {1, 443243, 4, 3243, 4324};
       int max = Utils.getMax(arr);
       System.out.println("最大值:" + max);

  }
}

小結:

第二章 接口

知識點-- 概述

目標:

  • 引用數據類型除了類其實還有接口,接下來學習接口的概述

路徑:

  • 接口的概述

講解:

接口,是Java語言中一種引用類型,是方法的集合,若是說類的內部封裝了成員變量、構造方法和成員方法,那麼接口的內部主要就是封裝了方法,包含抽象方法(JDK 7及之前),默認方法和靜態方法(JDK 8)。

接口的定義,它與定義類方式類似,可是使用 interface 關鍵字。它也會被編譯成.class文件,但必定要明確它並非類,而是另一種引用數據類型。

public class 類名.java-->.class

public interface 接口名.java-->.class

引用數據類型:數組,類,接口。

接口的使用,它不能建立對象,可是能夠被實現(implements ,相似於被繼承)。一個實現接口的類(能夠看作是接口的子類),須要實現接口中全部的抽象方法,建立該類對象,就能夠調用方法了,不然它必須是一個抽象類。

小結:

知識點-- 定義格式

目標:

  • 如何定義一個接口

路徑:

  • 定義格式的格式

講解:

public interface 接口名稱 {
   // 常量
   // 抽象方法
   // 默認方法
   // 靜態方法
}

含有常量

接口中的常量: 使用 public static final修飾,能夠省略

例如:

public static final int NUM = 10;// NUM就是一個常量

含有抽象方法

抽象方法:使用abstract 關鍵字修飾,能夠省略,沒有方法體。該方法供子類實現使用。

代碼以下:

public interface InterFaceName {
   public abstract void method();
}

含有默認方法和靜態方法

默認方法:使用 default 修飾,不可省略,供子類調用或者子類重寫。

靜態方法:使用 static 修飾,供接口直接調用。

代碼以下:

public interface InterFaceName {
   public default void method() {
       // 執行語句
  }
   public static void method2() {
       // 執行語句    
  }
}

小結:定義接口時就是將定義類的class改爲了interface,而且接口中的內容也有了一些變化。

小結:

  • 接口定義的格式

知識點-- 基本的實現

目標:

  • 如何建立已定義好的接口類型的對象呢?

路徑:

  • 實現的概述

  • 抽象方法的使用

  • 默認方法的使用

  • 靜態方法的使用

講解:

實現的概述

類與接口的關係爲實現關係,即類實現接口,該類能夠稱爲接口的實現類,也能夠稱爲接口的子類。實現的動做相似繼承,格式相仿,只是關鍵字不一樣,實現使用 implements關鍵字。

非抽象子類實現接口:

  1. 必須重寫接口中全部抽象方法。

  2. 繼承了接口的默認方法,便可以直接調用,也能夠重寫。

抽象子類實現接口: 能夠無需重寫接口中的抽象方法

實現格式:

class 類名 implements 接口名 {
   // 重寫接口中抽象方法【必須】
// 重寫接口中默認方法【可選】
}

抽象方法的使用

必須所有實現,代碼以下:

定義接口:

public interface LiveAble {
   // 定義抽象方法
   public abstract void eat();
   public abstract void sleep();
}

定義實現類:

public class Animal implements LiveAble {
   @Override
   public void eat() {
       System.out.println("吃東西");
  }

   @Override
   public void sleep() {
       System.out.println("晚上睡");
  }
}

定義測試類:

public class InterfaceDemo {
   public static void main(String[] args) {
       // 建立子類對象  
       Animal a = new Animal();
       // 調用實現後的方法
       a.eat();
       a.sleep();
  }
}
輸出結果:
吃東西
晚上睡

默認方法的使用

能夠繼承,能夠重寫,二選一,可是隻能經過實現類的對象來調用。

  1. 繼承默認方法,代碼以下:

定義接口:

public interface LiveAble {
   public default void fly(){
       System.out.println("天上飛");
  }
}

定義實現類:

public class Animal implements LiveAble {
// 繼承,什麼都不用寫,直接調用
}

定義測試類:

public class InterfaceDemo {
   public static void main(String[] args) {
       // 建立子類對象  
       Animal a = new Animal();
       // 調用默認方法
       a.fly();
  }
}
輸出結果:
天上飛
  1. 重寫默認方法,代碼以下:

定義接口:

public interface LiveAble {
   public default void fly(){
       System.out.println("天上飛");
  }
}

定義實現類:

public class Animal implements LiveAble {
   @Override
   public void fly() {
       System.out.println("自由自在的飛");
  }
}

定義測試類:

public class InterfaceDemo {
   public static void main(String[] args) {
       // 建立子類對象  
       Animal a = new Animal();
       // 調用重寫方法
       a.fly();
  }
}
輸出結果:
自由自在的飛

靜態方法的使用

靜態與.class 文件相關,只能使用接口名調用,不能夠經過實現類的類名或者實現類的對象調用,代碼以下:

定義接口:

public interface LiveAble {
   public static void run(){
       System.out.println("跑起來~~~");
  }
}

定義實現類:

public class Animal implements LiveAble {
// 沒法重寫靜態方法
}

定義測試類:

public class InterfaceDemo {
   public static void main(String[] args) {
       // Animal.run(); // 【錯誤】沒法繼承方法,也沒法調用
       LiveAble.run(); //
  }
}
輸出結果:
跑起來~~~

小結: 類實現接口使用的是implements關鍵字,而且一個普通類實現接口,必需要重寫接口中的全部的抽象方法

小結:

知識點-- 接口的多實現

目標:

  • 以前學過,在繼承體系中,一個類只能繼承一個父類。而對於接口而言,一個類是能夠實現多個接口的,這叫作接口的多實現。而且,一個類能繼承一個父類,同時實現多個接口。

路徑:

  • 抽象方法

  • 默認方法

  • 靜態方法

  • 父類中的成員方法與接口中的默認方法重名優先級問題

講解:

實現格式:

class 類名 [extends 父類名] implements 接口名1,接口名2,接口名3... {
   // 重寫接口中抽象方法【必須】
// 重寫接口中默認方法【不重名時可選】
}

[ ]: 表示可選操做。

抽象方法

接口中,有多個抽象方法時,實現類必須重寫全部抽象方法。若是抽象方法有重名的,只須要重寫一次。代碼以下:

定義多個接口:

interface A {
   public abstract void showA();
   public abstract void show();
}

interface B {
   public abstract void showB();
   public abstract void show();
}

定義實現類:

public class C implements A,B{
   @Override
   public void showA() {
       System.out.println("showA");
  }

   @Override
   public void showB() {
       System.out.println("showB");
  }

   @Override
   public void show() {
       System.out.println("show");
  }
}

默認方法

接口中,有多個默認方法時,實現類均可繼承使用。若是默認方法有重名的,必須重寫一次。代碼以下:

定義多個接口:

interface A {
   public default void methodA(){}
   public default void method(){}
}

interface B {
   public default void methodB(){}
   public default void method(){}
}

定義實現類:

public class C implements A,B{
   @Override
   public void method() {
       System.out.println("method");
  }
}

靜態方法

接口中,存在同名的靜態方法並不會衝突,緣由是隻能經過各自接口名訪問靜態方法。

優先級的問題

當一個類,既繼承一個父類,又實現若干個接口時,父類中的成員方法與接口中的默認方法重名,子類就近選擇執行父類的成員方法。代碼以下:

定義接口:

interface A {
   public default void methodA(){
       System.out.println("AAAAAAAAAAAA");
  }
}

定義父類:

class D {
   public void methodA(){
       System.out.println("DDDDDDDDDDDD");
  }
}

定義子類:

class C extends D implements A {
// 未重寫methodA方法
}

定義測試類:

public class Test {
   public static void main(String[] args) {
       C c = new C();
       c.methodA();
  }
}
輸出結果:
DDDDDDDDDDDD

小結: 一個類能夠實現多個接口,多個接口之間使用逗號隔開便可。

小結:

知識點-- 接口的多繼承【瞭解】

目標:

  • 接口的多繼承

路徑:

  • 接口的多繼承

  • 其餘成員特色

講解:

一個接口能繼承另外一個或者多個接口,這和類之間的繼承比較類似。接口的繼承使用 extends 關鍵字,子接口繼承父接口的方法。若是父接口中的默認方法有重名的,那麼子接口須要重寫一次。代碼以下:

定義父接口:

interface A {
   public default void method(){
       System.out.println("AAAAAAAAAAAAAAAAAAA");
  }
}

interface B {
   public default void method(){
       System.out.println("BBBBBBBBBBBBBBBBBBB");
  }
}

定義子接口:

interface D extends A,B{
   @Override
   public default void method() {
       System.out.println("DDDDDDDDDDDDDD");
  }
}

小貼士:

子接口重寫默認方法時,default關鍵字能夠保留。

子類重寫默認方法時,default關鍵字不能夠保留。

小結:接口和接口之間是繼承的關係,而不是實現。一個接口能夠繼承多個接口。

小結:

知識點-- 其餘成員特色

  • 接口中,沒法定義成員變量,可是能夠定義常量,其值不能夠改變,默認使用public static final修飾。

  • 接口中,沒有構造方法,不能建立對象。

  • 接口中,沒有靜態代碼塊。

知識點-- 抽象類和接口的練習

目標:

  • 抽象類和接口的練習

路徑:

  • 案例描述

  • 案例分析

  • 案例代碼實現

講解:

經過實例進行分析和代碼演示抽象類和接口的用法。

一、舉例:

犬:

行爲:

吼叫;

吃飯;

緝毒犬:

行爲:

吼叫;

吃飯;

緝毒;

二、思考:

因爲犬分爲不少種類,他們吼叫和吃飯的方式不同,在描述的時候不能具體化,也就是吼叫和吃飯的行爲不能明確。當描述行爲時,行爲的具體動做不能明確,這時,能夠將這個行爲寫爲抽象行爲,那麼這個類也就是抽象類。

但是有的犬還有其餘額外功能,而這個功能並不在這個事物的體系中 , 例如 : 緝毒犬。緝毒的這個功能有好多種動物都有 , 例如 : 緝毒豬 , 緝毒鼠。咱們能夠將這個額外功能定義接口中 ,讓緝毒犬繼承犬且實現緝毒接口 , 這樣緝毒犬既具有犬科自身特色也有緝毒功能。

//定義緝毒接口 緝毒的詞組(anti-Narcotics)比較長,在此使用拼音替代
interface JiDu{
   //緝毒
public abstract void jiDu();
}
//定義犬科,存放共性功能
abstract class Dog{
   //吃飯
public abstract void eat();
   //吼叫
public abstract void roar();
}
//緝毒犬屬於犬科一種,讓其繼承犬科,獲取的犬科的特性,
//因爲緝毒犬具備緝毒功能,那麼它只要實現緝毒接口便可,這樣即保證緝毒犬具有犬科的特性,也擁有了緝毒的功能
class JiDuQuan extends Dog implements JiDu{
public void jiDu() {
}
void eat() {
}
void roar() {
}
}

//緝毒豬
class JiDuZhu implements JiDu{
public void jiDu() {
}
}

講完抽象類和接口後,相信有許多同窗會存有疑惑,二者的共性那麼多,只留其中一種不就好了,這裏就得知道抽象類和接口從根本上解決了哪些問題.

一個類只能繼承一個直接父類(多是抽象類),卻能夠實現多個接口, 接口彌補了Java的單繼承

抽象類爲繼承體系中的共性內容, 接口爲繼承體系中的擴展功能

接口仍是後面一個知識點的基礎(lambada)

小結:

第三章 多態

知識點-- 概述

標:

  • 多態的概述

路徑:

  • 引入

  • 概念

  • 造成多態的條件

講解:

引入

多態是繼封裝、繼承以後,面向對象的第三大特性。

生活中,好比跑的動做,小貓、小狗和大象,跑起來是不同的。再好比飛的動做,昆蟲、鳥類和飛機,飛起來也是不同的。可見,同一行爲,經過不一樣的事物,能夠體現出來的不一樣的形態。多態,描述的就是這樣的狀態。

定義

  • 多態: 是指同一行爲,具備多個不一樣表現形式。

前提【重點】

  1. 繼承或者實現【二選一】

  2. 方法的重寫【意義體現:不重寫,無心義】

  3. 父類引用指向子類對象【格式體現】

小結:

知識點-- 多態的體現

目標:

  • 如何實現多態

路徑:

  • 多態的體現

講解:

多態體現的格式:

父類類型 變量名 = new 子類對象;
變量名.方法名();

父類類型:指子類對象繼承的父類類型,或者實現的父接口類型。

代碼以下:

Fu f = new Zi();
f.method();

當使用多態方式調用方法時,首先檢查父類中是否有該方法,若是沒有,則編譯錯誤;若是有,執行的是子類重寫後方法。

代碼以下:

定義父類:

public abstract class Animal {  
   public abstract void eat();  
}  

定義子類:

class Cat extends Animal {  
   public void eat() {  
       System.out.println("吃魚");  
  }  
}  

class Dog extends Animal {  
   public void eat() {  
       System.out.println("吃骨頭");  
  }  
}

定義測試類:

public class Test {
   public static void main(String[] args) {
       // 多態形式,建立對象
       Animal a1 = new Cat();  
       // 調用的是 Cat 的 eat
       a1.eat();          

       // 多態形式,建立對象
       Animal a2 = new Dog();
       // 調用的是 Dog 的 eat
       a2.eat();              
  }  
}

多態在代碼中的體現爲父類引用指向子類對象。

小結:

知識點-- 多態時訪問成員的特色

目標

  • 掌握多態時訪問成員的特色

路徑:

  • 多態時成員變量的訪問特色

  • 多態時成員方法的訪問特色

講解:

  • 多態時成員變量的訪問特色

    • 編譯看左邊,運行看左邊

      • 簡而言之:多態的狀況下,訪問的是父類的成員變量

  • 多態時成員方法的訪問特色

    • 非靜態方法:編譯看左邊,運行看右邊

      • 簡而言之:編譯的時候去父類中查找方法,運行的時候去子類中查找方法來執行

    • 靜態方法:編譯看左邊,運行看左邊

      • 簡而言之:編譯的時候去父類中查找方法,運行的時候去父類中查找方法來執行

  • 注意:多態的狀況下是沒法訪問子類獨有的方法

  • 演示代碼:

public class Demo1 {
   public static void main(String[] args) {
       // 父類的引用指向子類的對象
       Animal anl1 = new Dog();
       // 訪問非靜態方法
       anl1.eat();

       // 訪問成員變量num
       System.out.println(anl1.num);//10

       // 訪問靜態方法
       anl1.sleep();

       // 多態想要調用子類中獨有的方法
       // anl1.lookHome(); 錯誤的,沒法訪問 多態的弊端:沒法訪問子類獨有的方法

  }
}

public class Animal {

   int num = 10;

   public void eat(){
       System.out.println("吃東西...");
  }

   public static void sleep(){
       System.out.println("Animal類中的睡覺方法...");
  }

}

public class Dog extends Animal {

   int num = 20;

  // 重寫
   public void eat() {
       System.out.println("狗吃骨頭");
  }

   public static void sleep(){
       System.out.println("Dog類中的睡覺方法...");
  }

   public void lookHome(){
       System.out.println("狗正在看家...");
  }

}

小結:

知識點-- 多態的幾種表現形式

目標:

  • 多態的幾種表現形式

路徑:

  • 普通父類多態

  • 抽象父類多態

  • 父接口多態

講解:

  • 多態的表現形式:

    • 普通父類多態

      public class Fu{}
      public class Zi extends Fu{}
      public static void main(String[] args){
      Fu f = new Zi();//左邊是一個「父類」
      }
    • 抽象父類多態

      public abstract class Fu{}
      public class Zi extends Fu{}
      public static void main(String[] args){
      Fu f = new Zi();//左邊是一個「父類」
      }
    • 父接口多態

    public interface A{}
    public class AImp implements A{}
    public static void main(String[] args){
    A a = new AImp();
    }

小結:

知識點-- 多態的應用場景:

目標:

  • 掌握多態在開發中的應用場景

路徑:

  • 變量多態

  • 形參多態

  • 返回值多態

講解:

定義一個Animal類,讓Dog和Cat類繼承Animal類:

public class Animal {

   public void eat(){
       System.out.println("吃東西...");
  }


}
public class Cat extends Animal {
   @Override
   public void eat() {
       System.out.println("貓吃魚...");
  }
}

public class Dog extends Animal {
   @Override
   public void eat() {
       System.out.println("狗吃骨頭...");
  }
}

多態的幾種應用場景:

public class Demo1 {
   public static void main(String[] args) {
       /*
           多態的應用場景:
               1.變量多態
               2.形參多態
               3.返回值多態
        */
       // 1.變量多態
       Animal anl = new Dog();
       anl.eat();

       // 2.形參多態
       Dog dog = new Dog();
       invokeEat(dog);

       Cat cat = new Cat();
       invokeEat(cat);// 實參賦值給形參: Animal anl = new Cat();

       // 3.返回值多態
       Animal anl2 = getAnimal();// 返回值賦值給變量: Animal anl2 = new Dog()
  }

   // 3.返回值多態
   // 結論:若是方法的返回值類型爲父類類型,那麼就能夠返回該父類對象以及其全部子類對象
   public static Animal getAnimal(){
//       return new Animal();
       return new Dog();
//       return new Cat();
  }

   // 形參多態: 當你調用invokeEat方法的時候,傳入Animal類的子類對象
   // Animal anl = dog; ====> Animal anl = new Dog();
   // 結論:若是方法的參數是父類類型,那麼就能夠接收全部該父類對象以及其全部子類對象
   // Object類:是java中全部類的父類,因此若是參數爲Object類型,那麼就能夠傳入一切類的對象
   public static void invokeEat(Animal anl){
       anl.eat();
  }

}

小結:

知識點-- 多態的好處和弊端

目標:

  • 實際開發的過程當中,父類類型做爲方法形式參數,傳遞子類對象給方法,進行方法的調用,更能體現出多態的擴展性與便利。

步驟:

  • 多態的好處和弊端

講解:

代碼以下:

定義父類:

public abstract class Animal {  
   public abstract void eat();  
}  

定義子類:

class Cat extends Animal {  
   public void eat() {  
       System.out.println("吃魚");  
  }  
}  

class Dog extends Animal {  
   public void eat() {  
       System.out.println("吃骨頭");  
  }  
}

定義測試類:

public class Test {
   public static void main(String[] args) {
       // 多態形式,建立對象
       Cat c = new Cat();  
       Dog d = new Dog();

       // 調用showCatEat
       showCatEat(c);
       // 調用showDogEat
       showDogEat(d);

       /*
       以上兩個方法, 都可以被showAnimalEat(Animal a)方法所替代
       而執行效果一致
       */
       showAnimalEat(c);
       showAnimalEat(d);
  }

   public static void showCatEat (Cat c){
       c.eat();
  }

   public static void showDogEat (Dog d){
       d.eat();
  }

   public static void showAnimalEat (Animal a){
       a.eat();
  }
}

因爲多態特性的支持,showAnimalEat方法的Animal類型,是Cat和Dog的父類類型,父類類型接收子類對象,固然能夠把Cat對象和Dog對象,傳遞給方法。

當eat方法執行時,多態規定,執行的是子類重寫的方法,那麼效果天然與showCatEat、showDogEat方法一致,因此showAnimalEat徹底能夠替代以上兩方法。

不只僅是替代,在擴展性方面,不管以後再多的子類出現,咱們都不須要編寫showXxxEat方法了,直接使用showAnimalEat均可以完成。

因此,多態的好處,體如今,可使程序編寫的更簡單,並有良好的擴展。

小結:多態的好處是提升程序的靈活性,擴展性

多態的弊端: 沒法訪問子類的獨有方法

 

知識點-- 引用類型轉換

目標:

  • 多態的轉型分爲向上轉型與向下轉型

步驟:

  • 向上轉型

  • 向下轉型

講解:

向上轉型

  • 向上轉型:多態自己是子類類型向父類類型向上轉換的過程,這個過程是默認的。

當父類引用指向一個子類對象時,即是向上轉型。

使用格式:

父類類型  變量名 = new 子類類型();
如:Animal a = new Cat();

向下轉型

  • 向下轉型:父類類型向子類類型向下轉換的過程,這個過程是強制的。

一個已經向上轉型的子類對象,將父類引用轉爲子類引用,可使用強制類型轉換的格式,即是向下轉型。

使用格式:

子類類型 變量名 = (子類類型) 父類變量名;
如:Cat c =(Cat) a;  

爲何要轉型

當使用多態方式調用方法時,首先檢查父類中是否有該方法,若是沒有,則編譯錯誤。也就是說,不能調用子類有而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態給咱們帶來的一點"小麻煩"。因此,想要調用子類特有的方法,必須作向下轉型。

轉型演示,代碼以下:

定義類:

abstract class Animal {  
   abstract void eat();  
}  

class Cat extends Animal {  
   public void eat() {  
       System.out.println("吃魚");  
  }  
   public void catchMouse() {  
       System.out.println("抓老鼠");  
  }  
}  

class Dog extends Animal {  
   public void eat() {  
       System.out.println("吃骨頭");  
  }  
   public void watchHouse() {  
       System.out.println("看家");  
  }  
}

定義測試類:

public class Test {
   public static void main(String[] args) {
       // 向上轉型  
       Animal a = new Cat();  
       a.eat(); // 調用的是 Cat 的 eat

       // 向下轉型  
       Cat c = (Cat)a;      
       c.catchMouse(); // 調用的是 Cat 的 catchMouse
  }  
}

轉型的異常

轉型的過程當中,一不當心就會遇到這樣的問題,請看以下代碼:

public class Test {
   public static void main(String[] args) {
       // 向上轉型  
       Animal a = new Cat();  
       a.eat();               // 調用的是 Cat 的 eat

       // 向下轉型  
       Dog d = (Dog)a;      
       d.watchHouse();        // 調用的是 Dog 的 watchHouse 【運行報錯】
  }  
}

這段代碼能夠經過編譯,可是運行時,卻報出了 ClassCastException ,類型轉換異常!這是由於,明明建立了Cat類型對象,運行時,固然不能轉換成Dog對象的。這兩個類型並無任何繼承關係,不符合類型轉換的定義。

爲了不ClassCastException的發生,Java提供了 instanceof 關鍵字,給引用變量作類型的校驗,格式以下:

變量名 instanceof 數據類型 
若是變量屬於該數據類型,返回true。
若是變量不屬於該數據類型,返回false。

因此,轉換前,咱們最好先作一個判斷,代碼以下:

public class Test {
   public static void main(String[] args) {
       // 向上轉型  
       Animal a = new Cat();  
       a.eat();               // 調用的是 Cat 的 eat

       // 向下轉型  
       if (a instanceof Cat){
           Cat c = (Cat)a;      
           c.catchMouse();        // 調用的是 Cat 的 catchMouse
      } else if (a instanceof Dog){
           Dog d = (Dog)a;      
           d.watchHouse();       // 調用的是 Dog 的 watchHouse
      }
  }  
}

小結:多態向上轉型是將子類類型轉成父類類型,多態向下轉型是將父類類型轉成子類類型。

 

第四章 內部類

知識點-- 內部類

目標:

  • 內部類的概述

步驟:

  • 什麼是內部類

  • 成員內部類的格式

  • 成員內部類的訪問特色

講解:

什麼是內部類

將一個類A定義在另外一個類B裏面,裏面的那個類A就稱爲內部類,B則稱爲外部類

成員內部類

  • 成員內部類 :定義在類中方法外的類。

定義格式:

class 外部類 {
   class 內部類{
  }
}

在描述事物時,若一個事物內部還包含其餘事物,就可使用內部類這種結構。好比,汽車類Car 中包含發動機類Engine ,這時,Engine就可使用內部類來描述,定義在成員位置。

代碼舉例:

class Car { //外部類
   class Engine { //內部類

  }
}

訪問特色

  • 內部類能夠直接訪問外部類的成員,包括私有成員。

  • 外部類要訪問內部類的成員,必需要創建內部類的對象。

建立內部類對象格式:

外部類名.內部類名 對象名 = new 外部類型().new 內部類型()

訪問演示,代碼以下:

定義類:

public class Person {
   private  boolean live = true;
   class Heart {
       public void jump() {
           // 直接訪問外部類成員
           if (live) {
               System.out.println("心臟在跳動");
          } else {
               System.out.println("心臟不跳了");
          }
      }
  }

   public boolean isLive() {
       return live;
  }

   public void setLive(boolean live) {
       this.live = live;
  }

}

定義測試類:

public class InnerDemo {
   public static void main(String[] args) {
       // 建立外部類對象
       Person p  = new Person();
       // 建立內部類對象
       Person.Heart heart = p.new Heart();
       // 調用內部類方法
       heart.jump();
       // 調用外部類方法
       p.setLive(false);
       // 調用內部類方法
       heart.jump();
  }
}
輸出結果:
心臟在跳動
心臟不跳了

內部類仍然是一個獨立的類,在編譯以後會內部類會被編譯成獨立的.class文件,可是前面冠之外部類的類名和$符號 。

好比,Person$Heart.class

小結:內部類是定義在一個類中的類。

 

知識點-- 匿名內部類

目標:

  • 匿名內部類

步驟:

  • 匿名內部類的概述

  • 匿名內部類的格式

講解:

  • 匿名內部類 :是內部類的簡化寫法。它的本質是一個帶具體實現的 父類或者父接口的 匿名的 子類對象

開發中,最經常使用到的內部類就是匿名內部類了。以接口舉例,當你使用一個接口時,彷佛得作以下幾步操做,

  1. 定義子類

  2. 重寫接口中的方法

  3. 建立子類對象

  4. 調用重寫後的方法

咱們的目的,最終只是爲了調用方法,那麼能不能簡化一下,把以上四步合成一步呢?匿名內部類就是作這樣的快捷方式。

前提

存在一個類或者接口,這裏的類能夠是具體類也能夠是抽象類

格式

new 父類名或者接口名(){
   // 方法重寫
   @Override
   public void method() {
       // 執行語句
  }
};

使用方式

以接口爲例,匿名內部類的使用,代碼以下:

定義接口:

public abstract class FlyAble{
   public abstract void fly();
}

匿名內部類能夠經過多態的形式接受

public class InnerDemo01 {
   public static void main(String[] args) {
       /*
      1.等號右邊:定義並建立該接口的子類對象
      2.等號左邊:是多態,接口類型引用指向子類對象
      */
       FlyAble  f = new FlyAble(){
           public void fly() {
               System.out.println("我飛了~~~");
          }
      };
  }
}

匿名內部類直接調用方法

public class InnerDemo02 {
   public static void main(String[] args) {
       /*
      1.等號右邊:定義並建立該接口的子類對象
      2.等號左邊:是多態,接口類型引用指向子類對象
      */
      new FlyAble(){
           public void fly() {
               System.out.println("我飛了~~~");
          }
      }.fly();
  }
}

方法的形式參數是接口或者抽象類時,也能夠將匿名內部類做爲參數傳遞

public class InnerDemo3 {
   public static void main(String[] args) {
       /*
       1.等號右邊:定義並建立該接口的子類對象
       2.等號左邊:是多態,接口類型引用指向子類對象
      */
       FlyAble  f = new FlyAble(){
           public void fly() {
               System.out.println("我飛了~~~");
          }
      };
       // 將f傳遞給showFly方法中
       showFly(f);
  }
   public static void showFly(FlyAble f) {
       f.fly();
  }
}

以上能夠簡化,代碼以下:

public class InnerDemo2 {
   public static void main(String[] args) {      
       /*
      建立匿名內部類,直接傳遞給showFly(FlyAble f)
       */
       showFly( new FlyAble(){
           public void fly() {
               System.out.println("我飛了~~~");
          }
      });
  }

   public static void showFly(FlyAble f) {
       f.fly();
  }
}

小結:

匿名內部類作的事情是建立一個類的子類對象

第五章 引用類型使用小結

知識點--引用類型使用小結

目標:

實際的開發中,引用類型的使用很是重要,也是很是廣泛的。咱們能夠在理解基本類型的使用方式基礎上,進一步去掌握引用類型的使用方式。基本類型能夠做爲成員變量、做爲方法的參數、做爲方法的返回值,那麼固然引用類型也是能夠的。在這咱們使用兩個例子 , 來學習一下。

步驟:

  • 引用類型做爲方法參數和返回值

  • 引用類型做爲成員變量

講解:

5.1 引用類型做爲方法參數和返回值

public class Person{
 public void eat(){
   System.out.println("吃飯");
}
}
public class Test{
 public static void main(String[] args){
       method(new Person());
  Person p = createPerson();
}
   
 //引用類型做爲方法參數,在前面筆記本案例中咱們也使用了接口類型做爲方法參數
 pubic static void method(Person p){
      p.eat();
}
   
 //引用類型做爲返回值
 public static Person createPerson(){
  return new Person();
}
}

5.2 引用類型做爲成員變量

咱們每一個人(Person)都有一個身份證(IDCard) , 爲了表示這種關係 , 就須要在Person中定義一個IDCard的成員變量。定義Person類時,代碼以下:

class Person {
   String name;//姓名
   int age;//年齡
}

使用使用String 類型表示姓名 , int 類型表示年齡。其實,String自己就是引用類型,咱們每每忽略了它是引用類型。若是咱們繼續豐富這個類的定義,給Person 增長身份證號 , 身份證簽發機關等屬性,咱們將如何編寫呢?這時候就須要編寫一個IDCard類了

定義IDCard(身份證)類,添加身份證號 , 簽發地等屬性:

class IDCard {
   String idNum;//身份證號
   String authority;//簽發地
 
//getter和setter方法
//...

//toString方法
//...
}

修改Person類:

public class Person {
   String name;//姓名
   int age;//年齡

   IDCard idCard;//表示本身的身份證信息

   //name和age的getter、setter方法
   //...

   public IDCard getIdCard() {
       return idCard;
  }

   public void setIdCard(IDCard idCard) {
       this.idCard = idCard;
  }

   @Override
   public String toString() {
       return "Person{" +
               "name='" + name + '\'' +
               ", age=" + age +
               ", idCard=" + idCard +
               '}';
  }
}

測試類:

public class TestDemo {
   public static void main(String[] args) {
       //建立IDCard對象
       IDCard idCard = new IDCard();
       //設置身份證號
       idCard.setIdNum("110113201606066666");
       //設置簽發地
       idCard.setAuthority("北京市順義區公安局");

       //建立Person對象
       Person p = new Person();
       //設置姓名
       p.setName("小順子");
       //設置年齡
       p.setAge(2);
       //設置身份證信息
       p.setIdCard(idCard);

       //打印小順子的信息
       System.out.println(p);
  }
}
輸出結果:
Person{name='小順子', age=2, idCard=IDCard{idNum='110113201606066666', authority='北京市順義區公安局'}}
相關文章
相關標籤/搜索