1 Java IO流的概念,分类

1.1 Java IO流的概念

java的IO是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

1.2.1按照流的流向分,可以分为输入流和输出流。

输入流:只能从中读入数据,而不能向其写出数据。

输出流:只能向其写出数据,而不能向其读入数据。

注:为什么叫写出和读入,这里涉及到一个方向问题,因为划分输入/输出流时是从程序运行所在的内存的角度来考虑的;将内存中的数据写出到硬盘中的文件,或者是将硬盘中文件的信息读入到内存。

注:java的输入流主要是InputStream和Reader作为基类,而输出流则是主要由OutputStream和Writer作为基类。它们都是一些抽象基类,无法直接创建实例。

1.2.2 按照操作单元划分,可以划分为字节流和字符流。

字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。

字节流和字符流的区别:

读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。

处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

字节流:一次读入或读出是8位二进制。

字符流:一次读入或读出是16位二进制。

1.2.3 按照流的角色划分为节点流和处理流。

节点流(低级流):直接与数据源相连,读入或写出。直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。

处理流(高级流)和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

1.3.1 流的原理浅析:

java IO流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java IO流的40多个类都是从如下4个抽象类基类中派生出来的。

InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。

OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

对于InputStream和Reader而言,它们把输入设备抽象成为一个”水管“,这个水管的每个“水滴”依次排列。

字节流和字符流的处理方式其实很相似,只是它们处理的输入/输出单位不同而已。输入流使用隐式的记录指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InputStream或者Reader里面取出一个或者多个“水滴”后,记录指针自定向后移动;除此之外,InputStream和Reader里面都提供了一些方法来控制记录指针的移动。

对于OutputStream和Writer而言,它们同样把输出设备抽象成一个”水管“,只是这个水管里面没有任何水滴。

当执行输出时,程序相当于依次把“水滴”放入到输出流的水管中,输出流同样采用隐示指针来标识当前水滴即将放入的位置,每当程序向 OutputStream 或者 Writer 里面输出一个或者多个水滴后,记录指针自动向后移动。

注:Java的处理流模型则体现了Java输入和输出流设计的灵活性。处理流的功能主要体现在以下两个方面。

性能的提高:主要以增加缓冲的方式来提供输入和输出的效率。

操作的便捷:处理流可能提供了一系列便捷的方法来一次输入和输出大批量的内容,而不是输入/输出一个或者多个“水滴”。

1.3.2 java输入/输出流体系中常用的流的分类表

分类

字节输入流

字节输出流

字符输入流

字符输出流

抽象基类

InputStream

OutputStream

Reader

Writer

访问文件

FileInputStream

FileOutputStream

FileReader

FileWriter

访问数组

ByteArrayInputStream

ByteArrayOutputStream

CharArrayReader

CharArrayWriter

访问管道

PipedInputStream

PipedOutputStream

PipedReader

PipedWriter

访问字符串

StringReader

StringWriter

缓冲流

BufferedInputStream

BufferedOutputStream

BufferedReader

BufferedWriter

转换流

InputStreamReader

OutputStreamWriter

对象流

ObjectInputStream

ObjectInputStream

抽象基类

FilterInputStream

FilterOutputStream

FilterReader

FilterWriter

打印流

PrintStream

PrintWriter

推回输入流

PushbackInputStream

PushbackReader

特殊流

DataInputStream

DataOutputStreamn

注:表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联:斜体字标出的类代表抽象基类,无法直接创建实例。

2.1 IO体系的基类(InputStream/Reader,OutputStream/Writer)

字节流和字符流的操作方式基本一致,只是操作的数据单元不同——字节流的操作单元是字节,字符流的操作单元是字符。

InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行输入,但它们将成为所有输入流的模板,所以它们的方法是所有输入流都可使用的方法。

在InputStream里面包含如下3个方法。

int read(); 从输入流中读取单个字节(相当于从水管中取出一滴水),返回所读取的字节数据(字节数据可直接转换为int类型)。

int read(byte[] b)从输入流中最多读取b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数。

int read(byte[] b,int off,int len); 从输入流中最多读取len个字节的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字节数。

在Reader中包含如下3个方法。

int read(); 从输入流中读取单个字符(相当于从水管中取出一滴水),返回所读取的字符数据(字节数据可直接转换为int类型)。

int read(char[] b)从输入流中最多读取b.length个字符的数据,并将其存储在字节数组b中,返回实际读取的字符数。

int read(char[] b,int off,int len); 从输入流中最多读取len个字符的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数。

InputStream和Reader提供的一些移动指针的方法:

void mark(int readAheadLimit); 在记录指针当前位置记录一个标记(mark)。

boolean markSupported(); 判断此输入流是否支持mark()操作,即是否支持记录标记。

void reset(); 将此流的记录指针重新定位到上一次记录标记(mark)的位置。

long skip(long n); 记录指针向前移动n个字节/字符。

OutputStream和Writer的用法也非常相似,两个流都提供了如下三个方法:

void write(int c); 将指定的字节/字符输出到输出流中,其中c即可以代表字节,也可以代表字符。

void write(byte[]/char[] buf); 将字节数组/字符数组中的数据输出到指定输出流中。

void write(byte[]/char[] buf, int off,int len ); 将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中。

因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里面还包含如下两个方法。

void write(String str); 将str字符串里包含的字符输出到指定输出流中。

void write (String str, int off, int len); 将str字符串里面从off位置开始,长度为len的字符输出到指定输出流中。

2.2 IO体系的基类文件流的使用(FileInputStream/FileReader ,FileOutputStream/FileWriter)

使用FileInputStream读取文件:

@Test

public void fileInputStreamTest() throws IOException {

FileInputStream fis = null;

try {

// 创建字节输入流

fis = new FileInputStream(

"D:/test/test.txt");

// 创建一个长度为1024的竹筒

byte[] b = new byte[1024];

// 用于保存的实际字节数

int hasRead = 0;

// 使用循环来重复取水的过程

while ((hasRead = fis.read(b)) > 0) {

// 取出竹筒中的水滴(字节),将字节数组转换成字符串进行输出, 后面的gbk是为了处理中文乱码问题

System.out.print(new String(b, 0, hasRead, "gbk"));

}

} catch (IOException e) {

e.printStackTrace();

} finally {

fis.close();

}

}

注:上面程序最后使用了fis.close()来关闭该文件的输入流,与JDBC编程一样,程序里面打开的文件IO资源不属于内存的资源,垃圾回收机制无法回收该资源,所以应该显示的关闭打开的IO资源。Java 7改写了所有的IO资源类,它们都实现了AntoCloseable接口,因此都可以通过自动关闭资源的try语句来关闭这些IO流。

使用FileReader读取文件:

@Test

public void fileReaderTest() throws IOException{

FileReader fis = null;

try {

// 创建字节输入流

fis = new FileReader("D:/test/test.txt");

// 创建一个长度为1024的竹筒

char[] b = new char[1024];

// 用于保存的实际字节数

int hasRead = 0;

// 使用循环来重复取水的过程

while ((hasRead = fis.read(b)) > 0) {

// 取出竹筒中的水滴(字节),将字节数组转换成字符串进行输出

System.out.print(new String(b, 0, hasRead));

}

} catch (IOException e) {

e.printStackTrace();

} finally {

fis.close();

}

}

可以看出使用FileInputStream和FileReader进行文件的读写并没有什么区别,只是操作单元不同而且。

FileOutputStream的用法:

@Test

public void fileOutputStreamTest() throws IOException {

FileInputStream fis = null;

FileOutputStream fos = null;

try {

// 创建字节输入流

fis = new FileInputStream("D:/test/test.txt");

// 创建字节输出流

fos = new FileOutputStream("D:/test/test2.txt");

byte[] b = new byte[1024];

int hasRead = 0;

// 循环从输入流中取出数据

while ((hasRead = fis.read(b)) > 0) {

// 每读取一次,即写入文件输入流,读了多少,就写多少。

fos.write(b, 0, hasRead);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

fis.close();

fos.close();

}

}

注: 使用java的IO流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,可能还可以将输出流缓冲区中的数据flush到物理节点中里(因为在执行close()方法之前,自动执行输出流的flush()方法)。java很多输出流默认都提供了缓存功能,其实我们没有必要刻意去记忆哪些流有缓存功能,哪些流没有,只有正常关闭所有的输出流即可保证程序正常。

4.缓冲流的使用(BufferedInputStream/BufferedReader, BufferedOutputStream/BufferedWriter):

字节缓冲流

@Test

public void bufferedTest() throws IOException {

FileInputStream fis = null;

FileOutputStream fos = null;

BufferedInputStream bis = null;

BufferedOutputStream bos = null;

try {

// 创建字节输入流

fis = new FileInputStream("D:/test/test.txt");

// 创建字节输出流

fos = new FileOutputStream("D:/test/test3.txt");

// 创建字节缓存输入流

bis = new BufferedInputStream(fis);

// 创建字节缓存输出流

bos = new BufferedOutputStream(fos);

byte[] b = new byte[1024];

int hasRead = 0;

// 循环从缓存流中读取数据

while ((hasRead = bis.read(b)) > 0) {

// 向缓存流中写入数据,读取多少写入多少

bos.write(b, 0, hasRead);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

bis.close();

bos.close();

}

}

可以看到使用字节缓存流读取和写入数据的方式和文件流(FileInputStream,FileOutputStream)并没有什么不同,只是把处理流套接到文件流上进行读写。

上面代码中我们使用了缓存流和文件流,但是我们只关闭了缓存流。这个需要注意一下,当我们使用处理流套接到节点流上的使用的时候,只需要关闭最上层的处理就可以了。java会自动帮我们关闭下层的节点流。

2.3 转换流的使用(InputStreamReader/OutputStreamWriter):

下面以获取键盘输入为例来介绍转换流的用法。java使用System.in代表输入。即键盘输入,但这个标准输入流是InputStream类的实例,使用不太方便,而且键盘输入内容都是文本内容,所以可以使用InputStreamReader将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容,如下代码所示:

转换流的使用

@Test

public void changeStreamTest(){

try {

// 将System.in对象转化为Reader对象

InputStreamReader reader=new InputStreamReader(System.in);

//将普通的Reader包装成BufferedReader

BufferedReader bufferedReader=new BufferedReader(reader);

String buffer=null;

while ((buffer=bufferedReader.readLine())!=null){

// 如果读取到的字符串为“exit”,则程序退出

if(buffer.equals("exit")){

System.exit(1);

}

//打印读取的内容

System.out.print("输入内容:"+buffer);

}

}catch (IOException e){

e.printStackTrace();

}finally {

}

}

上面程序将System.in包装成BufferedReader,BufferedReader流具有缓存功能,它可以一次读取一行文本——以换行符为标志,如果它没有读到换行符,则程序堵塞。等到读到换行符为止。运行上面程序可以发现这个特征,当我们在控制台执行输入时,只有按下回车键,程序才会打印出刚刚输入的内容。

2.4 对象流的使用(ObjectInputStream/ObjectOutputStream)的使用:

对象类:

package com.design.prompt;

import java.io.Serializable;

public class UserTest implements Serializable {

/**

*

*/

private static final long serialVersionUID = 6522988290199824802L;

private String username;

transient String password;

public UserTest(){

}

public UserTest(String username, String password) {

super();

this.username = username;

this.password = password;

}

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

@Override

public String toString() {

return "UserTest [username=" + username + ", password=" + password

+ "]";

}

}

写出对象:

@Test

public void objectWriterTest() {

OutputStream outputStream = null;

BufferedOutputStream buf = null;

ObjectOutputStream obj = null;

try {

// 序列化文件輸出流

outputStream = new FileOutputStream("D:/test/test.txt");

// 构建缓冲流

buf = new BufferedOutputStream(outputStream);

// 构建字符输出的对象流

obj = new ObjectOutputStream(buf);

// 序列化数据写入

obj.writeObject(new UserTest("A", "123"));// Person对象

// 关闭流

obj.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

读入对象

@Test

public void objectReaderTest() {

try {

InputStream inputStream = new FileInputStream("D:/test/test.txt");

// 构建缓冲流

BufferedInputStream buf = new BufferedInputStream(inputStream);

// 构建字符输入的对象流

ObjectInputStream obj = new ObjectInputStream(buf);

UserTest tempPerson = (UserTest) obj.readObject();

System.out.println("UserTest对象为:" + tempPerson);

// 关闭流

obj.close();

buf.close();

inputStream.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

注:使用对象流的一些注意事项

1.读取顺序和写入顺序一定要一致,不然会读取出错。

2.在对象属性前面加transient关键字,则该对象的属性不会被序列化。

java按照io流向基类_Java IO详解相关推荐

  1. java的数组与Arrays类源码详解

    java的数组与Arrays类源码详解 java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用. 类的 ...

  2. java体系的四大基类_Java中的io流学习(了解四大基类和基本步骤)

    Java中io流四大基类及io流操作四大基本步骤 io流:(input/output)即输入输出流.面向对象的思想之一是面向接口编程,面向父类编程,也就是多态.所以学好基类(父类)很重要. 分类 按处 ...

  3. Java接口、基类、抽象类详解(图解接口、继承和实现、基类和抽象类的区别等)——Java基础系列

    文章目录 前言 总览(必看) 一.接口是"契约" 二.接口实例--Servlet接口 三.抽象标识符abstract和抽象类 四.基类作为"辅助" 求支持 前言 ...

  4. c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解

    文章目录 静态多态.动态多态 虚函数 哪些函数类型不可以被定义成虚函数? 虚函数的访问方式 析构函数中的虚函数 虚函数表指针 vptr 多继承下的虚函数表 虚基类表指针 bptr 纯虚函数 抽象类 虚 ...

  5. java多线程生产者与消费者问题_Java多线程详解之四:生产者消费者问题

    一.问题描述 生产者消费者问题(Producer-Consumer problem),也称有限缓冲区问题(Bounded-buffer promblem),是一个多线程同步问题的经典案例.对于一个固定 ...

  6. Java学习笔记之Pattern类的用法详解(正则表达式)

    转自:https://www.cnblogs.com/sparkbj/articles/6207103.html EG: //验证邮箱 public static final String REGEX ...

  7. Java知识点总结(JavaIO- System类对IO的支持与Scanner类 )

    Java知识点总结(JavaIO- System类对IO的支持与Scanner类 ) @(Java知识点总结)[Java, JavaIO] [toc] System类 public class Dem ...

  8. 09、IO流—File类与IO流

    文章目录 一.File类 基本认识 实用方法 获取功能 重命名功能(包含剪切) 判断功能 创建.删除文件 实际小案例 二.IO流 1.认识IO流 2.IO流基类介绍 字节流基类介绍 字符流基类介绍 三 ...

  9. java 重启线程_java 可重启线程及线程池类的设计(详解)

    了解JAVA多线程编程的人都知道,要产生一个线程有两种方法,一是类直接继承Thread类并实现其run()方法:二是类实现Runnable接口并实现其run()方法,然后新建一个以该类为构造方法参数的 ...

最新文章

  1. oracle列字符可以增加长度,ORACLE字符列长度语义
  2. thinkcmf 去掉index.php,​ThinkCMF5.0如何修改入口文件 解决方法
  3. 11、计算机图形学——几何(贝塞尔曲线与曲面)
  4. 用VC++实现一个文本文件阅读器
  5. Colossal Fibonacci Numbers! UVA - 11582(斐波那契求模)+快速幂+周期规律
  6. laravel 控制器中使用中间件_在 Laravel 中使用 Slack 进行异常通知
  7. ORACLE 10g EXPDP,IMPDP使用方法
  8. 新版知识付费系统付费阅读小程序源码知识付费平台
  9. 联想服务器改win7系统教程,联想IdeaCentre720改win7系统教程及BIOS设置方法
  10. 宛如造句,小学生怎么用宛如造句?
  11. 谷歌开源了量子算法框架CIRQ,拥抱NISQ新时代
  12. 来感受一下别人的密码
  13. 不用PLC编程,实现USB或串口条码枪对接PLC,数据直接写入寄存器
  14. 【渝粤题库】陕西师范大学202131组织行为学作业(高起本、专升本)
  15. 可以这样理解 TIM_INIT(arr,psc)重装载值和分频值
  16. 和尚挑水 java_用do...while语句编写程序t18_2.java
  17. 预产期计算器在线计算生男生女计算机,预产期计算器生男生女在线查询-预产期计算器及天数计算时间软件2017版-腾牛安卓网...
  18. 西北乱跑娃 --- python正则匹配中文以及数字和标点
  19. python生成等值线_在python中生成X,Y数据的等值线图
  20. UCOS/UCOSII基础知识

热门文章

  1. python eval函数格式_Python函数中eval函数知识点
  2. python脚本转换成apk_apktool反编译apk并回编译
  3. mysql授予权限和撤销权限的关系_MySQL数据库常用的授予权限和撤销权限的命令讲解...
  4. linux vim复制和粘贴
  5. MATLAB(四)在高等数学中的应用
  6. 如何使用UR机器人模拟软件URsim
  7. 数字图像处理-1.图像获取
  8. python3 中的 eval 函数
  9. Java加密与解密的艺术~AES-GCM-NoPadding实现
  10. Java并发编程实战~CountDownLatch