文章目录

  • 1. 流概述
  • 2. 输入/输出流
  • 2.1 输入流
  • 2.2 输出流
  • 3. File 类
  • 3.1 文件的创建与删除
  • 3.2 获取文件信息
  • 4. 文件输入/输出流
  • 4.1 FileInputStream 与 FileOutputStream 类
  • 4.2 FileReader 和 FIleWriter 类
  • 5. 带缓存的输入/输出流
  • 5.1 BufferedInputStream 类与 BufferedOutputStream 类
  • 5.2 BufferedReader 与 BufferedWriter 类
  • 6. 数据输入/输出流


1. 流概述

流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流两种。I/O(Input/Output)流提供了一条通道程序,可以使用这条通道把源中的字节序列送到目的地。虽然I/O流经常与磁盘文件存取有关,但是程序的源和目的地也可以是键盘、鼠标、内存或显示器窗口等。

java inputstream 放到outputstream中 java inputstream和outputstream_输出流


java inputstream 放到outputstream中 java inputstream和outputstream_java_02

2. 输入/输出流

2.1 输入流

java inputstream 放到outputstream中 java inputstream和outputstream_输入/输出流_03


InputStream类是字节输入流的抽象类,是所有字节输入流的父类。InputStream类的具体层次结构如图所示:

java inputstream 放到outputstream中 java inputstream和outputstream_java_04


该类中所有方法遇到错误时都会引发IOException异常。下面是对该类中的一些方法的简要说明如下表所示

java inputstream 放到outputstream中 java inputstream和outputstream_System_05


Java中的字符是Unicode编码,是双字节的。InputStream是用来处理字节的,在处理字符文本时不是很方便。Java为字符文本的输入提供了专门一套单独的类Reader,但Reader类并不是InputStream类的替换者,只是在处理字符串时简化了编程。Reader类是字符输入流的抽象类,所有字符输入流的实现都是它的子类,Reader类的具体层次结构如下图所示:

java inputstream 放到outputstream中 java inputstream和outputstream_输出流_06

2.2 输出流

java inputstream 放到outputstream中 java inputstream和outputstream_输出流_07

OutputStream类是字节输入流的抽象类,此抽象类是表示输出字节流的所有类的超类。OutputStream类的具体层次如图所示:

java inputstream 放到outputstream中 java inputstream和outputstream_java_08


OutputStream类中的所有方法均返回void,在遇到错误时会引发IOException异常。下面对OutputStream类中的方法作一简单的介绍,如下表所示:

java inputstream 放到outputstream中 java inputstream和outputstream_输出流_09


Writer类是字符输出流的抽象类,所有字符输出类的实现都是它的子类,Writer类的层次结构如下图所示:

java inputstream 放到outputstream中 java inputstream和outputstream_System_10

3. File 类

java inputstream 放到outputstream中 java inputstream和outputstream_System_11


java inputstream 放到outputstream中 java inputstream和outputstream_输入/输出流_12

3.1 文件的创建与删除

可以使用File类创建一个文件对象,通常使用以下3种构造方法来创建文件对象。

java inputstream 放到outputstream中 java inputstream和outputstream_输出流_13

import java.io.File;

public class Demo {
	public static void main(String[] args) {
		/*
		 * 项目下的路径(默认路径): word.txt 
		 * 包中的文件路径"src/mr/word.txt 
		 * 绝对路径: C:/test/word.txt
		 */
		File f1 = new File("D:/test/word.txt");// 第一种构造方法

		File f2 = new File("D:/test", "word.txt");// 第二种构造方法

		File dir = new File("D:/test");// 文件夹
		File f3 = new File(dir, "word.txt");// 第三种构造方法

		System.out.println(f1.getAbsolutePath());// 输出文件绝对路径
		System.out.println(f2.getAbsolutePath());// 输出文件绝对路径
		System.out.println(f3.getAbsolutePath());// 输出文件绝对路径

		System.out.println(f1 == f2);// f1、f2只是文件的代表
		System.out.println(f1.equals(f2));
	}

}

运行结果:

D:\test\word.txt
D:\test\word.txt
D:\test\word.txt
false
true

3.2 获取文件信息

File类提供了很多方法用于获取文件本身的一些信息,File类的常用方法如下表所示。

java inputstream 放到outputstream中 java inputstream和outputstream_System_14

我们在D盘test文件夹中新建一个word.txt文件,然后运行以下代码:

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo {
	public static void main(String[] args) {

		File f1 = new File("D:/test/word.txt");// 第一种构造方法
		System.out.println("文件是否存在:" + f1.exists());// 判断文件是否存在

		System.out.println("文件名:" + f1.getName());// 输出文件名
		System.out.println("文件绝对路径:" + f1.getAbsolutePath());// 输出文件绝对路径
		System.out.println("文件是否隐藏:" + f1.isHidden());// 是否为隐藏文件
		System.out.println("文件的字节数:" + f1.length());// 输出文件大小,单位为字节
		Date date = new Date(f1.lastModified());// 通过毫秒值创建日期类
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/ HH:mm:ss");
		System.out.println("文件最后修改时间:" + sdf.format(date));// 文件最后修改日期

	}

}

得到结果:

文件是否存在:true
文件名:word.txt
文件绝对路径:D:\test\word.txt
文件是否隐藏:false
文件的字节数:9
文件最后修改时间:2020/12/14/ 22:37:26

然后,对文件进行删除和新建的操作:

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo {
	public static void main(String[] args) {

		File f1 = new File("D:/test/word.txt");// 第一种构造方法

		boolean del = f1.delete();// 删除文件
		System.out.println("删除文件是否成功:" + del);

		try {
			// 已存在的文件,不能重新创建(不能覆盖已有文件)
			boolean create = f1.createNewFile();// 创建新的空文件
			System.out.println("创建文件是否成功:" + create);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

得到结果:

删除文件是否成功:true
创建文件是否成功:true

下面是新建文件夹的例子:

import java.io.File;

public class Demo {
	public static void main(String[] args) {

		File dir = new File("dir/dir1/dir2/dir3");// 只能创建1层文件夹
		boolean flag = dir.mkdir();// 创建文件夹
		boolean flag2 = dir.mkdirs();// 创建文件夹及父文件夹
		System.out.println("创建文件夹是否成功:"+flag);
		System.out.println("创建多层文件夹是否成功:"+flag2);	

	}

}

运行结果:

创建文件夹是否成功:false
创建多层文件夹是否成功:true

删除文件夹:

import java.io.File;

public class Demo {
	public static void main(String[] args) {

		File dir = new File("dir/dir1/dir2/dir3");// 只能创建1层文件夹
		
		// 删除文件路径最后一个文件夹
		boolean del = dir.delete();// 删除文件夹
		System.out.println("删除文件夹是否成功:"+del);

	}

}

运行结果:

删除文件夹是否成功:true

查看文件夹及文件名:

import java.io.File;

public class Demo {
	public static void main(String[] args) {
		
		File f = new File("C:/Windows/");// C盘Windows文件夹
		File files[] = f.listFiles();// 返回文件夹下所有子文件及子文件夹
		for(File tmp:files) {
			if(tmp.isFile()) {// 判断是否为文件
				System.out.println("文件:"+tmp.getName());			
			}else if(tmp.isDirectory()) {
				System.out.println("文件夹:"+tmp.getName());
			}
		}
	}
}

运行结果:

文件夹:addins
文件夹:appcompat
文件夹:apppatch
文件夹:AppReadiness
文件夹:assembly
文件夹:bcastdvr
文件:bfsvc.exe
文件夹:Boot
文件:bootstat.dat
文件夹:Branding
……略

4. 文件输入/输出流

程序运行期间,大部分数据都在内存中进行操作,当程序结束或关闭时,这些数据将消失。如果需要将数据永久保存,可以使用文件输入/输入流与指定的文件建立连接,将需要的数据永久保存到文件中。

4.1 FileInputStream 与 FileOutputStream 类

FileInputStream类与FileOutputStream类都是用来操作磁盘文件。如果用户的文件读取需求比较简单,则可以使用FileInputStream类。该类继承自InputStream类。FileOutputStream类与FileInputStream类对应,提供了基本的文件写入能力。FileOutputStream类是OutoputStream类的子类。

java inputstream 放到outputstream中 java inputstream和outputstream_java_15


例子:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(f, false);// 文件输出流,为true,在文件末尾添加;为false,替换掉文件内容。
			String str = "你见过洛杉矶凌晨4点的样子吗?";
			byte b[] = str.getBytes();// 字符串转换为字节数组
			out.write(b);// 将字节数组中的数据写入到文件当中

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		FileInputStream in = null;
		try {
			in = new FileInputStream(f);// 输入流读文件
			byte b2[] = new byte[2014];// 缓冲区
			int len = in.read(b2);// 读入缓冲区的总字节数
			System.out.println("文件中的数据是:" + new String(b2, 0, len));// 可以去掉1024字节中的空格
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

}

运行结果:

文件中的数据是:你见过洛杉矶凌晨4点的样子吗?

4.2 FileReader 和 FIleWriter 类

使用FileOutputStream类向文件中写入数据与使用FileInputStream类从文件中将内容读出来,存在一点不足,即这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占用两个字节,如果使用字节流,读取不好可能会出现乱码现象。此时采用字符流Reader或Writer类即可避免这种现象。

java inputstream 放到outputstream中 java inputstream和outputstream_输入/输出流_16


FileReaderFileWriter字符流对应了FileInputStreamFileOutputStream类。FileReader流顺序地读取文件,只要不关闭流,每次调用read()方法就顺序地读取源中其余的内容,直到源的末尾或流被关闭。

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileWriter fw = null;
		try {
			fw = new FileWriter(f, false);// true为在原文件后追加新内容
			String str = "天行健,君子当自强不息,地势坤,君子当厚德载物";
			fw.write(str);// 将字符串写入文本文档

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fw != null) {
				try {
					fw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		FileReader fr = null;
		try {
			fr = new FileReader(f);

			char ch[] = new char[2014];// 缓冲区
			int count;// 已读出的字符数
			while ((count = fr.read(ch)) != -1) {// 循环读取文件中的数据直到所有字符都读完
				System.out.println("文件中的内容为:" + new String(ch, 0, count));
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fr != null) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

运行结果:

文件中的内容为:天行健,君子当自强不息,地势坤,君子当厚德载物

5. 带缓存的输入/输出流

缓存是I/O的一种性能优化。缓存流为I/O流增加了内存缓存区。有了缓存区,使得在流上执行skip()mark()reset()方法都称为可能。

java inputstream 放到outputstream中 java inputstream和outputstream_输出流_17

5.1 BufferedInputStream 类与 BufferedOutputStream 类

BufferedInputStream类可以对任何的InputStream类进行带缓存区的包装以达到性能的优化。
使用BufferedOutputStream输出信息和往OutputStream输出信息完全一样,只不过BufferedOutputStream有一个flush()方法用来将缓存区的数据强制输出完。

无缓冲字节流情况下读取数据需要的时间:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo {
	public static void main(String[] args) {
		File f = new File("D:/Android/jdk1.8中文百度.CHM");

		FileInputStream in = null;
		long start = System.currentTimeMillis();// 数据流开始时毫秒值

		try {
			in = new FileInputStream(f);
			byte b[] = new byte[2014];// 缓冲区自己数组(这个缓冲区与Buffered不同)
			while (in.read(b) != -1) {

			}
			long end = System.currentTimeMillis();// 数据流结束时毫秒值
			System.out.println("运行经历的毫秒数:" + (end - start));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}
}

运行结果:

运行经历的毫秒数:124

使用缓冲字节流读取数据:

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo {
	public static void main(String[] args) {
		File f = new File("D:/Android/jdk1.8中文百度.CHM");
		BufferedInputStream bi = null;

		FileInputStream in = null;
		long start = System.currentTimeMillis();// 数据流开始时毫秒值

		try {
			in = new FileInputStream(f);
			bi = new BufferedInputStream(in);// 将文件字节流包装成缓冲流
			byte b[] = new byte[2014];// 缓冲区自己数组(这个缓冲区与Buffered不同)
			while (bi.read(b) != -1) {// 使用缓冲流读取数据

			}
			long end = System.currentTimeMillis();// 数据流结束时毫秒值
			System.out.println("运行经历的毫秒数:" + (end - start));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		}
		if (bi != null) {
			try {
				bi.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

运行结果:

运行经历的毫秒数:62

使用缓冲字节流可以大大提升效率。

同理,缓冲输出流例子:

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo2 {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileOutputStream out = null;
		BufferedOutputStream bo = null;

		try {
			out = new FileOutputStream(f);
			bo = new BufferedOutputStream(out);// 将文件输出流包装
			String str = "天生我材必有用,千金散尽还复来。";
			byte b[] = str.getBytes();
			bo.write(b);
			// 使用缓冲字节流时,要多进行刷新操作
			bo.flush();// 刷新,可强制将缓冲区数据写入文件中,即使缓冲区没有写满

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (bo != null) {
				try {
					bo.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

运行生成了word.txt文档。

5.2 BufferedReader 与 BufferedWriter 类

BufferedReader类与BufferedWriter类分别继承Reader类与Writer类。这两个类同样具有内部缓存机制,并可以以行为单位进行输入输出。

在使用BufferedWriter类的Write()方法时,数据并没有立刻被写入至输出流中,而是首先进入缓存区中。如果想立刻将缓存区中的数据写入输出流中,一定要调用flush()方法。

java inputstream 放到outputstream中 java inputstream和outputstream_输出流_18


缓冲输入流例子:

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileWriter fw = null;
		BufferedWriter bw = null;

		try {
			fw = new FileWriter(f);
			bw = new BufferedWriter(fw);// 将文件字符输出流包装成缓冲字符流
			String str1 = "世界那么大";
			String str2 = "我想去看看";
			bw.write(str1);// 第一行数据
			bw.newLine();// 创建一个新行
			bw.write(str2);// 第二行数据

		} catch (IOException e) {
			e.printStackTrace();
		} finally {// 要注意流的关闭顺序,先创建的后关闭
			if (bw != null) {
				try {
					bw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (fw != null) {
				try {
					fw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

运行生成word.txt文件,显示内容:

世界那么大
我想去看看

下面是BufferedReader实例,先将word.txt中的内容修改为“会当凌绝顶 一览众山小”,分为两行写入:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileReader fr = null;
		BufferedReader br = null;

		try {
			fr = new FileReader(f);
			br = new BufferedReader(fr);// 将文件字符输入流包装成缓冲字符输入流
			String tmp = null;// 创建临时变量
			int i = 1;// 计数器
			while ((tmp = br.readLine()) != null) {// 循环读取文件中的内容
				System.out.println("第" + i + "行内容:" + tmp);
				i++;
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			if (fr != null) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

运行结果:

第1行内容:会当凌绝顶
第2行内容:一览众山小

6. 数据输入/输出流

数据输入输出流(DataInputStream类与DataOutputStream类)允许应用程序以与机器无关的方式从底层输入流中读取基本Java数据类型。也就是说,当读取一个数据时,不必再关心这个数值应当是什么字节。

DataInputStream类只提供了一个readUTF()方法返回字符串。这是因为要在一个连续的字节流读取一个字符串,如果没有特殊的标记作为一个字符串的结尾,并且事先也不知道这个字符串的长度,也就无法知道读取到什么位置才是这个字符串的结束。DataOutputStream类中只有writeUTF()方法向目标设备中写入字符串的长度,所以我们也只能准确地读回写入字符串。

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileOutputStream out = null;
		DataOutputStream dos = null;
		try {
			out = new FileOutputStream(f);
			dos = new DataOutputStream(out);// 将文件流包装成数据流

			dos.writeUTF("这是写入字符串数据");// 写入字符串数据
			dos.writeInt(123);// 写入整型数据
			dos.writeDouble(3.14);// 写入浮点型数据
			dos.writeBoolean(true);// 写入布尔类型数据

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (dos != null) {
				try {
					dos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		DataInputStream di = null;
		FileInputStream in = null;
		try {
			in = new FileInputStream(f);
			di = new DataInputStream(in);
			System.out.println("readUTF()读取数据:" + di.readUTF());
			System.out.println("readInt()读取数据:" + di.readInt());
			System.out.println("readDouble()读取数据:" + di.readDouble());
			System.out.println("readBoolean()读取数据:" + di.readBoolean());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (di != null) {
				try {
					di.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

运行结果:

readUTF()读取数据:这是写入字符串数据
readInt()读取数据:123
readDouble()读取数据:3.14
readBoolean()读取数据:true

这里,因为程序写入的是字节码,如果我们不使用DataOutputStream读取数据,读出的便会是乱码。因此需要用DataOutputStream 方法将字节码解析成对应的数据类型。

java inputstream 放到outputstream中 java inputstream和outputstream_输出流_19