1. 程式人生 > 實用技巧 >Java面向物件基礎學習

Java面向物件基礎學習

一、面嚮物件語言程式設計

​ Java是一門面向物件的程式語言(OOP),萬物皆物件

​ 面向物件初步認識,在大多數程式語言中根據解決問題的思維方式不同分為兩種程式語言

​ 1、面向過程程式設計

​ 2、面向物件程式設計

面向過程 面向物件
區別 事物比較簡單,可以使用線性思維解決,具體每一個實現步驟清晰可見 事物比較複雜使用簡單的線性思維無法解決,存在物件與物件之間的引用
共同點 1、都是為了解決實際問題的一種方式 2、當解決複雜問題時,面向物件是從巨集觀角度把握問題的整體,面向過程是從微觀角度實現具體細節,兩者之間相輔相成
以每天下樓吃飯為例
面向過程 面向物件
1、下樓找餐廳 1、下樓找餐廳
2、看菜品,並熟悉掌握你吃的每一道菜的來源,製作流程,烹飪手法等具體細節 2、我要開吃了 (不關注具體菜的細節)
3、吃這道菜 3、吃完了

二、Java的面向物件程式設計

​ Java作為面向物件的語言,關於面嚮物件語言的核心概念

1、類和物件

​ 類:一類事物的抽象和模板,在顯示世界中類就是任意一類事物,在程式中類就是一個描述這類事物的類檔案。

​ 物件:在一類食物中,具體的某一個個體就是物件,在程式中物件就是new出來的,有記憶體空間

2、類和物件的關係

​ 類和物件的關係:類時抽象的而物件是具體的,物件是由類建立的示例(new出來的)

​ 類的組成(人類):

​ 類名:給某一類事物取個名字:People

​ 靜態的特徵稱為屬性:姓名、年齡、身高、體重(定義變數的語法)

​ 動態的行為稱為方法:吃飯、睡覺、打豆豆(方法的定義依然滿足之前所學)

​ 類的實現:

​ 在一個類檔案(People)中,定義屬性和方法

​ 物件的實現:

​ 通過類名建立這個類的物件

注意:類名不能直接訪問它裡面的屬性和方法,必須由類的物件訪問

package com.j2008.init;

/**
 * ClassName: People
 * Description:
 *  建立一個People類  ,並定義這個類的屬性(靜態的特徵)
 *  和 這個類的方法 (動態的行為)
 * @Author: HuSongSong by softeem
 * @Date: 2020/10/10 14:13
 */
public class People {
    // 定義姓名屬性   資料型別 和屬性名 = [初始值]
    String name="張三";
    // 定義性別屬性
    String sex="男";
    // 定義身高屬性
    double height =1.75;
    // 定義體重屬性
    double weight = 140;

    /**
     * 定義吃飯的行為(方法)
     */
    public void eat(){
        System.out.println("正在吃飯");
    }

    /**
     * 定義睡覺方法
     */
    public void sleep(){
        System.out.println("正在睡覺");
    }

    /**
     * 定義打豆豆方法
     */
    public void playGame(){
        System.out.println("正在打豆豆");
    }
    // 計算兩個數相加
    public int add(int a, int b){
        return a+b;
    }
}
public static void main(String[] args) {
        // 不同通過People直接訪問它屬性和方法 需要建立類的例項,也就是物件
        // 建立物件的過程稱為類的例項化
        // 語法: 類名  物件名   =  new  類名() ;
        People people = new People();
        // 這時候才可以通過物件名 訪問這個物件具有的屬性 和 方法
        // 物件名.屬性名
        // 物件名.方法名([實參])
        System.out.println("這個物件的屬性name :"+ people.name);
        System.out.println("這個物件的屬性 sex :" + people.sex);
        System.out.println("這個物件的屬性 weight :"+ people.weight);
        System.out.println("這個物件的屬性height:"+ people.height);
        // 呼叫物件的訪問
        people.eat();
        people.sleep();
        people.playGame();
       	int result =  people.add(2,4);
        System.out.println("這個物件還可以計算 結果:"+result);

    }

案例:定義一個汽車類 , 汽車類包含品牌,顏色,價格的屬性 ,速度包含 加速 和 減速的行為和 顯示汽車基本資訊的行為 ,加速的行為每次這個汽車的速度+20邁, 減速行為 汽車隨機減速 20邁以內 ,建立一個汽車物件,初始速度40邁, 連續給汽車加速5次 減速5次 問這個汽車剩下的速度是多少?

package com.j2008.init;
/**
 * ClassName: Car
 * Description:
 * @author husongsong
 */
public class Car {
    // 品牌
    String logo;
    // 顏色
    String color;
    // 價格
    double price;
    // 速度
    double speed;
    /**
     * 加速度
     */
    public void addSpeed(){
        // this 表示當前例項的物件,由於現在沒有例項,不知道用什麼物件名
        // 只能用this代替
        this.speed+=20;   //  this 也表示 car物件 等價於 car.speed
        System.out.println("汽車正在加速");
    }
    /**
     * 減速度
     */
    public void mulSpeed(){
        this.speed-= Math.random()*20;
        System.out.println("汽車正在隨機減速");
    }
    /**
     * 顯示汽車資訊  當程式執行時 這裡的this 代表的就是 car物件
     */
    public void showCar(){
        System.out.println("汽車的品牌:"+this.logo +",汽車的顏色:"+this.color
        +",汽車的價格(萬元):"+this.price +" 汽車的速度:"+this.speed);
    }
}
package com.j2008.init;
/**
 * ClassName: TestCar
 * Description:
 *
 * @author husongsong
 */
public class TestCar {
    public static void main(String[] args) {
         //建立一個汽車物件  (只有建立了具體的物件,汽車的屬性和行為才能使用)
        Car car = new Car();
        // 給汽車的屬性 賦值
        car.logo="寶馬";
        car.color="黑色";
        car.price=50;
        car.speed= 40;
        //訪問汽車的基本資訊
        car.showCar();
        // 加速 減速
        for(int i = 0 ;i< 5;i++){
            car.addSpeed();
            car.mulSpeed();
        }
        // 再次訪問汽車的速度
        System.out.println("汽車的最終速度:"+car.speed);
    }
}

​ 在類中定義的屬性稱為“成員屬性”,在類中定義的方法

稱為“成員方法”

3、構造器(Construct)

​ 定義;在建立物件時被自動呼叫的特殊方法,也稱為構造方法。在一個類中除了包含屬性,方法以外,還可以包含構造器(構造方法)。

​ 每一個類都自帶一個無參構造器,也可以在這個類中定義多個構造器,多個構造器之間也稱為“構造器過載”。

語法:

訪問修飾符 類名([引數列表]){
      
  } 

例如

public	class	Student{
    
     //無參構造器
    public	Student	(){
        System.out.println("這是一個無參構造器");
    }
}

構造器的作用:

1、用於建立物件自動呼叫,並可以給物件的屬性賦初始值

public	class	Student{
         String	name;//物件的屬性
       //有參構造器
       public	Student	(String	name1){
            name=name1;
      }
         //注意一個類中如果存在有參構造器,那麼它的無參構造器被覆蓋。
  }
建立物件:
   Student	stu	=	new	Student("張三");
     //這裡會自動呼叫有參構造器	並將“張三”賦值給name1,由於自動執行以上構造器,將name1的值賦值給name,這個name就是物件的屬性
     System.out.print(stu.name);//張三
package com.j2008.construct;
/**
 * ClassName: Student
 * Description:
 * @author husongsong
 */
public class Student {
    // 成員屬性
    String name;
    int age;
    // 驗證1  一個類是否自帶一個無參構造器 ,且建立物件自動呼叫
    public Student(){
        System.out.println("這是無參構造器");
    }

    // 驗證2 有參構造器的定義
    public Student(String name1){
        name = name1;
        System.out.println("這是有參構造器");
    }

    // 構造器的過載  : 它與方法的過載類似(在一個類中存在多個方法名的方法,其引數列表不同)
    public Student(String name,int age){
        // this.name 表示當前物件的成員屬性     name : 就近原則找形參name
        this.name=name;
        this.age = age;
    }
}
package com.j2008.construct;
/**
 * ClassName: TestStudent
 * Description:
 * @author husongsong
 */
public class TestStudent {
    public static void main(String[] args) {
        System.out.println("hello");
        // JVM通過類載入器ClassLoad首先載入 Student ,
        // 先初始化靜態資源(省略)
        // 然後執行  new 關鍵字,開闢一個堆記憶體空間, 自動執行 構造器
        // 然後 這個物件的屬性 ,和 方法 被初始化
        Student stu  = new Student();
        // 給物件賦值
        stu.name="";
        //如果想要在建立物件時,直接初始化屬性 ,可以使用有參構造器
        Student stu2 = new Student("張三");
        System.out.println("物件的屬性:"+stu2.name);
        Student stu3 = new Student("李四",20);
        System.out.println(stu3.name);
        System.out.println(stu3.age);
    }
}

案例:

1、 建立一個 狗類定義狗的名稱 狗的顏色 狗的體重構造不同的3只狗,通過構造器完成對狗的屬性的初始化。

public class Dog {
    String name;
    String color;
    double weight;
    public Dog(String name,String color,double weight){
        this.name=name;
        this.color=color;
        this.weight=weight;
    }
    public void dog(){
        System.out.println("小狗的資訊:名字:" + name + ",顏色:" + color + ",體重:" + weight);
    }
}

測試類:

public class TestDog {
    public static void main(String[] args) {
        Dog dog1 = new Dog("小張", "黃", 45);
        Dog dog2 = new Dog("小祁", "灰", 46);
        Dog dog3 = new Dog("小朱", "紅", 49);
        dog1.dog();
        dog2.dog();
        dog3.dog();
    }

2、定義一個圓(Round)類 ,定義圓的半徑 通過構造器初始化半徑,定義方法計算圓的周長 定義方法計算圓的面積

public class Round {
    double r;
    public Round(double r){
        this.r=r;
    }
    //計算圓的周長的方法
    public double C(double r){
        double C=2*Math.PI*r;
        return C;
    }
    //計算圓的面積的方法
    public double S(double r){
        double S=Math.PI*Math.pow(r,2);
        return S;
    }
}

測試類:

public class TestRound {
    public static void main(String[] args) {
        Round round = new Round(5);
        System.out.println("這個圓的周長為:" + round.C(round.r) + ",面積為:" + round.S(round.r));
    }
}

類與類的關聯關係:

​ 如果在一個類中引用另一個類,那麼這兩個類屬於關聯關係。

​ 例如小明同學養了一條狗,如何通過面向物件的方式定義小明同學與狗的關係。

​ 思路:定義一個People類,其中包含name屬性

​ 定義一個Dog類,包含dogName,dogColor屬性

​ 將People類與Dog類關聯,在People類中建立Dog類的引用

public class People {
    String name;
     // 在People類中 建立 People物件與Dog物件的關係
    Dog dog;
    public People(String name){
        this.name = name;  // 將形參name 賦值給 當前物件的成員屬性name
    }
}
public class Dog {
    // 由於以下屬性 屬於狗的特徵,所以必須放在Dog類中,而不能放在People類
    String dogName;
    String dogColor;

    /**
     * 建立Dog物件時 初始化狗的基本屬性
     * @param dogName
     * @param dogColor
     */
    public Dog(String dogName ,String dogColor){
        this.dogName = dogName;
        this.dogColor = dogColor;
    }
}
public static void main(String[] args) {
        // 先建立一個小明同學
        People people = new People("小明同學");
        System.out.println("people.dog:"+people.dog);
        // 再建立一個 Dog物件
        Dog dog = new Dog("拉布拉多","灰白");
        System.out.println("dog:"+dog);
        //設定dog和people物件的關係
        people.dog = dog;
        System.out.println("people.dog:" +dog);

    }

案例:玩一個小遊戲(石頭,剪刀,布),定義一個裁判類(Judger),屬性包括裁判姓名,兩個玩家物件,判斷輸贏的方法(贏的一方 加1分,輸的一方減1分 平的不加不減。), 定義一個玩家類(Player),包含玩家姓名,積分,和出拳的行為(1:石頭:2:剪刀:3:步,)並返回出拳值。 通過構造裁判物件時給玩家物件賦值

開始遊戲: 建立1個裁判,2個玩家,分佈玩10次,最後統計 玩家輸贏和得分

裁判類:

package com.j2008.game;

/**
 * ClassName: Judger
 * Description:
 * 裁判類
 * @author husongsong
 */
public class Judger {
    // 定義裁判的姓名屬性  和 玩家物件(可以是空物件)
    String jname;
    // 玩家1
    Player player1;
    // 玩家2
    Player player2;
    // 在建立裁判時指定玩家物件
    public Judger(String jname,Player player1 ,Player player2){
        this.jname = jname;
        this.player1 =player1;
        this.player2 = player2;
    }
    // 給裁判中對應的兩個玩家判分
    public void judge(){
       int num1= player1.play(); // 1 :石頭 2:剪刀 3:布
       int num2 = player2.play();
       //如何判斷
        if(num1-num2 == -1 || num1-num2 == 2 ){
            // player1贏
            player1.score++;
            player2.score--;
            System.out.println(player1.pname+"打贏了!!!");
        }else  if(num2-num1 == -1 || num2-num1 ==2){
            player2.score++;
            player1.score--;
            //  player2 贏
            System.out.println(player2.pname+"打贏了!!!");
        }else{
            System.out.println("兩個玩家打平");
        }

    }
}

玩家類:

package com.j2008.game;
/**
 * ClassName: Player
 * Description:
 * 玩家類
 * @author husongsong
 */
public class Player {
    // 玩家姓名
    String pname;
    //玩家分數
    int score;
    //玩家出拳  1:石頭 2:剪刀 3: 布
    public int play(){
        //隨機生成
        int n = (int)(Math.random()*100);
        if(n<33){
            return 1;
        }else if(n<66){
            return 2;
        }else{
            return 3;
        }
    }
}

測試類:

package com.j2008.game;
/**
 * ClassName: TestGame
 * Description:
 * @author husongsong
 */
public class TestGame {
    public static void main(String[] args) {
        // 建立兩個玩家
        Player player1 = new Player();
        player1.pname="張培進";
        Player player2 = new Player();
        player2.pname="祁鍵";
        //建立裁判
        Judger judger = new Judger("朱正康",player1,player2);
        for(int i = 0;i<10;i++){
            System.out.println("第"+i+"次玩");
            judger.judge();
        }
        //統計結果
        System.out.println(player1.pname +"的分數:"+player1.score);
        System.out.println(player2.pname +"的分數:"+player2.score);

    }
}

三、Java的面向物件的特徵

​ 面向物件的三大特徵:封裝、繼承、多型

1、封裝(隱藏)

對類中的成員屬性進行隱藏(私有化),對類中的成員方法公共。為了提高類的隱蔽性,對類實現的細節隱藏,提供外部訪問的介面即可,提高程式碼的可擴充套件性。

生活中的封裝: 例如筆記本 的內部結構統一封裝了,一般人使用筆記本時不需要了解筆記本的結構,而是直接開機關機使用。

對程式碼的封裝包括兩層意思:

1、對類的成員屬性的封裝 :

​ 將屬性私有化(private),提供對屬性的訪問給屬性新增公用的getter和setter方法

2、對程式碼的封裝:

​ 為了提高程式碼的複用性,儘量使用方法加引數傳遞對程式碼進行封裝,並使該方法公有(public)

package com.j2008.encapsulation;
/**
 * ClassName: People
 * Description:
 *  封裝類的成員時,所有的屬性私有化,所有的方法公有
 * @author husongsong
 */
public class People {
    private String pname;
    private int age;
    private String sex;
    // 提供 getter 和 setter
    public String getPname(){
        return pname;
    }
    public void setPname(String pname){
        this.pname=pname;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        // 對成員屬性的隱蔽性 可以防止隨意對屬性更改
        if(age>100 || age<0){
            System.out.println("賦值的年齡不合法");
            return;
        }
        this.age = age;
    }

    public String getSex(){
        return sex;
    }
    public void setSex(String sex){
        this.sex= sex;
    }
    // 通常為了方便給屬性賦值,會提供有參構造
    public People(String pname ,int age,String sex){
        this.pname = pname;
        this.age = age;
        this.sex = sex;
    }
    public People(){
    }
 }

測試類:

public class TestPeople {
    public static void main(String[] args) {
        //建立物件
        People people = new People();
        // 不能直接訪問私有的成員,只能通過訪問方法
        people.setPname("張三");
        people.setAge(120);
        people.setSex("男");
        System.out.println(people.getAge());
    }
}

對於boolean型別的屬性,需要使用isXxx返回屬性的值。

封裝的優點:

​ 1、良好的封裝可以減少類的耦合性(類與類的關聯)

​ 2、對類中封裝的程式碼可以自由修改,而不會影響其他類

​ 3、最大程度提高類中屬性的隱蔽性 和對屬性的控制

2、繼承

一個類A可以繼承另一個類B,這裡類A就是類B的子類,類A可以繼承類比的屬性和方法,也可以定義自己的屬性和方法

2.1、概念

當多個類中都存在相同的屬性和行為時,可以將這些共有的屬性和行為定義到一個新的類中,讓其他類服用這個新類的屬性和行為,這種關係就是繼承關係

當滿足 XXX是 一個XXX的時候,也是繼承關係,例如 蘋果是一種水果,其中水果就是父類,蘋果就是子類, 水果有多種水果,而蘋果只是水果的一種,所以蘋果繼承水果

其中 被繼承的類是 父類(超類,基類),繼承父類的類就是子類(新類,派生類)

2.2、繼承的語法

先定義父類

public  class 父類{
    
}

再定義子類

public class 子類 extends 父類{
    
}

2.2.1 子類繼承父類,子類擁有父類的哪些屬性和方法?

可訪問: 子類擁有父類的共有的屬性和方法, 同包下面的屬性和方法,不同包下的受保護的也可以訪問

不可訪問: 其中子類不能繼承 父類的私有的屬性和方法、不同包的預設屬性和方法 ,不能繼承父類的構造器

2.2.2 子類繼承父類,子類如何訪問父類的屬性和方法?

屬性: 子類通過super 關鍵字訪問父類的屬性 ,子類通過this關鍵字訪問自己的屬性

方法:子類通過super掛件自方法父類的方法, 子類通過this關鍵字訪問自己的方法

注意: 這裡的this和super可以省略,省略後子類通過“就近原則”訪問屬性和方法(子類中存在就訪問子類的,子類中不存在,就訪問父類的。)

super.屬性

super.方法(引數)

構造器:子類通過super([引數]) 呼叫父類的構造器, 子類通過this([引數])呼叫 自己類的其他構造器,其中 super(引數[]) 必須寫在子類構造器的第一行

通過在子類構造器手動呼叫父類的有參構造器給父類的屬性賦值

public class Employee {
    String ename="王麻子";//員工姓名
    double sal=5000 ; // 員工薪資
    
     public Employee(String ename,double sal){
        this.ename = ename;
        this.sal = sal;
    }
 }
public class Manager extends Employee{
    // 獎金
    private double comm;
     public Manager(String ename ,double sal ,double comm){
        // 如何覆蓋父類的無參構造器 ,手動呼叫父類的有參構造
        super(ename ,sal);  // 只能寫在第一句
        this.comm = comm;
    }
  }

注意: 子類構造器中預設呼叫父類的無參構造器

2.2.3 Java中只能是單繼承,一個類只能有一個直接父類的父類,可實現多層次繼承

​ 子類 -》 父類 -》 父類的父類

​ pupil -> Student-》 People

建立子類物件時,優先建立父類物件,再創子類物件, 執行順序 最上層父類的構造器 -》 父類構造器 -》子類構造器。

​ 擴充套件問題:當一個類中存在static元素時,它們的執行順序是如何?

順序: 最上層父類的靜態塊 - 》 父類的靜態塊-》 子類的靜態塊- 》最上層 父類的構造塊和構造方法

-》父類的構造塊和構造方法- 》 子類的構造塊和構造方法

public class People {
    static{
        System.out.println("People的靜態語句塊");
    }
    public People(){
        System.out.println("People類的無參構造器");
    }
    {
        System.out.println("People的構造語句塊");
    }
}
public class Student extends  People{
    static{
        System.out.println("Student 的靜態語句塊");
    }
    public Student(){
        // 預設呼叫父類的構造器
        System.out.println("Student的無參構造器");
    }
    {
        System.out.println("Student的構造語句塊");
    }
}
public class Pupil extends  Student {
    static{
        System.out.println("Pupil的靜態語句塊");
    }
    public Pupil(){
        // 呼叫它父類的無參構造器
        System.out.println("Pupil類的無參構造器");
    }
    {
        System.out.println("pupil的構造器語句塊");
    }
}
 public static void main(String[] args) {
          //建立Pupil物件
        Pupil pupil = new Pupil();
    }

結果:

People的靜態語句塊
Student 的靜態語句塊
Pupil的靜態語句塊
People的構造語句塊
People類的無參構造器
Student的構造語句塊
Student的無參構造器
pupil的構造器語句塊
Pupil類的無參構造器

案例:

package com.j2008.extend;
/**
 * ClassName: Animal
 * Description:
 * 父類
 * @author husongsong
 */
public class Animal {
    String animalName="哮天犬";

    public void info(){
        System.out.println("動物名稱:"+animalName);
    }
}
public class Dog extends  Animal{
    public void dogInfo(){
        //子類中可直接訪問父類的屬性
        System.out.println("狗狗資訊:"+animalName);
    }
}

2.3、重寫

子類可以繼承父類的方法,但是當父類的方法不能滿足子類的需要時,子類可以重寫父類的方法

重寫的必要條件:

1、兩個方法名必須相同,且存在不同類中(父子關係的類)

2、子類重寫的父類的方法,其方法的引數和返回值必須完全一樣,方法的具體實現可不一樣

3、訪問修飾符必須大於或等於父類的修飾符

注意: 子類的物件 呼叫父類方法時,如果子類重寫了父類的方法,則執行子類的方法,沒有重寫執行父類的方法。

面試題:

說一下方法的重寫與過載的區別?

1、重寫存在於父子關係中的不同類,過載存在同類中

2、重寫必須滿足 方法名相同,且引數相同,返回值相同 ,過載滿足方法名相同,引數不同,與返回值無關。

3、重寫的訪問修飾符必須 子類大於等於父類, 過載沒有要求

案例:

public class Father {

    protected void eat(){
        System.out.println("父親一餐吃一碗飯");
    }
    public void sleep(){
        System.out.println("正常人一天睡7小時");
    }

}
public class Son extends  Father {
    @Override  // 重寫
    public void eat() {
       // super.eat();
        System.out.println("兒子一餐吃5碗飯");
    }
}
public class Test {
    public static void main(String[] args) {
         Son son = new Son();
         son.eat();   // 重寫了 呼叫子類的方法
         son.sleep();// 沒有重寫呼叫父類的方法
         Father father = new Father();
         father.eat();
         father.sleep();
    }
    // 方法的過載
}

3、多型

多型:繼Java面向物件中封裝,繼承之後的第三個特徵

生活中的多型: 同一個行為,例如跑,人是兩條腿跑,動物是四條腿或兩條腿跑,飛的行為不同是事物飛的方式也不同,飛機飛,小鳥飛,無人機飛都不一樣,同一種行為對於不同的事物呈現的不同形態就是多型的表現。

3.1、 定義

同一種行為,具有多個不同的表現形式

3.2、實現多型的前提

  • 基於繼承關係或基於實現關係的
  • 子類或實現類必須對方法進行重寫(沒有重寫的方法 不具有多型行為)
  • 父類的引用指向子類物件( 介面的引用指向實現類的物件)

3.3、多型的物件轉型

1、子類物件轉父類物件時,稱為向上轉型是預設轉換,自動轉型

 Cat cat = new Cat();
         // 貓類 可以是一隻動物     an4的本質還是 貓物件
        Animal an4 = cat;    // 子類物件轉成父類物件 ,是自動轉型
         cat.eat();
         an4.eat();

2、父類的引用轉成子類物件,稱為向下轉型,向下轉型需要強轉 , 為了避免轉換錯誤,需要先判斷資料型別是否匹配

 // 建立一個動物類, 將動物類轉成子類引用
         Animal an5 = new Cat();
        //  an5.catchMouse(); // 動物型別物件不能訪問 它子類特有的方法
        if(an5 instanceof Cat) {
            Cat cat2 = (Cat) an5;
            cat2.eat();
            cat2.catchMouse();
        }
        if(an5 instanceof  Dog) {
            Dog dog2 = (Dog)an5;
        }else{
            System.out.println("不能轉成dog");
        }

instanceof : 判斷該物件是否屬於 這個型別

  • 為什麼需要做型別換行呢? 有時候我們需要呼叫子類特用的方法時必須用子類的引用。所有多型物件下父類應用需要強轉。

為了避免ClassCastException的發生,Java提供了 instanceof 關鍵字,給引用變數做型別的校驗,格式如下:

變數名 instanceof 資料型別如果變數屬於該資料型別,返回true。

如果變數不屬於該資料型別,返回false

3.4、在多型環境型,方法和屬性的呼叫規則

屬性: 當子類和父類中存在相同屬性時 ,以 物件的左邊引用型別為依據,所謂“看左邊”

方法: 以當前new出來的物件為依據,如果方法重寫了,就呼叫子類方法,如果方法沒有重寫 呼叫父類方法

靜態方法: 無論方法是否有重寫, 都已物件左邊的引用型別為依據。

public class Student extends  People  {
      int age =20;

    @Override
    public void showAge() {
        this.age++;
        System.out.println("年齡:"+this.age+" ----"+super.age);

    }
    // 重寫的靜態方法
    public static void  method1(){
        System.out.println("method1------------  重寫後的 方法");
    }
}
public static void main(String[] args) {
      People p1 = new People();
      System.out.println(p1.age);// 18
       p1.showAge();  // 物件的本質people  呼叫people

       People p2 = new Student();
      System.out.println( p2.age);// 18
      p2.showAge(); // 本質student ,且重寫了 調Student
      p2.sleep();  // 本質student  ,沒重寫,呼叫people

        Student p3 = (Student)p2;
     System.out.println( p3.age); // 20
        p3.showAge(); // 本質student ,且重寫了 調Student

        People p4 = p3;
     System.out.println( p4.age); // 18   看左邊
        p4.showAge(); // 本質student ,且重寫了 調Student

}

結果:

18
年齡:18
18
年齡:21 ----18
睡覺方法。。。。。
21
年齡:22 ----18
18
年齡:23 ----18

public static void main(String[] args) {
      People p1 = new People();
      p1.method1();
      p1.method2();

      People p2 = new Student();
      p2.method1();
      p2.method2();

      Student p3 = (Student)p2;
      p3.method1();
      p3.method2();

}

結果:

method1------------
method2---------
method1------------
method2---------
method1------------ 重寫後的 方法
method2---------

三、Java面向物件的基礎知識點

1、訪問修飾符的許可權

用於修飾類,屬性,方法的關鍵字都稱為訪問修飾符

  1. 1、public :公共的

​ 可被同一個專案的所有類方法(專案可見性)

  1. 2、protected :受保護

​ 可以被自身的類訪問

​ 可以被同包下的其他類訪問

​ 對於不同包的,存在父子關係的子類可以訪問

  1. 3、預設的

​ 可以被自身類訪問

​ 可以被同包下的其他類訪問

  1. 4、private: 私有的

​ 只能被自身類訪問

訪問修飾符 同一個類 同一包不同類(子類或非子類) 不同包的子類 不同包
public
protected ×
預設 × ×
private × × ×

案例:

public class Animal {
    //動物名
     public String name;
     //動物顏色
     protected String color;
     // 動物體重
     double weight;
     // 動物性別
     private String sex;
     // 以上四個修飾符 同一個類裡面都可以被訪問
     public  void animalInfo(){
         System.out.println("動物資訊:"+name+" 顏色:"+color+
                 " 體重:"+weight +" 性別:"+sex);
     }
}
public class Dog extends  Animal{

    public void dogInfo(){
        // 訪問父類的 屬性  子類繼承父類可以繼承父類中能被訪問的成員屬性和方法
        System.out.println("動物名:"+name +" 顏色: "+color + " 體重:"+weight);
    }
}
public class TestAnimal {
    public static void main(String[] args) {
         Animal an = new Animal();
         //給屬性賦值
          an.name="狗狗";
          an.color="灰色";
          an.weight=5; // 公斤
         // an.sex  私有
    }
}

2、static關鍵字

​ static表示“靜態” ,它可以修飾 屬性,方法,程式碼塊 , 在一個類中除了可以定義成員屬性、成員方法和構造器以外,還可以定義靜態的部分(靜態屬性,靜態方法,靜態程式碼塊)

static 修飾屬性:稱為 靜態屬性或類的屬性

static修飾方法:稱為靜態方法或類的方法

static修飾的語句塊: 稱為靜態程式碼塊

static修飾的元件不需要通過物件訪問,而是直接通過類名訪問,在類一載入時會給static修飾的屬性和方法分配記憶體區,這個記憶體分佈在 靜態記憶體區中。後續所有物件操作的是同一個記憶體區

案例一:

public class Student {
    //  成員屬性
    String name;
    // 靜態屬性  通常static寫在public的後面
    static  int age=20;
    // 靜態程式碼塊
    static{
        System.out.println("這是靜態程式碼塊,在類一載入時,就會被執行,且只執行一次");
    }
    //成員方法  : 既可以訪問 成員屬性,也可以訪問靜態屬性
    public void getInfo(){
        System.out.println("姓名:"+name +" 年齡:" +age);
    }
    // 靜態方法 : 只能訪問靜態屬性,不能訪問成員屬性(非靜態屬性
    //   這是為什麼? 由於成員屬性的存在需要依賴物件 
    // 靜態屬性和靜態方法在建立物件之前就必須初始化並分配記憶體
    public static void getStaticInfo(){
       // System.out.println("姓名:"+name);  成員屬性不能被訪問
        System.out.println("靜態屬性:"+age);
    }
    public Student(){
        System.out.println("無參構造器");
    }
    // 構造程式碼塊
    {
        System.out.println("構造程式碼塊");
    }
    public static void main(String[] args) {
        System.out.println("訪問靜態屬性:"+Student.age);
        // 訪問靜態方法
        Student.getStaticInfo();
    }
 // 類的元件執行順序
 // 類編譯成.class檔案被JVM的類載入器載入-》
 // 從上往下初始化static修飾的元件
 // (靜態屬性,靜態程式碼塊,靜態方法,其中靜態方法呼叫才執行,靜態屬性和靜態程式碼塊直接分配記憶體)-》
 //  --》構造程式碼塊-》執行構造器  -》初始化成員屬性,成員方法
        Student stu1 = new Student();
        stu1.name="張三";
 // 靜態屬性可以通過類名訪問,也可以通過物件名訪問
        stu1.age=21;
        System.out.println(stu1);
        Student stu2 = new Student();
        stu2.name="李四";
        stu2.age=22;
        System.out.println(stu2);
        System.out.println(stu1.name);
        System.out.println(stu1.age); //   22
        System.out.println(stu2.name); //李四
        System.out.println(stu2.age); // 22
        System.out.println(Student.age);//22
    }

案例二: 靜態的變數 在同一個記憶體中

public class People {
    double height;
    static int score;
    static{
       score++; // 1
    }
   public void setScore(){
       score++; //81  86
   }
   public static void setScore2(){
        score++;
   }
    public static void main(String[] args) {
         People p1 = new People();
         p1.score=80;//靜態屬性
         p1.setScore();
         People.score=85;
         p1.height= 1.75;
         People p2 = new People();
         p2.setScore(); //86
         p2.height= 1.80;
        System.out.println(p1.score); // 86
        System.out.println(p1.height); //1.75
        System.out.println(p2.score);//86
        System.out.println(p2.height);// 1.80
    }
}

案例三: 構造程式碼塊和靜態程式碼塊 的執行順序

package com.j2008.statics;
/**
 * ClassName: UserInfo
 * Description:
 * @author husongsong
 */
public class UserInfo {
    //  關於靜態的元件 從上往下執行
    // 靜態屬性  需要先初始化 ,需要new一個物件
    static UserInfo u = new UserInfo(); // 先執行構造程式碼塊 在執行構造器
    static{
        System.out.println("這是靜態程式碼塊,只執行一次");
    }
    public UserInfo(){
        System.out.println("這是無參構造器");
    }
    {
        System.out.println("構造程式碼塊");
    }
    public static void main(String[] args) {
         // 結果
        UserInfo u = new UserInfo();
    }
}

結果:

構造程式碼塊
這是無參構造器
這是靜態程式碼塊,只執行一次
構造程式碼塊
這是無參構造器

3、抽象類

3.1、定義

在已有類的基礎上,由於特殊情況將該類設定為抽象的,這個類就是抽象類

語法:

public abstract class 類{
      // 類的元素
}

什麼情況下需要定義抽象類?

1、當這個類不需要建立具體的例項時,可將類定義為抽象的

2、當這個類中存在沒有實現的方式時(沒有方法體的方法),可以將這個類定義抽象的

3.2、抽象類的特點

3.2.1 抽象類 不能例項化(不能new) ,通常抽象被當作父類使用

3.2.2 抽象類中 可以有抽象方法( 沒有方法體的方法) 也可以有普通方法

3.2.3 抽象類被當作父類時,它的子類必須重寫父類的抽象方法

public abstract class Fruit {
    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    // 水果的甜度  由於不知道是什麼水果,所以說過的甜度未知,可以定義為抽象方法
    // 抽象方法
    public abstract void getSweet();

}
public  class Apple extends  Fruit {

    // 子類重寫(實現)父類的抽象方法
    @Override
    public void getSweet() {
        System.out.println("這個水果有點甜");
    }
}
public class Lemon extends  Fruit {

    @Override
    public void getSweet() {
        System.out.println("這個水果有點酸,想想都覺得酸");
    }
}
public static void main(String[] args) {
        // 抽象類不能例項化
      //  Fruit fruit = new Fruit();
        // 建立子類物件
        Apple apple = new Apple();
        apple.setColor("紅色");
        apple.getSweet();
       System.out.println(apple.getColor());
        Lemon lemon = new Lemon();
        lemon.setColor("黃色");
        lemon.getSweet();
       System.out.println(lemon.getColor());
    }
}

面試題:

抽象方法和非抽象方法的區別?

回答: 抽象方法沒有方法體,需要使用abstract修飾, 只能寫在抽象類中

​ 非抽象方法就是普通方法(成員方法或靜態方法) ,可以寫在抽象類中或普通類中。

4、介面

4.1、定義

​ 介面用於對某件事物的功能的宣告,而沒有實現具體功能 ,介面提供對軟體開發的標準規範。

​ 利用介面的“多實現”完成Java的單一繼承

4.2、介面語法

public interface 介面名{
      抽象方法定義
}

一個類實現該介面,必須實現介面的所有方法

public class 實現類名  implements 介面名{
     實現介面的所有方法
}

介面的好處:

1、為了規範實現類的行為,讓所有的方法都通過介面定義

2、為了提高程式碼的可擴充套件性

介面定義實現類的物件 和 實現類定義試下類的物件的區別?

介面定義實現類的物件只能呼叫介面中定義的方法, 實現類定義的物件既可以呼叫實現類的方法,也可以呼叫介面類的方法。

4.3、介面中的成員組成部分 特點

介面中定義的變數預設全部是 public static final 修飾的

介面中的靜態方法可以直接呼叫。

介面中不能寫構造器(因為介面不能例項化,不需要構造器)

介面中的方法全部都是 抽象方法, 在JDK8以後可以定義default的非抽象方法

案例1 :

一個類既可以繼承一個類 也可以實現多個介面

//定義一個門類 ,定義一個防盜門,它具有防盜的功能,如何防盜 (防盜功能中可以 拍照, 密碼鎖)
// 門本 身有 開門,關門 功能public abstract class Door {
    //定義開門  關門的方法
    public void openDoor(){
        System.out.println("門開了");
    }
    public void closeDoor(){
        System.out.println("門是關的");
    }
}
/**
 * 防盜介面
 */
public interface Burglars {
    //定義初始密碼
    public static final String pwd ="888888";
    public void takePhoto();
    public boolean enterPassword(String password);
}
public class BurglarsDoor extends  Door implements Burglars {
    @Override
    public void takePhoto() {
        System.out.println("防盜門正在拍照。。。");
    }
    @Override
    public boolean enterPassword(String password) {
        // equals比較兩個字串的值是否相等
        if(password.equals(Burglars.pwd)){
            return true;
        }
        return false;
    }
}
public class Test {
    public static void main(String[] args) {
        // 建立門物件
        //1、第一個門只能開關
        Door door = new BurglarsDoor();
        door.openDoor();
        door.closeDoor();
        // 2 、 防盜功能
        Burglars door2 = new BurglarsDoor();
        door2.takePhoto();
        if(door2.enterPassword("888888")){
            System.out.println("鎖打開了,但是門沒有開");
        }
        // 3、使用防盜門類建立自己的物件
        BurglarsDoor door3 = new BurglarsDoor();
        door3.closeDoor();
        door3.takePhoto();
        System.out.println("輸入密碼驗證");
        if(door3.enterPassword("888888")){
            door3.openDoor();
        }
    }
}

案例2:

一個類可以實現多個介面,中間用逗號分隔 (那麼這個類要實現介面的所有方法)

/**
 *  程式設計師身份
 */
public interface Programer {
    public void takeMoney();
}
/**
 * 廚師
 */
public interface Cooker {
    /**
     * 做好吃
     */
    public void makeFood();
}
public interface Driver {
    // 功能
    public void driverCar();

    public void takeMoney();
}

public class People implements Programer ,Cooker ,Driver {

    private String name;
    public People (String name){
        this.name = name;
    }
    @Override
    public void makeFood() {
        System.out.println("做好吃的");
    }

    @Override
    public void driverCar() {
        System.out.println("開車的功能");
    }

    @Override
    public void takeMoney() {
        System.out.println("賺錢的功能");
    }
}

public static void main(String[] args) {
         People people = new People("張培進");
         people.takeMoney();
         people.makeFood();
         people.driverCar();
    }

案例3:

一個介面可以繼承 介面,或多個介面(介面可以多繼承)一個員工介面 一個程式設計師 (由於程式設計師是員工,所有程式設計師可以繼承員工介面 )一個人 是員工也是 程式設計師 (People 是員工 程式設計師 )

/**
 * ClassName: Employee
 * Description:
 *  員工身份
 * @author husongsong
 */
public interface Employee {
    // 是否有女朋友
    public boolean isGirlFriend();
}
public interface Boss {
    // 老闆可以賺更多錢
    public void takeMoreMoney();
}
/**
 * ClassName: Employee
 * Description:
 *  程式設計師身份
 * @author husongsong
 */
public interface Programer extends Employee ,Boss{
     public void writeCoding();
}
public class Student implements Programer {
    private String name;
    public Student(String name){
        this.name = name;
    }
    @Override
    public void writeCoding() {
        System.out.println(this.name+"寫程式碼");
    }
    @Override
    public boolean isGirlFriend() {
        System.out.println(this.name+"有女朋友");
        return true;
    }
    @Override
    public void takeMoreMoney() {
        System.out.println(this.name+"以後可以賺更多的錢,有更多的女朋友");
    }
}
public class Test5 {
    public static void main(String[] args) {
        Student stu = new Student("張培進");
        stu.isGirlFriend();
        stu.writeCoding();
        stu.takeMoreMoney();
    }
}

總結常見面試題:

1、抽象類與介面的區別

​ 共同點: 它們都是抽象的,都不能例項化

區別:

​ 1、抽象類是類 使用abstract修飾,介面使用interface定義

​ 2、抽象類中可以包含抽象方法和普通方法, 介面中是純粹的抽象類,都是抽象方法,除使用default定義以外

​ 3、子類繼承抽象類,必須實現抽象類的方法,或者自身變為抽象類

​ 介面中是實現關係,實現類必須實現介面的所有方法,介面的變數是public static final

2、過載和重寫的區別?

1、重寫存在於父子關係中的不同類,過載存在同類中

2、重寫必須滿足 方法名相同,且引數相同,返回值相同 ,過載滿足方法名相同,引數不同,與返回值無關。

3、重寫的訪問修飾符必須 子類大於等於父類, 過載沒有要求

3、在java中如何實現多型(多型的前提條件)?

  • 基於繼承關係或基於實現關係的
  • 子類或實現類必須對方法進行重寫(沒有重寫的方法 不具有多型行為)
  • 父類的引用指向子類物件( 介面的引用指向實現類的物件)

4、final的作用

​ final修飾變數 定義為常量

​ final 修飾方法 不能被重寫

​ final 修飾類 則不能被其他子類繼承