IO流概述
IO流的由来
在Java中,类操作的数据都是在内存中,一旦程序运行结束,这些数据就没有了,后面再不能继续使用。要使这些数据持久化存储起来就需要把内存中的数据存储到内存以外的其他持久化设备(硬盘、光盘、U盘等)上。
当把持久设备上的数据读取到内存中的这个动作称为输入(读)Input操作。
当需要把内存中的数据存储到持久化设备上这个动作称为输出(写)Output操作。
我们把这种输入和输出动作称为IO操作,其中的 一进一出均针对内存(程序)。
从文件中读取输入到内存是输入流;从内存把数据写入带文件是输出流。流就是文件到内存的一个桥梁,是数据传输的载体(单向)。
IO流的分类
- 按照方向
输入流
输出流 - 按照数据的类型
字节流
字符流
- 对于任何操作系统来说,文件最终都是以二进制的形式存储在硬盘里:
.txt
.mp3
.avi
.doc
最终都是二进制文件。但是对于.java
.py
.cpp
这些文件而言,唯一存储的数据是字符- 对于文件的操作既可以是字节流操作,也可以是字符流操作。
字符流只能针对纯文本文件,.doc
不算纯文本文件- 字符流 = 字节流 + 编码表
- IO它不一定非得和文件操作有关,但肯定和内存有关
字节流
OutputStream
此抽象类是表示输出字节流的所有类的超类。
基本特点:
- 操作的数据都是字节。
- 定义了输出字节流的基本共性功能。
- 输出流中定义都是写write方法。
- 操作字节数组write(byte[]),操作单个字节write(byte)。
子类有规律:所有的子类名称后缀是父类名,前缀名是这个流对象功能。
OutputStream 字节输出流
FileOutputStream 文件字节输出流
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
//需求:将数据写入到文件中。
//创建临时目录,
File dir = new File("tempfile");
if(!dir.exists()){
dir.mkdir();
}
//创建存储数据的文件。
File file = new File(dir,"file.txt");
//创建一个用于操作文件的字节输出流对象。一创建就必须明确数据存储目的地。
//输出流目的是文件,会自动创建。如果文件存在,则覆盖。
FileOutputStream fos = new FileOutputStream(file);
//调用父类中的write方法。
byte[] data = "abcde".getBytes();
fos.write(data);
//关闭流资源。
fos.close();
}
}
给文件中续写和换行
直接new FileOutputStream(file)创建对象会覆盖原有的文件,如何在原有的文件中续写内容呢?查阅FileOutputStream的API,发现在FileOutputStream的构造函数中,可以接受一个boolean类型的值,如果值为true,就能在文件末位继续添加。
public class FileOutputStreamDemo2 {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws Exception {
File file = new File("tempfile\\file.txt");
FileOutputStream fos = new FileOutputStream(file, true);
String str = LINE_SEPARATOR+"itcast";
fos.write(str.getBytes());
fos.close();
}
}
IO异常的处理
如果在前面书写代码中都发生了IO的异常:
public class FileOutputStreamDemo3 {
public static void main(String[] args) {
File file = new File("k:\\file.txt");
//定义FileOutputStream的引用
FileOutputStream fos = null;
try {
//创建FileOutputStream对象
fos = new FileOutputStream(file);
//写出数据
fos.write("abcde".getBytes());
} catch (IOException e) {
System.out.println(e.toString() + "----");
} finally {
//一定要判断fos是否为null,只有不为null时,才可以关闭资源
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException("");
}
}
}
}
}
InputStream
此抽象类是表示输入字节流的所有类的超类。
常见功能:
- int read(): 读取一个字节并返回,没有字节返回-1.
- int read(byte[]): 读取一定量的字节数,并存储到字节数组中,返回读取到的字节数。
InputStream 字节输入流
FileInputStream 文件字节输入流
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
File file = new File("tempfile\\file.txt");
//创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联。
FileInputStream fis = new FileInputStream(file);
//读取数据。使用 read();一次读一个字节。
int ch = 0;
while((ch=fis.read())!=-1){
System.out.println("ch="+(char)ch);
}
// 关闭资源。
fis.close();
}
}
读取数据read(byte[])方法
在读取文件中的数据时,调用read方法,每次只能读取一个,太麻烦了,所以我们可以定义数组作为临时的存储容器,这时可以调用重载的read方法,一次可以读取多个字符。
public class FileInputStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
* 演示第二个读取方法, read(byte[]);
*/
File file = new File("tempfile\\file.txt");
// 创建一个字节输入流对象,必须明确数据源,其实就是创建字节读取流和数据源相关联。
FileInputStream fis = new FileInputStream(file);
//创建一个字节数组。
byte[] buf = new byte[1024];//长度可以定义成1024的整数倍。
int len = 0;
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fis.close();
}
}
复制文件
读取一个已有的数据,并将这些读到的数据写入到另一个文件中
public class CopyFileTest {
public static void main(String[] args) throws IOException {
//1,明确源和目的。
File srcFile = new File("E:\\1.mp3");
File destFile = new File("E:\\copy_2.mp3");
//2,明确字节流 输入流和源相关联,输出流和目的关联。
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//3, 使用输入流的读取方法读取字节,并将字节写入到目的中。
int ch = 0;
while((ch=fis.read())!=-1){
fos.write(ch);
}
//4,关闭资源。
fos.close();
fis.close();
}
}
上述代码输入流和输出流之间是通过ch这个变量进行数据交换的。
上述复制文件有个问题,每次都从源文件读取一个,然后在写到指定文件,接着再读取一个字符,然后再写一个,一直这样下去。效率极低。
自定义缓冲数组复制
public class CopyFileByBufferTest {
public static void main(String[] args) throws IOException {
File srcFile = new File("E:\\1.mp3");
File destFile = new File("E:\\copy_1.mp3");
// 明确字节流 输入流和源相关联,输出流和目的关联。
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//定义一个缓冲区。
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);// 将数组中的指定长度的数据写入到输出流中。
}
// 4,关闭资源。
fos.close();
fis.close();
}
}
缓冲流
在之前文件读写的时候,我们一直都是在使用自己定义的数组作为缓冲区,但其实在IO流体系中有相应的缓冲区对象。
BufferedInputStream与BufferedOutputStream都叫做缓冲流,目的就是为了提高IO效率,其缓冲大小为8MB。
public class BufferedDemo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bufIn = new BufferedInputStream(new
FileInputStream("E:\\1.mp3"));
BufferedOutputStream bufOut = new BufferedOutputStream(new
FileOutputStream("e:\\2.mp3"));
// FileInputStream fis = new FileInputStream("E:\\1.mp3");
// FileOutputStream fos = new FileOutputStream("e:\\2.mp3");
byte[] buf = new byte[1024];
int len = 0;
while((len = bufIn.read())!=-1){
bufOut.write(len);
bufOut.flush();
}
bufIn.close();
bufOut.close();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
字符流
字节流读取字符的问题
通过以下程序读取带有文件的文件
public class CharStreamDemo {
public static void main(String[] args) throws IOException {
//给文件中写中文
writeCNText();
//读取文件中的中文
readCNText();
}
//读取中文
public static void readCNText() throws IOException {
FileInputStream fis = new FileInputStream("D:\\test\\cn.txt");
int ch = 0;
while((ch = fis.read())!=-1){
System.out.println(ch);
}
}
//写中文
public static void writeCNText() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\test\\cn.txt");
fos.write("欢迎你".getBytes());
fos.close();
}
}
上面程序在读取含有中文的文件时,我们并没有看到具体的中文,只看到一些数字,这是为什么呢?既然看不到中文,那么我们如何对其中的中文进行处理呢?要解决这些问题,我们必须研究字符的编码过程。
编码表
我们知道计算机底层数据都以二进制形式存储,而我们生活中存在各种各样的数据,为了将现实中的数据和计算机中存储的二进制数据对应起来,产生了编码表。
编码表:其实就是生活中文件和计算机二进制的对应关系表。
ascii:
一个字节中的7位就可以表示。对应的字节都是正数。0-xxxxxxx
iso8859-1:
拉丁码表 latin。用了一个字节用的8位。1-xxxxxxx 负数。
GB2312:
简体中文码表。6,7仟的中文和符号,用两个字节表示。两个字节开头都是1,两个字节都是负数。
GBK:
目前最常用的中文码表。2万多的中文和符号,用两个字节表示。一部分文字的第一个字节开头是1,第二字节开头是0。
GB18030:
最新的中文码表。目前还没有正式使用。
unicode:
国际标准码表。无论是什么文字,都用两个字节存储。Java中的char类型用的就是这个码表。char c = ‘a’; 占两个字节。在Java中,字符串是按照系统默认码表来解析的。简体中文版字符串默认的码表是GBK。
UTF-8:
基于unicode。一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头都加入了编码信息(后期到API中查找)。
能识别中文的码表:GBK、UTF-8。
正因为识别中文码表不唯一,涉及到了编码解码问题。
对于我们开发而言;常见的编码有:GBK、UTF-8、ISO8859-1
- 编码:文字 → 二进制(数字);把我们能够看得懂的编码成我们看不懂的(字节数据),并存储起来
- 解码:二进制(数字) →文字;把我们看不懂的,解码成我们能够看的懂的,展示给我们。
例如:
数据经常使用字符串来描述,而数据存储起来是采用字节数据在存储。所以我们需要把String类型的数据转成byte类型的数据进行存储。
String----->byte[] 编码
byte[]----->String 解码
public class EncodingDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "你好";
//对字符串编码。--->字节数组。
byte[] buf1 = str.getBytes("utf-8");
for(byte b : buf1){
System.out.print(b);
}
//对字节数组解码 。--->字符串。
//String s1 = new String(buf1,"GBK");这样会出现乱码问题
String s1 = new String(buf1,"utf-8");
System.out.println(s1);
}
}
举例:
“你好”:
GBK编码 -60 -29 -70 -61
UTF-8编码:-28 -67 -96 -27 -91 -67
当使用getBytes对字符进行编码后,在解码的时候,指定的码表一定要和编码时的码表一致,否则会发生解码错误的现象,出现乱码。
FileReader类
查阅FileInputStream的API,发现FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
查阅FileReader的API介绍:FileReader是用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的,即FileReader 用于读取字符流。使用FileReader时,先要了解它的功能,可以发现它所属的体系顶层Reader。
Reader:读取字符流的抽象超类。
方法:
- read():读取单个字符并返回
- read(char[]):将数据读取到数组中,并返回读取的个数。
public class CharStreamDemo {
public static void main(String[] args) throws IOException {
//给文件中写中文
writeCNText();
//读取文件中的中文
readCNText();
}
//读取中文
public static void readCNText() throws IOException {
FileReader fr = new FileReader("D:\\test\\cn.txt");
int ch = 0;
while((ch = fr.read())!=-1){
//输出的字符对应的编码值
System.out.println(ch);
//输出字符本身
System.out.println((char)ch);
}
}
//写中文
public static void writeCNText() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\test\\cn.txt");
fos.write("欢迎你".getBytes());
fos.close();
}
}
FileWriter类
既然有专门用于读取字符的流对象,那么肯定也有专门由于写字符的流对象。
查阅API,发现有一个Writer类,Writer是写入字符流的抽象类。其中描述了相应的写的动作。
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
//演示FileWriter 用于操作文件的便捷类。
FileWriter fw = new FileWriter("d:\\text\\fw.txt");
fw.write("你好谢谢再见");//这些文字都要先编码。都写入到了流的缓冲区中。
fw.flush();
fw.close();
}
}
flush()和close()的区别
- flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。
- close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则数据会丢失,然后再关闭流,流不可再使用。如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。
转换流
OutputStreamWriter
需求:既然识别中文的码表有两个(GBK和UTF-8),那么能不能将中文数据按照UTF-8的方式进行文件的存储呢?
这是不能再使用FileWriter了,因为FileWriter中默认的是GBK。通过FileWriter的api描述,要指定编码表这些值,需要使用OutputStreamWriter。
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它的作用的就是将字符串按照指定的编码表转成字节,再使用字节流将这些字节写出去。
public static void writeCN() throws Exception {
//创建与文件关联的字节输出流对象
FileOutputStream fos = new FileOutputStream("D:\\test\\cn8.txt");
//创建可以把字符转成字节的转换流对象,并指定编码
OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
//调用转换流,把文字写出去,其实是写到转换流的缓冲区中
osw.write("你好");//写入缓冲区。
osw.close();
}
问:OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?
答:其实在OutputStreamWriter流中维护自己的缓冲区,当我们调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter缓冲区中。然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。
InputStreamReader
查阅InputStreamReader的API介绍,InputStreamReader 是字节流通向字符流的桥梁:它使用指定的charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
//演示字节转字符流的转换流
readCN();
}
public static void readCN() throws IOException{
//创建读取文件的字节流对象
InputStream in = new FileInputStream("D:\\test\\cn8.txt");
//创建转换流对象
//InputStreamReader isr = new InputStreamReader(in); //这样创建对象,会用本地默认码表读取,将会发生错误解码的错误
InputStreamReader isr = new InputStreamReader(in,"utf-8");
//使用转换流去读字节流中的字节
int ch = 0;
while((ch = isr.read())!=-1){
System.out.println((char)ch);
}
//关闭流
isr.close();
}
}
注意:在读取指定的编码的文件时,一定要指定编码格式,否则就会发生解码错误,而发生乱码现象。
转换流和子类区别
发现有如下继承关系:
OutputStreamWriter→FileWriter:
InputStreamReader→FileReader;
问:父类和子类的功能有什么区别呢?
答:OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。
FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");
以上三句代码的功能是一样的,其中第三句最为便捷。
注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。
问:什么时候用子类呢?
答:操作的是文件;使用默认编码。
小结:
字节—>字符 : 看不懂的—>看的懂的。 需要读。输入流。 InputStreamReader
字符—>字节 :看的懂的—>看不懂的。 需要写。输出流。 OutputStreamWriter
示例:复制文本文件
public class CopyTextFileTest {
public static void main(String[] args) throws IOException {
copyTextFile();
}
public static void copyTextFile() throws IOException {
//1,明确源和目的。
FileReader fr = new FileReader("D:\\test\\cn.txt");
FileWriter fw = new FileWriter("D:\\test\\copy.txt");
//2,为了提高效率。自定义缓冲区数组。字符数组。
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){
fw.write(buf,0,len);
}
/*
2,循环读写操作。效率低。
int ch = 0;
while((ch=fr.read())!=-1){
fw.write(ch);
}
*/
//3,关闭资源。
fw.close();
fr.close();
}
}
字符流缓冲区对像赋值文本文件
在学习使用字节流复制文件时,介绍过可以使用具备缓冲功能的流对象,提高效率,同样在使用字符流复制文件时,也可以使用字符流缓冲提高效率。
注意:其实自定义数组就可以解决问题缓冲区问题并提高效率。但流中的缓冲区对象中除了封装数组以外,还提供了更多的操作缓冲区数据的方法。
字符流缓冲区中的特有方法:操作字符数据时,有一个文本特有的操作行的方法:
- BufferedReader:readLine():一次读取一行
- BufferedWriter:newLine():写出新的一行
public class CharStreamBufferedTest {
public static void main(String[] args) throws IOException {
copyTextByBuffer();
}
public static void copyTextByBuffer() throws IOException {
BufferedReader bufr = new BufferedReader(new FileReader("Test24.java"));
BufferedWriter bufw = new BufferedWriter(new
FileWriter("tempfile\\test24_bufcopy.txt"));
//循环读写一行数据。
String line = null;
while((line=bufr.readLine())!=null){
bufw.write(line);
bufw.newLine();
bufw.flush();
}
bufw.close();
bufr.close();
}
}
缓冲区原理:
- 使用了底层流对象从具体设备上读取数据,并将读取到的数据存储到缓冲区的数组中
- 通过缓冲区的read方法从缓冲区来获取具体的字符数据,这样就提高了效率
- 如果read方法读取字符数据,并存储到另外一个容器中,直到读取到了换行符时,另一个容器中临时存储的数据转成字符串返回,就形成了readLine的功能。
流的操作规律
把IO流进行了规律的总结(四个明确):
- 明确一:要操作的数据是数据源还是数据目的。
源:InputStream Reader
目的:OutputStream Writer
先根据需求明确要读,还是要写。 - 明确二:要操作的设备上的数据是字节还是文本呢?
源:
字节:InputStream
文本:Reader
目的:
字节:OutputStream
文本:Writer
已经明确到了具体的体系上。 - 明确三:明确数据所在的具体设备。
源设备:
硬盘:文件 File开头。
内存:数组,字符串。
键盘:System.in;
网络:Socket
目的设备:
硬盘:文件 File开头。
内存:数组,字符串。
屏幕:System.out
网络:Socket
完全可以明确具体要使用哪个流对象。 - 明确四:是否需要额外功能呢?
额外功能:
转换吗?转换流。InputStreamReader OutputStreamWriter
高效吗?缓冲区对象。BufferedXXX
批量复制文本文件
需求:将当前工程目录中的所有.java文件复制到另一个文件夹之中
public static void main(String[] args) {
File sourceDir = new File("C:\\Users\\HENG\\Desktop\\YD_Java");
File copyDir = new File("JavaSourceCode");
if (!copyDir.exists()) {
copyDir.mkdir();
}
copyJavaFile(sourceDir,copyDir);
}
private static void copyJavaFile(File sourceDir, File copyDir) {
//匿名实现子类(内部类-局部)的匿名对象
//一般是在创建某一个接口的实现子类对象的时候使用
File[] files = sourceDir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith(".java");
}
});
if (files == null || files.length == 0) {
return;
}
for (File file : files) {
if (file.isDirectory()) {
copyJavaFile(file,copyDir);
} else {
copyFile(file,new File(copyDir.getAbsolutePath() + File.separator + file.getName()));
}
}
}
private static void copyFile(File sourceFile, File copyFile) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile)));
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(copyFile),"gbk"));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件切割与合并
切割的代码:一个输入流,多个输出流
public static void main(String[] args) throws Exception {
int count = 1;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("music.mp3"));
byte[] buf = new byte[1024 * 1024 * 2];
BufferedOutputStream bos = null;
int len = 0;
while ((len = bis.read(buf)) != -1) {
bos = new BufferedOutputStream(new FileOutputStream("music.mp3.part" + count++));
bos.write(buf,0,len);
bos.close();
}
bis.close();
}
合并的代码:多个输入流,一个输出流
public static void main(String[] args) throws Exception {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("music_copy.mp3"));
for (int i = 1; i <= 7; i++) {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("music.mp3.part" + i));
byte[] buf = new byte[100];
int len = 0;
while ((len = bis.read(buf)) != -1) {
bos.write(buf,0,len);
}
bis.close();
}
bos.close();
}
Properties类介绍
特点:
- Hashtable的子类,map集合中的方法都可以用。
- 该集合没有泛型。键值都是字符串。
- 它是一个可以持久化的属性集。键值可以存储到集合中,也可以存储到持久化的设备上。键值的来源也可以是持久化的设备。
- 有和流技术相结合的方法。
load(InputStream) load(Reader)store(OutputStream,commonts);
stroe(Writer,comments);
public static void methodDemo() {
// Properties的基本存和取。
// 1,创建一个Properties
Properties prop = new Properties();
prop.setProperty("zhangsan", "20");
prop.setProperty("lisi", "23");
prop.setProperty("wangwu", "21");
prop.list(System.out);// 用于调试。少用!
Set<String> set = prop.stringPropertyNames();
for (String name : set) {
String value = prop.getProperty(name);
System.out.println(name + "...." + value);
}
}
将配置文件中的数据存储到文件中
public static void methodDemo2() throws IOException {
Properties prop = new Properties();
prop.setProperty("zhangsan", "20");
prop.setProperty("lisi", "23");
prop.setProperty("wangwu", "21");
// 将集合中的数据持久化存储到设备上。
// 需要输出流对象。
FileOutputStream fos = new FileOutputStream("tempfile\\info.properties");
// 使用prop的store方法。
prop.store(fos, "my demo ,person info");
fos.close();
}
读取配置文件中的数据,同时更新数据,并保存
public static void methodDemo3() throws IOException {
File configFile = new File("tempfile\\info.properties");
// 读取流中的数据。
Properties prop = new Properties();
// 定义读取流和数据文件关联。
FileInputStream fis = new FileInputStream(configFile);
prop.load(fis);
prop.setProperty("zhangsan", "12");
// 要将改完的数据重新持久化。
FileOutputStream fos = new FileOutputStream(configFile);
prop.store(fos, "");
fos.close();
fis.close();
}
其他常用流
序列流
序列流特点:流对象的有序的排列。
序列流解决问题:将多个输入流合并成一个输入流,将多个源合并成一个源。对于多个源的操作会变的简单。
序列流功能:特殊之处在构造函数上,一初始化就合并了多个流进来。
使用场景:对多个文件进行数据的合并,多个源对应一个目的。
public class SequenceInputStreamDemo {
public static void main(String[] args) throws IOException {
/*
* 演示序列流。SequenceInputStream。
*/
//如何获取一个Enumeration呢?Vector有,但是效率低,使用ArrayList。
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
//添加三个输入流对象,和指定的具体文件关联
for(int x=1; x<=3; x++){
al.add(new FileInputStream("tempfile\\"+x+".txt"));
}
//怎么通过ArrayList获取枚举接口。可以使用Collections工具类中的方法。
Enumeration<FileInputStream> en = Collections.enumeration(al);
//创建序列流对象。需要传递Enumeration。
SequenceInputStream sis = new SequenceInputStream(en);
//创建目录。文件。
FileOutputStream fos = new FileOutputStream("tempfile\\4.txt");
//频繁的读写操作。
//1,创建缓冲区。
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
//关闭流
fos.close();
sis.close();
}
}
对象序列化流
用于操作对象的流对象,对象的序列化:ObjectOutputStream
特点:用于操作对象。
解决问题:可以将对象进行序列化和反序列化。
注意:对象序列化一定要实现Serializable接口。为了给类定义一个serialVersionUID。
功能:ObjectInputStream readObject() ObjectOutputStream writeObject()
关键字:瞬态:transient
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException{
/*
* 将一个对象存储到持久化(硬盘)的设备上。
*/
writeObj();//对象的序列化。
}
public static void writeObj() throws IOException {
//1,明确存储对象的文件。
FileOutputStream fos = new FileOutputStream("tempfile\\obj.object");
//2,给操作文件对象加入写入对象功能。
ObjectOutputStream oos = new ObjectOutputStream(fos);
//3,调用了写入对象的方法。
oos.writeObject(new Person("wangcai",20));
//关闭资源。
oos.close();
}
}
对象反序列化流
当把一个对象持久化存储起来之后,需要使用反序列化技术获取存储起来的对象
使用此ObjectInputStream对象就可以完成反序列化动作
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException{
readObj();//对象的反序列化。
}
public static void readObj() throws IOException, ClassNotFoundException {
//1,定义流对象关联存储了对象文件。
FileInputStream fis = new FileInputStream("tempfile\\obj.object");
//2,建立用于读取对象的功能对象。
ObjectInputStream ois = new ObjectInputStream(fis);
Person obj = (Person)ois.readObject();
System.out.println(obj.toString());
}
}
打印流
PrintStream (字节流)、PrintWriter(字符流)
打印流特点:打印且不抛异常。
打印流解决问题:方便地打印各种数据值表示形式。它的打印方法可以保证数值的表现形式不变。
PrintStream
打印的目的:File对象,字符串路径,字节输出流。
public class PrintStreamDemo {
public static void main(String[] args) throws IOException {
File dir = new File("tempfile");
if(!dir.exists()){
dir.mkdir();
}
//演示PrintStream的特有方法。
//1,创建PrintStream对象。目的就定为文件。
PrintStream out = new PrintStream("tempfile\\print2.txt");
//将数据打印到文件中。
//out.write(353);//字节流的write方法一次只写出一个字节也就是将一个整数的最低8位写出。
//out.write("353".getBytes());//麻烦。
out.print(97);//保证数值的表现形式。其实原理就是将数值转成字符串。
out.close();
}
}
PrintWriter
同样具备打印功能。
打印的目的:File对象,字符串路径,字节输出流,字符输出流。
public class PrintWriterDemo {
public static void main(String[] args) throws IOException {
//演示一个小例子。 读取键盘录入。将数据转成大写显示在屏幕上。
// 1,键盘录入。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//2,定义目的。
//BufferedWriter bufw = new BufferedWriter(new
OutputStreamWriter(System.out)));
PrintWriter pw = new PrintWriter(System.out,true);//对println方法可以实现自动刷新。
//改变目的为文件。还想自动刷新。
pw = new PrintWriter(new BufferedWriter(new
FileWriter("tempfile\\1.txt")),true);
//3,读一行写一行。键盘录入一定要定义结束标记。
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line)){
break;
}
pw.println(line.toUpperCase());
// pw.flush();
}
pw.close();
// bufr.close();//不需要关闭键盘录入这种标准输入流。一旦关闭后面获取不到。
}
}