【Java⑬】P353~373(IO概述,字节输出流,字节输入流,字符输出流,字符输入流的使用,流对象的追加续写和换行,JDK7流中异常的处理,通过流复制图片或文本文件)

目录

IO概述(概念&分类)

一切文件数据(文本,图片,视频等)在存储时,都是以二进制数字的形式保存
一切皆为字节
位(bit):一个数字0或一个1,代表一位
字节(Byte):每逢8位是一个字节,这是数据存储的最小单位。
1 Byte = 8bit

按ascii码表对照:
一个英文字符,一个数字都是一个字节

字节码对应结果:
如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
(48-0,65-A,97-a)
如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)

关于编码字节数:
例如:"你好"写入文件转为字节数组时打印为->[-28,-67,-96,-27,-91,-67]
这是因为当前编码格式是 UTF-8,3个字节是一个中文
GBK中2个字节是一个中文

字节输出流抽象类OutputStream类(字节输出流)&FileOutput

当你创建一个流对象时,必须传入一个文件路径。
该路径下,如果没有这个文件,会创建该文件(只能单级创建)。
如果有这个文件,会清空这个文件的数据。(除非当前使用的流对象append为true,这个流对象当前次写入就不会清空而是续写)

  • java.io.OutputStream:字节输出流,此抽象类是表示输出字节流的所有类的超类有6个子类
    定义了一些共性的成员方法:
    public void close() 关闭释放
    public void flush()刷新,强制写出
    写入
    public void write(byte[] b):写入一个字节数组,
    public void write(byte[] b, int off, int len):off数组开始的索引,len写几个字节
    public abstract void write(int b):写入一个字节
    写入是跟在文件原有内容后,不会覆盖

java.io.FileOutputStream extends OutputStream文件字节输出流:往文件中写数据的流
作用:把内存中的数据写入到硬盘的文件中

构造方法
FileOutputStream(String name): 创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(File file): 创建一个向指定File对象表示的文件中写入数据的文件输出流。
参数:写入数据的目的地
String name:目的地是一个文件的路径
File file:目的地是一个文件

构造方法的作用(三个步骤都是自带IO异常):

  1. 创建一个FileOutputStream对象
  2. 会根据构造方法中传递的文件/文件路径,创建一个空的文件
  3. 会把FileOutputStream对象指向创建好的文件

?字节输出流写入数据“你好balabala”到当前模块下a.txt文件;使用write的三种方法

写入数据的原理(内存–>硬盘)
java程序–>JVM(java虚拟机)–>OS(操作系统)–>OS调用写数据的方法–>把数据写入到文件中

字节输出流的使用步骤(重点):
1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
2.调用FileOutputStream对象中的方法write,把数据写入到文件中
3.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)

输出流写入字符的方法:可以使用String类中的方法把字符串,转换为字节数组 byte[] getBytes()把字符串转换为字节数组
字符串.getBytes()是字符串——> 字节数组
Arrays.toString(byte数组)字节数组——> 数组字符串[元素1,元素2,……]
new String(字节or字符数组,) ——>返回值为字符串形式

复习File
构造(String,String)(File,String)(String)

A:-----------------------------------------------------------------------------------------------------------

注意:流的创建,写入,关闭。 三步都有异常,需要抛出或者处理。
文件路径如果写相对路径,即当前项目下,直接"模块名\\文件名"

	public static void main(String[] args)throws IOException {
        FileOutputStream fos = new FileOutputStream("17apr\\a.txt");
        fos.write("你好balabala".getBytes());//字符串转字节数组
        fos.close();
    }

在这里插入图片描述

  /*字节输出流写入数据“你好balabala”到当前模块下a.txt文件,使用三种方法*/
        /*File三种构造,注意:File类创建是不检查路径的,可以通过exists()检查是否存在*/
        File f1 = new File("18apr\\a");
        File f2 = new File("18apr","b");
        File f3 = new File(f1,"aa");

        System.out.println(f1.exists());
        System.out.println(f2.exists());
        System.out.println(f3.exists());
        f1.mkdir();//File类型的单层创建; mkidrs//多级创建。都是创建目录
        f2.createNewFile();//创建文件类型
        f3.createNewFile();
        System.out.println(f1.exists());
        System.out.println(f2.exists());
        System.out.println(f3.exists());
		//创建流
        FileOutputStream fos1 = new FileOutputStream(f3);
        FileOutputStream fos2 = new FileOutputStream("18apr\\demo02Out_1.txt");
        //字节输出的三种
        byte[] b1 = new byte[]{48, 49, 50, 51, 52};

//①写出一个字节
        fos1.write(48);//往f3输出
//②写出字节数组,上一问就是 
        fos1.write(b1);//结果:a/aa中"001234"因为输出了两次,第一次ascii48对应字节是"0"
//③写出字节数组的一部分
        fos2.write(b1, 1, 3);//demo02Out_1.txt中"123"

注意:若已经close,则写入操作无事发生
优化,一次写入一个字节,但循环写入

     byte[] b1 = "你好balabala".getBytes();

     for(int i=0; i < b1.length; i++){
         fos1.write(b1[i]);
     }

文件存储的原理和记事本打开文件的原理

A:-----------------------------------------------------------------------------------------------------------
写数据的时候,会把10进制的整数,转换为二进制的整数。
fos.write(1100001);97–>1100001

硬盘中存储的数据都是字节,1个字节=8个比特位(10101010)
任意的文本编辑器(记事本,notepad++……)在打开文件的时候,都会查询编码表,把字节转换为字符表示
0-127:查询ascii码表
其他值:查询系统默认码表(中文系统GBK)

在这里插入图片描述

?字节输出流的续写和换行①覆盖原文件写法②不覆盖原文件而是追加续写并换行

例如上一问写的代码(new FileOutputStream部分)如果是运行第二次会创建一个流对象,把数据再写一次,且覆盖原文件,无法续写,且没有换行。

  • 追加写/续写:使用两个参数的构造方法
    FileOutputStream(String name, boolean append)创建一个向具有指定name的文件中写入数据的输出文件流
    FileOutputStream(File file, boolean append)创建一个向指定File对象表示的文件 中写入数据的文件输出流
    参数 name,file:写入数据的目的地
    append 追加写开关
    true: 创建对象不会覆盖源文件,继续在文件的末尾追加写数据
    false: 创建一个新文件,覆盖源文件

注意:需要指向该文件的当前流对象有append且为true,才能成功续写。 而同一个流对象多次使用writer写入,是不覆盖上行write写入的,本身就在续写。
而此处的追加续写指的是 第二次写入即重复运行程序 或 不同流对象指向同一个文件后对所指向文件的续写
运行一次的同一个流对象的多行write写入是同一次写入,跟追加续写无关

例:两个流指向同一个文件,第一个流append为false写入“11111”,第二个流true写入“222222”。只能续写一轮
运行一次后:11111222222
运行两次后:11111222222 因为第二次运行时第一个流为false覆盖了原文件

  • 换行:写换行符号
    windows:\r\n
    Linux:/n
    mac:/r
    A:-----------------------------------------------------------------------------------------------------------
    ① 正常写入
		FileOutputStream fos1 = new FileOutputStream("17apr\\b.txt");
        fos1.write(48);
        fos1.write(48);

在这里插入图片描述
②再次创建流指向相同文件输出,new FileOutputStream同目的地,将会覆盖源文件。即没有append和append为false是一样的,都会清空该路径文件的原有数据

		FileOutputStream fos1 = new FileOutputStream("17apr\\b.txt");
        fos1.write(48);
        fos1.write(48);
        FileOutputStream fos2 = new FileOutputStream("17apr\\b.txt");
        fos2.write(49);
        fos2.write(49);

在这里插入图片描述
③文件输出流的目的地重合但不覆盖源文件,并换行追加
在构造输出流时,false指不可追加,true就是可追加

		//覆盖源文件
        FileOutputStream fos1 = new FileOutputStream("18apr\\b.txt");
        fos1.write(48);
        fos1.write(48);
        FileOutputStream fos2 = new FileOutputStream("18apr\\b.txt");
        fos2.write(49);
        fos2.write(49);
        //文件输出流的目的地重合但不覆盖源文件,并换行追加
        FileOutputStream fos3 = new FileOutputStream("18apr\\b.txt", true);
        fos3.write("\r\nwinner~\r\nBarbie!".getBytes());
        //参数false,依旧覆盖源文件
        FileOutputStream fos4 = new FileOutputStream("18apr\\b.txt", false);
        fos4.write("全部覆盖".getBytes());
        

在这里插入图片描述
在这里插入图片描述


例2

/*?字节输出流的续写和换行①覆盖原文件写法②不覆盖原文件而是追加续写并换行*/
        //续写,例如之前的代码反复运行多次,都是一样的结果。即第二次创建流再输出,会覆盖流指向的文件内容
        //加入续写后,不管代码重复运行多次,也是追加而非覆盖
        FileOutputStream fos1 = new FileOutputStream("18apr\\c1.txt", true);
        FileOutputStream fos2 = new FileOutputStream("18apr\\c2.txt", false);
        fos1.write("\r\nBarbie".getBytes());//和fos输出给f3的一样,往demo02Out_1输出
        fos2.write("K\r\nen".getBytes());

运行3次后结果

在这里插入图片描述
在这里插入图片描述

?★ 字节输出流,考虑两个流指向同一个路径,但两个appen各自是全true,全false,false-true,true-false。运行1次和5次后的情况

关于覆盖追加的四种情况
如果两个流指向同一个文件路径

流1,流2的append第一次运行后文件内容运行5次后
false,false流2的内容流2
true, false流2(每次运行前结果都先为:流2流1)-> 流2
false,true流1流2流1流2
true, true流1流2流1流2流1流2流1流2流1流2流1流2

June5是当下项目下的当前模块名,类似这个20sep。项目是空白项目创建java模块为June5

,类似这个20sep

 private static void demo02() throws IOException {
        //1. append为false,或无append
        FileOutputStream fos1 = new FileOutputStream("June5\\demo01_2", false);//当前木块名\\是相对路径
        fos1.write("1-1 false write".getBytes());//被覆盖
        FileOutputStream fos2 = new FileOutputStream("June5\\demo01_2");
        fos2.write("1-2 no append write ".getBytes());
        fos1.close();
        fos2.close();

        //2. 指向同一路径的不同流对象append为false,但最后一次为true
        FileOutputStream fos3 = new FileOutputStream("June5\\demo01_3", true);
        fos3.write("2-1 true write".getBytes());//被覆盖
        FileOutputStream fos4 = new FileOutputStream("June5\\demo01_3", false);
        fos4.write("2-2 false write".getBytes());
        fos3.close();
        fos4.close();

        //3. 指向同一路径的不同流对象append为true,但最后一次为false
        FileOutputStream fos5 = new FileOutputStream("June5\\demo01_4",false);
        fos5.write("3-1 falsel write".getBytes());
        FileOutputStream fos6 = new FileOutputStream("June5\\demo01_4", true);
        fos6.write("3-2 true write\r\n".getBytes());//追加成功
        fos5.close();
        fos6.close();

        //4.两个流都是true
        FileOutputStream fos7 = new FileOutputStream("June5\\demo01_5",true);
        fos7.write("4-1 true write".getBytes());
        FileOutputStream fos8 = new FileOutputStream("June5\\demo01_5", true);
        fos8.write("4-2 true write\n".getBytes());
        fos7.close();
        fos8.close();

    }

运行1次4个文件的预期内容:

demo01_2: 1-2 no append write
demo01_3: 2-2 false write
demo01_4: 3-1 falsel write3-2 true write
demo01_5: 4-1 true write4-2 true write

原因分析:
fos1,fos2指向同一个路径demo01_2的不同流,fos2的无append(和append为false一样),是创建一个新的文件,所以会覆盖掉fos1先写入的内容。
fos3,4是指向同一个路径demo01_3的不同流,fos4的append为false,所以覆盖fos3的写入数据。
fos5,6是指向同一个路径demo01_4的不同流,fos6的append为true,续写。
fos7,8是指向同一个路径demo01_5的不同流,fos8的append为true,续写。

运行2次预期结果:

demo01_2: 1-2 no append write
demo01_3: 2-2 false write
demo01_4: 3-1 falsel write3-2 true write
demo01_5: 
4-1 true write4-2 true write
4-1 true write4-2 true write

原因分析:
fos1是false,创建一个新文件demo01_2,覆盖掉源文件,1-1 false write。但fos2 无append ,继续覆盖。
fos3是true,在文件demo01_3末尾追加数据false write2-1 true write,但fos4append为false,会覆盖。
fos5是false,覆盖并创建新的demo01_4,3-1 falsel write,fos6是true,追加续写。
fos7是true,追加续写4-1 true write4-2 true write4-1 true write,fos8是true,追加续写。

运行5次后结果:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结:
是否覆盖或续写,取决于本次写入前最近一次指向该文件路径的流的append,没赋值或赋值false,就覆盖。apeend为true就追加续写

字节输入流InputStream类&FileInputStream

java.io.InputStream此抽象类是所有字节输入流的超类
定义了所有子类共性的方法:
int read()从输入流中读取数据的下一个字节。
int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。
void close()关闭输入流并释放与该流关联的所有系统资源。

java.io.FileInputStream extends InputStream

FileInputStream:文件字节输入流
作用把硬盘文件中的数据,读取到内存中使用

构造方法:
FileInputStream(String name):创建一个向具有指定名称的文件中读取数据的文件输入流。
FileInputStream(File File):创建一个向指定File对象表示的文件中读取数据的文件输入流
参数:读取文件的数据源
String name:文件的路径
File file :文件

构造方法的作用:
1.会创建一个FileInputStream对象
2.会把FileInputStream对象指定构造方法中要读取的文件

字节输入流一次读取一个字节的原理

创建文件输入流是有一个指针指向文件的第一个字节,每次读取返回指针当前所指位置,然后指针向后移,当指向结束标记时返回-1。

字节输入流一次读取多个字节原理

int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。
明确两件事:
1. 方法的参数byte[]的作用?
起到缓冲作用,存储每次读取到的多个字节
数组的长度一般定义为1024(1kb)或者1024的整数倍
2. 方法的返回值int是什么?
每次读取的有效字节个数

常用方法:String类的构造方法:
String(byte[] bytes):把字节数组转换为字符串
String(byte[] bytes, int offset, int length):把字节数组的一部分转换为字符串
offset:数组的开始索引
length:转换的字节个数

原理:
如果每次以两个字节长度读取,需要读取多次,读“ABCDE”时每次读到的分别为 AB, CD ,ED。因为每次读到都是存到这个长度2的byte数组里,新读到的覆盖数组旧的数据然后最后一次只读到E,[1]保留了上次读取的D
也是使用循环优化。while,读到-1结束
只转有效的字节个数

?字节输入流使用File构造,①从b.txt中一次读一个字节,②从c.txt中一次读3个字节

读取原理(硬盘–>内存)

java程序-->JVM-->OS-->OS读取数据的方法-->读取文件

字节输入流的使用步骤(重点):
1.创建FileInputStream对象,构造方法中绑定要读取的数据源
2.使用FileInputStream对象中的方法read,读取文件
3.释放资源
int read()读取文件中的一个字节并返回,读取到文件的末尾返回-1
,重复读还是-1
使用循环优化

注意:因为read方法读完以后内部指针会指向下一个字节,所以最好是用int接收一下,不然先判断再输出,两次read指针位置已经变了
A:-----------------------------------------------------------------------------------------------------------

    private static void demo01() throws IOException{
        FileInputStream fis1 = new FileInputStream("july08\\demo01_2");
        FileInputStream fis2 = new FileInputStream("july08\\demo01_5");
        System.out.println("--------------每个文件读三次----------");
        int len1 = 0;//获取每次读到的字节
        int len2 = 0;//获取读到字节的有效个数

        byte[] b1 = new byte[3];//用长度为3的字节数组接收

        /*每个文件读三次*/
        len1 = fis1.read();//只读一次
        System.out.print((char)len1);
        len1 = fis1.read();//只读一次
        System.out.print((char)len1);
        len1 = fis1.read();//只读一次
        System.out.println((char)len1);

        len2 = fis2.read(b1);
        System.out.print(new String(b1));
        len2 = fis2.read(b1);
        System.out.print(new String(b1));
        len2 = fis2.read(b1);
        System.out.println(new String(b1));


//        /*循环一直读,读到文件结束*/
        System.out.println("--------------循环一直读,读到文件结束--------------");
        System.out.println("demo01_2:");
        while((len1 = fis1.read()) != -1){
            System.out.print((char)len1);//获取到的字节强转字符
        }

        System.out.println("demo01_5:");
        while((len2 = fis2.read(b1)) != -1){

//            System.out.println(b1);//是数组地址//[B@19e1023e
//            System.out.println(Arrays.toString(b1));//打印字节数组元素,第一次为//[52, 45, 49]
            System.out.print(new String(b1,0,len2));
            //★把字节数组转为字符型可以按照字符串格式打印,并且按照有效长度转字符串并打印。避免多输出上次读取到的无效数据
        }

    }

★?练习_文件复制(①一次复制一个字节,②使用数组缓冲一次复制多个字节)

数组大小一般是1024的整数倍
文件复制:一读一写
因为jpg,txt,avi,mp3等等都是字节方式存储。
其实就是利用流,先input读到,然后output写出
例如:数据源:为FileInputStream的路径
数据的目的地:为FileOutputStream的路径

文件复制的步骤:

  1. 创建一个字节输入流对象,构造方法中绑定要读取的数据源
  2. 创建一个字节输出流对象,构造方法中绑定要写入的目的地
  3. 使用字节输入流对象中的方法read读取文件
  4. 使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
  5. 释放资源 (先关闭写,后关闭读。如果写完了,肯定读取完毕了)

测试程序效率 long e = System.currentTimeMillis();后- 前 毫秒
A:-----------------------------------------------------------------------------------------------------------
一次读一个b1数组的长度3

public class Demo01CopyFile {
    public static void main(String[] args) throws IOException {
        //复制一个图片文件
        /*★?练习_文件复制(①一次复制一个字节,②使用数组缓冲一次复制多个字节)*/
        //复制图片C:\Users\hasee\Pictures\Camera Roll\头像\20220612034131_fd301.jpg
        //到当前项目下
        //思路①先输入,再输出②fis创建一个输入流,
        long e1 = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("C:\\Users\\hasee\\Pictures\\Camera Roll\\头像\\20220612034131_fd301.jpg");
        FileOutputStream fos = new FileOutputStream("18apr" + File.separator + "头像.jpg", true);//允许追加
        int len = 0;
        byte[] b1 = new byte[3];//668ms,624ms
        //byte[] b1 = new byte[1024];//124ms,37ms,28ms

        while((len = fis.read(b1)) != -1){
            fos.write(b1);
        }
        fos.close();//先关写,再关读
        fis.close();//写完了肯定已经读完了
        long e2 = System.currentTimeMillis();
        System.out.println("用时毫秒" + (e2 - e1));
    }
}
  • ①一次复制一个字节

    private static void demo02() throws IOException{
        //把外部的一张图片复制到当前项目下
        FileInputStream fis1 = new FileInputStream("C:\\Users\\hasee\\Pictures\\Camera Roll\\头像\\20220612034131_fd301.jpg");
        FileOutputStream fos1 = new FileOutputStream("july08\\复制图片1.jpeg");
        int len = 0;
        while((len = fis1.read()) != -1){
            fos1.write(len);
        }
        fos1.close();
        fis1.close();
    }
  • 优化:②使用数组缓冲一次复制多个字节,避免读到数组尾部的上次读取的无效数据
	private static void demo03() throws IOException{
        FileInputStream fis1 = new FileInputStream("july08\\src\\demo03文本文件.txt");//ABCDE
        FileOutputStream fos1 = new FileOutputStream("july08\\demo03_2读文件.txt");
        int len = 0;
        byte[] b1 = new byte[3];
        while((len = fis1.read(b1)) != -1){
            //如何防止读到b1中的无效数据(上轮读取到的)
            //fos1.write(b1);//demo03_2读文件:ABCDEC
            fos1.write(b1,0,len);//★用write的三参数//demo03_2读文件:ABCDE
        }
        fos1.close();
        fis1.close();
    }

字符流

使用字节流读取中文的问题

使用字节流读取中文文件
1个中文
GBK:占用两个字节
UTF-8:占用三个字节
字节流可能会产生乱码,因为对于中文每次读取1/3个字符

  • 例如对于"你好balabala"字符串如果只取字节数组位置[2,5]的字节会乱码如下(GBK型)
// fos1.write(b1, 2, 4);//�好

例如字节流读取这句“jdk1.7前,第一次写入数据”在第一个中文处产生乱码。
此时右下角显示utf-8

	 private static void demo02() {
        //②jdk1.7之后,无需finally,try中内容结束时自动释放流对象
        try (FileOutputStream fos = new FileOutputStream("july19\\src\\readable03.txt", true);
             FileInputStream fis = new FileInputStream("july19\\src\\readable03.txt")) {
            fos.write("jdk1.7后,第二次写入数据".getBytes());
            int len = 0;
            while((len = fis.read()) != -1) {
                System.out.print(len+" ");
                System.out.println((char)len);
            }
        }catch (IOException e){
            e.getMessage();
        }
    }

产生乱码
在这里插入图片描述

字符输入流_Reader类&FileReader类介绍

java.io.Reader字符输入流,是字符输入流的最顶层的超类,定义了一些共性的成员方法,是一个抽象类
共性的成员方法:
int read() 读取单个字符并返回。
int read(char[] cbuf)一次读取多个字符,将字符读入数组。
void close()关闭该流并释放与之关联的所有资源。

java.io.FileReader extends InputStreamReader extends Reader

FileReader:文件字符输入流
作用: 把硬盘文件中的数据以字符的方式读取到内存中
构造方法:

FileReader(String fileName)
FileReader(File file)

参数: 读取文件的数据源
String fileName:文件的路径
File file:一个文件
FileReader构造方法的作用:

  1. 创建一个FileReader对象
  2. 会把FileReader对象指向要读取的文件

字符输入流读取字符数据

字符输入流的使用步骤:
1.创建FileReader对象,构造方法中绑定要读取的数据源
2.使用FileReader对象中的方法read读取文件
3.释放资源

读到结尾结束也是返回-1
读到后可以打印强转为char
int read()读取单个字符并返回
int read(char[] cbuf)一次读取多个字符,将字符读入数组。
char[] cbuf = new char[1024];存储读取到的多个字符
int len记录的是每次读取的有效字符个数

字符输出流_Writer类&FileWriter类介绍

java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类
共性的成员方法:
void write(int c)写入单个字符
void write(char[] cbuf)写入字符数组
abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
void write(String str)写入字符串
void write(String str, int off, int len)写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
void flush()刷新该流的缓冲。
void close()关闭此流,但要先刷新它。
java.io.Writer是FileWriter的父类

java.io.FileWriter extends OuputStreamWriter extends Writer

FileWriter:文件字符输出流
作用:把内存中字符数据写入到文件中
构造方法:
FileWriter(File file)根据给定的File对象构造一个FileWriter对象。
FileWriter(String fileName)根据给定的文件名构造一个FileWriter对象。
参数:写入数据的目的地
String fileName:文件的路径
File file:是一个文件
构造方法作用:
1.会创建一个FileWriter对象
2.会根据构造方法中传递的文件/文件的路径,创建文件
3.会把FileWriter对象指向创建好的文件

字符输出流的使用步骤

  1. 创建FileWriter对象,构造方法中绑定要写入数据的目的地
  2. 使用FileWriter中的方法write,把数据写入到内存缓冲区(字符转换为字节的过程)
  3. ★使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中★
  4. 释放资源(会先把内存缓冲区中的数据刷新到文件中)
    close和flush都会把缓冲区内容刷到文件中

flush方法和close方法的区别

flush:刷新缓冲区,流对象可以继续使用
close:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

JDK7和JDK9流中异常的处理

  1. jdk1.7之前使用try catch finally处理流中的异常
  • 格式:
try{
	可能会产出异常的代码
}catch(异常类变量 变量名){
	异常的处理逻辑
}finally{
	一定会执行的代码
	资源释放
}

流中都是IOException,关闭流必须在finally中,因为如果try中出现异常中断不会执行最后一句资源释放。但流作用域必须在try之前,finaly中才能访问到。
只能提高fw的作用域在try之前,但又因为fw没有赋值finally资源释放照样报错,即变量在定义的时候,可以没有值,但是使用的时候必须有值。所以复制给空。
但是空不可调用close,异常,在finally中也是,所以finally中嵌套一个try catch。
但close除了IO异常,还有空指针异常,需要增加一个判断只有不为空的时候才释放资源

总结:非常麻烦
2. JDK7 新特性

在try的后边增加一个(),在括号中可以定义流对象
那么这个流对象的作用域就在try{}中有效
try中的代码执行完毕后,会自动会流对象释放,不用写finally

  • 格式:
try(定义流对象;定义流对象……){
可能会产生异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}

不用写finally,try()定义的流作用域只在try{}内有效
3. JDK9新特性
try之前可以定义流对象,在try后边的()中可以直接引入流对象的名称(变量名) 在try代码执行完毕之后,流对象也可以自动释放掉,不用写finally释放

格式:
A a = new A();
B b = new B();//有异常还是要抛出
try(a;b){
可能会出现异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}

?字符输入流,一次读一个字符;一次读多个字符


注意:

  1. 同一个流对象,已经read()读完了,再read(ch1)就读不出了,因为这个流已经读到对应文件结束了
  2. 注意read方法始终是int返回值,无参时获取读到字符的int类型ascii码值,有参数时获取本次给数组中读取到的字符有效个数。都是读到-1结束
  3. 注意区分两种输出方式① System.out.print(new String(ch1, 0, len));//把获取的字符数组,按字符串形式打印 ②System.out.println(Arrays.toString(ch1));//按数组形式打印
    例如一次读三个字符:①给De②[给, D, e] //mo0[m, o, 0]
    private static void demo01() throws IOException {
        FileReader fr1 = new FileReader("july16\\src\\Readable02");
        int len = 0;//获取读到的字符,以Ascii码的int形式获取
        //一次读一个字符
//        while((len = fr1.read()) != -1) {
//            System.out.print((char) len);//给Demo02-demo01文件字符输入流读的
//        }// ●这次循环读完文件内容,后续同一个流对象就读不出了,因为已经读完了。

        //一次读多个字符
        len = 0;
        char[] ch1 = new char[3];
        while((len = fr1.read(ch1)) != -1){// ●此处len不再是接收读到的字符,而是返回一次读多个字符的有效字符个数
            System.out.print(new String(ch1, 0, len));//把获取的字符数组,按字符串形式打印
            //给Demo02-demo01文件字符输入流读的

            //两种输出方式的区别
            //给De[给, D, e]
            //mo0[m, o, 0]
            
            System.out.println(Arrays.toString(ch1));//打印成[,]的形式的字符数组
            //[给, D, e]
            //[m, o, 0]
            //[2, -, d]
            //[e, m, o]
            //[0, 1, 文]
            //[件, 字, 符]
            //[输, 入, 流]
            //[读, 的, 流]
        }
    }

?字符输出流的基本使用_ ?字符输出流写数据的5种方法(写入字符数组一部分)


写入字符数组,一部分。写入字符创,一部分。off,len开始位置,长度

	private static void demo02() throws IOException{
        FileWriter fw1 = new FileWriter("july18\\src\\writable02");//输出时,文件不存在会自动创建,但是只能单级创建
        //字符输出流肯定是写入字符
        // ①写入一个字符
        fw1.write(48);
        fw1.write(65);
        fw1.write(97);
        fw1.write('\n');//换行符

        //②写入一个字符数组What are you looking at?
        char[] ch1 = new String("What are you looking at?\n").toCharArray();
        fw1.write(ch1);
        //③写入一个字符数组的一部分
        fw1.write(ch1, 9, ch1.length-9);
        //④写入一个字符串
        String str1 = new String("写入本字符串~");
        fw1.write(str1);
        //⑤写入一个字符串的一部分
        fw1.write(str1, 2,4);
        /*该句之前,文件内容没有任何写入*/
        fw1.flush();
        fw1.close();
    }

?字符输出流的续写和换行,①第一次运行成功续写,多次运行续写不成功

②第一轮运行成功续写,多轮运行也成功
③单轮多次都不成功续写
④没有单轮运行续写失败,多轮成功的情况。因为多轮运行先续写成功,随后的当前轮的第二次运行中就覆盖第一次写入的数据
每轮运行最后一次写入文件时,末尾换行


复习:
boolean append:续写开关
true:不会创建新的文件覆盖源文件,可以续写;false:创建新的文件覆盖源文件

换行:符号
windows:\r\n
linux:/n
mac:/r

    private static void demo03() throws IOException{
        //①
        FileWriter fw1 = new FileWriter("july18\\src\\writable02_3_1.txt");
        FileWriter fw2= new FileWriter("july18\\src\\writable02_3_1.txt",true);
        fw1.write("null");
        fw2.write("true\n");

        //②
        FileWriter fw3 = new FileWriter("july18\\src\\writable02_3_2.txt",true);
        FileWriter fw4= new FileWriter("july18\\src\\writable02_3_2.txt",true);
        fw3.write("true");
        fw4.write("true\n");

        //③
        FileWriter fw5 = new FileWriter("july18\\src\\writable02_3_3.txt",false);
        FileWriter fw6 = new FileWriter("july18\\src\\writable02_3_3.txt");
        fw5.write("false");
        fw6.write("null\n");

        //④
        FileWriter fw7 = new FileWriter("july18\\src\\writable02_3_4.txt",true);
        FileWriter fw8 = new FileWriter("july18\\src\\writable02_3_4.txt",false);
        fw7.write("true");
        fw8.write("false\n");

        fw1.flush();
        fw2.flush();
        fw3.flush();
        fw4.flush();
        fw5.flush();
        fw6.flush();
        fw7.flush();
        fw8.flush();

    }

运行三轮结果:

writable02_3_1.txt:
nulltrue

writable02_3_2.txt:
truetrue
truetrue
truetrue

writable02_3_3.txt:
null

writable02_3_4.txt:
false

?使用try_catch_finally处理流中的异常,3种不同方式

①JDK1.7之前处理流异常;②JDK1.7之后处理流异常③JDK1.9之后

    /*③JDK1.9之后,很鸡肋,单独在外部定义流对象还是要抛出异常或者单独处理*/
    private static void demo03() throws IOException{
        System.out.println("--------------jdk1.9后-------------");
        //③jdk1.9之后

        FileWriter fw2 = new FileWriter("july19\\src\\readable03.txt", true);
        FileReader fr1 = new FileReader("july19\\src\\readable03.txt");
        //还要抛出异常,根本没意义。或者用jdk1.7之前的写一大堆包含finally的,或者用jdk1.7之后的直接定义在try()里

        try(fw2;fr1) {
            fw2.write("jdk1.9后,第三次写入数据\n");
            fw2.flush();
            char[] ch1 = new char[4];
            int len = 0;
            while((len = fr1.read(ch1)) != -1){
                System.out.print(new String(ch1, 0, len));//new String的返回值就是字符串本身
            }
        }catch(IOException e){
            e.toString();
        }
    }
    /*②JDK1.7之后处理流异常*/
    private static void demo02() {
        System.out.println("--------------jdk1.7后-------------");

        //②jdk1.7之后,无需finally,try中内容结束时自动释放流对象
        try (FileOutputStream fos = new FileOutputStream("july19\\src\\readable03.txt", true);
             FileInputStream fis = new FileInputStream("july19\\src\\readable03.txt")) {
            fos.write("jdk1.7后,第二次写入数据\n".getBytes());
            int len = 0;
            while((len = fis.read()) != -1) {
                System.out.print(len+" ");
                System.out.print((char)len);
            }
        }catch (IOException e){
            e.getMessage();
        }
    }



    /*使用try_catch_finally处理流中的异常①JDK1.7之前处理流异常;*/
    private static void demo01() {
        System.out.println("--------------jdk1.7前-------------");

        //以字符输出流为例
        //①jdk1.7之前,麻烦在close方法需要在finally中必然执行,但自身又有流异常还有空指针异常。都需要处理
        FileWriter fw1 = null;
        try{
            fw1 = new FileWriter("july19\\src\\readable03.txt", true);
            fw1.write("jdk1.7前,第一次写入数据\n");
            fw1.flush();
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(fw1 != null){
                try {
                    fw1.close();
                }catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
        
    }

在这里插入图片描述

在这里插入图片描述

?复制图片文件:①字节流②字符流


    private static void demo04() throws IOException{
        //字节流
        //C:\Users\hasee\Pictures\Camera Roll\电影电视剧截图\公主.PNG
        FileInputStream fis1 = new FileInputStream("C:\\Users\\hasee\\Pictures\\Camera Roll\\电影电视剧截图\\公主.PNG");
        FileOutputStream fos1 = new FileOutputStream("july19\\src\\复制图1.PNG");//从外面读,读完写入当前项目模块路径下
        int len = 0;
        while((len = fis1.read()) != -1){
            fos1.write(len);//一次一个字节的复制
        }
        //字符流
        //C:\Users\hasee\Pictures\Camera Roll\电影电视剧截图\公主2.PNG
//        FileReader fr1 = new FileReader("C:\\Users\\hasee\\Pictures\\Camera Roll\\电影电视剧截图\\公主2.PNG");
//        FileWriter fw1 = new FileWriter("july19\\src\\复制图2.PNG");
//        int len2 ;
//        while((len2 = fr1.read()) != -1){
//            fw1.write(len2);//一次一个字符的复制
//            fw1.flush();
//        }
        //不成功,解码出错。跳过


    }

注意

传输图片用字节流,
传输文本文件,内都为字符,用字符流
jdk1.7之后的流异常处理常用,其他两个鸡肋

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值