Java类库里有四个表示流的抽象父类:InputStream、OutputStream、Reader、Writer。

  其中 InputStream 和 OutputStream 是对字节进行操作的输入流和输出流;Reader 和 Writer 是对字符操作的输入输出流。

  它们是抽象类,在用它们的时候必须要对其进行实例化,因此类库也提供了具有不同功能的它们的子类,比如,以文件形式输入输出的 FileInputStream、FileOutputStream 和FileReader、FileWriter 等。

  以下列出了 Java IO 中一些比较常用的流类以及它们的方法使用,更多的流类请查阅API文档。

  --------------------------------------------------------------

  一、字节流

  1. InputStream、OutputStream 只能进行字节的传输数据

         InputStream抽象了应用程序读取数据的方式

       OutputStream抽象了应用程序写出数据的方式

  2. EOF = End,读到 -1 就读到结尾。

  3. 输入流的基本方法(以下的 in 代表输入流的对象)

      int b = in.read();

      从流读取一个字节无符号填充到int的低8位。(一个int变量是4个字节,1字节=8位,read()方法只能读取一个字节并返回int,所以该字节填充到int的低八位。)若读到结尾,返回 -1。用该方法读取大文件时,要使用while循环,这样效率较低,时间也较长,可以使用下面的批量读取方式。

      in.read(byte[] buf);

      从流读取数据直接填充到字节数组buf,读取字节的个数取决于数组的长度。返回的是读到的字节的个数。如果读到结尾返回-1。

      in.read(byte[] buf, int start, int size);

      从流读取数据到字节数组buf,从buf的start位置开始,存放最多size长度的数据。返回的是读到的字节的个数。如果读到结尾返回-1。

  4. 输出流的基本方法(以下的 out 代表输出流的对象)

      out.write(int b);

      写出一个byte到流,写出的是b的低8位。

      out.write(byte[] buf);

      将一个字节数组写出到流。

      out.write(byte[] buf, int start, int size);

      从buf的start位置开始,写size长度的数据到流。

  5. FileInputStream 是 InputStream 的子类,具体实现了在文件上读取字节数据:

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream; public class TestDemo { public static void main(String[] args) throws Exception {
//我们在D盘下创建了一个demo.txt,内容为“你好hello”。
File file = new File("D:\\demo.txt");
if(!file.exists()) file.createNewFile();
InputStream in = new FileInputStream(file);
int temp;
while((temp = in.read()) != -1) {
System.out.print(Integer.toHexString(temp & 0xff) + " ");
}
in.close();
System.out.println(); InputStream in2 = new FileInputStream(file);
byte[] buf = new byte[1024];
int bytes = in2.read(buf);
String s = new String(buf,0,bytes,"GBK");
System.out.println(s);
in2.close();
} }

Output: c4 e3 ba c3 68 65 6c 6c 6f
      你好hello

  6. FileOutputStream 是InputStream的子类,具体实现了在文件上读写出字节数据:

//这是一个拷贝文件的例子

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; public class TestDemo { public static void copyFile(File srcFile,File destFile) throws IOException{
if(!srcFile.exists()) {
throw new IllegalArgumentException("文件 " + srcFile + " 不存在!");
}else if(!srcFile.isFile()) {
throw new IllegalArgumentException(srcFile + " 不是文件!");
}else {
FileInputStream in = new FileInputStream(srcFile);
FileOutputStream out = new FileOutputStream(destFile);
byte[] bytes = new byte[8*1024];
int b;
while((b = in.read(bytes, 0, bytes.length)) != -1) {
out.write(bytes, 0, b);
out.flush(); //对于字节流而言,此句不加也没关系。
}
in.close();
out.close();
}
} public static void main(String[] args) {
try {
copyFile(new File("D:\\demo.txt"),new File("D:\\demo_copy.txt"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

  7. DataInputStream / DataOutputStream

      对“流”功能的扩展,可以更加方便的读写int,long,字符等基本数据类型

      如:DataOutputStream的方法有 writeInt() , writeDouble() , writeUTF() 等。

      在构造Data流时,要用到其他的流作为参数。如:DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));

      这其实是一种“装饰器(过滤器)”设计模式:首先用“其他的流”来构造Data流,然后像 writeInt() 这些方法里面其实都是用“其他的流”的 write() 方法来实现的,如:一个Int有4个字节,用 write() 八位八位地写,做4次 write() ,然后再做移位操作。说白了就是用 writeInt() 将这些操作包装好,方便以后的直接使用。

     

writeInt()源码 

package test;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; public class TestDemo { public static void main(String[] args) throws IOException{ DataOutputStream dos = new DataOutputStream(
new FileOutputStream("D:\\demo.dat"));
dos.writeInt(10);
dos.writeInt(-10);
dos.writeUTF("中国"); //采用utf-8编码写出
dos.writeChars("中国"); //采用utf-16be编码写出
PrintHex.printHex("D:\\demo.dat");
dos.close(); DataInputStream dis = new DataInputStream(
new FileInputStream("D:\\demo.dat"));
System.out.println();
int i = dis.readInt();
System.out.println(i);
i = dis.readInt();
System.out.println(i);
String s = dis.readUTF();
System.out.println(s);
dis.close();
} }
  /*--------------------------------------------*/
package test; import java.io.FileInputStream;
import java.io.IOException; public class PrintHex { public static void printHex(String fileName) throws IOException {
FileInputStream in = new FileInputStream(fileName);
int b;
while((b = in.read()) != -1) {
System.out.print(Integer.toHexString(b & 0xff) + " ");
}
in.close();
} } /**
* Output:
* 0 0 0 a ff ff ff f6 0 6 e4 b8 ad e5 9b bd 4e 2d 56 fd
* 10
* -10
* 中国
*/

  8. BufferedInputStream / BufferedOutputStream

      这两个流类为IO提供了带缓冲区的操作,一般打开文件进行读写操作时都会加上缓冲,这种流模式提高了IO的性能。

      从应用程序中把输入放入文件,相当于将一缸水倒入到另一个缸中:
      FileOutputStream  --->  write() 方法相当于一滴一滴地把水“转移”过去。
      DataOutputStream  --->  writeXxx() 方法会方便一些,相当于一瓢一瓢把水“转移”过去。
        BufferedOutputStream  --->  write 方法更方便,相当于一飘一瓢先放入桶中,再从桶中倒入到另一个缸中,性能提高了。

      这种字节缓冲流同样是采用装饰模式,要用其他的流来参与构造。

      以下是四种不同方式的文件拷贝,效率对比:

package test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; public class CopyMethod {
//以单字节传输
public static void copyByByte(String srcFile,String destFile) throws IOException {
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
int i;
while((i = fis.read()) != -1) {
fos.write(i);
}
fis.close();
fos.close();
}
//以字节数组传输
public static void copyByBytes(String srcFile,String destFile) throws IOException {
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
byte[] bytes = new byte[8*1024];
int i;
while((i = fis.read(bytes, 0, bytes.length)) != -1) {
fos.write(bytes, 0, i);
}
fis.close();
fos.close();
}
//以单字节缓冲流传输
public static void copyByByteBuffer(String srcFile,String destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destFile));
int i;
while((i = bis.read()) != -1) {
bos.write(i);
bos.flush(); //缓冲流要冲刷,否则写入不到文件
}
bis.close();
bos.close();
}
//以字节数组缓冲流传输
public static void copyByBytesBuffer(String srcFile,String destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destFile));
byte[] bytes = new byte[8*1024];
int i;
while((i = bis.read(bytes, 0, bytes.length)) != -1) {
bos.write(bytes, 0, i);
bos.flush(); //缓冲流要冲刷,否则写入不到文件
}
bis.close();
bos.close();
} }
package test;

import java.io.IOException;

public class TestDemo {

    public static void main(String[] args) {
try {
       //比较这四种拷贝方式的效率。
long start = System.currentTimeMillis();
//Copy.copyByByte("D:\\1.mp3", "D:\\2.mp3"); //用时2908ms
//Copy.copyByByteBuffer("D:\\1.mp3","D:\\2.mp3"); //用时1854ms
//Copy.copyByBytes("D:\\1.mp3","D:\\2.mp3"); //用时7ms
//Copy.copyByBytesBuffer("D:\\1.mp3","D:\\2.mp3"); //用时37ms,以字节数组缓冲流的方式反而比不带缓冲的字节数组传输得慢。
long end = System.currentTimeMillis();
System.out.println(end-start);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }

  ------------------------------------------------------------------

  二、字符流 

  1) 认识文本和文本文件:

    java的文本(char) 是16位无符号整数,是字符的unicode编码(双字节编码)。

    文件 是byte byte byte ... 的数据序列。

    文本文件 是文本(char)序列按照某种编码方案(utf-8,utf-16be,gbk)序列化为byte的存储结果。

  2) 字符流(Reader Writer为抽象类) ---> 一般操作的是文本文件

  1. InputStreamReader / OutputStreamWriter 

    字符的处理:一次处理一个字符。字符的底层依然是基本的字节序列,字符流的基本实现:

    InputStreamReader   完成byte流解析为char流,按照编码解析。

    OutputStreamWriter  提供char流到byte流,按照编码处理。

    字符流在构造时依然要用到字节流对象作为构造参数。

//我们利用字符流同时做了文本文件的打印和拷贝。
package test; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter; public class IsrAndOswDemo {
public static void main(String[] args)throws IOException {
FileInputStream in = new FileInputStream("D:\\douban-utf-8.txt");
InputStreamReader isr = new InputStreamReader(in,"utf-8"); //如不写编码方式就默认为项目的编码。操作的时候,要写文件本身的编码格式。 FileOutputStream out = new FileOutputStream("D:\\douban-utf-8-copy.txt");
OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8");
/*int c ;
while((c = isr.read())!=-1){
System.out.print((char)c);
}*/
char[] buffer = new char[8*1024];
int c;
/*批量读取,放入buffer这个字符数组,从第0个位置开始放置,最多放buffer.length个
返回的是读到的字符的个数
*/
while(( c = isr.read(buffer,0,buffer.length))!=-1){
String s = new String(buffer,0,c);
System.out.print(s);
osw.write(buffer,0,c);
osw.flush();
}
isr.close();
osw.close();
}
}

  2. FileReader / FileWriter

    方便直接读写文本文件,不用像 InputStreamReader 和 OutputStreamWriter 一样需要嵌套一个File字节流。

    在构造文件字符流时,有两点需要注意:

     1) 不能使用编码方式的参数,也就是说只能读取编码方式和项目编码方式相同的文本文件,否则读取进来再做输出时会乱码。

     2) 构造FileWriter时,可以使用append参数,如果设置为true,会在输出的文本文件后面追加文本内容。

//文本文件拷贝实例

package test;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; public class FrAndFwDemo {
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("D:\\douban-gbk.txt");
FileWriter fw = new FileWriter("D:\\douban-gbk-copy.txt",true); //可以添加append参数,如果有,则在文件后面追加内容。
char[] buffer = new char[2056];
int c ;
while((c = fr.read(buffer,0,buffer.length))!=-1){
fw.write(buffer,0,c);
fw.flush();
}
fr.close();
fw.close();
}
}

  3. 字符流的过滤器 BufferedReader / BufferedWriter / PrintWriter

    这两个过滤器最强大的功能是具有读写一整行的方法:

    BufferedReader ---> readLine() 读一行,但是不能识别换行符

    BufferedWriter / PrintWriter ---> 写一行

package test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter; public class BrAndBwOrPwDemo {
public static void main(String[] args) throws IOException{
//对文件进行读写操作
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("D:\\douban-utf-8.txt"),"utf-8"));
/*BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("D:\\douban-utf-8-copy.txt"),"utf-8")); //写出时如果不加编码方式,会按默认的编码方式写出,copy的文件不再是utf-8编码,而是gbk编码。*/
PrintWriter pw = new PrintWriter("e:\\javaio\\imooc4.txt","utf-8");
//PrintWriter pw1 = new PrintWriter(outputStream,boolean autoFlush);
String line ;
while((line = br.readLine())!=null) { //readLine方法一次读一行,但并不能识别换行
System.out.println(line);
/*bw.write(line);
//单独写出换行操作
bw.newLine();//换行操作
bw.flush();*/
pw.println(line); //写入到文件
pw.flush();
}
br.close();
//bw.close();
pw.close();
}
}

  ------------------------------------------------------------------

  三、对象的序列化和反序列化

    对象序列化,就是将 Object 转换成 byte序列,反之叫对象的反序列化。对对象进行序列化和反序列化为的是 方便将来在 网络上 或者 本地 进行对象的传输。

  1. 序列化的基本操作 ObjectOutputStream / ObjectInputStream

    1) 序列化流(ObjectOutputStream) ---> 方法 writeObject(Object)

      反序列化流(ObjectInputStream) ---> 方法 readObject() ,返回的是Object类

    2) 序列化接口(Serializable)

      要对对象进行序列化,那么这个对象必须要实现序列化接口Serializable,否则将出现异常。这个接口,没有任何方法,只是一个标准。

package test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; public class ObjectSeriaDemo1 {
public static void main(String[] args) throws Exception{
String file = "demo/obj.dat";
//对象的序列化,将stu对象转化为字节序列存入file。
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file));
Student stu = new Student("10001", "张三", 20);
oos.writeObject(stu);
oos.flush();
oos.close();
//对象的反序列化,将file中的字节序列转化为对象。
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(file));
Student stu2 = (Student)ois.readObject(); //需要进行强制类型转换,因为readObject方法返回的是Object类,而不是Student类。
System.out.println(stu2);
ois.close();
}
}

Java基础学习 -- I/O系统、流的更多相关文章

  1. 尚学堂JAVA基础学习笔记

    目录 尚学堂JAVA基础学习笔记 写在前面 第1章 JAVA入门 第2章 数据类型和运算符 第3章 控制语句 第4章 Java面向对象基础 1. 面向对象基础 2. 面向对象的内存分析 3. 构造方法 ...

  2. Java基础学习笔记总结

    Java基础学习笔记一 Java介绍 Java基础学习笔记二 Java基础语法之变量.数据类型 Java基础学习笔记三 Java基础语法之流程控制语句.循环 Java基础学习笔记四 Java基础语法之 ...

  3. 转载-java基础学习汇总

    共2页: 1 2 下一页  Java制作证书的工具keytool用法总结 孤傲苍狼 2014-06-24 11:03 阅读:25751 评论:3     Java基础学习总结——Java对象的序列化和 ...

  4. java基础学习总结——java环境变量配置(转)

    只为成功找方法,不为失败找借口! 永不放弃,一切皆有可能!!! java基础学习总结——java环境变量配置 前言 学习java的第一步就要搭建java的学习环境,首先是要安装 JDK,JDK安装好之 ...

  5. Java基础学习(2)

    Java基础学习(二) 面向对象 对象:客观存在的事物 面向对象:人具体关注的事物的某些信息 类:是模子,确定对象会拥有的特征(属性)和行为(方法) 对象的属性:对象具有的各种特征 对象的方法:对象能 ...

  6. Java基础学习-- 继承 的简单总结

    代码参考:Java基础学习小记--多态 为什么要引入继承? 还是做一个媒体库,里面可以放CD,可以放DVD.如果把CD和DVD做成两个没有联系的类的话,那么在管理这个媒体库的时候,要单独做一个添加CD ...

  7. 2015年12月28日 Java基础系列(六)流

    2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流

  8. Java基础学习中一些词语和语句的使用

    在Java基础学习中,我们刚接触Java会遇到一些词和语句的使用不清的情况,不能很清楚的理解它的运行效果会是怎么样的,如:break,continue在程序中运行效果及跳转位置, 1.先来看看brea ...

  9. java基础学习总结——开篇

    java是我学习的第一门编程语言,当初学习java基础的时候下了不少功夫,趁着这段时间找工作之际,好好整理一下以前学习java基础时记录的笔记,当作是对java基础学习的一个总结吧,将每一个java的 ...

随机推荐

  1. js事件(event)的运行原理

    昨天写click事件时候突然脑袋抽筋想了想浏览器是怎么执行click事件的,为什么我们可以用e或者window.event这个对象获取一些事件的属性呐?以下是我的理解.如果您有更好的理解,欢迎评论!! ...

  2. jdk1.7.0_80源码包结构

    解压源码src.zip,jdk源码结构如下所示: src |--com.sun    sun公司对jdk的实现,Oracle官方不支持,不保证跨平台(可能一些类linux有而windows没有),甚至 ...

  3. HTML5入门以及新标签

    HTML5 学习总结(一)--HTML5入门与新增标签   目录 一.HTML5概要 1.1.为什么需要HTML5 1.2.什么是HTML5 1.3.HTML5现状及浏览器支持 1.4.HTML5特性 ...

  4. 【Win10 开发】读取PDF文档

    关于用来读取PDF文档的内容的API,其实在Win8.1的时候就有,不过没关系,既咱们讨论的是10的UAP,连同8.1的内容也包括进去,所以老周无数次强调:把以前的内容学好了,就可以在不学习任何新知识 ...

  5. Linq 知识回顾

    开篇语 在说LINQ之前必须先说说几个重要的C#语言特性 与LINQ有关的语言特性 隐式类型 (1)源起 在隐式类型出现之前, 我们在声明一个变量的时候, 总是要为一个变量指定他的类型 甚至在fore ...

  6. 页面loading效果

    当网页太大,打开太慢的时候,为了增加良好的用户体验(不让用户眼巴巴的等,心中暗骂c,这么慢),我们需要加一个等待动画. 只需把以下代码加入页面中即可,图片可以根据自己的需求更换,更换图片之后需要改变l ...

  7. 深入理解DOM事件机制系列第一篇——事件流

    × 目录 [1]历史 [2]事件冒泡 [3]事件捕获[4]事件流 前面的话 javascript操作CSS称为脚本化CSS,而javascript与HTML的交互是通过事件实现的.事件就是文档或浏览器 ...

  8. c++与java中子类中调用父类成员的方法

    java中: import java.util.Scanner; public class ClassTest{ public static void main(String args[]){ chi ...

  9. 窥探Swift之字符串(String)

    之前总结过Objective-C中的字符串<Objective-C精选字符串处理方法>,学习一门新语言怎么能少的了字符串呢.Swift中的String和Objective-C语言中NSSt ...

  10. 前端编码风格规范之 JavaScript 规范

    JavaScript 规范 全局命名空间污染与 IIFE 总是将代码包裹成一个 IIFE(Immediately-Invoked Function Expression),用以创建独立隔绝的定义域.这 ...