I/O流入门
前言
Java IO流入门,了解IO简介、IO流入门案例、File类的使用、常用流对象、Apache IO包。
一、IO简介
1、什么是IO流?
1)与外部设备进行通信,进行数据交换,即Input/Output data。
2)Java IO API 屏蔽外部设备的差异性。
2、数据源(Data Source)
以程序为参考对象。
1)源设备:向程序传输所需数据的设备。
2)目的设备:程序将数据存储到外部的设备。
3、流的概念
流是一个抽象、动态的概念,这里指的数据流,数据以一定容量为单位不断流入程序或外部设备。
数据流即一连串连续动态的数据集合。
4、Java四大IO抽象类
这四个抽象类奠定了IO的基础,分别为:
1)字节流,以一字节(8bit)作为基本单位。
A)InputStream
int read(),返回一个0-255的十进制 或 -1,分别表示读取的字符和已读完。
void close(),关闭字节流输入对象,释放相关系统资源。
B)OutputStream
void write(int n),向目的设备写一个字节的二进制。
void close(),关闭字节流输出对象,释放相关系统资源。
2)字符流,以一个字符为基本单位。
A)Reader
int read(),读取字符数据,返回一个0-65535 的十进制,即Unicode字符,或者返回-1。分别表示读出的字符和已读完。
void close(),关闭字节流输出对象,释放相关系统资源。
B)Writer
void write(int n),向目的设备写入一个字符的二进制。
void close(),关闭字节流输出对象,释放相关系统资源。
5、IO流分类
1)按流方向
A)输入流:Data Source -> Program
B)输出流:Program -> Destination
2)按处理单元
A)字节流,以字节为基本单位。
B)字符流,以字符为基本单位,即Unicode字符。
3)按处理对象
A)节点流,直接链接到数据源。
B)处理流,对节点流的包装,让流有更加强大的功能,处理流的流对象。如BufferedInputStream、BufferedOutputStream。
6、Java IO流体系
重点类介绍
1)File,处理目录或文件,包装成类,就能够更好的操作目录和文件。
2)InputStream、OutputStream、Reader、Writer,四大基本抽象类,Java 中IO的规范。
3)FileInputStream / FileOutputStream
节点流,以字节为单位,直接操作文件。
4)ByteArrayInputStream / ByteArrayOutputStream
节点流,以字节为单位,直接处理“字节数组对象”。
5)ObjectInputStream / ObjectOutputStream
处理流,以字节为单位,操作字节流中的对象。
6)DataInputStream / DataOutputStream
处理流,以字节为单位,操作字节流中的基本数据类型与字符串类型。
7)FileReader / FileWriter
节点流,以字符为基本单位,直接操作文本文件。
8)BufferedReader / BufferedWriter
处理流,包装了Reader、Writer,增加了缓存功能,通过缓存减少等待时间来提高读写的效率。
9)BufferedInputStream / BufferedOutputStream
处理流,包装InputStream、OutputStream,增加缓存功能,提高读写效率。
10)InputStreamReader / OutputStreamWriter
处理流,将字节流对象包装成字符流对象。
11)PrintStream
处理流,包装OutputStream,方便输出字符。
二、IO流入门案例
1)只能展示0-255的字符
package com.xhu.java;
import java.io.*;
public class TestIO {
public static void main(String[] args) {
//1.创建字节输入流
try (FileInputStream fis = new FileInputStream("d:/IOCase.txt")) {
int n = 0;
while ((n = fis.read()) != -1)
System.out.print((char) (n));
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
Java 异常进阶
2)展示0-65535,即Unicode字符
package com.xhu.java;
import java.io.*;
import java.util.Arrays;
public class TestIO {
public static void main(String[] args) {
try (FileReader fr = new FileReader("d:/IOCase.txt")) {
StringBuilder sb = new StringBuilder();
int n = 0;
while ((n = fr.read()) != -1) {
sb.append((char) n);
}
System.out.println(sb.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、File类的使用
1、File的常见方法
四、常用流对象
1、文件字节流
1)FileInputStream / FileOutputStream
以字节的方式读取文件,所有文件底层都为二进制,所以这个能读取所有类型的文件和写任意类型的文件。(图像、视频、文本文件)
package com.xhu.java;
import java.io.*;
import java.util.Arrays;
public class TestIO {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("d:/生物钟.jpg");
FileOutputStream fos = new FileOutputStream("d:生物钟副本.png")) {
int n = 0;
while((n = fis.read()) != -1){
fos.write(n);
}
//把内存缓存的数据写入磁盘中
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2)通过缓存区提高读写效率
上面的操作是一个一个字节去读的,一个一个去写,IO的调用次数大多,当图片太大就会很慢。
A)指定长度的字节数组作为缓存区,该长度应为2的整数幂。
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("d:/生物钟.jpg");
FileOutputStream fos = new FileOutputStream("d:生物钟副本.png")) {
//1024位bit,缓存区
byte[] buffer = new byte[1024];
//每次读到buffer中的长度
int n = 0;
//每次读写1024位
while((n = fis.read(buffer)) != -1){
fos.write(buffer,0,n);
}
//把内存缓存的数据写入磁盘中
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
B)根据文件大小来确定buffer长度,相当于一次性读写文件,文件太大,占用内存也会很大,所以推荐小文件。
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("d:/生物钟.jpg");
FileOutputStream fos = new FileOutputStream("d:/生物钟副本.png")) {
//1024位bit,缓存区,文件太大,内存空间占用也大
byte[] buffer = new byte[fis.available()];
int n = fis.read(buffer);
fos.write(buffer, 0, n);
//把内存缓存的数据写入磁盘中
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
C)包装流BufferedInputStream / BufferedOutputStream
缓存区慢或手动提交时,才会把处理数据。底层缓冲数组。
public static void main(String[] args) {
//关闭流顺序,后开先关闭
try (FileInputStream fis = new FileInputStream("d:/生物钟.jpg");
FileOutputStream fos = new FileOutputStream("d:/生物钟副本.png");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
int n = 0;
//buffer的默认长度为2的13次方8192
//private static int DEFAULT_BUFFER_SIZE = 8192;
while ((n = bis.read()) != -1) {
//去写缓冲区的内容,字节转十进制为n
bos.write(n);
}
//把内存缓存的数据写入磁盘中
bos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
2、定义文件拷贝工具类
package com.xhu.java;
import java.io.*;
/**
* 用于复制文件
*/
public class CopyFileTools {
/**
* 复制文件
*
* @param source
* @param dest
* @throws IOException
*/
public static void copy(String source, String dest) throws IOException {
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest)) {
copyFile(fis, fos);
} catch (IOException e) {
throw e;
}
}
/**
* 通过缓冲区提高复制文件的效率
*
* @param fis
* @param fos
* @throws IOException
*/
private static void copyFile(FileInputStream fis, FileOutputStream fos) throws IOException {
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int n = 0;
while ((n = bis.read()) != -1) {
bos.write(n);
}
bos.flush();
}
}
3、文件字符流
1)FileReader / FileWriter
public static void main(String[] args) {
try (FileReader fr = new FileReader("d:/IOCase.txt");
FileWriter fw = new FileWriter("d:/IOCase_copy.txt")) {
int n = 0;
while ((n = fr.read()) != -1) {
fw.write(n);
}
fw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
2)用buffer来提高效率
public static void main(String[] args) throws IOException {
try (FileReader fr = new FileReader("d:/IOCase.txt");
FileWriter fw = new FileWriter("d:/IOCase_copy.txt");
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw)) {
int n = 0;
while ((n = br.read()) != -1) {
bw.write(n);
}
//换行
bw.newLine()
bw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
String str = null;
while ((str = br.readLine()) != null) {
bw.write(str);
}
4、为每行添加行号
public static void main(String[] args) throws IOException {
try (FileReader fr = new FileReader("d:/IOCase.txt");
FileWriter fw = new FileWriter("d:/IOCase_copy.txt");
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw)) {
int n = 0;
String str = null;
StringBuilder sb = new StringBuilder();
while ((str = br.readLine()) != null) {
bw.write(sb.append(++n).append(":").append(str).toString());
//读完一行就换还
bw.newLine();
sb.delete(0, sb.length());
}
bw.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
5、转换流
InputStreamReader / OutputStreamWriter ,将字节流转换为字符流。
如键盘输入一行System.in,用BufferedReader 读一行,键盘输入的是字节流,而BufferedReader是字符流;显示在屏幕的System.out也是字节流,但是我们需要把它变成字符显示在屏幕上。所以需要用到转换流,即一种包装流。
package com.xhu.java;
import java.io.*;
public class TestInputStreamReader {
public static void main(String[] args) {
//把键盘作为数据源
InputStream is = System.in;
//把显示屏作为目的设备
OutputStream os = System.out;
try (InputStreamReader ism = new InputStreamReader(is);
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedReader br = new BufferedReader(ism);
BufferedWriter bw = new BufferedWriter(osw)) {
//输入exit为出口
while (true) {
//将请输入flush到屏幕上
bw.write("请输入:");
bw.flush();
String str = br.readLine();
if ("exit".equals(str)) {
break;
}
//将输入的一行flush到屏幕上
bw.write("你输入的是:" + str);
bw.flush();
//手动换行
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
6、字符输出流
PrintWriter自带换行(print)和flush。
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("d:/IOCase.txt");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
PrintWriter pw = new PrintWriter("d:/IOCase_copy.txt");
) {
int n = 0;
String str = null;
StringBuilder sb = new StringBuilder();
while ((str = br.readLine()) != null) {
pw.println(sb.append((++n)).append(',').append(str));
sb.delete(0, sb.length());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
7、字节数组流
ByteArrayInputStream / ByteArrayOutStream
以字节数组为处理对象,进行读取和写入。
package com.xhu.java;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class TestByteArrayInputStream {
public static void main(String[] args) {
byte[] bytes = "abc".getBytes();
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();) {
int n = 0;
while ((n = bais.read()) != -1) {
//写回字符数组
baos.write(n);
}
//得到字节数组
byte[] res = baos.toByteArray();
for (byte b : res) {
System.out.print((char) b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
8、数据流
DataInputStream / DataOutputStream
字节和字符流便于存储和读取Java中的基本数据类型。
package com.xhu.java;
import java.io.*;
public class TestDataInputStream {
public static void main(String[] args) {
try (DataOutputStream dps = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("d:/data.txt")));
DataInputStream dis = new DataInputStream(new FileInputStream("d:/data.txt"))) {
int[] data = new int[]{1122211111, 2, 3};
for (int n : data) {
dps.writeInt(n);
}
dps.flush();
int n = 3;
while (n-- != 0) {
System.out.println(dis.readInt());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
9、对象流
将对象以二进制的形式进行输入和写出。
1)序列化和反序列化
含义)将Java对象转化为字节序列,为序列化;将二进制序列转化为Java对象,为反序列化。
作用)将对象持久化;网络通信。
序列化类)ObjectInputStream / ObjectOutputStream
writeObject(Object obj),将obj序列化,然后再写到目标输出流中。
Object readObject(),从数据流中读出序列数据,然后将字节序列反序列化为Object对象。
Serializable)一个空接口,起标记作用,只有实现了这个接口的类,其对象才能被序列化。
package com.xhu.java;
import java.io.*;
public class TestObjectInputStream {
public static void main(String[] args) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/data.txt"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/data.txt"));
) {
Person[] ps = new Person[]{new Person("林森"), new Person("李")};
oos.writeInt(ps.length);
for (Person p : ps) {
oos.writeObject(p);
}
//oos.writeObject(null);
//oos.flush();
int len = ois.readInt();
Object obj = null;
while (len-- > 0) {
System.out.println(((Person) ois.readObject()).name);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
10、随机访问流
1)RandomAccessFile
作用1)既可以读入文件,也可以写出文件。
作用2)可以访问文件的任意位置,不像其它流只能顺序读取。
核心函数)
A)RandomAccessFile(String name,String mode),name为文件名,mode为流的访问权限,如r(读)、rw(可读写)。
定位函数)seek(long a),定位流对象读写的位置,a是离文件头的字节数。
getFilePointer(),获取当前文件指针位置。
package com.xhu.java;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class TestRandomAccessFile {
public static void main(String[] args) {
try (RandomAccessFile raf = new RandomAccessFile("d:data.txt", "rw")) {
int[] data = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int n : data) {
raf.writeInt(n);
}
raf.writeInt(Integer.MAX_VALUE);
//把指针移回来
raf.seek(0);
int n = 0;
while ((n = raf.readInt()) != Integer.MAX_VALUE) {
System.out.println(n);
//打印偶数,int 4个字节,当前位置加4个字节就跳过了一位整数。
raf.seek(raf.getFilePointer() + 4);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注:当文件作为数据源或目标时,可以使用File类来表示。File file = new File("d:a.txt"); FileReader fr = new FileReader(file);
五、Apache IO包
为了简化实际开发中复杂的IO操作代码,第三方Apache提供了IO包来简化许多复杂繁琐的IO操作。
Apache-commons工具包中提供了IOUtils / FileUtils类。
1、下载Apache IO包
下载地址:commons.apache.org
2、添加jar包
3、FileUtils
进入docs查看API参考文档,该类提供了很多方法,功能强大。
package com.xhu.java;
import org.apache.commons.io.FileUtils;
import java.io.*;
public class TestFileUtils {
public static void main(String[] args) throws IOException {
File file = new File("d:data.txt");
//读取所有内容
String allContent = FileUtils.readFileToString(file, "UTF-8");
System.out.println(allContent);
//复制文件
FileUtils.copyToFile(new FileInputStream(file), new File("d:data_copy.txt"));
//拷贝文件夹
FileUtils.copyDirectory(new File("d:/file"), new File("d:/file_copy"), new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory() || pathname.getName().endsWith(".txt") ? true : false;
}
});
}
}
4、IOUtils
toString:将输入流或数组中的内容转化为字符串。其它查API参考文档。
package com.xhu.java;
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
import java.io.IOException;
public class TestIOUtils {
public static void main(String[] args) throws IOException {
//字节转字符串
String allContent = IOUtils.toString(new FileInputStream("d:data.txt"), "utf-8");
System.out.println(allContent);
}
}
总结
1)输入流、输出流、节点流、处理流
2)序列化、反序列化
3)常用流对象、FileUtils、IOUtils
参考文献
[1] [JDK 1.12]
[2] Java SE oldLu
[3] Apache IO