1. 程式人生 > >【面試】基本資料型別+String相關-這一篇全瞭解

【面試】基本資料型別+String相關-這一篇全瞭解

16、String類能不能被繼承,為什麼?這種設計有什麼好處?

解:

String是final型別,final類不能被繼承。

Java之所以被設計成final類是有一定的考慮在的,主要在以下幾個方面。

快取Hashcode

Java中經常會用到字串的雜湊碼(hashcode)。例如,在HashMap中,字串的不可變能保證其hashcode永遠保持一致,這樣就可以避免一些不必要的麻煩。這也就意味著每次在使用一個字串的hashcode的時候不用重新計算一次,這樣更加高效。

使其他類的使用更加便利

在介紹這個內容之前,先看以下程式碼:

HashSet<String> set = new HashSet<String>();

set.add(new String("a"));

set.add(new String("b"));

set.add(new String("c"));

for(String a: set)

     a.value = "a";

在上面的例子中,如果字串可以被改變,那麼以上用法將有可能違反Set的設計原則,因為Set要求其中的元素不可以重複。上面的程式碼只是為了簡單說明該問題,其實上面的程式碼也無法編譯通過,String的value欄位並無發從外部訪問。

安全性

String被廣泛的使用在其他Java類中充當引數。比如網路連線、開啟檔案等操作。如果字串可變,那麼類似操作可能導致安全問題。因為某個方法在呼叫連線操作的時候,他認為會連線到某臺機器,但是實際上並沒有(其他引用同一String物件的值修改會導致該連線中的字串內容被修改)。可變的字串也可能導致反射的安全問題,因為他的引數也是字串。

不可變物件天生就是執行緒安全的

因為不可變物件不能被改變,所以他們可以自由地在多個執行緒之間共享。不需要任何同步處理。總之,String被設計成不可變的主要目的是為了安全和高效。所以,使String是一個不可變類是一個很好的設計。

17:待編輯

Java中有8種基本資料型別

分為三大類。

字元型:char(16位)

布林型:boolean

數值型:1.整型:byte(8位)、short(16位)、int(32位)、long(64位) 2.浮點型:float(32位)、double(64位)

String不是基本資料型別,是引用型別。

19、整型的幾種中,各個型別的取值範圍是多少,如何計算的?超出範圍會發生什麼?

解:

Java中的整型主要保函byte、short、int和long這四種,表示的數字範圍也是從小到大的,之所以表示範圍不同主要和他們儲存資料時所佔的位元組數有關。

先來個簡答的科普,1位元組=8位(bit)。java中的整型屬於有符號數。

先來看計算中8bit可以表示的數字:最小值:10000000 (-128)(-2^7)最大值:01111111(127)(2^7-1)具體計算方式參考:連結:Java中,為什麼byte型別的取值範圍為-128~127? - CSDN部落格

整型的這幾個型別中,  

byte:byte用1個位元組來儲存,範圍為-128(-2^7)到127(2^7-1),在變數初始化的時候,byte型別的預設值為0。

short:short用2個位元組儲存,範圍為-32,768 (-2^15)到32,767 (2^15-1),在變數初始化的時候,short型別的預設值為0,一般情況下,因為Java本身轉型的原因,可以直接寫為0。  

int:int用4個位元組儲存,範圍為-2,147,483,648 (-2^31)到2,147,483,647 (2^31-1),在變數初始化的時候,int型別的預設值為0。  

long:long用8個位元組儲存,範圍為-9,223,372,036,854,775,808 (-2^63)到9,223,372,036, 854,775,807 (2^63-1),在變數初始化的時候,long型別的預設值為0L或0l,也可直接寫為0。

上面說過了,整型中,每個型別都有一定的表示範圍,但是,在程式中有些計算會導致超出表示範圍,即溢位。如以下程式碼:

int i = Integer.MAX_VALUE;

int j = Integer.MAX_VALUE;

int k = i + j;

System.out.println("i (" + i + ") + j (" + j + ") = k (" + k + ")");

輸出結果:i (2147483647) + j (2147483647) = k (-2)

這就是發生了溢位,溢位的時候並不會拋異常,也沒有任何提示。所以,在程式中,使用同類型的資料進行運算的時候,一定要注意資料溢位的問題。

20、什麼是浮點型。什麼是單精度和雙精度。為什麼程式碼中不要用浮點數表示金額。

解:

在電腦科學中,浮點是一種對於實數的近似值數值表現法,由一個有效數字(即尾數)加上冪數來表示,通常是乘以某個基數的整數次指數得到。以這種表示法表示的數值,稱為浮點數(floating-point number)。

計算機使用浮點數運算的主因,在於電腦使用二進位制的運算。例如:4÷2=2,4的二進位制表示為100、2的二進位制表示為010,在二進位制中,相當於退一位數(100 -> 010)。

1的二進位制是01,1.0/2=0.5,那麼,0.5的二進位制表示應該為(0.1),以此類推,0.25的二進位制表示為0.01,所以,並不是說所有的十進位制小數都能準確的用二進位制表示出來,如0.1,因此只能使用近似值的方式表達。

也就是說,十進位制的小數在計算機中是由一個整數或定點數(即尾數)乘以某個基數(計算機中通常是2)的整數次冪得到的,這種表示方法類似於基數為10的科學計數法。

一個浮點數a由兩個數m和e來表示:a = m × be。在任意一個這樣的系統中,我們選擇一個基數b(記數系統的基)和精度p(即使用多少位來儲存)。m(即尾數)是形如±d.ddd...ddd的p位數(每一位是一個介於0到b-1之間的整數,包括0和b-1)。如果m的第一位是非0整數,m稱作正規化的。有一些描述使用一個單獨的符號位(s 代表+或者-)來表示正負,這樣m必須是正的。e是指數。

位(bit)是衡量浮點數所需儲存空間的單位,通常為32位或64位,分別被叫作單精度和雙精度。

單精度浮點數在計算機儲存器中佔用4個位元組(32 bits),利用“浮點”(浮動小數點)的方法,可以表示一個範圍很大的數值。

比起單精度浮點數,雙精度浮點數(double)使用 64 位(8位元組) 來儲存一個浮點數。

由於前面說過,計算機中儲存的小數其實是十進位制的小數的近似值,並不是準確值,所以,千萬不要在程式碼中使用浮點數來表示金額等重要的指標。建議使用BigDecimal或者Long(單位為分)來表示金額。

21、Java中的char是否可以儲存中文字元?

解:

Java中的char儲存佔2個位元組。中文使用unicode編碼,同樣佔兩個位元組。只要在unicode編碼中有的中文,即可以用char儲存。除了一下生僻字和特殊字之外。

22、int和Integer,boolean和Boolean等,之間有什麼區別。

解:

1. 預設值不同,基本型別的預設值為0, false或\u0000,包裝類預設為null

2. 初始化不同,一個需要new,一個不需要

3. 儲存方式不同

4. int.class是原始型別,Integer.class是物件型別, 所以一個有成員變數和方法,一個沒有。包裝類就是把基本型別 「包裝」 在一個類裡,並提供一些常用的操作。畢竟面向物件。

23、拆箱裝箱

24、在介面定義的時候,要定義一個欄位表示是否成功,你會選以下哪種方式?為什麼?

boolean success

Boolean success

boolean isSuccess

Boolean isSuccess

解:

建議使用第一種

首先,作為介面的返回物件的引數,這個欄位不應該有不確定的null值,而Boolean型別的預設值是null,而boolean的預設值是false,所以,建議使用boolean來定義引數。其他,關於引數名稱,要使用success還是isSuccess,這一點在阿里巴巴Java開發手冊中有明確規定和解釋:

【強制】 POJO 類中的任何布林型別的變數,都不要加 is,否則部分框架解析會引起序列化錯誤。

反例: 定義為基本資料型別 boolean isSuccess;的屬性,它的方法也是 isSuccess(), RPC框架在反向解析的時候, “ 以為” 對應的屬性名稱是 success,導致屬性獲取不到,進而丟擲異常。

25、String s = new String("hollis"); 定義了幾個物件。

解:

字串的分配,和其他的物件分配一樣,耗費高昂的時間與空間代價。JVM為了提高效能和減少記憶體開銷,在例項化字串常量的時候進行了一些優化。為了減少在JVM中建立的字串的數量,字串類維護了一個字串池,每當程式碼建立字串常量時,JVM會首先檢查字串常量池。如果字串已經存在池中,就返回池中的例項引用。如果字串不在池中,就會例項化一個字串並放到池中。Java能夠進行這樣的優化是因為字串是不可變的,可以不用擔心資料衝突進行共享。

String s = new String("hollis"); 建立多少個物件?

在常量池中查詢是否有“hollis”物件

有則返回對應的引用例項

沒有則建立對應的例項物件

在堆中 new 一個 String("hollis") 物件

將物件地址賦值給s,建立一個引用

所以,常量池中沒有“hollis”字面量則建立兩個物件,否則建立一個物件,以及建立一個引用。

26、如何比較兩個字串?

解:對於字串的比較,一般目的是比較字串內容是否相等,這種情況下,要使用equals()方法來比較,而不是使用'=='。 equals()比較的是內容是否相同,'=='比較的是地址是否相同。

27、String有沒有長度限制,為什麼?如果有,超過限制會發生什麼?

解:

編譯期

首先,我們先來合理的推斷一下,當我們在程式碼中使用String s = "";的形式來定義String物件的時候,""中字元的個數有沒有限制呢?

既然是合理的推斷,那就要要足夠的依據,所以我們可以從String的原始碼入手,根據public String(char value[], int offset, int count)的定義,count是int型別的,所以,char value[]中最多可以儲存Integer.MAX_VALUE個,即2147483647字元。(jdk1.8.0_73)

但是,實驗證明,String s = "";中,最多可以有65534個字元。如果超過這個個數。就會在編譯期報錯。

public static void main(String[] args) {

String s = "a...a";// 共65534個a

System.out.println(s.length());

String s1 = "a...a";// 共65535個a

System.out.println(s1.length());

}

以上程式碼,會在String s1 = "a...a";// 共65535個a處編譯失敗:

✗ javac StringLenghDemo.java

StringLenghDemo.java:11: 錯誤: 常量字串過長

明明說好的長度限制是2147483647,為什麼65535個字元就無法編譯了呢?

當我們使用字串字面量直接定義String的時候,是會把字串在常量池中儲存一份的。那麼上面提到的65534其實是常量池的限制。

常量池中的每一種資料項也有自己的型別。Java中的UTF-8編碼的Unicode字串在常量池中以CONSTANT_Utf8型別表示。

CONSTANTUtf8info是一個CONSTANTUtf8型別的常量池資料項,它儲存的是一個常量字串。常量池中的所有字面量幾乎都是通過CONSTANTUtf8info描述的。CONSTANTUtf8_info的定義如下:

CONSTANT_Utf8_info {

u1 tag;

u2 length;

u1 bytes[length];

}

由於本文的重點並不是CONSTANTUtf8info的介紹,這裡就不詳細展開了,我們只需要我們使用字面量定義的字串在class檔案中,是使用CONSTANTUtf8info儲存的,而CONSTANTUtf8info中有u2 length;表明了該型別儲存資料的長度。

u2是無符號的16位整數,因此理論上允許的的最大長度是2^16=65536。而 java class 檔案是使用一種變體UTF-8格式來存放字元的,null 值使用兩個 位元組來表示,因此只剩下 65536- 2 = 65534個位元組。

關於這一點,在the class file format spec中也有明確說明:

The length of field and method names, field and method descriptors, and other constant string values is limited to 65535 characters by the 16-bit unsigned length item of the CONSTANTUtf8info structure (§4.4.7). Note that the limit is on the number of bytes in the encoding and not on the number of encoded characters. UTF-8 encodes some characters using two or three bytes. Thus, strings incorporating multibyte characters are further constrained.

也就是說,在Java中,所有需要儲存在常量池中的資料,長度最大不能超過65535,這當然也包括字串的定義咯。

執行期

上面提到的這種String長度的限制是編譯期的限制,也就是使用String s= "";這種字面值方式定義的時候才會有的限制。

那麼。String在執行期有沒有限制呢,答案是有的,就是我們前文提到的那個Integer.MAX_VALUE ,這個值約等於4G,在執行期,如果String的長度超過這個範圍,就可能會丟擲異常。(在jdk 1.9之前)

int 是一個 32 位變數型別,取正數部分來算的話,他們最長可以有

2^31-1 =2147483647 個 16-bit Unicodecharacter

2147483647 * 16 = 34359738352 位

34359738352 / 8 = 4294967294 (Byte)

4294967294 / 1024 = 4194303.998046875 (KB)

4194303.998046875 / 1024 = 4095.9999980926513671875 (MB)

4095.9999980926513671875 / 1024 = 3.99999999813735485076904296875 (GB)

有近 4G 的容量。

28、String的“+”是如何實現的?

解:

1. String s = "a" + "b",編譯器會進行常量摺疊(因為兩個都是編譯期常量,編譯期可知),即變成 String s = "ab"

2. 對於能夠進行優化的(String s = "a" + 變數 等)用 StringBuilder 的 append() 方法替代,最後呼叫 toString() 方法 (底層就是一個 new String())

29、String,StringBuilder和StingBuffer之間的區別與聯絡。

解:

String是字串常量,StringBuffer是字串變數(執行緒安全),StringBuilder是字串變數(非執行緒安全)。

簡要說,String型別和StringBuffer型別主要效能區別其實在於String是不可變物件,每次對String型別進行改變其實都等同於生成了一個新的String物件,然後將指標指向新的String物件,所以經常改變內容的字串最好不用String,而如果是StringBuffer,每次結果都會對StringBuffer物件本身進行操作,而不是新的物件,所以一般情況下推薦使用StringBuffer.

StringBuffer

java.lang.StringBuffer執行緒安全的可變字元序列。一個類似於String的字串緩衝區,可將字元緩衝區安全地用於多個執行緒。

StringBuffer上主要操作是append和insert方法,可過載這些方法以接受任意型別的資料。

StringBulider

java.lang.StringBuilder 提供一個與StringBuffer相容的API,但不保證同步,該類被設計用於StringBuffer的一個簡易替換,用於字串緩衝區被單個執行緒使用的時候。如果可能,建議優先使用。

大多數實現中,它比StringBuffer要快。

30、substring()方法到底做了什麼?不同版本的JDK中是否有區別?為什麼?

解:

31、如何理解String的intern方法,不同版本JDK有何不同?為什麼?

解:

intern()是String類中的一個public方法,用法如下:

String s1 = new String("Hollis");

String s2=s2.intern();

System.out.println( s1==s2 );

輸出結果為 true。

當一個String例項str呼叫intern()方法時,Java查詢常量池中是否有相同Unicode的字串常量,如果有,則返回其的引用,如果沒有,則在常量池中增加一個Unicode等於str的字串並返回它的引用;

不同版本的JDK中有何不同?

先看下以下程式碼:

String str1 = new StringBuilder("Hollis").append("Chuang").toString();

System.out.println(str1.intern() == str1);

列印結果:

jdk6 下false

jdk7 下true

Java 6 和Java7 中intern的表現有所不同,導致不同的原因是因為在Java 7中常量池的位置從PermGen區改到了Java堆區中。

jdk1.6中 intern 方法會把首次遇到的字串例項複製到永久待(常量池)中,並返回此引用;但在jdk1.7中,只是會把首次遇到的字串例項的引用新增到常量池中(沒有複製),並返回此引用。

對於以上程式碼中的str1.intern() ,在jdk1.6中,會把“HollisChuang”這個字串複製到常量池中,並返回他的引用。所以str2.intern()的值和str2的值,即兩個物件的地址是不一樣的。

對於以上程式碼中的str1.intern() ,在jdk1.7中,會把str2的引用儲存到常量池中,並把這個引用返回。所以str2.intern()的值和str2的值是相等的。

擴充套件另外一個例子:

String str2 = new StringBuilder("ja").append("va").toString();

System.out.println(str2.intern() == str2);以上程式碼,無論在1.6還是1.7中都會返回false。原因是"ja" + "va" = "java",這個常量在常量池中已經有了,因為這個字串在Java中隨處可見,曾經被初始化過。所以,在1.7中str2.intern()返回的內容是很久之前初始化的時候的那個引用,自然和剛剛建立的字串的應用不相等。

32、Java中整型的快取機制

33、在Java的程式碼中以及資料庫儲存中,如何對金額進行表示和計算。

解:

一般有兩種方案。

1.以元為單位。Java中儲存型別為BigDecimal,資料庫中儲存型別為number(10,2)。計算過程中保留兩位小數,可考慮四捨五入或者向上或者向下取整。根據業務實際情況決定。

2.以分為單位。Java中儲存型別為Long,資料庫中儲存型別為big int。計算過程中保留整數,考慮四捨五入或者取整。

34、打包分類

35、String限制(私密)

參考:球友