Java基礎知識(二)

1,字符串
new String(“abc”)創建了幾個對象?
一個或兩個,如果常量池中原來有“abc”,則只創建一個對象;如果常量池中原來沒有字符串“abc”,那麼就會創建兩個對象。

String s="abc";
String s1="ab"+"c";
System.out.println(s==s1);
輸出 true ,因爲"ab"+"c"在編譯器就被轉換爲"abc",存放在常量區,因此輸出結果爲true

2,Java定義了一個基類(java.lang.Throwable)作爲所有異常的父類。Error、Exception、RuntimeException都是Throwable的子類,因此都能使用throw拋出。

Java中,常見的運行時異常(runtime exception)有:NullPointerException、ClassCastException、ArrayIndexOutOfBoundsException、ArrayStoreException、BufferOverflowException、ArithmeticException等。。
運行時異常不能用try catch 捕獲。

3,Java IO流 流的主要作用是 爲了改善程序性能且使用方便。
分爲字節流 和字符流。
字節流以字節(8bit)爲單位,包含兩個抽象類:InputStream輸入流 和 OutputStream輸出流。
字符流以字符(16bit)爲單位,一次可以讀取多個字節,包含兩個抽象類:Reader輸入流 和 Writer輸出流。
字節流和字符流最主要的區別是: 字節流在處理輸入輸出時不會用到緩存,而字符流用到了緩存。

Java IO類在設計時用到了 Decorator(裝飾者)模式。

4,NIO(NonBlocking IO)和IO的區別
Java NIO和IO之間第一個最大的區別是,IO是面向流的,NIO是面向緩衝區的。 Java IO面向流意味着每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀 取的數據,需要先將它緩存到一個緩衝區。 Java NIO的緩衝導向方法略有不同。數據讀取到一個它稍後處理的緩衝區,需要時可在緩衝區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該 緩衝區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏尚未處理的數據。

阻塞與非阻塞IO

Java IO的各種流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再幹任何事情了。 Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什麼都不會獲取。而不是保持線程阻 塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。 線程通常將非阻塞IO的空閒時間用於在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。

選擇器(Selectors)

Java NIO的選擇器允許一個單獨的線程來監視多個輸入通道,你可以註冊多個通道使用一個選擇器,然後使用一個單獨的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。

5,Socket通信
常見筆試題

打開ServerSocket連接, 
accept 接受連接
read 讀取數據
send 發送數據
close 關閉連接
服務器端:

package Java基礎知識.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
public class Server {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        BufferedReader br=null;
        PrintWriter pw=null;
        try{
            ServerSocket server=new ServerSocket(2000);
            Socket socket=server.accept();
            //獲取輸入流
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //獲取輸出流
            pw = new PrintWriter(socket.getOutputStream(),true);
            String s=br.readLine();//獲取接收的數據
            pw.println(s);//發送相同的數據給客戶端
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                br.close();
                pw.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

}


客戶端:

package Java基礎知識.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.*;
public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        BufferedReader br=null;
        PrintWriter pw=null;
        try{
            Socket socket=new Socket("localhost",2000);
            //獲取輸入流與輸出流
            br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            pw=new PrintWriter(socket.getOutputStream(),true);
            //向服務器發送數據
            pw.println("Hello ");
            String s=null;
            while(true){
                s=br.readLine();
                if(s!=null){
                    break;
                }
            }
            System.out.println(s);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                br.close();
                pw.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

}

在非阻塞IO (NIO)出現之前,Java通過傳統的Socket實現基本的網絡通信功能,

NIO採用輪詢的方式在處理多線程請求時不需要上下文的切換,而採用多線程的實現方式在線程之間切換時需要上下文的切換,同時也需要進行壓棧和出棧的操作,因此,NIO有較高的執行效率。
且NIO採用Buffer保存數據(如ByteBuffer、CharBuffer等),簡化了對流數據的管理。

NIO在網絡編程中有着非常重要的作用,與傳統的Socket方式相比,由於NIO採用了非阻塞的方式,在處理大量併發請求時,使用NIO要比Socket效率高出很多。

6,Java序列化

兩種方式:實現Serializable接口(writeObject、readObject)、Externalizable(writeExternal、readExternal)

聲明爲static或者transient的數據成員不能被序列化。即Java在序列化時不會實例化被static或者transient修飾的變量
因爲:static代表類的成員,transient(透明的,如果用transient聲明一個實例變量,當對象存儲時,它的值不需要維持,代表對象的臨時數據)。

什麼情況下需要序列化(將對象轉化爲流 ,便於傳輸):
1)需要通過網絡來發送對象,或對象的狀態需要被持久化到數據庫或文件中。
2)序列化能實現深複製,即可用複製引用的對象。

package Java基礎知識;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
public class 序列化  implements Serializable{
    private String name;
    private int age;
    public 序列化(){
        this.name=name;
        this.age=age;
    }


    public String getName() {
        return name;
    }


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


    public int getAge() {
        return age;
    }


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


    public static void main(String[] args) {
        // TODO Auto-generated method stub
        序列化 p=new 序列化();
        ObjectOutputStream oos=null;
        ObjectInputStream ois=null;
        try{
            FileOutputStream fos=new FileOutputStream("perple.out");
            oos=new ObjectOutputStream(fos);
            oos.writeObject(p);
            oos.close();
        }catch(Exception e){
            序列化 p1;
            try{
                FileInputStream fis=new FileInputStream("perple.out");
                ois=new ObjectInputStream(fis);
                p1=(序列化)ois.readObject();
                System.out.println("name:"+p1.getName());
                System.out.println("age:"+p1.getAge());
                ois.close();
            }catch(Exception ex){
                ex.printStackTrace();
            }

        }
    }

}

反序列化(將流轉化爲對象)

一個Java程序運行 從上到下的環境次序 是 :
Java程序、JRE/JVM、操作系統、硬件。

java文件被 javac指令編譯爲 .class後綴的 字節碼 文件, 再由JVM執行。java程序經編譯後會產生字節碼。。

類加載步驟:
1)裝載。 找到對應的class文件,然後導入
2)檢查 檢查待加載的class文件的正確性。
3)準備。 給類中的靜態變量分配存儲空間。
4)解析。給符號引用轉換成直接引用。
5)初始化。 對靜態變量和靜態代碼塊 執行初始化工作。

7,垃圾回收(GC Garbage Collection)

回收程序中不再使用的內存。

垃圾回收器負責完成3項任務: 分配內存、確保被引用對象的內存不被錯誤地回收以及回收不再被引用的對象的內存空間。
只要有一個以上的變量引用該對象,該對象就不會被回收。

垃圾回收器,使用有向圖來記錄和管理內存中的所有對象,通過有向圖來識別哪些對象是“可達的”(有變量引用它就是“可達的”),所有“不可達”對象都是可被垃圾回收的。。

垃圾回收算法:
1)引用計數算法:效率較低,JVM沒有采用。
2)追蹤回收算法
3)壓縮回收算法
4)複製回收算法
5)按代回收算法

是否可以主動通知JVM進行垃圾回收?
不能實時地調用垃圾回收器對某個對象或所有對象進行垃圾回收。可以通過System.gc()方法來“通知”垃圾回收器運行,當然,JVM不會保證垃圾回收器馬上就會運行。

8,內存泄漏
有兩種情況:一,在堆中申請的空間沒有被釋放:
二,對象已不再被使用,還依然在內存中保留着。Java中內存泄漏主要爲第二種情況。

內存泄漏的原因:
1)靜態集合類。 如HashMap、 Vector 。 它們 的生命週期與程序一致,容器中的對象在程序結束之前將不能被釋放,從而造成內存泄漏。
2)各種連接、 如數據庫連接、網絡連接 以及 IO連接等。 不再使用時,需要調用close 方法來釋放與數據庫的連接。
3)監聽器
4)變量不合理的作用域。

9, 容器
Collection : Set 、 List
Map

1)Set接口有兩個實現類: HashSet 、TreeSet (實現了SortedSet接口,TreeSet容器中的元素是有序的)
2)List 接口的實現類 : LinkedList 、ArrayList 、Vector
3)Map 接口的實現類 : HashMap 、 TreeMap、LinkedHashMap、 WeakHashMap、IdentityHashMap。
HashMap是基於散列表實現的,採用對象的HashCode 可以進行快速查詢。 LinkedHashMap 採用列表來維護內部的順序。 TreeMap基於紅黑樹的數據結構來實現的,內部元素是按需排列的。

10,迭代器
1)使用容器的迭代器 iterator()方法 返回一個Iterator, 然後通過Iterator 的next()方法返回第一個元素。
2)使用Iterator的 hasNext()方法判斷容器中是否還有元素,如果有,可以使用next()方法獲取下一個元素。
3)可以通過remove()方法刪除迭代器返回的元素。

使用iterator()方法時經常會遇到 ConcurrentModificationException異常,通常是由於使用Iterator遍歷容器的同時 又對容器進行增加或者刪除的操作導致的,或者由於多線程操作導致。

單線程下的解決辦法: 在遍歷的過程中把需要刪除的對象保存到一個集合中,等遍歷結束後再調用removeAll()方法來刪除,或者使用 iter.remove()方法。
多線程的解決辦法: 使用線程安全容器代替非線程安全的容器,如 ConcurrentHashMap、 CopyOnWriteArrayList等,也可以把對容器的操作放到 synchronized 代碼塊中,

Iterator 和 ListIterator的區別:
Iterator 只能正向遍歷集合,適用於獲取、移出元素。 ListIterator 繼承自 Iterator,專門針對 List,可以從兩個方向來遍歷 List,同時支持元素的修改。

11, ArrayList、Vector、LinkedList的區別
ArrayList、Vector:
支持用下標訪問元素,同時索引數據的速度比較快,但是在插入刪除元素時需要移動元素,執行比較慢。ArrayList、Vector都有一個初始化的容量,Vector 每次默認擴充爲原來的2倍,ArrayList默認擴充爲原來的1.5倍。

區別在於 : Vector是線程安全的,而ArrayList 是線程非安全的,
LinkedList 採用雙向鏈表實現,插入刪除效率高,非線程安全。

12,HashMap、HashTable、ConcurrentHashMap的區別:
ConcurrentHashMap 也是一個基於散列的Map ,但它使用了一種完全不同的加鎖策略來提供更高的併發性和伸縮性。

ConcurrentHashMap 並不是將每個方法都在同一個鎖上同步並使得每次只能有一個線程訪問容器,而是使用一種粒度更細的加鎖機制來實現更大程度的共享,這種機制稱爲 分段鎖, 在這種機制中,任意數量的讀取線程可以併發地訪問Map, 執行讀取操作的線程和執行寫入操作的線程可以併發地訪問Map,

ConcurrentHashMap 帶來的結果是: 在併發訪問環境下將實現更高的吞吐量,而在單線程環境中只損失非常小的性能。不會拋出ConcurrentModificationException,因此不需要在迭代過程中對容器加鎖。在ConcurrentHashMap 中沒有實現對Map以提供獨佔訪問。在Hashtable和 synchronizedMap中,獲得Map的鎖能防止其他線程訪問這個Map,

大多數情況下,ConcurrentHashMap 來代替同步Map能進一步提高代碼的可伸縮性,只有當應用程序需要加鎖Map以進行獨佔訪問時,才應該放棄使用ConcurrentHashMap 。

1,儘量將域聲明爲final類型,除非需要它們是可變的。
2,不可變對象一定是線程安全的。

不可變對象能極大地減低併發編程的複雜性,它們更爲簡單而且安全,可以任意共享而無須使用加鎖或保護性複製等機制。

3,用鎖來保護每個可變變量。
4,當保護同一個不變性條件中所有變量時,要使用同一個鎖。
5,如果從多個線程中訪問同一個可變變量時沒有同步機制,那麼程序會出現問題。

Map處理衝突的方法: 開放地址法、再hash法、鏈地址法等,
HashMap使用的是 鏈地址法來解決衝突,
從HashMap 中通過key 查找value時,首先調用key 的hashCode()方法來獲取到key 對應的hash值 h,確定鍵爲key 的所有值存儲的首地址,如果 h 對應的key值有多個,那麼程序會接着遍歷所有key, 通過調用 key的equals()方法來判斷 key的內容是否相等,只有當 equals方法返回值爲 true時,對應的value纔是正確的結果。

13, Collection 和Collections的區別
Collection 是一個集合接口,主要 List 和Set,提供了對集合對象進行基本操作的通用接口方法。

Collections 是針對集合類的 一個包裝類。提供一系列的靜態方法,Collections類不能實例化。

多線程
1,線程和進程的區別
線程的4種狀態: 運行、就緒、掛起、結束。
一個進程可以擁有多個線程,各個線程之間共享程序的內存空間(代碼段、數據、堆空間、及一些進程級的資源(如打開的文件)),但是各個線程擁有自己的棧空間,
1)使用多線程減少程序的響應時間。
2)與進程相比,線程的創建和切換開銷更小,便於數據共享。
3)使用多線程能簡化程序結構,使程序便於理解和維護

2,實現多線程的方法;
1)繼承 Thread類, 複寫 run方法
2)實現runnable 接口,複寫run方法

3,start()方法和run()方法的區別
系統通過調用線程類的start()方法來啓動一個線程,只有通過調用線程類的start()方法才能真正實現多線程。

將一個 用戶線程設置爲守護線程的方法就是在調用start()方法之前調用對象的setDaemon(true)方法。 若以上參數設置爲false, 則表示的是用戶進程模式。

join()方法: 讓調用該方法的線程在執行完run()方法後,再執行join方法後面的代碼。

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