概念
数据传输技术,可以实现将数据在JVM和本地设备间进行传输
I:input 输入
O: output 输出
流
作用为实现数据传输,相当于管道
流的分类
1,从流方向上分
- 输入流:本地设备向JVM进行传输
- 输出流:JVM向本地设备进行传输
2,从传输单位上分
- 字节流: 以字节为单位进行数据传输,可以传输任意类型的文件。如文本,视频,图片,音频等
- 字符流:以字符为单位进行数据传输,只能传输文本类型的文件
3.从传输功能上分
- 节点流:真正的具有传输功能的流
- 过滤流:没有传输功能的流,作用为给节点流增强传输能力或者增加附加功能
字节流涉及的API
1,InputSteam:字节输入流的抽象总父类
2,OutputStream:字节输出流的抽象总父类
3,FileInputStream:字节输入节点流
4.FileOutoutStream:字节输出节点流
输入流
创建
FileInputStream fis = new FileInputStream("要读取的文件路径");
绝对书写
1,绝对路径:以电脑磁盘为基点的完整路径
2,相对路径:以项目路径为基点的路径(文件必须在当前项目下)
注意事项
1,路径书写必须截至至文件
2,文件必须存在,不然报FileNotFound异常
常用方法
1,int read(); 读取一个字节并返回,读取到达末尾返回-1
2.int read(byte[]); 尝试读取数组长度的数据至数组中,返回实际读取个数,读取到达末尾返回-1
3,close(); 关闭流链接,释放相关资源(所有流都具有的方法)
import java.io.FileInputStream;
public class TestFIS2 {
public static void main(String[] args)throws Exception {
//创建字节输入节点流对象
FileInputStream fis = new FileInputStream("file/b.txt");
//利用read()读取所有内容
while (true) {
//接收本次读取结果
int n= fis.read();
//判断读取是否到达末尾
if (n == -1) {
break;
}
System.out.println(n);
}
//利用read(byte[])读取所有内容
while (true) {
//创建用来接收的数组
byte[] bs = new byte[5];
//接收本次读取结果
int n = fis.read(bs);
//判断读取是否到达末尾
if (n == -1) {
break;
}
//遍历查看读取内容
for (byte b : bs) {
System.out.print(b+" ");
}
System.out.println();
}
//关流
fis.close();
}
}
输出流
创建
FileOutputStream fos=new FileOutputStream("接收的文件路径",true|false);
-
true表示数据追加,false表示数据覆盖。省略时默认为false
-
文件不存在,自动创建
-
无法创建文件夹
-
常用方法
1,write(int); 向目标文件写入一个字节
2,write(byte[]); 向目标文件写入一个数组的数据
3,flush(); 强制刷新缓冲区(所有输出流都有)
import java.io.FileOutputStream;
public class TestFOS1 {
public static void main(String[] args)throws Exception {
//创建字节输出节点流
//FileOutputStream fos = new FileOutputStream("file/c.txt");
FileOutputStream fos = new FileOutputStream("file/c.txt",true);//写入一个字节
fos.write(65);
fos.write(66);
fos.write(67);
fos.write(68);
//写入多个字节
String s = "abcdefg1234567";
//将字符串转为byte数组
byte[] bs = s.getBytes();fos.write(bs);
//关流
fos.close();System.out.println("操作成功!");
}
}
标准异常处理
try(需要自动关流的对象创建语句){}catch(){}
-
JDK7.0之后,所有的流对象都实现了AutoCloseable接口,该接口中提供了自动关流所需的close()方法
文件复制
原理:借助JVM使数据在两个文件之间传输,先将文件A的数据读取到JVM中,然后再从JVM中将数据写入到文件B,反复如此操作直至数据复制完成
先读后写
package com.by.test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class TestCopy {
public static void main(String[] args) {
//copy1();
copy2();
}
//一次复制一个字节
public static void copy1(){
try (
//被复制到文件路径
FileOutputStream fos=new FileOutputStream("file/text_copy1.txt");
//被复制的文件路径
FileInputStream fis=new FileInputStream("file/text.txt")
) {
while (true) {
//读取当前字节
int n = fis.read();
//判断读取是否到达末尾
if (n == -1) {
break;
}
//将当前读取字节写入到fos
fos.write(n);
}
System.out.println("文件复制成功!");} catch (FileNotFoundException e) {
System.out.println("文件路径不正确");
} catch (IOException e) {
System.out.println("文件复制失败");
} catch (Exception e) {
System.out.println("未知异常");
e.printStackTrace();
}
}
//一次复制一个数组
public static void copy2(){
try (
//被复制到文件路径
FileOutputStream fos=new FileOutputStream("file/text_copy2.txt");
//被复制的文件路径
FileInputStream fis=new FileInputStream("file/text.txt")
) {
while (true) {
//创建用来接收的数组
byte[] bs=new byte[1024];
//读取
int n = fis.read(bs);
if (n == -1) {
break;
}
//将读取数据写入到FOS
fos.write(bs);
}
System.out.println("文件复制成功!");} catch (FileNotFoundException e) {
System.out.println("文件路径不正确");
} catch (IOException e) {
System.out.println("文件复制失败");
} catch (Exception e) {
System.out.println("未知异常");
e.printStackTrace();
}
}
}
缓冲过滤流
输入:BufferedlnputStream
输出:BuffrerdOutputStream
创建
BufferedInputStream bis=new BufferedInputStream(fis对象);
BufferedOutputStream bos=new BufferedOutputStream(fos对象);
原理
内置数据缓冲区,在进行数据读写时,数据不会直接对接磁盘,而且对接缓冲区,最终由缓冲区一次性将数据写入磁盘,由此降低JVM与磁盘之间的传输频率,从而提高效率
//字节复制+缓冲过滤流
public static void copy(){
try (
//被复制到文件路径
FileOutputStream fos=new FileOutputStream("d:/file/JDK_API_1.8_zh_CN_copy1.chw");
//被复制的文件路径
FileInputStream fis=new FileInputStream("d:/file/JDK_API_1.8_zh_CN.chw");
BufferedOutputStream bos=new BufferedOutputStream(fos);
BufferedInputStream bis=new BufferedInputStream(fis)
) {
while (true) {
//读取当前字节
int n = bis.read();
//判断读取是否到达末尾
if (n == -1) {
break;
}
//将当前读取字节写入到fos
bos.write(n);
}
System.out.println("文件复制成功!");} catch (FileNotFoundException e) {
System.out.println("文件路径不正确");
} catch (IOException e) {
System.out.println("文件复制失败");
} catch (Exception e) {
System.out.println("未知异常");
e.printStackTrace();
}}
当使用缓冲过滤进行先写后读操作时,在写入完成后,必须刷新缓冲区将数据提前写入至目标文件,然后再进行读取
刷新缓冲区:
1,bos.flush(); 直接强刷缓冲区
2.bos.close():关流之前会自动刷新缓冲区
对象过滤流
输入:ObjectlnputStream
输出:ObjectOutputStream
附加功能;
读写基本类型
读写引用类型
读写基本类型
ois.readXxx()
oos.writeXxx(值);
注:Xxx表示的为基本类型,首字母大写,如:double readDouble | writeDouble
使用
使用对象过滤流嵌套了缓冲区,所以在先写后读时,需要在写入完成后刷新缓冲区
为了保证数据安全,在写入数据时会通过魔数机制对其加密,在读取时再对其解密,防止写入之后的数据泄露
import java.io.*;public class TestOIS_OOS {public static void main(String[] args) {//往当前项目下的file/a.txt中写入一个5.5,然后将其读取输出try (//创建节点流FileOutputStream fos = new FileOutputStream("file/a.txt");FileInputStream fis = new FileInputStream("file/a.txt");//添加对象过滤流ObjectOutputStream oos = new ObjectOutputStream(fos);ObjectInputStream ois = new ObjectInputStream(fis)) {//先写oos.writeDouble(5.5);System.out.println("写入成功!");//强刷缓冲区oos.flush();//读取System.out.println(ois.readDouble());} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败");} catch (Exception e) {System.out.println("未知异常");e.printStackTrace();}}}
读写引用类型
ios: Object readObject() 读取到达末尾,抛出EOFException异常oos: void writeObject(对象) 该方法自带缓冲区刷新,先写后读时无需手动刷新缓冲区
读写String
import java.io.*;public class Test_String {public static void main(String[] args) {//往file/b.txt中写入一首打油诗,然后读取输出try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file/b.txt"));ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file/b.txt"))) {oos.writeObject("12345");oos.writeObject("上山打老虎");oos.writeObject("老虎没打着");oos.writeObject("打着小松鼠");System.out.println("写入成功!");//读取while (true) {//接收本次读取结果try {String s=(String) ois.readObject();System.out.println(s);} catch (EOFException e) {//结束循环break;}}} catch (FileNotFoundException e) {System.out.println("文件路径不正确");} catch (IOException e) {System.out.println("读写失败");} catch (Exception e) {System.out.println("未知异常");e.printStackTrace();}}}
读写自定义类型
- 类必须实现Serializable接口,意味着允许被序列化
- 序列化:IO流拆解对象信息读取的过程
- 反序列化:IO流组装对象信息构建对象的过程
- 可以通过transient修饰符设定某个属性不参与序列化
访问修饰符 transient 数据类型 属性名;
public class Student implements Serializable {private String name;//防止年龄参与序列化private transient int age;private double score;//省略getter、setter、构造、toString}