面试题:JavaIO流分类详解与常用流用法实例
Java流概念:
Java把所有的有序数据都抽象成流模型,简化了输入输出,理解了流模型就理解了Java IO。可以把流想象成水流,里面的水滴有序的朝某一方向流动。水滴就是数据,且代表着最小的数据流动单位,在字节流中,水滴就是一字节(byte),在字符流中,水滴就是一字符(char)。
Java流的分类方法大致分为以下几种:
1、按流向划分,分为输入流、输出流
请注意,这里的流向是以程序的运行时内存为参照的。
输入流类名中包含关键字InputStream或Reader,输出流类名中包含关键字OutputStream或Writer。
2、按操作的数据单元类型划分,分为字节流、字符流
字节流操作的数据单元是8位的字节(byte),字符流操作的是16位的字符。
字节流类名中包含关键字InputStream或OutputStream,字符流类名中包含关键字Reader或Writer。
请注意,系统输入输出(System.in与System.out)都为字节流。
3、按流的角色来划分,分为节点流与处理流
节点流是指程序可以向一个特定的节点读写数据,直接连接数据源;
这个节点最常见的是文件,类名中包含关键字File;还可以是数组、管道、字符串,关键字分别为ByteArray/CharArray,Piped,String。
处理流并不直接连接数据源,它大多情况是对已存在的节点流进行包装,是一种典型的装饰器设计模式。使用处理流主要是为了更方便的执行输入输出工作,如PrintStream,输出功能很强大,推荐输出时都使用处理流包装。
注意:一个IO流可以即是输入流又是字节流又或是以其他方式分类的流类型,是不冲突的。比如FileInputStream,它既是输入流又是字节流还是文件节点流。
4、一些特别的的流类型
转换流,转换流只有字节流转换为字符流,因为字符流使用起来更方便,我们只会向更方便使用的方向转化。如:InputStreamReader与OutputStreamWriter。
缓冲流,有关键字Buffered,也是一种处理流,为其包装的流增加了缓存功能,提高了输入输出的效率,增加缓冲功能后需要使用flush()才能将缓冲区中内容写入到实际的物理节点。但是,在现在版本的Java中,只需记得关闭输出流(调用close()方法),就会自动执行输出流的flush()方法,可以保证将缓冲区中内容写入。
对象流,有关键字Object,主要用于将目标对象保存到磁盘中或允许在网络中直接传输对象时使用(对象序列化),具体可参看博客Java序列化与反序列化。
推回输入流,有关键字PushBack,当程序调用推回输入流的unread()方法时,系统回把指定数组内容的内容推回到一个推回缓冲区中,在调用read()方法读入内容时,就先从推回缓冲区中读取,直到读完推回缓冲区中内容后才会从原输入流中读取。
必须要掌握的流用法实例:
1、FileInputStream\FileOutputStream\FileReader\FileWriter(使用方法类似)
//文件字节输入流FileInputStream用法
public class TestFileIO1 {
public static void main(String[] args)throws IOException{
//此处路径可以使用相对路径与绝对路径
FileInputStream fileInputStream = new FileInputStream("D:\\Git\\TCCP\\IO\\src\\package1\\TestFileIO1.java");
//一个字节数组作为缓冲,意为每次读取1024个字节,提高效率
byte[] buffer = new byte[1024];
//记录读取的字节数
int hasRead = 0;
//调用read()方法,返回实际读取的字节数
while((hasRead = fileInputStream.read(buffer)) > 0){
System.out.print(new String(buffer, 0, hasRead));
}
//关闭流
fileInputStream.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
//文件字符输入流FileReader用法
public class TestFileIO2 {
public static void main(String[] args)throws IOException{
FileReader fileReader = new FileReader("D:\\Git\\TCCP\\IO\\src\\package1\\TestFileIO2.java");
char[] buffer = new char[32];
int hasRead = 0;
while((hasRead = fileReader.read(buffer)) > 0){
System.out.print(new String(buffer, 0, hasRead));
}
fileReader.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
//文件字节输入流FileInputStream与文件字节输出流FileOutputStream结合
public class TestFileIO3 {
public static void main(String[] args)throws IOException{
File result = new File("output.txt");
FileInputStream fileInputStream = new FileInputStream("D:\\Git\\TCCP\\IO\\src\\package1\\TestFileIO3.java");
FileOutputStream fileOutputStream = new FileOutputStream(result);
byte[] buffer = new byte[1024];
int hasRead = 0;
while((hasRead = fileInputStream.read(buffer)) > 0){
fileOutputStream.write(buffer, 0, hasRead);
}
System.out.println(result.getAbsolutePath());
fileInputStream.close();
fileOutputStream.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
//文件字符输出流FileWriter用法
public class TestFileIO4 {
public static void main(String[] args)throws IOException{
File result = new File("output.txt");
FileWriter fileWriter = new FileWriter(result);
fileWriter.write("飞流直下三千尺,\r\n");
fileWriter.write("疑是银河落九天.\r\n");
fileWriter.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
2、输出处理流PrintStream用法
public class TestFileIO5 {
public static void main(String[] args)throws IOException{
File result = new File("output.txt");
FileOutputStream fileOutputStream = new FileOutputStream(result);
//PrintStream处理流功能极其强大,所有字节输出流都应使用PrintStream包装
PrintStream printStream = new PrintStream(fileOutputStream);
printStream.println("床前明月光,");
fileOutputStream.close();
printStream.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
3、转换流inputStreamReader与缓冲流BufferedReader用法
public class TestFileIO6 {
public static void main(String[] args)throws IOException{
//系统输入为System.in,默认为从键盘输入,是字节输入流InputStream类型
//使用转换流将InputStream转换为Reader字符输入流对象
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
//将Reader包装为字符缓存处理流BufferedReader对象
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//定义缓存行
String bufferString = null;
//使用BufferedReader特色readLine()方法逐行读取输入
while((bufferString = bufferedReader.readLine()) != null){
//直到输入exit,停止程序
if(bufferString.equals("exit")){
System.exit(0);
}
//控制台输出输入内容
System.out.println("输入内容为:" + bufferString);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
4、推回输入流PushbackInputStream
//通过一个返回某个字符串之前的文件内容的demo,理解推回输入流的读取缓存机制
public class TestFileIO7 {
public static void main(String[] args)throws IOException{
//创建一个推回字节输入流对象,指定缓冲区为64
PushbackReader pushbackInputStream = new PushbackReader(new FileReader("D:\\Git\\TCCP\\IO\\src\\package1\\TestFileIO7.java"), 64);
char[] buffer = new char[32];
//记录上次读取的字符串
String lastContent = "";
int hasRead = 0;
while((hasRead = pushbackInputStream.read(buffer)) > 0){
//将读取的字符转换为字符串
String content = new String(buffer, 0, hasRead);
int targetIndex = 0;
//将上次读取的字符串和本次读取的字符串拼接
//查找拼接后的字符串是否包含"new PushbackReader"(文件为此段源代码),返回位置由targetIndex记录
if((targetIndex = (lastContent + content).indexOf("targetIndex")) > 0){
//将拼接后字符串转化成字符数组后推回缓冲区
String newContent = lastContent + content;
pushbackInputStream.unread(newContent.toCharArray());
//定义一个长度为targetIndex的char数组,如果新大小大于32,则需要重新定义
if(targetIndex > 32){
buffer = new char[targetIndex];
}
//再次读取targetIndex长度的内容,其实就是目标字符串之前的内容
pushbackInputStream.read(buffer, 0, targetIndex);
//输出结果
System.out.println(new String(buffer, 0, targetIndex));
//退出程序
System.exit(0);
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
5、重定向标准输入\输入
Java的标准输入为System.in默认为键盘输入,标准输入为System.out默认为屏幕输出。可通过setInt(InputStream in)方法与setOut(PrintStream out)方法修改(在这里,连标准输出的字节输出流都被包装成了PrintStream,我们在编程时有什么理由不适用输出流呢?)。
public class TestFileIO8 {
public static void main(String[] args)throws IOException{
FileInputStream fileInputStream = new FileInputStream("input.txt");
//重定向默认输入
System.setIn(fileInputStream);
//一次性创建PrintStream输出流对象(先创建文件字节输出流对象,再包装)
PrintStream printStream = new PrintStream(new FileOutputStream("output.txt"));
//重定向默认输出
System.setOut(printStream);
//获取System.in(input.txt文件中)的输入
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()){
//下面这段标准输出会输出在Output.txt中
System.out.println("输入的内容为:" + scanner.next());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
6、对象处理流ObjectInputStream\ObjectOutputStream
对象要想保存在磁盘或在网络上传输,其实体类必须可序列化,它是将对象转化为字节序列,使其可以脱机运行。
要想实现序列化,实体类必须实现java.io.serializable接口。
//创建一个可序列化的实体类
public class Person implements Serializable{
private String username;
private int age;
public Person(String username, int age){
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class TestObjectIO {
public static void main(String[] args) throws Exception{
//ObjectOutputStream是一个处理流,必须建立在节点流上才能工作
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("output.txt"));
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("output.txt"));
objectOutputStream.writeObject(new Person("Leeon", 21));
Person person = (Person)objectInputStream.readObject();
System.out.println(person.getUsername() + person.getAge());
objectInputStream.close();
objectOutputStream.close();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34