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. springboot学习笔记(一)

    一.什么是SpringBoot 描述:Spring Boot是Spring社区发布的一个开源项目,旨在帮助开发者快速并且更简单的构建项目.大多数SpringBoot项目只需要很少的配置文件.二.Spr ...

  2. 在微软伪静态处理机制下action导致伪静态的地址重现的问题

    伪静态前的地址:/sc/ProductList.aspx?pClass=0&descType=2&minPrice=1&maxPrice=11 伪静态后的地址:/product ...

  3. Evaluate Math Expression

    Evaluate Math Expression eryar@163.com 摘要Abstract:本文简要介绍了数学表达式解析求值的几款开源软件,并结合程序代码说明了OpenCascade中表达式包 ...

  4. ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】

    到目前为止,我们定义的ServiceProvider已经实现了基本的服务提供和回收功能,但是依然漏掉了一些必需的细节特性.这些特性包括如何针对IServiceProvider接口提供一个Service ...

  5. Difference Between HashMap and IdentityHashMap--转

    原文地址:https://dzone.com/articles/difference-between-hashmap-and Most of the time I use HashMap whenev ...

  6. swift2.0 如何隐藏和设置状态栏

    1.在ViewController中操作当前ViewController的状态栏/** 隐藏状态栏 */ override func prefersStatusBarHidden() -> Bo ...

  7. ZOJ Problem Set - 1006 Do the Untwist

    今天在ZOJ上做了道很简单的题目是关于加密解密问题的,此题的关键点就在于求余的逆运算: 比如假设都是正整数 A=(B-C)%D 则 B - C = D*n + A 其中 A < D 移项 B = ...

  8. ASP.NET通过递归添加树(Treeview)

    先来看看效果,基本上就是这样的. 所谓树,无非就是2点,第一个:根节点,第二:叶子节点,其中叶子节点中还可能有叶子节点,但是根节点始终只有一个. 下面贴上 各部分的代码 1.PAGE_LOAD载入事件 ...

  9. 【集合框架】JDK1.8源码分析之IdentityHashMap(四)

    一.前言 前面已经分析了HashMap与LinkedHashMap,现在我们来分析不太常用的IdentityHashMap,从它的名字上也可以看出来用于表示唯一的HashMap,仔细分析了其源码,发现 ...

  10. Mybatis 入门 -- 最简单的引入和使用

    参考:http://www.mybatis.org/mybatis-3/zh/getting-started.html 从今天开始学习官方文档. 1.项目搭建 项目结构: 首先,搭建一个maven项目 ...