流的本质是数据传输,根据数据传输特点抽象为不同的流对象,方便直观地进行数据操作。
根据处理的数据类型分为字节流和字符流。
字节流以字节(8bit)为单位,字符流以字符为单位。字节流能处理所有类型的数据(如图片,视频等),而字符流只能处理字符类型的数据。
1、InputStream
InputStream是所有输入字节流的父类,是一个抽象类。
1.1、FilterInputStream
1、InputStream
InputStream是所有输入字节流的父类,是一个抽象类。
ByteArrayInputStream,StringBufferInputStream(已弃用Deprecated,用StringReader代替),FileInputStream是三种基本的介质流,分别从Byte数组、StringBuffer和本地文件中读取数据。PipedInputSteam是从与其它线程共用的管道中读取数据。SequenceInputStream是把多个inputStream合成一个InputStream。而FilterInputStream和所有的FilterInputStream的子类都是装饰者模式(设计模式)中的装饰类。FilterInputStream的子类不能单独使用。
1.1、FilterInputStream
FilterInputStream类的构造方法是protected的,所以我们不直接使用此类,而是使用它的子类。
在FilterInputStream中的子类有:
BufferedInputStream (在内存中创建一个默认值8KB字节大小的缓冲区进行字节流读操作)
DataInputStream (能以一种与机器无关的方式,直接从字节流中读取JAVA基本类型和String类型的数据,常用于网络传输)
LineNumberInputStream (Deprecated 建立带有行功能过滤输入流)
PushbackInputStream (拥有一个Puchback缓冲区,若缓冲区没有满,则可以使用unread方法将数据推回流的前端,一般用于检测字节流错误)
还有诸如CheckedInputStream、DigestInputStream等,这里不展开,有需要可以自行查阅API。
2、OutputStream
2、OutputStream
而OutputStream是所有输出字节流的父类,是一个抽象类。
与InputStream相基本相对应,ByteArrayOutputStream和FileOutputStream是两种基本的介质流,分别向Byte数组和本地文件中写入数据。
PipedOutputStream是与其它线程共用的管道中写入数据。而FilterOutputStream和FilterOutputStream的子类都是装饰者模式(设计模式)中的装饰类。
2.1、FilterOutputStream
3、InputStream和OutputStream
3.1、InputStream
2.1、FilterOutputStream
FilterOutputStream类也是一个protected的类,所以不能直接使用该类,在
FilterOutputStream中的子类有:
BufferedOutputStream (与BufferedInputStream相反,创建了一个缓冲区进行字节流写操作,先写入缓冲区,缓冲区满了再写入文件或其他输出源)
DataOutputStream (与DataInputStream相反,将JAVA基础类和String类型直接写入到文件或者其他输出源)
PrintStream (System类中的in、out和err对象就是PrintStream类的实例,print用法大家应该都会)
3、InputStream和OutputStream
因为InputStream和OutputStream一般是整对出现,所以我们现在逐对类进行讲解,这里先给出个大纲:
FileInputStream与FileOutputStream 对文件进行读写
ByteArrayInputStream与ByteArrayOutputStream 对 Byte数组进行读写,一般用于内存数据(虚拟文件或则虚拟镜像)转换或缓存
DataInputStream与DataOutputStream 与机器无关地读写JAVA和String类,与文件流和Byte数组流等配合使用。
BufferedInputStream与BufferedOutputStream 创建一个buffer缓冲区进行读写操作与文件流和Byte数组流等配合使用。
3.1、InputStream
InputStream的基本操作:
3.2、OutputStream
InputStream in = new InputStream(filePath);
int b = in.read(); //读取一个byte字节填充int的低八位, 没有返回-1
int len = in.read(byte[] buff); //将流读取到buff中,并返回读取的byte的长度
int len = in.read(byte[] buff, int start, int size); //将流读取到buff中,从哪个位置读,读取多少size,返回已读取的byte的长度
in.close(); //关闭流
OutputStream的基本操作:
3.3、FilterInputStream和FileOutputStream
TestFileOutputStream
3.4、ByteArrayInputStream和ByteArrayOutputStream
OutputStream out = new OutputStream(filePath);
out.write(int b); //写入一个byte
out.write(byte[] b); //写入一个byte数组
out.write(byte[] b, int start, int size); //将b的一部分写入
out.flush(); //清理缓存
out.close(); //关闭流
3.3、FilterInputStream和FileOutputStream
使用FileInputStream和FileOutputStream对文件进行读写
TestFileInputStream
public class TestFileInputStream {
public static void main(String[] args) throws IOException {
//D:/users/rich/Workspaces/MyEclipse 10/NIO/src/rich/stream/in.txt
//注意这里的相对路径,是要把package都放进去
String file = "src/rich/stream/in.txt";
FileInputStream in = new FileInputStream(file);
int b;
while((b = in.read()) != -1) {
//不用char转换会读出ASCII码的值
System.out.print((char) b);
}
in.close();
System.out.println();
in = new FileInputStream(file);
//available返回剩下还没读的字节数,这里读取文件长度
byte[] buff = new byte[in.available()];
in.read(buff); //把数据读到buff中
in.close(); //可以关闭流
for(byte c: buff) {
System.out.print((char)c);
}
}
}
TestFileOutputStream
public class TestFileOutputStream {
public static void main(String[] args) throws IOException {
//注意文件路径
String file = "src/rich/stream/out.txt";
FileOutputStream out = new FileOutputStream(file);
String str = "Hi, I am Rich.";
byte[] b = str.getBytes();
out.write(b); //写入文件
out.flush(); //清理缓存
out.close(); //关闭流
//将读入流文件写入输出流文件
TestFileOutputStream.fileInputStreamToOutputStream();
}
public static void fileInputStreamToOutputStream() throws IOException {
String inFile = "src/rich/stream/in.txt";
FileInputStream in = new FileInputStream(inFile); //读取文件源
byte[] buff = new byte[in.available()]; //使用一个byte数组
in.read(buff); //把文件内容写入到buff数组中
in.close(); //关闭读入流
String outFile = "src/rich/stream/out1.txt";
FileOutputStream out = new FileOutputStream(outFile); //获取写入源
out.write(buff); //写入文件
out.flush(); //清理缓存
out.close(); //关闭写入流
}
}
3.4、ByteArrayInputStream和ByteArrayOutputStream
ByteArrayInputStream与ByteArrayOutputStream 对 Byte数组进行读写,一般用于内存数据(虚拟文件或则虚拟镜像)转换或缓存
TestByteArrayStream
3.5、DataInputStream和DataOutputStream
DataInputStream与DataOutputStream 与机器无关地读写JAVA和String类,与文件流和Byte数组流等配合使用。
TestDataInputStream
TestDataOutputStream
3.6、 BufferedInputStream与BufferedOutputStream
4、Reader和Writer
4.1、Reader
TestByteArrayStream
public class TestByteArrayStream {
public static void main(String[] args) throws IOException {
//这里只是构建一个string放在内存中,一般使用该Stream类是去读取虚拟文件
byte[] buff = new String("I am Rich again").getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(buff);
//这里不做打印操作,直接把read出来的数据放入到ByteArrayOutputStream中
//不需指定数组,内置存有一个byte数组
ByteArrayOutputStream out = new ByteArrayOutputStream();
int b;
while((b = in.read()) != -1) { //读出数据
//写入数据,会把数据写入到out的内置数组中
out.write(Character.toUpperCase(b));
}
in.close(); //关闭输入流
System.out.println(out.toString());
out.close(); //关闭输出流
}
}
3.5、DataInputStream和DataOutputStream
DataInputStream与DataOutputStream 与机器无关地读写JAVA和String类,与文件流和Byte数组流等配合使用。
TestDataInputStream
public class TestDataInputStream {
public static void main(String[] args) throws IOException {
//DataInputStream不能单独使用,这是装饰类
String file = "src/rich/stream/in1.txt";
FileInputStream fileIn = new FileInputStream(file);
DataInputStream dataIn = new DataInputStream(fileIn); //读进输入源
//DataInputStream对象增强了FileInputStream的read方法
//有readInt,看源码其实就是连续读取4个字节,即连续调用四个read方法
int b = dataIn.readInt();
//有readChar,readFloat等等。这里不一一实现。不过感觉并不好用
char c = dataIn.readChar();
System.out.println(b);
System.out.println(c);
dataIn.close();
}
}
TestDataOutputStream
public class TestDataOutputStream {
public static void main(String[] args) throws IOException {
String inFile = "src/rich/stream/in.txt";
FileInputStream fileIn = new FileInputStream(inFile); //读出数据源
String outFile = "src/rich/stream/out2.txt";
DataOutputStream dataOut =
new DataOutputStream(new FileOutputStream(outFile)); //增强写入源
int b;
while((b = fileIn.read()) != -1) {
dataOut.writeByte(b); //把in.txt的数据写入到out2.txt
}
fileIn.close();
dataOut.close();
}
}
3.6、 BufferedInputStream与BufferedOutputStream
BufferedInputStream与BufferedOutputStream 创建一个buffer缓冲区进行读写操作
,与文件流和Byte数组流等配合使用。
TestBufferStream
public class TestBufferStream {
public static void main(String[] args) throws IOException {
String file = "src/rich/stream/in.txt";
//装饰者模式,这样in这个读出源就拥有了一个buffer数组进行缓存,改善性能
BufferedInputStream in = new BufferedInputStream(
new FileInputStream(file));
while(in.available() > 0) {
System.out.print((char)in.read());
}
in.close();
System.out.println();
//可以继续封装,如封装了BufferedInputStream的DataInputStream类,
//拥有了BufferedInputStream的缓冲数组
DataInputStream in1 = new DataInputStream(
new BufferedInputStream(new FileInputStream(file)));
byte[] newBuff = new byte[in1.available()];
in1.read(newBuff);
for(byte c: newBuff) {
System.out.print((char)c);
}
in1.close();
//BufferOutSream的作用相反
byte[] outBuff = "It's an output stream.".getBytes();
String outfile = "src/rich/stream/out3.txt";
BufferedOutputStream out = new BufferedOutputStream(
new FileOutputStream(outfile));
for(byte b: outBuff) {
out.write(b);
}
out.close();
}
}
4、Reader和Writer
讲完了字节流,现在是字符流,Reader和Writer类主要用于Unicode编码的字符,而InputStream和OutputStream用于ASCII字符和二进制数据。注意:UTF-8是Unicode的其中一种存储方式。同时要理解,Reader和Writer是对字节流的扩充,底层还是使用字节流进行数据传输。
4.1、Reader
Reader是一个抽象类,用于读取字符流中的数据(文本文件,字符串,基本数据类型等)
TestBufferedReader
4.1、Writer
InputStreamReader是最常用的类,一般用于对InputStream中的字符进行读取操作,并指定编码格式,不填默认使用系统编码格式;而它的子类FileReader可以直接打开文件进行字符流读写,而FileInputStream则是字节流读入。而BufferedReader是一个装饰类,能够封装Reader,并一次读取一行数据。
TestInputStreamReader
public class TestInputStreamReader {
public static void main(String[] args) throws IOException {
String file = "src/rich/reader/in.txt";
FileInputStream in = new FileInputStream(file);
//如果不设置编码格式,会获取程序默认编码格式,跟上面那行是同一个意思
// InputStreamReader reader = new InputStreamReader(in);
InputStreamReader reader = new InputStreamReader(in, "GBK");
int b;
while((b = reader.read()) != -1) {
System.out.print((char)b);
}
reader.close();
in.close();
System.out.println();
//字节流不支持中文
in = new FileInputStream(file);
while(in.available() > 0) {
System.out.print((char)in.read());
}
in.close();
}
}
TestBufferedReader
public class TestBufferedReader {
public static void main(String[] args) throws IOException {
String file = "src/rich/reader/in.txt";
FileReader fileReader = new FileReader(file); //获取文件Reader
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
//使用bufferedReader的readline方法
while((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
//关闭流
bufferedReader.close();
//可以使用装饰嵌套InputStreamReader指定编码格式
FileInputStream in = new FileInputStream(file);
BufferedReader bufferedReaderForInput = new BufferedReader(
new InputStreamReader(in, "GBK"));
while((line = bufferedReaderForInput.readLine()) != null) {
System.out.println(line);
}
}
}
4.1、Writer
Writer也是一个抽象类,用于将字符数据写入文件或其他输出源,其中的write方法可以直接写入字符串。
OutputStreamReader是较为常用的类,用于对OutputStream的字符流进行写入,并指定编码格式。相对的,FileWriter是对文件字符流进行写入,FileOutputStream是对字节流进行写入。而BufferedWriter提供与系统文件格式无关的newLine方法,保证另起一行。而不是在字符串后面加"\r\n"。
TestOutputStreamWriter
TestBufferedWriter
TestOutputStreamWriter
public class TestOutputStreamWriter {
public static void main(String[] args) throws IOException {
String file = "src/rich/writer/out.txt";
FileOutputStream out = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(out);
String str1 = "我是一只小小小小鸟\r\n";
String str2 = "想要飞,却怎么也没不高\r\n";
writer.write(str1);
writer.write(str2);
writer.close();
}
}
TestBufferedWriter
public class TestBufferedWriter {
public static void main(String[] args) throws IOException {
String file = "src/rich/writer/out1.txt";
FileWriter fileWriter = new FileWriter(file); //写入文件源
BufferedWriter writer = new BufferedWriter(fileWriter);
String str1 = "我是一只小小小小鸟";
String str2 = "想要飞,却怎么也没不高";
String str3 = "I am a Chinese";
writer.write(str1); //写入字符串
writer.newLine(); //加入一个换行符
writer.write(str2);
writer.newLine();
writer.write(str3);
writer.close();
//使用OutputStreamWriter作为输出文件源,并指定编码格式
BufferedWriter fileWriterForOutput = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(file), "GBK"));
fileWriterForOutput.write(str1);
fileWriterForOutput.newLine();
fileWriterForOutput.write(str2);
fileWriterForOutput.close();
}
}
最后给出一个深复制代码,感受一下io的魅力
TestCopyObject
public class TestCopyObject {
public static void main(String[] args) throws Exception {
TestCopyObject test = new TestCopyObject();
//User对象是否与另外一个User对象一致
User user = new User("rich", 26);
User user1 = test.deepClone(user); //进行深复制
user.setName("jeff");
//如果是同一引用,user的name应该为jeff
System.out.println(user1.getName());
}
public User deepClone(User user) throws Exception {
//Byte数组输出流,内置一个Byte数组,使用toByteArray方法读出数据
ByteArrayOutputStream out = new ByteArrayOutputStream();
//序列化Byte数组中的数据,即放入ByteArrayOutputStream的对象都是可序列化的
//JAVA基本类型和String类型都已实现 java.io.Serializable
ObjectOutputStream objectOut = new ObjectOutputStream(out);
objectOut.writeObject(user); //写入对象
//toByteArray方法是复制一个byte数组,而不是直接引用
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
//读取序列化数据
ObjectInputStream objectIn = new ObjectInputStream(in);
//读取第一个对象(要cast)
return (User)objectIn.readObject();
}
}
参考文章: