傳智播客 2015年 劉意_Java基礎視頻-深刻淺出精華版 筆記(day21~day23)(2016年4月8日13:28:21)

day21 
1.編碼表概述和常見編碼表
 

計算機僅僅能識別二進制數據。早期由來是電信號。java

爲了方便應用計算機,讓它可以識別各個國家的文字。面試

就將各個國家的文字用數字來表示,並一一相應,造成一張表。windows

ASCII:美國標準信息交換碼。數組

用一個字節的7位可以表示。安全

ISO8859-1:拉丁碼錶。歐洲碼錶網絡

用一個字節的8位表示。多線程

GB2312:中國的中文編碼表。併發

GBK:中國的中文編碼表升級,融合了不少其它的中文文字符號。app

GB18030:GBK的代替版本號dom

BIG-5碼 :通行於臺灣、香港地區的一個繁體字編碼方案,俗稱「大五碼」。

Unicode:國際標準碼,融合了多種文字。

所有文字都用兩個字節來表示,Java語言使用的就是unicode

UTF-8:最多用三個字節來表示一個字符。

UTF-8不一樣。它定義了一種「區間規則」,這樣的規則可以和ASCII編碼保持最大程度的兼容:

它將Unicode編碼爲00000000-0000007F的字符,用單個字節來表示
它將Unicode編碼爲00000080-000007FF的字符用兩個字節表示 
它將Unicode編碼爲00000800-0000FFFF的字符用3字節表示 
 
 
 
2.String類中的編碼和解碼問題
 
String(byte[] bytes, String charsetName):經過指定的字符集解碼字節數組
  byte[] getBytes(String charsetName):使用指定的字符集合把字符串編碼爲字節數組
 
編碼:把看得懂的變成看不懂的
 * String -- byte[]
 * 
 * 解碼:把看不懂的變成看得懂的
 * byte[] -- String
 * 
 * 舉例:諜戰片(發電報。接電報)
 * 
 * 碼錶:小本子
 *         字符    數值
 * 
 * 要發送一段文字:
 *         今天晚上在老地方見
 * 
 *         發送端:今 -- 數值 -- 二進制 -- 發出去
 *         接收端:接收 -- 二進制 -- 十進制 -- 數值 -- 字符 -- 今
 * 
 *         今天晚上在老地方見
 * 
 * 編碼問題簡單,僅僅要編碼解碼的格式是一致的
 
public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "你好";
 
        // String -- byte[]
        byte[] bys = s.getBytes(); // [-60, -29, -70, -61]
        // byte[] bys = s.getBytes("GBK");// [-60, -29, -70, -61]//證實默認是GBK編碼
        // byte[] bys = s.getBytes("UTF-8");// [-28, -67, -96, -27, -91, -67]
        System.out.println(Arrays.toString(bys));
 
        // byte[] -- String
        String ss = new String(bys); // 你好
        // String ss = new String(bys, "GBK"); // 你好
        // String ss = new String(bys, "UTF-8"); // ?

??

        System.out.println(ss);
    }
=======================================
utf-8三個字節爲一組。GBK兩個字節爲一組,假設解碼編碼不一致會有分組上的問題
 
3.轉換流OutputStreamWriter的使用
轉換的意思就是利用編碼進行數據轉換
 
OutputStreamWriter(OutputStream out):依據默認編碼字節流的數據轉換字符流
  OutputStreamWriter(OutputStream out,String charsetName):依據指定編碼把字節流數據轉換爲字符流
  把字節流轉換爲字符流。

  字符流 = 字節流 +編碼表
 
==================切割線===================
 
public static void main(String[] args) throws IOException {
        // 建立對象
        // OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
        // "osw.txt")); // 默認GBK
        // OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
        // "osw.txt"), "GBK"); // 指定GBK
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
                "osw.txt"), "UTF-8"); // 指定UTF-8
        // 寫數據,這種方法可以直接寫字符串。實在太方便了。。
        osw.write("中國");
 
        // 釋放資源
        osw.close();
    }
4.轉換流InputStreamReader的使用
InputStreamReader(InputStream is):用默認的編碼讀取數據
  InputStreamReader(InputStream is,String charsetName):用指定的編碼讀取數據
 
public static void main(String[] args) throws IOException {
        // 建立對象
        // InputStreamReader isr = new InputStreamReader(new FileInputStream(
        // "osw.txt"));
 
        InputStreamReader isr = new InputStreamReader(new FileInputStream(
                "osw.txt"), "UTF-8");
 
        // 讀取數據
        // 一次讀取一個字符//注意,這是字符流!。!再也不是一次讀一個字節。!。應該是一次讀一個字符
        int ch = 0;
        while ((ch = isr.read()) != -1) {
            System.out.print((char) ch);
        }
 
        // 釋放資源
        isr.close();
    }
 
5.字符流的5種寫數據的方式
首先一個要注意的問題,flush問題
===============================
// 建立對象
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
                "osw2.txt"));
 
        // 寫數據
        // public void write(int c):寫一個字符
        // osw.write('a');
        // osw.write(97);
        // 爲何數據沒有進去呢?

(在沒寫close以前數據沒辦法顯示在txt文件裏)

        // 緣由是:字符 = 2字節
        // 文件裏數據存儲的基本單位是字節。(一個一個字節,停留在緩衝)
        //但是,假設沒有flush,但有close的話,字符仍是可以正常輸出的
        // void flush()
 
      // 刷新緩衝區
        osw.flush();
===============================

 

 

 

 

close()方法強制刷新(完畢了先刷新後關閉的功能)
 
OutputStreamWriter的方法:
 * public void write(int c):寫一個字符(可以傳'a',也可以傳97)
 * public void write(char[] cbuf):寫一個字符數組
 * public void write(char[] cbuf,int off,int len):寫一個字符數組的一部分
 * public void write(String str):寫一個字符串
 * public void write(String str,int off,int len):寫一個字符串的一部分
 
 面試題:close()和flush()的差異?

 * A:close()關閉流對象,但是先刷新一次緩衝區。關閉以後。流對象不可以繼續再使用了。
 * B:flush()只刷新緩衝區,刷新以後。流對象還可以繼續使用。(用於數據量大時刷新)
 
6.字符流的2種讀數據的方式
InputStreamReader的方法:
  int read():一次讀取一個字符
  int read(char[] chs):一次讀取一個字符數組
 
public static void main(String[] args) throws IOException {
        // 建立對象
        InputStreamReader isr = new InputStreamReader(new FileInputStream(
                "StringDemo.java"));
 
        // 一次讀取一個字符
        // int ch = 0;
        // while ((ch = isr.read()) != -1) {
        // System.out.print((char) ch);
        // }
 
        // 一次讀取一個字符數組
        char[] chs = new char[1024];
        int len = 0;
        while ((len = isr.read(chs)) != -1) {
            System.out.print(new String(chs, 0, len));
        }
 
        // 釋放資源
        isr.close();
    }
 
7.字符流複製文本文件案例1----copyfile
 
需求:把當前項目文件夾下的a.txt內容拷貝到當前項目文件夾下的b.txt中
  
  數據源:
          a.txt -- 讀取數據 -- 字符轉換流 -- InputStreamReader
  目的地:
          b.txt -- 寫出數據 -- 字符轉換流 -- OutputStreamWriter
==========================================
public static void main(String[] args) throws IOException {
        // 封裝數據源
        InputStreamReader isr = new InputStreamReader(new FileInputStream(
                "a.txt"));
        // 封裝目的地
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
                "b.txt"));
 
        // 讀寫數據
        // 方式1
        // int ch = 0;
        // while ((ch = isr.read()) != -1) {
        // osw.write(ch);
        // }
 
        // 方式2
        char[] chs = new char[1024];
        int len = 0;
        while ((len = isr.read(chs)) != -1) {
            osw.write(chs, 0, len);//注意這裏
            // osw.flush();//可選動做
        }
 
        // 釋放資源
        osw.close();
        isr.close();
    }
=============================================
8.轉換流的簡化寫法

 

 

解析:InputStreamReader,OutputStreamWriter 充當橋樑的做用(鏈接字節流和字符流)
而有時候寫起來很是麻煩,所以借用一個便捷子類來簡化書寫
 
 InputStreamReader isr = new InputStreamReader(new FileInputStream(
                "a.txt"));
等價於 FileReader fr = new FileReader("a.txt");
 
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
                "b.txt"));
等價於 FileWriter fw = new FileWriter("b.txt");
 
代碼被簡化
====================================================
需求:把當前項目文件夾下的a.txt內容拷貝到當前項目文件夾下的b.txt中
 
 數據源:
         a.txt -- 讀取數據 -- 字符轉換流 -- InputStreamReader -- FileReader
  目的地:
          b.txt -- 寫出數據 -- 字符轉換流 -- OutputStreamWriter -- FileWriter
 
public static void main(String[] args) throws IOException {
        // 封裝數據源
        FileReader fr = new FileReader("a.txt");
        // 封裝目的地
        FileWriter fw = new FileWriter("b.txt");
 
        // 一次一個字符
        // int ch = 0;
        // while ((ch = fr.read()) != -1) {
        // fw.write(ch);
        // }
 
        // 一次一個字符數組
        char[] chs = new char[1024];
        int len = 0;
        while ((len = fr.read(chs)) != -1) {
            fw.write(chs, 0, len);
            fw.flush();
        }
 
        // 釋放資源
        fw.close();
        fr.close();
    }
==================================================
9.字符緩衝輸出流BufferedWriter的使用
字符流爲了高效讀寫,也提供了相應的字符緩衝流。
  BufferedWriter:字符緩衝輸出流
  BufferedReader:字符緩衝輸入流
  
  BufferedWriter:字符緩衝輸出流
  將文本寫入字符輸出流,緩衝各個字符,從而提供單個字符、數組和字符串的高效寫入。 
  可以指定緩衝區的大小,或者接受默認的大小。在大多數狀況下。默認值就足夠大了。 
public static void main(String[] args) throws IOException {
        // BufferedWriter(Writer out)
        // BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
        // new FileOutputStream("bw.txt")));//原始寫法,帶轉換流
 
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
 
        bw.write("hello");
        bw.write("world");
        bw.write("java");
        bw.flush();
 
        bw.close();
    }
 
10.字符緩衝輸入流BufferedReader的使用
BufferedReader
  從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取。 
  可以指定緩衝區的大小,或者可以使用默認的大小。大多數狀況下,默認值就足夠大了。
BufferedReader(Reader in)
 
public static void main(String[] args) throws IOException {
        // 建立字符緩衝輸入流對象
        BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
 
        // 方式1
        // int ch = 0;
        // while ((ch = br.read()) != -1) {
        // System.out.print((char) ch);
        // }
 
        // 方式2
        char[] chs = new char[1024];
        int len = 0;
        while ((len = br.read(chs)) != -1) {
            System.out.print(new String(chs, 0, len));
        }
 
        // 釋放資源
        br.close();
    } 
 
11.字符緩衝流複製文本文件案例1
需求:把當前項目文件夾下的a.txt內容拷貝到當前項目文件夾下的b.txt中
  
  數據源:
          a.txt -- 讀取數據 -- 字符轉換流 -- InputStreamReader -- FileReader -- BufferedReader
  目的地:
          b.txt -- 寫出數據 -- 字符轉換流 -- OutputStreamWriter -- FileWriter -- BufferedWriter
public static void main(String[] args) throws IOException {
        // 封裝數據源
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        // 封裝目的地
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
 
        // 兩種方式當中的一種一次讀寫一個字符數組
        char[] chs = new char[1024];
        int len = 0;
        while ((len = br.read(chs)) != -1) {
            bw.write(chs, 0, len);
            bw.flush();
        }
 
        // 釋放資源
        bw.close();
        br.close();
    }
12.字符緩衝流的特殊功能---換行問題
字符緩衝流的特殊方法:
  BufferedWriter:
          public void newLine():依據系統來決定換行符
  BufferedReader:
          public String readLine():一次讀取一行數據
          包括該行內容的字符串,不包括不論什麼行終止符,假設已到達流末尾,則返回 null
 
 
 
/ public String readLine():一次讀取一行數據
        // String line = br.readLine();
        // System.out.println(line);//必須手動加換行ln才幹夠換行,讀數據的時候可以識別換行符但並不會加入換行符
        // line = br.readLine();
        // System.out.println(line);//必須手動加換行ln才幹夠換行,讀數據的時候可以識別換行符但並不會加入換行符
 
文件自己的內容(也就是說,文件自己有換行符)
 
 
public static void main(String[] args) throws IOException {
        // write();
        read();
    }
 
    private static void read() throws IOException {
        // 建立字符緩衝輸入流對象
        BufferedReader br = new BufferedReader(new FileReader("bw2.txt"));
 
        // public String readLine():一次讀取一行數據
        // String line = br.readLine();
        // System.out.println(line);
        // line = br.readLine();
        // System.out.println(line);
 
        // 終於版代碼
        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
 
        //釋放資源
        br.close();
    }
 
    private static void write() throws IOException {
        // 建立字符緩衝輸出流對象
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw2.txt"));
        for (int x = 0; x < 10; x++) {
            bw.write("hello" + x);
            // bw.write("\r\n");
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
輸出演示樣例
 
13.字符緩衝流複製文本文件案例2
需求:把當前項目文件夾下的a.txt內容拷貝到當前項目文件夾下的b.txt中
 
public static void main(String[] args) throws IOException {
        // 封裝數據源
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        // 封裝目的地
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
 
        // 讀寫數據
        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();//必定要記得換行
            bw.flush();
        }
 
        // 釋放資源
        bw.close();
        br.close();
    }
================================================
14.IO流小結圖解
15.複製文本文件的5種方式案例
  複製文本文件
  
  分析:
          複製數據。假設咱們知道用記事本打開並能夠讀懂,就用字符流。不然用字節流。
          經過該原理,咱們知道咱們應該採用字符流更方便一些。
          而字符流有5種方式。因此作這個題目咱們有5種方式。推薦掌握第5種。

  數據源:
          c:\\a.txt -- FileReader -- BufferdReader
 目的地:
          d:\\b.txt -- FileWriter -- BufferedWriter
=======================================
public static void main(String[] args) throws IOException {
        String srcString = "c:\\a.txt";
        String destString = "d:\\b.txt";
        // method1(srcString, destString);
        // method2(srcString, destString);
        // method3(srcString, destString);
        // method4(srcString, destString);
        method5(srcString, destString);
    }
 
    // 字符緩衝流一次讀寫一個字符串
    private static void method5(String srcString, String destString)
            throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(srcString));
        BufferedWriter bw = new BufferedWriter(new FileWriter(destString));
 
        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
 
        bw.close();
        br.close();
    }
 
    // 字符緩衝流一次讀寫一個字符數組
    private static void method4(String srcString, String destString)
            throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(srcString));
        BufferedWriter bw = new BufferedWriter(new FileWriter(destString));
 
        char[] chs = new char[1024];
        int len = 0;
        while ((len = br.read(chs)) != -1) {
            bw.write(chs, 0, len);
        }
 
        bw.close();
        br.close();
    }
 
    // 字符緩衝流一次讀寫一個字符
    private static void method3(String srcString, String destString)
            throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(srcString));
        BufferedWriter bw = new BufferedWriter(new FileWriter(destString));
 
        int ch = 0;
        while ((ch = br.read()) != -1) {
            bw.write(ch);
        }
 
        bw.close();
        br.close();
    }
 
    // 基本字符流一次讀寫一個字符數組
    private static void method2(String srcString, String destString)
            throws IOException {
        FileReader fr = new FileReader(srcString);
        FileWriter fw = new FileWriter(destString);
 
        char[] chs = new char[1024];
        int len = 0;
        while ((len = fr.read(chs)) != -1) {
            fw.write(chs, 0, len);
        }
 
        fw.close();
        fr.close();
    }
 
    // 基本字符流一次讀寫一個字符
    private static void method1(String srcString, String destString)
            throws IOException {
        FileReader fr = new FileReader(srcString);
        FileWriter fw = new FileWriter(destString);
 
        int ch = 0;
        while ((ch = fr.read()) != -1) {
            fw.write(ch);
        }
 
        fw.close();
        fr.close();
    }
 
16.複製圖片的4種方式案例
複製圖片
  
  分析:
          複製數據。假設咱們知道用記事本打開並能夠讀懂,就用字符流。不然用字節流。
          經過該原理,咱們知道咱們應該採用字節流。

          而字節流有4種方式。因此作這個題目咱們有4種方式。

推薦掌握第4種。

 
   數據源:
          c:\\a.jpg -- FileInputStream -- BufferedInputStream
  目的地:
          d:\\b.jpg -- FileOutputStream -- BufferedOutputStream
 
==========================================
public static void main(String[] args) throws IOException {
        // 使用字符串做爲路徑
        // String srcString = "c:\\a.jpg";
        // String destString = "d:\\b.jpg";
        // 使用File對象作爲參數
        File srcFile = new File("c:\\a.jpg");
        File destFile = new File("d:\\b.jpg");
 
        // method1(srcFile, destFile);
        // method2(srcFile, destFile);
        // method3(srcFile, destFile);
        method4(srcFile, destFile);
    }
 
    // 字節緩衝流一次讀寫一個字節數組
    private static void method4(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(destFile));
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
 
        bos.close();
        bis.close();
    }
 
    // 字節緩衝流一次讀寫一個字節
    private static void method3(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(destFile));
 
        int by = 0;
        while ((by = bis.read()) != -1) {
            bos.write(by);
        }
 
        bos.close();
        bis.close();
    }
 
    // 基本字節流一次讀寫一個字節數組
    private static void method2(File srcFile, File destFile) throws IOException {
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = fis.read(bys)) != -1) {
            fos.write(bys, 0, len);
        }
 
        fos.close();
        fis.close();
    }
 
    // 基本字節流一次讀寫一個字節
    private static void method1(File srcFile, File destFile) throws IOException {
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);
 
        int by = 0;
        while ((by = fis.read()) != -1) {
            fos.write(by);
        }
 
        fos.close();
        fis.close();
    }
==============================================
17.把集合中的數據存儲到文本文件案例 ArrayListToFile
 
需求:把ArrayList集合中的字符串數據存儲到文本文件
  
  分析:
          經過題目的意思咱們可以知道例如如下的一些內容。
              ArrayList集合裏存儲的是字符串。
              遍歷ArrayList集合,把數據獲取到。
              而後存儲到文本文件裏。
              文本文件說明使用字符流。
  
  數據源:
          ArrayList<String> -- 遍歷獲得每一個字符串數據
  目的地:
          a.txt -- FileWriter -- BufferedWriter
 
=================================================
public static void main(String[] args) throws IOException {
        // 封裝數據與(建立集合對象)
        ArrayList<String> array = new ArrayList<String>();
        array.add("hello");
        array.add("world");
        array.add("java");
 
        // 封裝目的地
        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
 
        // 遍歷集合
        for (String s : array) {
            // 寫數據
            bw.write(s);
            bw.newLine();
            bw.flush();
        }
 
        // 釋放資源
        bw.close();
    }
======================================
18.把文本文件裏的數據存儲到集合中案例  FileToArrayList
 
需求:從文本文件裏讀取數據(每一行爲一個字符串數據)到集合中。並遍歷集合
 * 
 * 分析:
 *         經過題目的意思咱們可以知道例如如下的一些內容,
 *             數據源是一個文本文件。
 *             目的地是一個集合。
 *             而且元素是字符串。
 * 
 * 數據源:
 *         b.txt -- FileReader -- BufferedReader
 * 目的地:
 *         ArrayList<String>
 
======================================
public static void main(String[] args) throws IOException {
        // 封裝數據源
        BufferedReader br = new BufferedReader(new FileReader("b.txt"));
        // 封裝目的地(建立集合對象)
        ArrayList<String> array = new ArrayList<String>();
 
        // 讀取數據存儲到集合中
        String line = null;
        while ((line = br.readLine()) != null) {
            array.add(line);
        }
 
        // 釋放資源
        br.close();//別忘記這一步
 
        // 遍歷集合
        for (String s : array) {
            System.out.println(s);
        }
    }
 
===========================================
19.隨機獲取文本文件裏的姓名案例
需求:我有一個文本文件裏存儲了幾個名稱。請你們寫一個程序實現隨機獲取一我的的名字。
  
  分析:
          A:把文本文件裏的數據存儲到集合中
          B:隨機產生一個索引
          C:依據該索引獲取一個值
 
public static void main(String[] args) throws IOException {
        // 把文本文件裏的數據存儲到集合中
        BufferedReader br = new BufferedReader(new FileReader("b.txt"));
        ArrayList<String> array = new ArrayList<String>();
        String line = null;
        while ((line = br.readLine()) != null) {
            array.add(line);
        }
        br.close();
 
        // 隨機產生一個索引
        Random r = new Random();
        int index = r.nextInt(array.size());
 
        // 依據該索引獲取一個值
        String name = array.get(index);
        System.out.println("該幸運者是:" + name);
    }
==========================================
20.複製單級目錄案例
/*
 * 需求:複製單極目錄
 * 
 * 數據源:e:\\demo
 * 目的地:e:\\test
 * 
 * 分析:
 *         A:封裝文件夾
 *         B:獲取該文件夾下的所有文本的File數組
 *         C:遍歷該File數組。獲得每一個File對象
 *         D:把該File進行復制
 */
public class CopyFolderDemo {
    public static void main(String[] args) throws IOException {
        // 封裝文件夾
        File srcFolder = new File("e:\\demo");
        // 封裝目的地
        File destFolder = new File("e:\\test");
        // 假設目的地目錄不存在,就建立(必定要注意這個問題)
        if (!destFolder.exists()) {
            destFolder.mkdir();
        }
 
        // 獲取該文件夾下的所有文本的File數組
        File[] fileArray = srcFolder.listFiles();
 
        // 遍歷該File數組。獲得每一個File對象
        for (File file : fileArray) {
            // System.out.println(file);
            // 數據源:e:\\demo\\e.mp3
            // 目的地:e:\\test\\e.mp3
            String name = file.getName(); // e.mp3
            File newFile = new File(destFolder, name); // e:\\test\\e.mp3
 
            copyFile(file, newFile);
        }
    }
 
    private static void copyFile(File file, File newFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                file));
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(newFile));
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
 
        bos.close();
        bis.close();
    }
}
=====================================================
注意:文件不存在可以本身主動建立。但是目錄不存在不會本身主動建立,所以
  if (!destFolder.exists()) {
            destFolder.mkdir();
        }
 
21.複製指定文件夾下指定後綴名的文件並改動名稱案例
數據源

 

 

/*
 * 需求:複製指定文件夾下的指定文件。並改動後綴名。

 * 指定的文件是:.java文件。

 * 指定的後綴名是:.jad
 * 指定的文件夾是:jad
 * 
 * 數據源:e:\\java\\A.java
 * 目的地:e:\\jad\\A.jad
 * 
 * 分析:
 *         A:封裝文件夾
 *         B:獲取該文件夾下的java文件的File數組
 *         C:遍歷該File數組,獲得每一個File對象
 *         D:把該File進行復制
 *         E:在目的地文件夾下更名
 */
public class CopyFolderDemo {
    public static void main(String[] args) throws IOException {
        // 封裝文件夾
        File srcFolder = new File("e:\\java");
        // 封裝目的地
        File destFolder = new File("e:\\jad");
        // 假設目的地文件夾不存在,就建立
        if (!destFolder.exists()) {
            destFolder.mkdir();
        }
 
        // 獲取該文件夾下的java文件的File數組
        File[] fileArray = srcFolder.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return new File(dir, name).isFile() && name.endsWith(".java");
            }
        });
 
        // 遍歷該File數組。獲得每一個File對象
        for (File file : fileArray) {
            // System.out.println(file);
            // 數據源:e:\java\DataTypeDemo.java
            // 目的地:e:\\jad\DataTypeDemo.java
            String name = file.getName();
            File newFile = new File(destFolder, name);
            copyFile(file, newFile);
        }
 
        // 在目的地文件夾下更名
        File[] destFileArray = destFolder.listFiles();
        for (File destFile : destFileArray) {
            // System.out.println(destFile);
            // e:\jad\DataTypeDemo.java
            // e:\\jad\\DataTypeDemo.jad
            String name =destFile.getName(); //DataTypeDemo.java
            String newName = name.replace(".java", ".jad");//DataTypeDemo.jad
 
            File newFile = new File(destFolder,newName);
            destFile.renameTo(newFile);
        }
    }
 
    private static void copyFile(File file, File newFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                file));
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(newFile));
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
 
        bos.close();
        bis.close();
    }
}

 

==================================
執行後演示樣例
 
而本人先前本身模仿了一個。功能稍微不同
數據源

 

 

/*
 * 需求:複製指定文件夾下的指定文件,並改動後綴名
 * 這個文件是我本人作的,問題是:目錄複製沒處理好(copyDirectory方法略繁瑣 )
 * 
 * 數據源:g:\\java\\A.java
 *  目的地:f:\\jad\\A.jad
 */
public class CopyFolderDemo2 {
    public static void main(String[] args) throws IOException {
        // 封裝文件夾
        File srcFolder = new File("g:\\java");
        File desFolder = new File("f:\\jad");
        if (!desFolder.exists()) {
            desFolder.mkdir();
        }
 
        File[] files = srcFolder.listFiles();
        for (File f : files) {
            if (!f.isDirectory()) {
                String name = f.getName();
                File newFile = new File(desFolder, name);
                copyFile(f, newFile);
                modifyFile(desFolder);
            } else {
                String folderName = f.getName();
                String desFolderName = desFolder.getName();
                copyDirectory(desFolder, folderName);
            }
        }
 
    }
 
    private static void copyDirectory(File desFolder, String folderName) {
 
        File newFolder = new File(desFolder, folderName);
 
        newFolder.mkdirs();
    }
 
    private static void modifyFile(File desFolder) {
        File[] files = desFolder.listFiles();
 
        for (File f : files) {
            if (!f.isDirectory()) {
                String name = f.getName();
                if (name.endsWith(".java")) {
                    int index = name.indexOf(".");
                    String fileNameString = name.substring(0, index);
                    String newNameString = fileNameString.concat(".jad");
                    File newFile = new File(desFolder, newNameString);
                    f.renameTo(newFile);
                }
            }
        }
 
    }
 
    private static void copyFile(File f, File newFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile));
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
 
        bis.close();
        bos.close();
 
    }
}
執行演示樣例
22.複製多級目錄案例
/*
 * 需求:複製多極目錄
 * 
 * 數據源:E:\JavaSE\day21\code\demos
 * 目的地:E:\\
 * 
 * 分析:
 *         A:封裝數據源File
 *         B:封裝目的地File
 *         C:推斷該File是目錄仍是文件
 *             a:是目錄
 *                 就在目的地文件夾下建立該文件夾
 *                 獲取該File對象下的所有文件或者目錄File對象
 *                 遍歷獲得每一個File對象
 *                 回到C
 *             b:是文件
 *                 就複製(字節流)
 */
public class CopyFoldersDemo {
    public static void main(String[] args) throws IOException {
        // 封裝數據源File
        File srcFile = new File("E:\\JavaSE\\day21\\code\\demos");
        // 封裝目的地File
        File destFile = new File("E:\\");
 
        // 複製目錄的功能
        copyFolder(srcFile, destFile);
    }
 
    private static void copyFolder(File srcFile, File destFile)
            throws IOException {
        // 推斷該File是目錄仍是文件
        if (srcFile.isDirectory()) {
            // 目錄
            File newFolder = new File(destFile, srcFile.getName());
            newFolder.mkdir();
 
            // 獲取該File對象下的所有文件或者目錄File對象
            File[] fileArray = srcFile.listFiles();
            for (File file : fileArray) {
                copyFolder(file, newFolder);
            }
        } else {
            // 文件
            File newFile = new File(destFile, srcFile.getName());
            copyFile(srcFile, newFile);
        }
    }
 
    private static void copyFile(File srcFile, File newFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(newFile));
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
 
        bos.close();
        bis.close();
    }
}
23.鍵盤錄入學生信息依照總分排序並寫入文本文件案例
Student類例如如下
===================
public class Student {
    // 姓名
    private String name;
    // 語文成績
    private int chinese;
    // 數學成績
    private int math;
    // 英語成績
    private int english;
 
    public Student() {
        super();
    }
 
    public Student(String name, int chinese, int math, int english) {
        super();
        this.name = name;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getChinese() {
        return chinese;
    }
 
    public void setChinese(int chinese) {
        this.chinese = chinese;
    }
 
    public int getMath() {
        return math;
    }
 
    public void setMath(int math) {
        this.math = math;
    }
 
    public int getEnglish() {
        return english;
    }
 
    public void setEnglish(int english) {
        this.english = english;
    }
 
    public int getSum() {
        return this.chinese + this.math + this.english;
    }
}
==========================================
測試類
=====
public class StudentDemo {
    public static void main(String[] args) throws IOException {
        // 建立集合對象
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num = s2.getSum() - s1.getSum();
                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
                int num3 = num2 == 0 ?

 s1.getMath() - s2.getMath() : num2;

                int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
                int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName())
                        : num4;
                return num5;
            }
        });
 
        // 鍵盤錄入學生信息存儲到集合
        for (int x = 1; x <= 5; x++) {
            Scanner sc = new Scanner(System.in);
            System.out.println("請錄入第" + x + "個的學習信息");
            System.out.println("姓名:");
            String name = sc.nextLine();
            System.out.println("語文成績:");
            int chinese = sc.nextInt();
            System.out.println("數學成績:");
            int math = sc.nextInt();
            System.out.println("英語成績:");
            int english = sc.nextInt();
 
            // 建立學生對象
            Student s = new Student();
            s.setName(name);
            s.setChinese(chinese);
            s.setMath(math);
            s.setEnglish(english);
 
            // 把學生信息加入到集合
            ts.add(s);
        }
 
        // 遍歷集合,把數據寫到文本文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("students.txt"));
        bw.write("學生信息例如如下:");
        bw.newLine();
        bw.flush();
        bw.write("姓名,語文成績,數學成績,英語成績");
        bw.newLine();
        bw.flush();
        for (Student s : ts) {
            StringBuilder sb = new StringBuilder();
            sb.append(s.getName()).append(",").append(s.getChinese())
                    .append(",").append(s.getMath()).append(",")
                    .append(s.getEnglish());
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
        }
        // 釋放資源
        bw.close();
        System.out.println("學習信息存儲完成");
    }
}
24.把一個文件裏的字符串排序後再寫入還有一個文件案例
 
已知s.txt文件裏有這種一個字符串:「hcexfgijkamdnoqrzstuvwybpl」
  請編敲代碼讀取數據內容,把數據排序後寫入ss.txt中。
 
分析:
          A:把s.txt這個文件給作出來
          B:讀取該文件的內容。存儲到一個字符串中
          C:把字符串轉換爲字符數組
          D:對字符數組進行排序
          E:把排序後的字符數組轉換爲字符串
          F:把字符串再次寫入ss.txt中
 
public static void main(String[] args) throws IOException {
        // 讀取該文件的內容,存儲到一個字符串中
        BufferedReader br = new BufferedReader(new FileReader("s.txt"));
        String line = br.readLine();
        br.close();
 
        // 把字符串轉換爲字符數組
        char[] chs = line.toCharArray();
 
        // 對字符數組進行排序
        Arrays.sort(chs);
 
        // 把排序後的字符數組轉換爲字符串
        String s = new String(chs);
 
        // 把字符串再次寫入ss.txt中
        BufferedWriter bw = new BufferedWriter(new FileWriter("ss.txt"));
        bw.write(s);
        bw.newLine();
        bw.flush();
 
        bw.close();
    }
 
//注意一下readLine(無需flush) 和 newLine,還有字符數組轉換爲字符串
 
25.本身定義類模擬BufferedReader的readLine()功能案例(有必定難度)
先寫MyBufferedReader類
================================================================
/*
 * 用Reader模擬BufferedReader的readLine()功能
 * 
 * readLine():一次讀取一行,依據換行符推斷是否結束,僅僅返回內容,不返回換行符
 */
public class MyBufferedReader {
    private Reader r;
 
    public MyBufferedReader(Reader r) {
        this.r = r;
    }
 
    /*
     * 思考:寫一個方法,返回值是一個字符串。

     */
    public String readLine() throws IOException {
        /*
         * 我要返回一個字符串,我該怎麼辦呢?

 咱們必須去看看r對象能夠讀取什麼東西呢? 兩個讀取方法,一次讀取一個字符或者一次讀取一個字符數組

         * 那麼。咱們要返回一個字符串。用哪一個方法比較好呢? 咱們很是easy想到字符數組比較好,但是問題來了。就是這個數組的長度是多長呢?
         * 根本就沒有辦法定義數組的長度,你定義多長都不合適。 因此,僅僅能選擇一次讀取一個字符。

         * 但是呢。這樣的方式的時候。咱們再讀取下一個字符的時候,上一個字符就丟失了 因此,咱們又應該定義一個暫時存儲空間把讀取過的字符給存儲起來。
         * 這個用誰比較和是呢?數組。集合,字符串緩衝區三個可供選擇。

         * 通過簡單的分析。終於選擇使用字符串緩衝區對象。並且使用的是StringBuilder
         */
        StringBuilder sb = new StringBuilder();
 
        // 作這個讀取最麻煩的是推斷結束,但是在結束以前應該是一直讀取,直到-1
 
 
        /*
        hello
        world
        java    
 
        104101108108111
        119111114108100
        1069711897
         */
 
        int ch = 0;
        while ((ch = r.read()) != -1) { //104,101,108,108,111
            if (ch == '\r') {
                continue;//必須\r\n同一時候存在的時候纔算換行,所以這裏繼續
            }
 
            if (ch == '\n') {
                return sb.toString(); //hello
            } else {
                sb.append((char)ch); //hello//必須轉換。否則就會104,101,108,108,111
            }
        }
 
        // 爲了防止數據丟失,推斷sb的長度不能大於0//反正就是無論怎樣,僅僅要有數據了。最後都來拼一下反正數據丟失
        if (sb.length() > 0) {
            return sb.toString();
        }
 
        return null;
    }
 
    /*
     * 先寫一個關閉方法
     */
    public void close() throws IOException {
        this.r.close();//表面上調用Buffered……的方法。實際上仍是用r自身的close方法
    }
}
============================================================

 

 

但是的話,。

 

如下是測試類
===========================================
/*
 * 測試MyBufferedReader的時候。你就把它看成BufferedReader同樣的使用
 */
public class MyBufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        MyBufferedReader mbr = new MyBufferedReader(new FileReader("my.txt"));
 
        String line = null;
        while ((line = mbr.readLine()) != null) {
            System.out.println(line);
        }
 
        mbr.close();
 
        // System.out.println('\r' + 0); // 13//經過加0的方法可以查看字符相應的數字編碼
        // System.out.println('\n' + 0);// 10
    }
}
 
==============================
26.LineNumberReader的使用案例
 
由上圖可知。其父類是BufferedReader
 BufferedReader
          |--LineNumberReader
              public int getLineNumber()得到當前行號。 
              public void setLineNumber(int lineNumber)
 
public static void main(String[] args) throws IOException {
        LineNumberReader lnr = new LineNumberReader(new FileReader("my.txt"));
 
        // 從10開始才比較好
        // lnr.setLineNumber(10);
 
        // System.out.println(lnr.getLineNumber());
        // System.out.println(lnr.getLineNumber());
        // System.out.println(lnr.getLineNumber());
 
        String line = null;
        while ((line = lnr.readLine()) != null) {
            System.out.println(lnr.getLineNumber() + ":" + line);
        }
 
        lnr.close();
    }
 
默認

 

 

set以後

 

(本身定義類模擬LineNumberReader的獲取行號功能案例省略==)
 
 
day21筆記補充
IO流小結(掌握)
    IO流
        |--字節流
            |--字節輸入流
                InputStream
                    int read():一次讀取一個字節
                    int read(byte[] bys):一次讀取一個字節數組
 
                    |--FileInputStream
                    |--BufferedInputStream
            |--字節輸出流
                OutputStream
                    void write(int by):一次寫一個字節
                    void write(byte[] bys,int index,int len):一次寫一個字節數組的一部分
 
                    |--FileOutputStream
                    |--BufferedOutputStream
        |--字符流
            |--字符輸入流
                Reader
                    int read():一次讀取一個字符
                    int read(char[] chs):一次讀取一個字符數組
 
                    |--InputStreamReader
                        |--FileReader
                    |--BufferedReader
                        String readLine():一次讀取一個字符串
            |--字符輸出流
                Writer
                    void write(int ch):一次寫一個字符
                    void write(char[] chs,int index,int len):一次寫一個字符數組的一部分
 
                    |--OutputStreamWriter
                        |--FileWriter
                    |--BufferedWriter
                        void newLine():寫一個換行符
 
                        void write(String line):一次寫一個字符串
 
今天寫過的案例(理解 練習一遍)
    A:複製文本文件 5種方式(掌握)
    B:複製圖片(二進制流數據) 4種方式(掌握)
    C:把集合中的數據存儲到文本文件
    D:把文本文件裏的數據讀取到集合並遍歷集合
    E:複製單級目錄
    F:複製單級目錄中指定的文件並改動名稱
        回想一下批量改動名稱
    G:複製多級目錄
    H:鍵盤錄入學生信息依照總分從高到低存儲到文本文件
    I:把某個文件裏的字符串排序後輸出到還有一個文本文件裏
    J:用Reader模擬BufferedReader的特有功能
    K:模擬LineNumberReader的特有功能

day22
 
1.登陸註冊案例IO版實現
簡要給代碼,詳細本身排錯。敲。
與集合的註冊案例想比,UserDaoImpl不同,其他的都同樣。
=========================================
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
import cn.itcast.dao.UserDao;
import cn.itcast.pojo.User;
 
/**
 * 這是用戶操做的詳細實現類(IO版)
 * 
 * @author 風清揚
 * @version V1.1
 * 
 */
public class UserDaoImpl implements UserDao {
    // 爲了保證文件一載入就建立,在這裏如下的紅色部分使用了static靜態代碼塊,類一載入就運行而且僅僅運行一次
    private static File file = new File("user.txt");
 
    static {
        try {
            file.createNewFile();
        } catch (IOException e) {
            System.out.println("建立文件失敗");
            // e.printStackTrace();
        }
    }
 
    @Override
    public boolean isLogin(String username, String password) {
        boolean flag = false;
 
        BufferedReader br = null;
        try {
            // br = new BufferedReader(new FileReader("user.txt"));
            br = new BufferedReader(new FileReader(file));
            String line = null;
            while ((line = br.readLine()) != null) {
                // username=password
                String[] datas = line.split("=");//利用了字符串的方法。利用正則表達式拆分字符串
                if (datas[0].equals(username) && datas[1].equals(password)) {
                    flag = true;
                    break;
                }
            }
        } catch (FileNotFoundException e) {//本類所有的異常僅僅能try……catch處理,因爲拋(throws)的話會比較麻煩而且影響其餘的類。違背了獨立改動且不影響其餘類的初衷
            System.out.println("用戶登陸找不到信息所在的文件");
            // e.printStackTrace();
        } catch (IOException e) {
            System.out.println("用戶登陸失敗");
            // e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    System.out.println("用戶登陸釋放資源失敗");
                    // e.printStackTrace();
                }
            }
        }
 
        return flag;
    }
 
    @Override
    public void regist(User user) {
        /*
         * 爲了讓註冊的數據能夠有必定的規則,我就自定義了一個規則: username=password
         */
        BufferedWriter bw = null;
        try {
            // bw = new BufferedWriter(new FileWriter("user.txt"));
            // bw = new BufferedWriter(new FileWriter(file));
            // 爲了保證數據是追加寫入,必須加true
            bw = new BufferedWriter(new FileWriter(file, true));//不加true的話。又一次建立文件會形成上一次的註冊信息丟失
            bw.write(user.getUsername() + "=" + user.getPassword());
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            System.out.println("用戶註冊失敗");
            // e.printStackTrace();
        } finally {
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    System.out.println("用戶註冊釋放資源失敗");
                    // e.printStackTrace();
                }
            }
        }
    }
}
 
 
附:String的split方法以及user.txt裏面保存的信息
 
 
註冊信息保存在文件裏。不會丟失。

 2.數據輸入輸出流
 
 

  可以讀寫基本數據類型的數據
  數據輸入流:DataInputStream
              DataInputStream(InputStream in)
  數據輸出流:DataOutputStream
              DataOutputStream(OutputStream out) 
===================================
public static void main(String[] args) throws IOException {
        // 寫
        // write();
 
        // 讀
        read();
    }
 
    private static void read() throws IOException {
        // DataInputStream(InputStream in)
        // 建立數據輸入流對象
        DataInputStream dis = new DataInputStream(
                new FileInputStream("dos.txt"));
 
        // 讀數據
        byte b = dis.readByte();
        short s = dis.readShort();
        int i = dis.readInt();
        long l = dis.readLong();
        float f = dis.readFloat();
        double d = dis.readDouble();
        char c = dis.readChar();
        boolean bb = dis.readBoolean();
 
        // 釋放資源
        dis.close();
 
        System.out.println(b);
        System.out.println(s);
        System.out.println(i);
        System.out.println(l);
        System.out.println(f);
        System.out.println(d);
        System.out.println(c);
        System.out.println(bb);
    }
 
    private static void write() throws IOException {
        // DataOutputStream(OutputStream out)
        // 建立數據輸出流對象
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(
                "dos.txt"));
 
        // 寫數據了
        dos.writeByte(10);
        dos.writeShort(100);
        dos.writeInt(1000);
        dos.writeLong(10000);
        dos.writeFloat(12.34F);//有一個類型轉換
        dos.writeDouble(12.56);
        dos.writeChar('a');
        dos.writeBoolean(true);
 
        // 釋放資源
        dos.close();
    }
==========================================
注意一個問題,假設僅僅有DataOutputStream來寫文件。直接雙擊打開文件。讀到的數據是亂碼
 
3.內存操做流的概述和解說
內存操做流:用於處理暫時存儲信息的。程序結束,數據就從內存中消失。

  字節數組:
          ByteArrayInputStream
          ByteArrayOutputStream
  字符數組:
          CharArrayReader
          CharArrayWriter
  字符串:
          StringReader
          StringWriter
 

對於ByteArrayOutputStream:

 

此類實現了一個輸出流,當中的數據被寫入一個 byte 數組。緩衝區會隨着數據的不斷寫入而本身主動增加。可以使用toByteArray() 和 toString() 獲取數據。

關閉 ByteArrayOutputStream 無效。此類中的方法在關閉此流後仍可被調用,而不會產生不論什麼 IOException。(說白了就是不用close方法了) 

 

 

對於ByteArrayInputStream :

ByteArrayInputStream 包括一個內部緩衝區,該緩衝區包括從流中讀取的字節。

內部計數器跟蹤 read 方法要提供的下一個字節。

關閉 ByteArrayInputStream 無效。

此類中的方法在關閉此流後仍可被調用。而不會產生不論什麼 IOException

 

public static void main(String[] args) throws IOException {
        // 寫數據
        // ByteArrayOutputStream()
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
        // 寫數據
        for (int x = 0; x < 10; x++) {
            baos.write(("hello" + x).getBytes());
        }
 
        // 釋放資源
        // 經過查看源代碼咱們知道這裏什麼都沒作,因此根本需要close()
        // baos.close();
 
        // public byte[] toByteArray()
        byte[] bys = baos.toByteArray();
 
        // 讀數據
        // ByteArrayInputStream(byte[] buf)
        ByteArrayInputStream bais = new ByteArrayInputStream(bys);
 
        int by = 0;
        while ((by = bais.read()) != -1) {
            System.out.print((char) by);
        }
 
        // bais.close();//不需要close
    }
 
4.打印流的概述和特色
 
打印流
  字節流打印流    PrintStream
  字符打印流    PrintWriter
  
  打印流的特色:
          A:僅僅有寫數據的。沒有讀取數據。

僅僅能操做目的地,不能操做數據源。

          B:可以操做隨意類型的數據。

          C:假設啓動了本身主動刷新,能夠本身主動刷新。(假設不啓動的話。不刷新文件(就是flush或者close)裏面是沒有數據的)
          D:該流是可以直接操做文本文件的。
              哪些流對象是可以直接操做文本文件的呢?

              FileInputStream
              FileOutputStream
              FileReader
              FileWriter
              PrintStream
              PrintWriter
              看API,查流對象的構造方法,假設同一時候有File類型和String類型的參數,通常來講就是可以直接操做文件的。

  
              流:
                  基本流:就是能夠直接讀寫文件
                  高級流:在基本流基礎上提供了一些其它的功能
 
==========================================
public static void main(String[] args) throws IOException {
        // 做爲Writer的子類使用
        PrintWriter pw = new PrintWriter("pw.txt");
 
        pw.write("hello");
        pw.write("world");
        pw.write("java");
 
        pw.close();//沒有這句話不出數據,因爲這不是本身主動刷新的(或者)
    }
 
5.PrintWriter實現本身主動刷新和換行
 
 可以操做隨意類型的數據。
          print()
          println()
 
 
public static void main(String[] args) throws IOException {
        PrintWriter pw = new PrintWriter("pw2.txt");
 
        pw.print("hello");//print方法可接受隨意類型的數據
        pw.print(100);
        pw.print(true);
 
        pw.close();
    }
 
但以上的方法並無實現本身主動刷新
要實現本身主動刷新的話,
 
啓動本身主動刷新
          PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);//要設置true
          仍是應該調用println()的方法才幹夠(調用print本身主動刷新不了)
          這個時候不僅本身主動刷新了,還實現了數據的換行。

public static void main(String[] args) throws IOException {
        // 建立打印流對象
        // PrintWriter pw = new PrintWriter("pw2.txt");
        PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
 
        // write()是搞不定的。怎麼辦呢?
        // 咱們就應該看看它的新方法
        // pw.print(true);
        // pw.print(100);
        // pw.print("hello");
 
        pw.println("hello");
        pw.println(true);
        pw.println(100);
 
        pw.close();
    }
 
注意:println()
         事實上等價于于:
         bw.write();
         bw.newLine();        
         bw.flush();
    一句頂三句
 
6.打印流改進複製文本文件案例
 
  需求:DataStreamDemo.java拷貝到Copy.java中
  數據源:
          DataStreamDemo.java -- 讀取數據 -- FileReader -- BufferedReader
  目的地:
          Copy.java -- 寫出數據 -- FileWriter -- BufferedWriter -- PrintWriter
 
public static void main(String[] args) throws IOException {
         //曾經的版本號
        // 封裝數據源
         BufferedReader br = new BufferedReader(new FileReader(
         "DataStreamDemo.java"));
          //封裝目的地
         BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));
        
         String line = null;
         while ((line = br.readLine()) != null) {
         bw.write(line);
         bw.newLine();
         bw.flush();
         }
        
         bw.close();
         br.close();
    }
===============================================
public static void main(String[] args) {
// 打印流的改進版
        // 封裝數據源
        BufferedReader br = new BufferedReader(new FileReader(
                "DataStreamDemo.java"));
        // 封裝目的地
        PrintWriter pw = new PrintWriter(new FileWriter("Copy.java"), true);
 
        String line = null;
        while((line=br.readLine())!=null){
            pw.println(line);
        }
 
        pw.close();
        br.close();
}
=============================================
7.標準輸入輸出流概述和輸出語句的本質------System.out
 
 
標準輸入輸出流
  System類中的兩個成員變量:
         public static final InputStream in 「標準」輸入流。
          public static final PrintStream out 「標準」輸出流。
  
          InputStream is = System.in;
          PrintStream ps = System.out;
public static void main(String[] args) {
        // 有這裏的解說咱們就知道了,這個輸出語句其本質是IO流操做,把數據輸出到控制檯。
        System.out.println("helloworld");
 
        // 獲取標準輸出流對象
        PrintStream ps = System.out;
        ps.println("helloworld");
 
        ps.println();
        // ps.print();//這種方法不存在
 
        // System.out.println();
        // System.out.print();
    }
 
 
8.標準輸入輸出流概述和輸出語句的本質------System.in
 
public static void main(String[] args) throws IOException {
         //獲取標準輸入流
         InputStream is = System.in;
         //我要一次獲取一行行不行呢?
         //行。

         //怎麼實現呢?
         //要想實現。首先你得知道一次讀取一行數據的方法是哪一個呢?
         //readLine()
         //而這種方法在哪一個類中呢?

         //BufferedReader
         //因此。你此次應該建立BufferedReader的對象,但是底層仍是的使用標準輸入流
         // BufferedReader br = new BufferedReader(is);
         //依照咱們的推想,現在應該可以了,但是卻報錯了
        //緣由是:字符緩衝流僅僅能針對字符流操做,而你現在是字節流。因此不能是用?
         //那麼。我還就想使用了,請你們給我一個解決方式?

        //把字節流轉換爲字符流而後在經過字符緩衝流操做
        // InputStreamReader isr = new InputStreamReader(is);
        // BufferedReader br= new BufferedReader(isr);
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //注意這裏的字節流轉換成字符流。利用了InputStreamReader轉換流
 
        System.out.println("請輸入一個字符串:");
        String line = br.readLine();
        System.out.println("你輸入的字符串是:" + line);
 
        System.out.println("請輸入一個整數:");
        // int i = Integer.parseInt(br.readLine());
        line = br.readLine();
        int i = Integer.parseInt(line);
        System.out.println("你輸入的整數是:" + i);
    }
 
 
//利用上述代碼模仿了Scanner的鍵盤錄入功能
注意下面方法
 
9.輸出語句用字符緩衝流改進
public static void main(String[] args) throws IOException {
        // 獲取標準輸入流
        // // PrintStream ps = System.out;
        // // OutputStream os = ps;//這個就是多態
        //上面兩句簡化爲一句---- OutputStream os = System.out; // 多態
        // // 我能不能依照剛纔使用標準輸入流的方式同樣把數據輸出到控制檯呢?
        // OutputStreamWriter osw = new OutputStreamWriter(os);
        // BufferedWriter bw = new BufferedWriter(osw);
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                System.out));//合併爲一句
 
        bw.write("hello");
        bw.newLine();
        // bw.flush();
        bw.write("world");
        bw.newLine();
        // bw.flush();
        bw.write("java");
        bw.newLine();
        bw.flush();
 
        bw.close();
    }
 
10.隨機訪問流概述和寫出數據
隨機訪問流:
          RandomAccessFile類不屬於流,是Object類的子類。
          但它融合了InputStream和OutputStream的功能。
          支持對文件的隨機訪問讀取和寫入。
  
  public RandomAccessFile(String name,String mode):第一個參數是文件路徑。第二個參數是操做文件的模式。
          模式有四種,咱們最常用的一種叫"rw",這樣的方式表示我既可以寫數據,也可以讀取數據 
 
這個RandomAccessFile類還可以讀和寫
 
11.隨機訪問流讀取數據和操做文件指針
===========================================
public static void main(String[] args) throws IOException {
        read();
    }
 
    private static void read() throws IOException {
        // 建立隨機訪問流對象
        RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");
 
        int i = raf.readInt();
        System.out.println(i);
        // 該文件指針可以經過 getFilePointer方法讀取。並經過 seek 方法設置。
        System.out.println("當前文件的指針位置是:" + raf.getFilePointer());//4
 
        char ch = raf.readChar();
        System.out.println(ch);
        System.out.println("當前文件的指針位置是:" + raf.getFilePointer());//6
 
        String s = raf.readUTF();
        System.out.println(s);
        System.out.println("當前文件的指針位置是:" + raf.getFilePointer());//14(原本是12的。多出來兩個字節的緣由可以看API,事實上是識別UTF編碼的兩個字節)
 
        // 我不想重頭開始了,我就要讀取a,怎麼辦呢?
        raf.seek(4);//從0開始計算
        ch = raf.readChar();
        System.out.println(ch);
    }
======================================================
輸出演示樣例
12.合併流讀取兩個文件的內容拷貝到一個文件裏
合併流就是按順序讀取兩個文件而後合併到一個新的文件裏
public static void main(String[] args) throws IOException {
        InputStream is1 = new FileInputStream("pw.txt");
        InputStream is2 = new FileInputStream("pw2.txt");
 
        // SequenceInputStream(InputStream s1, InputStream s2)
        SequenceInputStream sis = new SequenceInputStream(is1is2);
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.txt"));
        
        // 怎樣寫讀寫呢。事實上很是easy。你就依照曾經怎麼讀寫,現在仍是怎麼讀寫
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = sis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
 
        sis.close();
        bos.close();
=============================================
執行演示樣例
執行前
 
 
執行後
 
13.合併流讀取多個文件的內容拷貝到一個文件裏
public static void main(String[] args) throws IOException {
// 需求:把如下的三個文件的內容拷貝到pwsum.txt中
 
// SequenceInputStream(Enumeration e)
        // 經過簡單的回想咱們知道了Enumeration是Vector中的一個方法的返回值類型
        // Enumeration<E> elements()
        Vector<InputStream> v = new Vector<InputStream>();
        InputStream is1 = new FileInputStream("pw.txt");
        InputStream is2 = new FileInputStream("pw2.txt");
        InputStream is3 = new FileInputStream("pw3.txt");
 
        v.add(is1);
        v.add(is2);
        v.add(is3);
 
        Enumeration<InputStream> e = v.elements();
 
        SequenceInputStream sis = new SequenceInputStream(e);//接收一個參數
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("pwsum.txt"));
 
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = sis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
 
        sis.close();
        bos.close();
 
    }
 
14.序列化流和反序列化流的概述和使用
序列化流   ObjectOutputStream
反序列化流  ObjectInputStream
 
序列化流:把對象依照流同樣的方式存入文本文件或者在網絡中傳輸。對象 -- 流數據(ObjectOutputStream)
 反序列化流:把文本文件裏的流對象數據或者網絡中的流對象數據還原成對象

流數據 -- 對象(ObjectInputStream)

 
首先。寫序列化流Demo的write方法。
public static void main(String[] args) throws IOException,
            ClassNotFoundException {
        // 由於咱們要對對象進行序列化,因此咱們先本身定義一個類
        // 序列化數據事實上就是把對象寫到文本文件
        write();
 
    
    }
 
    private static void write() throws IOException {
        // 建立序列化流對象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
                "oos.txt"));
 
        // 建立對象
        Person p = new Person("林青霞", 27);
 
        // public final void writeObject(Object obj)
        oos.writeObject(p);
 
        // 釋放資源
        oos.close();
    }
======================================================
而後,建立一個Person類
import java.io.Serializable;
 
public class Person implements Serializable {
    private String name;
    private int age;
 
    public Person() {
        super();
    }
 
    public Person(String name, int age) {
        super();
        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;
    }
 
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
 
}
============================
要說明的是。在Person類還沒實現 Serializable 這個接口的時候。執行會有異常
NotSerializableException:未序列化異常
查API
 
所以,要想沒報錯,必須在Person類中實現 Serializable 這個接口
但是:你會發現。沒有重寫不論什麼方法啊!!。
沒錯,因爲,原本就不用重寫不論什麼的方法!
解析例如如下:
類經過實現 java.io.Serializable 接口以啓用其序列化功能。

未實現此接口的類將沒法使其不論什麼狀態序列化或反序列化。

  該接口居然沒有不論什麼方法,相似於這樣的沒有方法的接口被稱爲標記接口

而後。再次執行沒報錯後,直接打開文件是看不懂的
 
序列化的流必須要反序列的流"解鎖"才幹夠啊
最後。增長反序列流的代碼例如如下
 
執行(不報錯了)toString方法在Person類中已經重寫
注意這一步 
// 還原對象
        Object obj = ois.readObject();
obj儘管是Object類型,但是事實上是Person對象
 
但是。另外一個小問題
怎樣解決序列化時候的黃色警告線問題
 
當Person類進行了修改的時候(比方private int age 改成 int age)把代碼保存再次執行(僅僅執行讀操做而不進行第二次寫入)會報錯產生異常
 
產生的緣由:
 
  爲何會有問題呢?
          Person類實現了序列化接口。那麼它自己也應該有一個標記值。
          這個標記值若是是100。
          開始的時候:
          Person.class -- id=100
          wirte數據: oos.txt -- id=100
          read數據: oos.txt -- id=100    
  
          現在:
          Person.class -- id=200
          wirte數據: oos.txt -- id=100
          read數據: oos.txt -- id=100
解決問題的辦法:
咱們在實際開發中。可能還需要使用曾經寫過的數據,不能又一次寫入。

怎麼辦呢?

  回憶一下緣由是因爲它們的id值不匹配。

  每次改動java文件的內容的時候,class文件的id值都會發生改變
  而讀取文件的時候。會和class文件裏的id值進行匹配。因此。就會出問題
  但是呢,假設我有辦法。讓這個id值在java文件裏是一個固定的值,這樣,你改動文件的時候。這個id值還會發生改變嗎?

  不會。

現在的關鍵是我怎樣能夠知道這個id值怎樣表示的呢?

  不用操心。你不用記住,也不要緊,點擊鼠標就能夠。

  你難道沒有看到黃色警告線嗎?

 
在Person類中加一下語句
private static final long serialVersionUID = -2071565876962058344L;
 
另外一個要注意的問題:
注意:
          我一個類中可能有很是多的成員變量,有些我不想進行序列化。請問該怎麼辦呢?

          使用transientkeyword聲明不需要序列化的成員變量
也就是用transientkeyword
 
把private int age改動爲
private transient int age;
而後執行會發現age的值爲0了,也就是說,age的值將不被記住是27了
 
15.Properties的概述和做爲Map集合的使用(注意:Properties並無泛型)
 Properties:屬性集合類。是一個可以和IO流相結合使用的集合類。

  Properties 可保存在流中或從流中載入。

屬性列表中每個鍵及其相應值都是一個字符串。

 

  
  是Hashtable的子類。說明是一個Map集合。
 
public static void main(String[] args) {
        // 做爲Map集合的使用
        // 如下這樣的使用方法是錯誤的,必定要看API,假設沒有<>,就說明該類不是一個泛型類,在使用的時候就不能加泛型
        // Properties<String, String> prop = new Properties<String, String>();
 
        Properties prop = new Properties();
 
        // 加入元素
        prop.put("it002", "hello");
        prop.put("it001", "world");
        prop.put("it003", "java");
 
        // System.out.println("prop:" + prop);
 
        // 遍歷集合,注意紅字是Object而不是String,因爲上面的put方法原本就是接受Object參數
        Set<Object> set = prop.keySet();
        for (Object key : set) {
            Object value = prop.get(key);
            System.out.println(key + "---" + value);
        }
    }
 
16.Properties的特殊功能使用
特殊功能:
  public Object setProperty(String key,String value):加入元素
  public String getProperty(String key):獲取元素
  public Set<StringstringPropertyNames():獲取所有的鍵的集合
=========================
public static void main(String[] args) {
        // 建立集合對象
        Properties prop = new Properties();
 
        // 加入元素
        prop.setProperty("張三", "30");
        prop.setProperty("李四", "40");
        prop.setProperty("王五", "50");
 
        // public Set<String> stringPropertyNames():獲取所有的鍵的集合
        Set<String> set = prop.stringPropertyNames();
        for (String key : set) {
            String value = prop.getProperty(key);
            System.out.println(key + "---" + value);
        }
    }
================================================
17.Properties的load()和store()功能
這裏的集合必須是Properties集合
  public void load(Reader reader):把文件中的數據讀取到集合中
  public void store(Writer writer,String comments):把集合中的數據存儲到文件
=================================================
    public static void main(String[] args) throws IOException {
         myLoad();
 
        myStore();
    }
 
    private static void myStore() throws IOException {
        // 建立集合對象
        Properties prop = new Properties();
 
        prop.setProperty("林青霞", "27");
        prop.setProperty("武鑫", "30");
        prop.setProperty("劉曉曲", "18");
 
        //public void store(Writer writer,String comments):把集合中的數據存儲到文件
        Writer w = new FileWriter("name.txt");
        prop.store(w, "helloworld");//這個helloworld僅僅是凝視的做用。。。
        w.close();
    }
 
    private static void myLoad() throws IOException {
        Properties prop = new Properties();
 
        // public void load(Reader reader):把文件裏的數據讀取到集合中
        // 注意:這個文件的數據必須是鍵值對形式
        Reader r = new FileReader("prop.txt");
        prop.load(r);
        r.close();
 
        System.out.println("prop:" + prop);
    }
 
執行打開name.txt文件
 
18.推斷文件裏是否有指定的鍵假設有就改動值的案例
 
  我有一個文本文件(user.txt),我知道數據是鍵值對形式的,但是不知道內容是什麼。

  請寫一個程序推斷是否有「lisi」這種鍵存在,假設有就改變事實上爲」100」
 
分析:
          A:把文件裏的數據載入到集合中
          B:遍歷集合,獲取獲得每一個鍵
          C:推斷鍵是否有爲"lisi"的。假設有就改動其值爲"100"
          D:把集合中的數據又一次存儲到文件裏
========================================
public static void main(String[] args) throws IOException {
        // 把文件裏的數據載入到集合中
        Properties prop = new Properties();
        Reader r = new FileReader("user.txt");
        prop.load(r);
        r.close();
 
        // 遍歷集合,獲取獲得每一個鍵
        Set<String> set = prop.stringPropertyNames();
        for (String key : set) {
            // 推斷鍵是否有爲"lisi"的,假設有就改動其值爲"100"
            if ("lisi".equals(key)) {
                prop.setProperty(key, "100");
                break;
            }
        }
 
        // 把集合中的數據又一次存儲到文件裏
        Writer w = new FileWriter("user.txt");
        prop.store(w, null);//這裏設爲null,不用附加東西了
        w.close();
    }
=============================================
user.txt
執行後
 
19.怎樣讓猜數字小遊戲僅僅能玩5次案例
 我有一個猜數字小遊戲的程序,請寫一個程序實現在測試類中僅僅能用5次,超過5次提示:遊戲試玩已結束。請付費。
初始化:手動建一個文件保存玩遊戲的次數(不需要寫代碼建立文件)
public static void main(String[] args) throws IOException {
        // 讀取某個地方的數據。假設次數不大於5,可以繼續玩。不然就提示"遊戲試玩已結束。請付費。

        // 把數據載入到集合中
        Properties prop = new Properties();
        Reader r = new FileReader("count.txt");
        prop.load(r);
        r.close();//別忘記關閉流
 
        // 我本身的程序,我固然知道里面的鍵是誰
        String value = prop.getProperty("count");
        int number = Integer.parseInt(value);//String轉換爲int
 
        if (number > 5) {
            System.out.println("遊戲試玩已結束,請付費。");
            System.exit(0);//別漏了這一步退出!!

        } else {
            number++;
            prop.setProperty("count", String.valueOf(number));//int轉爲String
            Writer w = new FileWriter("count.txt");
            prop.store(w, null);
            w.close();//別忘記關閉流
 
            GuessNumber.start();//這個猜數字的小遊戲代碼省略
        }
    }
 
注意幾個問題:別忘記關閉流。注意字符串與Integer的轉換.別漏了 System.exit(0)
==================================================
20.NIO的介紹和JDK7下NIO的一個案例
NIO:就是New IO
nio包在JDK4出現,提供了IO流的操做效率。但是眼下還不是大範圍的使用。
  有空的話瞭解下,有問題再問我。

  
  JDK7的以後的nio:
  Path:路徑
  Paths:有一個靜態方法返回一個路徑
          public static Path get(URI uri)//返回值爲Path
  Files:提供了靜態方法供咱們使用
          public static long copy(Path source,OutputStream out):拷貝文件
          public static Path write(Path path,Iterable<? extends CharSequence> lines,Charset cs,OpenOption... options)//後面那個可變參數就忽略不理,僅僅接受3個參數
===================================================
下面爲課後閱讀資料

1:JDK4新IO要了解的類(本身看)

Buffer(緩衝),Channer(通道)

2:JDK7要了解的新IO類

Path:與平臺無關的路徑。

Paths:包括了返回Path的靜態方法。

  public static Path get(URI uri):依據給定的URI來肯定文件路徑。

Files:操做文件的工具類。提供了大量的方法,簡單瞭解例如如下方法

  public static long copy(Path source, OutputStream out) :拷貝文件

  public static Path write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options):

  把集合的數據寫到文件。

//拷貝文件

Files.copy(Paths.get("Demo.java"), newFileOutputStream("Copy.Java"));

//把集合中的數據寫到文件

List<String> list = new ArrayList<String>();

list.add("hello");

list.add("world");

list.add("java");

Files.write(Paths.get("list.txt"), list, Charset.forName("gbk"));

 

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

注意:ArrayList實現了Iterable接口

所以Iterable<?

 extends CharSequence> lines相應的參數可以是ArrayList的對象

先測試copy功能

 

而後測試write功能
public static void main(String[] args) throws IOException {
   
        ArrayList<String> array = new ArrayList<String>();
        array.add("hello");
        array.add("world");
        array.add("java");
        Files.write(Paths.get("array.txt"), array, Charset.forName("GBK"));
    }
 
====================================================
下面是本身寫的代碼
Charset.forName("GBK")是設置編碼的
===========
執行後
 
執行後
 
===================================================
day22 筆記補充
登陸註冊IO版本號案例(掌握)
    要求,對着寫一遍。
 
    寫的順序參考:
    cn.itcast.pojo User
    cn.itcast.dao UserDao
    cn.itcast.dao.impl UserDaoImpl(實現可以是集合版或者IO版)
    cn.itcast.game GuessNumber
    cn.itcast.test    UserTest
===============================================
內存操做流(理解)
    (1)有些時候咱們操做完成後,未必需要產生一個文件。就可以使用內存操做流。

    (2)三種
        A:ByteArrayInputStream,ByteArrayOutputStream
        B:CharArrayReader,CharArrayWriter
        C:StringReader,StringWriter
===============================================
打印流(掌握)
    (1)字節打印流。字符打印流
    (2)特色:
        A:僅僅操做目的地,不操做數據源
        B:可以操做隨意類型的數據
        C:假設啓用了本身主動刷新。在調用println()方法的時候,能夠換行並刷新
        D:可以直接操做文件
            問題:哪些流可以直接操做文件呢?

            看API,假設其構造方法能夠同一時候接收File和String類型的參數,通常都是能夠直接操做文件的
    (3)複製文本文件
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        PrintWriter pw = new PrintWriter(new FileWriter("b.txt"),true);
 
        String line = null;
        while((line=br.readLine())!=null) {
            pw.println(line);
        }
 
        pw.close();
        br.close();
===============================================
 
5:標準輸入輸出流(理解)
    (1)System類如下有這種兩個字段
        in 標準輸入流
        out 標準輸出流
    (2)三種鍵盤錄入方式
        A:main方法的args接收參數
        B:System.in經過BufferedReader進行包裝
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        C:Scanner
            Scanner sc = new Scanner(System.in);
    (3)輸出語句的原理和怎樣使用字符流輸出數據
        A:原理
            System.out.println("helloworld");
 
            PrintStream ps = System.out;//PrintStream 屬於字節流
            ps.println("helloworld");
        B:把System.out用字符緩衝流包裝一下使用
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
============================================================
序列化流(理解)
    (1)可以把對象寫入文本文件或者在網絡中傳輸
    (2)怎樣實現序列化呢?

        讓被序列化的對象所屬類實現序列化接口。
        該接口是一個標記接口。沒有功能需要實現。
    (3)注意問題:
        把數據寫到文件後,在去改動類會產生一個問題。

        怎樣解決該問題呢?
            在類文件裏,給出一個固定的序列化id值。
            而且,這樣也可以解決黃色警告線問題
    (4)面試題:
        何時序列化?

        怎樣實現序列化?
        什麼是反序列化?
========================================================
NIO(瞭解)
    (1)JDK4出現的NIO,對曾經的IO操做進行了優化,提供了效率。但是大部分咱們看到的仍是曾經的IO
    (2)JDK7的NIO的使用    
        Path:路徑
        Paths:經過靜態方法返回一個路徑
        Files:提供了常見的功能
            複製文本文件
            把集合中的數據寫到文本文件
=============================================================

day23
1.多線程程序的引入
紅色表明一個運行的流程,線程
 
2.進程概述及多進程的意義
 
 
A:要想了解多線程,必須先了解線程。而要想了解線程。必須先了解進程,因爲線程是依賴於進程而存在。

 
B:什麼是進程?
    經過任務管理器咱們就看到了進程的存在。
    而經過觀察,咱們發現僅僅有執行的程序纔會出現進程。
    進程:就是正在執行的程序。
    進程是系統進行資源分配和調用的獨立單位。每一個進程都有它本身的內存空間和系統資源。
 
C:多進程有什麼意義呢?
    單進程的計算機僅僅能作一件事情,而咱們現在的計算機都可以作多件事情。
    舉例:一邊玩遊戲(遊戲進程),一邊聽音樂(音樂進程)。

    也就是說現在的計算機都是支持多進程的,可以在一個時間段內運行多個任務。
    並且呢。可以提升CPU的使用率。
 
    問題:
        一邊玩遊戲,一邊聽音樂是同一時候進行的嗎?

        不是

因爲單CPU在某一個時間點上僅僅能作一件事情。

        而咱們在玩遊戲。或者聽音樂的時候,是CPU在作着程序間的高效切換讓咱們認爲是同一時候進行的
================================
單進程:好比windows下cmd的dos命令
多進程:好比咱們的計算機
 
================================
 
3.線程概述及多線程的意義
A:什麼是線程呢?
    在同一個進程內又可以運行多個任務,而這每一個任務我就可以看出是一個線程。
    線程:是程序的運行單元,運行路徑。

是程序使用CPU的最基本單位。

    單線程:假設程序僅僅有一條運行路徑。
    多線程:假設程序有多條運行路徑。
 
B:多線程有什麼意義呢?
    多線程的存在,不是提升程序的運行速度。

事實上是爲了提升應用程序的使用率。

    程序的運行事實上都是在搶CPU的資源,CPU的運行權。

    多個進程是在搶這個資源,而當中的某一個進程假設運行路徑比較多就會有更高的概率搶到CPU的運行權
    咱們是不敢保證哪個線程能夠在哪一個時刻搶到。因此線程的運行有隨機性。
 
4.多線程舉例及並行和併發的差異
進程:
         正在執行的程序。是系統進行資源分配和調用的獨立單位。
         每一個進程都有它本身的內存空間和系統資源。
     線程:
         是進程中的單個順序控制流。是一條運行路徑
         一個進程假設僅僅有一條運行路徑,則稱爲單線程程序。
         一個進程假設有多條運行路徑,則稱爲多線程程序。

 
   舉例:
       掃雷程序。迅雷下載
   
   你們注意兩個詞彙的差異:並行併發
         前者是邏輯上同一時候發生。指在某一個時間內同一時候執行多個程序。
         後者是物理上同一時候發生。指在某一個時間點同一時候執行多個程序。
 
5.Java程序執行原理和JVM的啓動是不是多線程的
Java程序的執行原理:
          由java命令啓動JVM,JVM啓動就至關於啓動了一個進程。

          接着有該進程建立了一個主線程去調用main方法。
  
 思考題:
          jvm虛擬機的啓動是單線程的仍是多線程的?

              多線程的。
              緣由是垃圾回收線程也要先啓動。不然很是easy會出現內存溢出。

              現在的垃圾回收線程加上前面的主線程。最低啓動了兩個線程,因此。jvm的啓動事實上是多線程的
 

JVM啓動至少啓動了垃圾回收線程和主線程。因此是多線程的。

6.多線程方式1的代碼實現
需求:咱們要實現多線程的程序。

  怎樣實現呢?
          由於線程是依賴進程而存在的,因此咱們應該先建立一個進程出來。
          而進程是由系統建立的,因此咱們應該去調用系統功能建立一個進程。

          Java是不能直接調用系統功能的,因此,咱們沒有辦法直接實現多線程程序。
          但是呢?Java可以去調用C/C++寫好的程序來實現多線程程序。

          由C/C++去調用系統功能建立進程,而後由Java去調用這種東西,
           而後提供一些類供咱們使用。

咱們就可以實現多線程程序了。

  那麼Java提供的類是什麼呢?

          Thread
          經過查看API。咱們知道了有2中方式實現多線程程序。
  
  方式1:繼承Thread類
  步驟
          A:本身定義類MyThread繼承Thread類。
          B:MyThread類裏面重寫run()?

              爲何是run()方法呢?
          C:建立對象
          D:啓動線程
==============================================================
下面爲注意事項
直接調用run()方法並無啓動一個新的線程。僅僅是運行了這種方法的內容
         my.run();
         my.run();
         調用run()方法爲何是單線程的呢?
         因爲run()方法直接調用事實上就至關於普通的方法調用,因此你看到的是單線程的效果
         要想看到多線程的效果,就必須說說還有一個方法:start()
 
 
要想看到多線程的效果,必須調用start()方法
 
下面爲錯誤演示樣例
 
IllegalThreadStateException:非法的線程狀態異常
      爲何呢?因爲這個至關因而my線程被調用了兩次。而不是兩個線程啓動。
 
面試題:run()和start()的差異?
         run():不過封裝被線程運行的代碼。直接調用是普通方法
         start():首先啓動了線程。而後再由jvm去調用該線程的run()方法。
 
終於版代碼
MyThread類(繼承父類Thread)
/*
 * 該類要重寫run()方法,爲何呢?
 * 不是類中的所有代碼都需要被線程運行的。
 * 而這個時候,爲了區分哪些代碼能夠被線程運行。java提供了Thread類中的run()用來包括那些被線程運行的代碼
 */
public class MyThread extends Thread {
 
    @Override
    public void run() {
        // 本身寫代碼
        // System.out.println("好好學習。每天向上");//通常不會那麼無聊爲了輸出一句話而又一次開一個線程(浪費)
        // 通常來講。被線程運行的代碼確定是比較耗時的。因此咱們用循環改進
        for (int x = 0; x < 200; x++) {//不必定是200.可能需要更大才幹看到效果
            System.out.println(x);
        }
    }
 
}
============================================
測試類
public class MyThreadDemo {
    public static void main(String[] args) {
        // 建立兩個線程對象
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();
 
        my1.start();
        my2.start();
    }
===================================================
執行演示樣例(x<900)
可以看到兩個0~899的輸出過程互相搶奪資源,並不是輸出0~899了再輸出0~899的
 
7.獲取和設置線程對象名稱
怎樣獲取線程對象的名稱呢?
  public final String getName():獲取線程的名稱。
  怎樣設置線程對象的名稱呢?
  public final void setName(String name):設置線程的名稱
 
設置線程的名稱(假設不設置名稱的話。默認是Thread-?

 編號)

在測試類中:
方法一:無參構造+setXxx()
         // 建立線程對象
         MyThread my1 = new MyThread();
         MyThread my2 = new MyThread();
         //調用方法設置名稱
         my1.setName("林青霞");
         my2.setName("劉意");
         my1.start();
         my2.start();
前提是MyThread類要改一下run()方法(getName()就是用來獲取線程名字的)
public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
方法二(略微麻煩,要手動寫MyThread的帶參構造方法,方法一不用):
public class MyThread extends Thread {
 
    public MyThread() {
    }
 
    public MyThread(String name){
        super(name);//直接調用父類的就好
    }
 
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);//紅字至關於this.getName()
        }
    }
}
====================================
而後在測試類中:
 
        //帶參構造方法給線程起名字
         MyThread my1 = new MyThread("林青霞");
         MyThread my2 = new MyThread("劉意");
         my1.start();
         my2.start();
=====================================
另外一個問題:我要獲取main方法所在的線程對象的名稱。該怎麼辦呢?

這時候注意:main方法所在的測試類並不繼承Thread類。所以並不能直接使用getName()方法來獲取名稱。

        //遇到這樣的狀況,Thread類提供了一個很是好玩的方法:
        //public static Thread currentThread():返回當前正在運行的線程對象,返回值是Thread,而Thread恰巧可以調用getName()方法
        System.out.println(Thread.currentThread().getName());
執行輸出main
8.線程調度及獲取和設置線程優先級
線程調度
 
 
==========================================================
咱們的線程沒有設置優先級,確定有默認優先級。

  那麼,默認優先級是多少呢?
  怎樣獲取線程對象的優先級?
          public final int getPriority():返回線程對象的優先級
  怎樣設置線程對象的優先級呢?

          public final void setPriority(int newPriority):更改線程的優先級。

 

  
  注意:
          線程默認優先級是5。
          線程優先級的範圍是:1-10

          線程優先級高只表示線程獲取的 CPU時間片的概率高,但是要在次數比較多。或者屢次執行的時候才幹看到比較好的效果。
          
  IllegalArgumentException:非法參數異常。
  拋出的異常代表向方法傳遞了一個不合法或不對的參數。 
==========================================================
線程優先級:最大爲10,最小爲1,默以爲5
 
public static void main(String[] args) {
        ThreadPriority tp1 = new ThreadPriority();
        ThreadPriority tp2 = new ThreadPriority();
        ThreadPriority tp3 = new ThreadPriority();
 
        tp1.setName("東方不敗");
        tp2.setName("嶽不羣");
        tp3.setName("林平之");
 
        //設置正確的線程優先級
        tp1.setPriority(10);
        tp2.setPriority(1);
 
        tp1.start();
        tp2.start();
        tp3.start();
    }
 
9.線程控制之休眠線程
 
  線程休眠(靜態方法)
         public static void sleep(long millis)
======================================
代碼
主要在ThreadSleep中修改
public class ThreadSleep extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x + ",日期:" + new Date());
            // 睡眠
            // 困了。我略微歇息1秒鐘
            try {//僅僅能用try catch。不能throws。因爲父類沒拋這個異常,子類也不能拋
                Thread.sleep(1000);//事實上在這裏也可以直接寫sleep(1000);而省略Thread
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
==============================
再寫一個類測試一下
public static void main(String[] args) {
        ThreadSleep ts1 = new ThreadSleep();
        ThreadSleep ts2 = new ThreadSleep();
        ThreadSleep ts3 = new ThreadSleep();
 
        ts1.setName("林青霞");
        ts2.setName("林志玲");
        ts3.setName("林志穎");
 
        ts1.start();
        ts2.start();
        ts3.start();
    }
 
 
10.線程控制之增長線程
join()方法(非靜態方法)
public final void join():等待該線程終止。
================================= 
代碼
ThreadJoin類
public class ThreadJoin extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}
===============================
下面爲測試類
public class ThreadJoinDemo {
    public static void main(String[] args) {
        ThreadJoin tj1 = new ThreadJoin();
        ThreadJoin tj2 = new ThreadJoin();
        ThreadJoin tj3 = new ThreadJoin();
 
        tj1.setName("李淵");
        tj2.setName("李世民");
        tj3.setName("李元霸");
 
        tj1.start();
        try {
            tj1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        tj2.start();
        tj3.start();
    }
}
==========================================
下面是本人寫的測試類以及執行狀況
 
11.線程控制之禮讓線程
yield()----靜態方法
public static void yield():暫停當前正在運行的線程對象,並運行其它線程。 
  讓多個線程的運行更和諧,但是不能靠它保證一人一次。(理論上是一人運行一次。輪流來。也就是一個線程運行一次後。等待下一個線程運行,而後第一個線程再運行第二次,……實際上可能有"偏差")
代碼
ThreadYield類
public class ThreadYield extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
            Thread.yield();
        }
    }
}
=====================================
測試類
 
public class ThreadYieldDemo {
    public static void main(String[] args) {
        ThreadYield ty1 = new ThreadYield();
        ThreadYield ty2 = new ThreadYield();
 
        ty1.setName("林青霞");
        ty2.setName("劉意");
 
        ty1.start();
        ty2.start();
    }
}
=========================================
 
可以看見上面 劉意進程與林青霞進程相互交替進行,但不保證必定這樣。
 
12.線程控制之守護線程
 setDaemon---非靜態
public final void setDaemon(boolean on):將該線程標記爲守護線程或用戶線程。
  當正在執行的線程都是守護線程時。Java 虛擬機退出。 該方法必須在啓動線程前調用。
ThreadDaemon類
public class ThreadDaemon extends Thread {
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            System.out.println(getName() + ":" + x);
        }
    }
}
==================================
測試類
public class ThreadDaemonDemo {
    public static void main(String[] args) {
        ThreadDaemon td1 = new ThreadDaemon();
        ThreadDaemon td2 = new ThreadDaemon();
 
        td1.setName("關羽");
        td2.setName("張飛");
 
        // 設置守護線程(注意:必須在start()方法以前設置。不然會有異常!。)
        td1.setDaemon(true);//true表明線程設置爲"守護"
        td2.setDaemon(true);//設置後,當主線程運行結束(主線程名字爲劉備),td1,td2線程也結束了(守護任務已完畢)
 
        td1.start();
        td2.start();
 
        Thread.currentThread().setName("劉備");//改一改main線程的名字
        for (int x = 0; x < 5; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }
}

 

================================================
13.線程控制之中斷線程
public final void stop():讓線程中止。過期了,但是還可以使用。(不建議使用,太暴力)
  public void interrupt():中斷線程。 把線程的狀態終止,並拋出一個InterruptedException。

 
ThreadStop 類
public class ThreadStop extends Thread {
    @Override
    public void run() {
        System.out.println("開始運行:" + new Date());
 
        // 我要歇息10秒鐘。親。不要打攪我哦
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // e.printStackTrace();
            System.out.println("線程被終止了");
        }
 
        System.out.println("結束運行:" + new Date());
    }
}
============================================
測試類
public class ThreadStopDemo {
    public static void main(String[] args) {
        ThreadStop ts = new ThreadStop();
        ts.start();
 
        // 你超過三秒不醒過來,我就乾死你
        try {
            Thread.sleep(3000);
            // ts.stop();
            ts.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
 
================================================
幾個問題:

 

 

14.線程生命週期圖解

15.多線程方式2的思路及代碼實現
方式2:實現Runnable接口
  步驟:
          A:本身定義類MyRunnable實現Runnable接口
          B:重寫run()方法
          C:建立MyRunnable類的對象
          D:建立Thread類的對象。並把C步驟的對象做爲構造參數傳遞
 
MyRunnable 類
public class MyRunnable implements Runnable {
 
    @Override
    public void run() {
        for (int x = 0; x < 100; x++) {
            // 由於實現接口的方式就不能直接使用Thread類的方法了,但是可以間接的使用
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
    }
 
}
 
測試類
    public class MyRunnableDemo {
    public static void main(String[] args) {
        // 建立MyRunnable類的對象
        MyRunnable my = new MyRunnable();//多個一樣程序的代碼去處理同一個資源
    //注意:MyRunnable對象僅僅需要建立一個就能夠,多個Thread對象可以接收同一個MyRunnable對象
 
        // 建立Thread類的對象。並把C步驟的對象做爲構造參數傳遞
        // Thread(Runnable target)
        // Thread t1 = new Thread(my);
        // Thread t2 = new Thread(my);
        // t1.setName("林青霞");
        // t2.setName("劉意");
 
        // Thread(Runnable target, String name)
        Thread t1 = new Thread(my, "林青霞");//同一個對象my,而不需要my1。my2。……
        Thread t2 = new Thread(my, "劉意");//同一個對象my    而不需要my1,my2,……
 
        t1.start();
        t2.start();
    }
}
//注意紅字
=============================================================
16.多線程兩種方式的圖解比較及差異
怎樣理解------可以避免由於Java單繼承帶來的侷限性
比方說,某個類已經有父類了。而這個類想實現多線程,但是這個時候它已經不能直接繼承Thread類了(接口可以多實現implements。但是繼承extends僅僅能單繼承)。它的父類也不想繼承Thread因爲不需要實現多線程。

 
17.兩種方式實現賣電影票案例
方式一:繼承Thread類的方式賣電影票案例
 
某電影院眼下正在上映賀歲大片(紅高粱,少林寺傳奇藏經閣),共同擁有100張票。而它有3個售票窗體售票。請設計一個程序模擬該電影院售票。

 繼承Thread類來實現

=====================================
SellTicket類(注意tickets變量是static的!)
public class SellTicket extends Thread {
 
    // 定義100張票
    // private int tickets = 100;
    // 爲了讓多個線程對象共享這100張票。咱們事實上應該用靜態修飾
    private static int tickets = 100;
 
    @Override
    public void run() {
        // 定義100張票
        // 每個線程進來都會走這裏。這種話,每個線程對象至關於買的是本身的那100張票,這不合理,因此應該定義到外面
        // int tickets = 100;
 
        // 是爲了模擬一直有票
        while (true) {
            if (tickets > 0) {
                System.out.println(getName() + "正在出售第" + (tickets--) + "張票");
            }
        }
    }
}
===========================================================
測試類SellTicketDemo 
public class SellTicketDemo {
    public static void main(String[] args) {
        // 建立三個線程對象
        SellTicket st1 = new SellTicket();
        SellTicket st2 = new SellTicket();
        SellTicket st3 = new SellTicket();
 
        // 給線程對象起名字
        st1.setName("窗體1");
        st2.setName("窗體2");
        st3.setName("窗體3");
 
        // 啓動線程
        st1.start();
        st2.start();
        st3.start();
    }
}
============================================================
說明的是,經過繼承Thread類來實現題中的需求並不是很是好(tickets要用static修飾,並不太好),其有用Runnable接口更好地進行數據分離
 
 
下面爲方式2---實現Runnable接口的方式賣電影票案例
SellTicket 類
public class SellTicket implements Runnable {
    // 定義100張票
    private int tickets = 100;
 
    @Override
    public void run() {
        while (true) {
            if (tickets > 0) {
                System.out.println(Thread.currentThread().getName() + "正在出售第"
                        + (tickets--) + "張票");
            }
        }
    }
}
 
=================================================
測試類
/*
 * 實現Runnable接口的方式實現
 */
public class SellTicketDemo {
    public static void main(String[] args) {
        // 建立資源對象
        SellTicket st = new SellTicket();
 
        // 建立三個線程對象
        Thread t1 = new Thread(st, "窗體1");
        Thread t2 = new Thread(st, "窗體2");
        Thread t3 = new Thread(st, "窗體3");
 
        // 啓動線程
        t1.start();
        t2.start();
        t3.start();
    }
}
=====================================================
18.買電影票出現了同票和負數票的緣由分析
首先在SellTicket類中加入sleep方法。延遲一下線程,拖慢一下運行的速度
try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
 
減小速度以後出現了問題
 
 
經過增長延遲後,就產生了連個問題:
  A:一樣的票賣了屢次
          CPU的一次操做必須是原子性的(在讀取tickets--的原來的數值和減1以後的中間擠進了兩個線程而出現反覆)
  B:出現了負數票
          隨機性和延遲致使的(三個線程同一時候擠進一個循環裏。tickets--的減法操做有可能在同一個循環中被運行了屢次而出現越界的狀況,比方說tickets要大於0卻越界到了-1)
 
也就是說,線程1運行的同一時候線程2也可能在運行。而不是線程1運行的時候線程2不能運行
 
19.線程安全問題的產生緣由分析
怎樣解決線程安全問題呢?
  
  要想解決這個問題。就要知道哪些緣由會致使出問題:(而且這些緣由也是之後咱們推斷一個程序是否會有線程安全問題的標準)
  A:是不是多線程環境
  B:是否有共享數據
  C:是否有多條語句操做共享數據
  
  咱們來回憶一下咱們的程序有沒有上面的問題呢?

  A:是不是多線程環境    是
  B:是否有共享數據    是
  C:是否有多條語句操做共享數據    是
  
  因而可知咱們的程序出現故障是正常的,因爲它知足出問題的條件。

 
20.同步代碼塊的方式解決線程安全問題
接下來纔是咱們要想一想怎樣解決這個問題呢?
  A和B的問題咱們改變不了。咱們僅僅能想辦法去把C改變一下。

  思想:
          把多條語句操做共享數據的代碼給包成一個整體,讓某個線程在運行的時候。別人不能來運行。
  問題是咱們不知道怎麼包啊?事實上我也不知道。但是Java給咱們提供了:同步機制。
  
  同步代碼塊:
          synchronized(對象){
              需要同步的代碼;
          }
  
          A:對象是什麼呢?

              咱們可以隨便建立一個對象試試。
          B:需要同步的代碼是哪些呢?

              把多條語句操做共享數據的代碼的部分給包起來
 
         注意:
              同步可以解決安全問題的根本緣由就在那個對象上。該對象如同鎖的功能。
              多個線程必須是同一把鎖
在SellTicket類中改寫例如如下
public class SellTicket implements Runnable {
    // 定義100張票
    private int tickets = 100;
    //建立鎖對象
    private Object obj = new Object();//把這個關鍵的鎖對象定義到run方法(獨立於線程以外)以外。形成同一把鎖
 
//    @Override
//    public void run() {
//        while (true) {
//            synchronized(new Object()){//這裏用new Object是不正確的,因爲這會致使不是同一把鎖
//                if (tickets > 0) {
//                    try {
//                        Thread.sleep(100); 
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    System.out.println(Thread.currentThread().getName() + "正在出售第"
//                            + (tickets--) + "張票");
//                }
//            }
//        }
//    }
 
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {//obj至關於一把鎖。把synchroned包括的代碼鎖住
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "張票");
                }
            }
        }
    }
}
 
=====================================================
再執行就沒有問題了
 
 
21.同步代碼塊解決線程安全問題的解釋
 
public class SellTicket implements Runnable {
 
    // 定義100張票
    private int tickets = 100;
 
    // 定義同一把鎖
    private Object obj = new Object();
 
    @Override
    public void run() {
        while (true) {
            // t1,t2,t3都能走到這裏
            // 若是t1搶到CPU的運行權,t1就要進來
            // 若是t2搶到CPU的運行權,t2就要進來,發現門是關着的,進不去。因此就等着。

            // 門(開,關)
            synchronized (obj) { // 發現這裏的代碼未來是會被鎖上的,因此t1進來後。就鎖了。

(關)

                if (tickets > 0) {
                    try {
                        Thread.sleep(100); // t1就睡眠了
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "張票 ");
                    //窗體1正在出售第100張票
                }
            } //t1就出來可。而後就開門

(開)(門開後,三個線程又開始搶佔)

        }
    }
}
 
 
22.同步的特色及優勢和弊端
 舉例:
          火車上廁所。
  
  同步的特色:
          前提:
              多個線程
         解決這個問題的時候要注意:
             多個線程使用的是同一個鎖對象
  同步的優勢 
         同步的出現攻克了多線程的安全問題
  同步的弊端
         當線程至關多時,因爲每個線程都會去推斷同步上的鎖。這是很是耗費資源的,無形中會減小程序的執行效率。

 
23.同步代碼快的鎖及同步方法應用和鎖的問題
概述:
  A:同步代碼塊的鎖對象是誰呢?
          隨意對象。
  
  B:同步方法的格式及鎖對象問題?
          把同步keyword加在方法上。
  
          同步方法的鎖對象是誰呢?
              this
  
  C:靜態方法及鎖對象問題?
          靜態方法的鎖對象是誰呢?

              類的字節碼文件對象。

(反射會講)

========================================================
分部解析(測試類都同樣,如下是SellTicket類的代碼變化)
同步代碼塊的鎖對象是隨意對象

 
public class SellTicket implements Runnable {
 
    // 定義100張票
    private int tickets = 100;
 
    // 定義同一把鎖
    private Object obj = new Object();
    private Demo d = new Demo();//隨意對象
 
 
    //同步代碼塊用obj作鎖
    @Override
    public void run() {
        while (true) {
           synchronized (obj) {
                if (tickets > 0) {
                    try {
                       Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "張票 ");
                }
            }
        }
    }
 
    //同步代碼塊用隨意對象作鎖
    @Override
    public void run() {
        while (true) {
            synchronized (d) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                   } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                           + "正在出售第" + (tickets--) + "張票 ");
                }
            }
        }
    }
 
class Demo {
}
=============================================================
同步方法的格式及鎖對象問題?
          把同步keyword加在方法上。( sellTicket()方法)
同步方法的鎖對象是---------------------- this
// 定義100張票
    private int tickets = 100;
 
    // 定義同一把鎖
    private Object obj = new Object();
    private int x = 0;
 
@Override
    public void run() {
        while (true) {
            if(x%2==0){
                synchronized (this) {//注意,這裏必須用this對象而不能用其餘鎖對象,不然同步功能會失敗
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()
                                + "正在出售第" + (tickets--) + "張票 ");
                    }
                }
            }else {
                sellTicket();//用方法取代
 
            }
            x++;
        }
    }
 
    private void sellTicket() {
        synchronized (d) {
           if (tickets > 0) {
            try {
                   Thread.sleep(100);
            } catch (InterruptedException e) {
                    e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()
                        + "正在出售第" + (tickets--) + "張票 ");
            }
       }
    }
=================================================================
靜態方法及鎖對象問題?
          靜態方法的鎖對象是誰呢?

              類的字節碼文件對象。(Xxx.class文件)
 
public class SellTicket implements Runnable {
// 定義100張票
    private static int tickets = 100;//使用了靜態方法。因此變量也要改成static
 
    // 定義同一把鎖
    private Object obj = new Object();
 
       private int x = 0;
 
@Override
    public void run() {
        while (true) {
            if(x%2==0){
                synchronized (SellTicket.class) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()
                                + "正在出售第" + (tickets--) + "張票 ");
                    }
                }
            }else {
                sellTicket();
            }
            x++;
        }
    }
}
 
 
private static synchronized void sellTicket() {
        if (tickets > 0) {
        try {
                Thread.sleep(100);
        } catch (InterruptedException e) {
                e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()
                    + "正在出售第" + (tickets--) + "張票 ");
        }
}
 
24.曾經的線程安全的類回想
回想曾經線程安全的類
// 線程安全的類,效率低
        StringBuffer sb = new StringBuffer();
        Vector<String> v = new Vector<String>();
        Hashtable<String, String> h = new Hashtable<String, String>();
 
注意
 
        // Vector是線程安全的時候纔去考慮使用的,但是我還說過即便要安全。我也不用你
        // 那麼究竟用誰呢?
        // public static <T> List<T> synchronizedList(List<T> list)
        List<String> list1 = new ArrayList<String>();// 線程不安全
        List<String> list2 = Collections
                .synchronizedList(new ArrayList<String>()); // 線程安全
 
Collections.synchronizedList(接收List對象)

 

演示樣例

 
day23筆記補充
Java程序的執行原理及JVM的啓動是多線程的嗎?
        A:Java命令去啓動JVM,JVM會啓動一個進程,該進程會啓動一個主線程。

        B:JVM的啓動是多線程的,因爲它最低有兩個線程啓動了,主線程和垃圾回收線程。
多線程的實現方案(掌握)
        A:繼承Thread類
        B:實現Runnable接口
曾經的線程安全的類
        A:StringBuffer
        B:Vector
        C:Hashtable
        D:怎樣把一個線程不安全的集合類變成一個線程安全的集合類
            用Collections工具類的方法就能夠。
相關文章
相關標籤/搜索