1.Java基礎

題目來源于:https://www.bilibili.com/read/cv7046891

1.JDK和JRE有什么區(qū)別?

  • JDK:Java Development Lit,為Java開發(fā)工具包,提供包括編譯器(javac)、開發(fā)工具(javadoc、jre、keytool、jconsole)和更多類庫(tools.jar)等,JDK中包含JRE。
  • JRE:Java Runtime Environment,為Java程序運行時環(huán)境,包含Java虛擬機(JVM)、Java基礎類庫等。是運行Java編寫的程序的必備環(huán)境。
    一句話解釋:JDK用于開發(fā),JRE用于運行,JDK包含JRE。

2. ==和equals的區(qū)別是什么?
基本引用類型(byte,short,char,int,long,float,double,boolean):==為值比較。
引用數(shù)據類型(class,interface,array):==為內存地址比較。
對于equals來說,equals其實是屬于Object超類定義實現(xiàn)的,源代碼如下:

// Object超類equals實現(xiàn)
public boolean equals(Object obj) {
    return (this == obj);
}

由源代碼可得,實際上equals等同于==。
但是,對于String、Integer等包裝類來說,它們實在對equals進行過重寫,使得equals功能為值比較。源代碼如下:

// String類型equals實現(xiàn)
public boolean equals(Object anObject) {
    // 如果傳入的類型與本類型內存地址一致,則直接返回true
    if (this == anObject) {
        return true;
    }
    // 如果傳入的類型也為String類型,那么進行字符串值比較
    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;
}
// Integer類型equals實現(xiàn)
public boolean equals(Object obj) {
    // 如果傳入的類型與本類型相同,則返回本類型的值與傳入類型的值的對比
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

3.兩個對象的hashCode()相同,則equals()也一定為true,對嗎?
要解決這個問題,需要理解什么是hash?
維基百科解釋為:散列函數(shù)(英語:Hash function)又稱散列算法、哈希函數(shù),是一種從任何一種數(shù)據中創(chuàng)建小的數(shù)字“指紋”的方法。散列函數(shù)把消息或數(shù)據壓縮成摘要,使得數(shù)據量變小,將數(shù)據的格式固定下來。該函數(shù)將數(shù)據打亂混合,重新創(chuàng)建一個叫做散列值(hash values,hash codes,hash sums,或hashes)的指紋。散列值通常用一個短的隨機字母和數(shù)字組成的字符串來代表。[1]好的散列函數(shù)在輸入域中很少出現(xiàn)散列沖突。在散列表數(shù)據處理中,不抑制沖突來區(qū)別數(shù)據,會使得數(shù)據庫記錄更難找到。
因此可知,hash是可能存在散列沖突的:即不同的兩個值,hash后的結果卻是一致的。
hashCode為超類Object實現(xiàn)的方法,它是調用本地方法hashCode()實現(xiàn)的。源代碼如下:

public native int hashCode();

因此hash沖突在Java的hashCode()實現(xiàn)中也是存在的,因此也存在多種hash沖突的解決方案,如開放地址法、拉鏈法等。
因此可得結論:hashCode()結果相同,equals的結果不一定為true。

4.final在Java中有什么作用?
在Java中,final可以用來修飾類、函數(shù)和變量。final類中的所有方法,會被隱式的指定為final方法。
當其修飾類時,表示該類不可繼承。

public final class Person {
}

當其修飾方法時,表示該方法不可被重寫。類的private方法會被隱式的指定為final方法。

public class Person {
    final void say() {
        System.out.println("say...");
    }
}

當其修飾變量時,如果該變量類型為基本數(shù)據類型,則表示該變量不可被修改;如果該變量為引用數(shù)據類型時,則表示該變量引用地址不可被修改。

final String A = "123";

5.Java中的Math.round(-1.5)等于多少?
在Java中,四舍五入的根本實現(xiàn)可以理解為:在原值的基礎上+0.5,然后向下取整。源代碼如下:

public static int round(float a) {
    int intBits = Float.floatToRawIntBits(a);
    int biasedExp = (intBits & FloatConsts.EXP_BIT_MASK)
            >> (FloatConsts.SIGNIFICAND_WIDTH - 1);
    int shift = (FloatConsts.SIGNIFICAND_WIDTH - 2
            + FloatConsts.EXP_BIAS) - biasedExp;
    if ((shift & -32) == 0) { // shift >= 0 && shift < 32
        // a is a finite number such that pow(2,-32) <= ulp(a) < 1
        int r = ((intBits & FloatConsts.SIGNIF_BIT_MASK)
                | (FloatConsts.SIGNIF_BIT_MASK + 1));
        if (intBits < 0) {
            r = -r;
        }
        // In the comments below each Java expression evaluates to the value
        // the corresponding mathematical expression:
        // (r) evaluates to a / ulp(a)
        // (r >> shift) evaluates to floor(a * 2)
        // ((r >> shift) + 1) evaluates to floor((a + 1/2) * 2)
        // (((r >> shift) + 1) >> 1) evaluates to floor(a + 1/2)
        return ((r >> shift) + 1) >> 1;
    } else {
        // a is either
        // - a finite number with abs(a) < exp(2,FloatConsts.SIGNIFICAND_WIDTH-32) < 1/2
        // - a finite number with ulp(a) >= 1 and hence a is a mathematical integer
        // - an infinity or NaN
        return (int) a;
    }
}

public static long round(double a) {
    long longBits = Double.doubleToRawLongBits(a);
    long biasedExp = (longBits & DoubleConsts.EXP_BIT_MASK)
            >> (DoubleConsts.SIGNIFICAND_WIDTH - 1);
    long shift = (DoubleConsts.SIGNIFICAND_WIDTH - 2
            + DoubleConsts.EXP_BIAS) - biasedExp;
    if ((shift & -64) == 0) { // shift >= 0 && shift < 64
        // a is a finite number such that pow(2,-64) <= ulp(a) < 1
        long r = ((longBits & DoubleConsts.SIGNIF_BIT_MASK)
                | (DoubleConsts.SIGNIF_BIT_MASK + 1));
        if (longBits < 0) {
            r = -r;
        }
        // In the comments below each Java expression evaluates to the value
        // the corresponding mathematical expression:
        // (r) evaluates to a / ulp(a)
        // (r >> shift) evaluates to floor(a * 2)
        // ((r >> shift) + 1) evaluates to floor((a + 1/2) * 2)
        // (((r >> shift) + 1) >> 1) evaluates to floor(a + 1/2)
        return ((r >> shift) + 1) >> 1;
    } else {
        // a is either
        // - a finite number with abs(a) < exp(2,DoubleConsts.SIGNIFICAND_WIDTH-64) < 1/2
        // - a finite number with ulp(a) >= 1 and hence a is a mathematical integer
        // - an infinity or NaN
        return (long) a;
    }
}

因此,-1.5四舍五入結果為(-1.5+0.5)向下取整為-1。

6.String屬于基礎的數(shù)據類型嗎?
Java的數(shù)據類型分為基礎數(shù)據類型和引用數(shù)據類型。
其中基礎數(shù)據類型為8種:

  • 整形:byte、short、int、long
  • 浮點型:float、double
  • 字符型:char
  • 布爾型:boolen
    因此String不屬于基礎數(shù)據類型,而是引用數(shù)據類型。

7.Java中操作字符串都有哪些類型?它們之間有什么區(qū)別?
Java中操作字符串都有String、StringBuffer、StringBuilder。
String:使用final修飾,其返回返回都為new String。對String對象的任何更改都會生成新的String對象。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    ...
    public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                // 返回新的String對象
                return new String(buf, true);
            }
        }
        return this;
    }
    ...
}

StringBuffer:大部分方法都使用了synchronized修飾,為線程安全的類,推薦在多線程并發(fā)的情況下使用。

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ...
    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @since      1.5
     */
    @Override
    public synchronized StringBuffer append(CharSequence s, int start, int end)
    {
        toStringCache = null;
        super.append(s, start, end);
        return this;
    }
    ...
}

StringBuild:類似于StringBuffer,但是方法未使用synchornized進行修飾,為線程不安全的類,但是使用效率上要遠高于StringBuffer。

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ...
    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    ...
}

總結:Java中操作字符串的類型共有三種,分別為String、StringBuffer、StringBuilder。其中String為不可變的,適用于字符串生成不再變化的場景;StringBuffer為線程安全型,適用于需要大量拼接字符串且對線程安全要求較高的場景;StringBuilder為線程不安全類型,適用于需要大量拼接字符串且對線程安全要求較低的場景。

8.String str = "i"與String str = new String("i")一樣嗎?
java String str = "i",相當于將內存地址中的"i"的地址賦值給str變量。如果此時再次定義java String str2 = "i",那么str和str2的地址相同。

    public static void main(String[] args) {
        String str1 = "i";
        String str2 = "i";
        System.out.println(str1 == str2);
    }

java String str = new String("i"),實際上是重新創(chuàng)建了對象,并賦值為"i",雖然多次賦值的值相同,但是其內存地址并不相同。

      public static void main(String[] args) {
        String str1 = new String("i");
        String str2 = new String("i");
        System.out.println(str1.equals(str2));
        System.out.println(str1 == str2);
    }

9.如何將字符串反轉?
如果此處考察的是Java基礎,則可以使用StringBuffer或者StringBuilder進行反轉。

    public static void main(String[] args) {
        String str = "abcdefg";
        String res = new StringBuilder(str).reverse().toString();
        System.out.println(res);
    }

StringBuilder反轉實現(xiàn)源碼:

    // StringBuilder源碼,該方法繼承自AbstractStringBuilder
    @Override
    public StringBuilder reverse() {
        super.reverse();
        return this;
    }

    // AbstractStringBuilder源碼
    public AbstractStringBuilder reverse() {
        boolean hasSurrogates = false;
        int n = count - 1;
        for (int j = (n-1) >> 1; j >= 0; j--) {
            int k = n - j;
            char cj = value[j];
            char ck = value[k];
            value[j] = ck;
            value[k] = cj;
            if (Character.isSurrogate(cj) ||
                Character.isSurrogate(ck)) {
                hasSurrogates = true;
            }
        }
        if (hasSurrogates) {
            reverseAllValidSurrogatePairs();
        }
        return this;
    }

如果此處考察的是算法,則可以使用多種方法進行反轉。例如:

    public static void main(String[] args) {
        String str = "abcdefg";
        char[] strArr = str.toCharArray();
        int i = 0;
        int j = strArr.length - 1;
        while (i < j) {
            char temp = strArr[i];
            strArr[i++] = strArr[j];
            strArr[j--] = temp;
        }
        System.out.println(new String(strArr));
    }

10.String類的常用方法有哪些?
indexOf():返回指定字符的索引。
charAt():返回指定索引處的字符。
replace():字符串替換。
trim():字符串兩端空白去除。
split():將字符串按照指定分隔符分割為字符串數(shù)組。
getBytes():返回字符串的byte類型的數(shù)據。
length():返回字符串的字符長度。
toLowerCase():將字符串轉換為全部小寫。
toUpperCase():將字符串轉換為全部大寫。
substring():截取字符串。
...

11.抽象類必須要有抽象方法嗎?
抽象類中的方法不需要必須是抽象法。
但是擁有抽象方法的類必須是抽象類。

public abstract class Person {
    protected String name;
    protected int age;
    
    public abstract void say();
    
    public void play() {
        System.out.println("play...");
    }
}

12.普通類和抽象類有哪些區(qū)別?
抽象類和普通類是相對的概念。通常抽象類是由一系列普通類的共性抽象而成,例如:男人、女人的普通類,可以抽象成人類的類。

  • 抽象類必須使用abstract關鍵字修飾,而普通類不可以使用該關鍵字修飾。
  • 普通類可以實例化,抽象類不可以實例化。
  • 抽象類方法必須為public或者protected,而普通類還可以使用private(private修飾的方法不可以被繼承)。
  • 一個普通類若繼承自抽象類,則必須實現(xiàn)抽象類中的抽象方法必須。如果子類沒有實現(xiàn)父抽象類中抽象方法,那么該子類必須也是抽象類。

13.抽象類能使用final修飾嗎?
被final關鍵字修飾的類不可被繼承,因此抽象類不能使用final關鍵字進行修飾。

14.接口和抽象類有什么區(qū)別?

  • 從概念上來說,抽象類是對事物本質的抽象,例如男人、女人和人類;而接口是對動作的抽象,例如說話、吃飯等。
  • 從設計層面來說,抽象類一種模板設計,而接口是一種行為規(guī)范。如果抽象類中新增了方法,子類是無需進行更新的;但是接口內新增了方法,所有實現(xiàn)該接口的類必須進行更新。
  • 從語法層面上來說
    • 抽象類可以提供成員方法的實現(xiàn)細節(jié),而接口中只能存在java public abstract方法。
    • 抽象類中的成員變量可以是各種類型的,但是接口中的成員變量只能是java public static final類型的。
    • 抽象類中可以包含靜態(tài)方法和靜態(tài)代碼塊,而接口中不能含有靜態(tài)方法和靜態(tài)代碼塊。
    • 一個類只能繼承一個抽象類,但是可以實現(xiàn)多個接口。

15.Java中的IO流分為幾種?

  • 按照流的流向分:可以分為輸入流和輸出流。
  • 按照流的劃分單元分:可以分為字節(jié)流和字符流。
    • 字節(jié)流處理的最基本單位1byte,通常用來處理二進制數(shù)據。
    • 字符流處理的最基本單位為2byte的Unicode,通常用來處理文本數(shù)據。
  • 按照流的角色劃分為節(jié)點流和處理流。
    Java IO.png

    圖片來源于:https://blog.csdn.net/qq_35771266/article/details/94850047

16.BIO、NIO、AIO有什么區(qū)別?

  • BIO(Blocking IO),同步阻塞IO。
  • NIO(Non-Blocking IO),同步非阻塞IO。
  • AIO(Aysnc IO),異步非阻塞IO。

17.Files的常用方法有哪些?
Files.exists():檢測文件路徑是否存在。
Files.createFile():創(chuàng)建文件。
Files.createDirectory():創(chuàng)建目錄。
Files.delete():刪除文件或者目錄。
Files.copy():復制文件。
Files.move():移動文件。
Files.size():查看文件數(shù)量。
Files.read():讀取文件。
Files.write():寫入文件。
...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容