文件傳輸基礎(Java IO流)--慕課網筆記

第1章 文件的編碼
1-1 文件的編碼
  • 編碼問題
  • File類的使用
  • RandomAccessFile的使用
  • 字節流的使用
  • 字符流的使用
  • 對象的序列化和反序列化

記事本 BOM 之 “聯通” “聯” “聯想” 爲什麼有的顯示不正常

package com.imooc.io;

public class EncodeDemo {

    public static void main(String[] args) throws Exception{

        String s = "慕課ABC";
        byte[] bytes1 = s.getBytes();//轉換成字節序列用的是項目默認編碼gbk
        for(byte b:bytes1){
            //把字節(轉換成int)以16進制的方式顯示
            System.out.print(Integer.toHexString(b & 0xff) + " ");
        }
        System.out.println();
        byte[] bytes2 = s.getBytes("gbk");
        for (byte b : bytes2) {
            System.out.print(Integer.toHexString(b & 0xff) + " ");
        }
        System.out.println();
        byte[] bytes3 = s.getBytes("utf-8");
        //utf-8編碼中文佔用3個字節,英文佔用1個字節
        for (byte b : bytes3) {
            System.out.print(Integer.toHexString(b & 0xff) + " ");
        }
        System.out.println();
        //java是雙字節編碼 utf-16be
        byte[] bytes4 = s.getBytes("utf-16be");
        //utf-16be 中文佔用2個字節,英文佔用2個字節
        for (byte b : bytes4) {
            System.out.print(Integer.toHexString(b & 0xff) + " ");
        }
        System.out.println();
        /*
         * 當你的字節序列是某種編碼時,這個時候想把字節序列變成
         * 字符串,也需要用這種編碼方式,否則會出現亂碼
         */
        String str1 = new String(bytes4);
        System.out.println(str1);
        String str2 = new String(bytes4,"utf-16be");
        System.out.println(str2);
        /*
         * 文本文件,就是字節序列
         * 可以是任意編碼的字節序列
         * 如果我們在中文機器上直接創建文本文件,那麼該文本文件只認識ansi編碼
         * 聯通、聯這是一種巧合,他們正好符合了utf-8編碼規則
         */

    }

}
第2章 File 類的使用
2-1 File 類常用 API 介紹

java.io.File類用於表示文件(目錄)
File類只用於表示文件(目錄)的信息(名稱、大小等),不能用於文件內容的訪問

public class FileDemo {


    public static void main(String[] args) {
        //瞭解構造函數的情況  查幫助
        File file = new File("E:\\javaio\\imooc");
//      System.out.println(file.exists());
        if(!file.exists())
            file.mkdir();//file.mkdirs();
        else
            file.delete();
        //是否是一個目錄 如果是目錄返回true,如果不是目錄or目錄不存在返回false
        System.out.println(file.isDirectory());
        //是否是一個文件
        System.out.println(file.isFile());


//      File file2 = new File("e:\\javaio\\日記1.txt");
        File file2 = new File("e:\\javaio","日記1.txt");
        if(!file.exists())
            try{
                file2.createNewFile();
            }catch(IOException e){
                e.printStackTrace();
            }
        else
            file2.delete();

        //常用的File對象的API
        System.out.println(file);//file.toString()的內容
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getName());
        System.out.println(file2.getName());
        System.out.println(file.getParent());
        System.out.println(file2.getParent());
        System.out.println(file.getParentFile().getAbsolutePath());
    }


}
2-2 遍歷目錄
//列出File的一些常用操作,比如過濾,遍歷
public class FileUtils {

    /**
     * 列出指定目錄下(包括子目錄)的所有文件
     * @param dir
     * @throws IOException
     */
    public static void listDirectory(File dir) throws IOException{
        if(!dir.exists()){
            throw new IllegalArgumentException("目錄:"+dir+"不存在.");
        }
        if(!dir.isDirectory()){
            throw new IllegalArgumentException(dir+"不是目錄");
        }
//      String[] filenames = dir.list();//返回的是字符串數組 直接子的名稱,不包含子目錄的內容
//      for (String string : filenames) {
//          System.out.println(dir + "\\" + string);
//      }
        //如果要遍歷子目錄下的內容就需要構造File對象做遞歸操作,File提供了直接返回file對象
        File[] files = dir.listFiles();//返回的是直接子目錄(文件)的抽象
        if(files != null & files.length > 0){
            for(File file:files){
                if(file.isDirectory()){
                    listDirectory(file);
                }else{
                    System.out.println(file);
                }
            }
        }
    } 


}
第3章 RandomAccessFile類的使用
3-1 RandomAccessFile基本操作

RandomAccessFile java提供的對文件內容的訪問,既可以讀文件,也可以寫文件。
RandomAccessFile 支持隨機訪問文件,可以訪問文件的任意位置

  1. java文件模型
    在硬盤上是byte byte byte存儲的,是數據的集合
  2. 打開文件
    有兩種模式”rw”(讀寫) “r”(只讀)
    RandomAccessFile raf = new RandomAccessFile(file,”rw”)
    文件指針,打開文件時指針在開頭 pointer = 0
  3. 寫文件
    raf.write(int)—>只寫一個字節(後8位),同事指針指向下一個位置,準備在次寫
  4. 讀方法
    int b = raf.read()—>讀一個字節
  5. 文件讀寫完畢以後一定要關閉(Oracle官方說明)
public class RafDemo {


    public static void main(String[] args) throws IOException{

        File demo = new File("demo");
        if(!demo.exists()){
            demo.mkdir();
        }
        File file = new File(demo,"raf.dat");
        if(!file.exists()){
            file.createNewFile();
        }

        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        //指針的位置
        System.out.println(raf.getFilePointer());

        raf.write('A');//只寫了一個字節
        System.out.println(raf.getFilePointer());
        raf.write('B');


        int i = 0x7fffff;
        //用write方法每次只能寫一個字節,如果要把i寫進去就得寫4次
        raf.write(i >>> 24);//高8位
        raf.write(i >>> 16);
        raf.write(i >>> 8);
        raf.write(i);
        System.out.println(raf.getFilePointer());

        //可以直接寫一個int
        raf.write(i);

        String s = "中";
        byte[] gbk = s.getBytes("gbk");
        raf.write(gbk);


        //讀文件,必須把指針移動到頭部
        raf.seek(0);
        //一次性讀取,把文件的內容讀取到字節數組中
        byte[] buf = new byte[(int)raf.length()];
        raf.read(buf);

        System.out.println(Arrays.toString(buf));
        for (byte b : buf) {
            System.out.print(Integer.toHexString(b & 0xff) + " ");
        }
        raf.close();
    }


}
第4章 字節流
4-1 字節流之文件輸入流FileInputStream-1

IO流(輸入流、輸出流)
字節流、字符流

  1. 字節流
    1. InputStream、OutputStream
      • InputStream抽象了應用程序讀取文件的方式
      • OutputStream抽象了應用程序寫出數據的方式
    2. EOF = End 讀到-1就讀到結尾
    3. 輸入流基本方法
      • int b = in.read();讀取一個字節無符號填充到int低八位。-1是EOF。
      • int read(byte[] buf)
      • int read(byte[] buf,int start,int size)
    4. 輸出流基本方法
      • out.write(int b) 寫出一個byte到流,b的低8位
      • out.write(byte[] buf) 將buf字節數組都寫入流
      • out.write(byte[] buf,int start,int size)
    5. FileInputStream —> 具體實現了在文件上讀取數據
    6. FileOutputStream 實現了向文件中寫出byte數據的方法
    7. DataOutputStream/DataInputStream
      對“流”功能的擴展,可以更加方便的讀取int,long,字符等類型數據
      DataOutputStream
      writeInt()/writeDouble()/writeUTF()
public class IOUtil {


    /**
     * 讀取指定文件內容,按照16進制輸出到控制檯
     * 並且每輸出10個byte換行
     * @param fileName
     * 單字節讀取不適合大文件,大文件效率很低
     */
    public static void printHex(String fileName)throws IOException{
        //把文件作爲字節流進行讀操作
        FileInputStream in = new FileInputStream(fileName);
        int b ;
        int i = 1;
        while((b = in.read())!=-1){
            if(b <= 0xf){
                //單位數前面補0
                System.out.print("0");
            }
            System.out.print(Integer.toHexString(b)+"  ");
            if(i++%10==0){
                System.out.println();
            }
        }
        in.close();
    }


}
4-2 字節流之文件輸入流FileInputStream-2
  • FileInputStream —> 具體實現了在文件上讀取數據
/**
     * 批量讀取,對大文件而言效率高,也是我們最常用的讀文件的方式
     * @param fileName
     * @throws IOException
     */
    public static void printHexByByteArray(String fileName)throws IOException{
        FileInputStream in = new FileInputStream(fileName);
        byte[] buf = new byte[8 * 1024];
        /*從in中批量讀取字節,放入到buf這個字節數組中,
         * 從第0個位置開始放,最多放buf.length個 
         * 返回的是讀到的字節的個數
        */
        int bytes = in.read(buf,0,buf.length);//一次性讀完,說明字節數組足夠大
        int j = 1; 
        for(int i = 0; i < bytes;i++){
            System.out.print(Integer.toHexString(buf[i] & 0xff)+"  ");
            if(j++%10==0){
                System.out.println();
            }
        }
//    int bytes = 0;
//    int j = 1;
//    while((bytes = in.read(buf,0,buf.length))!=-1){
//        for(int i = 0 ; i < bytes;i++){
//            System.out.print(Integer.toHexString(buf[i] & 0xff)+"  ");
//            if(j++%10==0){
//                System.out.println();
//            }
//        }
//    }
      in.close();
    }
4-3 字節流之文件輸出流FileOutputStream
  • FileOutputStream 實現了向文件中寫出byte數據的方法
    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        //如果該文件不存在,則直接創建,如果存在,刪除後創建
        FileOutputStream out = new FileOutputStream("demo/out.dat");
        out.write('A');//寫出了'A'的低八位
        out.write('B');//寫出了'B'的低八位
        int a = 10;//write只能寫八位,那麼寫一個int需要些4次每次8位
        out.write(a >>> 24);
        out.write(a >>> 16);
        out.write(a >>> 8);
        out.write(a);
        byte[] gbk = "中國".getBytes("gbk");
        out.write(gbk);
        out.close();

        IOUtil.printHex("demo/out.dat");


    }
    /**
     * 文件拷貝,字節批量讀取
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copyFile(File srcFile,File destFile)throws IOException{
        if(!srcFile.exists()){
            throw new IllegalArgumentException("文件:"+srcFile+"不存在");
        }
        if(!srcFile.isFile()){
            throw new IllegalArgumentException(srcFile+"不是文件");
        }
        FileInputStream in = new FileInputStream(srcFile);
        FileOutputStream out = new FileOutputStream(destFile);
        byte[] buf = new byte[8*1024];
        int b ;
        while((b = in.read(buf,0,buf.length))!=-1){
            out.write(buf,0,b);
            out.flush();//最好加上
        }
        in.close();
        out.close();

    }
4-4 字節流之數據輸入輸出流
  • DataOutputStream/DataInputStream
    對“流”功能的擴展,可以更加方便的讀取int,long,字符等類型數據
    DataOutputStream
    writeInt()/writeDouble()/writeUTF()
    public static void main(String[] args) throws IOException {
        String file = "demo/dos.dat";
        DataOutputStream dos = new DataOutputStream(
                         new FileOutputStream(file));
        dos.writeInt(10);
        dos.writeInt(-10);
        dos.writeLong(10l);
        dos.writeDouble(10.5);
        //採用utf-8編碼寫出
        dos.writeUTF("中國");
        //採用utf-16be編碼寫出
        dos.writeChars("中國");
        dos.close();
        IOUtil.printHex(file);
    }
public static void main(String[] args) throws IOException{
        // TODO Auto-generated method stub
        String file = "demo/dos.dat";
        IOUtil.printHex(file);
       DataInputStream dis = new DataInputStream(
               new FileInputStream(file));
       int i = dis.readInt();
       System.out.println(i);
       i = dis.readInt();
       System.out.println(i);
       long l = dis.readLong();
       System.out.println(l);
       double d = dis.readDouble();
       System.out.println(d);
       String s = dis.readUTF();
       System.out.println(s);

       dis.close();
    }
4-5 字節緩衝流
  • BufferedInputStream/BufferedOutputStream
    這兩個流類爲IO提供了帶緩衝的操作,一般打開文件進行寫入
    或讀取操作時,都會加上緩衝,這種流模式提高了IO的性能
    從應用程序中把輸入放入文件,相當於一缸水導入另一個缸中:
    FileOutputStream —> write() 方法相當於一滴一滴地把水“轉移”過去
    DataOutputStream —> write() 方法會方便一些,相當於一瓢一瓢把水“轉移”過去
    BufferedOutputStream —> write() 方法更方便一些,相當於一瓢一瓢先放入桶中,再從桶中倒入到缸中
    /**
     * 文件拷貝,字節批量讀取
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copyFile(File srcFile,File destFile)throws IOException{
        if(!srcFile.exists()){
            throw new IllegalArgumentException("文件:"+srcFile+"不存在");
        }
        if(!srcFile.isFile()){
            throw new IllegalArgumentException(srcFile+"不是文件");
        }
        FileInputStream in = new FileInputStream(srcFile);
        FileOutputStream out = new FileOutputStream(destFile);
        byte[] buf = new byte[8*1024];
        int b ;
        while((b = in.read(buf,0,buf.length))!=-1){
            out.write(buf,0,b);
            out.flush();//最好加上
        }
        in.close();
        out.close();

    }
    /**
     * 進行文件的拷貝,利用帶緩衝的字節流
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copyFileByBuffer(File srcFile,File destFile)throws IOException{
        if(!srcFile.exists()){
            throw new IllegalArgumentException("文件:"+srcFile+"不存在");
        }
        if(!srcFile.isFile()){
            throw new IllegalArgumentException(srcFile+"不是文件");
        }
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(destFile));
        int c ;
        while((c = bis.read())!=-1){
            bos.write(c);
            bos.flush();//刷新緩衝區
        }
        bis.close();
        bos.close();
    }
    /**
     * 單字節,不帶緩衝進行文件拷貝
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copyFileByByte(File srcFile,File destFile)throws IOException{
        if(!srcFile.exists()){
            throw new IllegalArgumentException("文件:"+srcFile+"不存在");
        }
        if(!srcFile.isFile()){
            throw new IllegalArgumentException(srcFile+"不是文件");
        }
        FileInputStream in = new FileInputStream(srcFile);
        FileOutputStream out = new FileOutputStream(destFile);
        int c ;
        while((c = in.read())!=-1){
            out.write(c);
            out.flush();
        }
        in.close();
        out.close();
    }
    public static void main(String[] args) {
        try {
            long start = System.currentTimeMillis();
            /*IOUtil.copyFileByByte(new File("e:\\javaio\\1.mp3"), new File(
                    "e:\\javaio\\2.mp3"));*/  //兩萬多毫秒
            /*IOUtil.copyFileByBuffer(new File("e:\\javaio\\1.mp3"), new File(
                    "e:\\javaio\\3.mp3"));//一萬多毫秒*/
            IOUtil.copyFile(new File("e:\\javaio\\1.mp3"), new File(
                    "e:\\javaio\\4.mp3"));//7毫秒
            long end = System.currentTimeMillis();
            System.out.println(end - start );
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
第5章 字符流
5-1 字節字符轉換流
  • 字符流
    1. 編碼問題
    2. 認識文本和文本文件
      java的文本是16位無符號整數,是字符的unicode編碼(雙字節編碼)
      文件是byte byte byte …的數據序列
      文本文件是文本(char)按照某種編碼方案(utf-8,utf-16be,gbk)序列化爲byte
    3. 字符流(Reader Writer)
      字符的處理,一次處理一個字符
      字符的底層仍然是基本的字節序列
      字符的基本實現
      InputStreamReader 完成byte流解析成char流,按照編碼解析
      OutputStreamWriter 提供char流到byte流,按照編碼處理
    public static void main(String[] args)throws IOException {
        FileInputStream in = new FileInputStream("e:\\javaio\\imoocutf8.txt");
        InputStreamReader isr = new InputStreamReader(in,"utf-8");//默認項目的編碼,操作的時候,要寫文件本身的編碼格式

        FileOutputStream out = new FileOutputStream("e:\\javaio\\imoocutf81.txt");
        OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8");
        /*int c ;
        while((c = isr.read())!=-1){
            System.out.print((char)c);
        }*/
        char[] buffer = new char[8*1024];
        int c;
        /*批量讀取,放入buffer這個字符數組,從第0個位置開始放置,最多放buffer.length個
          返回的是讀到的字符的個數
        */
        while(( c = isr.read(buffer,0,buffer.length))!=-1){
            String s = new String(buffer,0,c);
            System.out.print(s);
            osw.write(buffer,0,c);
            osw.flush();
        }
        isr.close();
        osw.close();

    }
5-2 字符流之文件讀寫流

FileReader/FileWriter

    public static void main(String[] args) throws IOException{
        FileReader fr = new FileReader("e:\\javaio\\imooc.txt");
        FileWriter fw = new FileWriter("e:\\javaio\\imooc2.txt");
        //FileWriter fw = new FileWriter("e:\\javaio\\imooc2.txt",true);
        char[] buffer = new char[2056];
        int c ;
        while((c = fr.read(buffer,0,buffer.length))!=-1){
            fw.write(buffer,0,c);
            fw.flush();
        }
        fr.close();
        fw.close();
    }
5-3 字符流的過濾器
  • 字符過濾器
    BufferedReader —> readLine 一次讀一行
    BufferWriter/PrintWriter —> 寫一行
    public static void main(String[] args) throws IOException{
         //對文件進行讀寫操作 
        BufferedReader br = new BufferedReader(
                new InputStreamReader(
                        new FileInputStream("e:\\javaio\\imooc.txt")));
        /*BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(
                        new FileOutputStream("e:\\javaio\\imooc3.txt")));*/
        PrintWriter pw = new PrintWriter("e:\\javaio\\imooc4.txt");
        //PrintWriter pw1 = new PrintWriter(outputStream,boolean autoFlush);
        String line ;
        while((line = br.readLine())!=null){
            System.out.println(line);//一次讀一行,並不能識別換行
            /*bw.write(line);
            //單獨寫出換行操作
            bw.newLine();//換行操作
            bw.flush();*/
            pw.println(line);
            pw.flush();
        }
        br.close();
        //bw.close();
        pw.close();
    }
第6章 對象的序列化和反序列化
6-1 序列化基本操作
  • 對象的序列化,反序列化
    1. 對象序列化,就是將Object轉化爲byte序列,反之叫對象的反序列化
    2. 序列化流(ObjectOutputStream),是過濾流 —> writeObject
      反序列化流(ObjectInputStream) —> readObject
    3. 序列化接口(Serializable)
      對象必須實現序列化接口,才能進行序列化,否則將出現異常
      這個接口,沒有任何方法,只是一個標準
    public static void main(String[] args) throws Exception{
        String file = "demo/obj.dat";
        //1.對象的序列化
        /*ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(file));
        Student stu = new Student("10001", "張三", 20);
        oos.writeObject(stu);
        oos.flush();
        oos.close();*/
        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(file));
        Student stu = (Student)ois.readObject();
        System.out.println(stu);
        ois.close();

    }
6-2 transient及ArrayList源碼分析
  • transient關鍵字
    private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException
    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException,ClassNotFoundException

    分析ArrayList源碼中序列化和反序列化問題

6-3 序列化中子父類構造函數問題
  • 序列化中 子類和父類構造函數的調用問題
public class ObjectSeriaDemo2 {
    public static void main(String[] args) throws Exception{
        /*ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("demo/obj1.dat"));
        Foo2 foo2 = new Foo2();
        oos.writeObject(foo2);
        oos.flush();
        oos.close();*/

        //反序列化是否遞歸調用父類的構造函數
        /*ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("demo/obj1.dat"));
        Foo2 foo2 = (Foo2)ois.readObject();
        System.out.println(foo2);
        ois.close();*/


        /*ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("demo/obj1.dat"));
        Bar2 bar2 = new Bar2();
        oos.writeObject(bar2);
        oos.flush();
        oos.close();*/

        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("demo/obj1.dat"));
        Bar2 bar2 = (Bar2)ois.readObject();
        System.out.println(bar2);
        ois.close();


        /*
         * 對子類對象進行反序列化操作時,
         * 如果其父類沒有實現序列化接口
         * 那麼其父類的構造函數會被調用
         */
    }
}
/*
 *   一個類實現了序列化接口,那麼其子類都可以進行序列化
 */
class Foo implements Serializable{  
    public Foo(){
        System.out.println("foo...");
    }
}
class Foo1 extends Foo{
    public Foo1(){
        System.out.println("foo1...");
    }
}
class Foo2 extends Foo1{
    public Foo2(){
        System.out.println("foo2...");
    }
}
class Bar{
    public Bar(){
        System.out.println("bar");
    }
}
class Bar1 extends Bar{
    public Bar1(){
        System.out.println("bar1..");
    }
}
class Bar2 extends Bar1 implements Serializable{
    public Bar2(){
        System.out.println("bar2...");
    }
}

《文件傳輸基礎——Java IO流》視頻地址

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