目录
1.1、FileInputStream和FileOutputStream
1.2、BufferedInputStream和BufferedOutputStream
2.2、BufferedReader和BufferedWriter
3、转换流InputStreamReader和OutputStreamWriter
5、对象操作流ObjectInputStream和ObjectOutputStream
一、流的概念和作用
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
二、IO流的分类
在总体上可以知道,io就是数据流的传递过程,那必然是两端节点的操作。一端读取数据,另一端可以将处理好的数据回写。那么先来看看对于java中的IO流是如何分类的呢?
- 根据处理的流的数据单位不同,可以分为:字符流,字节流。
- 根据数据流动方向不同,可以分为:输入流,输出流。(数据流的方向是相对而言的)
- 根据读取流的功能不同,可以分为:节点流,处理流。
那么,java中的io流除了知道是有字节字符io流几大类,其实内部还有很多根据不同的场景实现的其他流,这都是因为抽象IO基类无法高效的完整资源数据流的输入输出,所以java又自定义了几种用于不同场景的流。java-io流类体系如下:
可以看到,针对不同的使用需求,访问文件,数组,字符串,管道,缓冲流,转换流等等,都有合适的类去完整任务。我们的目的就是在理解基础IO操作流程,底层结构,常用api的情况下,可以因地制宜的选择合适类去处理业务。
1、字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:
- 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
- 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
- 字节流:一次读入或读出是8位二进制。
- 字符流:一次读入或读出是16位二进制。
设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
2、输入流和输出流
输入流只能进行读操作(从文件读取),输出流只能进行写操作(向文件中写入),程序中需要根据待传输数据的不同特性而使用不同的流。
- 输入流:InputStream或者Reader:从文件中读到程序中;
- 输出流:OutputStream或者Writer:从程序中输出到文件中;
3、节点流和处理流
节点流:直接与数据源相连,读入或读出。直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。
处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader
.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
4、转换流
InputStreamReader
、OutputStreamWriter
要InputStream
或OutputStream
作为参数,实现从字节流到字符流的转换。
构造函数:
InputStreamReader(InputStream); //通过构造函数初始化,使用的是本系统默认的编码表GBK。
InputStreamWriter(InputStream,String charSet); //通过该构造函数初始化,可以指定编码表。
OutputStreamWriter(OutputStream); //通过该构造函数初始化,使用的是本系统默认的编码表GBK。
OutputStreamwriter(OutputStream,String charSet); //通过该构造函数初始化,可以指定编码表。
三、File类
File: 文件和目录路径名的抽象表示形式,File类的实例是不可改变的。
- File类的属性:
static String | pathSeparator 与系统有关的路径分隔符,windows是":" |
static String | separator 与系统有关的默认名称分隔符,在UNIX系统上,此字段的值为 ’/ ’;在window系统上,它为‘\’。 |
- File类的构造方法:
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。 |
File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 |
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。 |
File(URI uri) 通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。 |
- File类的常用方法:
方法名称 | 说明 |
Boolean exists() | 判断文件或目录是否存在 |
Boolean isFile() | 判读是否是文件 |
Boolean isDirectory() | 判断是否是目录 |
String getName() | 返回此对象表示的文件或目录的名称 |
String getPath() | 返回此对象的文件相对的路径名 |
String getParent() | 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null |
File getParentFile() | 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null 。 |
String getAbsolutePath() | 返回此对象表示的文件的绝对路径名 |
Boolean delete() | 删除此对象指定文件或目录 |
Boolean greateNewFile() | 创建名称的空文件,不创建文件夹 |
Long.length() | 返回文件的长度,单位为字节,如果文件不存在,则返回0L |
Boolean mkdir() | 创建此抽象路径名指定的目录 |
Boolean mkdirs() | 创建此抽象路径名指定的目录,包括所必须但不存在的目录 |
String[] list() | 返回此目录中的文件名和目录名的数组 |
File[] listFiles() | 返回此目录中的文件和目录的File实例数组 |
File[] listFiles(FileFilter filter) | 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 |
File[] listFiles(FilenameFilter filter) | 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 |
boolean renameTo(File dest) | 重新命名此抽象路径名表示的文件。 |
- FileFilter
FileFilter是io包里面的一个接口,从名字上可以看出,这个类是文件过滤功能的,需要重写accept
方法。
- FilenameFilter
FileFilter是io包里面的一个接口,从名字上可以看出,这个类是文件名字过滤功能的。需要重写里面的accept
方法。
四、主要方法及例子
1、字节流
1.1、FileInputStream和FileOutputStream
- public class FileInputStream extends InputStream
FileInputStream
从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream
用于读取诸如图像数据之类的原始字节流。
构造方法摘要 | |
---|---|
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的 File 对象 file 指定。 | |
FileInputStream(FileDescriptor fdObj) 通过使用文件描述符 fdObj 创建一个 FileInputStream ,该文件描述符表示到文件系统中某个实际文件的现有连接。 | |
FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的路径名 name 指定。 |
- public class FileOutputStream extends OutputStream
- 文件输出流是用于将数据写入
File
或FileDescriptor
的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。 FileOutputStream
用于写入诸如图像数据之类的原始字节的流。
构造方法摘要 | |
---|---|
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 | |
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 | |
FileOutputStream(FileDescriptor fdObj) 创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 | |
FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。 | |
FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。 |
使用字节流复制图片:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopImg {
public static void main(String[] args) throws IOException {
FileInputStream fin=new FileInputStream("C:\\Users\\Administrator\\Desktop\\Img.jpg");
FileOutputStream fout=new FileOutputStream("C:\\Users\\Administrator\\Desktop\\ImgCopy.jpg");
int len=0;
byte[] buff=new byte[1024];
while((len=fin.read(buff))!=-1) {
fout.write(buff, 0, len);
}
fin.close();
fout.close();
}
}
1.2、BufferedInputStream和BufferedOutputStream
- public class BufferedInputStream extends FilterInputStream
BufferedInputStream
为另一个输入流添加一些功能,即缓冲输入以及支持 mark
和 reset
方法的能力。在创建 BufferedInputStream
时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark
操作记录输入流中的某个点,reset
操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark
操作后读取的所有字节。
- public class BufferedOutputStream extends FilterOutputStream
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
使用字节缓冲流实现图片的复制:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyImg {
public static void main(String[] args) throws IOException {
BufferedInputStream bfin=new BufferedInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\Img.jpg"));
BufferedOutputStream bfout=new BufferedOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\ImgCopybuff.jpg"));
int len=0;
byte[] buff=new byte[1024];
while((len=bfin.read(buff))!=-1) {
bfout.write(buff, 0, len);
}
bfin.close();
bfout.close();
}
}
2、字符流
2.1、FileReader和FileWriter
FileReader类
构造方法摘要 | |
---|---|
FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 | |
FileReader(FileDescriptor fd) 在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader。 | |
FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。 |
FileWriter类
构造方法摘要 | |
---|---|
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。 | |
FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 | |
FileWriter(FileDescriptor fd) 构造与某个文件描述符相关联的 FileWriter 对象。 | |
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 | |
FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 |
使用FileReader和FileWriter类完成文本文件复制:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyFile {
public static void main(String[] args) throws IOException {
//创建输入流对象
FileReader fr=new FileReader("C:\\Test\\copyfrom.txt");//文件不存在会抛出java.io.FileNotFoundException
//创建输出流对象
FileWriter fw=new FileWriter("C:\\Test\\copyto.txt");
/*创建输出流做的工作:
* 1、调用系统资源创建了一个文件
* 2、创建输出流对象
* 3、把输出流对象指向文件
* */
//文本文件复制,一次读一个字符
method1(fr, fw);
//文本文件复制,一次读一个字符数组
method2(fr, fw);
fr.close();
fw.close();
}
public static void method1(FileReader fr, FileWriter fw) throws IOException {
int ch;
while((ch=fr.read())!=-1) {//读数据
fw.write(ch);//写数据
}
fw.flush();
}
public static void method2(FileReader fr, FileWriter fw) throws IOException {
char chs[]=new char[1024];
int len=0;
while((len=fr.read(chs))!=-1) {//读数据
fw.write(chs,0,len);//写数据
}
fw.flush();
}
}
2.2、BufferedReader和BufferedWriter
字符缓冲流具备文本特有的表现形式,行操作。
- public class BufferedReader extends Reader
- 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
- 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
- 通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如, BufferedReader in = new BufferedReader(new FileReader("foo.in"));
- 将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。
- public class BufferedWriter extends Writer
- 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
- 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
- 该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。
- 通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如, PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));
- 缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。
使用BufferedReader和BufferedWriter完成文件复制
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyFile2 {
public static void main(String[] args) throws IOException {
//创建输入流对象
BufferedReader br=new BufferedReader(new FileReader("C:\\Test\\copyfrom.txt"));//文件不存在会抛出java.io.FileNotFoundException
//创建输出流对象
BufferedWriter bw=new BufferedWriter(new FileWriter("C:\\Test\\copyto.txt"));
//文本文件复制
char [] chs=new char[1024];
int len=0;
while((len=br.read(chs))!=-1) {
bw.write(chs, 0, len);
}
//释放资源
br.close();
bw.close();
}
}
缓冲区的工作原理:
- 使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内。
- 通过缓冲区的read()方法从缓冲区获取具体的字符数据,这样就提高了效率。
- 如果用read方法读取字符数据,并存储到另一个容器中,直到读取到了换行符时,将另一个容器临时存储的数据转成字符串返回,就形成了readLine()功能。
3、转换流InputStreamReader和OutputStreamWriter
InputStreamReader和OutputStreamWriter是字符和字节的桥梁,也可称之为字符转换流。原理:字节流+编码。
FileReader和FileWriter作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接使用子类完成操作,简化代码。一旦要指定其他编码时,不能使用子类,必须使用字符转换流。
- public class InputStreamReader extends Reader
- InputStreamReader 是字节流通向字符流的桥梁:它使用指定的
charset
读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。 - 每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。
- 为了达到最高效率,可以考虑在 BufferedReader 内包装 InputStreamReader。例如: BufferedReader in = new BufferedReader(new InputStreamReader(System.in));//重要
- public class OutputStreamWriter extends Writer
- OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的
charset
将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。 - 每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。
- 为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:Writer out = new BufferedWriter(new OutputStreamWriter(System.out));//重要
利用标准输出流将文本输出到命令行:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
/**
* 读取项目目录下的文件copy.java,并输出到命令行
* 由于标准输出流是字节输出流,所以只能输出字节或者字节数组,但是我们读取到的数据是字符串,如果想进行输出,
* 还需要转换成字节数组(method1)。
* 要想通过标准输出流输出字符串,把标准输出流转换成一种字符输出流即可(method2)。
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
//创建输入流
BufferedReader br=new BufferedReader(new FileReader("copy.java"));
//创建输出流
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
String line;//用于接收读到的数据
while((line=br.readLine())!=null) {
bw.write(line);
bw.write("\r\n");
}
br.close();
bw.close();
}
public static void method2() throws FileNotFoundException, IOException {
//创建输入流
BufferedReader br=new BufferedReader(new FileReader("copy.java"));
//创建输出流
//OutputStream os=System.out;
Writer w=new OutputStreamWriter(System.out);//多态,父类引用指向子类对象
String line;//用于接收读到的数据
while((line=br.readLine())!=null) {
w.write(line);
w.write("\r\n");
}
br.close();
w.close();
}
public static void method1() throws FileNotFoundException, IOException {
//创建输入流
BufferedReader br=new BufferedReader(new FileReader("copy.java"));
//创建输出流
OutputStream os=System.out;
String line;//用于接收读到的数据
while((line=br.readLine())!=null) {
os.write(line.getBytes());
os.write("\r\n".getBytes());
}
br.close();
os.close();
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
public class TransStreamDemo {
public static void main(String[] args) throws IOException {
writeCN();
readCN();
}
public static void readCN() throws UnsupportedEncodingException, FileNotFoundException, IOException {
//InputStreamReader将字节数组使用指定的编码表解码成文字
InputStreamReader isr=new InputStreamReader(new FileInputStream("temp.txt"),"utf-8");
char[] buff=new char[1024];
int len=isr.read(buff);
System.out.println(new String(buff,0,len));
isr.close();
}
public static void writeCN() throws UnsupportedEncodingException, FileNotFoundException, IOException {
//OutputStreamWriter将字符串按照指定的编码表转成字节,再使用字符流将这些字节写出去
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("temp.txt"),"utf-8");//本身是字符流,传入字节流
osw.write("你好");
osw.close();
}
}
4、打印流PrintWriter和PrintStream
- public class PrintWriter extends Writer
- 向文本输出流打印对象的格式化表示形式。此类实现在
PrintStream
中的所有 print 方法。不能输出字节,但是可以输出其他任意类型。 - 与
PrintStream
类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。 - 此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常。客户端可能会查询调用
checkError()
是否出现错误。
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 注意:创建FileWriter对象时boolean参数表示是否追加;
* 而创建打印流对象时boolean参数表示是否自动刷新
*/
public class PrintWriterDemo {
public static void main(String[] args) throws IOException {
//PrintWriter pw=new PrintWriter("print.txt");
PrintWriter pw=new PrintWriter(new FileWriter("print.txt"),true);
pw.write("测试打印流");
pw.println("此句之后换行");
pw.println("特有功能:自动换行和自动刷新");
pw.println("利用构造器设置自动刷新");
pw.close();
}
}
使用字符打印流复制文本文件:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 使用打印流复制文本文件
*/
public class PrintWriterDemo {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("copy.java"));
PrintWriter pw=new PrintWriter("printcopy.java");
String line;
while((line=br.readLine())!=null) {
pw.println(line);
}
br.close();
pw.close();
}
}
- public class PrintStream extends FilterOutputStream
- 为其他输出流添加了功能(增加了很多打印方法),使它们能够方便地打印各种数据值表示形式(例如:希望写一个整数,到目的地整数的表现形式不变。字节流的write方法只将一个整数的最低字节写入到目的地,比如写fos.write(97),到目的地(记事本打开文件)会变成字符'a',需要手动转换:fos.write(Integer.toString(97).getBytes());而采用PrintStream:ps.print(97),则可以保证数据的表现形式)。
PrintStream永远不会抛出 IOException;而是,异常情况仅设置可通过 checkError方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream;这意味着可在写入 byte 数组之后自动调用 flush方法,可调用其中一个 println方法,或写入一个换行符或字节 (\n')。
- 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
public class PrintStreamDemo {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("copy.java"));
PrintStream ps=new PrintStream("printcopy2.java");
String line;
while((line=br.readLine())!=null) {
ps.println(line);
}
br.close();
ps.close();
}
}
PrintStreamDemo
5、对象操作流ObjectInputStream和ObjectOutputStream
- public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
- ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
- 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
readObject
方法用于从流读取对象。应该使用 Java 的安全强制转换来获取所需的类型。在 Java 中,字符串和数组都是对象,所以在序列化期间将其视为对象。读取时,需要将其强制转换为期望的类型。
- public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants
- ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。只能使用 ObjectInputStream 读取(重构)对象。
- 只能将支持 java.io.Serializable 接口的对象写入流中。
- writeObject 方法用于将对象写入流中。所有对象(包括 String 和数组)都可以通过 writeObject 写入。可将多个对象或基元写入流中。必须使用与写入对象时相同的类型和顺序从相应 ObjectInputstream 中读回对象。
- 构造方法:
ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream。
对象读写:
import java.io.Serializable;
//学生类
public class Student implements Serializable{
private static final long serialVersionUID = -8942780382144699003L;
String name;
int age;
public Student(String name,int age){
this.name=name;
this.age=age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 使用对象输出流写对象和对象输入流读对象
*注意:如果Student没有序列化,会抛出java.io.NotSerializableException
*Serializable:序列号,是一个标识接口,只起标识作用,没有方法
*当一个类的对象需要IO流进行读写的时候,这个类必须实现接口
*/
public class ObjectOperate {
public static void main(String[] args) throws IOException, ClassNotFoundException {
writeObject();
//创建对象输入流的对象
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a.txt"));
//读取对象
try {
while(true){
Object obj=ois.readObject();
System.out.println(obj);
}
}catch(EOFException e){
System.out.println("读到了文件末尾");
}
//释放资源
ois.close();
}
public static void writeObject() throws FileNotFoundException, IOException {
//创建对象输出流的对象
FileOutputStream fos=new FileOutputStream("a.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
//创建学生对象
Student s1=new Student("张三",20);
Student s2=new Student("李四",30);
Student s3=new Student("王五",10);
//写出学生对象
oos.writeObject(s1);
oos.writeObject(s2);
oos.writeObject(s3);
//释放资源
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
/**
* 使用对象输出流写对象和对象输入流读对象
*解决读取对象出现异常的问题,使用集合类
*/
public class ObjectOperate2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
listMethod();
//创建对象输入流对象
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("b.txt"));
//读取数据
Object obj=ois.readObject();
//System.out.println(obj);
//向下转型
ArrayList<Student> list=(ArrayList<Student>) obj;
for(Student s:list) {
System.out.println(s);
}
//释放资源
ois.close();
}
public static void listMethod() throws IOException, FileNotFoundException {
//创建对象输出流的对象
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("b.txt"));
//创建集合类
ArrayList<Student> list=new ArrayList<Student>();
//添加学生对象
list.add(new Student("zhangsan",20));
list.add(new Student("lisi",30));
//写出集合对象
oos.writeObject(list);
//释放资源
oos.close();
}
}
序列化接口Serializable的作用:没有方法,不需要覆写,是一个标记接口为了启动一个序列化功能。唯一的作用就是给每一个需要序列化的类都分配一个序列版本号,这个版本号和类相关联。在序列化时,会将这个序列号也一同保存在文件中,在反序列化时会读取这个序列号和本类的序列号进行匹配,如果不匹配会抛出java.io.InvalidClassException。
注意:静态数据不会被序列化,因为静态数据在方法区,不在对象里。或者使用transient关键字修饰,也不会序列化。
6、SequenceInputStream
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
媒体文件切割与合并:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class CutFile {
/**
* 将一个媒体文件切割成碎片
* 思路:1、读取源文件,将源文件的数据分别复制到多个文件
* 2、切割方式有两种:按照碎片个数切,或者按照指定大小切
* 3、一个输入流对应多个输出流
* 4、每个碎片都需要编号,顺序不能错
* @throws IOException
*/
public static void main(String[] args) throws IOException {
File srcFile=new File("C:\\Users\\Administrator\\Desktop\\Test\\img.jpg");
File partsDir=new File("C:\\Users\\Administrator\\Desktop\\cutFiles");
splitFile(srcFile,partsDir);
}
//切割文件
private static void splitFile(File srcFile, File partsDir) throws IOException {
if(!(srcFile.exists()&&srcFile.isFile())) {
throw new RuntimeException("源文件不是正确文件或者不存在");
}
if(!partsDir.exists()) {
partsDir.mkdirs();
}
FileInputStream fis=new FileInputStream(srcFile);
FileOutputStream fos=null;
byte[] buf=new byte[1024*60];
int len=0;
int count=1;
while((len=fis.read(buf))!=-1) {
//存储碎片文件
fos=new FileOutputStream(new File(partsDir,(count++)+".part"));
fos.write(buf, 0, len);
fos.close();
}
/*将源文件和切割的信息也保存起来,随着碎片文件一起发送
* 信息:源文件的名称
* 碎片的个数
*将这些信息单独封装到一个文件中
*还要一个输出流完成此操作 */
String fileName=srcFile.getName();
int partCount=count;
fos=new FileOutputStream(new File(partsDir,count+".properties"));
// fos.write(("fileName="+fileName+System.lineSeparator()).getBytes());
// fos.write(("fileCount="+Integer.toString(partCount)).getBytes());
Properties prop=new Properties();
prop.setProperty("fileName", srcFile.getName());
prop.setProperty("partCount", Integer.toString(partCount));
//将属性集中的信息持久化
prop.store(fos, "part file info");
fis.close();
fos.close();
}
}
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
public class mergeFile {
public static void main(String[] args) throws IOException {
File pathDir=new File("C:\\Users\\Administrator\\Desktop\\cutFiles");
//获取配置文件
File configFile=getconfigFile(pathDir);
//获取配置文件信息的属性集
Properties prop=getProperties(configFile);
System.out.println(prop);
//获取属性集信息,将属性集信息传递到合并方法中
merge(pathDir,prop);
}
private static Properties getProperties(File configFile) throws IOException {
FileInputStream fis=null;
Properties prop=null;
try {
//读取流和配置文件相关联
fis=new FileInputStream(configFile);
prop=new Properties();
//流中的数据加载到集合中
prop.load(fis);
}finally {
if(fis!=null) {
fis.close();
}
}
return prop;
}
public static File getconfigFile(File pathDir) {
//判断是否存在properties文件
if(!(pathDir.exists()&&pathDir.isDirectory())) {
throw new RuntimeException(pathDir.toString()+"不是有效目录");
}
File[] files=pathDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().endsWith(".properties");
}
});
if(files.length!=1) {
throw new RuntimeException(pathDir.toString()+"properties扩展名的文件不存在或者不唯一");
}
File configFile=files[0];
return configFile;
}
public static void merge(File pathDir, Properties prop) throws FileNotFoundException, IOException {
String fileName=prop.getProperty("fileName");
int partCount=Integer.valueOf(prop.getProperty("partCount"));
List<FileInputStream> list=new ArrayList<FileInputStream>();
for(int i=1;i<partCount;i++) {
list.add(new FileInputStream(pathDir.toString()+"\\"+i+".part"));
}
//List自身无法获取Enumeration工具类,到Collection中找
Enumeration<FileInputStream> en=Collections.enumeration(list);
SequenceInputStream sis=new SequenceInputStream(en);
FileOutputStream fos=new FileOutputStream(pathDir.toString()+"\\"+fileName);
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1) {
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
7、用于操作数组和字符串的流对象
ByteArrayInputStream ByteArrayOutputStream
CharArrayReader CharArrayWriter
StringReader StringWriter
关闭这些流都是无效的,因为这些都未调用系统资源,不需要抛IO异常。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
/**
* 源和目的都是内存的读写过程
*用流的思想操作数组中的数据
*/
public class ByteArrayStreamDemo {
public static void main(String[] args) {
//源:内存
ByteArrayInputStream bis=new ByteArrayInputStream("andhhshad".getBytes());
//目的:内存
ByteArrayOutputStream bos=new ByteArrayOutputStream();//内部有个可自动增长的数组
//因为都是源和目的都是内存,没有调用底层资源,所以不要关闭,即使调用了close也没有任何效果,关闭后仍然可使用,不会抛出异常。
int ch=0;
while((ch=bis.read())!=-1) {
bos.write(ch);
}
System.out.println(bos.toString());
}
}
五、IO流使用规律总结
(1)明确要操作的数据是数据源还是数据目的(要读还是要写)
- 源:InputStream Reader
- 目的:OutputStream Writer
(2)明确要操作的设备上的数据是字节还是文本
- 源:
- 字节:InputStream
- 文本:Reader
- 目的:
- 字节:OutputStream
- 文本:Writer
(3)明确数据所在的具体设备
- 源设备:
- 硬盘:文件 File开头
- 内存:数组,字符串
- 键盘:System.in
- 网络:Socket
- 目的设备:
- 硬盘:文件 File开头
- 内存:数组,字符串
- 屏幕:System.out
- 网络:Socket
(4)明确是否需要额外功能?
- 需要转换——转换流 InputStreamReader OutputStreamWriter
- 需要高效——缓冲流Bufferedxxx
- 多个源——序列流 SequenceInputStream
- 对象序列化——ObjectInputStream,ObjectOutputStream
- 保证数据的输出形式——打印流PrintStream Printwriter
- 操作基本数据,保证字节原样性——DataOutputStream,DataInputStream
六、推荐博客
- 输入输出流
https://www.cnblogs.com/zhaoyanjun/p/6292384.html
- File
http://www.cnblogs.com/zhaoyanjun/p/6292399.html
- 缓冲流-BufferedInputStream、BufferedOutputStream
http://www.cnblogs.com/zhaoyanjun/p/6376937.html
- 缓冲流-BufferedReader、BufferedWriter
http://www.cnblogs.com/zhaoyanjun/p/6376951.html
- 转换流-InputStreamReader、OutputStreamWriter
http://www.cnblogs.com/zhaoyanjun/p/6376996.html
- ByteArrayInputStream、ByteArrayOutputStream
http://www.cnblogs.com/zhaoyanjun/p/6394443.html
- Commons IO 2.5-FileUtils
http://www.cnblogs.com/zhaoyanjun/p/6396419.html
- Commons IO 2.5-IOUtils
http://www.cnblogs.com/zhaoyanjun/p/6401314.html