Java學習筆記_2

  1. 多線程。線程是進程中的一個執(zhí)行單元(執(zhí)行路徑),可以并發(fā)。
    System.gc();申請啟動垃圾回收器,垃圾回收器比較慢,可能會在虛擬機結束后出現(xiàn)。
    JVM啟動時就啟動了多個線程,至少有兩個線程可以出來:
    1,執(zhí)行main;2,負責垃圾回收的線程。
    第一種創(chuàng)建線程方法:繼承Thread類,覆蓋run方法,調用start()啟動線程;可以通過Thread類的getName()或Thread.currentThread().getName()獲取線程編號(從0開始)
    其他方法有wait(); notify(); sleep(time); stop();
    狀態(tài):
    凍結狀態(tài):由sleep(time);和wait();進入,notify();喚醒,醒后進入臨時阻塞狀態(tài)。連個都不具備。
    臨時阻塞狀態(tài):具備執(zhí)行資格,但不具備執(zhí)行權。
    第二種創(chuàng)建線程方法(常用):Runnable接口,用來幫助有父類的類創(chuàng)建線程。通過Thread類創(chuàng)建對象,并將Runnable接口的子類對象作為構造函數(shù)的參數(shù)進行傳遞。
    Thread類設計思想:帶一個Runnable()接口,運行這個接口的方法體,或者運行用戶自定義的run()方法,或者運行用戶自定義的Runnable子類里的run()方法。這處講解在視頻13-10!
    如果僅僅使用run();那最好只用Runnable來實現(xiàn),它的作用就是封裝線程任務。

  2. 實現(xiàn)Runnable接口的好處:
    1,將線程任務封裝成對象,面向對象的思想
    2,避免單繼承的局限性。

  3. 同步代碼塊 同步函數(shù) 和 靜態(tài)同步函數(shù)
    synchronized(obj){ } obj是任意對象可以用object類String類等。 synchronized 修飾一個函數(shù)。
    同步函數(shù)的鎖是固定的this,也就是當前對象,
    同步代碼塊的鎖是任意對象。
    靜態(tài)同步函數(shù)用的鎖是該類字節(jié)碼文件對象用this.getclass() 或 類名.class獲取。
    建議使用同步代碼塊?。?/em>
    同步的好處是:解決線程的安全問題。
    同步的壞處是:相對降低了效率,同步外的線程每次都會判斷同步鎖。
    必須保證 同步中每個線程用的都是同一個鎖。別用局部變量。
    sleep(time);有異常要拋 interruptedException

  4. Thread(同一個對象)多次.start會再運行一次對象的run(),對象的內部變量不會初始化。

  5. 字節(jié)碼文件所屬對象。JVM把所有類都封裝成對象,用對象.getclass() 或者 類名.class 調用這個屬性來獲得該對象,在用到靜態(tài)同步函數(shù)的鎖會碰到。

  6. 多線程通信。多個線程在處理同一個資源,但是任務卻不同,可能一個寫,一個讀。
    1.創(chuàng)建資源。2.創(chuàng)建任務。3.創(chuàng)建線程,執(zhí)行任務。4.開啟線程。

  7. 如何實現(xiàn)對象之間共享數(shù)據(jù)。除了靜態(tài),單例,還可以用傳參的方式,就是用一個私有成員某類變量,在構造函數(shù)中傳入一個對象初始化這個成員變量。

  8. 等待和喚醒機制。涉及的方法:
    1、wait(): 讓線程處于凍結狀態(tài),被wait的線程會被存儲到線程池中,其中有InterruptedException異常要捕捉。
    2、notify(): 喚醒線程池中的一個線程(任意)。
    3、 notifyAll(): 喚醒線程池中的所有線程,線程池是按照鎖區(qū)分的。
    注意這些方法都必須定義在同步中。
    因為這些方法是用于操作線程狀態(tài)的方法。
    必須要明確到底操作的是那個鎖上的線程。也就是說r.wait() 和 r.notify() 操作的是r鎖(監(jiān)視器)對應線程池中的線程。
    之所以上面這些方法定義在Object類中,因為這些都是監(jiān)視器的方法,監(jiān)視器其實是鎖,鎖可以是任意對象。
    把同步代碼塊放在共享資源類里,比如輸入和輸出都是。
    4、注意:使用wait(), notify(), synchronized(lock)來管理3個以上線程時候,容易造成死鎖和安全問題。比如隨機notify一直是那幾個生產(chǎn)者。解決:可以用notifyAll()和無限循環(huán)判斷flag來解決這個安全問題。具體來說,while(flag)解決了線程取得執(zhí)行權后判斷該不該執(zhí)行。用while()判斷標記安全?。?!

  9. final變量的初始化引用方法見此
    分靜態(tài)和非靜態(tài)final類型變量,要么直接初始化,要么構造函數(shù)初始化,要么構造代碼塊初始化。

  10. 同步的顯示鎖方式Lock。在java.util.concurrent.locks這個包里,用try{}finally{}方式鎖上和解鎖,lock(),unlock(); Lock lock = new Lock();
    創(chuàng)建鎖上的監(jiān)視器對象。通過已有鎖獲取,Condition con_1 = lock.newCondition();
    所以手法可以,同種線程共用一個監(jiān)視器。
    方法有:awai();signal();signalAll();
    總結:
    1、Lock借口:出現(xiàn)替代了同步代碼塊或者同步函數(shù)。將同步的隱式鎖操作變成顯式操作,而且更靈活,可以一個鎖上加多組監(jiān)視器。
    lock()獲取鎖。unlock()釋放鎖,定義在finally代碼快中。
    2、conditon接口:出現(xiàn)替代了Object中的wait notify notifyAll方法。
    將這些監(jiān)視器方法單獨進行了封裝,變成Condition監(jiān)視器對象??梢宰屓我怄i進行組合。
    方法有:await();signal();signalAll();
    3、其他: (1). wait 和 sleep 區(qū)別在于 wait可以制定時間也可以不制定時間。sleep必須制定時間。
    (2). 在同步中,wait: 釋放執(zhí)行器,釋放鎖。sleep: 釋放執(zhí)行權,不釋放鎖。

  11. 停止線程
    1、 stop方法,但已過時,不安全。
    2、run方法結束,不能控??身斠粋€標志位while(flag);和public setFlag(){} 把flag置false,退出循環(huán)結束任務。但是如果線程死鎖或凍結wait()狀態(tài),就無法讀取標志位退出。如何解決?
    3、可以使用interrupt()方法將線程從凍結狀態(tài)強制恢復到運行狀態(tài)中來,讓線程具備CPU的執(zhí)行資格。。但是強制終端等待動作會發(fā)生InterruptedException,記得要處理。

  12. 線程其他方法。
    1、Daemon線程。t.setDaemon();設為守護線程,后臺運行,必須在啟動前調用,當全是后臺線程時虛擬機退出。
    2、join()方法。t1.join();調用這條語句的線程,必須要等t1線程先運行完終止后才能接著運行,其他不用等。
    3、線程.toString()。會顯示線程名 優(yōu)先級 線程組。優(yōu)先級可以通過.setPriority()設置,最大10最小1越大優(yōu)先級越高,最高用常量來設置。線程組,可以通過對組進行操作,比如對整個組進行中斷處理。
    4、Thread.yield()方法。用來暫時釋放執(zhí)行權。
    5、抽象的類一定要用abstract修飾,比如定義一個實現(xiàn)某接口的類,如果沒有覆蓋住,那些方法還是抽象的,那么這個類必須要用abstract修飾,否則報錯。

  13. eclipse快捷鍵1
    eclipse快捷鍵2

  14. 字符串。字符串是不可變的。
    new String()還不如直接用""就好;
    常用的split();涉及到正則,重點掌握;
    trim()重點掌握;
    concat()拼接字符串,代替使用“” + “”;
    charAt代替使用[];
    String.valueOf()可以將基本數(shù)據(jù)類型轉換為字符串形式;
    equalsIgnoreCase()忽略大小寫比較兩字符串;
    intern()不知道具體什么用;
    length是所有數(shù)組都有的屬性;

GB2312是中文碼表。**最高位是1,所以顯示都是負數(shù)。

  1. F3顯示源代碼,cmd + 鼠標也可以進入聲明等內容,按cmd + [返回
    cmd + shift + f代碼格式化

  2. StringBuffer字符串緩沖區(qū)。用于存儲數(shù)據(jù)的容器。
    特點:
    1, 長度可變。
    2, 可以存儲不同類型的數(shù)據(jù)。
    3, 最終要轉成字符串進行使用
    4, 可以對字符串進行修改。字符串本身不能,通過緩沖區(qū)可以改。
    功能:
    1, 添加:
    StringBuffer append(); StringBuffer Insert(index, data);
    2, 刪除:
    StringBuffer delete(start, end); 包含頭,不包含尾
    StringBuffer deleteCharAt(int index);刪除指定位置的元素
    3, 查找:
    char charAt(index);
    int indexOf(string);
    int lastIndexOf(string);
    4, 修改:
    StringBuffer replace(start, end, string);先去掉那部分,再放入string
    void setCharAt(index, char);

  3. StringBuilder與StringBuffer用法兼容。不同點在于前者不保證同步,多線程里不安全,后者保證同步和多線程安全。前者是JDK1.5才出現(xiàn),后者JDK1.0就出現(xiàn)了。前者優(yōu)點是速度快 用于單線程,后者加了同步鎖比較慢 用于多線程。

  4. 基本數(shù)據(jù)類型對象包裝類。比如有Integer, Byte, Character, Boolean等。
    1, 取某數(shù)據(jù)類型的最大最小值(.MAX_VALUE),
    2, 還常用在與其他類型字符串形式之間轉換(.toBinaryString()),
    基本數(shù)值類型+"",String類中的valueOf(基本數(shù)值類型)
    3, 還常用到將字符串轉換成int型整數(shù) parseInt(string,radix) 或其他類型。 用包裝類中的方法 中的 xxx parseXxx("xxx類型的字符串")只有character沒有parse方法它不需要。
    4, 用對象.intValue()轉成相應數(shù)值
    5, 進制轉換 十進制->其他進制 .toString(data,radix);.toBinaryString(data); .toOctalString(data); .toHexString(data);
    其他進制->十進制 parseInt(string, radix); 這個最常用!

  5. Integer對象包裝類。這個類最常用,很多實用方法比如進制轉換。
    JDK更新后簡化了書寫:Integer i = 4; 自動裝箱,如果裝箱的是同樣的一個字節(jié),那該數(shù)據(jù)會被共享,不會重開空間。比如Integer i = 4;Integer j = 4;判斷i ==j返回的是True,注意,只有一個字節(jié)是這樣的,多于一個就不是這樣。
    i = i+4;(i.intValue() + 4); 自動拆箱 萬一對象為null,所以要判斷過。

  6. 集合框架。存儲多個對象,其特點是:
    1,是用于存儲對象的容器;
    2,集合的長度是可變的,比如remove();
    3,集合不可以存儲基本數(shù)據(jù)類型。
    4,那么對象是如何在集合中存儲的?
    因為集合容器內部的數(shù)據(jù)結構不同,有多種具體容器,不斷向上抽取,就形成了集合框架??蚣茼攲邮荂ollection接口 定義了多種容器常用方法:
    增刪改查,
    用迭代器取對象元素,Iterator iterator(); 用上for循環(huán)。迭代器接口在容器內部類中實現(xiàn),直接取得容器元素,讓每個容器取出方式變通用,降低了容器與取出方式之間的耦合性,在多種容器中使用迭代器變得很方便,iterator接口就是所有的Collection容器進行元素取出的公共接口。
    所以一個事物直接在訪問另一個事物的成員,這用內部類來完成。
    取交集保留 retainAll(coll),與removeAll]正相反。

  7. 常用Collection接口:List, Set,Map
    1,List集合:有序(存入和取出順序一致),元素都有索引,可重復
    2,List特有常見方法:有一個共性特點是都可以操作索引,按位置操作容器內容。
    ArrayList 是List的子類接口,可以用List list = new ArrayList();
    只有List可以利用索引循環(huán)取元素,也可以用迭代器取。只有List!
    3,快速失敗迭代器,集合和迭代器同時對元素進行修改,就會發(fā)生異常,因為獲取集合的迭代器后,對集合內元素進行修改,迭代器是不知道的,這樣迭代就會出問題。所以不要在迭代過程中使用集合操作元素,容易出現(xiàn)異常。
    但只有List容器有一個listIterator()方式可以解決這個問題,因為它提供了多種修改元素的方法。
    4,常用的List有:Vector,ArrayList,LinkList
    5,Vector:內部是數(shù)組數(shù)據(jù)結構,具備List所有功能特點。1.0就出現(xiàn),最早,是同步線程安全的。但是增刪都很慢,Vector幾乎不用,枚舉遍歷也不用了。
    ----ArrayList:內部是數(shù)組數(shù)據(jù)結構,不同步線程不安全。替代了Vector,查詢快因為空間局部性(緩存的存在),數(shù)組迭代效率更高 直接尋址,自己加鎖。
    ----LinkedList:內部是鏈表數(shù)據(jù)結構,是不同步的,增刪速度快。jdk1.6以后的新方法更好用:用offerFirst代替addFirst,用peekFirst peekLast代替getFirst getLast,用pollFirst代替removeFirst方法。
    6,擴大數(shù)組方法是創(chuàng)建一個新數(shù)組,把原來數(shù)組里的數(shù)據(jù)復制到新數(shù)組里。
    7,默認存儲的是Object類型,需要向下轉型才能使用實際存入的類方法。
    8,eclipse快捷鍵
    1, Set集合:不包含重復元素,無序,方法和Collection方法一致
    |-- HashSet:內部數(shù)據(jù)結構是哈希表,是不同步的,hashSet是通過哈希值hashCode()映射,來確定存放位置,哈希值和對象地址是不同的。而hashCode()通過值計算,所以基本數(shù)據(jù)類型的值如果一樣,hashCode()值也基本一樣,但自定義的類不一定,要重寫。
    那么,哈希表如何確定元素是否相同:1. 判斷哈希值對應位置有無元素(哈希值不同,內容肯定不相同,因為hashCode()是通過值映射,比如"ab" "ba"哈希值相同內容不同)。如果哈希值相同,再判斷內容是否相同用equals()。2. 判斷哈希值相同,其實是判斷的是對象的hashCode方法(有必要的話需要重寫);判斷內容相同,用的是equals方法(有必要的話需要重寫)。 至于我的理解,就是按當前對象放入容器里,如果碰到該映射位置上有對象,就判斷一下,哈希值不相同或者哈希值同內容不同,就存下一個空間,一直延順到空位置上,或者有同哈希值同內容的元素而退出來,這里操作會調用這兩個函數(shù)。
    注意:如果元素要存儲到HashSet集合中,必須覆蓋hashCode方法和equals方法。 remove() contains()方法也是用equals來找元素內容一樣的元素而不是找同地址的。所以一定要復寫好equals方法。
    |-- LinkedHashSet:按插入有序,不可重復。
    |-- TreeSet:原理二叉排序樹,按字典(自然順序)有序,是不同步的,自然順序由compareTo方法決定。compareTo判斷元素唯一的方法:就是根據(jù)compareTo()方法返回結果是否是0,是0就是相同元素,不存(這里要小心,等于0的話會去掉),所以要使用TreeSet容器,就需要實現(xiàn)Comparable接口,覆蓋compareTo方法。
    如果不要按照對象中具備的自然順序進行排序,因為有時使用的類不是我們自定義的,使用TreeSet集合第二種排序方式二: 讓集合自身具備比較功能
    TreeSet(Comparator<? super [E]> comparator) 構造一個新的空 TreeSet,它根據(jù)指定比較器進行排序。
    經(jīng)常需要寫比較器!!* 也就是一個實現(xiàn)Comparable接口的類,并覆蓋compare方法,傳給構造函數(shù),來定義自己的排序規(guī)則。
    1,Map:一次添加一對元素。Collection 一次添加一個元素。
    ---Map頁稱為雙列集合,Collection集合稱為單列集合。
    ---其實Map集合中存儲的就是鍵值對,而且必須保證鍵的唯一性。
    常用方法:添加put,刪除clear remove,判斷containskey containsValue,獲取get size,迭代方法keySet Values entrySet()找Map.Entry<T,E>這個靜態(tài)的映射項類對象。
    2,Map常用的子類:
    |-- Hashtable: 內部結構上哈希表,是同步,不允許null作為鍵和值。
    ------Properties: 用來存儲鍵值對應的配置文件信息,可以和IO技術相結合。
    |-- HashMap: 內部結構上哈希表,不說同步的,允許null作為鍵和值,重寫hashCode, equals方法。
    |--TreeMap: 內部結構上二叉樹,可以對Map集合中的鍵進行排序,能排序的是Tree*。

  8. 泛型。jdk1.5出現(xiàn)的安全機制。
    好處是:
    1,將運行時期的問題ClassCastException轉到了編譯時期,確保編譯時類型的安全。運行時會將泛型去掉,生成的class文件中是不帶泛型的,這個稱為泛型的擦除。
    那么為什么擦除?為了兼容以前不認識泛型的類加載器。后面運行用泛型的補償來自動向下轉型,原理是用getClass方法,再getName得到類名。
    2,避免了強制轉換的麻煩。
    那么<>什么時候用?當操作的飲用數(shù)據(jù)類型不確定的時候,就使用<>。將要操作的引用數(shù)據(jù)類型傳入即可。其實<>就是一個用于接收具體引用數(shù)據(jù)類型的參數(shù)范圍。
    在程序中,只要用到了帶有<>的類或者接口,就要明確傳入的具體引用數(shù)據(jù)類型。
    3,可以自定義泛型類,用來接收類中要操作的引用數(shù)據(jù)類型。public Class Tool<Q>{}; 當類中的操作的引用數(shù)據(jù)類型不確定的時候,就使用泛型來表示。
    4,泛型方法。public <W> void show(W str);(泛型必須寫在返回類型符前面)。 當方法為靜態(tài)時,不能訪問泛型類中的泛型,如果要訪問,只能把泛型定義在方法上,也就是做靜態(tài)泛型方法。
    5,泛型接口。實現(xiàn)泛型接口要在子類中確定泛型類型,如果還是不確定類型,就定義好泛型類來實現(xiàn)接口,使用泛型來傳給接口的泛型。比如接口是<T>,實現(xiàn)它的類是<Q> <Q>,看你參數(shù)類型明確不明確。
    6,泛型的通配符:? 傳參的時候不明確類型,使用(Collection<?> arr)來傳遞容器泛型不明確的參數(shù)。直接(Collection arr)也是一樣 這是老版本。使用?通配符的情況是僅在不明確類型而且不對這種類型的對象進行操作的時候,用?通配符,這種情況比較多。
    注意:不能用ArrayList<Student>的變量傳給Collection<Person>的變量,只認Person。這時候可以用通配符<? extends Person>來幫忙。
    7,泛型的限定。上限:<? extends Person>,只接受P和P的子類,一般存元素的時候用上限,因為這樣取出都是按上限類型進行運算,安全。
    --------下限:<? super P>,只接受P和P的父類。一般對元素進行取出操作時,可以用下限。

  9. 綜上,集合選擇的技巧
    需要唯一嗎?
    需要:Set
    ---需要定制順序:
    ------需要:TreeSet
    ------不需要:HashSet
    ------但是想要一個和存儲一致的順序(有序):LinkedHashSet
    不需要:List
    ---需要頻繁增刪嗎?
    ------需要:LinkedList
    ------不需要:ArrayList
    具體集合中源碼及其原理解析看這個博客

  10. 集合框架的工具類:有 Collections Arrays 。
    Collections:
    1,Collections.reverseOrder();想逆序,用這個。
    2,將非同步類改為同步,見視頻19-16(就加了個同步代碼塊再封裝一下),或者直接用Collections中定義好的工具方法。
    Arrays:
    1,Array.toString(); 將數(shù)組輸出成字符串形式。
    2,Arrays.asList(); 將數(shù)組轉成列表來使用。要注意:因為數(shù)組的長度的固定,所以對于這種集合的增刪方法是不可以使用的,否則會發(fā)生UnsupportedOperationException。
    并且,如果數(shù)組中的元素是對象,那么轉成集合時,直接將數(shù)組中的元素作為集合中的元素進行存儲。
    如果數(shù)組中的元素是基本類型,那么會將數(shù)組作為集合元素存儲。
    3,集合轉成數(shù)組:用Collection接口中的.toArray(); 需要傳入一個指定類型的數(shù)組大小寫上,太長會給null,太短會固化到集合長度。
    可以對集合中的元素操作方法進行限定,就是不允許增刪。

  11. for each語句。
    1,格式:
    for(類型 變量:Collection集合|數(shù)組){}
    2,foreach循環(huán)的局限是,不能對集合或數(shù)組修改,如重新賦值,因為它是先取出元素給單獨的一個變量來用的,比如P p = ps[i]; 所以foreach僅僅在不對集合修改的情況下使用非常方便,比如遍歷打印。而且這個for不能執(zhí)行指定次數(shù)的循環(huán)。
    3,另一個局限是:
    只能遍歷單列集合,也就是不能遍歷Map集合。解決辦法是:用keySet()方法轉單列就可以用了。

  12. 函數(shù)可變參數(shù):public static int add(int... arr){}
    可以直接寫多個參數(shù)在里面或者數(shù)組,而不用必須先創(chuàng)建一個數(shù)組再傳進入。如果要傳另外的單個參數(shù),要放可變參數(shù)前面,也即可變參數(shù)必須放列表結尾。應用有asList()方法。

  13. 靜態(tài)導入。import static java.util.Collections.sort; 其實是導入類中的靜態(tài)成員。如果名字沖突,那么一定要加包名。靜態(tài)導入用的不多。

  14. **System類中的方法和屬性都是靜態(tài)的。常見方法:
    1,long currentTimeMillis(); 獲取當前時間的毫秒值
    2,System.getProperties();獲取系統(tǒng)的屬性信息,并存儲到Properties(Map子類)集合中。 Properties集合中存儲都是String類型的鍵和值。最好使用它自己的存儲和取出方法來完成對元素的操作。
    重點:虛擬機啟動的時候都會先拿該系統(tǒng)上的信息,因為不同系統(tǒng)對應著不同的習慣,比如換行符在win上是/r/n,在unix上是/n,為了保證Java的強大移植性,用系統(tǒng)拿到的信息來寫這些易變化的符號。比如換行符用System.getProperty("line.separator");或System.lineSeparator(); 就是換行符。可以把它最終化為靜態(tài)常量。Line.separator這些鍵信息可以在getProperties方法里查到。
    3,System.setProperty();往系統(tǒng)里插入一些信息。F2顯示代碼信息??梢栽趀ditor里關掉自動懸停。

  15. Runtime運行時對象。沒有構造函數(shù)意味著這個類不能創(chuàng)建對象,因為構造函數(shù)被私有化了。如果一個類沒有構造函數(shù),這類里的方法都是靜態(tài)的,在這個類里不一樣,但是百分之百有一個靜態(tài)方法,返回這個對象,這是單例設計模式。常見方法有:
    1,exec("notepad.exe xxxx路徑"); 用記事本打開對應文件。返回值是Process對象,這個對象只有進程才創(chuàng)建,我們可以獲取。
    2,Process,有一個destroy()可以殺死由java打開的進程。

  16. Math類:提供了操作數(shù)學運算的方法。都是靜態(tài)的。有:
    1,ceil();floor();round();
    2,max();min();pow();
    3,random(); util里有Random類,更豐富的方法。比如new Random().nextInt(6) + 1;

  17. Date日期類默認初始化為當前時間,也可指定毫秒值來做。所以:
    1,毫秒值 -> 日期對象:通過new Date(timeMillis); Date打印出來就是日期。
    2,日期對象 -> 毫秒值:通過getTime方法。還可以通過setTime()設置
    3,調整日期輸出格式:使用DateFormat這個抽象類,通過其靜態(tài)工廠方法獲取實例對象,并使用該實例的format()格式化方法轉換日期對象成合適的字符串,還可以指定風格。這里的靜態(tài)工廠方法放回的是SimpleDateFormat這個子類。
    4,想要自定義格式,可以用SimpleDateFormat("yyyy--MM--dd");或者在DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);來指定風格。
    5,將日期格式的字符串 ---> 日期對象,使用的是DateFormat類中的parse()方法。

  18. Calendar類 基本取代了Date。也是個抽象類,需要get實例,它弄成了很多字段。
    可以通過鍵取值,鍵用類的靜態(tài)字段來寫 c.get(Calendar.YEAR)。小心月從0開始,周日按序到周六。
    set(2011, 3, 20);設置時間,記得月數(shù)要加1。
    add(Calendar.YEAR, -2); 在該日歷對象上加-2年,操作月也類似。

  19. IO流: 是相對于內存而言的。內存到硬盤是輸出(寫),硬盤到內存是輸入(讀)。分為字節(jié)流和字符流,處理字節(jié)的數(shù)據(jù)流對象是字節(jié)流。后來出現(xiàn)了碼表,如ascii表,java內置的unicode表-->無論什么字符都用兩個字節(jié)表示。
    字符流的由來:其實就是 字節(jié)流讀取文字字節(jié)數(shù)據(jù)后,不直接操作而現(xiàn)查指定的編碼表,獲取對應的文字,再對這個文字進行操作。也就是:字節(jié)流 + 編碼表。
    字節(jié)流的兩個頂層父類:
    1,InputStream 2,OutputStream
    字符流的兩個頂層父類:
    1,Reader 2,Writer
    這些體系的子類都以父類名做為后綴。
    一:往文件中寫入字符數(shù)據(jù)用FileWriter類的write()方法,先會寫到臨時存儲緩沖區(qū)中,要用flush()方法刷新下就寫入。這會使用系統(tǒng)的寫入資源,記得close();在關閉前會先調用flush刷新緩沖中的數(shù)據(jù)要目的地。
    如果想要續(xù)寫同一個文件,就用帶第二個參數(shù)的構造函數(shù)FileWriter(xxx, true);
    二:IO異常處理,打開會出異常,寫入會出異常比如寫失敗,關閉出異常,掛了wins返回異常。
    記住,流對象的初始化,在try外面創(chuàng)建引用變量,在try里面進行對象初始化,因為流對象創(chuàng)建都有異常發(fā)生。常見做法,打開和讀寫一起try,關閉在finally里再try。
    三:讀取方法:fr.read()讀出一個字符,讀出一個字符數(shù)組。
    小技巧,讀出來的字符數(shù)組可以用new String(buffer, 0, num)將指定范圍的數(shù)組字符內容轉換成字符串形式,可以用來寫入另一個文件或輸出?;蛘咧苯佑胒w.write(cbuff, off, len);來指定要寫入的數(shù)組內容范圍。
    四:緩沖區(qū)對象:提高效率。創(chuàng)建緩沖區(qū)必須要有被緩沖的對象。
    寫緩沖區(qū):FileWriter,用BufferedWriter.write()和.flush()寫入文件中,記得關閉緩沖區(qū).close();其實關閉的就是被緩沖的流對象。這對象又給了個newLine()寫換行符的方法。
    讀緩沖區(qū):FIleReader,多了readLine()讀一行的方法。原理是在緩沖區(qū)里讀到換行符就停止,把臨時StringBuilder內容輸出。
    裝飾設計模式:對一組對象的功能進行增強時,就可以使用該模式進行問題的解決 比如BufferedReader等。把舊類傳參給新類。與繼承一樣可以進行功能的擴展增強。
    有什么區(qū)別? 用不斷繼承來擴展會產(chǎn)生很多關系,變得臃腫,用裝飾更靈活。
    特點:裝飾類和被裝飾類都必須所屬同一個接口或者父類。
    五:LineNumberReader,可以讀取行號的帶緩沖技術讀文件。字符流常用的就是FileReader BufferedReader 都是Reader的子類。
    六:字節(jié)流:緩沖區(qū)是字節(jié)數(shù)組,父類是InputStream OutputStream,操作文件就用FileInputStream。寫數(shù)據(jù)是直接寫入目的地,可以不用flush(),但是close()要做 關閉資源。.available()可獲取文件大小,用這個可以定義一個剛剛夠的緩沖區(qū)大小 但易溢出,或者分段讀。BufferedOutputStream有Flush();的用法,但是不要每寫一個字節(jié)就刷一次會很慢。
    注意:千萬不要用直接與文件讀一個寫一個 不用緩沖區(qū)。
    字符流讀完字節(jié)查表轉字符,如果表里查不到內容 會用未知字符區(qū)的數(shù)據(jù)來表示 元數(shù)據(jù)和目標數(shù)據(jù)不會一致,所以別用字符流讀非文本文件。

  20. 鍵盤錄入。
    1,讀取鍵盤輸入 System.in 是InputStream對象,相當于打開一個字節(jié)流文件(當個文件使用就行)。查看這個類的方法比如read();clode();關一次就沒了別關。cmd + c是-1。
    2,InputStreamReader 是字節(jié)流通向字符流的橋梁(解碼 硬盤字節(jié)文件到內存字符),將字節(jié)流轉換成字符流。想要從鍵盤讀一行,要用緩沖流的readLine()。
    2,OutputStreamWriter 是字符流通向字節(jié)流的橋梁(編碼 內存字符到硬盤字節(jié)文件),將字節(jié)流轉換成字符流來使用。用這個來輸出記得flush();
    3,總結起來四個點:
    獲取控制臺輸入流:InputStream in = System.in;
    字節(jié)流變字符流:InputStreamReader isr= new InputStreamReader(in);
    對字符流高效裝飾:BufferedReader bufr = new BufferedReader(isr);
    String line = bufr.readLine();
    OutputStream out = System.out;
    OutputStreamWriter osw = new OutputStreamWriter(out);
    BufferedWriter bufw = new BufferedWriter(osw);
    bufw.write(line.toUpperCase());
    記得整合成一條語句。
    4,流的操作規(guī)律:
    之所以要弄清楚這個規(guī)律,是因為流對象太多,開發(fā)時不知道用哪個對象合適。
    想要知道開發(fā)時用到哪些對象。只要通過四個明確即可。
    (1) 明確源和目的(匯)
    源:InputStream Reader
    目的:OutputStream Writer

    (2) 明確數(shù)據(jù)是否是純文本數(shù)據(jù)。
    源:是純文本:Reader
    否:InputStream
    目的:是純文本 Writer
    否:OutputStream

    (3) 明確具體的設備。
    源設備:
    硬盤:File
    鍵盤:System.in
    內存:數(shù)組
    網(wǎng)絡:Socket流

    目的設備:
    硬盤:File
    控制臺:System.out
    內存:數(shù)組
    網(wǎng)絡:Socket流

    (4) 是否需要其他額外功能。
    1,是否需要高效(緩沖區(qū));
    是,就加上buffer.
    2,轉換。

需求1:復制一個文本文件。

1,明確源和目的。
    源:InputStream Reader
    目的:OutputStream  Writer
2,是否是純文本?
    是!
    源:Reader
    目的:Writer
    
3,明確具體設備。
    源:
        硬盤:File
    目的:
        硬盤:File

    FileReader fr = new FileReader("a.txt");
    FileWriter fw = new FileWriter("b.txt");
    
4,需要額外功能嗎?
    需要,需要高效。
    BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
    BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));

================================================

需求2:讀取鍵盤錄入信息,并寫入到一個文件中。

1,明確源和目的。
    源:InputStream Reader
    目的:OutputStream  Writer
2,是否是純文本呢?
    是,
    源:Reader
    目的:Writer
3,明確設備
    源:
        鍵盤。System.in
    目的:
        硬盤。File
        
    InputStream in = System.in;
    FileWriter fw = new FileWriter("b.txt");
    這樣做可以完成,但是麻煩。將讀取的字節(jié)數(shù)據(jù)轉成字符串。再由字符流操作。
4,需要額外功能嗎?
    需要。轉換。  將字節(jié)流轉成字符流。因為名確的源是Reader,這樣操作文本數(shù)據(jù)做便捷。
        所以要將已有的字節(jié)流轉成字符流。使用字節(jié)-->字符 。InputStreamReader
    InputStreamReader isr = new InputStreamReader(System.in);
    FileWriter fw = new FileWriter("b.txt");
    
    還需要功能嗎?
    需要:想高效。
    BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
    BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));

===================================================

需求3:將一個文本文件數(shù)據(jù)顯示在控制臺上。

1,明確源和目的。
    源:InputStream Reader
    目的:OutputStream  Writer
2,是否是純文本呢?
    是,
    源:Reader
    目的:Writer
3,明確具體設備
    源:
        硬盤:File
    目的:
        控制臺:System.out
        
    FileReader fr = new FileReader("a.txt");
    OutputStream out = System.out;//PrintStream
4,需要額外功能嗎?
    需要,轉換。
    FileReader fr= new FileReader("a.txt");
    OutputStreamWriter osw = new OutputStreamWriter(System.out);
    需要,高效。 
    BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
    BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

========================================================

需求4:讀取鍵盤錄入數(shù)據(jù),顯示在控制臺上。

1,明確源和目的。
    源:InputStream Reader
    目的:OutputStream  Writer
2,是否是純文本呢?
    是,
    源:Reader
    目的:Writer
3,明確設備。
    源:
        鍵盤:System.in
    目的:
        控制臺:System.out
    
    InputStream in = System.in;
    OutputStream out = System.out;
    
4,明確額外功能?
    需要轉換,因為都是字節(jié)流,但是操作的卻是文本數(shù)據(jù)。
    所以使用字符流操作起來更為便捷。
    InputStreamReader isr = new InputStreamReader(System.in);
    OutputStreamWriter osw = new OutputStreamWriter(System.out);
    
    為了將其高效。
    BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
    BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

========================================================

將一個中文字符串數(shù)據(jù)按照指定的編碼表寫入到一個文本文件中.

1,目的。OutputStream,Writer
2,是純文本,Writer。
3,設備:硬盤File 
FileWriter fw = new FileWriter("a.txt");
fw.write("你好"); 

注意:既然需求中已經(jīng)明確了指定編碼表的動作。
那就不可以使用FileWriter,因為FileWriter內部是使用默認的本地碼表。
只能使用其父類。OutputStreamWriter.
OutputStreamWriter接收一個字節(jié)輸出流對象,既然是操作文件,那么該對象應該是FileOutputStream

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);

需要高效嗎?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));

問:那么什么時候使用轉換流呢?

1,源或者目的對應的設備是字節(jié)流,但是操作的卻是文本數(shù)據(jù),可以使用轉換作為橋梁。
提高對文本操作的便捷。
2,一旦操作文本涉及到具體的指定編碼表時,必須使用轉換流 。

注意: FileWriter fw = new FileWriter("文件名");
封裝的就是
OutputStreamWrite osw = new OutputStreamWrite(new FileOutputStream("文件名"));
它是為了簡化書寫。所以FileWriter 會是OutputStreamWrite的子類。而前者其實就是轉換流默認使用了本機默認碼表,后者可以支持指定特定的碼表。
簡單來說:字節(jié)流 + 本機默認編碼表(Mac上是UTF-8) 就是是 ??!轉換流 == 字符流?。?。這是按照默認碼表操作文件的便捷類。UTF-8有3個字節(jié)表示中文。

  1. FIle類封裝文件或文件夾。 和流不同,流對象只能操作文件上的數(shù)據(jù)。和File類封裝文件和文件夾成對象,對文件和文件夾的屬性信息進行操作。File還可以封裝一個父路徑對象。其子段可以直接獲取本系統(tǒng)的文件 路徑 行分隔符,F(xiàn)ile.separator == System.getProperties(line.separator);等。
    1,相對路徑名:就是默認使用當前目錄,getParent()獲得的是空,但是可以用getAbsoluteFile()來獲得絕對路徑名或絕對路徑形式的文件。絕對路徑名:具體路徑,getParent()不為空。
    2,可以根據(jù)lastModified()是否改變,來讀取一次文件,以獲得最新的文件信息。
    3,創(chuàng)建和刪除文件:
    (1) 創(chuàng)建文件:File f = File("file.txt"); f.createNewFile();
    (2) 創(chuàng)建多級目錄:mkdirs();
    (3) 刪除文件:f.delete();f.deleteOnExit();指定好退出時刪除文件。對于有內容的文件夾不能刪。
    4,判斷:
    (1) 是否存在:f.exists();
    (2) 是不是文件 目錄:f.isFile(); f.isDirectory(); 操作作判斷用。
    5, 重命名:f.renameTo(); 抽象路徑名也會改變,改了路徑名會導致剪切操作。
    6,列出文件系統(tǒng)根目錄:File.listRoots();
    7,獲得指定分區(qū)容量情況:getFreeSpace(); getTotalSpace(); getUsableSpace();
    8,列出指定路徑下的文件和文件夾:list(); 注意File必須是目錄,否則NullPointerException無法創(chuàng)建返回值。
    9,帶過濾器的列出文件:
    接口 FilenameFilter accept()實現(xiàn)這方法。list(new FilterbyName()); 實現(xiàn)時可以創(chuàng)建一個成員變量,來指定要滿足的條件的信息。
    10,列出次抽象路徑名表示的目錄中的文件,不是文件名; listFiles();
    11,刪除一個帶內容的目錄。必須用(遞歸)深度遍歷文件夾 必須從最里面往外刪,
  2. Properties類。Map里有個HashTable可以和IO流相結合,HashTable 的子類Properties 是一個沒有泛型的類,其結構是:
    Map
    |---HashTable
    |---|---Properties:
    .
    Properties特點:
    1,該集合中的鍵和值都是字符串類型。
    2,集合中的數(shù)據(jù)可以保存到流中,或者從流獲取。
    .
    通常該集合用于操作以鍵值對形式存在的配置文件 xml sql。
    .
    Properties集合的操作:
    1,存 setProperty("xx", "xx");
    2,取 getProperty("xx");
    3,取出所有 stringPropertyNmaes();
    4,改 setProperty("xx", "xx");
    5,與IO結合 list() 可以將列表信息輸出到對應的流。比如打印到控制臺上。 System.getProperties()返回的就是這個類,可以直接list到輸出流。
    6,數(shù)據(jù)持久化 存儲到硬盤上 store(x, "xx");
    7,讀取文件上的屬性信息,要求:
    --集合中的數(shù)據(jù)來自一個文件,保證數(shù)據(jù)是鍵值對,使用讀取流。
    load() 可以將文件中的鍵值對信息加載到Prop對象上。
    讀行并按=切就可以模擬load()方法。
    8,操作讀取或寫入文件時,可以先封裝成File來看這個抽象路徑名是否存在,不存在可以新建,這樣就不會在讀取的時候報錯。
    9,常用來修改一個配置文件信息,先用讀取字符流讀進prop,再用輸出字符流重寫整個配置文件。
    10,map + io = Properties
    11,簡單的配置文件用Properties鍵值對,復雜的用.xml文件。
  3. 打印流PrintWriter PrintStream --可以直接操作輸入流和文件。PrintSteam 唯一一個不拋IO異常的IO對象,字符流用PrintWriter。
    ---提供打印方法可以對多種數(shù)據(jù)類型值進行打印。并保持數(shù)據(jù)的表示形式。
    ---他不會拋IOException
    其構造函數(shù),接受三種數(shù)據(jù)類型:
    ---字符串路徑
    ---File文件
    ---字節(jié)輸出流
    PrintWriter功能和PrintStream功能基本一樣,就多了一個接受Writer類的構造函數(shù)。
  4. 序列流SequenceInputStream --對多個流進行合并。
    ---對于字節(jié)輸入流,可以用數(shù)組來直接讀取,read(b);返回值是讀入的數(shù)據(jù)長度,無數(shù)據(jù)可讀返-1。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Java多線程學習 [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 3,111評論 1 18
  • 該文章轉自:http://blog.csdn.net/evankaka/article/details/44153...
    加來依藍閱讀 7,470評論 3 87
  • 寫在前面的話: 這篇博客是我從這里“轉載”的,為什么轉載兩個字加“”呢?因為這絕不是簡單的復制粘貼,我花了五六個小...
    SmartSean閱讀 4,953評論 12 45
  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽閱讀 2,599評論 1 15
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,734評論 18 399

友情鏈接更多精彩內容