欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java面試題沖刺第一天--基礎(chǔ)篇1

 更新時(shí)間:2021年07月13日 09:38:21   作者:_陳哈哈  
這篇文章主要為大家分享了最有價(jià)值的三道java面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下

面試題1:Java 中操作字符串都有哪些類?它們之間有什么區(qū)別? 正經(jīng)回答:

操作字符串的類有:String、StringBuffer、StringBuilder。

String 和 StringBuffer、StringBuilder 的區(qū)別在于 String 聲明的是不可變的對象,每次操作都會生成新的 String 對象,然后將指針指向新的 String 對象,而 StringBuffer、StringBuilder 可以在原有對象的基礎(chǔ)上進(jìn)行操作,所以在經(jīng)常改變字符串內(nèi)容的情況下最好不要使用 String。

而StringBuffer 和 StringBuilder 最大的區(qū)別在于,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高于 StringBuffer,所以在單線程環(huán)境下推薦使用 StringBuilder,多線程環(huán)境下推薦使用 StringBuffer。

String StringBuffer StringBuilder
類是否可變 不可變(Final) 可變 可變
功能介紹 每次對String的操作都會在“常量池”中生成新的String對象 任何對它指向的字符串的操作都不會產(chǎn)生新的對象。每個(gè)StringBuffer對象都有一定的緩沖區(qū)容量,字符串大小沒有超過容量時(shí),不會分配新的容量,當(dāng)字符串大小超過容量時(shí),自動(dòng)擴(kuò)容 功能與StringBuffer相同,相比少了同步鎖,執(zhí)行速度更快
線程安全性 線程安全 線程安全 線程不安全
使用場景推薦 單次操作或循環(huán)外操作字符串 多線程操作字符串 單線程操作字符串

深入追問:

追問1:這三者在效率上怎么說?

StringBulider > StringBuffer > String

String <(StringBuffer,StringBuilder)的原因?

  • String:字符串常量
  • StringBuffer:字符串變量(有同步鎖)
  • StringBuilder:字符串變量(無同步鎖)

從上面的名字可以看到,String是"字符串常量",也就是不可改變的對象。源碼如下:

public final class String{}

對于上面這句話的理解你可能會產(chǎn)生這樣一個(gè)疑問 ,比如這段代碼:

String str = "唐伯虎";
str = str + "點(diǎn)香煙";
System.out.print(str); // result : "唐伯虎點(diǎn)香煙"

我們明明改變了String型的變量str啊,為什么說是沒有改變呢?我們來看一下這張對String操作時(shí)內(nèi)存變化的圖:

在這里插入圖片描述

我們可以看到,初始String值為"唐伯虎",然后在這個(gè)字符串后面加上新的字符串"點(diǎn)香煙",這個(gè)過程是需要重新在棧堆內(nèi)存中開辟內(nèi)存空間的,最終得到了"唐伯虎點(diǎn)香煙"字符串也相應(yīng)的需要開辟內(nèi)存空間,這樣短短的兩個(gè)字符串,卻需要開辟三次內(nèi)存空間,不得不說這是對內(nèi)存空間的極大浪費(fèi),執(zhí)行效率同理。

為了應(yīng)對經(jīng)常性操作字符串的場景,Java才提供了其他兩個(gè)操作字符串的類 —— StringBuffer、StringBuilder。

他們倆均屬于字符串變量,是可改變的對象,每當(dāng)我們用它們對字符串做操作時(shí),實(shí)際上是在一個(gè)對象上操作的,這樣就不會像String一樣創(chuàng)建一些而外的對象進(jìn)行操作了,速度自然就相對快了。

我們一般在StringBuffer、StringBuild類上的主要操作是 append 和 insert 方法,這些方法允許被重載,以接受任意類型的數(shù)據(jù)。每個(gè)方法都能有效地將給定的數(shù)據(jù)轉(zhuǎn)換成字符串,然后將該字符串的字符追加或插入到字符串緩沖區(qū)中。append 方法始終將這些字符添加到緩沖區(qū)的末端;而 insert 方法則在指定的點(diǎn)(index)添加字符。

  1. StringBuilder一個(gè)可變的字符序列是JDK1.5新增的。此類提供一個(gè)與 StringBuffer 兼容的 API,但不保證同步。該類被設(shè)計(jì)用作 StringBuffer 的一個(gè)簡易替換,用在字符串緩沖區(qū)被單個(gè)線程使用的時(shí)候(這種情況很普遍)。如果可能,建議優(yōu)先采用S
  2. tringBuilder類,因?yàn)樵诖蠖鄶?shù)實(shí)現(xiàn)中,它比 StringBuffer 要快。且兩者的方法基本相同。然而在應(yīng)用程序要求線程安全的情況下,則必須使用 StringBuffer 類。

String 類型和 StringBuffer、 StringBuild類型的主要性能區(qū)別其實(shí)在于 String 是不可變的對象(final), 因此在每次對 String 類型進(jìn)行改變的時(shí)候其實(shí)都等同于在堆中生成了一個(gè)新的 String 對象,然后將指針指向新的 String 對象,這樣不僅效率低下,而且大量浪費(fèi)有限的內(nèi)存空間,所以經(jīng)常改變內(nèi)容的字符串最好不要用 String 。因?yàn)槊看紊蓪ο蠖紩ο到y(tǒng)性能產(chǎn)生影響,特別是當(dāng)內(nèi)存中的無引用對象過多了以后, JVM 的 GC 開始工作,那速度是一定會相當(dāng)慢的。另外當(dāng)GC清理速度跟不上new String的速度時(shí),還會導(dǎo)致內(nèi)存溢出Error,會直接kill掉主程序!報(bào)錯(cuò)如下:

Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded

Exception in thread "I/O dispatcher 3797236" java.lang.OutOfMemoryError: GC overhead limit exceeded

追問2:那StringBuffer和StringBuffer線程安全主要差在哪里呢?

StringBuffer和StringBuilder可以算是雙胞胎了,這兩者的方法沒有很大區(qū)別。但在線程安全性方面,StringBuffer允許多線程進(jìn)行字符操作。這是因?yàn)樵谠创a中StringBuffer的很多方法都被關(guān)鍵字synchronized 修飾了,而StringBuilder沒有。

synchronized的含義:   

每一個(gè)類對象都對應(yīng)一把鎖,當(dāng)某個(gè)線程A調(diào)用類對象O中的synchronized方法M時(shí),必須獲得對象O的鎖才能夠執(zhí)行M方法,否則線程A阻塞。一旦線程A開始執(zhí)行M方法,將獨(dú)占對象O的鎖。使得其它需要調(diào)用O對象的M方法的線程阻塞。只有線程A執(zhí)行完畢,釋放鎖后。那些阻塞線程才有機(jī)會重新調(diào)用M方法。這就是解決線程同步問題的鎖機(jī)制。 >  了解了synchronized的含義以后,大家可能都會有這個(gè)感覺。多線程編程中StringBuffer比StringBuilder要安全多了 ,事實(shí)確實(shí)如此。如果有多個(gè)線程需要對同一個(gè)字符串緩沖區(qū)進(jìn)行操作的時(shí)候,StringBuffer應(yīng)該是不二選擇。

注意:是不是String也不安全呢?事實(shí)上不存在這個(gè)問題,String是不可變的。線程對于堆中指定的一個(gè)String對象只能讀取,無法修改。試問:還有什么不安全的呢?

實(shí)際應(yīng)用場景中:

  • 如果不是在循環(huán)體中進(jìn)行字符串拼接的話,直接使用 String 的 “+” 就好了;
  • 單線程循環(huán)中操作大量字符串?dāng)?shù)據(jù) → StringBuilder.append();
  • 多線程循環(huán)中操作大量字符串?dāng)?shù)據(jù) → StringBuffer.append();

面試題2:請你說一下Error 和 Exception 區(qū)別是什么?

正經(jīng)回答:

Error 和 Exception 都是 Throwable 的子類,在Java中只有Throwable類型的實(shí)例才可以被拋出或者捕獲,它是異常處理機(jī)制的基本類型。

在這里插入圖片描述

  • Exception和Error體現(xiàn)了java平臺設(shè)計(jì)者對不同異常情況的分類,Exception是程序正常運(yùn)行中,可以預(yù)料的意外情況,可能并且應(yīng)該被捕獲,進(jìn)行相應(yīng)的處理。
  • Error是指正常情況下,不大可能出現(xiàn)的情況,絕大部分的Error都會導(dǎo)致程序處于非正常的、不可恢復(fù)的狀態(tài)。既然是非正常情況,不便于也不需要捕獲。常見的比如OutOfMemoryError之類都是Error的子類。
  • Exception又分為可檢查(checked)異常和不可檢查(unchecked)異常。可檢查異常在源代碼里必須顯式的進(jìn)行捕獲處理,這是編譯期檢查的一部分。不可檢查時(shí)異常是指運(yùn)行時(shí)異常,像NullPointerException、ArrayIndexOutOfBoundsException之類,通常是可以編碼避免的邏輯錯(cuò)誤,具體根據(jù)需要來判斷是否需要捕獲,并不會在編譯期強(qiáng)制要求。

面試題3:== 和 equals 的區(qū)別是什么

正經(jīng)回答:

  • == : 它的作用是判斷兩個(gè)對象的地址是不是相等。即,判斷兩個(gè)對象是不是同一個(gè)對象。(基本數(shù)據(jù)類型 == 比較的是值,引用數(shù)據(jù)類型 == 比較的是內(nèi)存地址)
  • equals(): 它的作用也是判斷兩個(gè)對象是否相等。但它一般有兩種使用情況:

情況1:類沒有覆蓋 equals() 方法。則通過 equals() 比較該類的兩個(gè)對象時(shí),等價(jià)于調(diào)用了Object類的equals() 方法,也就是通過“==”比較這兩個(gè)對象。

// Object類中的equals() 方法
public boolean equals(Object obj) {
    return (this == obj);
}

情況2:類覆蓋了 equals() 方法。一般,我們都會覆蓋 equals() 方法來兩個(gè)對象的內(nèi)容相等;若它們的內(nèi)容相等,則返回 true (即,認(rèn)為這兩個(gè)對象相等)。

// String類中的equals() 方法,已覆蓋,用于比較內(nèi)容
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;
}

深入追問:

追問1:如果我們不重寫equals() 方法,會怎么樣?

舉例說明:

重點(diǎn)說明:是否重寫Object類中的equals方法,會對結(jié)果造成的影響

public static void main(String[] args) {
    // 字符串比較
    String a = "陳哈哈";
    String b = "陳哈哈";
    if (a == b) {// true  a==b
        System.out.println("a==b");
    }
    if (a.equals(b)) {// true  a.equals(b)
        System.out.println("a.equals(b)");
    }
    // StringBuffer 對象比較,由于StringBuffer沒有重寫Object的equal方法,因此結(jié)果出現(xiàn)錯(cuò)誤
    StringBuffer c = new StringBuffer("陳哈哈");
    StringBuffer d = new StringBuffer("陳哈哈");
    if (c == d) {// false  c != d
        System.out.println("c == d");
    } else {
        System.out.println("c != d");
    }
    if (c.equals(d)) { // false 調(diào)用了Object類的equal方法
        System.out.println("StringBuffer equal true");
    }else {
        System.out.println("StringBuffer equal false");
    }
}
  • object的equals方法是比較的對象的內(nèi)存地址,而String的equals方法比較的是對象的值。
  • 因?yàn)镾tring中的equals方法是被重寫過的,而StringBuilder沒有重寫equals方法,從而調(diào)用的是Object類的equals方法,也就相當(dāng)于用了 ==;

追問2:重寫equals的同時(shí),我們需要重寫hashCode()方法么?為什么?

在重寫equals()方法時(shí),也有必要對hashCode()方法進(jìn)行重寫,尤其是當(dāng)我們自定義一個(gè)類,想把該類的實(shí)例存儲在集合中時(shí)。  

hashCode方法的常規(guī)約定為:值相同的對象必須有相同的hashCode,也就是equals()結(jié)果為相同,那么hashcode也要相同,equals()結(jié)果為不相同,那么hashcode也不相同;

當(dāng)我們使用equals方法比較說明對象相同,但hashCode不同時(shí),就會出現(xiàn)兩個(gè)hashcode值,比如在HashMap中,就會認(rèn)為這是兩個(gè)對象,因此會出現(xiàn)矛盾,說明equals方法和hashCode方法應(yīng)該成對出現(xiàn),當(dāng)我們對equals方法進(jìn)行重寫時(shí),也要對hashCode方法進(jìn)行重寫。

可以通過ide快捷鍵快速生成兩個(gè)方法,假設(shè)現(xiàn)在有一個(gè)學(xué)生Student類,其中有 age 和 name 兩個(gè)特征。生成代碼如下:

@Override
public boolean equals(Object o){
//首先比較兩個(gè)的地址值是否相同,如果相同,那內(nèi)容也一定相同
	if(this == o) return true;
//如果o為空值或者兩個(gè)對象的類型是否相同,如果類型不同或者o為空值則內(nèi)容一定不同
	if(o == null || getClass() != o.getClass()) return false;
//將object類型的實(shí)例強(qiáng)轉(zhuǎn)為Student類型
  	Student student = (Student)o;
//比較兩個(gè)實(shí)例的age是否相同
	if(age != student.age) return false;
//在比較name是否相同
	return name != null ? name.equals(student.name ) : student.name == null;
}
@Override
public int hashCode() {
	int result = age;
	result = 31 * result + (name != null ? name.hashCode() : 0);
	return result;
}

總結(jié)

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

最新評論