Java中IO流的体系结构如图
Java流类的类结构图
1:File
File 类是 IO 包中唯一代表磁盘文件本身的对象,File 类定义了一些与平台无关的方法 来操纵文件,通过调用 File 类提供的各种方法,我们能够创建、删除文件,重命名文件, 判断文件的读写权限及是否存在,设置和查询文件的最近修改时间。 在 Java 中,目录也被当作 File 使用,只是多了一些目录特有的功能——可以用 list 方法列出目录中的文件名。在 Unix 下的路径分隔符为(/),在 Dos 下的路径分隔符为(\)
, Java 可以正确处理 Unix 和 Dos 的路径分隔符,即使我们在 Windows 环境下使用(/)作为 路径分隔符,Java 仍然能够正确处理。 我们用下面的一个简单应用来演示一下 File 类用法,判断某个文件是否存在,存在则 删除,不存在则创建,读者可以在 Windows 的资源管理器下观察到这个变化
package com.example.springboot.test.IOTestDemo;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
/**
* * * GOOK LUCK * *
*
* @Author by wukainian,
* @Date on 2018/12/11.
*/
public class IODemo01 {
@Test
public void test01() {
String filePath = "E:\\test\\test.txt";
File file = new File(filePath);
//判断路径文件是否存在
if(file.exists()) {
//删除该文件
file.delete();
}else {
try {
//不存在创建一个文件
Boolean b = file.createNewFile();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
System.out.println(file.getName());
}
}
二 FileInputStream 与 FileOutputStream
这两个流节点用来操作磁盘文件,在创建一个 FileInputStream 对象时通过构造函数指 定文件的路径和名字,当然这个文件应当是存在的和可读的。在创建一个 FileOutputStream 对象时指定文件如果存在将要被覆盖。 下面是对同一个磁盘文件创建 FileInputStream 对象的两种方式。其中用到的两个构造 函数都可以引发 FileNotFoundException 异常: FileInputStream inOne=new FileInputStream("hello.test"); File f = new File("hello.test"); FileInputStream inTwo = new FileInputStream(f); 尽管第一个构造函数更简单,但第二个构造函数允许在把文件连接到输入流之前对文件 做进一步分析。 FileOutputStream 对象也有两个和 FileInputStream 对象具有相同参数的构造函数, 创建一个 FileOutputStream 对象时,可以为其指定还不存在的文件名,但不能是存在的目 录名,也不能是一个已被其他程序打开了的文件。FileOutputStream 先创建输出对象,然 后再准备输出。
/**
* 写内容到文件
* FileOutPutStream
* write
*/
@Test
public void test01() {
String filePath = "E:\\test\\test.txt";
File file = new File(filePath);
try {
FileOutputStream outputStream = new FileOutputStream(file);
byte[] b = "helloword".getBytes();
outputStream.write(b);
outputStream.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
/**
* 读文件内容
* FileOutPutStream
* write
*/
@Test
public void test03() {
String filePath = "E:\\test\\test.txt";
File file = new File(filePath);
try {
FileInputStream inputStream = new FileInputStream(file);
byte[] b = new byte[1024];
int i = inputStream.read(b);
System.out.println(new String(b,0,i));
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
三 Reader与 Writer
Java 中的字符是 unicode 编码,是双字节的,而 InputStream 与 OutputStream 是用来 处理字节的,在处理字符文本时不太方便,需要编写额外的程序代码。Java 为字符文本的 输入输出专门提供了一套单独的类, Reader、Writer 两个抽象类与 InputStream、 OutputStream 两个类相对应,同样,Reader、Writer 下面也有许多子类,对具体 IO 设备进 行字符输入输出,如 FileReader 就是用来读取文件流中的字符。
/**
* 写内容到文件
* FileWrite
* write
*/
@Test
public void test04() {
String filePath = "E:\\test\\test.txt";
File file = new File(filePath);
try {
FileWriter fileWriter = new FileWriter(filePath);
fileWriter.write("wellcom!");
fileWriter.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
/**
* 读文件内容
* FileRead
* write
*/
@Test
public void test05() {
String filePath = "E:\\test\\test.txt";
File file = new File(filePath);
try {
FileReader fileReader = new FileReader(filePath);
char[] chars = new char[1024];
int i = fileReader.read(chars);
System.out.println(new String(chars,0,i));
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
四 PipedInputStream 与 PipedOutputStream
一个 PipedInputStream 对象必须和一个 PipedOutputStream 对象进行连接而产生一个 通信管道,PipedOutStream 可以向管道中写入数据,PipedInputStream 可以从管道中读取 PipedOutputStream 写入的数据。这两个类主要用来完成线程之间的通信,一个线程的 PipedInputStream 对象能够从另外一个线程的 PipedOutputStream 对象中读取数据。
package com.example.springboot.test.IOTestDemo;
import java.io.IOException;
import java.io.PipedOutputStream;
/**
* * * GOOK LUCK * *
*
* @Author by wukainian,
* @Date on 2018/12/12.
*/
public class Sender extends Thread{
private PipedOutputStream out=new PipedOutputStream();
public PipedOutputStream getOutputStream() {
return out;
}
@Override
public void run() {
String s=new String("hello,receiver ,how are you");
try {
out.write(s.getBytes());
out.close();
} catch(IOException e) {
System.out.println(e.getMessage());
}
}
}
package com.example.springboot.test.IOTestDemo;
import java.io.IOException;
import java.io.PipedInputStream;
/**
* * * GOOK LUCK * *
*
* @Author by wukainian,
* @Date on 2018/12/12.
*/
public class Receiver extends Thread {
private PipedInputStream in=new PipedInputStream();
public PipedInputStream getInputStream() {
return in;
}
@Override
public void run() {
String s = null;
byte [] buf = new byte[1024]; try {
int len =in.read(buf);
s = new String(buf,0,len);
System.out.println("the following message comes from sender:\n"+s);
in.close();
} catch(IOException e) {
System.out.println(e.getMessage());
}
}
}
@Test
public void test06() {
try {
Thread t1 = new Sender();
Thread t2=new Receiver();
PipedOutputStream out = ((Sender) t1).getOutputStream();
PipedInputStream in = ((Receiver) t2).getInputStream();
out.connect(in);
t1.start();
t2.start();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
过滤流与包装类
BufferedInputStream 与 BufferedOuputStream
对 I/O 进行缓冲是一种常见的性能优化。缓冲流为 I/O 流增加了内存缓冲区。增加缓冲 区有两个基本目的: � 允许 Java 的 I/O 一次不只操作一个字节,这样提高了整个系统的性能。 � 由于有缓冲区,使得在流上执行 skip、mark 和 reset 方法都成为可能。 BufferedInputStream BufferedInputStream BufferedInputStream BufferedInputStream Java 的 BufferedInputStream 类可以对任何的 InputStream 进行带缓冲区的封装以达 到性能的改善。BufferedInputStream 有两个构造函数: BufferedInputStream(InputStream in) BufferedInputStream(InputStream in,int size) 第一种形式的构造函数创建了一个带有 32 字节缓冲区的缓冲流,第二种形式的构造函 数按指定的大小来创建缓冲区。通常缓冲区大小是内存、磁盘扇区或其它系统容量的整数倍, 这样就可以充分提高 I/O 的性能。一个最优的缓冲区的大小,取决于它所在的操作系统、可 用的内存空间以及机器的配置。 对输入流进行缓冲可以实现部分字符的回流。除了 InputStream 中常用的 read 和 skip 方法,BufferedInputStream 还支持 mark 和 reset 方法。mark 方法在流的当前位置作一个 标记,该方法接收的一个整数参数用来指定从标记处开始,还能通过 read 方法读取的字节 数,reset 方法可以让以后的 read 方法重新回到 mark 方法所作的标记处开始读取数据
DataInputStream 与 DataOutputStream
这两个类提供了可以读写各种基本数据类型的数据的各种方法,这些方法是使用非常简 单,在前面我们已经看到了 DataOutputStream 类的大部分方法,在 DataInputStream 类有 与这些 write 方法对应的 read 方法,读者可以在 JDK 文档帮助查看详细的信息
@Test
public void test07() {
String filePath = "E:\\test\\test.txt";
File file = new File(filePath);
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);
dataOutputStream.writeBytes("2sfsdgdfhthjhg11");
dataOutputStream.writeBytes("3sfsdgdfhthjhg14");
dataOutputStream.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
ObjectInputStream 与 ObjectOutputStream
这两个类是用于存储和读取对象的输入输出流类,不难想象,我们只要把对象中的所有 成员变量都存储起来,就等于保存了这个对象,我们只要读取到一个对象中原来保存的所有 成员变量的取值,就等于读取到了一个对象。ObjectInputStream 与 ObjectOutputStream 类,可以帮我们完成保存和读取对象成员变量取值的过程,但要读写或存储的对象必须实现 了 Serializable 接口,Serializable 接口中没有定义任何方法,仅仅被用作一种标记,以 被编译器作特殊处理。ObjectInputStream 与 ObjectOutputStream 类不会保存和读取对象 中的transient和static类型的成员变量,使用ObjectInputStream与ObjectOutputStream 类保存和读取对象的机制叫序列化,如下面定义了一个可以被序列化的 MyClass 类:
@Test
public void test08() {
String filePath = "E:\\test\\test.txt";
File file = new File(filePath);
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
UserEntity userEntity = new UserEntity(1L,"d",1,"dd");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(userEntity);
objectOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void test09() {
String filePath = "E:\\test\\test.txt";
File file = new File(filePath);
try {
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Object o = objectInputStream.readObject();
System.out.println(o);
objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
字节流与字符流的转换
,Java 支持字节流和字符流,我们有时需要字节流和字符流之间的转换。 InputStreamReader 和 OutputStreamWriter 这两个类是字节流和字符流之间转换的类,InputStreamReader 可以将一个字节流中的 字节解码成字符,OuputStreamWriter 将写入的字符编码成字节后写入一个字节流。其中 InputStreamReader 有两个主要的构造函数: InputStreamReader(InputStream in) //用默认字符集创建一个 InputStreamReader 对象
InputStreamReader(InputStream in,String CharsetName) //接受以指定字符集名的字符串,并用
//该字符集创建对象
OutputStreamWriter 也有对应的两个主要的构造函数: OutputStreamWriter(OutputStream in) //用默认字符集创建一个 OutputStreamWriter 对象
OutputStreamWriter(OutputStream in,String CharsetName) //接受以指定字符集名的字符串,
//并用该字符集创建 OutputStreamWriter 对象 为了达到最好的效率,避免频繁的字符与字节间的相互转换,我们最好不要直接使用这 两个类来进行读写,应尽量使用 BufferedWriter 类包装 OutputStreamWriter 类,用 BufferedReader 类包装 InputStreamReader。
例如:
BufferedWriter out=new BufferedWriter(newOutputStreamWriter(System.out));
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
我们接着从一个更实际的应用中来熟悉 InputStreamReader 的作用,怎样用一种简单的 方式一下就读取到键盘上输入的一整行字符?只要用下面的两行程序代码就可以解决这个 问题:
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String strLine = in.readLine();