Io
- 数据传输技术,可以实现将数据在jvm和本地设备间进行传输
- I :input 输入
- O : output
流
作用为实现数据传输,相当于管道
分类
- 从传输方向看
- 输入流:本地磁盘的数据向jvm传输
- 输出流:jvm数据向本地磁盘传输
- 从传输单位上看
- 字节流:以字节为单位进行数据传输,可以传输任意类型的数据,如文本,图片,视频,音频等
- 字符流:以字符为单位进行数据传输,只可以传输文本类型的数据,如.TXT,.java, .css
- 从传输功能上看
- 节点流:具有传输功能和意义的流
- 过滤流:没有传输功能,用来给节点流增强传输能力或增加附加功能
字节流
- 输入流:InputStream 抽象父类
- 输出流:OutputStream抽象父类
输入流
- 节点流:FileInoutStream
创建
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
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[] ): 向目标文件写入一个数组的数据
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方法
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之间的数据复制
- 先读后写
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(): 关流之前也会刷新缓冲区
-
关流时外层过滤流关闭内层节点流会一并关闭
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对应的为基本类型名,首字母大写
- 由于对象过滤流底层嵌套了缓冲区,所以先写后读操作时仍然需要在写入完成后刷新缓冲区
- 为了保证数据的安全性,所以在写入时数据会根据魔数机制对其进行加密,在读取时进行解密
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
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修饰符对其修饰
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
}
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系列方法是该流中提供的写入方法,作用为向控制台写入一个内容
字符流
传输char和String类型的数据
输入流
- 抽象父类:Reader
- 节点流: FileREader
常用方法
- int read():读取一个字符,读取到末尾,返回-1
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class TestReader {
public static void main(String[] args) {
try(
//字符输入节点流
FileReader fr=new FileReader("file/d.txt")
){
while (true) {
int n = fr.read();
if (n == -1) {
break;
}
System.out.println((char) n);
}
}catch (FileNotFoundException e) {
System.out.println("文件路径不正确");
} catch (IOException e) {
System.out.println("读写失败!");
e.printStackTrace();
} catch (Exception e) {
System.out.println("未知异常!");
e.printStackTrace();
}
}
}
输出流
- 抽象父类:Writer
- 节点流 :FileWriter
常用方法
- write(int):向目标文件写入一个字符
- write(String):向目标文件写入一个字符串
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestWriter {
public static void main(String[] args) {
try(
//字符输出节点流
FileWriter fw=new FileWriter("file/e.txt")
){
fw.write(65);
fw.write(66);
fw.write(67);
//写入字符串
fw.write("一二三四五");
fw.write("上山打老虎");
System.out.println("写入成功!");
}catch (FileNotFoundException e) {
System.out.println("文件路径不正确");
} catch (IOException e) {
System.out.println("读写失败!");
e.printStackTrace();
} catch (Exception e) {
System.out.println("未知异常!");
e.printStackTrace();
}
}
}
输入缓冲过滤流
- bufferedReader
方法
- String readLine():一次读取一行数据,读取达到末尾返回null
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class TestBufferedReader {
public static void main(String[] args) {
try(
//字符输入节点流
FileReader fr=new FileReader("file/d.txt");
//添加缓冲过滤流
BufferedReader br=new BufferedReader(fr)
){
while (true) {
//接收本次读取内容
String s = br.readLine();
//判断读取是否到达末尾
if (s == null) {
break;
}
System.out.println(s);
}
}catch ...
}
}
输出缓冲过滤流
- PrintWriter
- BufferedWriter中的方法没有PrintWriter更多更实用
方法
- print(值):向目标文件写入一个数据,默认不换行
- println(值):向目标文件写入一个数据,默认换行
- println();向目标文件写入一个空行
import com.by.entity.Student;
import java.io.*;
public class TestPrintWriter {
public static void main(String[] args) {
try(
//字符输出节点流
FileWriter fw=new FileWriter("file/e.txt");
//添加缓冲过滤流
PrintWriter pw=new PrintWriter(fw)
){
pw.print(5.5);
pw.print(true);
pw.println();
pw.println("一二三四五");
pw.println("上山打老虎");
Student s = new Student("zhangsan", 20, 99.5);
pw.println(s);
System.out.println("写入成功!");
}catch...
}
}
pw 对象中的print | println方法写对象与对象过滤流写对象的区别
pw只是在读写对象引用的toString方法的结果,并未读写对象的完整信息所以无法对对象进行序列化和反序列化
对象过滤流是在读写对象完整信息,所以可以对对象进行序列化和反序列化
pw 中的print | println 方法与输出语句中的有何不同?
pw是将数据写入到目标文件长久保存
标准输出流是将数据写入到控制台临时查看
字符编码集
- 编码:原内容-> 加密->数字
- 解码:数字->解密->原内容
常见编码集
- GBK:简体中文
- Big5:繁体中文
- ASC ||:美国
- ISO-8859-1:西欧
- unicode:
- UTF-16:java默认编码集,所有内容统一占用2个字节
- UTF-8行业标准,所占字节由内容大小决定,通常为1-3个字节
不同编码集拥有独立的编码方式,之间互不相通
桥转换流
- 输入:InputStreamReader
- 输出:OutputStreamWriter
创建
- 基于字节节点流对象,将字节流中的内容转换为字符流并在转换过程中设置数据传输的编码集
InputStreamReader isr=new InputStreamReader(fis对象,"编码集");
OutputStreamWriter osw=new OutputStreamWriter(fos对象,"编码集");
步骤
- 创建字节节点流,声明被操作的文件路径
- 创建桥转换流,声明操作文件时所用的编码集
- 添加缓冲过滤流,提高数据操作的便捷性
使用
对同一文件的输入输出操作必须保证编码集一致
import java.io.*;
public class Test_ISR_OSW {
public static void main(String[] args) {
try(
//创建输出流
//创建字节输出节点流
FileOutputStream fos=new FileOutputStream("file/m.txt");
//创建桥转换流
OutputStreamWriter osw=new OutputStreamWriter(fos,"GBK");
//添加缓冲过滤流
PrintWriter pw=new PrintWriter(osw);
//创建输入流
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("file/m.txt"),"GBK"))
){
//先写
pw.println("一二三四五");
pw.println("上山打老虎");
pw.println("老虎没打着");
pw.println("打着小松鼠");
//刷新缓冲区
pw.flush();
//后读
while (true) {
String s = br.readLine();
if (s == null) {
break;
}
System.out.println(s);
}
}catch...
}
}