林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka
摘要:本文主要讲解了Java I/O解读与使用实例。
I/O全称是Input/Output,Java的I/O就是Java的输入与输出操作。与之相关的接口和类都放在java.io包里面,因而,在进行Java输入输出操作时,需要导入该包。利用Java的I/O大大地扩展了系统的输入与输出范畴,不仅可以从控制台输入输出,还可以从其他数据存储形式进行输入输出,例如本地文件、远程数据库等。Java的I/O在文件数据的读写、数据的网络发送与接收等很多场合发挥着重要作用。
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。对于文件内容的操作主要分为两大类分别是:字符流和字节流
(1)字节流有两个抽象类:InputStream OutputStream其对应子类有FileInputStream和FileOutputStream实现文件读写。而BufferedInputStream和BufferedOutputStream提供缓冲区功能。
根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。
结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
输入流和输出流
对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。
InputStream
此抽象类是表示字节输入流的所有类的超类。需要定义 InputStream 的子类的应用程序必须始终提供返回下一个输入字节的方法。
int available()
返回此输入流方法的下一个调用方可以不受阻塞地从此输入流读取(或跳过)的字节数。
void close()
关闭此输入流并释放与该流关联的所有系统资源。
void mark(int readlimit)
在此输入流中标记当前的位置。
boolean markSupported()
测试此输入流是否支持 mark 和 reset 方法。
abstract int read()
从输入流读取下一个数据字节。
int read(byte[] b)
从输入流中读取一定数量的字节并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len)
将输入流中最多 len 个数据字节读入字节数组。
void reset()
将此流重新定位到对此输入流最后调用 mark 方法时的位置。
long skip(long n)
跳过和放弃此输入流中的 n 个数据字节
其类图如下:
使用实例如下:
package com.lin; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * 功能概要:字节流读取文件 * * @author linbingwen * @since 2015年9月5日 */ public class Test1 { /** * @author linbingwen * @since 2015年9月5日 * @param args * @throws IOException */ public static void main(String[] args) { String path = "D:" + File.separator + "test1.txt"; readFile1(path); readFile2(path); } /** * 字节流读取文件:单个字符读取 * @author linbingwen * @since 2015年9月5日 * @param path */ public static void readFile1(String path) { FileInputStream is = null; try { is = new FileInputStream(path); System.out.println("===============================单个字符读取begin==============================="); int ch = 0; while ((ch = is.read()) != -1) { System.out.print((char) ch); } System.out.println(); System.out.println("===============================单个字符读取end==============================="); } catch (IOException e) { e.printStackTrace(); } finally { // 关闭输入流 if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 字节流读取文件:数组循环读取 * @author linbingwen * @since 2015年9月5日 * @param path */ public static void readFile2(String path) { FileInputStream is = null; try { // 创建文件输入流对象 is = new FileInputStream(path); // 设定读取的字节数 int n = 512; byte buffer[] = new byte[n]; // 读取输入流 System.out.println("===============================数组循环读取begin==============================="); while ((is.read(buffer, 0, n) != -1) && (n > 0)) { System.out.print(new String(buffer)); } System.out.println(); System.out.println("===============================数组循环读取end==============================="); } catch (IOException ioe) { System.out.println(ioe); } catch (Exception e) { System.out.println(e); } finally { // 关闭输入流 if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
程序运行结果:
注意:对于中文字符,会出现乱码,中文字符要用字符流来读取
如果把内容改成如下:
输出结果如下:
可以看到,中文确实变成乱码了,这就是按字节读取的坏处。
OutputStream
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。需要定义OutputStream 子类的应用程序必须始终提供至少一种可写入一个输出字节的方法。
void close()
关闭此输出流并释放与此流有关的所有系统资源。
void flush()
刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[] b)
将 b.length 个字节从指定的字节数组写入此输出流。
void write(byte[] b, int off, int len)
将指定字节数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract void write(int b)
将指定的字节写入此输出流。
进行I/O操作时可能会产生I/O例外,属于非运行时例外,应该在程序中处理。如:FileNotFoundException, EOFException, IOException等等,下面具体说明操作JAVA字节流的方法。
其类图如下:
使用实例如下:
package com.lin; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; /** * 功能概要: * * @author linbingwen * @since 2015年9月5日 */ public class Test2 { /** * @author linbingwen * @since 2015年9月5日 * @param args */ public static void main(String[] args) { String input = "D:" + File.separator + "hello.jpg"; String output = "D:" + File.separator + "hello1.jpg"; writeFile(input,output); } /** * 文件复制操作,可以是图片、文字 * * @author linbingwen * @since 2015年9月5日 * @param input * @param output */ public static void writeFile(String input, String output) { FileInputStream fis = null; FileOutputStream fos = null; byte[] buffer = new byte[100]; int temp = 0; try { fis = new FileInputStream(input); fos = new FileOutputStream(output); while (true) { temp = fis.read(buffer, 0, buffer.length); if (temp == -1) { break; } fos.write(buffer, 0, temp); } } catch (Exception e) { System.out.println(e); } finally { try { fis.close(); fos.close(); } catch (Exception e2) { System.out.println(e2); } } } }运行结果:
还可以进行MP3的写!
java采用16位的Unicode来表示字符串和字符,对应的数据流就称为字符流。Reader和Writer为字符流设计。FileReader是InputStreamReader的子类,而InputStreamReader是Reader的子类;FileWriter是OutputStreamWriter的子类,而OutputStreamWriter则是Writer的子类。字符流和字节流的区别在于,字符流操作的对象是字符及字符数组,而字节流操作的对象则是字节及字节数组。
字符输入流
FileReader的常用构造包括以下几种。
FileReader(String fileName):根据文件名创建FileReader对象。
FileReader(File file):根据File对象创建FileReader对象。
FileReader的常用方法包括以下几种。
int read():读取单个字符。返回字符的整数值,如果已经到达文件尾,则返回-1.
int read(char[] cbuf):将字符读入cbuf字符数组。返回读取到的字符数,如果已经到达文件尾,则返回-1.
int read(char[] cbuf,int off,int len):将读取到的字符存放到cbuf字符数组从off标识的偏移位置开始处,最多读取len个字符。
与字节流不同,BufferReader是Reader的直接子类,这一点和BufferInputStream是InputStream的二级子类有所不同。通过BufferReader.readLine()方法可以实现读取文本行、返回字符串,因为我们平时读取的文本文件大多是断行的,而且该方法能直接返回字符串,因此BufferReader使用得比FileReader更为广泛。
BufferReader用有以下两种构造方法。
BufferReader(Reader in):根据in代表的Reader对象创建BufferReader实例,缓冲区大小采用默认值。
BufferReader(Reader in,int sz):根据in代表的Reader对象创建BufferReader实例,缓冲区大小采用指定sz值。
BufferReader.readLine()方法遇到以下字符或者字符串认为当前行结束:‘\n’(换行符),'\r'(回车符),'\r\n'(回车换行)。返回值为该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null。
其类图如下:
实例代码如下:
package com.lin; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; /** * 功能概要:字符流读取操作 * * @author linbingwen * @since 2015年9月5日 */ public class Test3 { /** * @author linbingwen * @since 2015年9月5日 * @param args */ public static void main(String[] args) { String path = "D:" + File.separator + "test3.txt"; readFile1(path); readFile2(path); readFile3(path,"utf-8"); } /** * 字符流读取文件方法一 * @author linbingwen * @since 2015年9月5日 * @param path */ public static void readFile1(String path) { FileReader r = null; try { r = new FileReader(path); // 读入到字符数组的优化 // 由于有时候文件太大,无法确定需要定义的数组大小 // 因此一般定义数组长度为1024,采用循环的方式读入 char[] buf = new char[1024]; int temp = 0; System.out.println("========================== 字符流读取文件方法一=========================="); while ((temp = r.read(buf)) != -1) { System.out.print(new String(buf, 0, temp)); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } finally { if (r != null) { try { r.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 字符流读取文件方法二 * @author linbingwen * @since 2015年9月5日 * @param path * @return */ public static String readFile2(String path) { File file = new File(path); StringBuffer sb = new StringBuffer(); if (file.isFile()) { BufferedReader bufferedReader = null; FileReader fileReader = null; try { fileReader = new FileReader(file); bufferedReader = new BufferedReader(fileReader); String line = bufferedReader.readLine(); System.out.println("========================== 字符流读取文件方法二=========================="); while (line != null) { System.out.println(line); sb.append(line + "\r\n"); line = bufferedReader.readLine(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileReader.close(); bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } /** * 字符流读取文件:可以指定文件编码格式 * @author linbingwen * @since 2015年9月5日 * @param path * @param charset * @return */ public static String readFile3(String path,String charset) { File file = new File(path); StringBuffer sb = new StringBuffer(); if (file.isFile()) { BufferedReader bufferedReader = null; InputStreamReader inputStreamReader = null; try { inputStreamReader = new InputStreamReader(new FileInputStream(file), charset); bufferedReader = new BufferedReader(inputStreamReader); String line = bufferedReader.readLine(); System.out.println("========================== 字符流读取文件方法三=========================="); while (line != null) { System.out.println(line); sb.append(line + "\r\n"); line = bufferedReader.readLine(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { inputStreamReader.close(); bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } }这是运行结果:
package com.lin; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; /** * 功能概要: * * @author linbingwen * @since 2015年9月5日 */ public class Test4 { /** * @author linbingwen * @since 2015年9月5日 * @param args */ public static void main(String[] args) { String path = "D:" + File.separator + "test4.txt"; String str= "Evankaka林炳文Evankaka林炳文Evankaka林炳文\r\n"; writeFile(path,str); writeFile(path,str); writeFile(path,str); } /** * 利用字符流写入文件 * @author linbingwen * @since 2015年9月5日 * @param path * @param content */ public static void writeFile(String path,String content){ //由于IO操作会抛出异常,因此在try语句块的外部定义FileWriter的引用 FileWriter w = null; try { //以path为路径创建一个新的FileWriter对象 //如果需要追加数据,而不是覆盖,则使用FileWriter(path,true)构造方法 //w = new FileWriter(path,true); w = new FileWriter(path,true); //将字符串写入到流中,\r\n表示换行 w.write(content); //如果想马上看到写入效果,则需要调用w.flush()方法 w.flush(); } catch (IOException e) { e.printStackTrace(); } finally { //如果前面发生异常,那么是无法产生w对象的 //因此要做出判断,以免发生空指针异常 if(w != null) { try { //关闭流资源,需要再次捕捉异常 w.close(); } catch (IOException e) { e.printStackTrace(); } } } } }