Java學習——基礎篇(四)

1.String是基本數據類型嗎?

答:不是。

一、基本數據類型:
byte:Java中最小的數據類型,在內存中佔8位(bit),即1個字節,取值範圍-128~127,默認值0
short:短整型,在內存中佔16位,即2個字節,取值範圍-32768~32767,默認值0
int:整型,用於存儲整數,在內在中佔32位,即4個字節,取值範圍-2147483648~2147483647,默認值0
long:長整型,在內存中佔64位,即8個字節-263~263-1,默認值0L
float:浮點型,在內存中佔32位,即4個字節,用於存儲帶小數點的數字(與double的區別在於float類型有效小數點只有6~7位),默認值0
double:雙精度浮點型,用於存儲帶有小數點的數字,在內存中佔64位,即8個字節,默認值0
char:字符型,用於存儲單個字符,佔16位,即2個字節,取值範圍0~65535,默認值爲空
boolean:布爾類型,佔1個字節,用於判斷真或假(僅有兩個值,即true、false),默認值false
二、引用數據類型:
類、接口類型、數組類型、枚舉類型、註解類型。
區別:
基本數據類型在被創建時,在棧上給其劃分一塊內存,將數值直接存儲在棧上。
引用數據類型在被創建時,首先要在棧上給其引用(句柄)分配一塊內存,而對象的具體信息都存儲在堆內存上,然後由棧上面的引用指向堆中對象的地址。

相關知識:
靜態區: 保存自動全局變量和 static 變量(包括 static 全局和局部變量)。靜態區的內容在總個程序的生命週期內都存在,由編譯器在編譯的時候分配。
堆區: 一般由程序員分配釋放,由 malloc 系列函數或 new 操作符分配的內存,其生命週期由 free 或 delete 決定。在沒有釋放之前一直存在,直到程序結束,由OS釋放。其特點是使用靈活,空間比較大,但容易出錯
棧區: 由編譯器自動分配釋放,保存局部變量,棧上的內容只在函數的範圍內存在,當函數運行結束,這些內容也會自動被銷燬,其特點是效率高,但空間大小有限
文字常量區: 常量字符串就是放在這裏的。 程序結束後由系統釋放。

2.float和double的區別

1.在內存中佔有的字節數不同

單精度浮點數在機內存佔4個字節

雙精度浮點數在機內存佔8個字節

2.有效數字位數不同

單精度浮點數有效數字8位

雙精度浮點數有效數字16位

3.數值取值範圍

單精度浮點數的表示範圍:-3.40E+38~3.40E+38

雙精度浮點數的表示範圍:-1.79E+308~-1.79E+308

4.在程序中處理速度不同

一般來說,CPU處理單精度浮點數的速度比處理雙精度浮點數快,如果不聲明,默認小數爲double類型,所以如果要用float的話,必須進行強轉

例如:float a=1.3; 會編譯報錯,正確的寫法 float a = (float)1.3;或者float a = 1.3f;(f或F都可以不區分大小寫)。

3.Java中的8種基本和對應的封裝類

Java的數據類型分爲三大類,即數值型,布爾型,字符型。數值型還可細分爲整數型和浮點型分別對應不同的精度和範圍。還有兩種類變量String和Date
8種基本類型按照類型劃分:byte,short,int,long,float,double,boolean,char。
8種基本類型的封裝類:Byte,Short,Integer,Long,Float,Double,Boolean,Character。

爲什麼引入封裝類?

Java是一個近乎純潔的面向對象編程語言,但是爲了編程的方便還是引入了基本數據類型,但是爲了能夠將這些基本數據類型當成對象操作,所以Java爲每一個基本數據類型都引入了對應的包裝類型(wrapper class),int的包裝類就是Integer,從Java 5開始引入了自動裝箱/拆箱機制,使得二者可以相互轉換。 

自動裝箱/拆箱機制

class AutoUnboxingTest {

    public static void main(String[] args) {
        Integer a = new Integer(3);
        Integer b = 3;                  // 將3自動裝箱成Integer類型
        int c = 3;
        System.out.println(a == b);     // false 兩個引用沒有引用同一對象
        System.out.println(a == c);     // true a自動拆箱成int類型再和c比較
    }
}

面試題

public class Test03 {

    public static void main(String[] args) {
        Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;

        System.out.println(f1 == f2);
        System.out.println(f3 == f4);
    }
}

       如果整型字面量的值在-128到127之間,那麼不會new新的Integer對象,而是直接引用常量池中的Integer對象,所以上面的面試題中f1==f2的結果是true,而f3==f4的結果是false。

4.棧、隊列和堆的區別

1.堆

堆是在程序運行時,而不是在程序編譯時,申請某個大小的內存空間。即動態分配內存,對其訪問和對一般內存的訪問沒有區別。堆是指程序運行時申請的動態內存,而棧只是指一種使用堆的方法(即先進後出)。

2.棧

棧(stack)——先進後出,刪除與加入均在棧頂操作

棧也稱爲堆棧,是一種線性表。

堆棧的特性: 最先放入堆棧中的內容最後被拿出來,最後放入堆棧中的內容最先被拿出來, 被稱爲先進後出、後進先出。

堆棧中兩個最重要的操作是PUSH和POP,兩個是相反的操作。

PUSH:在堆棧的頂部加入一 個元素。

POP:在堆棧頂部移去一個元素, 並將堆棧的大小減一。
 

隊列:什麼是隊列?

①隊列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱爲隊尾,進行刪除操作的端稱爲隊頭。

②隊列中沒有元素時,稱爲空隊列。

③建立順序隊列結構必須爲其靜態分配或動態申請一片連續的存儲空間,並設置兩個指針進行管理。一個是隊頭指針front,它指向隊頭元素;另一個是隊尾指針rear,它指向下一個入隊元素的存儲位置。

④隊列採用的FIFO(first in first out),新元素(等待進入隊列的元素)總是被插入到鏈表的尾部,而讀取的時候總是從鏈表的頭部開始讀取。每次讀取一個元素,釋放一個元素。所謂的動態創建,動態釋放。因而也不存在溢出等問題。由於鏈表由結構體間接而成,遍歷也方便。(先進先出)

5.Math.round(11.5) 等於多少?Math.round(-11.5)等於多少? 


答:Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四捨五入的原理是在參數上加0.5然後進行下取整。

此外,5/2=2,3/2=1這個運算都是向下取整的。除號和數據類型是相關的,除號前後數值相等,數據類型不一樣,得到的結果是不同。例如:3.0/2.0和3/2結果是不同的;

例如:int a = 3;
double b = 2.0;
double c;
c=a/b;
則c的值爲1.5

精度由低到高轉換正確,精度由高到低轉換報錯

例如:double a = 3.0;
int b = 2;
int c;
c=a/b;
直接報錯,編譯不通過,因爲a/b爲double,將double轉換成int型是將精度由高到低轉換,會造成精度丟失。

6.什麼是構造器?

用於創建對象並初始化對象屬性的方法,叫“構造方法”,也叫“構造器”;構造器在類中定義。說簡單點構造器的作用就是創建對象。

1.構造方法的定義聲明

特點:

1.方法名和類名相等

2.沒有返回值,並且不能用void(如果用void來聲明其返回值類型,那麼java會把這個構造器當做方法來處理——那它不再是構造器)

 3.構造器中的參數列表中的參數可有可無

2.構造方法的使用

 java中構造方法的使用有兩個地方,一個是跟在關鍵字new後面,類名加上一個小括號(),小括號內根據實際加上實參,另外一個是跟在關鍵字super或this後加上一個小括號(),小括號內根據實際添加實參.

例1.Demo demo = new Demo();

例2.public Demo(){

   this(2); //這裏調用參數爲int類型的本類的構造方法

 }

例 3.public Demo(){

   super(1); //調用參數爲int類型的父類的構造方法

 }

構造器注意事項:

  1. 類一定有構造器!這是真的,不需要質疑!
  2. 如果類沒有聲明(定義)任何的構造器,Java編譯器會自動插入默認構造器!
  3. 默認構造是無參數,方法體是空的構造器,且默認構造器的訪問權限隨着所屬類的訪問權限變化而變化。如,若類被public修飾,則默認構造器也帶public修飾符。
  4. 默認構造器是看不到的,一旦自己寫上構造器則默認構造器就沒有了,自己寫的叫自定義構造器,即便自己寫的是空參數的構造器,也是自定義構造器,而不是默認構造器。
  5. 如果類聲明瞭構造器,Java編譯器將不再提供默認構造器。若沒手動寫出無參構造器,但卻調用了無參構造器,將會報錯!
  6. 構造器是可以重載的,重載的目的是爲了使用方便,重載規則與方法重載規則相同。 
  7. 構造器是不能繼承的,因此不能被重寫。 
  8. 子類繼承父類,那麼子類型構造器默認調用父類型的無參數構造器。
     

關鍵字注意事項:

  1. this:在運行期間,哪個對象在調用this所在的方法,this就代表哪個對象,隱含綁定到當前“這個對象”。
  2. super():調用父類無參構造器,一定在子類構造器第一行使用!如果沒有則是默認存在super()的!這是Java默認添加的super()。
  3. super.是訪問父類對象,父類對象的引用,與this.用法一致
  4. this():調用本類的其他構造器,按照參數調用構造器,必須在構造器中使用,必須在第一行使用,this() 與 super() 互斥,不能同時存在
  5. this.是訪問當前對象,本類對象的引用,在能區別實例變量和局部變量時,this可省略,否則一定不能省!
  6. 如果子父類中出現非私有的同名成員變量時,子類要訪問本類中的變量用this. ;子類要訪問父類中的同名變量用super. 

7.equals,==和hashcode

  1. == : 該操作符生成的是一個boolean結果,它計算的是操作數的值之間的關係
  2. equals : Object 的 實例方法,比較兩個對象的content是否相同
  3. hashCode : Object 的 native方法 , 獲取對象的哈希值,用於確定該對象在哈希表中的索引位置,它實際上是一個int型整數

對於關係操作符 ==:

  • 若操作數的類型是基本數據類型,則該關係操作符判斷的是左右兩邊操作數的是否相等
  • 若操作數的類型是引用數據類型,則該關係操作符判斷的是左右兩邊操作數的內存地址是否相同。也就是說,若此時返回true,則該操作符作用的一定是同一個對象。

equals方法,內部實現分爲三個步驟:

  • 先 比較引用是否相同(是否爲同一對象),
  • 再 判斷類型是否一致(是否爲同一類型),
  • 最後 比較內容是否一致

哈希相關概念 
 我們首先來了解一下哈希表: 就是把任意長度的輸入(又叫做預映射, pre-image),通過散列算法,變換成固定長度的輸出(int),該輸出就是散列值。這種轉換是一種 壓縮映射,也就是說,散列值的空間通常遠小於輸入的空間。不同的輸入可能會散列成相同的輸出,從而不可能從散列值來唯一的確定輸入值。簡單的說,就是一種將任意長度的消息壓縮到某一固定長度的消息摘要的

應用–數據結構 : 數組的特點是:尋址容易,插入和刪除困難; 而鏈表的特點是:尋址困難,插入和刪除容易。那麼我們能不能綜合兩者的特性,做出一種尋址容易,插入和刪除也容易的數據結構?答案是肯定的,這就是我們要提起的哈希表,哈希表有多種不同的實現方法,我接下來解釋的是最常用的一種方法——拉鍊法,我們可以理解爲 “鏈表的數組”,如圖:

è¿éåå¾çæè¿°

左邊很明顯是個數組,數組的每個成員是一個鏈表。該數據結構所容納的所有元素均包含一個指針,用於元素間的鏈接。我們根據元素的自身特徵把元素分配到不同的鏈表中去,也是根據這些特徵,找到正確的鏈表,再從鏈表中找出這個元素。

hashcode詳細介紹:

1.概念:在 Java 中,由 Object 類定義的 hashCode 方法會針對不同的對象返回不同的整數。(這是通過將該對象的內部地址轉換成一個整數來實現的,但是 JavaTM 編程語言不需要這種實現技巧)。

2.hashCode 的常規協定是:

  1. 在 Java 應用程序執行期間,在對同一對象多次調用 hashCode 方法時,必須一致地返回相同的整數,前提是將對象進行 equals 比較時所用的信息沒有被修改。從某一應用程序的一次執行到同一應用程序的另一次執行,該整數無需保持一致。
  2. 如果根據 equals(Object) 方法,兩個對象是相等的,那麼對這兩個對象中的每個對象調用 hashCode 方法都必須生成相同的整數結果。
  3. 如果根據 equals(java.lang.Object) 方法,兩個對象不相等,那麼對這兩個對象中的任一對象上調用 hashCode 方法 不要求 一定生成不同的整數結果。但是,程序員應該意識到,爲不相等的對象生成不同整數結果可以提高哈希表的性能。 

  
  要想進一步瞭解 hashCode 的作用,我們必須先要了解Java中的容器,因爲 HashCode 只是在需要用到哈希算法的數據結構中才有用,比如 HashSet, HashMap 和 Hashtable。

       關於Java集合類,可參考:https://blog.csdn.net/zhangqunshuai/article/details/80660974

  Java中的集合(Collection)有三類,一類是List,一類是Queue,再有一類就是Set。 前兩個集合內的元素是有序的,元素可以重複;最後一個集合內的元素無序,但元素不可重複。

  那麼, 這裏就有一個比較嚴重的問題:要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢? 這就是 Object.equals 方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後添加到集合中的元素比較的次數就非常多了。 也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。於是,Java採用了哈希表的原理。 這樣,我們對每個要存入集合的元素使用哈希算法算出一個值,然後根據該值計算出元素應該在數組的位置。所以,當集合要添加新的元素時,可分爲兩個步驟: 
     

先調用這個元素的 hashCode 方法,然後根據所得到的值計算出元素應該在數組的位置。如果這個位置上沒有元素,那麼直接將它存儲在這個位置上;

如果這個位置上已經有元素了,那麼調用它的equals方法與新元素進行比較:相同的話就不存了,否則,將其存在這個位置對應的鏈表中(Java 中 HashSet, HashMap 和 Hashtable的實現總將元素放到鏈表的表頭)。

4、equals 與 hashCode

 前提: 談到hashCode就不得不說equals方法,二者均是Object類裏的方法。由於Object類是所有類的基類,所以一切類裏都可以重寫這兩個方法。(想想哈希表的拉鍊法就明白了,左邊數組中地址code相同,但值不一定相同;值相同,hashcode一定相同)

原則 1 : 如果 x.equals(y) 返回 “true”,那麼 x 和 y 的 hashCode() 必須相等 ;
原則 2 : 如果 x.equals(y) 返回 “false”,那麼 x 和 y 的 hashCode() 有可能相等,也有可能不等 ;
原則 3 : 如果 x 和 y 的 hashCode() 不相等,那麼 x.equals(y) 一定返回 “false” ;
原則 4 : 一般來講,equals 這個方法是給用戶調用的,而 hashcode 方法一般用戶不會去調用 ;
原則 5 : 當一個對象類型作爲集合對象的元素時,那麼這個對象應該擁有自己的equals()和hashCode()設計,而且要遵守前面所說的幾個原則。


8.String和StringBuilder、StringBuffer的區別?

Java平臺提供了兩種類型的字符串:String和StringBuffer/StringBuilder,它們可以儲存和操作字符串。其中String是隻讀字符串,也就意味着String引用的字符串內容是不能被改變的。而StringBuffer/StringBuilder類表示的字符串對象可以直接進行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,區別在於它是在單線程環境下使用的,因爲它的所有方面都沒有被synchronized修飾,因此它的效率也比StringBuffer要高。
 

9.描述一下JVM加載class文件的原理機制?

JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java中的類加載器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。 
由於Java的跨平臺性,經過編譯的Java源程序並不是一個可執行程序,而是一個或多個類文件。當Java程序需要使用某個類時,JVM會確保這個類已經被加載、連接(驗證、準備和解析)和初始化。類的加載是指把類的.class文件中的數據讀入到內存中,通常是創建一個字節數組讀入.class文件,然後產生與所加載類對應的Class對象。加載完成後,Class對象還不完整,所以此時的類還不可用。當類被加載後就進入連接階段,這一階段包括驗證、準備(爲靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換爲直接引用)三個步驟。最後JVM對類進行初始化,包括:1)如果類存在直接的父類並且這個類還沒有被初始化,那麼就先初始化父類;2)如果類中存在初始化語句,就依次執行這些初始化語句。 
類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。從Java 2(JDK 1.2)開始,類加載過程採取了父親委託機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能爲力時才由其子類加載器自行加載。JVM不會向Java程序提供對Bootstrap的引用。下面是關於幾個類加載器的說明:
 

  • Bootstrap:一般用本地代碼實現,負責加載JVM基礎核心類庫(rt.jar);
  • Extension:從java.ext.dirs系統屬性所指定的目錄中加載類庫,它的父加載器是Bootstrap;
  • System:又叫應用類加載器,其父類是Extension。它是應用最廣泛的類加載器。它從環境變量classpath或者系統屬性                   java.class.path所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。
     

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章