Java基礎(chǔ)知識面試題

1.Java為什么跨平臺

java語言編寫的程序,一次編譯后,可以在多個系統(tǒng)平臺上運行,因為只要裝了Java虛擬機就可以運行,而C語言需要編譯成匯編程序,而不同CPU的匯編指令是不同的。

2.什么是注解,注解如何被處理

//作用范圍,可以用于類,方法,構(gòu)造器,參數(shù)等
@Target
//在生成javadoc的時候就會把@Documented注解給顯示出來
@Documented
//作用時機  source(源碼中保留) 、class(class中保留)、runtime(運行時保留)
@Retention
//如果類A標(biāo)注了該注解,那么其子類也會有該注解
@Inherited

注解簡單使用,已測試,請安心食用

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
@interface Anno{
    String value();
}

@Anno(value = "hello")
public class AnnotationTest {
    public static void main(String[] args) {
        Annotation[] annotations = AnnotationTest.class.getAnnotations();
        System.out.println(annotations.length);
        for (Annotation annotation : annotations) {
            if(annotation instanceof Anno){
                System.out.println(((Anno) annotation).value());
            }
        }
    }
}
//輸出 1 hello

3.Java的異常

Error

Error 類是指 java 運行時系統(tǒng)的內(nèi)部錯誤和資源耗盡錯誤。應(yīng)用程序不會拋出該類對象。如果出現(xiàn)了這樣的錯誤,除了告知用戶,剩下的就是盡力使程序安全的終止。

RuntimeException

算數(shù)溢出、除0等

CheckedException

I/O錯誤導(dǎo)致

4.Java的泛型

泛型

泛型擦除
JVM并不知道泛型的存在,因為泛型在編譯階段就已經(jīng)被處理成普通的類和方法。比如:下面兩個list的類型是相同的。


image

5.Object類的常用方法

  1. hashCode()
    String的hashCode實現(xiàn)方式:
    (char[0] *31 + char[1]) * 31 + char[2] + ......
public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
  1. equals()
  2. watit()
  3. notify()
  4. notifyAll()
  5. finalize()
    在被垃圾回收器清除對象內(nèi)存空間前調(diào)用一次,但方法里面的內(nèi)容不一定能執(zhí)行,可能還沒有執(zhí)行對象就掛掉了。如果進行了自救,下次垃圾回收時不會再調(diào)用該方法。
    用于釋放非java資源和給對象最后一次存活的機會(修改指針指向)
  6. toString() 返回文本形式表示對象的字符串
  7. clone()

6.String字符串常量池的用法

JDK8字符串常量池位于堆,運行時常量池位于方法區(qū)

String str1 = "abcd";//先檢查字符串常量池中有沒有"abcd",如果字符串常量池中沒有,則創(chuàng)建一個,然后 str1 指向字符串常量池中的對象,如果有,則直接將 str1 指向"abcd"";
String str2 = new String("abcd");//堆中創(chuàng)建一個新的對象
String str3 = new String("abcd");//堆中創(chuàng)建一個新的對象
System.out.println(str1==str2);//false
System.out.println(str2==str3);//false

String s1 = "abc",s2="a";
String s3=s2+"bc";
String s6 = "a"+"bc";
System.out.println(s3 == "abc");  //false,說明s3引用指向的對象不在字符串常量池
System.out.println(s6 == "abc");  //true
System.out.println(s1 == "abc");  //true

new這個關(guān)鍵字,毫無疑問會在堆中分配內(nèi)存,創(chuàng)建一個String類的對象。因此,a這個在棧中的引用指向的是堆中的這個String對象的。

然后,因為"abc"是個常量,所以會去常量池中找,有沒有這個常量存在,沒的話分配一個空間,放這個"abc"常量,并將這個常量對象的空間地址給到堆中String對象里面;如果常量池中已經(jīng)有了這個常量,就直接用那個常量池中的常量對象的引用唄,就只需要創(chuàng)建一個堆中的String對象。
+ 的原理是StringBuilder

 public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = new String("b");
        String s4 = s1 + s3;
        String s5="ab";
        String s6 = s1 + s2;
        String s66= s1 + s2;
        String s7 = "a" + s2;
        String s8 = s1 + "b";
        String s9 = "a" + "b";

        System.out.println(s2 == s3);  //false   
        System.out.println(s4 == s5);   //false   s4 是使用了StringBulider來相加了
        System.out.println(s4 == s6);  //false  s4和s6 兩個都是使用了StringBulider來相加了
        System.out.println(s6 == s66);     //false   兩個都是使用了StringBulider來相加了
          System.out.println(s5 == s7);    //false   s7是使用了StringBulider來相加了
        System.out.println(s5 == s8);  //false   s8是使用了StringBulider來相加了
        System.out.println(s7 == s8);  //false  兩個都是使用了StringBulider來相加了
        System.out.println(s9 == s8);  //false  兩個都是使用了StringBulider來相加了
    }

String s1 = new String("abc");這句話創(chuàng)建了幾個字符串對象?

將創(chuàng)建 1 或 2 個字符串。如果池中已存在字符串常量“abc”,則只會在堆空間創(chuàng)建一個字符串常量“abc”。如果池中沒有字符串常量“abc”,那么它將首先在池中創(chuàng)建,然后在堆空間中創(chuàng)建,因此將創(chuàng)建總共 2 個字符串對象

7.自動裝箱和自動拆箱

//自動裝箱
int a = 1;
Integer b = a;
調(diào)用了Integer b = Integer.valueOf(a);

//自動拆箱
Integer x =3;
int y = x; 其實執(zhí)行了int y = x.intValue();

例子:

 Integer i1 = 40;
 Integer i2 = 40;
 Integer i3 = 0;
 Integer i4 = new Integer(40);
 Integer i5 = new Integer(40);
 Integer i6 = new Integer(0);

 System.out.println("i1=i2   " + (i1 == i2));
 System.out.println("i1=i2+i3   " + (i1 == i2 + i3));
 System.out.println("i1=i4   " + (i1 == i4));
 System.out.println("i4=i5   " + (i4 == i5));
 System.out.println("i4=i5+i6   " + (i4 == i5 + i6)); 
 System.out.println("40=i5+i6   " + (40 == i5 + i6));     Copy to clipboardErrorCopied</pre>

結(jié)果:
i1=i2   true  //Integer的緩存機制
i1=i2+i3   true   //+只作用于int,而Integer和int無法比較,自動拆箱
i1=i4   false   //一個位于堆中,一個位于緩存中
i4=i5   false   //兩個對象引用地址不一樣
i4=i5+i6   true   //+只作用于int,而Integer和int無法比較,自動拆箱
40=i5+i6   true    //+只作用于int,自動拆箱

8.內(nèi)存泄漏

對象不會再被程序用到了,但是gc又不能回收

9.try catch finally 返回值

如果finnally中有return,那么先執(zhí)行try和catch中的語句和return后的賦值操作,再執(zhí)行finnally,在finally里面返回。
如果finnally中沒有return,么先執(zhí)行try和catch中的語句和return后的賦值操作,再執(zhí)行finnally,再返回到try或catch中 返回。

10.==和equals的區(qū)別

11.hashCode和equals

1、如果兩個對象equals,Java運行時環(huán)境會認(rèn)為他們的hashcode一定相等。
2、如果兩個對象不equals,他們的hashcode有可能相等,但會讓hashCode盡量不相等。
3、如果兩個對象hashcode相等,他們不一定equals。
4、如果兩個對象hashcode不相等,他們一定不equals。

//HashMap的hash方法
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

一般而言:
equals等,hashCode一定等。hashCode等,equals不一定等,因為可能哈希沖突。
什么時候要重寫equals和hashCode?
有時我們僅僅希望一個類中的一些比較重要的字段一樣就認(rèn)為兩個對象就是相等的,因此就要重寫兩個方法.
為什么是重寫equals一定要重寫hashCode
為了效率,當(dāng)該類對象作為HashMap的Key的時候,由于HashMap先比較hashCode,再比較equals,如果該類對象hashCode不相等但equals等,那么HashMap的算法就沒意義了,因此如果equals相等,那么hashCode一定要相等.
如何重寫equals和hashCode
如:

public class User {
    private String id;
    private String name;
    private String age;

    public User(){

    }

    public User(String id, String name, String age){
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return this.id + " " + this.name + " " + this.age;
    }

    @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;//地址相等
        }

        if(obj == null){
            return false;//非空性:對于任意非空引用x,x.equals(null)應(yīng)該返回false。
        }

        if(obj instanceof User){
            User other = (User) obj;
            //需要比較的字段相等,則這兩個對象相等
            if(equalsStr(this.name, other.name)
                    && equalsStr(this.age, other.age)){
                return true;
            }
        }

        return false;
    }

    private boolean equalsStr(String str1, String str2){
        if(StringUtils.isEmpty(str1) && StringUtils.isEmpty(str2)){
            return true;
        }
        if(!StringUtils.isEmpty(str1) && str1.equals(str2)){
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + (name == null ? 0 : name.hashCode());
        result = 31 * result + (age == null ? 0 : age.hashCode());
        return result;
    }
}

hashCode使用31的原因

  • 31是一個不大不小的質(zhì)數(shù),太小會造成乘積會在一個很小的范圍,造成哈希沖突.太大會超出int的范圍
  • 而且使用31*i = (1<<5 - 1) * i = i<<5 - i 位運算容易計算

12.String StringBuilder StringBuffer

效率 StringBuilder > StringBuffer > String
由于String是final類型,每次更改都會重寫拷貝,而StringBuilder基于數(shù)據(jù),以擴容方式實現(xiàn)更改。
為什么StringBuilder是線程不安全的?
如果同時有10個線程,每個線程循環(huán)1000次進行append,最后輸出長度,會發(fā)現(xiàn)長度小于10000,并且會拋出數(shù)組越界。

public class StringBuilderDemo {
    public static void main(String[] args) throws InterruptedException {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 10; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 1000; j++){
                        stringBuilder.append("a");
                    }
                }
            }).start();
        }
        Thread.sleep(100);
        System.out.println(stringBuilder.length());
    }
}

為什么長度小于1000?
可以看到,append方法是線程不安全的,如果當(dāng)前數(shù)組長度為10,同時有兩個線程執(zhí)行到count+=len那么count=11,而不是12

//存儲字符串的具體內(nèi)容
char[] value;
//已經(jīng)使用的字符數(shù)組的數(shù)量
int count;


public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

為什么會拋出數(shù)組越界?
當(dāng)線程A,B同時進入append邏輯,線程A加入兩個字符,線程B也加入兩個字符,兩個線程同時執(zhí)行完了ensureCapacityInternal(count + len),然后線程A擴容完成,并將count值修改,這是B執(zhí)行拷貝添加的字符到原字符末尾,但起始位置可能是沒有分配的下標(biāo)因此數(shù)組越界.
此外還可能發(fā)生數(shù)據(jù)添加覆蓋的問題
考慮兩個線程同時執(zhí)行了ensureCapacityInternal(count + len),然后線程A執(zhí)行了拷貝數(shù)組,之后線程B執(zhí)行拷貝數(shù)據(jù)將A添加的值覆蓋,這就導(dǎo)致數(shù)據(jù)覆蓋.
而StringBuffer使用synchronized確保線程安全

13.幾種I/O

14.NIO

15.select poll epoll

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

相關(guān)閱讀更多精彩內(nèi)容

  • 1. String 是最基本的數(shù)據(jù)類型嗎?不是,String是類,String是不可變的,對String類的任何改...
    云鯨魚rain閱讀 255評論 0 1
  • static關(guān)鍵字什么意思?java中是否可以覆蓋一個private或者是static的方法? “static”表...
    woshi開心閱讀 8,665評論 0 2
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險厭惡者,不喜歡去冒險,但是人生放棄了冒險,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 7,874評論 0 4
  • 公元:2019年11月28日19時42分農(nóng)歷:二零一九年 十一月 初三日 戌時干支:己亥乙亥己巳甲戌當(dāng)月節(jié)氣:立冬...
    石放閱讀 7,460評論 0 2

友情鏈接更多精彩內(nèi)容