林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

摘要:本文主要讲解了Java I/O解读与使用实例。

一、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提供缓冲区功能。

(2)字符流有两个抽象类:Writer Reader其对应子类FileWriter和FileReader可实现文件的读写操作.BufferedWriter和BufferedReader能够提供缓冲区功能,用以提高效率。

二、I/O流的分类

根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。
结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
输入流和输出流
对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。

三、字节流读写操作

3.1、按字节流读文件

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 个数据字节

其类图如下:

使用实例如下:

  1. package com.lin;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. /**
  8. * 功能概要:字节流读取文件
  9. *
  10. * @author linbingwen
  11. * @since 2015年9月5日
  12. */
  13. public class Test1 {
  14. /**
  15. * @author linbingwen
  16. * @since 2015年9月5日
  17. * @param args
  18. * @throws IOException
  19. */
  20. public static void main(String[] args) {
  21. String path = "D:" + File.separator + "test1.txt";
  22. readFile1(path);
  23. readFile2(path);
  24. }
  25. /**
  26. * 字节流读取文件:单个字符读取
  27. * @author linbingwen
  28. * @since  2015年9月5日
  29. * @param path
  30. */
  31. public static void readFile1(String path) {
  32. FileInputStream is = null;
  33. try {
  34. is = new FileInputStream(path);
  35. System.out.println("===============================单个字符读取begin===============================");
  36. int ch = 0;
  37. while ((ch = is.read()) != -1) {
  38. System.out.print((char) ch);
  39. }
  40. System.out.println();
  41. System.out.println("===============================单个字符读取end===============================");
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. } finally {
  45. // 关闭输入流
  46. if (is != null) {
  47. try {
  48. is.close();
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. }
  54. }
  55. /**
  56. * 字节流读取文件:数组循环读取
  57. * @author linbingwen
  58. * @since  2015年9月5日
  59. * @param path
  60. */
  61. public static void readFile2(String path) {
  62. FileInputStream is = null;
  63. try {
  64. // 创建文件输入流对象
  65. is = new FileInputStream(path);
  66. // 设定读取的字节数
  67. int n = 512;
  68. byte buffer[] = new byte[n];
  69. // 读取输入流
  70. System.out.println("===============================数组循环读取begin===============================");
  71. while ((is.read(buffer, 0, n) != -1) && (n > 0)) {
  72. System.out.print(new String(buffer));
  73. }
  74. System.out.println();
  75. System.out.println("===============================数组循环读取end===============================");
  76. } catch (IOException ioe) {
  77. System.out.println(ioe);
  78. } catch (Exception e) {
  79. System.out.println(e);
  80. } finally {
  81. // 关闭输入流
  82. if (is != null) {
  83. try {
  84. is.close();
  85. } catch (IOException e) {
  86. e.printStackTrace();
  87. }
  88. }
  89. }
  90. }
  91. }

test1.txt内容如下:

程序运行结果:

注意:对于中文字符,会出现乱码,中文字符要用字符流来读取

如果把内容改成如下:

输出结果如下:

可以看到,中文确实变成乱码了,这就是按字节读取的坏处。

3.2、按字节流写文件

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字节流的方法。

其类图如下:

使用实例如下:

  1. package com.lin;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. /**
  6. * 功能概要:
  7. *
  8. * @author linbingwen
  9. * @since 2015年9月5日
  10. */
  11. public class Test2 {
  12. /**
  13. * @author linbingwen
  14. * @since 2015年9月5日
  15. * @param args
  16. */
  17. public static void main(String[] args) {
  18. String input = "D:" + File.separator + "hello.jpg";
  19. String output = "D:" + File.separator + "hello1.jpg";
  20. writeFile(input,output);
  21. }
  22. /**
  23. * 文件复制操作,可以是图片、文字
  24. *
  25. * @author linbingwen
  26. * @since 2015年9月5日
  27. * @param input
  28. * @param output
  29. */
  30. public static void writeFile(String input, String output) {
  31. FileInputStream fis = null;
  32. FileOutputStream fos = null;
  33. byte[] buffer = new byte[100];
  34. int temp = 0;
  35. try {
  36. fis = new FileInputStream(input);
  37. fos = new FileOutputStream(output);
  38. while (true) {
  39. temp = fis.read(buffer, 0, buffer.length);
  40. if (temp == -1) {
  41. break;
  42. }
  43. fos.write(buffer, 0, temp);
  44. }
  45. } catch (Exception e) {
  46. System.out.println(e);
  47. } finally {
  48. try {
  49. fis.close();
  50. fos.close();
  51. } catch (Exception e2) {
  52. System.out.println(e2);
  53. }
  54. }
  55. }
  56. }

运行结果:

还可以进行MP3的写!

四、字符流读写操作

4.1、字符流读取操作

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。

其类图如下:

实例代码如下:

  1. package com.lin;
  2. import java.io.BufferedReader;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. import java.io.FileReader;
  7. import java.io.IOException;
  8. import java.io.InputStreamReader;
  9. /**
  10. * 功能概要:字符流读取操作
  11. *
  12. * @author linbingwen
  13. * @since 2015年9月5日
  14. */
  15. public class Test3 {
  16. /**
  17. * @author linbingwen
  18. * @since 2015年9月5日
  19. * @param args
  20. */
  21. public static void main(String[] args) {
  22. String path = "D:" + File.separator + "test3.txt";
  23. readFile1(path);
  24. readFile2(path);
  25. readFile3(path,"utf-8");
  26. }
  27. /**
  28. * 字符流读取文件方法一
  29. * @author linbingwen
  30. * @since  2015年9月5日
  31. * @param path
  32. */
  33. public static void readFile1(String path) {
  34. FileReader r = null;
  35. try {
  36. r = new FileReader(path);
  37. // 读入到字符数组的优化
  38. // 由于有时候文件太大,无法确定需要定义的数组大小
  39. // 因此一般定义数组长度为1024,采用循环的方式读入
  40. char[] buf = new char[1024];
  41. int temp = 0;
  42. System.out.println("========================== 字符流读取文件方法一==========================");
  43. while ((temp = r.read(buf)) != -1) {
  44. System.out.print(new String(buf, 0, temp));
  45. }
  46. System.out.println();
  47. } catch (IOException e) {
  48. e.printStackTrace();
  49. } finally {
  50. if (r != null) {
  51. try {
  52. r.close();
  53. } catch (IOException e) {
  54. e.printStackTrace();
  55. }
  56. }
  57. }
  58. }
  59. /**
  60. * 字符流读取文件方法二
  61. * @author linbingwen
  62. * @since  2015年9月5日
  63. * @param path
  64. * @return
  65. */
  66. public static String readFile2(String path) {
  67. File file = new File(path);
  68. StringBuffer sb = new StringBuffer();
  69. if (file.isFile()) {
  70. BufferedReader bufferedReader = null;
  71. FileReader fileReader = null;
  72. try {
  73. fileReader = new FileReader(file);
  74. bufferedReader = new BufferedReader(fileReader);
  75. String line = bufferedReader.readLine();
  76. System.out.println("========================== 字符流读取文件方法二==========================");
  77. while (line != null) {
  78. System.out.println(line);
  79. sb.append(line + "\r\n");
  80. line = bufferedReader.readLine();
  81. }
  82. } catch (FileNotFoundException e) {
  83. e.printStackTrace();
  84. } catch (IOException e) {
  85. e.printStackTrace();
  86. } finally {
  87. try {
  88. fileReader.close();
  89. bufferedReader.close();
  90. } catch (IOException e) {
  91. e.printStackTrace();
  92. }
  93. }
  94. }
  95. return sb.toString();
  96. }
  97. /**
  98. * 字符流读取文件:可以指定文件编码格式
  99. * @author linbingwen
  100. * @since  2015年9月5日
  101. * @param path
  102. * @param charset
  103. * @return
  104. */
  105. public static String readFile3(String path,String charset) {
  106. File file = new File(path);
  107. StringBuffer sb = new StringBuffer();
  108. if (file.isFile()) {
  109. BufferedReader bufferedReader = null;
  110. InputStreamReader inputStreamReader = null;
  111. try {
  112. inputStreamReader = new InputStreamReader(new FileInputStream(file), charset);
  113. bufferedReader = new BufferedReader(inputStreamReader);
  114. String line = bufferedReader.readLine();
  115. System.out.println("========================== 字符流读取文件方法三==========================");
  116. while (line != null) {
  117. System.out.println(line);
  118. sb.append(line + "\r\n");
  119. line = bufferedReader.readLine();
  120. }
  121. } catch (FileNotFoundException e) {
  122. e.printStackTrace();
  123. } catch (IOException e) {
  124. e.printStackTrace();
  125. } finally {
  126. try {
  127. inputStreamReader.close();
  128. bufferedReader.close();
  129. } catch (IOException e) {
  130. e.printStackTrace();
  131. }
  132. }
  133. }
  134. return sb.toString();
  135. }
  136. }

这是运行结果:

其中,第三种方法如果指定编码后结果如下:
  1. readFile3(path,"GBK");
可以看到,中文变成乱码了。

4.2、按字符写入

字符输出流
FileWriter的常用构造有以下几种。
FileWriter(String fileName):根据文件名创建FileWriter对象。
FileWriter(String fileName,boolean append):根据文件名创建FileWriter对象,append参数用来指定是否在原文件之后追加内容。
FileWriter(File file):根据File对象创建FileWriter对象。
FileWriter(File file,boolean append):根据File对象创建FileWriter对象,append参数用来指定是否在原文件之后追加内容。
FileWriter的常用方法包括以下几种。
void writer(int c):向文件中写入正整数c代表的单个字符。
void writer(char[] cbuf):向文件中写入字符数组cbuf。
void writer(char[] cbuf,int off, in len):向文件中写入字符数组cbuf从偏移位置off开始的len个字符。
void writer(String str):向文件中写入字符串str,注意此方法不会在写入完毕之后自动换行。
void writer(String str,int off,int len):向文件中写入字符串str的从位置off开始、长度为len的一部分子串。
Writer append(char c):向文件中追加单个字符c。
Writer append(CharSequence csq):向文件中追加csq代表的一个字符序列。CharSequence是从JDK1.4版本开始引入的一个接口,代表字符值的一个可读序列,此接口对许多不同种类的字符序列提供统一的只读访问。
Writer append(CharSequence csq,int start,int end):向文件中追加csq字符序列的从位置start开始、end结束的一部分字符。
void flush():刷新字符输出流缓冲区。
void close():关闭字符输出流。
和BufferReader相对应,启用缓冲区的BufferWriter也拥有一下两种形式的构造方法。
BufferWriter(Writer out): 根据out代表的Writer对象创建BufferWriter实例,缓冲区大小采用默认值。
BufferWriter(Writer out,int sz):根据out代表的Writer对象创建BufferWriter实例,缓冲区大小采用指定的sz值。
我们知道,BufferReader类的readLine()方法能一次从输入流中读入一行,但对于BufferWriter类,却没有一次写一行的方法。若要向输出流中一次写一行,可用PrintWriter类(PrintWriter也是Writer的直接子类)将原来的流改造成新的打印流,PrintWriter类有一个方法println(String),能一次输出一行,即在待输出的字符串后自动补“\r\n”.
其类图如下:
实例代码如下:

  1. package com.lin;
  2. import java.io.BufferedWriter;
  3. import java.io.File;
  4. import java.io.FileWriter;
  5. import java.io.IOException;
  6. /**
  7. * 功能概要:
  8. *
  9. * @author linbingwen
  10. * @since  2015年9月5日
  11. */
  12. public class Test4 {
  13. /**
  14. * @author linbingwen
  15. * @since  2015年9月5日
  16. * @param args
  17. */
  18. public static void main(String[] args) {
  19. String path = "D:" + File.separator + "test4.txt";
  20. String str= "Evankaka林炳文Evankaka林炳文Evankaka林炳文\r\n";
  21. writeFile(path,str);
  22. writeFile(path,str);
  23. writeFile(path,str);
  24. }
  25. /**
  26. * 利用字符流写入文件
  27. * @author linbingwen
  28. * @since  2015年9月5日
  29. * @param path
  30. * @param content
  31. */
  32. public static void writeFile(String path,String content){
  33. //由于IO操作会抛出异常,因此在try语句块的外部定义FileWriter的引用
  34. FileWriter w = null;
  35. try {
  36. //以path为路径创建一个新的FileWriter对象
  37. //如果需要追加数据,而不是覆盖,则使用FileWriter(path,true)构造方法
  38. //w = new FileWriter(path,true);
  39. w = new FileWriter(path,true);
  40. //将字符串写入到流中,\r\n表示换行
  41. w.write(content);
  42. //如果想马上看到写入效果,则需要调用w.flush()方法
  43. w.flush();
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. } finally {
  47. //如果前面发生异常,那么是无法产生w对象的
  48. //因此要做出判断,以免发生空指针异常
  49. if(w != null) {
  50. try {
  51. //关闭流资源,需要再次捕捉异常
  52. w.close();
  53. } catch (IOException e) {
  54. e.printStackTrace();
  55. }
  56. }
  57. }
  58. }
  59. }

写入的文件内容如下:

参考文章:
http://developer.51cto.com/art/201309/410902.htm
http://www.ibm.com/developerworks/cn/java/j-lo-javaio/
 
from: http://blog.csdn.net/evankaka/article/details/48225085

Java I/O解读与使用实例的更多相关文章

  1. 转载:Java Lock机制解读

    Java Lock机制解读 欢迎转载: https://blog.csdn.net/chengyuqiang/article/details/79181229 1.synchronized synch ...

  2. Java反射机制可以动态修改实例中final修饰的成员变量吗?

    问题:Java反射机制可以动态修改实例中final修饰的成员变量吗? 回答是分两种情况的. 1. 当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了. ...

  3. Java学习-014-文本文件写入实例源代码(两种写入方式)

    此文源码主要为应用 Java 读取文本文件内容实例的源代码.若有不足之处,敬请大神指正,不胜感激! 第一种:文本文件写入,若文件存在则删除原文件,并重新创建文件.源代码如下所示: /** * @fun ...

  4. Java学习-013-文本文件读取实例源代码(两种数据返回格式)

    此文源码主要为应用 Java 读取文本文件内容实例的源代码.若有不足之处,敬请大神指正,不胜感激! 1.读取的文本文件内容以一维数组[LinkedList<String>]的形式返回,源代 ...

  5. Java学习-008-判断文件类型实例

    此文源码主要为应用 Java 如何判断文件类型的源码及其测试源码.若有不足之处,敬请大神指正,不胜感激!源代码测试通过日期为:2015-2-2 23:02:00,请知悉. Java 判断文件类型源码如 ...

  6. Java Class类以及获取Class实例的三种方式

    T - 由此 Class 对象建模的类的类型.例如,String.class 的类型是Class<String>.如果将被建模的类未知,则使用Class<?>.   publi ...

  7. java并发之固定对象与实例

    java并发之固定对象与实例 Immutable Objects An object is considered immutable if its state cannot change after ...

  8. (转)Java.lang.reflect.Method invoke方法 实例

    背景:今天在项目中用到Method 的invoke方法,但是并不理解,查完才知道,原来如此! import java.lang.reflect.Method; /** * Java.lang.refl ...

  9. Java生成MD5加密字符串代码实例

    这篇文章主要介绍了Java生成MD5加密字符串代码实例,本文对MD5的作用作了一些介绍,然后给出了Java下生成MD5加密字符串的代码示例,需要的朋友可以参考下   (1)一般使用的数据库中都会保存用 ...

随机推荐

  1. li标签之间的空隙问题(转)

    原文地址:http://www.cnblogs.com/laogao/archive/2012/08/13/2636419.html css li 空隙问题   IE6/7的Bug:纵向排列的li中加 ...

  2. HOSTS文件详解【win|mac】

    hosts文件是一个用于储存计算机网络中各节点信息的计算机文件.这个文件负责将主机名映射到相应的IP地址. hosts文件通常用于补充或取代网络中DNS的功能.和DNS不同的是,计算机的使用者可以直接 ...

  3. Qt自定义model

    前面我们说了Qt提供的几个预定义model.但是,面对变化万千的需求,那几个model是远远不能满足我们的需要的.另外,对于Qt这种框架来说,model的选择首先要能满足绝大多数功能的需要,这就是说, ...

  4. mysql 存储过程--- 创建,调用,删除

    DELIMITER //CREATE PROCEDURE p_addscore(nums INT,OUT retrows INT)BEGINDECLARE i INT DEFAULT 0;add_lo ...

  5. php--yii框架表单验证

    在视图层利用表单小部件生成表单时,field只能是数据库中存在的, 例如: use yii\helpers\Html; use yii\widgets\ActiveForm; use yii\capt ...

  6. android动态调试samli代码(转)

    转载自看雪http://bbs.pediy.com/showthread.php?t=189610,非常感谢原作者分享! 跟踪apk一般的做法是在反编译的smali代码中插入log输出,然后重新编译运 ...

  7. Android笔记:gson处理多层嵌套的复杂形式的json

    当一个Class的字段属性中包含另一个class时gson能正常处理吗? 最初看到网上有说使用static的说法 经验证是不需要的 直接当普通类来用就可以了. 直接使用gson.fromJson方法即 ...

  8. jQuery获取自身HTML

    <html><head> <title>jQuery获取自身HTML</title> <meta http-equiv="Content ...

  9. imx6 uboot lvds clock

    在uboot中添加logo,lvds接口的lcd显示不正常,出现波动.网上说是lvds时钟频率的问题. 使用示波器测量之后,发现频率是60M,而lcd最大频率才46.8M. 因此就需要更改uboot中 ...

  10. XLST

    xlst转换 // 读入源请求和mapping配置 StreamSource xmlSource = new StreamSource(new InputStreamReader(new ByteAr ...