IO流
一、流的分类:
- 1.操作数据单位:字节流、字符流
- 2.数据的流向:输入流、输出流
- 3.流的角色: 节点流、处理流
二、流的体系结构
抽象基类 节点流(或文件流) 缓冲流(处理流的一种)
InputStream FileInputStream ---> (read(byte[] buffer)) <--- BufferedInputStream
OutputStream FileOutputStream --->(write(byte[] buffer,0,len))<---BufferedOutputStream
Reader FileReader ---> (read(char[] chars)) <--- BufferedReader
Writer FileWriter ---> (write(char[] chars,0,len) <--- BufferedWriter
节点流:字符流
适用于文本文件的读写(.txt .java .c)
FileReader
FileWriter
read()
- 从该输入流读取一个字节的数据,返回读入的一个字符,如果到达文件末尾,返回-1
- 异常处理:为保证资源一定可以执行关闭操作。需要使用try-catch-finally
- 读入的文件一定要存在,否则就会会FileNotFoundException异常
@Test
public void test() {
/*
read():从该输入流读取一个字节的数据,返回读入的一个字符,如果到达文件末尾,返回-1
异常处理:为保证资源一定可以执行关闭操作。需要使用try-catch-finally
读入的文件一定要存在,否则就会会FileNotFoundException异常
*/
FileReader fs = null;
try {
//1.File类的实例化
File file = new File("zmj.txt");
System.out.println(file.getAbsolutePath());
//2.FileReader流的实例化
fs = new FileReader(file);
//3.读入操作
int read = fs.read();
while (read != -1){
System.out.print((char)read);
read = fs.read();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4、流的关闭操作
if(fs != null)
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
read(char[] cbf):更高效
@Test
public void test2() {
FileReader fr = null;
try {
File file = new File("zmj.txt");
fr = new FileReader(file);
//read(char[] cbf);返回每次读入cbf数组中的字符个数。如果达到文件末尾,返回-1
char[] chars = new char[3];
int read;
while ( (read= fr.read(chars))!=-1){
//方式一
// for (int i=0;i<read;i++){
// System.out.println(chars[i]);
// }
//方式二
String str = new String(chars, 0, read);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
write()
从内存中写出数据到硬盘的文件里
说明:
-
1.输出操作,对应的File可以不存在,不会报异常
-
***File对应的硬盘文件如果不存在:***在输出的过程中,会自动创建该文件
File对应的硬盘文件如果存在:
如果流使用的构造器是:FileWriter(file,true);写的内容将在原文件的基础上追加
如果流使用的构造器是:FileWriter(file,false);写的内容将覆盖原文件的内容
构造器FileWriter(file)默认为false
@Test
public void test3()throws Exception{
//1.初始化文件类,指明要写到的文件
File file = new File("zmj1.txt");
//2.初始化FileWriter流对象,用于数据的写出
FileWriter fw = new FileWriter(file);
//3.写出操作
fw.write("zmj123!\n");
fw.write("hello!");
//4.关闭流文件
fw.close();
}
/*
文件的复制操作
*/
@Test
public void test4(){
FileReader fr = null;
FileWriter fw = null;
try {
File zmj1File = new File("zmj1.txt");
File zmj2File = new File("zmj2.txt");
fr = new FileReader(zmj1File);
fw = new FileWriter(zmj2File);
//3.数据的读入和写出操作
char[] chars = new char[5];
int len;
while ((len=fr.read(chars)) != -1){
fw.write(chars,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fw!=null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
节点流:字节流
适用于非文本文件(.jpg .png .mp3 .doc .ppt)
FileInputStream
FileOutputStream
- 使用字节流处理文本文件 有可能有会导致字符乱码:因为其使用的
byte[]
存储数据而非char[]
//使用字节流处理文本文件测试
@Test
public void test() {
FileInputStream fis = null;
try {
File file = new File("zmj2.txt");
fis = new FileInputStream(file);
byte[] bytes = new byte[5];
int len;
while ((len=fis.read(bytes)) != -1){
for (int i=0;i<len;i++){
System.out.print((char) bytes[i]);//hello!¦ᄌᆳ₩ヨヌ¦ᄍᄆᅠチ
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
非文本文件的复制
//非文本文件的复制操作
@Test
public void test2(){
long start = System.currentTimeMillis();
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File file = new File("zmj.jpg");
File file1 = new File("zmj1.jpg");
fis = new FileInputStream(file);
fos = new FileOutputStream(file1);
byte[] bytes = new byte[5];
int len;
while ((len=fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("花费时间:"+(end-start));//6800毫秒
}
public void copyFile(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File file = new File(srcPath);
File file1 = new File(destPath);
fis = new FileInputStream(file);
fos = new FileOutputStream(file1);
byte[] bytes = new byte[1024];
int len;
while ((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void test3(){
long start = System.currentTimeMillis();
String srcPath="被复制视频.mp4";
String destPath="生成目标视频.mp4";
copyFile(srcPath,destPath);
long end = System.currentTimeMillis();
System.out.println("执行时间:"+(end-start));
}
处理流:缓冲流
处理流,就是“套接”在已有流的基础之上的流
-
非文本文件缓冲流
BufferedInputStream
BufferedOutputStream
-
文本文件缓冲流
BufferedReader
BufferedWriter
缓冲流作用:
- 提供流的读取,写入的速度
- 提高读写速度的原因,内部提供了一个缓冲区
复制非文本文件:同样的两张图片:使用缓冲流比不使用复制快了100倍
/*
复制非文本文件:缓冲流
*/
@Test
public void test(){
long start = System.currentTimeMillis();
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.实例化文件构造器
File file = new File("zmj.jpg");
File file1 = new File("zmj1.jpg");
//2.实例化流
//2.1实例化节点流
fis = new FileInputStream(file);
fos = new FileOutputStream(file1);
//2.2实例化缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.复制操作
byte[] bytes = new byte[10];
int len;
while ((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
if(bis!=null)
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
if(bos!=null)
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
//关闭外层流的同时,内层流也会自动进行关闭,所以可以省略内层流的关闭操作
// fis.close();
// fos.close();
}
long end = System.currentTimeMillis();
System.out.println("使用缓冲流的处理时间:"+(end-start));//65毫秒
}
复制文本文件
//文本文件缓冲流复制操作
public void test1(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(new File("zmj.txt")));
bw = new BufferedWriter(new FileWriter(new File("zmj2.txt")));
//方式一
// char[] buffer = new char[1024];
// int len;
// while ((len=br.read(buffer))!=-1){
// bw.write(buffer,0,len);
// }
//方式二 readLine() -->字符流缓冲流的方法
String date;
while ((date=br.readLine()) != null){
// bw.write(date);//data中不包含换行符
bw.write(date);
bw.newLine();//换行方法
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br!=null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bw!=null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
处理流:转换流
InputStreamReader
:将一个字节的输入流转换为字符的输入流
OutputStreamWriter
:将一个字符的输出流转换为字节的输出流
转换流作用
-
提供字节流与字符流之间的转换
解码:字节、字节数组—>字符数组、字符串
編码:字符数组、字符串—> 字节、字节数组
@Test
public void test2(){
FileInputStream fis = null;
InputStreamReader isr = null;
try {
//使用字节流读取文本文件
fis = new FileInputStream("zmj2.txt");
//使用转换流对字节流进行转换 --字符
//参数2指明了字符集,具体使用哪个字符集,取决于文件保存是使用的字符集编码格式
isr = new InputStreamReader(fis, "utf-8");
char[] chars = new char[5];
int len;
while ((len=isr.read(chars))!=-1){
// for(int i=0;i<len;i++){
// System.out.println(chars[i]);
// }
String str = new String(chars, 0, len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (isr!=null)
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
标准的输入输出流
System.in
:标准的输入流,默认从键盘输入System.out
:标准的输出流,默认从控制台输出- System类的
setIn(InputStream is)
/setOut(PrintStream ps)
方式重新指定输入和输出的方式位置
练习:从键盘输入字符串,要求将读取到的整行字符串转成大写输出,然后继续进行输入操作,输入e退出
方法:使用System.in实现。System.in —> 转换流 —> BufferedReader的readLine()
public class IoInAndOut {
public static void main(String[] args) {
InputStreamReader isr = null;
BufferedReader br = null;
try {
isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
while (true){
System.out.println("输入字符串");
String str = br.readLine();
if("e".equals(str)){
System.out.println("程序退出");
break;
}
//toUpperCase()将所有在此字符 String使用默认语言环境的规则大写
String s = str.toUpperCase();
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(isr!=null)
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br!=null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
数据流
作用:用于读取或写出基本数据类型的变量或字符串
DataInputStream
DataOutputStream
练习:将内存中的字符串,基本数据类型的变量写出到文件中
@Test
public void test(){
DataOutputStream dos = null;
try {
dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeUTF("张明均");
dos.flush();
dos.writeInt(22);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象流
- 作用:用于存储和读取基本数据类型数据或对象的处理流。他的强大之处就是可以把java中的对象写入到数据源中,也能把对象从数据源中还原回来
ObjectInputStream
ObjectOutputStream
-
java对象可序列化需要满足响应条件,见Person. java
-
序列化机制:
- 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
-
当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。
- 谈谈你对对象序列化机制的理解。
序列化过程: 4
反序列化过程: - 对象要想实现序列化,需要满足哪几个条件。
1.实现接口: Serializable\标识接口+
2.对象所在的类提供常量:序列版本号
3.要求对象的属性也必须是可序列化的。(基本数据类型、String: 本身就已经是可序列化)
- 谈谈你对对象序列化机制的理解。
可序列化的类Person
/**
* Created by KingsLanding on 2022/7/2 17:28
*
* Person需要满足如下要求,才能序列化
* 1.需要实现接口Serializable:标识性接口
* 2.当前类需要提供一个全局常量:serialVersionUID 相当于对象的身份标识,兼容版本控制
* 3.除了当前Person类需要实现Serializable接口之外,也必须保证其内部所有属性也必须是可序列化的
* (默认情况下,基本数据类型是可序列化的),如果有内部类的话该内部类也要可序列化设置
*
* ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
*/
public class Person implements Serializable {
public static final long serialVersionUID = 42123354653L;
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试
public class ObjectStream {
/*
序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
使用ObjectOutputStream实现
*/
@Test
public void test(){
ObjectOutputStream obs = null;
try {
//1.实例化对象流
obs = new ObjectOutputStream(new FileOutputStream("object.dat"));
//2.写入对象数据
obs.writeObject(new String("对像流测试"));
obs.flush();//刷新操作
obs.writeObject(new Person("张明均",20));
obs.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
obs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
反序列化过程:将磁盘文件中的对象还原为内存中的一个java对象
使用ObjectOutputStream进行实现
*/
@Test
public void test1(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = ois.readObject();
String str = (String)obj;
Person person =(Person)ois.readObject();
System.out.println(str);
System.out.println(person);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
NIO
1.NIO的使用说明:
Java NIO (New I0, Non-Blocking I0) 是从Java 1. 4版本开始引入的一套新的I0 API, 可以替代标准的Java I0 API。
NIO与原来的I0同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的I0操作。
NIO将以更加高效的方式进行文件的读写操作。
随着JDK 7的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为NIO2 。
随机存储流RandomAccessFile
RandomAccessFile
的使用
1.RandomAccessFile直接继承于java. lang. object
类, 实现了DataInput
和DataOutput
接口
2.RandomAccessFile 既可以作为一个输入流,又可以作为一个输出流
3.如果RandomAccessFile作为输出流时, 写出到的文件如果不存在,则在执行过程中自动创建
如果写出的文件存在,则会对原有文件内容进行覆盖。(默认情况下, 从头覆盖)
- 可以通过操作,实现
RandomAccessFile
“插入”的效果,使用seek()
定位,将该位置后面的所有字符转移到内存中,"插入"完成后再将内存中的字符复写到原文件中;存在内存占用过多的问题
public class Random {
@Test
public void test(){
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
raf1 = new RandomAccessFile(new File("zmj.jpg"),"r");//可读
raf2 = new RandomAccessFile(new File("zmj2.jpg"),"rw");//可读写
byte[] bytes = new byte[1024];
int len;
while ((len=raf1.read(bytes))!=-1){
raf2.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test2()throws Exception{
RandomAccessFile rw = new RandomAccessFile(new File("zmj.txt"), "rw");
rw.seek(3);
rw.write(123);
rw.close();
}
}