Java输入、输入、IO流 类层次关系梳理
本文主要关注在Java编程中涉及到的IO相关的类库、方法。以及对各个层次(抽线、接口继承)的流之间的关系进行梳理
相关学习资料
http://baike.baidu.com/view/1007958.htm?noadapt=1 http://blog.csdn.net/hguisu/article/details/7418161 https://www.ibm.com/developerworks/cn/java/j-lo-javaio/ http://docs.oracle.com/javase/7/docs/api/ http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/java/io/package-tree.html http://tutorials.jenkov.com/java-io/index.html
目录
1. Java中IO简介 2. Java中的流 3. Java中和IO相关的类库层次结构
1. Java中IO简介
IO(Input And Output)在编程中是一个很常见的需求,IO即意味着我们的java程序需要和"外部"进行通信,这个"外部"可以是很多介质
1) 本地磁盘文件、远程磁盘文件 2) 数据库连接 3) TCP、UDP、HTTP网络通信 4) 进程间通信 5) 硬件设备(键盘、串口等)...
2. Java中的流
IO是我们的目的,而要达到这一目的,我们需要一种机制来帮助我们完全,这种机制就是"流"、或者叫"数据流"。
数据流是一串连续不断的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。
Java的IO模型设计非常优秀,它使用Decorator模式,按功能划分Stream
记住这句话对我们在编程中选择合适的类库很重要,Java中按照功能提供不同类别的流,我们接下来深入学习一下java中的各个流、以及它们的层次结构关系
3. Java中和IO相关的类库层次结构
首先,java中所有的对象(包括)流对象都从Object 超类继承而来,所以,所有的流类的父类都是Object类
以下的缩进关闭表示的的类的继承关系
Object(超类) 1. 基于"字节"操作的 I/O 接口: 1) InputStream InputStream类是一个abstract class(抽象父类),它不能被直接用于实例化进行流操作,我们在编程中使用的是它的子类 1.1) ByteArrayInputStream: 从字节数组(byte[])中进行以字节为单位的读取 1.2) FileInputStream: 从文件中进行以字节为单位的读取 1.2.1) SocketInputStream org.apache.commons.net.io.SocketInputStream: 封装了对Socket的字节型流式读取 1.3) FilterInputStream: 用来"封装其它的输入流,并为它们提供额外的功能" 1.3.1) InflaterInputStream java.util.zip.InflaterInputStream: 从压缩数据源(zip)中以字节为单位读取数据 1.3.1.1) ZipInputStream java.util.zip.ZipInputStream: 从zip文件中以字节为单位读取数据 1.3.2) BufferedInputStream: 开辟"内部字节数组"对输入流进行缓存,函数的返回也是一个字节数组 1.3.3) DataInputStream: DataInputStream 是用来装饰其它输入流,它"允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型"。应用程序可以使用DataOutputStream(数据输出流) 写入由DataInputStream(数据输入流)读取的数据。 1.4) ObjectInputStream: 从输入流中读取序列化后的数据,并进行反序列化(deserializes) 1.5) PipedInputStream: 从管道中读取数据 2) OutputStream OutputStream类是一个abstract class(抽象父类),它不能被直接用于实例化进行流操作,我们在编程中使用的是它的子类 2.1) ByteArrayOutputStream: 以字节为单位将数据写入到从字节数组(byte[])中 2.2) FileOutputStream: 以字节为单位将数据写入到文件中 2.2.1) SocketOutputStream org.apache.commons.net.io.SocketOutputStream: 封装了对Socket的字节型流式写入 2.3) FilterOutputStream: 用来"封装其它的输出流,并为它们提供额外的功能" 2.3.1) ZipOutputStream: java.util.zip.ZipOutputStream: 以字节为单位向zip文件写入数据 2.3.2) PrintStream: PrintStream 是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式 2.3.3) DataOutputStream: DataOutputStream 是用来装饰其它输入流,它"允许应用程序以与机器无关方式向底层输出流中写入基本 Java 数据类型"。应用程序可以使用DataInputStream(数据输入流) 写入由DataOutputStream(数据输出流)写入的数据()。有点类似管道、或者进程间通信的感觉 2.3.4) BufferedInputStream: 2.4) ObjectOutputStream: 对数据进行序列化(serializes),并向输出流中写入序列化后的数据 2.5) PipedOutputStream: 向管道中写入数据 2. 基于"字符"操作的 I/O 接口 不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符,所以 I/O 操作的都是字节而不是字符,为了操作方便,java封装了一个直接写字符的 I/O 接口,这里就涉及到java的流机制中的一个很重要的概念,包装(装饰)。即所有的流操作在底层实现都是字节流的形式,以这个底层字节流为基础,在其上封装了各种"附加功能"(缓存、字符、管道..) 1) Reader Reader类是一个abstract class(抽象父类),它不能被直接用于实例化进行流操作,我们在编程中使用的是它的子类 1.1) InputStreamReader: 我们知道,字符型的流接口是在字节型的流接口基础之上进行了一次封装,提供了一些额外的功能。所以,从名字上也可以看出来,InputStreamReader是字节流通向字符流的桥梁, 封裝了InputStream在里头, 它以较高级的方式,一次读取一个一个字符,以文本格式输入/输出,可以指定编码格式。 1.1.1) FileReader: 提供对文本文件(保存字符的文件)进行以字符为单位的读取 1.2) BufferedReader: BufferedReader会一次性从物理流中读取8k(默认数值,可以设置)字节内容到内存,如果外界有请求,就会到这里存取,如果内存里没有才到物理流里再去读。即使读,也是再8k 而直接读物理流,是按字节来读。对物理流的每次读取,都有IO操作。IO操作是最耗费时间的。BufferedReader就是减少了大量IO操作,节省了时间 1.3) CharArrayReader: CharArrayReader 是字符数组输入流。它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流。 CharArrayReader 是用于读取字符数组,它继承于Reader。操作的数据是以字符为单位 1.4) FilterReader: 用来"封装其它的字符输入流,并为它们提供额外的功能" 1.5) PipedReader: PipedReader 是字符管道输入流,它继承于Reader。 1.6) StringReader: 以String作为数据源,进行以字符为单位的读取 2) Writer Writer类是一个abstract class(抽象父类),它不能被直接用于实例化进行流操作,我们在编程中使用的是它的子类 2.1) OutputStreamWriter: 2.1.1) FileWriter: 提供对文本文件(保存字符的文件)进行以字符为单位的写入 2.2) BufferedWriter 2.3) StringWriter 2.4) PipedWriter 2.5) PrintWriter 2.6) CharArrayWriter 3. 基于"磁盘"操作的 I/O 接口: 1) File: (文件特征与管理): 用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等,它不负责数据的输入输出,而专门用来管理磁盘文件与目录 1) public boolean exists( ) 判断文件或目录是否存在 2) public boolean isFile( ) 判断是文件还是目录 3) public boolean isDirectory( ) 判断是文件还是目录 4) public String getName( ) 返回文件名或目录名 5) public String getPath( ) 返回文件或目录的路径。 6) public long length( ) 获取文件的长度 7) public String[] list( ) 将目录中所有文件名保存在字符串数组中返回 8) public boolean renameTo( File newFile ); 重命名文件 9) public void delete( ); 删除文件 10) public boolean mkdir( ); 创建目录 4. 基于网络操作的 I/O 接口: 1) Socket
以上是按照Java官方API文档列出的总的目录,我们接下来逐一学习一下它们的应用场景,因为输入、输出流在编程上具有对称性,所以我们把它们合并在一起学习
0x1: InputStream: 字节输入流
0x2:
OutputStream: 字节输出流
ByteArrayInputStream、ByteArrayOutputStream
ByteArrayOutputStream类是在创建它的实例时,程序内部创建一个byte型数组的缓冲区,然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写入或读出byte型数据。在网络传输中我们往往要传输很多变量,我们可以利用ByteArrayOutputStream把所有的变量收集到一起,然后一次性把数据发送出去
import java.io.*; public class circle { public static void main(String[] args) throws Exception { int a=0; int b=1; int c=2; /* * ByteArrayOutputStream() * Creates a new byte array output stream. */ ByteArrayOutputStream bout = new ByteArrayOutputStream(); /* * write(int b) * Writes the specified byte to this byte array output stream. */ bout.write(a); bout.write(b); bout.write(c); /* * toByteArray() * Creates a newly allocated byte array. * 返回内部保存的临时byte缓存数组 */ byte[] buff = bout.toByteArray(); for(int i=0; i<buff.length; i++) { System.out.println(buff[i]); } System.out.println("***********************"); /* * ByteArrayInputStream(byte[] buf) * Creates a ByteArrayInputStream so that it uses buf as its buffer array. */ ByteArrayInputStream bin = new ByteArrayInputStream(buff); /* * read() * Reads the next byte of data from this input stream. */ while((b = bin.read()) != -1) { System.out.println(b); } } }
FileInputStream、FileOutputStream
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; public class circle { public static void main(String[] args) throws Exception { File f = new File("C:\1.txt"); //向文件里写如"Hello"字符串. try { //要写入的数据转换成字节数组 byte[] buf = "Hello".getBytes(); /* * FileOutputStream(File file) * Creates a file output stream to write to the file represented by the specified File object. * 如果1.txt存在,则删除1.txt里面的内容,文本所有内容变为Hello * 如果1.txt不存在,在新建1.txt文本,写入Hello */ FileOutputStream out = new FileOutputStream(f); /* * write(byte[] b) * Writes b.length bytes from the specified byte array to this file output stream. */ out.write(buf); out.close(); } catch(Exception e) { System.out.println(e); } //读取文件中的内容 try { /* * FileInputStream(File file) * Creates a FileInputStream by opening a connection to an actual file, the file named by the File object file in the file system. */ FileInputStream in = new FileInputStream(f); byte[] buf = new byte[1024]; /* * read(byte[] b) * Reads up to b.length bytes of data from this input stream into an array of bytes. * 从流中读取内容 */ int len = in.read(buf); String str = new String(buf,0,len); //打印f文件的内容. System.out.println(str); } catch(Exception e) { System.out.println(e); } } }
ZipInputStream、ZipOutputStream(包装流)
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import java.io.FileOutputStream; public class circle { public static void main(String[] args) throws Exception { // 定义要压缩的文件 File file = new File("C:" + File.separator + "in.txt"); // 定义压缩文件名称 File zipFile = new File("C:" + File.separator + "in.zip"); /* * FileInputStream(File file) * Creates a FileInputStream by opening a connection to an actual file, the file named by the File object file in the file system. * 定义文件的输入流 */ InputStream input = new FileInputStream(file); // 声明压缩流对象 ZipOutputStream zipOut = null; /* * ZipOutputStream(OutputStream out) * Creates a new ZIP output stream. * 再次体现了Java的流函数架构中的"装饰器设计模式"的强大之处,我们可以根据我们对功能的 * 需要任意组合、加载我们需要的流,用通俗的话来说,把它们层层包裹在一起 */ zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); /* * putNextEntry(ZipEntry e) * Begins writing a new ZIP file entry and positions the stream to the start of the entry data. * 设置ZipEntry对象 */ zipOut.putNextEntry(new ZipEntry(file.getName())); int temp = 0; /* * read() * Reads a byte of data from this input stream. * 从文件流中读取内容 */ while((temp = input.read()) != -1) { // 压缩输出 zipOut.write(temp); } // 关闭输入流 input.close(); // 关闭输出流 zipOut.close(); // read zip file as input stream InputStream is= new FileInputStream("C:" + File.separator + "in.zip"); //zip is read by ZipInputStream ZipInputStream zis= new ZipInputStream(is); // now write zip file in extracted file ZipEntry ze; byte[] buff = new byte[1024]; /* * getNextEntry() * Reads the next ZIP file entry and positions the stream at the beginning of the entry data. */ while((ze = zis.getNextEntry()) != null) { // get file name FileOutputStream fos= new FileOutputStream("C:" + File.separator + "out.txt"); int l=0; // write buffer to file while((l = zis.read(buff)) > 0) { fos.write(buff,0, l); } } zis.close(); } }
BufferedInputStream、BufferedInputStream(包装流)
import java.io.*; public class circle { public static void main(String[] args) throws Exception { File filein = new File("C:/1.png"); File fileout = new File("C:/out.png"); try { //前面说过,File类它不负责数据的输入输出,而专门用来管理磁盘文件与目录 if (fileout.exists() == false) { fileout.createNewFile(); } //要使用文件流,自然要使用文件读取流函数 FileInputStream in = new FileInputStream(filein); FileOutputStream out = new FileOutputStream(fileout); byte[] b = new byte[1]; //在文件流外面包一层缓冲流读取函数 BufferedInputStream bin = new BufferedInputStream(in); BufferedOutputStream bout = new BufferedOutputStream(out); //缓存读取、缓存写入 while (bin.read(b) != -1) { bout.write(b); } bout.close(); bin.close(); out.close(); in.close(); } catch (Exception e) { e.printStackTrace(); } } }
DataInputStream、DataOutputStream(包装流)
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class circle { public static void main(String[] args) throws Exception { writeDemo(); readDemo(); } public static void readDemo() throws IOException { DataInputStream dos = new DataInputStream(new FileInputStream("C:/in.txt")); String s = dos.readUTF(); System.out.println(s); } public static void writeDemo() throws IOException { /* * DataOutputStream(OutputStream out) * Creates a new data output stream to write data to the specified underlying output stream. */ DataOutputStream dos = new DataOutputStream(new FileOutputStream("C:/in.txt")); /* * writeUTF(String str) * Writes a string to the underlying output stream using modified UTF-8 encoding in a machine-independent manner. */ dos.writeUTF("你好啊");//UTF-8修改版 } }
ObjectInputStream、ObjectOutputStream
ObjectOutputStream可以把对象直接存入到文件中,然后利用ObjectInputStream读取文件还原成对象,前提是该对象实现了Serializable接口
import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class circle { public static void main(String[] args) throws Exception { FileOutputStream fop = new FileOutputStream("C:/out.txt"); /* * ObjectOutputStream(OutputStream out) * Creates an ObjectOutputStream that writes to the specified OutputStream. */ ObjectOutputStream oos = new ObjectOutputStream(fop); People p = new People(1,"zhang"); /* * writeObject(Object obj) * Write the specified object to the ObjectOutputStream. */ oos.writeObject(p); p = new People(2,"li"); oos.writeObject(p); p = new People(3,"zhao"); oos.writeObject(p); //写入三个对象 oos.close(); //关闭输出流 FileInputStream fis=new FileInputStream("C:/out.txt"); /* * ObjectInputStream(InputStream in) * Creates an ObjectInputStream that reads from the specified InputStream. */ ObjectInputStream ois = new ObjectInputStream(fis); try { while(true) { /* * readObject() * Read an object from the ObjectInputStream. */ People p2=(People)ois.readObject(); System.out.println(p2); } //没有办法判断文件中对象的数量,所以,只有通过EOFException异常来中断 //或者在写入的时候把所有的对象都放到一个ArrayLis里,这样就不需要判断了 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch(EOFException e) { System.out.println("读取结束"); } } } class People implements Serializable { //必须实现Serializable接口 int id; String name; People(int id,String name) { this.id=id; this.name=name; } public String toString() { return "id:"+id+" name:"+name; } }
PipedInputStream、PipedOutputStream
管道流内部在实现时还有大量的对同步数据的处理。管道输出流和管道输入流执行时不能互相阻塞,所以一般要开启独立线程分别执行
import java.io.*; public class circle { public static void main(String[] args) throws Exception { /* * PipedInputStream() * Creates a PipedInputStream so that it is not yet connected. */ PipedInputStream pin = new PipedInputStream(); PipedOutputStream pout = new PipedOutputStream(); /* * connect(PipedOutputStream src) * Causes this piped input stream to be connected to the piped output stream src. * 输入流与输出流连接 */ pin.connect(pout); ReadThread readTh = new ReadThread(pin); WriteThread writeTh = new WriteThread(pout); new Thread(readTh).start(); new Thread(writeTh).start(); } public static void sop(Object obj) //打印 { System.out.println(obj); } } class ReadThread implements Runnable { private PipedInputStream pin; ReadThread(PipedInputStream pin) // { this.pin=pin; } //由于必须要覆盖run方法,所以这里不能抛,只能try public void run() { try { sop("R:读取前没有数据,阻塞中...等待数据传过来再输出到控制台..."); byte[] buf = new byte[1024]; int len = pin.read(buf); //read阻塞 sop("R:读取数据成功,阻塞解除..."); String s= new String(buf,0,len); sop(s); //将读取的数据流用字符串以字符串打印出来 pin.close(); } catch(Exception e) { throw new RuntimeException("R:管道读取流失败!"); } } public static void sop(Object obj) //打印 { System.out.println(obj); } } class WriteThread implements Runnable { private PipedOutputStream pout; WriteThread(PipedOutputStream pout) { this.pout= pout; } public void run() { try { sop("W:开始将数据写入:但等个5秒让我们观察..."); Thread.sleep(5000); //释放cpu执行权5秒 /* * write(int b) * Writes the specified byte to the piped output stream. * 向管道写入数据,同时解除管道上的阻塞状态 */ pout.write("W: writePiped 数据...".getBytes()); pout.close(); } catch(Exception e) { throw new RuntimeException("W:WriteThread写入失败..."); } } //打印 public static void sop(Object obj) { System.out.println(obj); } }
在了解了"字节型"流处理函数的应用场景后,我们接下来继续学习"字符型"流处理函数
0x3: Reader: 字符输入流
0x4: Writer: 字符输出流
在开始学习"字符型"流处理函数的应用场景之前,我们必须牢记一个概念,Java的流函数的整体架构是"装饰器设计模式",也就是说,所有的流函数都可以按照所需的功能进行任意组合、互相嵌套、包裹。而我们的字符型流处理函数本质上也是对字节型流处理函数的一次包裹(或者说加载了字节型流处理函数的功能)
另外数据持久化或网络传输都是以字节进行的,所以必须要有字符到字节或字节到字符的转化。字符到字节需要转化,其中读的转化过程如下图所示:
InputStreamReader 类是字节到字符的转化桥梁,InputStream 到 Reader 的过程要指定编码字符集,否则将采用操作系统默认字符集,很可能会出现乱码问题。StreamDecoder 正是完成字节到字符的解码的实现类。
写入也是类似的过程如下图所示
通过 OutputStreamWriter 类完成,字符到字节的编码过程,由StreamEncoder完成编码过程
InputStreamReader、OutputStreamReader
import java.io.*; public class circle { public static void main(String[] args) throws Exception { InputStream inputStream = new FileInputStream("C:/in.txt"); /* * InputStreamReader(InputStream in) * Creates an InputStreamReader that uses the default charset. * 在字节型输入流之上包裹一层字符型输入流 */ Reader reader = new InputStreamReader(inputStream); /* * read() * Reads a single character. */ int data = reader.read(); while(data != -1) { char theChar = (char) data; System.out.print(theChar); data = reader.read(); } reader.close(); OutputStream outputStream = new FileOutputStream("C:/out.txt"); /* * OutputStreamWriter(OutputStream out) * Creates an OutputStreamWriter that uses the default character encoding. * 在字节型输出流之上包裹一层字符型输出流 */ Writer writer = new OutputStreamWriter(outputStream); /* * write(String str, int off, int len) * Writes a portion of a string. */ writer.write("Hello World"); writer.close(); } }
从上面的代码我们可以看到,我们使用字节型的文件流读取文件,然后再在上面包裹一层字符型流读取函数。除此之外,还有另一种方法(严格来说不能算另一种方法,因为java的流函数架构是"装饰器设计模式",功能之间可以任意组装),直接之用字符型文件流读取(本质上来说,字符型文件流读取也是一些功能的组装)。
FileReader、FileWriter
import java.io.*; public class circle { public static void main(String[] args) throws Exception { /* * FileReader(String fileName) * Creates a new FileReader, given the name of the file to read from. */ Reader reader = new FileReader("C:/in.txt"); int data = reader.read(); while(data != -1) { char theChar = (char) data; System.out.print(theChar); data = reader.read(); } reader.close(); Writer writer = new FileWriter("C:/out.txt"); writer.write("Hello World"); writer.close(); } }
BufferedReader、BufferedWriter
和BufferedInputStream、BufferedOutputStream不同的是,BufferedReader、BufferedWriter提供了ReadLine、newLine()这种以行为单位的字符读写机制
import java.io.*; public class circle { public static void main(String[] args) throws Exception { /* * FileReader(String fileName) * Creates a new FileReader, given the name of the file to read from. */ Reader reader = new FileReader("C:/in.txt"); /* * BufferedReader(Reader in) * Creates a buffering character-input stream that uses a default-sized input buffer. * 在Reader之上再包一层Buffer缓冲区的功能 */ BufferedReader brd = new BufferedReader(reader); String data = brd.readLine(); while(data != null) { System.out.print(data); data = brd.readLine(); } brd.close(); reader.close(); Writer writer = new FileWriter("C:/out.txt"); /* * BufferedWriter(Writer out) * Creates a buffered character-output stream that uses a default-sized output buffer. */ BufferedWriter bwd = new BufferedWriter(writer); /* * write(String s, int off, int len) * Writes a portion of a String. */ bwd.write("Hello World"); /* * newLine() * Writes a line separator. */ bwd.newLine(); bwd.close(); writer.close(); } }
CharArrayReader、CharArrayWriter
CharArrayReader 是字符数组输入流。它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流。CharArrayReader 是用于读取字符数组,它继承于Reader。操作的数据是以字符为单位
import java.io.*; public class circle { public static void main(String[] args) throws Exception { char a = ‘a‘; char b = ‘b‘; char c = ‘c‘; /* * CharArrayWriter() * Creates a new CharArrayWriter. */ CharArrayWriter carrWrt = new CharArrayWriter(); /* * write(int c) * Writes a character to the buffer. */ carrWrt.write(a); carrWrt.write(b); carrWrt.write(c); /* * toCharArray() * Returns a copy of the input data. */ char[] buff = carrWrt.toCharArray(); for(int i = 0; i < buff.length; i++) { System.out.println(buff[i]); } System.out.println("***********************"); /* * CharArrayReader(char[] buf) * Creates a CharArrayReader from the specified array of chars. */ CharArrayReader carrRed = new CharArrayReader(buff); /* * read() * Reads the next byte of data from this input stream. */ int data; while((data = carrRed.read()) != -1) { System.out.println(data); } } }
FilterReader、FilterWriter
用来"封装其它的字符输入流,并为它们提供额外的功能"
PipedReader、PipedWriter
字符管道流,原理上和PipedInputStream类似
PrintWriter
printwriter是向文本输出流打印对象的格式化表示形式,它允许以一种格式化的方式进行数据流的写入,类似C语言中的printf()函数
import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class circle { public static void main(String[] args) throws Exception { String filename = "C:/out.txt"; /* * PrintWriter(Writer out) * Creates a new PrintWriter, without automatic line flushing. */ PrintWriter pw = new PrintWriter(new FileWriter(filename)); String[] words = new String[]{"hello", "world", "!"}; for (int i = 0; i < words.length; i++) { /* * format(String format, Object... args) * Writes a formatted string to this writer using the specified format string and arguments. */ pw.format("words: %s\n", words[i]); } /* * flush() * Flushes the stream. */ pw.flush(); } }
0x5: File 文件、目录操作接口
我们已经学习了关于流操作的各种函数接口,接下来我么继续学习File类,要明确的一点是,虽然这个类名是File类,但是这个类却不负责文件流的实际读写,在我本人看来,我更愿意叫它"文件元数据Meta操作接口"。而File类常常和文件流读写函数配合起来,进行文件的操作
import java.io.*; public class circle { public void FileOperate() { } /** * 新建目录 * @param folderPath String 如 c:/fqf * @return boolean */ public void newFolder(String folderPath) { try { String filePath = folderPath; filePath = filePath.toString(); java.io.File myFilePath = new java.io.File(filePath); if (!myFilePath.exists()) { myFilePath.mkdir(); } } catch (Exception e) { System.out.println("新建目录操作出错"); e.printStackTrace(); } } /** * 新建文件 * @param filePathAndName String 文件路径及名称 如c:/fqf.txt * @param fileContent String 文件内容 * @return boolean */ public void newFile(String filePathAndName, String fileContent) { try { String filePath = filePathAndName; filePath = filePath.toString(); File myFilePath = new File(filePath); if (!myFilePath.exists()) { myFilePath.createNewFile(); } FileWriter resultFile = new FileWriter(myFilePath); PrintWriter myFile = new PrintWriter(resultFile); String strContent = fileContent; myFile.println(strContent); resultFile.close(); } catch (Exception e) { System.out.println("新建目录操作出错"); e.printStackTrace(); } } /** * 删除文件 * @param filePathAndName String 文件路径及名称 如c:/fqf.txt * @param fileContent String * @return boolean */ public void delFile(String filePathAndName) { try { String filePath = filePathAndName; filePath = filePath.toString(); java.io.File myDelFile = new java.io.File(filePath); myDelFile.delete(); } catch (Exception e) { System.out.println("删除文件操作出错"); e.printStackTrace(); } } /** * 删除文件夹 * @param filePathAndName String 文件夹路径及名称 如c:/fqf * @param fileContent String * @return boolean */ public void delFolder(String folderPath) { try { delAllFile(folderPath); //删除完里面所有内容 String filePath = folderPath; filePath = filePath.toString(); java.io.File myFilePath = new java.io.File(filePath); myFilePath.delete(); //删除空文件夹 } catch (Exception e) { System.out.println("删除文件夹操作出错"); e.printStackTrace(); } } /** * 删除文件夹里面的所有文件 * @param path String 文件夹路径 如 c:/fqf */ public void delAllFile(String path) { File file = new File(path); if (!file.exists()) { return; } if (!file.isDirectory()) { return; } String[] tempList = file.list(); File temp = null; for (int i = 0; i < tempList.length; i++) { if (path.endsWith(File.separator)) { temp = new File(path + tempList[i]); } else { temp = new File(path + File.separator + tempList[i]); } if (temp.isFile()) { temp.delete(); } if (temp.isDirectory()) { delAllFile(path+"/"+ tempList[i]);//先删除文件夹里面的文件 delFolder(path+"/"+ tempList[i]);//再删除空文件夹 } } } /** * 复制单个文件 * @param oldPath String 原文件路径 如:c:/fqf.txt * @param newPath String 复制后路径 如:f:/fqf.txt * @return boolean */ public void copyFile(String oldPath, String newPath) { try { int bytesum = 0; int byteread = 0; File oldfile = new File(oldPath); if (oldfile.exists()) { //文件存在时 InputStream inStream = new FileInputStream(oldPath); //读入原文件 FileOutputStream fs = new FileOutputStream(newPath); byte[] buffer = new byte[1444]; int length; while ( (byteread = inStream.read(buffer)) != -1) { bytesum += byteread; //字节数 文件大小 System.out.println(bytesum); fs.write(buffer, 0, byteread); } inStream.close(); } } catch (Exception e) { System.out.println("复制单个文件操作出错"); e.printStackTrace(); } } /** * 复制整个文件夹内容 * @param oldPath String 原文件路径 如:c:/fqf * @param newPath String 复制后路径 如:f:/fqf/ff * @return boolean */ public void copyFolder(String oldPath, String newPath) { try { (new File(newPath)).mkdirs(); //如果文件夹不存在 则建立新文件夹 File a=new File(oldPath); String[] file=a.list(); File temp=null; for (int i = 0; i < file.length; i++) { if(oldPath.endsWith(File.separator)) { temp=new File(oldPath+file[i]); } else { temp=new File(oldPath+File.separator+file[i]); } if(temp.isFile()) { FileInputStream input = new FileInputStream(temp); FileOutputStream output = new FileOutputStream(newPath + "/" + (temp.getName()).toString()); byte[] b = new byte[1024 * 5]; int len; while ( (len = input.read(b)) != -1) { output.write(b, 0, len); } output.flush(); output.close(); input.close(); } if(temp.isDirectory()) {//如果是子文件夹 copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]); } } } catch (Exception e) { System.out.println("复制整个文件夹内容操作出错"); e.printStackTrace(); } } /** * 移动文件到指定目录 * @param oldPath String 如:c:/fqf.txt * @param newPath String 如:d:/fqf.txt */ public void moveFile(String oldPath, String newPath) { copyFile(oldPath, newPath); delFile(oldPath); } /** * 移动文件到指定目录 * @param oldPath String 如:c:/fqf.txt * @param newPath String 如:d:/fqf.txt */ public void moveFolder(String oldPath, String newPath) { copyFolder(oldPath, newPath); delFolder(oldPath); } }
4. 后记
1. 什么时候改用什么流函数: 我在学习Java的的API的时候第一个问自己的问题就是这个,在我看来,可以遵循以下两点: 1) 明确我们的目的,例如需要读取文件,就使用文件流FileInputStream、需要缓存就包上一层BufferedInputStream、要进行字符型读取就使用InputStreamReader 2) 理解"装饰器设计模式"的概念,我们需要的功能可以通过流函数之间的包裹来进行实现,在编程的时候,注意参考API文档,查明哪些流对象之间可以互相包裹很重要 2. 对流函数的深入理解: Java的源代码是公开的,为了深入理解Java中的IO机制,下一步希望从源代码角度去深入研究一下Java中的IO、流、缓存机制,以及优化方案