2021精選 Java面試題附答案(一)

1.什麼是Java

Java是一門面向對象的高級編程語言,不只吸取了C++語言的各類優勢,好比繼承了C++語言面嚮對象的技術核心。還摒棄了C++裏難以理解的多繼承、指針等概念,,同時也增長了垃圾回收機制,釋放掉不被使用的內存空間,解決了管理內存空間的煩惱。java

所以Java語言具備功能強大和簡單易用兩個特徵。Java語言做爲靜態面向對象編程語言的表明,極好地實現了面向對象理論,容許程序員以優雅的思惟方式進行復雜的編程 。mysql

面試手冊PDF下載連接https://shimo.im/docs/Wyj8QRpq39jkC3jD
面試手冊PDF下載連接https://shimo.im/docs/Wyj8QRpq39jkC3jD
面試手冊PDF下載連接https://shimo.im/docs/Wyj8QRpq39jkC3jD程序員

2. Java的特色有哪些

Java 語言是一種分佈式的面嚮對象語言,具備面向對象、平臺無關性、簡單性、解釋執行、多線程、安全性等不少特色,下面針對這些特色進行逐一介紹。web

1. 面向對象面試

Java 是一種面向對象的語言,它對對象中的類、對象、繼承、封裝、多態、接口、包等均有很好的支持。爲了簡單起見,Java 只支持類之間的單繼承,可是可使用接口來實現多繼承。使用 Java 語言開發程序,須要採用面向對象的思想設計程序和編寫代碼。算法

2. 平臺無關性sql

平臺無關性的具體表如今於,Java 是「一次編寫,處處運行(Write Once,Run any Where)」的語言,所以採用 Java 語言編寫的程序具備很好的可移植性,而保證這一點的正是 Java 的虛擬機機制。在引入虛擬機以後,Java 語言在不一樣的平臺上運行不須要從新編譯。編程

Java 語言使用 Java 虛擬機機制屏蔽了具體平臺的相關信息,使得 Java 語言編譯的程序只需生成虛擬機上的目標代碼,就能夠在多種平臺上不加修改地運行。數組

3. 簡單性緩存

Java 語言的語法與 C 語言和 C++ 語言很相近,使得不少程序員學起來很容易。對 Java 來講,它捨棄了不少 C++ 中難以理解的特性,如操做符的重載和多繼承等,並且 Java 語言不使用指針,加入了垃圾回收機制,解決了程序員須要管理內存的問題,使編程變得更加簡單。

4. 解釋執行

Java 程序在 Java 平臺運行時會被編譯成字節碼文件,而後能夠在有 Java 環境的操做系統上運行。在運行文件時,Java 的解釋器對這些字節碼進行解釋執行,執行過程當中須要加入的類在鏈接階段被載入到運行環境中。

5. 多線程

Java 語言是多線程的,這也是 Java 語言的一大特性,它必須由 Thread 類和它的子類來建立。Java 支持多個線程同時執行,並提供多線程之間的同步機制。任何一個線程都有本身的 run() 方法,要執行的方法就寫在 run() 方法體內。

6. 分佈式

Java 語言支持 Internet 應用的開發,在 Java 的基本應用編程接口中就有一個網絡應用編程接口,它提供了網絡應用編程的類庫,包括 URL、URLConnection、Socket 等。Java 的 RIM 機制也是開發分佈式應用的重要手段。

7. 健壯性

Java 的強類型機制、異常處理、垃圾回收機制等都是 Java 健壯性的重要保證。對指針的丟棄是 Java 的一大進步。另外,Java 的異常機制也是健壯性的一大致現。

8. 高性能

Java 的高性能主要是相對其餘高級腳本語言來講的,隨着 JIT(Just in Time)的發展,Java 的運行速度也愈來愈高。

9. 安全性

Java 一般被用在網絡環境中,爲此,Java 提供了一個安全機制以防止惡意代碼的攻擊。除了 Java 語言具備許多的安全特性之外,Java 還對經過網絡下載的類增長一個安全防範機制,分配不一樣的名字空間以防替代本地的同名類,幷包含安全管理機制。

Java 語言的衆多特性使其在衆多的編程語言中佔有較大的市場份額,Java 語言對對象的支持和強大的 API 使得編程工做變得更加容易和快捷,大大下降了程序的開發成本。Java 的「一次編寫,處處執行」正是它吸引衆多商家和編程人員的一大優點。

3. JDK和JRE和JVM的區別

1. JDK

JDK(Java SE Development Kit),Java標準的開發包,提供了編譯、運行Java程序所須要的各類工具和資源,包括了Java編譯器、Java運行時環境、以及經常使用的Java類庫等。

2. JRE

JRE(Java Runtime Environment),Java運行時環境,用於解釋執行Java的字節碼文件。普通用戶只須要安裝JRE來運行Java程序便可,而做爲一名程序員必須安裝JDK,來編譯、調試程序。

3. JVM

JVM(Java Virtual Mechinal),Java虛擬機,是JRE的一部分。它是整個Java實現跨平臺的核心,負責解釋執行字節碼文件,是可運行Java字節碼文件的虛擬計算機。全部平臺上的JVM向編譯器提供相同的接口,而編譯器只須要面向虛擬機,生成虛擬機能識別的代碼,而後由虛擬機來解釋執行。

當使用Java編譯器編譯Java程序時,生成的是與平臺無關的字節碼,這些字節碼只面向JVM。也就是說JVM是運行Java字節碼的虛擬機。

不一樣平臺的JVM是不一樣的,可是他們都提供了相同的接口。JVM是Java程序跨平臺的關鍵部分,只要爲不一樣平臺實現了相同的虛擬機,編譯後的Java字節碼就能夠在該平臺上運行。

爲何要採用字節碼:

在 Java 中,JVM 能夠理解的代碼就叫作字節碼(即Java源代碼通過虛擬機編譯器編譯後擴展名爲 .class 的文件),它不面向任何特定的處理器,只面向虛擬機。Java 語言經過字節碼的方式,在必定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留了解釋型語言可移植的特色。因此 Java 程序運行時比較高效,並且,因爲字節碼並不針對一種特定的機器,所以,Java 程序無須從新編譯即可在多種不一樣操做系統的計算機上運行。

什麼是跨平臺:

所謂跨平臺性,是指java語言編寫的程序,一次編譯後,能夠在多個系統平臺上運行。

實現原理:Java程序是經過java虛擬機在系統平臺上運行的,只要該系統能夠安裝相應的java虛擬機,該系統就能夠運行java程序。

Java 程序從源代碼到運行須要三步:

4. 總結

  1. JDK 用於開發,JRE 用於運行java程序 ;若是隻是運行Java程序,能夠只安裝JRE,無序安裝JDK。
  2. JDk包含JRE,JDK 和 JRE 中都包含 JVM。
  3. JVM 是 Java 編程語言的核心而且具備平臺獨立性。

4. Oracle JDK 和 OpenJDK 的對比

  • Oracle JDK版本將每三年發佈一次,而OpenJDK版本每三個月發佈一次;

  • OpenJDK 是一個參考模型而且是徹底開源的,而Oracle JDK是OpenJDK的一個實現,並非徹底開源的;

  • Oracle JDK 比 OpenJDK 更穩定。OpenJDK和Oracle JDK的代碼幾乎相同,但Oracle JDK有更多的類和一些錯誤修復。所以,若是您想開發企業/商業軟件,我建議您選擇Oracle JDK,由於它通過了完全的測試和穩定。某些狀況下,有些人提到在使用OpenJDK 可能會遇到了許多應用程序崩潰的問題,可是,只需切換到Oracle JDK就能夠解決問題;

  • 在響應性和JVM性能方面,Oracle JDK與OpenJDK相比提供了更好的性能;

  • Oracle JDK不會爲即將發佈的版本提供長期支持,用戶每次都必須經過更新到最新版本得到支持來獲取最新版本;

  • Oracle JDK根據二進制代碼許可協議得到許可,而OpenJDK根據GPL v2許可得到許可。

5. Java有哪些數據類型

Java有 8 種基本數據類型,分別爲:

  • 6 種數字類型 (四個整數形,兩個浮點型):byte、short、int、long、float、double

  • 1 種字符類型:char

  • 1 種布爾型:boolean。

byte:

  • byte 數據類型是8位、有符號的,以二進制補碼錶示的整數;
  • 最小值是 -128(-2^7)
  • 最大值是 127(2^7-1)
  • 默認值是 0
  • byte 類型用在大型數組中節約空間,主要代替整數,由於 byte 變量佔用的空間只有 int 類型的四分之一;
  • 例子:byte a = 100,byte b = -50。

short:

  • short 數據類型是 16 位、有符號的以二進制補碼錶示的整數
  • 最小值是 -32768(-2^15)
  • 最大值是 32767(2^15 - 1)
  • Short 數據類型也能夠像 byte 那樣節省空間。一個short變量是int型變量所佔空間的二分之一;
  • 默認值是 0
  • 例子:short s = 1000,short r = -20000。

int:

  • int 數據類型是32位、有符號的以二進制補碼錶示的整數;
  • 最小值是 -2,147,483,648(-2^31)
  • 最大值是 2,147,483,647(2^31 - 1)
  • 通常地整型變量默認爲 int 類型;
  • 默認值是 0
  • 例子:int a = 100000, int b = -200000。

long:

  • 注意:Java 裏使用 long 類型的數據必定要在數值後面加上 L,不然將做爲整型解析

  • long 數據類型是 64 位、有符號的以二進制補碼錶示的整數;

  • 最小值是 -9,223,372,036,854,775,808(-2^63)

  • 最大值是 9,223,372,036,854,775,807(2^63 -1)

  • 這種類型主要使用在須要比較大整數的系統上;

  • 默認值是 0L

  • 例子: long a = 100000L,Long b = -200000L。
    "L"理論上不分大小寫,可是若寫成"l"容易與數字"1"混淆,不容易分辯。因此最好大寫。

float:

  • float 數據類型是單精度、32位、符合IEEE 754標準的浮點數;
  • float 在儲存大型浮點數組的時候可節省內存空間;
  • 默認值是 0.0f
  • 浮點數不能用來表示精確的值,如貨幣;
  • 例子:float f1 = 234.5f。

double:

  • double 數據類型是雙精度、64 位、符合IEEE 754標準的浮點數;
  • 浮點數的默認類型爲double類型;
  • double類型一樣不能表示精確的值,如貨幣;
  • 默認值是 0.0d
  • 例子:double d1 = 123.4。

char:

  • char類型是一個單一的 16 位 Unicode 字符;
  • 最小值是 \u0000(即爲 0);
  • 最大值是 \uffff(即爲 65535);
  • char 數據類型能夠儲存任何字符;
  • 例子:char letter = 'A';(單引號

boolean:

  • boolean數據類型表示一位的信息;
  • 只有兩個取值:true 和 false;
  • 這種類型只做爲一種標誌來記錄 true/false 狀況;
  • 默認值是 false
  • 例子:boolean one = true。

這八種基本類型都有對應的包裝類分別爲:Byte、Short、Integer、Long、Float、Double、Character、Boolean

類型名稱 字節、位數 最小值 最大值 默認值 例子
byte字節 1字節,8位 -128(-2^7) 127(2^7-1) 0 byte a = 100,byte b = -50
short短整型 2字節,16位 -32768(-2^15) 32767(2^15 - 1) 0 short s = 1000,short r = -20000
int整形 4字節,32位 -2,147,483,648(-2^31) 2,147,483,647(2^31 - 1) 0 int a = 100000, int b = -200000
lang長整型 8字節,64位 -9,223,372,036,854,775,808(-2^63) 9,223,372,036,854,775,807(2^63 -1) 0L long a = 100000L,Long b = -200000L
double雙精度 8字節,64位 double類型一樣不能表示精確的值,如貨幣 0.0d double d1 = 123.4
float單精度 4字節,32位 在儲存大型浮點數組的時候可節省內存空間 不一樣統計精準的貨幣值 0.0f float f1 = 234.5f
char字符 2字節,16位 \u0000(即爲0) \uffff(即爲65,535) 能夠儲存任何字符 char letter = 'A';
boolean布爾 返回true和false兩個值 這種類型只做爲一種標誌來記錄 true/false 狀況; 只有兩個取值:true 和 false; false boolean one = true

6. Java中引用數據類型有哪些,它們與基本數據類型有什麼區別?

引用數據類型分3種:類,接口,數組;

簡單來講,只要不是基本數據類型.都是引用數據類型。 那他們有什麼不一樣呢?

一、從概念方面來講

1,基本數據類型:變量名指向具體的數值

2,引用數據類型:變量名不是指向具體的數值,而是指向存數據的內存地址,.也及時hash值

二、從內存的構建方面來講(內存中,有堆內存和棧內存二者)

1,基本數據類型:被建立時,在棧內存中會被劃分出必定的內存,並將數值存儲在該內存中.

2,引用數據類型:被建立時,首先會在棧內存中分配一塊空間,而後在堆內存中也會分配一塊具體的空間用來存儲數據的具體信息,即hash值,而後由棧中引用指向堆中的對象地址.

舉個例子

//基本數據類型做爲方法參數被調用
public class Main{
	public static void main(String[] args){
		//基本數據類型
		int i = 1;
		int j = 1;
		double d = 1.2;
 
		//引用數據類型
		String str = "Hello";
		String str1= "Hello";
	}
}

由上圖可知,基本數據類型中會存在兩個相同的1,而引用型類型就不會存在相同的數據。
假如"hello"的引用地址是xxxxx1,聲明str變量並其賦值"hello"實際上就是讓str變量引用了"hello"的內存地址,這個內存地址就存儲在堆內存中,是不會改變的,當再次聲明變量str1也是賦值爲"hello"時,此時就會在堆內存中查詢是否有"hello"這個地址,若是堆內存中已經存在這個地址了,就不會再次建立了,而是讓str1變量也指向xxxxx1這個地址,若是沒有的話,就會從新建立一個地址給str1變量。

7. 從使用方面來講

1,基本數據類型:判斷數據是否相等,用和!=判斷。
2,引用數據類型:判斷數據是否相等,用equals()方法,
和!=是比較數值的。而equals()方法是比較內存地址的。

補充:數據類型選擇的原則

  • 若是要表示整數就使用int,表示小數就使用double;

  • 若是要描述日期時間數字或者表示文件(或內存)大小用long;

  • 若是要實現內容傳遞或者編碼轉換使用byte;

  • 若是要實現邏輯的控制,可使用booleam;

  • 若是要使用中文,使用char避免中文亂碼;

  • 若是按照保存範圍:byte < int < long < double;

8. Java中的自動裝箱與拆箱

什麼是自動裝箱拆箱?

從下面的代碼中就能夠看到裝箱和拆箱的過程

//自動裝箱
Integer total = 99;

//自定拆箱
int totalprim = total;

裝箱就是自動將基本數據類型轉換爲包裝器類型;拆箱就是自動將包裝器類型轉換爲基本數據類型。

在Java SE5以前,自動裝箱要這樣寫:Integer i = new` `Integer(10``);

對於Java的自動裝箱和拆箱,咱們看看源碼編譯後的class文件,其實裝箱調用包裝類的valueOf方法,拆箱調用的是Integer.Value方法,下面就是變編譯後的代碼:

常見面試一:

這段代碼輸出什麼?

public class Main {
    public static void main(String[] args) {
         
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;
         
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

答案是:

true
false

爲何會出現這樣的結果?輸出結果代表i1和i2指向的是同一個對象,而i3和i4指向的是不一樣的對象。此時只需一看源碼便知究竟,下面這段代碼是Integer的valueOf方法的具體實現:

public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }
private static class IntegerCache {
        static final int high;
        static final Integer cache[];

        static {
            final int low = -128;

            // high value may be configured by property
            int h = 127;
            if (integerCacheHighPropValue != null) {
                // Use Long.decode here to avoid invoking methods that
                // require Integer's autoboxing cache to be initialized
                int i = Long.decode(integerCacheHighPropValue).intValue();
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - -low);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

從這2段代碼能夠看出,在經過valueOf方法建立Integer對象的時候,若是數值在[-128,127]之間,便返回指向IntegerCache.cache中已經存在的對象的引用;不然建立一個新的Integer對象。

上面的代碼中i1和i2的數值爲100,所以會直接從cache中取已經存在的對象,因此i1和i2指向的是同一個對象,而i3和i4則是分別指向不一樣的對象。

常見面試二:

public class Main {
    public static void main(String[] args) {
         
        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;
         
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

輸出結果爲:

false
false

緣由很簡單,在某個範圍內的整型數值的個數是有限的,而浮點數卻不是。

9. 爲何要有包裝類型?

讓基本數據類型也具備對象的特徵

基本類型 包裝器類型
boolean Boolean
char Character
int Integer
byte Byte
short Short
long Long
float Float
double Double

爲了讓基本類型也具備對象的特徵,就出現了包裝類型(如咱們在使用集合類型Collection時就必定要使用包裝類型而非基本類型)由於容器都是裝object的,這是就須要這些基本類型的包裝器類了。

自動裝箱:new Integer(6);,底層調用:Integer.valueOf(6)

自動拆箱: int i = new Integer(6);,底層調用i.intValue();方法實現。

Integer i  = 6;
Integer j = 6;
System.out.println(i==j);

答案在下面這段代碼中找:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

兩者的區別:

  1. 聲明方式不一樣:基本類型不使用new關鍵字,而包裝類型須要使用new關鍵字來在堆中分配存儲空間
  2. 存儲方式及位置不一樣:基本類型是直接將變量值存儲在棧中,而包裝類型是將對象放在堆中,而後經過引用來使用;
  3. 初始值不一樣:基本類型的初始值如int爲0,boolean爲false,而包裝類型的初始值爲null;
  4. 使用方式不一樣:基本類型直接賦值直接使用就好,而包裝類型在集合如Collection、Map時會使用到。

10. a=a+b與a+=b有什麼區別嗎?

+=操做符會進行隱式自動類型轉換,此處a+=b隱式的將加操做的結果類型強制轉換爲持有結果的類型,而a=a+b則不會自動進行類型轉換.如:

byte a = 127;
byte b = 127;
b = a + b; // 報編譯錯誤:cannot convert from int to byte
b += a;

如下代碼是否有錯,有的話怎麼改?

short s1= 1;
s1 = s1 + 1;

有錯誤.short類型在進行運算時會自動提高爲int類型,也就是說s1+1的運算結果是int類型,而s1是short類型,此時編譯器會報錯.

正確寫法:

short s1= 1; 
s1 += 1;

+=操做符會對右邊的表達式結果強轉匹配左邊的數據類型,因此沒錯.

11. 能將 int 強制轉換爲 byte 類型的變量嗎?若是該值大於 byte 類型的範圍,將會出現什麼現象?

咱們能夠作強制轉換,可是 Java 中 int 是 32 位的,而 byte 是 8 位的,因此,若是強制轉化,int 類型的高 24 位將會被丟棄,由於byte 類型的範圍是從 -128 到 127

12. Java程序是如何執行的

咱們平常的工做中都使用開發工具(IntelliJ IDEA 或 Eclipse 等)能夠很方便的調試程序,或者是經過打包工具把項目打包成 jar 包或者 war 包,放入 Tomcat 等 Web 容器中就能夠正常運行了,但你有沒有想過 Java 程序內部是如何執行的?其實不管是在開發工具中運行仍是在 Tomcat 中運行,Java 程序的執行流程基本都是相同的,它的執行流程以下:

  • 先把 Java 代碼編譯成字節碼,也就是把 .java 類型的文件編譯成 .class 類型的文件。這個過程的大體執行流程:Java 源代碼 -> 詞法分析器 -> 語法分析器 -> 語義分析器 -> 字符碼生成器 -> 最終生成字節碼,其中任何一個節點執行失敗就會形成編譯失敗;
  • 把 class 文件放置到 Java 虛擬機,這個虛擬機一般指的是 Oracle 官方自帶的 Hotspot JVM;
  • Java 虛擬機使用類加載器(Class Loader)裝載 class 文件;
  • 類加載完成以後,會進行字節碼效驗,字節碼效驗經過以後 JVM 解釋器會把字節碼翻譯成機器碼交由操做系統執行。但不是全部代碼都是解釋執行的,JVM 對此作了優化,好比,以 Hotspot 虛擬機來講,它自己提供了 JIT(Just In Time)也就是咱們一般所說的動態編譯器,它可以在運行時將熱點代碼編譯爲機器碼,這個時候字節碼就變成了編譯執行。Java 程序執行流程圖以下:

13. final 在 Java 中有什麼做用?

final做爲Java中的關鍵字能夠用於三個地方。用於修飾類、類屬性和類方法。

特徵:凡是引用final關鍵字的地方皆不可修改!

(1)修飾類:表示該類不能被繼承;

(2)修飾方法:表示方法不能被重寫;

(3)修飾變量:表示變量只能一次賦值之後值不能被修改(常量)。

14. final有哪些用法?

final也是不少面試喜歡問的地方,但我以爲這個問題很無聊,一般能回答下如下5點就不錯了:

  • 被final修飾的類不能夠被繼承
  • 被final修飾的方法不能夠被重寫
  • 被final修飾的變量不能夠被改變.若是修飾引用,那麼表示引用不可變,引用指向的內容可變.
  • 被final修飾的方法,JVM會嘗試將其內聯,以提升運行效率
  • 被final修飾的常量,在編譯階段會存入常量池中.

除此以外,編譯器對final域要遵照的兩個重排序規則更好:

在構造函數內對一個final域的寫入,與隨後把這個被構造對象的引用賦值給一個引用變量,這兩個操做之間不能重排序 初次讀一個包含final域的對象的引用,與隨後初次讀這個final域,這兩個操做之間不能重排序.

15. static都有哪些用法?

全部的人都知道static關鍵字這兩個基本的用法:靜態變量和靜態方法.也就是被static所修飾的變量/方法都屬於類的靜態資源,類實例所共享.

除了靜態變量和靜態方法以外,static也用於靜態塊,多用於初始化操做:

public calss PreCache{
    static{
        //執行相關操做
    }
}

此外static也多用於修飾內部類,此時稱之爲靜態內部類.

最後一種用法就是靜態導包,即import static.import static是在JDK 1.5以後引入的新特性,能夠用來指定導入某個類中的靜態資源,而且不須要使用類名,能夠直接使用資源名,好比:

import static java.lang.Math.*;
 
public class Test{
 
    public static void main(String[] args){
        //System.out.println(Math.sin(20));傳統作法
        System.out.println(sin(20));
    }
}

16. static和final區別

關鍵詞 修飾物 影響
final 變量 分配到常量池中,程序不可改變其值
final 方法 子類中將不能被重寫
final 不能被繼承
static 變量 分配在內存堆上,引用都會指向這一個地址而不會從新分配內存
static 方法塊 虛擬機優先加載
static 能夠直接經過類來調用而不須要new

17. 爲何有些java類要實現Serializable接口

爲了網絡進行傳輸或者持久化

什麼是序列化

將對象的狀態信息轉換爲能夠存儲或傳輸的形式的過程

除了實現Serializable接口還有什麼序列化方式

  • Json序列化
  • FastJson序列化
  • ProtoBuff序列化

18. 什麼是java序列化,如何實現java序列化?或者請解釋Serializable接口的做用。

咱們有時候將一個java對象變成字節流的形式傳出去或者從一個字節流中恢復成一個java對象,例如,要將java對象存儲到硬盤或者傳送給網絡上的其餘計算機,這個過程咱們能夠本身寫代碼去把一個java對象變成某個格式的字節流再傳輸。

可是,jre自己就提供了這種支持,咱們能夠調用OutputStreamwriteObject方法來作,若是要讓java幫咱們作,要被傳輸的對象必須實現serializable接口,這樣,javac編譯時就會進行特殊處理,編譯的類才能夠被writeObject方法操做,這就是所謂的序列化。須要被序列化的類必須實現Serializable接口,該接口是一個mini接口,其中沒有須要實現方法,implements Serializable只是爲了標註該對象是可被序列化的。

例如,在web開發中,若是對象被保存在了Session中,tomcat在重啓時要把Session對象序列化到硬盤,這個對象就必須實現Serializable接口。若是對象要通過分佈式系統進行網絡傳輸,被傳輸的對象就必須實現Serializable接口。

19. 什麼是內部類?內部類的做用

內部類的定義

將一個類定義在另外一個類裏面或者一個方法裏面,這樣的類稱爲內部類。

內部類的做用:

一、成員內部類 成員內部類能夠無條件訪問外部類的全部成員屬性和成員方法(包括private成員和靜態成員)。 當成員內部類擁有和外部類同名的成員變量或者方法時,會發生隱藏現象,即默認狀況下訪問的是成員內部類的成員。

二、局部內部類 局部內部類是定義在一個方法或者一個做用域裏面的類,它和成員內部類的區別在於局部內部類的訪問僅限於方法內或者該做用域內。

三、匿名內部類 匿名內部類就是沒有名字的內部類

四、靜態內部類 指被聲明爲static的內部類,他能夠不依賴內部類而實例,而一般的內部類須要實例化外部類,從而實例化。靜態內部類不能夠有與外部類有相同的類名。不能訪問外部類的普通成員變量,可是能夠訪問靜態成員變量和靜態方法(包括私有類型) 一個 靜態內部類去掉static 就是成員內部類,他能夠自由的引用外部類的屬性和方法,不管是靜態仍是非靜態。可是不能夠有靜態屬性和方法

20. Excption與Error包結構

Java可拋出(Throwable)的結構分爲三種類型:被檢查的異常(CheckedException)運行時異常(RuntimeException)錯誤(Error)

一、運行時異常

定義:RuntimeException及其子類都被稱爲運行時異常。

特色:Java編譯器不會檢查它。也就是說,當程序中可能出現這類異常時,假若既"沒有經過throws聲明拋出它",也"沒有用try-catch語句捕獲它",仍是會編譯經過。例如,除數爲零時產生的ArithmeticException異常,數組越界時產生的IndexOutOfBoundsException異常,fail-fast機制產生的ConcurrentModificationException異常(java.util包下面的全部的集合類都是快速失敗的,「快速失敗」也就是fail-fast,它是Java集合的一種錯誤檢測機制。當多個線程對集合進行結構上的改變的操做時,有可能會產生fail-fast機制。記住是有可能,而不是必定。例如:假設存在兩個線程(線程一、線程2),線程1經過Iterator在遍歷集合A中的元素,在某個時候線程2修改了集合A的結構(是結構上面的修改,而不是簡單的修改集合元素的內容),那麼這個時候程序就會拋出 ConcurrentModificationException 異常,從而產生fail-fast機制,這個錯叫併發修改異常。Fail-safe,java.util.concurrent包下面的全部的類都是安全失敗的,在遍歷過程當中,若是已經遍歷的數組上的內容變化了,迭代器不會拋出ConcurrentModificationException異常。若是未遍歷的數組上的內容發生了變化,則有可能反映到迭代過程當中。這就是ConcurrentHashMap迭代器弱一致的表現。ConcurrentHashMap的弱一致性主要是爲了提高效率,是一致性與效率之間的一種權衡。要成爲強一致性,就獲得處使用鎖,甚至是全局鎖,這就與Hashtable和同步的HashMap同樣了。)等,都屬於運行時異常。

常見的五種運行時異常:

  • ClassCastException(類轉換異常)

  • IndexOutOfBoundsException(數組越界)

  • NullPointerException(空指針異常)

  • ArrayStoreException(數據存儲異常,操做數組是類型不一致)

  • BufferOverflowException

二、被檢查異常

定義:Exception類自己,以及Exception的子類中除了"運行時異常"以外的其它子類都屬於被檢查異常。

特色 : Java編譯器會檢查它。 此類異常,要麼經過throws進行聲明拋出,要麼經過try-catch進行捕獲處理,不然不能經過編譯。例如,CloneNotSupportedException就屬於被檢查異常。

當經過clone()接口去克隆一個對象,而該對象對應的類沒有實現Cloneable接口,就會拋出CloneNotSupportedException異常。被檢查異常一般都是能夠恢復的。 如:

IOException

FileNotFoundException

SQLException

被檢查的異常適用於那些不是因程序引發的錯誤狀況,好比:讀取文件時文件不存在引起的FileNotFoundException。然而,不被檢查的異常一般都是因爲糟糕的編程引發的,好比:在對象引用時沒有確保對象非空而引發的NullPointerException

三、錯誤

定義 : Error類及其子類。

特色 : 和運行時異常同樣,編譯器也不會對錯誤進行檢查。

當資源不足、約束失敗、或是其它程序沒法繼續運行的條件發生時,就產生錯誤。程序自己沒法修復這些錯誤的。例如,VirtualMachineError就屬於錯誤。出現這種錯誤會致使程序終止運行。OutOfMemoryError、ThreadDeath。

Java虛擬機規範規定JVM的內存分爲了好幾塊,好比堆,棧,程序計數器,方法區等

21. try {}裏有一個return語句,那麼緊跟在這個try後的finally{}裏的code會不會被執行,何時被執行,在return前仍是後?

咱們知道finally{}中的語句是必定會執行的,那麼這個可能正常脫口而出就是return以前,return以後可能就出了這個方法了,鬼知道跑哪裏去了,但更準確的應該是在return中間執行,請看下面程序代碼的運行結果:

public classTest {
    public static void main(String[]args) {
       System.out.println(newTest().test());;
    }
    static int test()
    {
       intx = 1;
       try
       {
          return x;
       }
       finally
       {
          ++x;
       }
    }
}

執行結果以下:

1

運行結果是1,爲何呢?主函數調用子函數並獲得結果的過程,比如主函數準備一個空罐子,當子函數要返回結果時,先把結果放在罐子裏,而後再將程序邏輯返回到主函數。所謂返回,就是子函數說,我不運行了,你主函數繼續運行吧,這沒什麼結果可言,結果是在說這話以前放進罐子裏的。

22. 運行時異常與通常異常有何異同?

異常表示程序運行過程當中可能出現的非正常狀態,運行時異常表示虛擬機的一般操做中可能遇到的異常,是一種常見運行錯誤。java編譯器要求方法必須聲明拋出可能發生的非運行時異常,可是並不要求必須聲明拋出未被捕獲的運行時異常。

23. error和exception有什麼區別?

error 表示恢復不是不可能但很困難的狀況下的一種嚴重問題。好比說內存溢出。不可能期望程序能處理這樣的狀況。exception表示一種設計或實現問題。也就是說,它表示若是程序運行正常,從不會發生的狀況。

24. 簡單說說Java中的異常處理機制的簡單原理和應用。

異常是指java程序運行時(非編譯)所發生的非正常狀況或錯誤,與現實生活中的事件很類似,現實生活中的事件能夠包含事件發生的時間、地點、人物、情節等信息,能夠用一個對象來表示,Java使用面向對象的方式來處理異常,它把程序中發生的每一個異常也都分別封裝到一個對象來表示的,該對象中包含有異常的信息。

Java對異常進行了分類,不一樣類型的異常分別用不一樣的Java類表示,全部異常的根類爲java.lang.Throwable。

Throwable下面又派生了兩個子類:

  • Error和Exception,Error表示應用程序自己沒法克服和恢復的一種嚴重問題,程序只有奔潰了,例如,說內存溢出和線程死鎖等系統問題。

  • Exception表示程序還可以克服和恢復的問題,其中又分爲系統異常和普通異常:

系統異常是軟件自己缺陷所致使的問題,也就是軟件開發人員考慮不周所致使的問題,軟件使用者沒法克服和恢復這種問題,但在這種問題下還可讓軟件系統繼續運行或者讓軟件掛掉,例如,數組腳本越界(ArrayIndexOutOfBoundsException),空指針異常(NullPointerException)、類轉換異常(ClassCastException);

普通異常是運行環境的變化或異常所致使的問題,是用戶可以克服的問題,例如,網絡斷線,硬盤空間不夠,發生這樣的異常後,程序不該該死掉。

java爲系統異常和普通異常提供了不一樣的解決方案,編譯器強制普通異常必須try..catch處理或用throws聲明繼續拋給上層調用方法處理,因此普通異常也稱爲checked異常,而系統異常能夠處理也能夠不處理,因此,編譯器不強制用try..catch處理或用throws聲明,因此係統異常也稱爲unchecked異常。

25. == 和 equals 的區別是什麼?

"=="

對於基本類型和引用類型 == 的做用效果是不一樣的,以下所示:

  • 基本類型:比較的是值是否相同;
  • 引用類型:比較的是引用是否相同;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

由於 x 和 y 指向的是同一個引用,因此 == 也是 true,而 new String()方法則重寫開闢了內存空間,因此 == 結果爲 false,而 equals 比較的一直是值,因此結果都爲 true。

equals

equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較。看下面的代碼就明白了。

首先來看默認狀況下 equals 比較一個有相同值的對象,代碼以下:

class Cat {
    public Cat(String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

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

Cat c1 = new Cat("葉痕秋");
Cat c2 = new Cat("葉痕秋");
System.out.println(c1.equals(c2)); // false

輸出結果出乎咱們的意料,居然是 false?這是怎麼回事,看了 equals 源碼就知道了,源碼以下:

public boolean equals(Object obj) {
        return (this == obj);
}

原來 equals 本質上就是 ==。

那問題來了,兩個相同值的 String 對象,爲何返回的是 true?代碼以下:

String s1 = new String("葉子");
String s2 = new String("葉子");
System.out.println(s1.equals(s2)); // true

一樣的,當咱們進入 String 的 equals 方法,找到了答案,代碼以下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

原來是 String 重寫了 Object 的 equals 方法,把引用比較改爲了值比較。

總結

== 對於基本類型來講是值比較,對於引用類型來講是比較的是引用;而 equals 默認狀況下是引用比較,只是不少類從新了 equals 方法,好比 String、Integer 等把它變成了值比較,因此通常狀況下 equals 比較的是值是否相等。

26. Hashcode的做用

java的集合有兩類,一類是List,還有一類是Set。前者有序可重複,後者無序不重複。當咱們在set中插入的時候怎麼判斷是否已經存在該元素呢,能夠經過equals方法。可是若是元素太多,用這樣的方法就會比較滿。

因而有人發明了哈希算法來提升集合中查找元素的效率。 這種方式將集合分紅若干個存儲區域,每一個對象能夠計算出一個哈希碼,能夠將哈希碼分組,每組分別對應某個存儲區域,根據一個對象的哈希碼就能夠肯定該對象應該存儲的那個區域。

hashCode方法能夠這樣理解:它返回的就是根據對象的內存地址換算出的一個值。這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一會兒能定位到它應該放置的物理位置上。若是這個位置上沒有元素,它就能夠直接存儲在這個位置上,不用再進行任何比較了;若是這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。這樣一來實際調用equals方法的次數就大大下降了,幾乎只須要一兩次。

27. 兩個對象的 hashCode() 相同, 那麼 equals() 也必定爲 true嗎?

不對,兩個對象的 hashCode() 相同,equals() 不必定 true。

代碼示例:

String str1 = "keep";
String str2 = "brother";
System. out. println(String. format("str1:%d | str2:%d",  str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));

執行結果:

str1:1179395 | str2:1179395

false

代碼解讀:很顯然「keep」和「brother」的 hashCode() 相同,然而 equals() 則爲 false,由於在散列表中,hashCode() 相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不必定能得出鍵值對相等。

28. 泛型經常使用特色

泛型是Java SE 1.5以後的特性, 《Java 核心技術》中對泛型的定義是:

「泛型」 意味着編寫的代碼能夠被不一樣類型的對象所重用。

「泛型」,顧名思義,「泛指的類型」。咱們提供了泛指的概念,但具體執行的時候卻能夠有具體的規則來約束,好比咱們用的很是多的ArrayList就是個泛型類,ArrayList做爲集合能夠存放各類元素,如Integer, String,自定義的各類類型等,但在咱們使用的時候經過具體的規則來約束,如咱們能夠約束集合中只存放Integer類型的元素,如

List<Integer> iniData = new ArrayList<>()

使用泛型的好處?

以集合來舉例,使用泛型的好處是咱們沒必要由於添加元素類型的不一樣而定義不一樣類型的集合,如整型集合類,浮點型集合類,字符串集合類,咱們能夠定義一個集合來存放整型、浮點型,字符串型數據,而這並非最重要的,由於咱們只要把底層存儲設置了Object便可,添加的數據所有均可向上轉型爲Object。 更重要的是咱們能夠經過規則按照本身的想法控制存儲的數據類型。

29. 面向對象的特徵

面向對象的編程語言有封裝、繼承 、抽象、多態等4個主要的特徵。

  1. 封裝: 把描述一個對象的屬性和行爲的代碼封裝在一個模塊中,也就是一個類中,屬性用變量定義,行爲用方法進行定義,方法能夠直接訪問同一個對象中的屬性。
  2. 抽象: 把現實生活中的對象抽象爲類。分爲過程抽象和數據抽象
  • 數據抽象 -->鳥有翅膀,羽毛等(類的屬性)
  • 過程抽象 -->鳥會飛,會叫(類的方法)
  1. 繼承:子類繼承父類的特徵和行爲。子類能夠有父類的方法,屬性(非private)。子類也能夠對父類進行擴展,也能夠重寫父類的方法。缺點就是提升代碼之間的耦合性。
  2. 多態: 多態是指程序中定義的引用變量所指向的具體類型和經過該引用變量發出的方法調用在編程時並不肯定,而是在程序運行期間才肯定(好比:向上轉型,只有運行才能肯定其對象屬性)。方法覆蓋和重載體現了多態性。

30. Java多態的理解

  1. 多態是繼封裝、繼承以後,面向對象的第三大特性。
  2. 多態現實意義理解:
  • 現實事物常常會體現出多種形態,如學生,學生是人的一種,則一個具體的同窗張三既是學生也是人,即出現兩種形態。
  • Java做爲面向對象的語言,一樣能夠描述一個事物的多種形態。如Student類繼承了Person類,一個Student的對象便既是Student,又是Person。
  1. 多態體現爲父類引用變量能夠指向子類對象。
  2. 前提條件:必須有子父類關係。

注意:在使用多態後的父類引用變量調用方法時,會調用子類重寫後的方法。

  1. 多態的定義與使用格式

定義格式:父類類型 變量名=new 子類類型();

31. 重載和重寫的區別

重寫(Override)

從字面上看,重寫就是 從新寫一遍的意思。其實就是在子類中把父類自己有的方法從新寫一遍。子類繼承了父類原有的方法,但有時子類並不想原封不動的繼承父類中的某個方法,因此在方法名,參數列表,返回類型(除過子類中方法的返回值是父類中方法返回值的子類時)都相同的狀況下, 對方法體進行修改或重寫,這就是重寫。但要注意子類函數的訪問修飾權限不能少於父類的。

public class Father {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Son s = new Son();
        s.sayHello();
    }

    public void sayHello() {
        System.out.println("Hello");
    }
}

class Son extends Father{

    @Override
    public void sayHello() {
        // TODO Auto-generated method stub
        System.out.println("hello by ");
    }
}

重寫 總結:

1.發生在父類與子類之間

2.方法名,參數列表,返回類型(除過子類中方法的返回類型是父類中返回類型的子類)必須相同

3.訪問修飾符的限制必定要大於被重寫方法的訪問修飾符(public>protected>default>private)

4.重寫方法必定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常

重載(Overload)

在一個類中,同名的方法若是有不一樣的參數列表(參數類型不一樣、參數個數不一樣甚至是參數順序不一樣)則視爲重載。同時,重載對返回類型沒有要求,能夠相同也能夠不一樣,但不能經過返回類型是否相同來判斷重載

public class Father {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Father s = new Father();
        s.sayHello();
        s.sayHello("wintershii");

    }

    public void sayHello() {
        System.out.println("Hello");
    }

    public void sayHello(String name) {
        System.out.println("Hello" + " " + name);
    }
}

重載 總結:

1.重載Overload是一個類中多態性的一種表現

2.重載要求同名方法的參數列表不一樣(參數類型,參數個數甚至是參數順序)

3.重載的時候,返回值類型能夠相同也能夠不相同。沒法以返回型別做爲重載函數的區分標準

33. Java建立對象有幾種方式?

java中提供瞭如下四種建立對象的方式:

  • new建立新對象
  • 經過反射機制
  • 採用clone機制
  • 經過序列化機制

34. ConcurrentModificationException異常出現的緣由

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                list.remove(integer);
        }
    }
}

執行上段代碼是有問題的,會拋出ConcurrentModificationException異常。

緣由:調用list.remove()方法致使modCountexpectedModCount的值不一致。

final void checkForComodification() {
    if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
}

解決辦法:在迭代器中若是要刪除元素的話,須要調用Iterator類的remove方法。

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                iterator.remove();   //注意這個地方
        }
    }
}

35. HashMap和HashTable、ConcurrentHashMap區別?

相同點:

  1. HashMap和Hashtable都實現了Map接口
  2. 均可以存儲key-value數據

不一樣點:

  1. HashMap能夠把null做爲key或value,HashTable不能夠
  2. HashMap線程不安全,效率高。HashTable線程安全,效率低。
  3. HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。

什麼是fail-fast?
就是最快的時間能把錯誤拋出而不是讓程序執行。

36. 如何保證線程安全又效率高?

Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。

ConcurrentHashMap將整個Map分爲N個segment(相似HashTable),能夠提供相同的線程安全,可是效率提高N倍,默認N爲16。

37. 咱們可否讓HashMap同步?

HashMap能夠經過下面的語句進行同步:

Map m = Collections.synchronizeMap(hashMap);

38. Java 中 IO 流分爲幾種?

按功能來分:輸入流(input)、輸出流(output)。

按類型來分:字節流和字符流。

字節流和字符流的區別是:字節流按 8 位傳輸以字節爲單位輸入輸出數據,字符流按 16 位傳輸以字符爲單位輸入輸出數據。

39. BIO、NIO、AIO 有什麼區別?

  • BIO:Block IO 同步阻塞式 IO,就是咱們日常使用的傳統 IO,它的特色是模式簡單使用方便,併發處理能力低。
  • NIO:Non IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端經過 Channel(通道)通信,實現了多路複用。
  • AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操做基於事件和回調機制。

40. Files的經常使用方法都有哪些?

  • Files. exists():檢測文件路徑是否存在。
  • Files. createFile():建立文件。
  • Files. createDirectory():建立文件夾。
  • Files. delete():刪除一個文件或目錄。
  • Files. copy():複製文件。
  • Files. move():移動文件。
  • Files. size():查看文件個數。
  • Files. read():讀取文件。
  • Files. write():寫入文件。

41. Java反射的做用於原理

一、定義:

反射機制是在運行時,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意個對象,都可以調用它的任意一個方法。在java中,只要給定類的名字,就能夠經過反射機制來得到類的全部信息。

這種動態獲取的信息以及動態調用對象的方法的功能稱爲Java語言的反射機制。

二、哪裏會用到反射機制?

jdbc就是典型的反射

Class.forName('com.mysql.jdbc.Driver.class');//加載MySQL的驅動類

這就是反射。如hibernate,struts等框架使用反射實現的。

42. 反射的實現方式

第一步:獲取Class對象,有4種方法: 1)Class.forName(「類的路徑」); 2)類名.class 3)對象名.getClass() 4)基本類型的包裝類,能夠調用包裝類的Type屬性來得到該包裝類的Class對象

43. 實現Java反射的類:

1)Class:表示正在運行的Java應用程序中的類和接口 注意: 全部獲取對象的信息都須要Class類來實現。 2)Field:提供有關類和接口的屬性信息,以及對它的動態訪問權限。 3)Constructor:提供關於類的單個構造方法的信息以及它的訪問權限 4)Method:提供類或接口中某個方法的信息

44. 反射機制的優缺點:

優勢:

一、可以運行時動態獲取類的實例,提升靈活性;

二、與動態編譯結合

缺點:

一、使用反射性能較低,須要解析字節碼,將內存中的對象進行解析。

解決方案:

​ 一、經過setAccessible(true)關閉JDK的安全檢查來提高反射速度;

​ 二、屢次建立一個類的實例時,有緩存會快不少

​ 三、ReflectASM工具類,經過字節碼生成的方式加快反射速度

二、相對不安全,破壞了封裝性(由於經過反射能夠得到私有方法和屬性)

45. Java 中 IO 流分爲幾種?

  • 按照流的流向分,能夠分爲輸入流和輸出流;
  • 按照操做單元劃分,能夠劃分爲字節流和字符流;
  • 按照流的角色劃分爲節點流和處理流。

Java Io 流共涉及 40 多個類,這些類看上去很雜亂,但實際上頗有規則,並且彼此之間存在很是緊密的聯繫, Java I0 流的 40 多個類都是從以下 4 個抽象類基類中派生出來的。

  • InputStream/Reader: 全部的輸入流的基類,前者是字節輸入流,後者是字符輸入流。
  • OutputStream/Writer: 全部輸出流的基類,前者是字節輸出流,後者是字符輸出流。

按操做方式分類結構圖:

相關文章
相關標籤/搜索