Java入门 第十三节(一) IO流

1. 什么是IO流?

  1. java中IO是以 “流” 为基础进行输入输出的。
    • 如:文件读写,上传下载等。
  2. 这就需要先搞明白一个概念 啥是流(Stream)!?
    • 流,是一个很抽象的概念,如同水流一样。
    • 当程序需要读取数据的时候,就会开启一个通向数据源的流。 这个 数据源[可以是,文件,内存,或是网络连接] , 当程序需要写入数据的时候,就会开启一个通向目的地的流。
    • 数据好像在这其中 “流动” 一样。
  3. IO 缩写 input/output。带表流入或者流出, 相对于 内存程序 或者 磁盘 而言
    • 那么既然是 ,那么就会有流向一说,会像管道一样,只会朝着一个方向流动,可以按照流向分类。

1.1 流向分类:输入流,输出流

  1. 一,按照流向分类:以内存程序而言。

    • 输入流 :相当于 in 的过程, 将磁盘文件 读取 到内存程序过程。
    • 输出流 :相当于out的过程,将 内存中文件 写出 到磁盘的过程。
      流的理解
  2. 老吴个人建议:你把你当做java程序(就是内存程序)

    • 往内存程序中来 (将磁盘文件读取到内存中) 即: 读\输入
    • 从内存程序中出去(将内存中文件写出到磁盘中) 即: 写\输出!
  3. 并且管道中的流只会向一个方向!那它们是如何传输的!?

1.2 读取分类:字节流,字符流

  1. IO流的主要目的就是: 在 内存程序和数据源 之间传输数据。

  2. 怎么样去传输呢!? 这就要涉及数据的 写入\读取 根据不同的数据源,就有不同的,读取方式

    • 数据源:文本,视频,图片,音频等。
  3. 二 ,读取方式分类:对于不同的数据源而言

    • 字节流:字节 为单位读取数据的流,这种流什么都可以读取,人称“万能流”;
    • 字符流:字符 方式读取,就是为了读取文本文件的,但是不能读取“视频”,“音频”,“图片”。

2. 继承结构

2.1 字节流与字符流

  1. 分辨上来说:只要是以Stream结尾的都是 字节流
  • 字节输入流,读的过程。
字节流输入流针对二进制文件,读
InputStream字节流 输入父类
FileInputStream子类
BufferedInputStream子类
ObjectInputStream子类
  • 字节输出流,写的过程。
字节输出流针对二进制文件,写
OutputStream字节流 输出父类
FileOutputStream子类
BufferedOutputStream子类
ObjectOutputStream子类
  1. Writer \ Reader 结尾的都是 字符流
  • 字符输入流,读的过程。
字符流输入针对文本文件。读写容易发生乱码现象,在读写时最好指定编码集为utf-8
Reader字符流 输入父类
BufferedReader子类
InputStreamReader子类
  • 字符输出流,写的过程。
字符流输出针对文本文件。读写容易发生乱码现象,在读写时最好指定编码集为utf-8
Writer字符流 输出父类
BufferedWriter子类
OutputStreamWriter子类

3. File 类

3.1 概述

  1. File 表示 文件类,并不是流,它是 文件和目录路径名 的抽象表示,用户界面和操作系统使用依赖于系统的路径名字符串命名 文件和目录
    • 举个栗子:上传下载中 文件 与 读写文件存放 目录。
  2. 一个File的对象,就有可能是文件,也有可能是目录。所以需要了解其用法,怎么使用!

3.2 创建对象

  1. 查看Api我们发现可以通过构造方法创建对象。

    • 通过构造方法创建对象。
    1. new File(String pathname); 
    	    pathname 是路径字符串,通过将给定的路径名字符串转换为抽象路径名来创建新的File实例 
    	
    2. new File(String parent, String child); 
    	    从父路径名字符串和子路径名字符串创建新的 File实例。
    	    
    

3.3 常用方法介绍

  1. 关于IO流操作的方法比较多,不能选择硬背,多练习使用。“唯手熟尔”!

3.3.1 创建

  1. 创建方法:

    		 public boolean createNewFile() throws IOException ;  
    				用法:  新建文件,不存在则创建,返回true,如果文件存在,则返回false,
    				注意:  创建文件时,如果,在文件夹下创建文件,文件夹不存在,则报io异常。
    					    new File("D:\\", "java\\1"); // 子路径 java需要存在。
    					    
    		 public boolean mkdir();  
    				用法: 创建单级目录,就是只能创建一级,多级只创建一级。 
    					  如果中间为空,则返回false。
    					  
    				注意;  有同名的文件夹也不能创建!
    		 public boolean mkdirs();
    				用法: 创建多级目录, \\文件夹1\\文件夹2\\文件夹3
    

3.3.2 删除

  1. 删除方法:

    		 public boolean delete();
    				    用法: 删除由此路径名表示的 文件,目录。 
    				    
    				    注意: 如果此路径名表示目录,则目录必须为空才能删除。
    				    	   而且只能删除最后一级目录或者文件!
    

3.3.3 显示文件夹

  1. 显示文件夹中包含内容:

    		 public String[] list();  
    			用法: 返回一个 String[] 字符串数组,文件名或者文件夹名!
    			例如:"\\文件夹\\文件夹2"; 返回最后一级,文件夹2中所有 文件名 和 文件夹名;
    			注意: 如果该目录是文件,则返回 NullPointerException 错误; 
    		
    		 public File[] listFiles();
    			用法: 返回 File[] 文件对象,可以获取文件属性!
    

3.3.4 查看File对象 属性

  1. File对象属性:

    	  1 public long length()
    	  			用法:只显示文件字节量,相当于右键文件属性。
    	  2 public boolean exists()
    	  			用法:判断文件是否存在。
    	  3 public boolean isFile()
    	  			用法: 是否为文件,是就返回true;
    	  			注意: 要是系统依赖的文件且有后缀。没有后缀,就会发挥false4  public boolean isDirectory()
    	  			用法:判断是否是文件夹,是就返回true5  public String getName()
    	  			用法: 获取文件/文件夹名称。
    	  6  public String getParent()
    	  			用法: 获取父类文件夹的路径。
    	  7	public boolean isHidden()
    				用法:判断是否隐藏
    	  8	public String getAbsolutePath()
    				用法:返回完整路径。                        
    

3.3.5 重命名

  1. 重命名方法:可以操作文件或文件夹

    • 相当于移动复制。
    
        1 public boolean renameTo(File dest)
        		 File dest : 新名的文件/文件夹名称。
    			 用法:重命名,文件或者文件夹。首先文件或文件夹得存在!
    			 
    			
    

3.4 File类方法测试

  1. 注意:创建文件或者文件夹时,需要检查3点,

    • 路径,是否正确
    • 文件格式,是否正确。
    • 文件夹,是否存在!
    /**
        本类对File中的方法进行测试!
     */
    public class IoMethodTest {
        public static void main(String[] args) throws IOException {
    
            //1.先测试文件属性,然后再测试功能属性, File文件路径可以是文件,也可以是文件夹!
            File file = new File("C:\\新建文件夹\\java.txt");
    
            //2.测试File中的方法
            System.out.println(file.length()); // 文件属性。
            System.out.println(file.exists());// 是否存在!
            System.out.println(file.isFile()); // 是文件码!?
            System.out.println(file.isDirectory()); // 是文件夹么!?
            System.out.println(file.getName());// 获取文件名称。
            System.out.println(file.getParent());//获取父类路径。
            System.out.println(file.isHidden()); // 文件属性是否隐藏。
            System.out.println(file.getAbsolutePath());// 返回绝对路径。
    
            System.out.println("---------------华丽分割线---------------");
    
            //3.测试文件夹
            File file1 = new File("C:","新建文件夹");
    
            System.out.println(file1.length()); // 为什么是0? 因为这个只返回文件字节量!
            System.out.println(file1.exists());// 是否存在!
            System.out.println(file1.isFile()); // 是文件码!?
            System.out.println(file1.isDirectory()); // 是文件夹么!?
            System.out.println(file1.getName());// 获取文件名称。
            System.out.println(file1.getParent());//获取父类路径。
            System.out.println(file1.isHidden()); // 文件属性是否隐藏。
            System.out.println(file1.getAbsolutePath());// 返回绝对路径。
    
    
            System.out.println("---------------华丽分割线---------------");
    
            //4,功能测试
            File file2 = new File("C:\\新建文件夹1\\一人之下.doc");
            System.out.println(file2.createNewFile());//创建文件!只是文件!
            //4.1 注意这是创建目录!
            System.out.println(file2.mkdir());//创建单级目录!前提要有中间目录,否则失败!
            System.out.println(file2.mkdirs());//创建单级目录!
            System.out.println(file2.delete());// 删除不为空的文件夹!要是文件直接删除!
    
            System.out.println("---------------华丽分割线---------------");
    
            //4.2 显示设置
            File file3 = new File("C:\\新建文件夹2");
            String[] list = file3.list();
            System.out.println(Arrays.toString(list)); //返回的是字符串数组
    
            File[] files = file3.listFiles();
            System.out.println(Arrays.toString(files));// 返回的是文件对象。这样好处就是可以使用File方法!
    
    
            System.out.println("---------------华丽分割线---------------");
    
            //5.前提文件夹得存在! 如果有重复名称则无法修改!返回false
            File file4 = new File("C:\\文件夹\\文件夹1");
            File file5 = new File("C:\\文件夹\\你好");
    
            boolean b = file4.renameTo(file5);
            System.out.println(b);
    
        }
    }
    
    

输出结果:

15
true
true
false
java.txt
C:\新建文件夹
false
C:\新建文件夹\java.txt
---------------华丽分割线---------------
0
true
false
true
新建文件夹
C:\
false
C:\新建文件夹
---------------华丽分割线---------------
true
false
false
true
---------------华丽分割线---------------
[你好, 新建 Microsoft Excel 工作表.xlsx, 新建 Microsoft Word 文档.docx, 新建位图图像.bmp, 新建文件夹, 新建文本文档.txt]
[C:\新建文件夹2\你好, C:\新建文件夹2\新建 Microsoft Excel 工作表.xlsx, C:\新建文件夹2\新建 Microsoft Word 文档.docx, C:\新建文件夹2\新建位图图像.bmp, C:\新建文件夹2\新建文件夹, C:\新建文件夹2\新建文本文档.txt]
---------------华丽分割线---------------
true

3.5 课堂练习

3.5.1 递归求目录大小

  1. 啥是递归!? 是指在函数的定义中使用函数自身的方法。

    • 就是在自己的方法里面调用自己,但是得有出口,一般多用户File类操作,在文件类里经常用到!
    • 达到一定条件就要停下来 ,否则会出现栈内存出错!
     public static void main(String[] args) {
           method();
    }
    /**
     * 打豆豆的方法
     */
    public static void method(){
        System.out.println("打豆豆");
        //1,吃饭
        //2.睡觉
        //3.打豆豆
        method(); //调用打豆豆方法。会栈内存溢出!
    }
    
  2. 需求准备: 需要先创建文件夹

    • 准备:文件夹里面套文件夹和文件。
	public class DiGuiMethod {
	    public static void main(String[] args) {
	        /**思路: 
	         *      1.分析目录,然后列出文件对象。
	         *      2.判断是文件 或者 文件目录。
	         *        2.1  如果是文件,就统计length();
	         *        2.2  如果是文件目录, 重写执行 1步骤和2 步骤!递归就在这!
	         */
	
	        //解析文件目录
	        File file = new File("D:\\a");
	
	        //方法统计目录大小
	        long length= total(file);
	        System.out.println(length);//输出结果
	
	    }
	
	    /**
	     * @param  file  文件对象
	     * @return count long类型
	     */
	    private static long total(File file) {
	        long count = 0; //2.2求字节量,初始化为0;
	        //1.列出文件信息
	        File[] files = file.listFiles();
	
	        //1.1 遍历数组 拿到单个对象。
	        for (int i=0;i<files.length;i++){
	
	            //2判断file对象是文件,还是文件目录
	            if (files[i].isFile()){
	                //2.1 如果你是文件,将文件的字节量求和。
	                count = count+files[i].length();
	
	            }else if (files[i].isDirectory()){
	                //2.3 如果你是文件目录,重新走 1和2 连个步骤!
	                count = count + total(files[i]);  //递归
	            }
	        }
	        return count;
	    }
	}

3.5.2 递归删除文件夹

  1. file.delete(); 只能删除空文件夹,且只能一级一级删除。
    • 执行顺序:先从外往里执行,在从里面往外面返回。
    • 例如:File file = new File("D://digui"); 如果digui文件夹里有,空新建文件夹也删除不了,
    • 必须指定File file = new File("D://digui//新建文件夹"); 到空文件夹目录,才能删除,所以很麻烦。
	public class DiGuiDelete {
	    public static void main(String[] args) {
	        File file = new File("D:\\a1");
	
	         remove(file);
	
	        /*1.这个问题难点在与目录删除!
	            1.1 先判断是不是文件,如果是文件就直接删除。
	            1.2 在判断是不是目录,如果是目录就列出信息,然后递归删除!
	            1.2.1 处理剩下的文件夹。

	         */
	    }
	    
	    /**
	     * @param file 文件对象
	     * @return boolean 类型的返回结果
	     */
	    private static void remove(File file) {
	        //1.显示所有的文件信息。
	        File[] files = file.listFiles();
	
	        //1.1 进行遍历
	        for (int i = 0; i <files.length ; i++) {
	            if (files[i].isFile()){
	                files[i].delete();//如果是文件就直接删除。
	                System.out.println("已删除"+ files[i]);
	            }else if (files[i].isDirectory()){
	                //1.2,这有一个问题哈! 这个位置我删除文件夹了吗!?
	                remove(files[i]);//递归删除!
	            }
	        }
	
	        //2. 删除剩下的文件夹!
	        file.delete();
	        System.out.println("已删除文件夹"+file);
	    }
	}

4. 字节流的读取

4.1 概述

  1. 字节流输入流,其中一种按照字节流读取, 底层字节byte[]数组
  2. 字节流主要处理是二进制数据,计算机数据组成就是二进制的。所以,字节流处理的范围很广泛,相当于万能流。
    • 如: 视频,音频,应用程序,文本文件 等等。

4.2 InputStream 超类

  1. public abstract class InputStream本身是抽象类,表示字节流,是所有字节输入流的父类。
  2. 因为是父类,它的方法相当于 子类共有。
    • 既然是输入流,主要的方法就是 “读”
方法名称作用描述
abstract int read()从输入流中读取数据的下一个字节
int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
int read(byte[] b, int off, int len)将输入流中最多 len 个数据字节读入 byte 数组,off 表示存时的偏移量
void close()关闭此输入流并释放与该流关联的所有系统资源

4.3 FileInputStream 子类

  1. InputStream子类。针对文件的,文件读取流

    • 作用:打开一个与文件连接的读取流,开始读取数据。
    • 可以理解成:生活中的 “吸管插入水杯里,直接吸取”。
  2. 创建对象:

    • 通过 构造方法
    
    		1.FileInputStream(File file)
    					该文件通过文件系统中的 File 对象 file 指定,打开连接。
    					
    		2.FileInputStream(String pathname);
    				该文件通过文件系统中的路径名 name 指定,打开连接。
    

4.4 BufferedInputStream 子类

  1. 字节缓冲输入流,高效输入流 。

    • 每当缓冲区的数据,读完之后,输入流会再次填充数据缓冲区,直到读完数据。
  2. 创建对象:

    • 通过 构造方法
    
    		 1.BufferedInputStream(InputStream in) 
    				创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。 
    			
    

4.5 字节流输入

4.5.1 练习:字节流的 read() 方法
  1. 前期准备:提前创建文本文件,并输入内容。
    • 注意:检查路径,文件名称,是否正确。
    • IO流会抛出异常。
     public static void main(String[] args) throws IOException {
            //1. 字节流的读取
            File file = new File("D://a.txt");
            method(file);
        }
    
        /**
         * 字节流的读取
         * a.txt 内容
         *      abc
         *      a
         * @param file
         */
        private static void method(File file) throws IOException {
             //1. 使用子类去读取
            FileInputStream fis = new FileInputStream(file);//1.抛出异常
    
            //2.调用read()方法, 注意read()方法是一个阻塞的方法。
            //2.1 直到文件全部读取完,才会返回 -1。 
            int read1 = fis.read();  //2.2 抛出异常
            int read2 = fis.read();
            int read3 = fis.read();
            //TODO 如果我提前关流了,后面还能读么?
            // fis.close();
            int read4 = fis.read();
            int read5 = fis.read();
            int read6 = fis.read();
            int read7 = fis.read();
    
            //3.输出结果。
            System.out.println(read1); // 97
            System.out.println(read2); // 98
            System.out.println(read3); // 99
            System.out.println(read4); // 13
            System.out.println(read5); // 10
            System.out.println(read6); // 97
            System.out.println(read7); // -1
        }
    
    
4.5.2 练习:字节流的读取
  1. 字节流读取需要 三个步骤:

    • 创建字节流读取,开始读取 ,关流。
     public static void main(String[] args) throws IOException {
            //1. 字节流的读取
            File file = new File("D://a.txt");
            method1(file); //循环改造
    
            method2(file); //高效缓冲
        }
    
        /**
         * 字节流 高效读取
         * @param file 文件路径
         */
        private static void method2(File file) throws IOException {
            //创建高效缓冲流对象
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); //多态传入子类
            byte[] buf = new byte[3]; //设置一个缓存,每次输入3个字节
            int b;
            while((b=bis.read(buf))!=-1){
                System.out.println(b);//输出的数据的长度,跟buf大小有关系。
            }
    
            bis.close();//关流
    
        }
    
        /**字节流循环读取
         * @param file 文件
         */
        private static void method1(File file) throws IOException {
            //1.注意3个步骤: 创建输入流对象, 开始读取 , 关闭流。
            FileInputStream fis = new FileInputStream(file);
    
            //2.如果改变成循环读取。
            int b;
            //为什么使用while循环?
            while((b=fis.read())!=-1){ //直到读到-1才结束
                System.out.println(b);
            }
    
            fis.close(); //关流
        }
    
    
  2. 为什么带缓存区的高效读取,效率高 ?

    • 缓冲区:的主要作用就是减少磁盘IO。
    1.因为fileInputStream 每读取一个字节都要访问磁盘。 
    2.bufferedInputStream 不设置的话是默认8k,那就是先读8k的文件到缓冲区(即内存中),然后从内存中读取数据,减少了访问磁盘次数。这样效率就高。
    
    所以,从内存中直接拿,和每次从磁盘中拿,哪个快。
    	   如同生活中的: 用大勺子和小镊子去锅里盛饭。
    

5. 字符流的读取

5.1 概述

  1. 字符流不能像字节流那也处理很广泛,因为字符流输入 底层char[]数组。 常用于处理纯 文本 数据。
    • 但是注意,字符流读写容易出现乱码,需统一编码

5.2 Reader 超类

  1. public abstract class Reader 本身是抽象类,表示 字符流,是所有字符读取流父类
方法名称作用描述
int read()读取单个字符
int read(char[] cbuf)将字符读入数组
abstract int read(char[] cbuf, int off, int len)将字符读入数组的某一部分
abstract void close()关流

5.3 FileReader 子类

  1. 用来读取 字符文件 的便捷类。
    • 此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。
  2. 创建对象:
    • 通过 构造方法
    
    			1.FileReader(String fileName) ; 
    							通过文件系统路径,创建一个新 FileReader2.FileReader(File file) ;
    							通过file 对象解析路径,创建一个新 FileReader

5.4 BufferedReader子类

  1. 高效字符读取流,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
  2. 创建对象:
    • 通过 构造方法
    
    	1.BufferedReader(Reader in) 
              创建一个使用默认大小输入缓冲区的缓冲字符输入流。
    
  3. 在BufferedReader 高效读取流之中 ,有一个方法 一次读取一行,readLine();
    
    	 1. public String readLine(); 一次读取一行,如果到达结尾 返回 null
    
    

5.5 字符流输入

5.5.1 练习:字符流的read()方法
  1. 不同于字节流可以什么都读,只针对 纯文本类型
    • 前期准备: 提前创建文本文件 a.txt,并输入内容 abcd
    • 循环读取,read()方法返回的也是int类型。
  public static void main(String[] args) throws IOException {
        //1. 字符流的读取
        File file = new File("D://a.txt");
        method1(file);
    }

    /**
     * 字符流读取
     * @param file
     */
    private static void method1(File file) throws IOException {
        //1.创建对象
        FileReader fileReader  = new FileReader(file);
        //2.开始读取,返回也是int
        int b;
        while((b=fileReader.read())!=-1){
            System.out.println(b);
        }
        // 3.关流
        fileReader.close();
    }

5.5.1 练习:字符流读取
  1. 高效字符流读取,BufferReader
    • 有自己的定义的方法,也有父类的方法。
    public static void main(String[] args) throws IOException {
            //1. 字符流的读取
            File file = new File("D://a.txt");
            //1.1 高效字符流读取
             method1(file);
    
            //1.2 一次读取一行
            method2(file);
        }
    
        /**
         * 高效字符流读取,一次读取一行。
         * @param file
         */
        private static void method2(File file) throws IOException {
            BufferedReader br = new BufferedReader(new FileReader(file));//多态传入子类
    
    //        //1. 调用方法 readline();
    //        String s = br.readLine();
    //        String s2 = br.readLine();
    //        System.out.println(s);
    //        System.out.println(s2);//没有就返回 null
    
            //2.改变成循环模式
            String line;
            while((line=br.readLine())!=null){
                System.out.println(line);
            }
    
            //3.关流
            br.close();
        }
    
        /**
         * 高效字符流读取
         * @param file
         */
        private static void method1(File file) throws IOException {
            //1.创建对象
            BufferedReader br = new BufferedReader(new FileReader(file));//多态传入子类
            //2.开始读取,返回也是int
            int b;
            while((b=br.read())!=-1){
                System.out.println(b);
            }
            // 3.关流
            br.close();
        }
    
    

6. 字节流的写出

6.1 概述

  1. 字节写出流(Output)。
    • 需要注意的是:读用字节流,写就用字节流写。 不能混合使用。
  2. 写出相当于: 将读取的字节流,写出到磁盘中 的一种 的过程!

6.2 OutputStream 超类

  1. public abstract class OutputStream此抽象类是表示写出字节流的所有类的超类。写出流接受输出字节并将这些字节发送到某个接收器。
    • 既然是写出流,猜一下里面的方法,肯定都是关于写出的!
方法名称作用
void flush()刷新此写出流并强制写出所有缓冲的输出字节
Abstract void write(int b)将指定的字节写入此输出流
void write(byte[ ] b)将b.length个字节从指定的byte数组写入此输出流
void write(byte[ ] b,int off ,int len)将指定byte数组中从偏移量off开始的len个字节写入输出流
void close()关闭此输出流并释放与此流相关的所有系统资源

6.3 FileOutputStream 子类

  1. OutputStream子类。文件写出流,打开一个与文件连接的写出流,开始写出数据。

    • 类似于生活中的 “饮水机倒水的过程 ”。
  2. 创建对象:

    • 使用 构造方法重载。
    
    	       1.FileOutputStream(String name) 
    	          		创建一个向具有指定名称的文件中写入数据的输出文件流。
    	       2.FileOutputStream(File file) 
    	          		创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    	       3.FileOutputStream(File file, boolean append)  
    	          		创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    	       4.FileOutputStream(String name, boolean append) 
    	       			创建一个向指定文件中写入数据。  append是否向源文件中表示追加的意思。
    
    

6.4 BufferedOutputstream 子类

  1. 该类实现缓冲的输出流。属于高效的缓冲流输出。

    • 每次通过缓冲区来读取数据,减少堆底层的访问次数。
  2. 创建对象:

    • 通过构造方法:
    
       1.BufferedOutputStream(OutputStream out);
    						创建一个新的缓冲输出流,用以将数据写入指定的底层输出流。
    

6.5 字节流输出

6.5.1 练习:字节流写出write()方法
  1. 之所以前面没有讲解,标准的关流方式是先让大家熟悉io流。

  2. 流资源的关闭的重要性!标准关闭的写法。

    • 注意:作用域,和 finally{}
     public static void main(String[] args) {
    
            File file = new File("D://a.txt");
            method1(file);
    
        }
    
        /**字节流,写出
         * 标准的关闭写法。
         * @param file
         */
        private static void method1(File file) {
            FileOutputStream fos =null; //作用域
            try{
                //fos = new FileOutputStream(file);
                fos = new FileOutputStream(file,true);//追加,不覆盖。
                fos.write(97);
                fos.write(98);
    
                fos.write("青花瓷".getBytes()); //获取系统默认编码 getBytes("UTF-8")
                fos.write("烟花易冷".getBytes());
                //程序从上往下执行...
                //sout(3/0);发生了错误..
                // fos.close();//无法关流。
    
            } catch (IOException e) {
                e.printStackTrace();
            }finally { //所以使用finally{}一定会执行。
                try {
                    System.out.println("准备关闭");
                    fos.close(); //为什么会报错.. fos变量作用域。
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
6.5.2 练习:字节流写出
  1. 高效缓存写出的使用。
 public static void main(String[] args) {
        File file = new File("D://a.txt");
        method2(file);
    }

    /**
     * 高效写出
     * @param file
     */
    private static void method2(File file) {
        BufferedOutputStream bos =null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(file,true));//追加
            bos.write(97);
            bos.write("印第安的老斑鸠".getBytes()); //转换成当前编码的字节数组输出。
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bos.close(); //关闭流资源
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

7. 字符流的写出

7.1 Writer超类

  1. public abstract class Writer字符流抽象类。是所有字符写出流的父类。
    • 接下来看看父类常用方法:
方法名作用
void write(String str)写入字符串
void write(int c)写入单个字符
void write写入字符数组
abstract void close()关闭此流,但要先刷新它。
abstract void flush()刷新流
abstract void write(char[] cbuf,int off,int len)写入字符数组的某一部分
void write(String str,int off,int len)写入字符串的某一部分

7.2 FileWriter子类

  1. Writer子类,用来写出文件的便捷类,

    • 此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。
  2. 创建对象

    • 使用构造方法:
    
    		1.FileWriter(String filename)
    							根据给定的文件名构造一个FileWriter对象。
    		2.FileWriter(File file) 
    							给一个File对象构造一个FileWriter对象。 FileWriter3.FileWriter(String fileName, boolean append) 
    				构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
    

7.3 BufferedWriter 子类

  1. 加了“buffer”的都是高效的意思。将文本写入字符输出流,缓冲各个字符,从而提供单个字符,数组和字符串的高效写入.有默认大小,也可以指定大小。

  2. 创建对象

    • 使用构造方法:
    
    		1.BufferedWriter(Writer out)
    				创建一个使用默认大小输出缓冲区的缓冲字符输出流。
    		2. BufferedWriter(Writer out, int sz) 
    		 		创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。 
    
  3. 高效缓存写出有一个自己的方法。

    • 可以理解为 换行。
    		newLine()   写一行行分隔符。
    

7.5 字符流输出

7.5.1 字符流写出 write()方法
  1. try{}catch(){}finally()这是一个标准流的结构。

     public static void main(String[] args) {
            File file = new File("D://a.txt");
            method1(file);
        }
    
        private static void method1(File file) {
            FileWriter fileWriter =null;
            try {
                //fileWriter = new FileWriter(file);
                fileWriter = new FileWriter(file,true);//追加
                fileWriter.write("你好,java");
                fileWriter.write(97);
    
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    fileWriter.close(); //关流
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
7.5.2 字符流写出
  1. 高效缓存写出使用。
    • 注意在关闭之前,刷新方法flush()
       public static void main(String[] args) {
            File file = new File("D://a.txt");
            method2(file);
        }
    
        private static void method2(File file) {
            BufferedWriter bw =null;
            try{
                 bw = new BufferedWriter(new FileWriter(file, true));
                bw.write("吃俺老孙一棒!");
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    bw.flush();//刷新
                    bw.close();//关闭
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
    

8. 综合练习:读写练习

8.1 文件复制(一) 字节流普通复制

  1. 文件读入和写出最好都用同一种流。读用字节流,写就用字节流写。
    • 不能混合使用。
    • 例如:同字节流读取,同字符流读取(针对纯文本文件)。
  2. 读取思路如下图:
    在这里插入图片描述
public class IO_Copy {
    public static void main(String[] args) {
        //1.数据源位置
        File file_source = new File("D://10.rar");

        //2.数据源位置
        File file_data = new File("D://a//你好.rar");

        //3. 开始复制
        boolean resault =  copyIOUtils(file_source,file_data);
        System.out.println(resault);

    }

    /**
     * 读取文件内容 字节流
     * @param file_source 数据源
     * @param file_data    目的源
     * @return boolean 返回是否成功
     */
    private static boolean copyIOUtils(File file_source, File file_data) {
        //创建返回标志
        boolean flag = false;
        BufferedInputStream bis=null;
        BufferedOutputStream bos =null;
        try {
            //1.创建输入流 开始读数据源数据
             bis = new BufferedInputStream(new FileInputStream(file_source));
            //3. 创建输出流,开始往写到目的源
             bos = new BufferedOutputStream(new FileOutputStream(file_data));
            //2.开始读取
            int b;
            while((b=bis.read())!=-1){
                //3.1 读进来,然后写出去。
                bos.write(b);
            }
            //执行成功,返回结果
            flag = true;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4.关流自己关自己的。
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return flag; 
    }
}

8.2 文件复制(二) 字节流高效缓存读写复制

  1. 写法升级,自动关流,不需要手动关闭。
    • try(流1;流2){}catch(){} 新用法。jdk1.7之后。
  2. 升级读取。
    • 之前一次读一个字节或者字符,现在一次读一个数组。
 public static void main(String[] args) {
        //1.数据源位置
        File file_source = new File("D://a.txt");

        //2.数据源位置
        File file_data = new File("D://a//你好.txt");

        boolean resault2 = copyIOUtils2(file_source,file_data);
        System.out.println(resault2);
    }

    /**
     *  高效缓存读取。
     *  自动关闭流。
     * @param file_source 数据源
     * @param file_data 目的源
     * @return boolean 返回是否成功
     */
    private static boolean copyIOUtils2(File file_source, File file_data) {
        boolean flag =false;
        //1.自动关闭,需要将流写在try()扩号中。
        try(BufferedInputStream bis =
                     new BufferedInputStream(new FileInputStream(file_source));
            BufferedOutputStream bos =
                    new BufferedOutputStream(new FileOutputStream(file_data))  ){

            //2.开始读写
            byte[] buf = new byte[10];
            int b;
            while((b=bis.read(buf))!=-1){
                //写数据。
                bos.write(buf,0,b);//从0开始有多少写多少
                //bos.write(buf)//直接写出一个字节数组大小。
            }
            //返回true
            flag = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;
    }
 }

8.3 文件复制(三) 字符流高效缓存读写复制

  1. 使用字符流高效读取内容。

    • 只能针对纯文本文档形式
    • 注意使用方法 readline() 和 newline()
     public static void main(String[] args) {
        //1.数据源位置
        File file_source = new File("D://a.txt");
    
        //2.数据源位置
        File file_data = new File("D://a//你好.txt");
    
        boolean resault3 = copyIOUtils3(file_source,file_data);
        System.out.println(resault3);
    
    
    }
    
    /**
     * 字符流高效读取
     * @param file_source
     * @param file_data
     * @return boolean
     */
    private static boolean copyIOUtils3(File file_source, File file_data) {
        boolean falg=false;//初始化返回标志。
        try(BufferedReader br = 
                    new BufferedReader(new FileReader(file_source));
            BufferedWriter bw =
                    new BufferedWriter(new FileWriter(file_data))){
    
            //2.开始读取
            String line;
            while((line=br.readLine())!=null){
                bw.write(line);
                //写完之后需要换行,如果不换号就会都在一起。
                bw.newLine();//换行
            }
    
            falg =true;//复制成功返回true
        } catch (IOException e) {
            e.printStackTrace();
        }
        return falg;
    }
    

9. 扩展练习:复制指定文件,并修改文件后缀

9.1 FilenameFilter 接口使用

  1. 参考接口文件名过滤器,Interface FilenameFilter ,有两种方式实现。
    • 可以使用匿名内部类的形式,
    • 也可以实现接口的形式。
      在这里插入图片描述
方法名称作用
boolean accept(File dir, String name)测试指定文件是否应包含在文件列表中
public class IO_Filter {
    public static void main(String[] args) {
        //1,文件过滤器
        File file = new File("D://计算");

        //文件名过滤器:用来过滤不符合规格的文件名,并返回合格的文件.
        File[] files = file.listFiles( new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) { //dir当前目录,name文件名。
                //写业务逻辑,首先判断是不是文件,防止不是文件的文件夹
                // new File(dir,name).isFile() 防止不是文件。 如上图 1.txt 文件夹。。
                if (new File(dir,name).isFile()&&name.endsWith(".txt")){
                    return true;
                }
                return false;

                //或者   return new File(dir, name).isFile() && name.endsWith(".txt");
            }
        });

        //2.遍历结果
        for (File f:files){
            System.out.println(f);
        }
    }
}

9.2 复制目录并修改文件名

  1. 一个目录里怎么找出只是自己需要的文件!?
    • 注意 目的目录一定要提前创建。
    • 也可以使用NIO Files.copy(source.toPath(), dest.toPath());
public class Test {
    public static void main(String[] args) {
        File path = new File("D:\\a"); //创建分析目录

        //1.使用内部类的方式,文件名过滤器。
        File[] files = path.listFiles(new FilenameFilter() { //文件名过滤器:用来过滤不符合规格的文件名,并返回合格的文件.
            @Override
            public boolean accept(File dir, String name) { //dir 文件当前目录,过滤文件名
                return new File(dir, name).isFile() && name.endsWith(".txt");// 判断。
            }
        });

        //2.打印遍历数组信息
        for (File f:files){
            //System.out.println(f.getName()); 获取文件名。
            //File src = new File(path,f.getName()) //相当于 D://+f.getName(); 即 f
            //3.1 修改文件名
            String new_File = f.getName().replace(".java", ".txt");
            File dest = new File("D:\\b",new_File); //目的地文件目录组合,提前新建,记住一定要有这个目录D:\\b


            //4.创建。读取和写出
            //copy(src,dest);
            copy(f,dest);
        }
    }

    //拷贝和复制
    private static void copy(File src, File dest) {
        try(BufferedInputStream bis =
                        new BufferedInputStream(new FileInputStream(src));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));) {

            //读
            int b =0;
            byte[] buf = new byte[1024];
            while ((b=bis.read(buf))!=-1){
                //写出
                //bos.write(buf); //一次写出 buf数组大小
               bos.write(buf,0,b); //有多少写多少
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴琼老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值