java 筆面試第一彈

目錄##

0.Set,List,Map的區(qū)別
1.Vector 與 Array 的區(qū)別
2.HashMap 與 Hashtable 的區(qū)別
3.String 和 StringBuilder 的區(qū)別
4.32 位 JVM 和 64 JVM的區(qū)別
5.transient與volatile
6.equals()和hashCode()區(qū)別
7.final

0.Set,List,Map的區(qū)別

數(shù)組是大小固定的,并且同一個數(shù)組只能存放類型一樣的數(shù)據(jù)(基本類型/引用類型)

JAVA集合可以存儲和操作數(shù)目不固定的一組數(shù)據(jù)。所有的JAVA集合都位于 java.util包中.

JAVA集合只能存放引用類型的的數(shù)據(jù),不能存放基本數(shù)據(jù)類型。

一:數(shù)組聲明了它容納的元素的類型,而集合不聲明。這是由于集合以object形式來存儲它們的元素。

二:一個數(shù)組實例具有固定的大小,不能伸縮。集合則可根據(jù)需要動態(tài)改變大小。

三:數(shù)組是一種可讀/可寫數(shù)據(jù)結(jié)構(gòu)---沒有辦法創(chuàng)建一個只讀數(shù)組。然而可以使用集合提供的ReadOnly方法,以只讀方式來使用集合。該方法將返回一個集合的只讀版本。

集合分類
--Collection:List、Set
--Map:HashMap、HashTable、TreeMap

1.Vector 與 Array 的區(qū)別

(1)Vector與數(shù)組最大區(qū)別在于,數(shù)組對象創(chuàng)建之后長度就不能改變了,而Vector的存儲空間可擴充。但注意,Vector存儲類型必須是引用類型。

(2)Vector

Vector的聲明格式一般是:Vector<類型> 變量名;
vector是通過封裝數(shù)組實現(xiàn)的,大小動態(tài)的,同時線程安全的;
訪問它比訪問ArrayList慢。

下面是Vector擴展大小源碼(Vector是默認(rèn)擴展1倍):

private void ensureCapacityHelper(int minCapacity){
 int oldCapacity = elementData.length;
 if (minCapacity > oldCapacity) {
     Object[] oldData = elementData;
     int newCapacity = (capacityIncrement > 0) ?(oldCapacity + capacityIncrement) : (oldCapacity * 2);
     if (newCapacity < minCapacity) {
        newCapacity = minCapacity;
     }
    elementData = Arrays.copyOf(elementData, newCapacity);
 }

}

(3)ArrayList

內(nèi)部是通過數(shù)組實現(xiàn)的,它允許對元素進(jìn)行快速隨機訪問。下面是ArrayList擴展大小源碼:

public boolean add(E e) {
 ensureCapacity(size + 1);  // 增加元素,判斷是否能夠容納。不能的話就要新建數(shù)組
 elementData[size++] = e;
 return true;
 }
public void ensureCapacity(int minCapacity) {
 modCount++; 
 int oldCapacity = elementData.length;
 if (minCapacity > oldCapacity) {
     Object oldData[] = elementData; // 此行沒看出來用處,不知道開發(fā)者出于什么考慮
     int newCapacity = (oldCapacity * 3)/2 + 1; // 增加新的數(shù)組的大小
     if (newCapacity < minCapacity)
    newCapacity = minCapacity;
         // minCapacity is usually close to size, so this is a win:
         elementData = Arrays.copyOf(elementData, newCapacity);
 }
}

(4)LinkedList
用鏈表結(jié)構(gòu)存儲數(shù)據(jù)的,很適合數(shù)據(jù)的動態(tài)插入和刪除,隨機訪問和遍歷速度比較慢。

List接口一共有三個實現(xiàn)類,分別是ArrayList、Vector和LinkedList.

2.HashMap 與 Hashtable 的區(qū)別

(1)Hashtable繼承自Dictionary類,而HashMap繼承自AbstractMap類。但二者都實現(xiàn)了Map接口。

(2)Hashtable中的方法是Synchronize的,而HashMap中的方法在缺省情況下是非Synchronize的。在多線程并發(fā)的環(huán)境下,可以直接使用Hashtable,不需要自己為它的方法實現(xiàn)同步,但使用HashMap時就必須要自己增加同步處理。

(3)HashMap把contains方法去掉了,改成containsValue和containsKey

Hashtable則保留了contains,containsValue和containsKey三個方法,其中contains和containsValue功能相同。

(4)Hashtable中,key和value都不允許出現(xiàn)null值。

HashMap中,null可以作為鍵,這樣的鍵只有一個;可以有一個或多個鍵所對應(yīng)的值為null。

(5)Hashtable、HashMap都使用了 Iterator。而由于歷史原因,Hashtable還使用了Enumeration的方式

(6)Hashtable和HashMap它們兩個內(nèi)部實現(xiàn)方式的數(shù)組的初始大小和擴容的方式。

HashTable中hash數(shù)組默認(rèn)大小是11,增加的方式是 old*2+1。

HashMap中hash數(shù)組的默認(rèn)大小(DEFAULT_INITIAL_CAPACITY)是16,而且一定是2的指數(shù)。

(7)哈希值的使用不同,HashTable直接使用對象的hashCode,代碼是這樣的:

  int hash = key.hashCode();
  int index = (hash & 0x7FFFFFFF) % tab.length;

而HashMap重新計算hash值,而且用與代替求模:

int hash = hash(k);
int i = indexFor(hash, table.length);

HashMap的部分源碼:

public V put(K key, V value)   
{   

if (key == null)   
    return putForNullKey(value);   
// 根據(jù) key 的 keyCode 計算 Hash 值  
int hash = hash(key.hashCode());   
// 搜索指定 hash 值在對應(yīng) table 中的索引  
int i = indexFor(hash, table.length);  
// 如果 i 索引處的 Entry 不為 null,通過循環(huán)不斷遍歷 e 元素的下一個元素  
for (Entry<K,V> e = table[i]; e != null; e = e.next)   
{   
    Object k;   
    if (e.hash == hash && ((k = e.key) == key   
        || key.equals(k)))   
    {   
        V oldValue = e.value;   
        e.value = value;   
        e.recordAccess(this);   
        return oldValue;   
    }   
}    
modCount++;   
addEntry(hash, key, value, i);   
return null;   
}  

//hash
 final int hash(Object k) {
    int h = hashSeed;
    if (0 != h && k instanceof String) {
        return sun.misc.Hashing.stringHash32((String) k);
    }

    h ^= k.hashCode();

    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

static int indexFor(int h, int length) 
{ 
return h & (length-1); 
}

void addEntry(int hash, K key, V value, int bucketIndex) 
{ 

Entry<K,V> e = table[bucketIndex];       

table[bucketIndex] = new Entry<K,V>(hash, key, value, e); 
// 如果 Map 中的 key-value 對的數(shù)量超過了極限
if (size++ >= threshold)     // 
    // 把 table 對象的長度擴充到 2 倍。
    resize(2 * table.length);       
}
public V get(Object key) 
 { 
 // 如果 key 是 null,調(diào)用 getForNullKey 取出對應(yīng)的 value 
 if (key == null) 
     return getForNullKey(); 
 // 根據(jù)該 key 的 hashCode 值計算它的 hash 碼
 int hash = hash(key.hashCode()); 
 // 直接取出 table 數(shù)組中指定索引處的值,
 for (Entry<K,V> e = table[indexFor(hash, table.length)]; 
     e != null; 
     // 搜索該 Entry 鏈的下一個 Entr 
     e = e.next)          // ①
 { 
     Object k; 
     // 如果該 Entry 的 key 與被搜索 key 相同
     if (e.hash == hash && ((k = e.key) == key 
         || key.equals(k))) 
         return e.value; 
 } 
 return null; 
}

// 以指定初始化容量、負(fù)載因子創(chuàng)建 HashMap 
public HashMap(int initialCapacity, float loadFactor) 
 { 
 // 初始容量不能為負(fù)數(shù)
 if (initialCapacity < 0) 
     throw new IllegalArgumentException( 
    "Illegal initial capacity: " + 
         initialCapacity); 
 // 如果初始容量大于最大容量,讓出示容量
 if (initialCapacity > MAXIMUM_CAPACITY) 
     initialCapacity = MAXIMUM_CAPACITY; 
 // 負(fù)載因子必須大于 0 的數(shù)值
 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 
     throw new IllegalArgumentException( 
     loadFactor); 
 // 計算出大于 initialCapacity 的最小的 2 的 n 次方值。
 int capacity = 1; 
 while (capacity < initialCapacity) 
     capacity <<= 1; 
 this.loadFactor = loadFactor; 
 // 設(shè)置容量極限等于容量 * 負(fù)載因子
 threshold = (int)(capacity * loadFactor); 
 // 初始化 table 數(shù)組
 table = new Entry[capacity];              // table是一個數(shù)組
 init(); 
 }

注:

HashSet :HashSet實現(xiàn)了Set接口,它不允許集合中有重復(fù)的值. 它是基于 HashMap 實現(xiàn)的,HashSet 底層采用 HashMap 來保存所有元素

HashSet 的實現(xiàn)其實非常簡單,它只是封裝了一個 HashMap 對象來存儲所有的集合元素,所有放入 HashSet 中的集合元素實際上由 HashMap 的 key 來保存,而 HashMap 的 value 則存儲了一個 PRESENT,它是一個靜態(tài)的 Object 對象。

public boolean add(Object o)方法用來在Set中添加元素,當(dāng)元素值重復(fù)時則會立即返回false,如果成功添加的話會返回true。

public class HashSet<E> 
 extends AbstractSet<E> 
 implements Set<E>, Cloneable, java.io.Serializable 
 { 
 // 使用 HashMap 的 key 保存 HashSet 中所有元素
 private transient HashMap<E,Object> map; 
 // 定義一個虛擬的 Object 對象作為 HashMap 的 value 
 private static final Object PRESENT = new Object(); 
 ... 
 // 初始化 HashSet,底層會初始化一個 HashMap 
 public HashSet() 
 { 
     map = new HashMap<E,Object>(); 
 } 
 // 以指定的 initialCapacity、loadFactor 創(chuàng)建 HashSet 
 // 其實就是以相應(yīng)的參數(shù)創(chuàng)建 HashMap 
 public HashSet(int initialCapacity, float loadFactor) 
 { 
     map = new HashMap<E,Object>(initialCapacity, loadFactor); 
 } 
 public HashSet(int initialCapacity) 
 { 
     map = new HashMap<E,Object>(initialCapacity); 
 } 
 HashSet(int initialCapacity, float loadFactor, boolean dummy) 
 { 
     map = new LinkedHashMap<E,Object>(initialCapacity 
         , loadFactor); 
 } 
 // 調(diào)用 map 的 keySet 來返回所有的 key 
 public Iterator<E> iterator() 
 { 
     return map.keySet().iterator(); 
 } 
 // 調(diào)用 HashMap 的 size() 方法返回 Entry 的數(shù)量,就得到該 Set 里元素的個數(shù)
 public int size() 
 { 
     return map.size(); 
 } 
 // 調(diào)用 HashMap 的 isEmpty() 判斷該 HashSet 是否為空,
 // 當(dāng) HashMap 為空時,對應(yīng)的 HashSet 也為空
 public boolean isEmpty() 
 { 
     return map.isEmpty(); 
 } 
 // 調(diào)用 HashMap 的 containsKey 判斷是否包含指定 key 
 //HashSet 的所有元素就是通過 HashMap 的 key 來保存的
 public boolean contains(Object o) 
 { 
     return map.containsKey(o); 
 } 
 // 將指定元素放入 HashSet 中,也就是將該元素作為 key 放入 HashMap 
 public boolean add(E e) 
 { 
     return map.put(e, PRESENT) == null; 
 } 
 // 調(diào)用 HashMap 的 remove 方法刪除指定 Entry,也就刪除了 HashSet 中對應(yīng)的元素
 public boolean remove(Object o) 
 { 
     return map.remove(o)==PRESENT; 
 } 
 // 調(diào)用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素
 public void clear() 
 { 
     map.clear(); 
 } 
 ... 
}

ConcurrentHashMap

引用:
java中的HashTable,HashMap和HashSet

3. String 和 StringBuilder 的區(qū)別

String : 創(chuàng)建字符串常量 ,字符串長度不可變

StringBuilder : 字符串變量 ,線程非安全

StringBuffer:字符串變量,線程安全

對于三者使用的總結(jié): 1.如果要操作少量的數(shù)據(jù)用 = String ;2.單線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuilder;3.多線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuffer

不要使用String類的"+"來進(jìn)行頻繁的拼接,因為那樣的性能極差的,應(yīng)該使用StringBuffer或StringBuilder類,這在Java的優(yōu)化上是一條比較重要的原則。

4.32 位 JVM 和 64 JVM的區(qū)別

64位將會多需要30%-50%的堆內(nèi)存。
GC中斷時間更長。

JVM 32bit 和JVM 64bit的區(qū)別如下:
1.目前只有server VM支持64bit JVM,client不支持32bit JVM。

2.The Java Plug-in, AWT Robot and Java Web Start這些組件目前不支持64bit JVM

3.本地代碼的影響:對JNI的編程接口沒有影響,但是針對32-bit VM寫的代碼必須重新編譯才能在64-bit VM工作。

4.32-bit JVM堆大小最大是4G, 64-bit VMs 上, Java堆的大小受限于物理內(nèi)存和操作系統(tǒng)提供的虛擬內(nèi)存。(這里的堆并不嚴(yán)謹(jǐn))

5.線程的默認(rèn)堆棧大?。涸趙indows上32位JVM,默認(rèn)堆棧最大是320k 64-bit JVM是1024K。

6.性能影響:
(1)64bit JVM相比32bit JVM,在大量的內(nèi)存訪問的情況下,其性能損失更少,AMD64和EM64T平臺在64位模式下運行時,Java虛擬機得到了一些額外的寄存器,它可以用來生成更有效的原生指令序列。

(2)性能上,在SPARC 處理器上,當(dāng)一個java應(yīng)用程序從32bit 平臺移植到64bit平臺的64bit JVM會用大約 10-20%的性能損失,而在AMD64和 EM64T平臺上,其性能損失的范圍在0-15%.

參考 : http://java.sun.com/docs/hotspot/HotSpotFAQ.html#64bit_description

5.transient volatile

transient

transient是類型修飾符,只能用來修飾字段。在對象序列化的過程中,標(biāo)記為transient的變量不會被序列化。

當(dāng)類Test的實例對象被序列化(比如將Test類的實例對象 t 寫入硬盤的文本文件t.txt中),變量 a 的內(nèi)容不會被保存,變量 b 的內(nèi)容則會被保存。

參考:
把一個對象的表示轉(zhuǎn)化為字節(jié)流的過程稱為串行化(也稱為序列化,serialization),從字節(jié)流中把對象重建出來稱為反串行化(也稱為為反序列化,deserialization)。

transient為不應(yīng)被串行化的數(shù)據(jù)提供了一個語言級的標(biāo)記數(shù)據(jù)方法。

volatile

volatile也是變量修飾符,只能用來修飾變量。volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內(nèi)存中重讀該成員變量的值。而且,當(dāng)成員變量發(fā)生變化時,強迫線程將變化值回寫到共享內(nèi)存。這樣在任何時刻,兩個不同的線程總是看到某個成員變量的同一個值。

Java語言規(guī)范中指出:為了獲得最佳速度,允許線程保存共享成員變量的私有拷貝,而且只當(dāng)線程進(jìn)入或者離開同步代碼塊時才與共享成員變量的原始值對比。

volatile關(guān)鍵字就是提示VM:對于這個成員變量不能保存它的私有拷貝,而應(yīng)直接與共享成員變量交互。

使用建議:在兩個或者更多的線程訪問的成員變量上使用volatile。當(dāng)要訪問的變量已在synchronized代碼塊中,或者為常量時,不必使用。

由于使用volatile屏蔽掉了VM中必要的代碼優(yōu)化,所以在效率上比較低,因此一定在必要時才使用此關(guān)鍵字。

《java并發(fā)編程實踐》--滿足以下所有標(biāo)準(zhǔn),才能使用volatile(2016.10.25)

1.寫入變量時并不依賴變量當(dāng)前的值;或者能夠保證只有單一的線程修改變量的值
2.變量不需要與其他狀態(tài)變量共同參與不變約束
3.訪問變量時,沒有其他的原因需要加鎖。

6.equals()和hashCode()區(qū)別?

參考:
equals()和hashCode()區(qū)別

equals():反映的是對象或變量具體的值,即兩個對象里面包含的值--可能是對象的引用,也可能是值類型的值。
hashCode():計算出對象實例的哈希碼,并返回哈希碼,又稱為散列函數(shù)。根類Object的hashCode()方法的計算依賴于對象實例的D(內(nèi)存地址),故每個Object對象的hashCode都是唯一的;當(dāng)然,當(dāng)對象所對應(yīng)的類重寫了hashCode()方法時,結(jié)果就截然不同了。
之所以有hashCode方法,是因為在批量的對象比較中,hashCode要比equals來得快,很多集合都用到了hashCode,比如HashTable。

兩個obj,如果equals()相等,hashCode()一定相等。
兩個obj,如果hashCode()相等,equals()不一定相等(Hash散列值有沖突的情況,雖然概率很低)。
所以:
可以考慮在集合中,判斷兩個對象是否相等的規(guī)則是:
第一步,如果hashCode()相等,則查看第二步,否則不相等;
第二步,查看equals()是否相等,如果相等,則兩obj相等,否則還是不相等。

7.final

(1)修飾類

final修飾的類不能被繼承,該類中的成員方法被隱式的置為final方法。所以說,使用的時候需謹(jǐn)慎。

final類不能被繼承

(2)修飾方法

final修飾方法,是以防任何繼承類修改它的含義。

(3)修飾變量

一個final變量,如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改;如果是引用類型的變量,則在對其初始化之后便不能再讓其指向另一個對象(其內(nèi)容是可以改變的)。

當(dāng)用final作用于類的成員變量時,成員變量(注意是類的成員變量,局部變量只需要保證在使用之前被初始化賦值即可)必須在定義時或者構(gòu)造器中進(jìn)行初始化賦值,而且final變量一旦被初始化賦值之后,就不能再被賦值了。

so 舉個例子感受下。

public class test1 {

final int i = 0;

public static void main(String[] args) {

    i = 1;//error:Cannot make a static reference to the non-static field i

    final StringBuffer str = new StringBuffer("123");

    str = new StringBuffer("");//error:The final local variable str cannot be assigned. It must be blank and not using a compound assignment

    str.append("123555");
}
}

參考:

http://www.cnblogs.com/dolphin0520/p/3736238.html

3

最后編輯于
?著作權(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 、一個".java" 源文件中是否可以包括多個類(不是內(nèi)部類)?有什么限制?可以有多個類,但只能有一個 pub...
    盡人事聽天命_6c6b閱讀 182評論 0 0
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,011評論 0 11
  • 面試 一般都是由淺到深去問,思路是:先考察基礎(chǔ)是否過關(guān),因為基礎(chǔ)知識決定了一個技術(shù)人員發(fā)展的上限再通過深度考察是否...
    攻城獅Chova閱讀 906評論 0 1
  • (一)Java部分 1、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,257評論 0 62
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,832評論 28 54

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