将数据在虚拟机内存和本地磁盘之间进行传输
I:input 输入
O:output 输出
相当于管道,作用为进行数据传输
从传输方向上看
输入流:本地磁盘的数据向JVM传输
输出流:JVM数据向本地磁盘传输
从传输单位上看
字节流:以字节为单位进行数据传输。可以传输任意类型的数据,如文本、图片、视频、音频等
字符流:以字符为单位进行数据传输。只能传输文本类型的数据,如.txt、.java、.html等
从传输功能上看
节点流:具有传输功能和意义的流
过滤流:没有传输功能,用来给节点流增强传输能力或增加附加功能
输入流:InputStream 抽象父类
输出流:OutputStream 抽象父类
节点流:FileInputStream
FileInputStream fis=new FileInputStream("本地的文件路径");
路径:
绝对路径:以电脑磁盘为基点的完整路径
FileInputStream fis = new FileInputStream("D:\\test\\a.txt"); FileInputStream fis = new FileInputStream("D:/test/a.txt");
相对路径:以当前项目路径为基点的路径,前提是文件必须在项目下
FileInputStream fis = new FileInputStream("file\\a.txt"); FileInputStream fis = new FileInputStream("file/a.txt");
路径书写必须截至至文件
文件必须存在,否则抛出异常
void close():关闭流链接,释放相关资源。(每个流中都有)
int read(): 读取一个字节返回,读取到达末尾,返回-1
int read(byte[] b): 尝试读取数组长度的数据至数组中, 返回实际读取个数.读取到达末尾,返回-1
package com.by.test;
?
import java.io.FileInputStream;
?
public class TestFIS {
? ?public static void main(String[] args)throws Exception {
? ? ? ?// FileInputStream fis = new FileInputStream("D:\\test\\a.txt");
? ? ? ?// FileInputStream fis = new FileInputStream("D:/test/a.txt");
?
? ? ? ?// FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\IdeaProjects\\Chp16\\file\\a.txt");
? ? ? ? FileInputStream fis = new FileInputStream("file\\a.txt");
? ? ? ? //FileInputStream fis = new FileInputStream("file/a.txt");
? ? ? ?//读取一个字节
? ? ? ?//利用循环读取文件所有内容
? ? ? ?while (true) {
? ? ? ? ? ?//先接收本次读取内容
? ? ? ? ? ?int n = fis.read();
? ? ? ? ? ?//判断读取是否到达末尾
? ? ? ? ? ?if (n == -1) {
? ? ? ? ? ? ? ?break;
? ? ? ? ? }
? ? ? ? ? ?//未到达末尾,输出查看
? ? ? ? ? ?System.out.println((char) n);
? ? ? }
?
? ? ? ?//利用read(byte[])+循环读取文件所有内容
? ? ? ?while (true) {
? ? ? ? ? ?byte[] bs = new byte[3];
? ? ? ? ? ?//接收本次读取结果
? ? ? ? ? ?int n = fis.read(bs);
? ? ? ? ? ?//判断读取是否到达末尾
? ? ? ? ? ?if (n == -1) {
? ? ? ? ? ? ? ?break;
? ? ? ? ? }
? ? ? ? ? ?//遍历数组查看本次读取结果
? ? ? ? ? ?for (int i = 0; i < bs.length; i++) {
? ? ? ? ? ? ? ?System.out.println(bs[i]);
? ? ? ? ? }
? ? ? }
?
?
?
? ? ? ?//关流
? ? ? ?fis.close();
?
? ? ? ?System.out.println("执行成功!");
? }
}
节点流:FileOutputStream
FileOutputStream fos=FileOutputStream("本地的文件路径",true|false);
如果文件不存在,会自动创建
无法创建文件夹
true表示数据追加,false表示数据覆盖
默认是false
void flush(): 刷新缓冲区,所有输出流中都具备该方法
void write(int ): 向目标文件写入一个字节
void write(byte[] ): 向目标文件写入一个数组的数据
package com.by.test;
?
import java.io.FileOutputStream;
?
public class TestFOS {
? ?public static void main(String[] args)throws Exception {
? ? ? ?FileOutputStream fos=new FileOutputStream("file/b.txt");
? ? ? ?//写入一个字节
? ? ? ?fos.write(65);
? ? ? ?fos.write(66);
? ? ? ?fos.write(67);
? ? ? ?//写入一个数组
? ? ? ?String str = "abcdefg123456";
? ? ? ?byte[] bs = str.getBytes();
? ? ? ?fos.write(bs);
?
? ? ? ?//关流
? ? ? ?fos.close();
?
? ? ? ?System.out.println("执行成功!");
? }
}
JDK7.0之后,提供了自动关流的语法结构,简化了finally的工作内容
try( //需要自动关流的创建语句 ){ ? ? }catch(){ ? ? }
原理: JDK7.0之后,所有的流都默认实现了AutoCloseable接口,该接口中提供了自动关流所需的close方法
package com.by.test;
?
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
?
public class TestFOS2 {
? ?public static void main(String[] args) {
? ? ? ?try (
? ? ? ? ? ? ? ?FileOutputStream fos = new FileOutputStream("file/b.txt");
? ? ? ? ? ? ? ){
?
? ? ? ? ? ?//写入一个字节
? ? ? ? ? ?fos.write(65);
? ? ? ? ? ?fos.write(66);
? ? ? ? ? ?fos.write(67);
? ? ? ? ? ?//写入一个数组
? ? ? ? ? ?String str = "abcdefg123456";
? ? ? ? ? ?byte[] bs = str.getBytes();
? ? ? ? ? ?fos.write(bs);
? ? ? } catch (FileNotFoundException e) {
? ? ? ? ? ?System.out.println("文件路径不正确");
? ? ? } catch (IOException e) {
? ? ? ? ? ?System.out.println("读写失败");
? ? ? } catch (Exception e) {
? ? ? ? ? ?System.out.println("未知异常!");
? ? ? ? ? ?e.printStackTrace();
? ? ? }
?
? ? ? ?System.out.println("执行成功!");
? }
}
原理: 先将文件A中的内容读取到JVM中,再从JVM中将读取内容写入到文件B, 借助JVM来实现A与B之间的数据复制
先读后写
package com.by.test;
?
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
?
public class TestFileCopy {
? ?public static void main(String[] args) {
? ? ? ?copy1();
? ? ? ?copy2();
? }
? ?/**
? ? * 一次复制一个字节
? ? */
? ?public static void copy1(){
? ? ? ?try (
? ? ? ? ? ? ? ?//创建输出节点流-复制到的文件路径
? ? ? ? ? ? ? ?FileOutputStream fos=new FileOutputStream("d:/test/2.pdf");
? ? ? ? ? ? ? ?//创建输入节点流-被复制的文件路径
? ? ? ? ? ? ? ?FileInputStream fis=new FileInputStream("d:/test/1.pdf")
? ? ? ? ? ? ? ) {
? ? ? ? ? ?//先循环读取文件所有内容
? ? ? ? ? ?while (true) {
? ? ? ? ? ? ? ?int n = fis.read();
? ? ? ? ? ? ? ?if (n == -1) {
? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?//将本次读取的字节写入到目标文件
? ? ? ? ? ? ? ?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("d:/test/3.pdf");
? ? ? ? ? ? ? ?//创建输入节点流-被复制的文件路径
? ? ? ? ? ? ? ?FileInputStream fis=new FileInputStream("d:/test/1.pdf")
? ? ? ) {
? ? ? ? ? ?//先循环读取文件所有内容
? ? ? ? ? ?while (true) {
? ? ? ? ? ? ? ?//创建数组
? ? ? ? ? ? ? ?byte[] bs = new byte[1024];
? ? ? ? ? ? ? ?//读取一个数组的数据
? ? ? ? ? ? ? ?int n = fis.read(bs);
? ? ? ? ? ? ? ?if (n == -1) {
? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?//将数组中的数据写入到目标文件
? ? ? ? ? ? ? ?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();
? ? ? }
? }
}
?
BufferedInputStream: 输入缓冲过滤流
BufferedOutputStream: 输出缓冲过滤流
BufferedInputStream bis=new BufferedInputStream(fis对象); BufferedOutputStream bos=new BufferedOutputStream(fos对象);
拥有一个内置的数据缓冲区, 文件数据将对接至数据缓冲区中,在缓冲区刷新或关闭时再将内部内容一并的给到目标文件
/**
? ? * 一次复制一个字节+缓冲过滤流
? ? */
? ?public static void copy3(){
? ? ? ?try (
? ? ? ? ? ? ? ?//创建输出节点流-复制到的文件路径
? ? ? ? ? ? ? ?FileOutputStream fos=new FileOutputStream("d:/test/4.pdf");
? ? ? ? ? ? ? ?//创建输入节点流-被复制的文件路径
? ? ? ? ? ? ? ?FileInputStream fis=new FileInputStream("d:/test/1.pdf");
? ? ? ? ? ? ? ?//添加缓冲过滤流
? ? ? ? ? ? ? ?BufferedOutputStream bos=new BufferedOutputStream(fos);
? ? ? ? ? ? ? ?BufferedInputStream bis=new BufferedInputStream(fis)
? ? ? ) {
? ? ? ? ? ?//先循环读取文件所有内容
? ? ? ? ? ?while (true) {
? ? ? ? ? ? ? ?int n = bis.read();
? ? ? ? ? ? ? ?if (n == -1) {
? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?//将本次读取的字节写入到目标文件
? ? ? ? ? ? ? ?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();
? ? ? }
? }
如果先写后读,需要在写入完成后刷新缓冲区才能保证读取的正常进行
调用flush():强刷缓冲区 (推荐)
调用close(): 关流之前也会刷新缓冲区
关流时外层过滤流关闭内层节点流会一并关闭
package com.by.test;
?
import java.io.*;
?
public class TestBuffered {
? ?public static void main(String[] args) {
? ? ? ?try(
? ? ? ? ? ? ? //输出
? ? ? ? ? ? ? BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("file/a.txt"));
? ? ? ? ? ? ? //输入
? ? ? ? ? ? ? BufferedInputStream bis=new BufferedInputStream(new FileInputStream("file/a.txt"))
? ? ? ? ? ? ? ){
? ? ? ? ? ?//先写
? ? ? ? ? ?bos.write("abcd".getBytes());
? ? ? ? ? ?//刷新缓冲区
? ? ? ? ? ?bos.flush(); ?
? ? ? ? ? ?//bos.close();
? ? ? ? ? ?//再读
? ? ? ? ? ?while (true) {
? ? ? ? ? ? ? ?int n = bis.read();
? ? ? ? ? ? ? ?if (n == -1) {
? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?System.out.println((char) n);
? ? ? ? ? }
?
? ? ? }catch (FileNotFoundException e) {
? ? ? ? ? ?System.out.println("文件路径不正确");
? ? ? } catch (IOException e) {
? ? ? ? ? ?System.out.println("读写失败!");
? ? ? } catch (Exception e) {
? ? ? ? ? ?System.out.println("未知异常!");
? ? ? ? ? ?e.printStackTrace();
? ? ? }
? }
}
ObjectInputStream 对象输入过滤流
ObjectOutputStream 对象输出过滤流
附加功能1: 读写基本类型
附加功能2: 读写引用类型
读取: ois.readXxx() 写入: oos.writeXxx(值); ? ?注: Xxx对应的为基本类型名,首字母大写
由于对象过滤流底层嵌套了缓冲区,所以先写后读操作时,仍然需要在写入完成后刷新缓冲区
为了保证数据的安全性,所以在写入时数据会根据魔数机制对其加密,在读取时在进行解密
package com.by.test;
?
import java.io.*;
?
public class TestObject_Double {
? ?public static void main(String[] args) {
? ? ? ?try (
? ? ? ? ? ? ? ?//输出流
? ? ? ? ? ? ? ?ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("file/c.txt"));
? ? ? ? ? ? ? ?//输入流
? ? ? ? ? ? ? ?ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file/c.txt"))
?
? ? ? ? ? ? ? ) {
? ? ? ? ? ?//先写
? ? ? ? ? ?oos.writeDouble(10.5);
? ? ? ? ? ?//强刷缓冲区
? ? ? ? ? ?oos.flush();
? ? ? ? ? ?//读取
? ? ? ? ? ?System.out.println(ois.readDouble());
?
?
? ? ? } catch (FileNotFoundException e) {
? ? ? ? ? ?System.out.println("文件路径不正确");
? ? ? } catch (IOException e) {
? ? ? ? ? ?System.out.println("读写失败!");
? ? ? ? ? ?e.printStackTrace();
? ? ? } catch (Exception e) {
? ? ? ? ? ?System.out.println("未知异常!");
? ? ? ? ? ?e.printStackTrace();
? ? ? }
? }
}
读取: Object ois.readObject() ?读取到达末尾,抛出EOFException异常 写入: oos.writeObject(对象) ?可以自动刷新缓冲区,所以先写后读时无需进行flush
package com.by.test;
?
import java.io.*;
?
public class TestObject_String {
? ?public static void main(String[] args) {
? ? ? ?try (
? ? ? ? ? ? ? ?//输出流
? ? ? ? ? ? ? ?ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("file/c.txt"));
? ? ? ? ? ? ? ?//输入流
? ? ? ? ? ? ? ?ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file/c.txt"))
?
? ? ? ? ? ? ? ) {
? ? ? ? ? ?//先写
? ? ? ? ? ?oos.writeObject("床前明月光");
? ? ? ? ? ?oos.writeObject("疑是地上霜");
? ? ? ? ? ?oos.writeObject("举头望明月");
? ? ? ? ? ?oos.writeObject("低头思故乡");
? ? ? ? ? ?//后读
? ? ? ? ? ?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("读写失败!");
? ? ? ? ? ?e.printStackTrace();
? ? ? } catch (Exception e) {
? ? ? ? ? ?System.out.println("未知异常!");
? ? ? ? ? ?e.printStackTrace();
? ? ? }
? }
}
自定义类必须实现Serializable接口,表示允许被序列化,否则IO流没有读写权限
序列化:拆分对象信息的过程
反序列化:通过信息组装对象的过程
将属性通过transient修饰符修饰则可以防止其参与序列化
如果对象中有自定义类型的属性,则必须使该属性类型也实现序列化接口或者通过transient修饰符对其修饰
package com.by.entity;
?
import java.io.Serializable;
?
public class Student implements Serializable {
? ?private String name;
? ?private int age;
? ?//防止被序列化
? ?private transient double score;
? // private Teacher tea;
//省略getter、setter、构造、toString
}
?
package com.by.test;
?
import com.by.entity.Student;
?
import java.io.*;
?
public class TestObject_Student {
? ?public static void main(String[] args) {
? ? ? ?try (
? ? ? ? ? ? ? ?//输出流
? ? ? ? ? ? ? ?ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("file/c.txt"));
? ? ? ? ? ? ? ?//输入流
? ? ? ? ? ? ? ?ObjectInputStream ois=new ObjectInputStream(new FileInputStream("file/c.txt"))
?
? ? ? ? ? ? ? ) {
? ? ? ? ? ?//先写
? ? ? ? ? ?oos.writeObject(new Student("zhangsan", 20, 88.5));
? ? ? ? ? ?//后读
? ? ? ? ? ?System.out.println((Student)ois.readObject());
?
?
?
? ? ? } catch (FileNotFoundException e) {
? ? ? ? ? ?System.out.println("文件路径不正确");
? ? ? } catch (IOException e) {
? ? ? ? ? ?System.out.println("读写失败!");
? ? ? ? ? ?e.printStackTrace();
? ? ? } catch (Exception e) {
? ? ? ? ? ?System.out.println("未知异常!");
? ? ? ? ? ?e.printStackTrace();
? ? ? }
? }
}
怎么理解输出语句?
System是类库中的工具类,out为该工具类中的静态属性,类型为标准输出流类型,print和println系列方法是该流中提供的写入方法,作用为向控制台写入一个内容
流的分类
文件复制的源码(字节复制+缓冲过滤流)
对象过滤流读写自定义类型