IO流(非常重要)
概述
IO流的分类
总体来看,只是传输时的单位不同
IO流的体系
字节输入流:FileInputStream
读单个字节
InputStream r=new FileInputStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\a.txt");
int b;//记录每次读取返回的字节数
while ((b=r.read())!=-1){
System.out.print((char) b);
}
//最后一定要关闭资源
r.close();
读多个字节(字节数组)
InputStream r=new FileInputStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\a.txt");
int b;//记录每次读取返回的字节数
byte[] buffer=new byte[3];//每次读到的字节都放在这个“桶”中
while ((b=r.read(buffer))!=-1){
//记得此处解码时,一定要指明起始,即string的构造器中new String(字节数组,0,每次读的字节数)
//即读多少,往外道多少
String s = new String(buffer, 0, b);
System.out.print(s);
}
//最后一定要关闭资源
r.close();
一次读完全部字节
第一种方式:
InputStream r=new FileInputStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\a.txt");
File file=new File("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\a.txt");
long length = file.length();
byte[] buffer=new byte[(int) length];//每次读到的字节都放在这个“桶”中
r.read(buffer);
System.out.println(new String(buffer));
//最后一定要关闭资源
r.close();
第二种方式
InputStream r=new FileInputStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\a.txt");
byte[] bytes = r.readAllBytes();
System.out.println(new String(bytes));
//最后一定要关闭资源
r.close();
字节输出流:FileOutputStream
//FileOutputStream构造器第二个参数表示对这个文件是否为追加操作
OutputStream os=new FileOutputStream("b.txt",true);
os.write(97);//97表示一个字节
os.write('h');//每次只能写入一个字节
// os.write('嘿');//每次只能写入一个字节,而汉字在utf-8中是3个字节,所以会出现乱码
//每次写入一个字节数组
os.write("我爱你中国".getBytes());
//每次写入指定写入的字节范围,一个汉字3个字节
os.write("哈哈哈哈".getBytes(),0,6);
//写入一个换行符,\r\n是每个系统通用的换行符
os.write("\r\n".getBytes());
os.close();
文件复制
// 需求:复制照片。
// 1、创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("D:/resource/meinv.png");
// 2、创建一个字节输出流管道与目标文件接通。
OutputStream os = new FileOutputStream("C:/data/meinv.png");
// 3、创建一个字节数组,负责转移字节数据。
byte[] buffer = new byte[1024]; // 1KB.
// 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len; // 记住每次读取了多少个字节。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
os.close();
is.close();
System.out.println("复制完成!!");
资源释放
InputStream is=null;
OutputStream os=null;
try {
// 1、创建一个字节输入流管道与源文件接通
is= new FileInputStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\a.txt");
// 2、创建一个字节输出流管道与目标文件接通。
os= new FileOutputStream("F:\\JavaEE\\JavaEE\\b.txt");
// 3、创建一个字节数组,负责转移字节数据。
byte[] buffer = new byte[1024]; // 1KB.
// 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len; // 记住每次读取了多少个字节。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (os!=null)os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (is!=null)is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
该种方式不建议使用,建议采用JDK7之后的try-catch-resource
try ( // 1、创建一个字节输入流管道与源文件接通
InputStream is= new FileInputStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\a.txt");
// 2、创建一个字节输出流管道与目标文件接通。
OutputStream os= new FileOutputStream("F:\\JavaEE\\JavaEE\\b.txt");
//注意:这里只能放置资源对象(流对象)
//像int age =21;就不行
//什么是资源?资源都是会实现AutoCloseable接口。资源都会有一个close方法,并且资源放到这里后
//用完之后,会被自动调用他的close方法来释放资源
){
// 3、创建一个字节数组,负责转移字节数据。
byte[] buffer = new byte[1024]; // 1KB.
// 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len; // 记住每次读取了多少个字节。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
字符输入流:FileReader
字符输出流:FileWriter
比如:下面的代码只调用了写数据的方法,没有关流的方法。当你打开目标文件时,是看不到任何数据的。
//1.创建FileWriter对象
Writer fw = new FileWriter("io-app2/src/itheima03out.txt");
//2.写字符数据出去
fw.write('a');
fw.write('b');
fw.write('c');
而下面的代码,加上了flush()方法之后,数据就会立即到目标文件中去。
//1.创建FileWriter对象
Writer fw = new FileWriter("io-app2/src/itheima03out.txt");
//2.写字符数据出去
fw.write('a');
fw.write('b');
fw.write('c');
//3.刷新
fw.flush();
下面的代码,调用了close()方法,数据也会立即到文件中去。因为close()方法在关闭流之前,会将内存中缓存的数据先刷新到文件,再关流。
//1.创建FileWriter对象
Writer fw = new FileWriter("io-app2/src/itheima03out.txt");
//2.写字符数据出去
fw.write('a');
fw.write('b');
fw.write('c');
//3.关闭流
fw.close(); //会先刷新,再关流
但是需要注意的是,关闭流之后,就不能在对流进行操作了。否则会出异常。
缓冲流
缓冲流就是把原始流包装一下,提高原始流读写能力
- **读数据时:**它先用原始字节输入流一次性读取8KB的数据存入缓冲流内部的数组中(ps: 先一次多囤点货),再从8KB的字节数组中读取一个字节或者多个字节(把消耗屯的货)。
- 写数据时: 它是先把数据写到缓冲流内部的8BK的数组中(ps: 先攒一车货),等数组存满了,再通过原始的字节输出流,一次性写到目标文件中去(把囤好的货,一次性运走)。
字节缓冲流:
字符缓冲流:
缓冲流得到核心是dingyil一个默认的8kb的缓冲区域,这个缓冲区域的大小也可以自己通过缓冲流构造器设置
- BufferedReader读数据时:**它先原始字符输入流一次性读取8KB的数据存入缓冲流内部的数组中(ps: 先一次多囤点货),再从8KB的字符数组中读取一个字符或者多个字符(把消耗屯的货)。
创建BufferedReader对象需要用到BufferedReader的构造方法,内部需要封装一个原始的字符输入流,我们可以传入FileReader.
而且BufferedReader还要特有的方法,一次可以读取文本文件中的一行
使用BufferedReader读取数据的代码如下
public class BufferedReaderTest2 {
public static void main(String[] args) {
try (
Reader fr = new FileReader("io-app2\\src\\itheima04.txt");
// 创建一个字符缓冲输入流包装原始的字符输入流
BufferedReader br = new BufferedReader(fr);
){
// char[] buffer = new char[3];
// int len;
// while ((len = br.read(buffer)) != -1){
// System.out.print(new String(buffer, 0, len));
// }
// System.out.println(br.readLine());
// System.out.println(br.readLine());
// System.out.println(br.readLine());
// System.out.println(br.readLine());
String line; // 记住每次读取的一行数据
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- BufferedWriter写数据时: 它是先把数据写到字符缓冲流内部的8BK的数组中(ps: 先攒一车货),等数组存满了,再通过原始的字符输出流,一次性写到目标文件中去(把囤好的货,一次性运走)。如下图所示
创建BufferedWriter对象时需要用到BufferedWriter的构造方法,而且内部需要封装一个原始的字符输出流,我们这里可以传递FileWriter。
而且BufferedWriter新增了一个功能,可以用来写一个换行符
接下来,写一个代码示例,使用BufferedWriter往文件中写入字符数据。
public class BufferedWriterTest3 {
public static void main(String[] args) {
try (
Writer fw = new FileWriter("io-app2/src/itheima05out.txt", true);
// 创建一个字符缓冲输出流管道包装原始的字符输出流
BufferedWriter bw = new BufferedWriter(fw);
){
bw.write('a');
bw.write(97);
bw.write('远');
bw.newLine();
bw.write("我爱你中国");
bw.newLine();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意
对于读取文件性能的比较,并不是说缓冲流一定会比原始流好。对于原始流可以通过增大每次读取的字节数组或字符数组的大小来提高性能,对于缓冲流来说,可以增大它的缓冲池大小。但是这两者都有一个共同点,当数组大小或缓冲池大小增大到一定程度时,性能提高的不会很明显。
转换流
字符输入转换流
过程:先拿到原始文件的字节流,然后用转换流按原先的编码方式转换成一个字符流。
hh.txt文件 GBK形式
China,我爱你
China,我爱你
China,我爱你
try (
//1、先获取原文件的字节流
InputStream is=new FileInputStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\hh.txt");
//2、利用转换流将原文件的字节流转换为字符流
Reader idr = new InputStreamReader(is, "gbk");
//再用缓冲流包装一下
BufferedReader br = new BufferedReader(idr);
){
//按行读
String len;
while ((len=br.readLine())!=null){
System.out.println(len);
}
} catch (Exception e) {
e.printStackTrace();
}
字符输出转换流
try (
//1、先获取原文件的字节流
OutputStream fw=new FileOutputStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\hh.txt");
//2、指定写入文件的字符集格式
OutputStreamWriter os = new OutputStreamWriter(fw,"utf-8");
){
os.write(97);
os.write("我爱你中国!");
} catch (Exception e) {
e.printStackTrace();
}
打印流(非常方便)
注意:这里的打印出去是相对于程序(内存)来说的,即将数据写出去到文件中
try (
//指定写入文件编码的字符集格式
// PrintStream p=new
// PrintStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\hh.txt", Charset.forName("gbk"));
//不指明,会用默认的
PrintStream p=new
PrintStream("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\hh.txt");
){
//打印进去的是啥,文件中写的就是啥
p.println(97);//97
p.println("我爱你中国");
p.println('a');
//这个不一样
p.write(97);//a
} catch (Exception e) {
e.printStackTrace();
}
try (
//指定写入文件编码的字符集格式
// PrintWriter p=new
// PrintWriter("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\hh.txt", Charset.forName("gbk"));
//不指明,会用默认的
PrintWriter p=new
PrintWriter("F:\\JavaEE\\api_learning\\api_learning\\src\\main\\java\\IO\\hh.txt");
){
//打印进去的是啥,文件中写的就是啥
p.println(97);//97
p.println("我爱你中国");
p.println('a');
//这个不一样
p.write(97);//a
} catch (Exception e) {
e.printStackTrace();
}
PrintStream与PrintWriter代码书写上雷同,区别在于前者可以写出字节数组,后者可以写出字符数组。
打印流的一种应用:输出语句的重定向
意思就是,打印出的信息在自己定义的文件中而不是控制台,可以理解成一个程序的日志
红色加框的意思是将system的out静态变量设置成我们定义的打印流。
以下是system的源码部分:
设置成我们定义的打印流后,每次调用ps.println()时,相关信息就会写入自定义文件中
System.out.println("老骥伏枥");
System.out.println("志在千里");
try ( PrintStream ps = new PrintStream("rjxy.txt"); ){
// 把系统默认的打印流对象改成自己设置的打印流
System.setOut(ps);
System.out.println("烈士暮年");
System.out.println("壮心不已");
} catch (Exception e) {
e.printStackTrace();
}
此时打印语句,将往文件中打印数据,而不在控制台。
数据流
序列化流(掌握)
对象序列化:将java对象写入网络中或文件中去
对象反序列化:从文件或网络中将java对象读出来
步骤:(该操作为对象的序列化操作)
1、创建字节输出流对象
2、将实例对象传入writeObject()方法中即可
注意:自定义类前提必须实现Serializable接口,该接口为标记接口,即接口内容为空,无方法
步骤:(该操作为对象的反序列化操作)
1、创建字节输入流对象
2、带哦用readObject()方法返回Object对象,之后进行强转即可
注意:对于自定义类不想要序列化的字段,需要添加transient修饰!!!