JavaSE基礎知識面試題總結

該文章是博主採集於各大博文,用於複習和總結相關知識點,將會持續的收集和更新。

一、 概述

1. JDK版本

目前只維護兩個JDK版本,一個是8,一個是11(2018年)

2. Java語言特性

  • 可移植性,跨平臺,因爲Java有一個JVM虛擬機,虛擬機負責執行字節碼文件
  • 健壯性,具有GC,有自動垃圾回收機制

3. Java的加載與執行過程

  • T.java(源文件)通過javac命令變成字節碼文件
  • 字節碼文件通過類加載器加載到JVM中
  • JVM屏蔽了和操作系統打交道的操作

Java程序運行過程

4. JVM、JDK和JRE的區別

Java虛擬機(JVM)

  • 是運行 Java 字節碼的虛擬機。JVM有針對不同系統的特定實現(Windows,Linux,macOS),目的是使用相同的字節碼,它們都會給出相同的結果。字節碼和不同系統的 JVM 實現是 Java 語言“一次編譯,隨處可以運行”的關鍵所在。

JDK

  • 是Java Development Kit,它是功能齊全的Java SDK。它擁有JRE所擁有的一切,還有編譯器(javac)和工具(如javadoc和jdb)。它能夠創建和編譯程序。

JRE

  • 是 Java運行時環境。 它是運行已編譯 Java 程序所需的所有內容的集合,包括 Java虛擬機(JVM),Java類庫,java命令和其他的一些基礎構件。
  • 但是,它不能用於創建新程序。

二、Java語言基礎

1. 八種基本類型

八種基本數據類型:byte、short、int、long、float、double、boolean、char。

image-20200628113348515

一個字節等於8位

IEE754標準(32位):1位是符號位,8位是階碼用移碼錶示,23位尾數

2. 字符編碼

類型可以存放一個漢字, java 中的 char 使用 utf-16 編碼

編碼名稱 解釋
ASCII 字符編碼 只支持英文字母、標點符號、數字字符等, ASCII 碼佔用 1 個字節,所以 ASCII 碼最多可以表示 256 個字符. 小 a 97 大 A 65,’0’是 48
ISO-8859-1 有稱 latin-1,是國際化標準或組織 ISO 制定的,主要爲了西歐語言中的字符 編碼,和 ASCII 兼容,仍不支持中文
GB2312/GBK/GB18030 主要是漢字編碼,三種編碼從容量上看是包含關係 簡體中文: GBK < GB2312 < GB18030 繁體中文: Big5【大五碼】
unicode Unicode 統 一 了 全 世 界 上 的 所 有 文 字 編 碼 , unicode 有 幾 種 實 現 : UTF-8,UTF-16,UTF-32 java 語言採用的是 Unicode 編碼,所以在 java 中標識符也可以使用中文

3. 類型轉換

  • 在 java 中基本類型可以相互轉換, boolean 類型比較特殊不可以轉換成其他類型

  • 轉換分爲默認轉換和強制轉換:

    • 默認轉換:容量小的類型會默認轉換爲容量大的類型

      • byte–>short–> int–>long–>float–>double

      • byte、 short、 char 之間計算不會互相轉換,首先先轉換成 int

  • 強制轉換:

    • 將容量大的類型轉換成容量小的類型,需要進行強制轉換
    • 注意:只要不超出範圍可以將整型值直接賦值給 byte, short, char
    • 在多種類型混合運算過程中,首先先將所有數據轉換成容量最大的那種,再運算
public class DataTypeTest08
{
    public static void main(String[] args){
        long x = 100L;
        int y = x;//編譯不通過
        
        long a = 2147483648L;
        int b = (int)a;
        System.out.println(b);//出現精度丟失問題,大類型-->>小類型會出現問題,輸出-2147483648
        
        byte a = 1000;//出現錯誤, 1000 超出了 byte 的範圍
        
        long g = 10;
        int h = g/3;//出現錯誤,多個數值在運算過程中,會轉換成容量最大的類型

        byte h3 = (byte)(int)g/3;//考察優先級,將g先轉換成int,再強轉成byte,再除以3得到int,賦值錯誤
        byte h4 = (byte)(int)(g/3);//正確的
        byte h5 = (byte)g/3;//考察優先級,先轉換成byte,再運算
        byte h6 = (byte)(g/3);//正確
        short h7 = (short)(g/3);//正確
        
        short i = 10;
        byte j = 5;
        short k = i + j;//錯誤的,short和byte運算,首先會轉換成int再運算
    }
}

4. 運算符

短路與和邏輯與的區別?

短路與比邏輯與智能,短路與效率高。

短路或和邏輯或的區別?

短路或:左邊的算子結果是 true,右邊的表達式不執行,發生短路

a += 3和 a = a + 3; 是一樣的嗎?

  • 結論(重點)擴展賦值運算符不改變運算結果的類型。初始類型和最終運算結果類型完全相同。
public class OperatorTest09
{
    public static void main(String[] args){
        byte b = 10;
        //編譯錯誤
        //b = b + 3;
        //修改
        b = (byte)(b + 3);
        System.out.println(b); //13
        b += 3;
        System.out.println(b); //16
        b += 10000; //等同於 b = (byte)(b + 10000);
        System.out.println(b); //32
    }
}

5. 控制語句

switch 語句

  • switch 也稱爲多重分支,具體格式如下
    switch (表達式) {
    case 值 1:
    語句
    break;
    case 值 2:
    語句
    break;
    default:
    語句
    Break;
    }

  • 說明:

    • 表達式的值只能爲: char、 byte、 short、 int 類型(JDK7 以後支持 String), boolean、 long、 float、
    • double 都是非法的
    • break 語句可以省略,但會出現 switch 穿透
    • default 語句也可以省略,一般不建議省略,並且放置在最後

    需求:

    假定系統給定學生的考試成績,考試成績可以帶有小數。
    假定成績是合法的[0-100],請根據學生考試成績判斷該
    學生成績等級:
    [90-100] A
    [80-90) B
    [70-80) C
    [60-70) D
    [0-60) E

    以上業務只能使用 switch 語句完成,不允許使用 if 語句。

    public class SwitchTest04 {
        public static void main(String[] args) {
            //考試成績合法
            double score = 100;
            //開始判斷
            int grade = (int) (score / 10);//case條件不能爲浮點數
    
            switch (grade) {
                case 10:
                    System.out.println("A");
                    break;
                case 9:
                    System.out.println("A");
                    break;
                case 8:
                    System.out.println("B");
                    break;
                case 7:
                    System.out.println("C");
                    break;
                case 6:
                    System.out.println("D");
                    break;
                default:
                    System.out.println("E");
            }
    
            //重點: case 是可以合併的
            switch (grade) {
                case 10:
                case 9:
                    System.out.println("A");
                    break;
                case 8:
                    System.out.println("B");
                    break;
                case 7:
                    System.out.println("C");
                    break;
                case 6:
                    System.out.println("D");
                    break;
                default:
                    System.out.println("E");
            }
        }
    }
    

for語句

for(;false;){//會出現編譯錯誤,因爲無法訪問
    System.out.println("呵呵");
}
for(;true;){//死循環
	System.out.println("哈哈");
}

6. 方法

方法的返回值問題

public class MethodTest07
{
    //缺少返回語句,程序編譯時無法判斷是否能走到else,無法編譯通過
    public static int m1(){
        boolean flag = true;
        if(flag){
            return 1;
        }
    }

    //正確
    public static int m2(){
        boolean flag = true;
        if(flag){
            return 1;
        }else{
            return 0;
        }
    }

    //編譯錯誤
    public static int m3(){
        boolean flag = false;
        if(flag){//
            return 1;//return後不能接任何語句
            System.out.println("??????????");
        }
        System.out.println("??????????");
        return 0;
        System.out.println("??????????");
    }
}

三、 面向對象

1. 面向過程與面向對象的區別

  • 面向過程面向過程性能比面向對象高。 因爲類調用時需要實例化,開銷比較大,比較消耗資源,所以當性能是最重要的考量因素的時候,比如單片機、嵌入式開發、Linux/Unix等一般採用面向過程開發。但是,面向過程沒有面向對象易維護、易複用、易擴展。

  • 面向對象面向對象易維護、易複用、易擴展。 因爲面向對象有封裝、繼承、多態性的特性,所以可以設計出低耦合的系統,使系統更加靈活、更加易於維護。但是,面向對象性能比面向過程低

這個並不是根本原因,面向過程也需要分配內存,計算內存偏移量,Java性能差的主要原因並不是因爲它是面嚮對象語言,而是Java是半編譯語言,最終的執行代碼並不是可以直接被CPU執行的二進制機械碼。

而面向過程語言大多都是直接編譯成機械碼在電腦上執行,並且其它一些面向過程的腳本語言性能也並不一定比Java好。

2. 面向對象特徵

封裝

封裝把一個對象的屬性私有化,同時提供一些可以被外界訪問的屬性的方法,如果屬性不想被外界訪問,我們大可不必提供方法給外界訪問。但是如果一個類沒有提供給外界訪問的方法,那麼這個類也沒有什麼意義了。

繼承

繼承是使用已存在的類的定義作爲基礎建立新類的技術,新類的定義可以增加新的數據或新的功能,也可以用父類的功能,但不能選擇性地繼承父類。通過使用繼承我們能夠非常方便地複用以前的代碼。

關於繼承如下 3 點請記住:

  1. 子類擁有父類對象所有的屬性和方法(包括私有屬性和私有方法),但是父類中的私有屬性和方法子類是無法訪問,只是擁有
  2. 子類可以擁有自己屬性和方法,即子類可以對父類進行擴展。
  3. 子類可以用自己的方式實現父類的方法。(以後介紹)。

多態

所謂多態就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時並不確定,而是在程序運行期間才確定,即一個引用變量到底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。

在Java中有兩種形式可以實現多態:繼承(多個子類對同一方法的重寫)和接口(實現接口並覆蓋接口中同一方法)。

抽象

  • 抽象就是忽略一個主題中與當前目標無關的那些方面,以便更充分地注意與當前目標有關的方面。

  • 抽象並不打算了解全部問題,而只是選擇其中的一部分,暫時不用部分細節。比如,我們要設計一個學生成績管理系統,考察學生這個對象時,我們只關心他的班級、學號、成績等,而不用去關心他的身高、體重這些信息。

3. 參數傳遞

所有基本數據類型的都是值傳遞,其他類型的爲址傳遞

4. 關鍵字

this 關鍵字:

  • this 關鍵字指的是當前調用的對象, 只能用在構造函數和實例方法內部,還可以應用在成員變量的聲明上, static 標識的方法裏是不能使用 this 的。

  • 作用:代碼複用。

super 關鍵字

  • 調用父類的構造方法

    • 沒有顯示地調用 super(); 父類的無參構造方法也執行

    • 必須將 super 放到子類的構造函數的第一語句來調用父類的構造方法

  • 調用父類的成員方法

    • 需要注意: super 只能應用在成員方法和構造方法中,不能應用在靜態方法中(和 this 是一樣的),如果在構造方法中使用必須放在第一行

爲什麼會有 super 關鍵字?

  • 因爲子類必須要調用父類的構造方法,先把父類構造完成,因爲子類依賴於父類,沒有父,也就 沒有子
  • 有時需要在子類中顯示的調用父類的成員方法

那麼我們以前爲什麼沒有看到 super,而且我們也有繼承,如: Student 繼承了 Person?

  • 因爲子類中我們沒有顯示的調用父類構造方法,那麼他會默認調用父類的無參構造方法,此種情況下
    如果父類中沒有無參構造方法,那麼編譯時將會失敗

注意構造方法不存在覆蓋的概念,構造方法可以重載

static 關鍵字

可以用來修飾它可以用來修飾的成員變量成員方法被修飾的成員是屬於類的,而不是單單是屬於某個對象的。也就是說,既然屬於類,就可以不靠創建對象來調用了。

final關鍵字

final 表示不可改變的含義

  • 採用 final 修飾的類不能被繼承
  • 採用 final 修飾的方法不能被覆蓋
  • 採用 final 修飾的變量不能被修改
  • final 修飾的變量必須顯示初始化
    局部變量: 一旦賦值不能重新賦值
    成員變量: 不能採用系統默認值,必須手動賦值
    如果修飾的引用,那麼這個引用只能指向一個對象,也就是說這個引用不能再次賦值,但被指向的對象是可以修改的
  • 構造方法不能被 final 修飾 會影響 JAVA 類的初始化:
  • final 定義的靜態常量調用時不會執行 static 代碼塊等相關語句,這是由 java 虛擬機規定的。我們不
    需要了解的很深,有個概念就可以了

修飾引用變量:

  • final 修飾引用變量,主要修飾的是變量的地址,那麼這個引用只能指向一個對象,也就是說這個引用不能再次賦值,但被指向的對象是可以修改的
public class FinalTest05 {
    public static void main(String[] args) {
        Person p1 = new Person();
        //可以賦值
        p1.name = "張三";
        System.out.println(p1.name);
        final Person p2 = new Person();
        p2.name = "李四";
        System.out.println(p2.name);
        //不能編譯通過
        //p2 採用 final 修飾,主要限制了 p2 指向堆區中的地址不能修改(也就是 p2 只能指向一個對象)
        //p2 指向的對象的屬性是可以修改的
        p2 = new Person();
    }
}
class Person {
    String name;
}

final 和 static 聯合修飾實例變量==常量 (儘量使用一個靜態工具類抽取出常量)

  • 常量名要求全部大寫[規範]
  • 常量都是 public static final 的
  • 常量在類加載的時候完成初始化,存儲在 JVM 的方法區中
  • 常量是值不可改變的變量

5. 代碼塊

靜態屬性和靜態代碼塊按照代碼順序執行,實例代碼塊和成員屬性同理

靜態代碼塊

使用 static 關鍵字可以定義靜態語句塊,靜態語句塊具有以下特點

  • 靜態語句塊在類加載時執行,在 main 方法執行之前就已經執行了。
  • 類只加載一次,所以靜態語句塊也是隻執行一次
  • 一個類中可以編寫多個靜態語句塊,執行順序是: 自上而下依次執行。
  • 靜態語句塊的使用時機:當程序需要在類加載的時候就做一些事情,可以在靜態語句塊中來實現

實例語句塊

實例語句塊和靜態代碼塊沒有關係,實例語句塊有以下特點

  • 實例語句塊在構造方法執行之前執行,構造函數執行一次,實例語句塊對應執行一次。
  • 每調用一次構造函數之前就會執行一次實例語句塊
  • 實例語句塊可以編寫多個,也是按照自上而下的順序依次執行。
  • 實例語句塊使用時機: 當程序需要在對象初始化時刻就做一些事情,可以在實例語句塊中實現
//靜態語句塊
static{
	System.out.println(1);
}

//實例語句塊
{
	System.out.println(1);
}

6. 類的繼承

如何實現Java多繼承?

繼承特徵

  • 繼承是面向對象的重要概念,軟件中的繼承和現實中的繼承概念是一樣的

  • 繼承是實現軟件可重用性的重要手段,如: A 繼承 B, A 就擁有了 B 的所有特性,如現實世界中的兒子繼承父親的財產,兒子不用努力就有了財產,這就是重用性

  • Java 中只支持類的單繼承,也就是說 A 只能繼承 B, A 不能同時繼承 C

  • Java 中的繼承使用 extends 關鍵字,語法格式:

    [修飾符] class 子類 extends 父類 {
        
    }  
    

方法的重載的條件

  • 方法名相同
  • 方法的參數類型,個數,順序至少有一個不同
  • 方法的返回類型可以不同(不依靠返回類型來區分重載)
  • 方法的修飾符可以不同,因爲方法重載和修飾符沒有任何關係
  • 方法重載只出現在同一個類中

方法的覆蓋(Override)的條件:

  • 必須要有繼承關係
  • 覆蓋只能出現在子類中,如果沒有繼承關係,不存在覆蓋,只存在重載
  • 在子類中被覆蓋的方法,必須和父類中的方法完全一樣,也就是方法名, 返回類型、參數列表,
    完全一樣
  • 子類方法的訪問權限不能小於父類方法的訪問權限
  • 子類方法不能拋出比父類方法更多的異常,但可以拋出父類方法異常的子異常
  • 父類的靜態方法不能被子類覆蓋
  • 父類的私有方法不能覆蓋
  • 覆蓋是針對成員方法,而非屬性

爲什麼需要覆蓋?

  • 就是要改變父類的行爲。
  • 方法重寫之後,“子類對象”執行的一定是重寫之後的方法,也體現了就近原則

7. static、構造方法和父子類的調用順序

要點

  • 靜態的代碼塊一定比構造方法先執行
  • 如果都是靜態代碼,一個類裏面,按照先後順序執行,父子之間,父類靜態代碼塊先執行
  • 靜態代碼只會執行一次,多次 new 新的對象,構造方法,非靜態代碼塊會多次執行
class Parent {
    static {
        System.out.println("父類的靜態塊");
    }
    private static String staticStr = getStaticStr();
    private String str = getStr();
    {
        System.out.println("父類的實例塊");
    }
    public Parent() {
        System.out.println("父類的構造方法");
    }
    private static String getStaticStr() {
        System.out.println("父類的靜態屬性初始化");
        return null;
    }
    private String getStr() {
        System.out.println("父類的實例屬性初始化");
        return null;
    }
}

class Child extends Parent {
    private static String staticStr = getStaticStr();

    static {
        System.out.println("子類的靜態塊");
    }

    {
        System.out.println("子類的實例塊");
    }

    public Child() {
        System.out.println("子類的構造方法");
    }

    private String str = getStr();

    private static String getStaticStr() {
        System.out.println("子類的靜態屬性初始化");
        return null;
    }

    private String getStr() {
        System.out.println("子類的實例屬性初始化");
        return null;
    }
}

public class Test {
    public static void main(String[] args) {
        new Child();
    }
}

分析

  • 首先先加載類到JVM的方法區中,則先加載靜態的內容,比如靜態代碼塊和靜態屬性,並且先加載父類,且按照代碼順序加載

  • 接着加載對象到堆內存中,先加載父類的實例語句塊和實例屬性,按照父類優先,根據代碼順序加載,最後加載構造方法

執行結果

父類的靜態塊
父類的靜態屬性初始化
子類的靜態屬性初始化
子類的靜態塊
父類的實例屬性初始化
父類的實例塊
父類的構造方法
子類的實例塊
子類的實例屬性初始化
子類的構造方法

8. 抽象類和接口(***)

抽象類

看我們以前示例中的 Person、 Student 和 Employee,從我們使用的角度來看主要對 Student 和 Employee 進行實例化, Person 中主要包含了一些公共的屬性和方法,而 Person 我們通常不會實例化,所以我們可以把它
定義成抽象的:

  • 在 java 中採用 abstract 關鍵字定義的類就是抽象類,採用 abstract 關鍵字定義的方法就是抽象方法

  • 抽象的方法只需在抽象類中,提供聲明,不需要實現

  • 如果一個類中含有抽象方法,那麼這個類必須定義成抽象類,一個抽象類不一定含有抽象方法

  • 如果這個類是抽象的,那麼這個類被子類繼承,抽象方法必須被重寫。如果在子類中不復寫該抽象方法,那麼必須將此類再次聲明爲抽象類

  • 抽象的類是不能實例化的,就像現實世界中人其實是抽象的,張三、李四纔是具體的

  • 抽象類不能被 final 修飾

  • 抽象方法不能被 final 修飾,因爲抽象方法就是被子類實現的

抽象類中可以包含方法實現,可以將一些公共的代碼放到抽象類中,另外在抽象類中可以定義一些抽象的方法,這樣就會存在一個約束,而子類必須實現我們定義的方法,如: teacher 必須實現 printInfo 方法, Student也必須實現 printInfo 方法,方法名稱不能修改,必須爲 printInfo,這樣就能實現多態的機制,有了多態的機制,我們在運行期就可以動態的調用子類的方法。所以在運行期可以靈活的互換實現。

抽象類和普通類的區別?

抽象類 普通類
不能被實例化,也就是使用new關鍵字 可以被實例化
權限限定於Public和Protected,因爲需要子類去繼承抽象類
JDK 1.8以前,抽象類的方法默認訪問權限爲protected
JDK 1.8時,抽象類的方法默認訪問權限變爲default
沒有權限限制
如果一個類繼承抽象類,則必須實現抽象類的抽象方法
如果沒有實現抽象方法,則該類必須定義成抽象類
不強制實現父類的方法

接口

:JDK 1.8 以後,接口裏可以有靜態方法和方法體了。

接口我們可以看作是抽象類的一種特殊情況,在接口中只能定義抽象的方法和常量(完全抽象)

  • 接口中的方法默認都是 public abstract 的(可以省略寫),不能更改
  • 接口中的變量默認都是 public static final 的(省略不寫),不能更改,所以必須顯示的初始化
    注意:接口裏的所有數據都是 public 修飾的!
  • 如果一個非抽象的類實現了接口,那麼接口中所有的方法必須實現
  • 一類可以實現多個接口,接口和接口之間支持也是多繼承的 ,但接口之間不能實現

在 java 中接口其實描述了類需要做的事情,類要遵循接口的定義來做事,使用接口到底有什麼本質的好
處?可以歸納爲兩點:

  • 採用接口明確的聲明瞭它所能提供的服務
  • 解決了 Java 單繼承的問題

接口和抽象類的區別?

接口 抽象類
不能被實例化 不能被實例化
需要被子類實現,並實現接口的方法 需要被子類繼承,並實現抽象方法
只能做方法的聲明(JDK1.8之後允許方法體) 可以做方法的聲明,也可以做方法的實現
如果子類不能實現接口中的所有方法,則該類只能是抽象類 如果子類不能實現抽象類的所有抽象,則該類只能是抽象類
屬性只能是靜態的常量 沒有限制
接口與接口之間可以多繼承 只能單繼承

9. 類之間的關係

泛化關係

  • 類與類之間的繼承以及接口與接口之間的繼承

實現關係

  • 類對接口的實現

關聯關係

  • 一個類中屬性是另個類

    public class 學生 {
        private 班級 班級;
        // getter/setter
    }
    public class 班級 {
    }
    

聚合關係

  • 是關聯關係的一種,有着較強的關聯關係

  • 在java中一個類是整體,使用對象數組包含另個類;另個類屬於某個整體

    public class 汽車 {
        private 輪胎集合 輪胎;
        //getter/setter
    }
    public class 輪胎 {
        private 汽車 汽車;
        //getter/setter
    }
    

依賴關係

  • 依賴關係是比關聯關係弱的關係,在 java 語言中體現爲返回值,參數,局部變量和靜態方法調用

    public class Test {
        public static void main(String[] args) {
            Person person = new Person();
        }
    }
    class Person {
    }
    

10. Object類

  • Object 類是所有 Java 類的根基類
  • 如果在類的聲明中未使用 extends 關鍵字指明其基類,則默認基類爲 Object 類

equals

equals的源碼是這樣寫的

public boolean equals(Object obj) {
    //this - s1
    //obj - s2
    return (this == obj);
}

所以,默認情況下比較的是地址值,但是可以讓我們覆寫該方法,實現對象的比較。

如何覆寫equals方法?

  • 首先爲了提高效率,需要用==判斷是否是同一個對象,如果是直接返回true
  • 接着爲了提高健壯性,判斷是否對象是否是該類的一個對象,如果是,需要對其向下轉型
  • 最後是比較的邏輯
public class ObjectDemo {
    public static void main(String args[]){
        Student student1 = new Student("生命壹號",22,"成都");
        Student student2 = new Student("生命壹號",22,"成都"); 
        System.out.println(student1==student2);
        System.out.println(student1.equals(student2));
    }
 }
 class Student {
     private String name;
     private int age;
     private String address;
     public Student(String name,int age,String address){
         this.name = name;
         this.age = age;
         this.address = address;
     }
     //重寫Object類中的equals方法(比較兩個對象的值是否相等)
     public boolean equals(Object obj){
         //爲了提高效率:如果兩個內存地址相等,那麼一定是指向同一個對內存中的對象,就無需比較兩個對象的屬性值(自己跟自己比,沒啥意義嘛)
         if(this==obj){
             return true;
         }
         
         //爲了提供程序的健壯性
         //我先判斷一下,obj是不是學生的一個對象,如果是,再做向下轉型,如果不是,直接返回false。
         //這個時候,我們要判斷的是對象是否是某個類的對象?
         //記住一個格式:對象名 instanceof 類名。表示:判斷該對象是否是該類的一個對象       
         if(!(obj instanceof Student)){        
             return false;                
         }
         
         //如果是就繼續
         Student s = (Student)obj;//強制轉換,即向下轉型(畢竟Object類型沒有具體的對象屬性)        
         return this.name.equals(s.name) && this.age == s.age && this.address.equals(s.address);//判斷兩個對象的屬性值是否相等
     }
 }

==與 equals()區別?

== equals()
等號比較的是值, 特別是比較引用類型,比較的是引用的內存地址的那個值 默認源碼使用的是==,但是可以通過覆寫該方法,實現對象的比較

對於基本數據的包裝類型(Byte, Short, Character,Integer, Float, Double, Long, Boolean)除了 Float和 Double 之外,其他的六種都是實現了常量池的,因此對於這些數據類型而言,一般我們也可以直接通過==來判斷是否相等

public class Test {
    public static void main(String[] args){
        Integer a = 127;
        Integer b = 127;
        System.out.println(a==b);//true

        Integer c = 128;
        Integer d = 128;
        System.out.println(c==d);//false
    }
}

因爲 Integer 在常量池中的存儲範圍爲[-128,127], 127 在這範圍內,因此是直接存儲於常量池的,而
128 不在這範圍內,所以會在堆內存中創建一個新的對象來保存這個值,所以 m, n 分別指向了兩個不同的
對象地址,故而導致了不相等。

finalize

當垃圾收集器將要收集某個垃圾對象時將會調用 finalize,建議不要使用此方法,因爲此方法的運行時間不確定,如果執行此方法出現錯誤,程序不會報告,仍然繼續運行

JVM當看到對象類含有finalize函數,會將該對象交給FinalizerThread處理,但是處理的時間不確定。

11. 訪問控制權限

範圍由大到小的排序public > protected > 缺省 > private

對類的修飾只有public和缺省,內部類除外

修飾符 類的內部 同一個包中 子類 任何地方
public Y Y Y Y
protected Y Y Y N
缺省 Y Y N N
private Y N N N

總結爲一句話private修飾的只能類的內部調用;缺省的可以在一個包中調用;protected擴展到了子類中,比如繼承某個類,則可以使用那個類的屬性和方法;public可以在任何地方訪問。

12 內部類

分爲四種內部類

  • 實例內部類:在類的內部定義的普通類
  • 靜態內部類:在類的內部定義的靜態類
  • 局部內部類:在方法中定義的普通類
  • 匿名內部類:方法使用中定義的普通類,主要用來實現接口

實例內部類

特點

  • 創建實例內部類,外部類的實例必須已經創建
  • 實例內部類會持有外部類的引用,可以直接訪問外部類的屬性
  • 不允許有靜態聲明
public class OuterClass
{
    //靜態變量
    private static String s1 = "靜態變量";
    //實例變量
    private String s2 = "實例變量";
    //實例內部類
    public class InnerClass
    {
        //編譯錯誤,實例內部類中不允許有靜態的聲明
        public static void m1(){}
        //實例方法
        public void m2(){
            System.out.println(s1);
            System.out.println(s2);
        }
    }
    //入口
    public static void main(String[] args){
        OuterClass oc = new OuterClass();
        InnerClass innerClass = oc.new InnerClass();//??
        innerClass.m2();
    }
}

靜態內部類

特點

  • 靜態內部類不會持有外部的類的引用
  • 創建時可以不用創建外部類,在靜態內部類中只能直接訪問外部類中所有的靜態數據。
  • 靜態內部類等同於靜態變量
public class OuterClass
{
    //靜態變量
    private static String s1 = "靜態變量";
    //實例變量
    private String s2 = "實例變量";
    //靜態內部類
    //靜態內部類可以使用任何一個訪問控制權限修飾符修飾。
    protected static class InnerClass{
        //靜態方法
        public static void m1(){
            System.out.println(s1);
        //System.out.println(s2);
        }
        //實例方法
        public void m2(){
            System.out.println(s1);
        //System.out.println(s2);
        }
    } //入口
    public static void main(String[] args){
        OuterClass.InnerClass.m1();//外部類.可以省略
        InnerClass innerClass = new OuterClass.InnerClass();
        innerClass.m2();
    }
}

局部內部類

  • 局部內部類等同於局部變量
  • 局部內部類是在方法體中聲明的類,該類只能在方法體中使用
  • 局部內部類不能使用 public 、 protected 、 private 修飾
  • 局部內部類訪問本地變量的時候,方法中的參數需要使用 final 修飾
public class OuterClass {
    private int a = 100;
    //局部變量在內部類中使用必須採用 final 修飾
    public void method1(final int temp) {
        class Inner3 {
            int i1 = 10;
            //可以訪問外部類的成員變量
            int i2 = a;
            int i3 = temp;
        }
        //使用內部類
        Inner3 inner3 = new Inner3();
        System.out.println(inner3.i1);
        System.out.println(inner3.i3);
    }
    public static void main(String[] args) {
        OuterClass out = new OuterClass ();
        out.method1(300);
    }
}

匿名內部類

  • 是一種特殊的內部類,該類沒有名字
  • 通過new 關鍵字創建,並加上方法體
  • 主要用於實現接口
public class Test {
    public static void main(String[] args){
        //在方法中實現接口
        new Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        }).run();
    }
}

四、異常

任意的異常都是在運行時發生的!!!

1. 異常的體系

所有的異常都是Throwable的子類

image-20200628105116238

Thorwable有兩個直接子類Error和Exception

Error:

  • 在 Java 中只要 Error 發生了就一種結果——退出 JVM,例如 StackOverError

Exception的直接子類

  • Exception 的直接子類叫做編譯時異常、受控異常、檢查異常。它雖然叫做編譯時異常,但是它不是發
    生在編譯階段的異常, 之所以叫做編譯時異常是因爲編譯時異常要求必須在程序編譯的階段就手動的處理,如果不處理這些異常的話,程序無法編譯通過。

  • 對於編譯時異常有兩種手段處理,一是 try catch 捕獲,一是 throws 拋出

RuntimeException 的直接子類

  • RuntimeException 的直接子類叫做運行時異常、非受控異常、非檢查異常。這種異常不要求在程序編譯
    階段處理,編譯也可以通過
  • 比如說除0異常

自定義異常

  • JDK 提供的異常不能夠滿足要求的情況下用戶可以自己自定義異常,可以根據實際情況選擇繼承Exception 或者 RuntimeException 兩種形式。

2. 說出幾個常見的異常

Error

  • StackOverError(堆溢出)
  • OutOfMemoryError(內存溢出)

受控異常

  • IOException(IO異常)
  • SQLException(SQL異常)
  • ClassNotFoundException(找不到指定的類異常)

不受控異常

  • NullPointerException(空指針異常)

  • ArithmeticException(算術異常)

  • ArrayIndexOutOfBoundsException(數組下表越界異常)

3. 異常處理

異常的捕獲應該從小到大

一般有兩種方式

  • try…catch…finally…
  • thorws拋給調用者

finally 在任何情況下都會執行,除非JVM掛掉,通常在 finally 裏關閉資源

public class ExceptionTest12 {
    public static void main(String[] args) {
        int r = method1();
        //輸出爲: 100? 50?
        System.out.println(r);//輸出是50
    }
    private static int method1() {
        int a = 10;
        try {
            a = 50;
            return a;//直接返回值
        }finally {
            a = 100;//該語句也會執行,只是a已經返回
        }
    }
}

throws 和 throw 的區別 ?

throws throw
thorws 是聲明異常 thorws 是聲明異常
用在函數上 用在函數內部
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章