目录
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异常):
- 创建一个FileOutputStream对象
- 会根据构造方法中传递的文件/文件路径,创建一个空的文件
- 会把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

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的路径
文件复制的步骤:
- 创建一个字节输入流对象,构造方法中绑定要读取的数据源
- 创建一个字节输出流对象,构造方法中绑定要写入的目的地
- 使用字节输入流对象中的方法read读取文件
- 使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
- 释放资源 (先关闭写,后关闭读。如果写完了,肯定读取完毕了)
测试程序效率 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构造方法的作用:
- 创建一个FileReader对象
- 会把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对象指向创建好的文件
字符输出流的使用步骤
- 创建FileWriter对象,构造方法中绑定要写入数据的目的地
- 使用FileWriter中的方法write,把数据写入到内存缓冲区(字符转换为字节的过程)
- ★使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中★
- 释放资源(会先把内存缓冲区中的数据刷新到文件中)
close和flush都会把缓冲区内容刷到文件中
flush方法和close方法的区别
flush:刷新缓冲区,流对象可以继续使用
close:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
JDK7和JDK9流中异常的处理
- 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(异常类变量 变量名){
异常的处理逻辑
}
?字符输入流,一次读一个字符;一次读多个字符
注意:
- 同一个流对象,已经read()读完了,再read(ch1)就读不出了,因为这个流已经读到对应文件结束了
- 注意read方法始终是int返回值,无参时获取读到字符的int类型ascii码值,有参数时获取本次给数组中读取到的字符有效个数。都是读到-1结束。
- 注意区分两种输出方式①
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之后的流异常处理常用,其他两个鸡肋
8337

被折叠的 条评论
为什么被折叠?



