Java中的输入输出流是Java的高级特性之一,其主要分为输入流和输出流,按照输入输出格式又分为字节流和字符流,按照这个规则拓展又出现了多个子类,具体可以参照下图。
1、字节流和字符流的主要区别
字符流主要是Reader和Writer,字节流主要是InputStream和OutPutStream。这其中的最要区别在于,字符流是以字符的形式进行传输,而字节流的传输是按照字节来进行传输的,一个字节byte有8位,值在-128~127之间。而一个字符则按照编码方式的不同有所差异,目前主流的Unicode编码,无论是中文还是英文都是占用的2个字节。字符的本质上是按照一定编码规则组成的字节。
因此,我们在使用字符流和字节流的时候就可以知道何时何地怎么使用了,通常我们在传输MP3、视频、音乐、图片等格式的文件时,就应该使用字节流,而在传输文本、固定格式文件时一般来说时候Reader、Writer这样的字符流。
2、Java IO字节流输入输出InputStream、OutputStream
我们可以通过一个小栗子来举例说明字节输入输出流:
package com.dingtao.test;
import java.io.*;
public class Solution {
public static void main(String[] args) throws IOException {
String s1 = "hello outputStream~~";
File file = new File("d://outputStream.txt");
OutputStream outputStream = new FileOutputStream(file);
try {
outputStream.write(s1.getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
outputStream.close();
}
}
}
输出为:
这里执行完之后,就可以在d盘的根目录下找到outputStream .txt文件,里面内容是我们输入的hello outputStream~~,这里演示的就是通过outputStream将String输出到一个txt文件中去,要注意的是,这里一定要使用.getBytes()方法将String转化为byte字节流才可以使用write方法。
同理 InputStream,我们可以使用它把 文件里的内容输出到java程序中去,具体代码:
package com.dingtao.test;
import java.io.*;
public class Solution {
public static void main(String[] args) throws IOException {
// String s1 = "hello outputStream~~";
// File file = new File("d://outputStream.txt");
// OutputStream outputStream = new FileOutputStream(file);
// try {
// outputStream.write(s1.getBytes());
// } catch (IOException e) {
// e.printStackTrace();
// }finally {
// outputStream.close();
// }
File file = new File("d://outputStream.txt");
InputStream inputStream = new FileInputStream(file);
int flen = (int) file.length();
byte[] b = new byte[flen];
int len =0;
try {
len = inputStream.read(b);
}catch (IOException e1){
e1.printStackTrace();
}finally {
inputStream.close();
}
System.out.println(len);
System.out.print(new String(b));
}
}
输出为:
从这里就可以看出,我们利用输入流InputStream将文件中的内容输出到了程序中,这里输出的是一个byte[]数组,需要通过new String()构造成一个字符串输出既可。inputStream.read(b)的输出是该输入流的长度length。
3、字符输入输出流Reader、Writer
上个小节我们通过字节输入输出流学习了InputStream和OutPutStream,这里我们来看看Reader和Writer。首先还是来个小栗子。
package com.dingtao.test;
import java.io.*;
public class Solution {
public static void main(String[] args) throws IOException {
File file = new File("D://writer.txt");
Writer writer = new FileWriter(file);
writer.write("你好呀,赛利亚");
writer.close();
}
}
结果为:
这里就可以看到,使用方法基本一致,除了中间用于融合File类和Writer的类FileWriter类不一样之外,其他基本一致,这里的汉子一样被写入到了文件中去。
再将该文件,Reader读取到Java内存中去:
package com.dingtao.test;
import java.io.*;
public class Solution {
public static void main(String[] args) throws IOException {
// File file = new File("D://writer.txt");
// Writer writer = new FileWriter(file);
// writer.write("你好呀,赛利亚");
// writer.close();
File file = new File("D://writer.txt");
Reader reader = new FileReader(file);
int flen = (int) file.length();
char[] c = new char[flen];
int rlen = reader.read(c);
System.out.println(rlen);
System.out.println(new String(c));
}
}
输出为:
4、文件随机读取RandomAccessFile
上面的两种方法,都只能从文件一开始的开头进行读取,一旦我想要读取文件中间的某个信息,就会浪费大量的I/O资源,因此这里就诞生了随机读取RandomAccessFile,这个类可以帮助我们直接从某个地方开始读取信息。具体代码如下:
package com.dingtao.test;
import java.io.*;
public class Solution {
public static void main(String[] args) throws IOException {
File file = new File("D://writer.txt");
RandomAccessFile randomAccessFile = new RandomAccessFile(file,"rw");
randomAccessFile.seek(4);
long l = randomAccessFile.getFilePointer();
byte[] b = new byte[(int) file.length()];
int rlen = randomAccessFile.read(b);
System.out.println(rlen);
System.out.println(new String(b));
}
}
具体结果如下:
这里会乱码的主要原因是在于,char字符类型通过Unicode编码是两个字节组成的,所以就会产生乱码。再看看通过这个进行特定节点的写入。
package com.dingtao.test;
import java.io.*;
public class Solution {
public static void main(String[] args) throws IOException {
File file = new File("D://writer.txt");
RandomAccessFile randomAccessFile = new RandomAccessFile(file,"rw");
randomAccessFile.seek(5);
long l = randomAccessFile.getFilePointer();
byte[] b = "嘿嘿嘿".getBytes();
randomAccessFile.write(b);
System.out.println(randomAccessFile.getFilePointer());
randomAccessFile.close();
}
}
结果是:
如果所示,这段话插入到了seek(5)的地方并保存了下来。
5、Java缓冲输入输出流
Java的缓冲输入输出流的主要应用场景在于,I/O的速度和CPU的速度不匹配的原因造成的,所以通过缓存,一次性将大量的数据刷入内存或者保存在本地上。具体可以举例看下以下的代码,使用BufferReader包装以下既可:
package com.dingtao.test;
import java.io.*;
public class Solution {
public static void main(String[] args) throws IOException {
File file = new File("D://writer.txt");
Reader reader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(reader);
char[] c = new char[(int) file.length()];
int blen = bufferedReader.read(c);
bufferedReader.close();
reader.close();
System.out.println(blen);
System.out.println(new String(c));
}
}
输出为:
至于其他类型的BufferInputStream、BufferOutputStream、BufferWriter在这里就不一一举例了。
6、Java管道IO
PipedInputStream、PipedOutputStream主要用户进程间通信,具体例子如下:
package com.dingtao.test;
import java.io.*;
public class Solution {
public static void main(String[] args) throws IOException {
PipedInputStream pipedInputStream = new PipedInputStream();
PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
pipedOutputStream.write("hello,i am pipedOutputStream".getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
pipedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
byte[] b = new byte[1024];
pipedInputStream.read(b);
System.out.println(new String(b));
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
pipedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}
}