Back

Java_IO流

JavaIO流的基本概念与节点流

Java IO流1(概念及节点流)


  1. 文件
  2. IO流原理及流的分类
  3. InputStream的子类及使用
  4. OutputStream的子类及使用
  5. 利用FileInputStream及FileOutputStream进行文件copy
  6. Reader与Writer的子类及使用
  7. 节点流和处理流

文件

  • 文件是保存数据的地方,例如word文档,txt文档等都是文件。

  • 文件流

    文件在程序中是以流的形式来操作的

    image01

    • 流:数据在数据源(文件)和程序(内存)之间经历的路径
    • 输入流:数据从数据源(文件)到程序(内存)的路径
    • 输出流:数据从程序(内存)到数据源(文件)的路径
  • 常用的文件操作

  1. 使用new File(String pathname)创建

    package com.java_io;
    
    import org.junit.Test;
    
    import java.io.File;
    import java.io.IOException;
    
    public class FileCreate1 {
        public static void main(String[] args) {
    
        }
        //方法1:使用new File(filePath)创建文件
        @Test
        public void createFile01() throws IOException {
             //创建的文件路径名称
            String filePath = "e:\\news1.txt";
            File file = new File(filePath);
    
            try {
                file.createNewFile();
                System.out.println("文件创建成功");
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
  2. 使用new File(File parent,String child)创建

     @Test
        public void createFile02(){
            File parentFile = new File("e:\\");
            String fileName = "news2.txt";
            //此时的file对象在Java程序(内存)中只是一个对象而已
            //只有执行了createNewFile方法才会真正在磁盘创建文件
            File file = new File(parentFile,fileName);
    
    
            try {
                //此时在磁盘中创建了文件
                file.createNewFile();
                System.out.println("文件创建成功");
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
  3. 使用new File(String parent,String child)创建

        @Test
        public void createFile03(){
            String parentPath = "e:\\";
            //String parentPath = "e:/"; 也可以
            String fileName = "new3.txt";
            File file = new File(parentPath,fileName);
    
            try {
                file.createNewFile();
                System.out.println("文件创建成功");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    

    image02

  4. 获取文件信息

    • getName():获取文件名字
    • getAbsolutePath():获取文件绝对路径
    • getParent():获取父级目录
    • length():获取文件大小(字节)
    • exists():查看文件是否存在
    • isFile():检测是不是一个文件
    • isDirectory():检测是不是一个目录

    代码演示

    package com.java_io;
    
    import org.junit.Test;
    
    import java.io.File;
    
    public class fileInformation {
        public static void main(String[] args) {
    
        }
        @Test
        public void fileInformation(){
            //先创建文件对象
            File file = new File("e:\\news1.txt");
            //调用对应的方法,得到相应的信息
            System.out.println("文件名字="+file.getName());
            System.out.println("文件绝对路径="+file.getAbsolutePath());
            System.out.println("文件父级目录="+file.getParent());
            System.out.println("文件大小(字节)="+file.length());
            System.out.println("文件是否存在="+file.exists());
            System.out.println("是不是一个文件="+file.isFile());
            System.out.println("是不是一个目录="+file.isDirectory());
    
        }
    }
    

    image03

  5. 目录的操作

    • delete():删除目录。
    • mkdir():创建一级目录
    • mkdirs():创建多级目录

    代码示例

        //检查目录下是否存在news2.txt文件,若存在则删除
        @Test
        public void m1(){
            File file = new File("e:\\news2.txt");
    
            if (file.exists()) {
                if (file.delete()) {
                    System.out.println("删除成功");
                }else {
                    System.out.println("删除失败");
                }
            }else {
                System.out.println("找不到改文件...");
            }
        }
    }
    

    创建目录

    	//检查e:\\demo\\a\\b\\c是否存在,若不存在则创建
        @Test
        public void m2(){
            String directoryPath = "e:\\demo\\a\\b\\c";
            File file = new File(directoryPath);
            if (file.exists()) {
                System.out.println("改目录存在...");
            }else {
                if (file.mkdirs()) {
                    System.out.println(directoryPath+"目录创建成功");
                }else {
                    System.out.println(directoryPath+"目录创建失败");
                }
            }
    
        }
    

    image04

IO流原理及流的分类

  • 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中

  • 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

  • 流的分类

    • 按操作数据单位不同分为:
      • 字节流(8bit):二进制文件使用
      • 字符流(按字符):文本文件使用
    • 按数据流的流向不同分为:输入流,输出流
    • 按流的角色的不同分为:字节流,处理流/包装流

    而Java IO流有四个抽象基类

    字节流 字符流
    输出流 OutputStream Writer
    输入流 InputStream Reader

    其他的流都是继承于这四大基类的。下图是Java IO流的整体架构图

    image05

    所理解的流就像快递小哥,在物流中心和客户之间做传递作用

    image06

InputStream的子类及使用

  • InputStream的常用子类

    1. FileInputStream:文件输入流
    2. BufferedInputStream:缓冲字节输入流
    3. ObjectInoutStream:对象字节输入流
    • 使用FileInputStream读取文件

      read()读取单个字节

      package com.java_io;
      
      import org.junit.Test;
      
      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class FileInputStreamTest {
          public static void main(String[] args) {
      
          }
          @Test
          public void InputStream(){
              String  filePath = "e:\\hello.txt";
              FileInputStream fileInputStream = null;
              int readFile = 0;
              try {
                  //创建FileInputStream对象,用于读取文件
                  fileInputStream = new FileInputStream(filePath);
                  //read():从输入流中读取一个字节的数据,如果没有输入,则被阻止
                  //如果返回-1.则表示读取完毕
                  while ((readFile = fileInputStream.read())!=-1){
                      System.out.print((char) readFile);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }finally {
                  //关闭文件流,释放资源
                  try {
                      fileInputStream.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
      
              }
          }
      }
      

      image07

      read(byte[b])缓冲区读取b个字节

      	@Test
          public void InputStream02(){
              String filePath = "e:\\hello.txt";
              byte [] buf = new byte[8];//每次读取8个字节的数据
              FileInputStream fileInputStream = null;
              int readLen = 0;//记录读取的字节数
              try {
                  fileInputStream = new FileInputStream(filePath);
                  /*
                  * 创建FileInputStream对象,用于读取文件
                  * 从输入流读取最多buf.length字节数据到字节数组,若没有输入,则阻塞此方法
                  * 如果返回值为-1,表示读取完毕
                  * 如果读取正常则返回读取的字节数*/
                  while((readLen = fileInputStream.read(buf))!=-1){
                      //new Sting(buf,0,len):把一个字节数组从0取到len构造成一个新的String
                      System.out.print(new String(buf,0,readLen));
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              } finally {
                  try {
                      //关闭流,释放资源
                      fileInputStream.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      

      image09

      需要注意的是使用FileInputStream读取文件时,读取到的字符是乱码的

      image08

OutputStream的子类及使用

  • FileOutputStream

    演示使用FileOutputStream。将数据写到文件中,如果该文件不存在,则创建该文件。

    package com.java_io;
    
    import org.junit.Test;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class FileOutputStreamTest {
        public static void main(String[] args) {
    
        }
        @Test
        public void fileOutputStream(){
            String filePath = "e:\\hello.txt";
            FileOutputStream fileOutputStream = null;
    
            try {
                //创建FileOutputStream对象,对文件操作
                fileOutputStream = new FileOutputStream(filePath,true);
                //向文件写入一个字节的数据
                fileOutputStream.write('H');
                //向文件写入字符串
                //str.getBytes()将字符串转换为字节数组
                String str = "Hello,Word!";
                fileOutputStream.write(str.getBytes());
                System.out.println("写入成功");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    image10

    • 若需要写入指定的字符,使用

      //write(byte[] b,int off,int len);
      fileOutputStream.write(str.getBytes(),0,str.length());
      

      可以将len字节从偏移量off的指定字节数组写入此文件输入流

    • 需要注意的是,如果使用

      fileOutputStream = new FileOutputStream(File file,boolean append);
      

      时没有将append设置为true的话,系统会默认append的值为false,则会清空记事本再写入新的内容(覆盖之前的内容)。

      若需要换行或者添加制表符输入,使用其转义字符即刻

      fileOutputStream.write("\n".getBytes());//换行
      fileOutputStream.write("\t".getBytes());//增加制表符
      

利用FileInputStream及FileOutputStream进行文件Copy

  • 思路分析

    1. 创建文件的输入流,将文件读到程序中
    2. 创建文件的输出流,将读取到的文件输入写到指定的文件中
  • 代码演示

    package com.java_io;
    
    import org.junit.Test;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class FileCopy {
        public static void main(String[] args) {
    
        }
        /*
        * 将"e:\\567.jpg"复制到"d:\\567.jpg"*/
        @Test
        public void fileCopy(){
            String srcPath = "e:\\567.jpg";
            String destPath = "d:\\567.jpg";
            //创建输入流及输出流对象
            FileInputStream fileInputStream = null;
            FileOutputStream fileOutputStream = null;
    
            try {
                //定义字节数组接收,提高效率
                byte [] buf = new byte[1024];
                int readlen = 0;
                fileInputStream = new FileInputStream(srcPath);
                fileOutputStream = new FileOutputStream(destPath);
                while ((readlen = fileInputStream.read(buf))!=-1){
                    fileOutputStream.write(buf,0,readlen);
                }
                System.out.println("写入成功!");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    //关闭输入输出流,释放资源
                    if(fileInputStream==null){
                        fileInputStream.close();
                    }
                    if(fileOutputStream==null){
                        fileOutputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    image11

    需要注意的是,写入文件时,必须使用

    write(byte[],0,readLength)
    

    写入。readLength为读入数据的长度,这样写可以避免byte数组过大时把其余子集读入造成文件损失

Reader与Writer的子类及使用

  • FileReader的相关方法:

    1. new FileReader(File/String):创建对象
    2. read():每次读取单个字符并返回,如果到文件末尾,返回-1
    3. read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾则返回-1。

    相关API

    1. new String(char[]):将char[] 转换成String
    2. new String(char[],off,len):将char[]的指定部分转换成String

    代码演示

    package com.java_io;
    
    import org.junit.Test;
    
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileReaderTest {
        public static void main(String[] args) {
    
        }
        @Test
        public void FileReaderTest(){
            String filePath = "e:\\story.txt";
            FileReader fileReader = null;
            char [] chars = new char[1024];
            try {
              //新建对象
                fileReader = new FileReader(filePath);
                try {
                    while ((fileReader.read(chars))!=-1){
                        System.out.println(chars);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                try {
                    if(fileReader==null){
                        //关闭流,释放资源
                         fileReader.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  • FileWriter的相关方法

    1. new FileWriter(File/String):覆盖模式,相当于流的指针在首端
    2. new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
    3. write(int):写入单个字符
    4. write(char[]):写入指定数组
    5. write(char[],off,len):写入指定数组的指定部分
    6. write(String):写入整个字符串
    7. write(String,off,len):写入字符串的指定部分

    相关API

    1. toCharArray:将String转换成char[];

    注意:

    • FileWriter使用后,必须关闭(close)或刷新(flush),否则写入不到指定的文件

    • FileWriter的类图如下

      image12

    FileWriter的使用

    package com.java_io;
    
    import org.junit.Test;
    
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.Writer;
    
    public class FileWriterTest {
        public static void main(String[] args) {
    
        }
        @Test
        public void FileWriter(){
            String filePath = "e:\\note.txt";
    
            FileWriter fileWriter = null;
            String str = "AD";
            char [] chars ={'a','b','c','d'};
            int writerLen = 3;
            try {
                fileWriter = new FileWriter(filePath,true);
                fileWriter.write('H');
                fileWriter.write("\n");//换行
                fileWriter.write(str);
                fileWriter.write("\n");
                fileWriter.write(chars);
                fileWriter.write("\n");
                fileWriter.write(chars,0,writerLen);
                fileWriter.write("\n");
                fileWriter.write(str,0,1);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                        fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

    image13

    • 如果需要换行或者输入制表符则可以

      fileWriter.write("\n");//换行
      fileWriter.write("\t");//制表符
      

节点流和处理流

  • 节点流可以从一个特定的数据源读写数据

  • 处理流(包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,如BufferedReader、BufferedWriter

    image14

  • 分析BufferedReader处理流

    image15

    查看BufferedReader的源码可以知道,BufferedReader中定义了一个Reader属性,即他可以封装任意一个节点流,所以使用BufferedReader去操作的时候,其可以访问各种不同的数据源。其功能更为强大

    BufferedWriter同理。

  • 节点流与处理流的区别和联系

    1. 节点流是底层流/低级流,直接跟数据源相接。
    2. 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出
    3. 处理流(包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
  • 模拟装饰者设计模式来理解处理流BufferedReader/BufferedWriter

    创建Reader抽象类(Reader_)

    package com.decorator_pattern;
    
    public abstract class Reader_ {
        public void readString(){
    
        }
        public void readFile(){
    
        }
    }
    

    创建节点流子类FileReader_

    package com.decorator_pattern;
    
    public class FileReader_ extends Reader_{
        public void readFile(){
            System.out.println("readFile");
        }
    }
    

    创建节点流子类StringReader_

    package com.decorator_pattern;
    
    public class StringReader_ extends Reader_{
        public void readString(){
            System.out.println("readString");
        }
    }
    

    创建处理流BufferedReader_

    package com.decorator_pattern;
    
    public class BufferedReader_ extends Reader_{
        private Reader_ reader_;//属性时Reader_类型
    
        public BufferedReader_(Reader_ reader_) {
            this.reader_ = reader_;
        }
        //封装方法
        public void readFile(){
            reader_.readFile();
        }
        //装饰者模式的特性:增加新功能
        public void readStrings(int num){
            for (int i = 0; i < num; i++) {
                reader_.readString();
            }
        }
    }
    

    Test类

    package com.decorator_pattern;
    
    public class Test {
        public static void main(String[] args) {
            //新建bufferedReader_对象,把FileReader_传进去,使方法可以对文件操作
            BufferedReader_ bufferedReader_1 = new BufferedReader_(new FileReader_());
            bufferedReader_1.readFile();
            //必须是相应的对象的方法才会有用
            bufferedReader_1.readStrings(10);//无效
            //新建bufferedReader_对象,把StringReader_传进去,使方法可以对字符串操作
            BufferedReader_ bufferedReader_2 = new BufferedReader_(new StringReader_());
            bufferedReader_2.readStrings(10);
        }
    }
    

    测试结果

    image16

    通过上述设计模式可以让BufferedReader包装其他节点流,这样子就能提高对数据源操作的效率

    可以使用统一的一个read方法来优化上述代码。如:

    在Reader_抽象类中使用抽象方法read

    package com.decorator_pattern;
    
    public abstract class Reader_ {
        public abstract void read();
    }
    

    然后子类实现此方法即可

    package com.decorator_pattern;
    
    public class FileReader_ extends Reader_{
        public void read(){
            System.out.println("readFile");
        }
    }
    

    此时,想要操作相应的数据则需要利用对象的动态绑定机制,绑定到对应的实现子类即可 。

    所以Test中的

            //多态性,因为bufferedReader_1是new了FileReader_对象,所以去找FileReader_对象
            bufferedReader_1.readStrings(10);
    

    能输出readFile。

  • 处理流的功能

    • 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
    • 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便
Built with Hugo
Theme Stack designed by Jimmy