------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
对程序语言设计者来说,设计一个令人满意的I/O系统,是极其艰巨的一个任务。——《Think in java》
关于I/O系统的设计,话再多的时间去研究也不足为怪,这里仅仅我再初步学习I/O时的点点滴滴的记录。
一、File类
File类是File类是java.io包中唯一处理磁盘文件和目录的类。它提供的方法都是与平台无关的,
通过使用File类提供各种方法能够创建、删除文件和目录,或者获取磁盘上指定的文件和目录的相关信息。
package com.tai.io; import java.io.File; import java.io.FileFilter; import java.io.IOException; public class FileDemo { /** * File类是java.io包中唯一处理磁盘文件和目录的类。它提供的方法都是与平台无关的, * 通过使用File类提供各种方法能够创建、删除文件和目录,或者获取磁盘上指定的文件和目录的相关信息。 * @param args * @throws IOException */ public static void main(String[] args) throws IOException { File file = new File("E:/文件夹"); if(file.mkdir()) { System.out.println("文件夹已创建!"); } File file2 = new File("E:/文件夹/文件.txt"); if(file2.createNewFile()) { System.out.println("文件已经创建!"); } getInfor(file2); if(file2.delete()) { System.out.println("文件已删除!"); } File Dir = new File("C:"+File.separator+"abc");//separator 文件分割符这样写便于代码跨平台,更加健壮! fFilter(Dir); } /* *获取相关信息 */ private static void getInfor(File file) { System.out.println("文件名"+file.getName()); System.out.println("文件父路径"+file.getParent()); System.out.println("文件的绝对路径"+file.getAbsolutePath()); System.out.println("所在磁盘的大小"+file.getTotalSpace()/(1024*1024*1024)+"GB"); System.out.println("所在磁盘的可用空间"+file.getUsableSpace()/(1024*1024*1024)+"GB"); System.out.println("是否是文件"+file.isFile()); System.out.println("是否是文件夹"+file.isDirectory()); System.out.println("是否是隐藏文件"+file.isHidden()); System.out.println("是否可读"+file.canRead()+" 是否可写"+file.canWrite()+" 是否可执行"+file.canExecute()); } /* * FilenameFilter 过滤接口 此处用内部类实现FileFilter的accept()方法 * FilenameFilter和FileFilter类似,也提供文件过滤功能 * 性能上FilennameFilter较FileFilter效率稍高 */ private static void fFilter(File file) { File[] files = file.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { if(pathname.isFile()&&pathname.getName().endsWith(".txt")) { return true; } return false; } }); System.out.println("该目录下的所有文本文件"); for(File f:files) { System.out.println(f.getName()); } } }运行结果:
文件夹已创建! 文件已经创建! 文件名文件.txt 文件父路径E:\文件夹 文件的绝对路径E:\文件夹\文件.txt 所在磁盘的大小84GB 所在磁盘的可用空间31GB 是否是文件true 是否是文件夹false 是否是隐藏文件false 是否可读true 是否可写true 是否可执行true 文件已删除! 该目录下的所有文本文件 b.txt 新建文本文档 (2).txt 新建文本文档.txt
二、字节流
计算机中的数据都是以字节为单位进行处理的,这类的数据保存文件也成为“二进制文本”,在Java中对这些文本的操作就是要使用Java的字节流对象。
InputStream和OutputStream分别是输入与输出字节流,它们都是抽象类型的,都不能实例化,它们在I/O包中的顶层,作为了众多字节流的父类。
Java中的字节流有很多,这是我写的几个常用的字节流Demo.
FileInputStream/FileOutputStream
FileInputStream与FileOutputStream 被称为文件流,是最常见的字节流。用于对磁盘文件的读写操作。
* File类不只能创建、删除、获取文剪信息,但是不能打开和读写文件。FileInputStream与FileOutoutStream就是用于读写文件的
* FileInputStream 的构造方法
* FileInputStream fis = new FileInputStream("c:/abc");
File file = new File("c:/abc");
FileInputStream fis = new FileInputStream(file);
为了简化常用:
FileInputStream fis = new FileInputStream(new File("c:/abc"));
package com.tai.io; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class FileInputStreamDemo { /** * FileInputStream与FileOutputStream 被称为文件流,是最常见的字节流。用于对磁盘文件的读写操作。 * File类不只能创建、删除、获取文剪信息,但是不能打开和读写文件。FileInputStream与FileOutoutStream就是用于读写文件的 * FileInputStream 的构造方法 * FileInputStream fis = new FileInputStream("c:/abc"); File file = new File("c:/abc"); FileInputStream fis = new FileInputStream(file); 为了简化常用: FileInputStream fis = new FileInputStream(new File("c:/abc")); * @throws IOException */ public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream(new File("C:/abc/Demo.txt")); FileInputStream fis2 = new FileInputStream(new File("C:/abc/b.txt")); int b=0,len=0; System.out.println("使用read()方法,每次读一个字节:"); //使用循环,每次读入一个字节,直到文件结尾。 while((b=fis.read())!=-1) { System.out.print((char)b); } fis.close(); System.out.println("\n使用read(byte[] b)的方法,读取文件存入字符数组中"); //读取文件,直到结尾,存入byte数组中,打印。 byte[] bt = new byte[516]; while(-1!=(len=fis2.read(bt,0,516))) { String str = new String(bt,0,len);////这里需要特别注意的是 从0到len len是实际的长度可能不到516 、 System.out.print(str); } fis2.close(); } }
使用read()方法,每次读一个字节: this is a FileInputDemo. 使用read(byte[] b)的方法,读取文件存入字符数组中 回家过年,这本是很简单的四个字,却是许许多多游子一年中的期盼,当然,这也包括那些在外求学的莘莘学子们。 在外瓢泼久了,多想回到家乡看看为我们牵挂的父母亲人。曾经多少个日日夜夜想及这些,我们可以做的只是一壶浊酒而后在心底默默的祝福与思念。 “举头望明月,低头思故乡”一年到头,隐藏在我们心底的除了深深的牵挂还剩下什么呢?一年到头,又有多少父母无时无刻不在思念着远方的儿女? 记得还小的时候我们是多么的喜欢过年,有新衣服穿、有压岁钱拿,好多好吃好玩的,还不用担心作业是否已经完成。而今我们盼望着过年,却只是那短暂的团聚,可以陪父母陪亲人说说心里的话。同时我们又多么地害怕,害怕父母那期望的眼神,尤其是害怕看见父母那满头乌发渐渐泛白,甚至脱落。虽然我知道,人没有不会老的,但看见父母脸上渐添的皱纹,和那渐渐泛白的乌发,心中总会有一种心酸的感觉。
FileOutputStream
* FileOutputStream类是用于向磁盘文件写入字符数据的输出类
* 当想磁盘某个文件写入时如果文件不存在它不会向FileInputStream那样抛异常,而是创建一个新的文件。
* 构造方法与FileInputStream类似,但是有的会多个参数:
* FileOutputStream fos = new FileOutputStream(File file,boolean append)
* append 是否想文件尾部追加 true 在该文件的结尾追加内容,flase将清除原有的内容,添加新的内容。
package com.tai.io; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class FileOutputStreamDemo { /** * 本程序的功能是将一个文本中的内容复制到另一个文本中。 * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream(new File("C:/abc/Demo.txt")); FileOutputStream fos = new FileOutputStream(new File("C:/abc/Demo2.txt"),false); int b= 0,len=0; byte[] bt = new byte[512]; while(-1!=(len=fis.read(bt,0,512) ) ) { fos.write(bt,0,len); } System.out.println("已经吧Demo.txt中的内容复制到了Demo2.txt中!!"); fos.close(); fis.close(); FileOutputStream fos2 = new FileOutputStream(new File("C:/abc/Demo3.txt"),false); writein(fos2,"hello world!"); System.out.println("已经hello world写入到了文件C:/abc/Demo3.txt中"); } /* * 简单的向文件写入字符串 */ private static void writein(FileOutputStream fos,String str) { byte[] b = str.getBytes(); try { fos.write(b); fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
执行结果:
已经吧Demo.txt中的内容复制到了Demo2.txt中!!
已经hello world写入到了文件C:/abc/Demo3.txt中
java.io包中提供了FilterInputStream和FilterOutputStream类分别对其他输入输出流进行特殊的处理,它们在
* 读/写数据的同时还能够对数据进行特殊的处理。另外它们还提供了同步机制,是的某一时刻之后一个线程可以访问输入/输出流。
* FilterInputStream/FilterOutputStream都是抽象类。
* FilterInputStream有三个子类:BufferedInputStream、DataInputStream、PushbackInputStream
* FilterOutputStream也有三个子类:BufferedOutputStream、DataOutoutStream、PrintStream
*
* 使用过滤字节流的时候,必须先制定节点流对象处理底层的数据,然后把节点流对象作为过滤流对象的实参使用。即把过滤流对象连接到某个输入/输出节点流对象上如下:
* FileInputStream fis = new FileInputStream(new File("c:/abc.txt"));
* DataInputSream dis = new DataInputStream(fis);
一、BufferedInputStream/BufferedOutputStream
* BufferedInputStream/BufferedOutputStream 是缓冲字节流,它引入了针对内存缓冲区的操作,从而提高了读写数据的效率。
* 缓冲区的默认大小为32字节,也可以指定缓冲区的大小。
* 相应的构造的方法
* BufferedInputStream(InputStream in) 这里的in只要是继承自InputSream的对象都可以作为参数
* BufferedInputStream(InputStream in,int size)
* BufferedOutputStream与上面的对应。
Demo:使用BufferedInputStream/BufferdeOutputStream完成文件的复制。
package com.tai.io; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class BufferedInputStreamDemo { /** * java.io包中提供了FilterInputStream和FilterOutputStream类分别对其他输入输出流进行特殊的处理,它们在 * 读/写数据的同时还能够对数据进行特殊的处理。另外它们还提供了同步机制,是的某一时刻之后一个线程可以访问输入/输出流。 * FilterInputStream/FilterOutputStream都是抽象类。 * FilterInputStream有三个子类:BufferedInputStream、DataInputStream、PushbackInputStream * FilterOutputStream也有三个子类:BufferedOutputStream、DataOutoutStream、PrintStream * * 使用过滤字节流的时候,必须先制定节点流对象处理底层的数据,然后把节点流对象作为过滤流对象的实参使用。即把过滤流对象连接到某个输入/输出节点流对象上 * * FileInputStream fis = new FileInputStream(new File("c:/abc.txt")); * DataInputSream dis = new DataInputStream(fis); * BufferedInputStream/BufferedOutputStream 是缓冲字节流,它引入了针对内存缓冲区的操作,从而提高了读写数据的效率。 * 缓冲区的默认大小为32字节,也可以指定缓冲区的大小。 * 相应的构造的方法 * BufferedInputStream(InputStream in) 这里的in只要是继承自InputSream的对象都可以作为参数 * BufferedInputStream(InputStream in,int size) * BufferedOutputStream与上面的对应。 */ public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream(new File("c:/曲婉婷 - 没有什么不同 [mqms].mp3")); BufferedInputStream bis = new BufferedInputStream(fis,1024); FileOutputStream fos = new FileOutputStream(new File("c:/没有什么不同.mp3")); BufferedOutputStream bos = new BufferedOutputStream(fos,1024); byte[] buffer = new byte[1024]; int len =0; while(-1!=(len=bis.read(buffer,0,1024))) { //将buffer中从fis读取来的数据写入到bos中 bos.write(buffer,0,len); } bos.flush();//最近一次读取的数据可能达不到1024字节,这里强制清空缓冲区\ System.out.println("已经将c:/曲婉婷 - 没有什么不同 [mqms].mp3复制到c:/没有什么不同.mp3"); bos.close(); fos.close(); } }
二、DataInputStream和DataOutputStream
* java.io包中还有连个接口DataInput和DataOutput,这两个接口设计了较高级的数据输入输出方式,除了可以处理字节个字节数组外
* 还可以处理 int、float、boolean等基本的数据类型,这些数据在文件中的表示方式和它们在内存中的方式一样,无序转换,它们相应的提供了
* 很多处理基本数据类型的方法,如:DataInput提供了read()、readInt()、readByte()等DataOutput提供了write()、writeInt()、
* writeFloat()、writeChar()等 ....
*
* 而数据流类 DataInputStream和DataOutputStream分别实现了DataInput和DataOutput接口。它们以统一的方式向输入流中写入boolean、int、long
* double等基本的数据类型,并且还可以把基本的数据类型取出来,同时它们也有针对字符串的读写方法
Demo:
package com.tai.io; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Scanner; public class DataInputStreamDemo { /** * java.io包中还有连个接口DataInput和DataOutput,这两个接口设计了较高级的数据输入输出方式,除了可以处理字节个字节数组外 * 还可以处理 int、float、boolean等基本的数据类型,这些数据在文件中的表示方式和它们在内存中的方式一样,无序转换,它们相应的提供了 * 很多处理基本数据类型的方法,如:DataInput提供了read()、readInt()、readByte()等DataOutput提供了write()、writeInt()、 * writeFloat()、writeChar()等 .... * * 而数据流类 DataInputStream和DataOutputStream分别实现了DataInput和DataOutput接口。它们以统一的方式向输入流中写入boolean、int、long * double等基本的数据类型,并且还可以把基本的数据类型取出来,同时它们也有针对字符串的读写方法 * @throws IOException */ public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream(new File("C:/abc/a.txt")); DataOutputStream dos = new DataOutputStream(fos); Scanner scan = new Scanner(System.in); System.out.println("请输入数据: "); dos.writeBoolean(scan.nextBoolean()); dos.writeByte(scan.nextInt()); dos.writeFloat(scan.nextFloat()); dos.writeShort(scan.nextInt()); dos.writeChar('A'); dos.close(); FileInputStream fis = new FileInputStream(new File("C:/abc/a.txt")); DataInputStream dis = new DataInputStream(fis); System.out.println("读取Boolean数据 \t" + dis.readBoolean()); System.out.println("读取Byte数据 \t" + dis.readByte()); System.out.println("读取Float数据 \t" + dis.readFloat()); System.out.println("读取Short数据 \t" + dis.readShort()); System.out.println("读取Char数据 \t" + dis.readChar()); dis.close(); } }
管道字节流
* 管道用来把程序、线程或程序块的输出连接到另一个程序、线程或者程序块作为它的输入。
* java.io提供了类PipedInputSteramh和PipedOutputStream 作为管道的输入/输出流。
* 管道输入流作为一个通信管道接收端,管道输出流作为发送端。管道流必须是输入流输出流同时并用,即在使用管道前,两者必须进行连接。
* 无论PipedInputStream还是PipdeOutputStream类都提供了connect()方法可以使用connect()方法建立一个管道进行数据传输。
* 管道输入/输出流可以用两种方式进行连接,一种方式是使用connect()方法:
* PipedInputStream pis = new PipedInputStream();
* PipedOutputStream pos = new PipedOutputStream();
* pis.connect(pos);
* pos.connect(pis);
* 另外一种是直接使用构造方法进行连接:
* PipedInputStream pis = new PipedInputStream();
* PipedOutputStream pos = new PipedOutputStream(pis);
*
* 除了connect()方法PipedInputStream类还继承了InputStream类的read()方法。另外又增加了receive()方法
* PipedOutputStream类也继承自OutputStream类的write()方法。
Demo1:
package com.tai.io; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class PipedStreamDemo { /** * 管道字节流 * 管道用来把程序、线程或程序块的输出连接到另一个程序、线程或者程序块作为它的输入。 * java.io提供了类PipedInputSteramh和PipedOutputStream 作为管道的输入/输出流。 * 管道输入流作为一个通信管道接收端,管道输出流作为发送端。管道流必须是输入流输出流同时并用,即在使用管道前,两者必须进行连接。 * 无论PipedInputStream还是PipdeOutputStream类都提供了connect()方法可以使用connect()方法建立一个管道进行数据传输。 * 管道输入/输出流可以用两种方式进行连接,一种方式是使用connect()方法: * PipedInputStream pis = new PipedInputStream(); * PipedOutputStream pos = new PipedOutputStream(); * pis.connect(pos); * pos.connect(pis); * 另外一种是直接使用构造方法进行连接: * PipedInputStream pis = new PipedInputStream(); * PipedOutputStream pos = new PipedOutputStream(pis); * * 除了connect()方法PipedInputStream类还继承了InputStream类的read()方法。另外又增加了receive()方法 * PipedOutputStream类也继承自OutputStream类的write()方法。 * @throws IOException */ public static void main(String[] args) throws IOException { int ch1 = 0; PipedInputStream pis = new PipedInputStream(); PipedOutputStream pos = new PipedOutputStream(pis); //也可以使用connect()方法 // PipedOutputStream pos = new PipedOutputStream(); // pos.connect(pis); System.out.println("请输入一个字符,按#结束程序!"); while((ch1 = System.in.read())!='#') { pos.write(ch1); System.out.println((char)pis.read()); } } }
package com.tai.io; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class PipedStreamDemo2 { public static void main(String[] args) throws IOException { PipedInputStream pis = new PipedInputStream(); //实例管道输出流并与上面的管道输入流连接 PipedOutputStream pos = new PipedOutputStream(pis); new Thread(new InputThread(pis)).start(); new Thread(new OutputThread(pos)).start(); } } class InputThread implements Runnable { private PipedInputStream pis; public InputThread(PipedInputStream in) { this.pis = in; } @Override public void run() { byte[] buffer = new byte[1024]; int len = 0; try { len = pis.read(buffer); String str = new String(buffer,0,len); System.out.println("管道输入流作为接收端,接受来自管道输出流发送的内容。 内容是: "+ str); pis.close(); } catch (IOException e) { e.printStackTrace(); } } } class OutputThread implements Runnable { private PipedOutputStream pos; public OutputThread (PipedOutputStream out) { this.pos = out; } @Override public void run() { try { pos.write("管道输出流作为发送端向管道输入流发送内容".getBytes()); pos.close(); } catch (IOException e) { e.printStackTrace(); } } }
package com.tai.io; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class PersistenceDome { /** * 对象持久化(Persistence)就是 将对象保存到磁盘文件或者网络中,保存对象的过程实质上就是对象持久化的过程。 * 也就是记录对象的状态以便于将来具有再生的能力。 Java提供了对象流以实现对象的输入输出,也就是实现对象的持久化。 * 需要注意的是在使用对象流之前必须要将该对象序列化。所谓的序列化就是将对象的状态转化成字节流,并且以后可以通过这些值 再生成相同状态的对象。 * 简单来说序列化就是一种用来处理对象流的机制,而对象流也就是将对象的内容进行流化,我们可以对流化后的对象进行读取操作, * 也可以将流化后的对象传输于网络之间。 * * 对象序列化,只需要被序列化的类实现Serializable接口就可以了。 * Serializable接口中没有任何的方法,让一个类实现该接口只是为了标注该对象是可以被序列化的,然后就可以对该对象进行持久化。 * * 注意: 序列化只能保存对象的实例变量,但不能保存类的任何方法和类变量,并且保存的只是变量的值。 对于任何修饰符都不能保存。 * 有些类型的变量是瞬时的,这样的对象也是无法保存的,如:线程对象、或流对象。对于这样的变量,必须用transient关键字标明,否则编译器将报错。 * * 任何被transient标明的成员变量,将不被保存。 * 考虑到序列化可能涉及对象保存在磁盘上或者网络上,对于一些保密的数据就要加上transient关键字。 * * * 对象序列化之后,必须与一定的对象输入/输出流联系起来,通过对象输出流将对象的状态保存下来,再通过对象输入流将对象的状态恢复。 * java提供了ObjectInputStream * 类和ObjectOutputStream类分别实现了ObjectInput和ObjectOutput。 * ObjectOutput接口用writeObject * ()方法,可以将对象的状态保存到输出流中。而ObjectInput接口用readObject()方法以从输入流中读入一个对象。 * * @throws Exception */ public static void main(String[] args) throws Exception { Person p = new Person("张三", "Mauiie", "男"); Person p2 = new Person("p2", "Mauiie", "男"); Person p3 = new Person("p3", "Mauiie", "男"); Student s = new Student("孙俪", "Mauiie", "女"); Person temp = null; Person temp2 = null; Student stemp = null; FileOutputStream fos = new FileOutputStream("c:/data.dat"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(p); oos.writeObject(p2); // oos.writeObject(p3); oos.writeObject(s); oos.close(); System.out.println("已经将p和s对象写入到了 c:/data.dat"); FileInputStream fis = new FileInputStream("c:/data.dat"); ObjectInputStream ois = new ObjectInputStream(fis); temp = (Person) ois.readObject(); temp2 = (Person) ois.readObject(); stemp = (Student) ois.readObject(); ois.close(); System.out.println(temp.toString()); System.out.println(stemp.toString()); System.out.println(temp2.toString()); } } class Person implements Serializable { String Pname; transient String Pid; String Psex; public Person(String name, String ID, String sex) { this.Pname = name; this.Pid = ID; this.Psex = sex; } public String toString() { return "Name " + Pname + " ID " + Pid + " sex " + Psex; } } class Student implements Serializable { String Sname; transient String Sid; String Ssex; public Student(String name, String ID, String sex) { this.Sname = name; this.Sid = ID; this.Ssex = sex; } public String toString() { return "这是Student对象 " + "Name " + Sname + " ID " + Sid + " sex " + Ssex; } } ObjectInputStream/ObjectOutputStream
已经将p和s对象写入到了 c:/data.dat Name 张三 ID null sex 男 这是Student对象 Name 孙俪 ID null sex 女 Name p2 ID null sex 男
值得注意的是:
使用缺省的serializetion的实现时,一个ObjectOutputStream的构造和一个ObjectInputStream的构造必须一一对应。
ObjectOutputStream的构造函数会向输出流中写入一个标识头,而ObjectInputStream会首先读入这个标识头。因此,多次以追加方式向一个文件中写入object时,该文件将会包含多个标识头。
所以用ObjectInputStream来deserialize这个ObjectOutputStream时,将产生StreamCorruptedException。
一种解决方法是可以构造一个ObjectOutputStream的子类,并覆盖writeStreamHeader()方法。被覆盖后的writeStreamHeader()方法应判断是否为首次向文件中写入object?
若是,则调用super.writeStreamHeader();若否,即以追加方式写入object时,则应调用ObjectOutputStream.reset()方法。