I/O 指的是 input 和 output ,也就是输入和输出,我们说的是 Java 中的 I/O,那我们就在站在虚拟机的角度去看看有哪些输入和输出。输入又可以称为数据源端,能想到的会有,文件,网络,控制台手动输入。而输出又可以称为数据接收端,能想到依旧还是那几个,输出到文件,网络,控制台。

那好,目前只是理清楚了数据从哪里来到哪里去,然而,我们的数据交互肯定不是这么的简单,我们还需要考虑数据传输的多种方式,我是以字符传输还是字节传输,或是二进制传输,要不要缓冲存取,等等问题。这样一来,想要表示出数据的传输可想而知肯定会需要很多对象。

为了解决上述存在的多种多样的数据端和数据交互方式,Java 设计者们以避免设计过多的类为初衷(其实类并不少...)设计了 I/O 体系。

先来放整体图,这个图简易却不简单,今天我也只是说其中的一小部分东西,好多的实现类都没有拿出来单独说。

首先来看看 File 类,File 类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。看清楚了,File 类虽然名字看起来像是指文件,实际上并非如此,它既能代表一个特定文件的名称,又能代表一个目录下一组文件的名称。

我们来看个例子感受一下 File 类的使用。

 public static void main(String args[]) {
String dirname = ".";
File f1 = new File(dirname); //当前项目工作目录
if (f1.isDirectory()) {
System.out.println("Directory of " + dirname);
String s[] = f1.list();
for (int i = 0; i < s.length; i++) {
File f = new File(dirname + "/" + s[i]);
if (f.isDirectory()) {
System.out.println(s[i] + " is a directory");
} else {
System.out.println(s[i] + " is a file");
}
}
} else {
System.out.println(dirname + " is not a directory");
}
}

那我想要向文件中读取或是写入内容怎么办呢 ?那就需要借助输入输出流来完成了。

首先说一下流的概念,流代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象。划重点,流代表的是对象。这个对象有发送或接收数据的能力。所以说流的本质也就是将数据源(数据源端,数据接受端)和数据的传输方式(字符,字节,二进制等)抽象成类的结果。作用就是为了传输数据。

在 I/O 体系中,因为需要的流有太多,Java 设计者又避免设计过多的类,所以最终采用装饰者模式来对整个流结构进行设计,按功能划分 Stream,还可以动态装配这些 Stream,以便获得需要的流。假如你想要获得一个具有缓冲的文件输入字节流,这样即可。

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream; public class IOTest3 { public static void main(String[] args) throws Exception {
InputStream fis = new FileInputStream("test.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
}
}

流分类:

  1. 字节流。InputStream 是所有字节输入流的基类,而 OutputStream 是所有字节输出流的基类。

  2. 字符流。Reader 是所有读取字符串输入流的基类,而 Writer 是所有输出字符串的基类。

另外 InputStream,OutputStream,Reader,Writer 都是抽象类。

字节流是最基本的,所有的 InputStream 和 OutputStream 的子类都是字节流,主要用来处理二进制数据,它是按字节来处理的,但实际中很多的数据是文本,所以又提出了字符流的概念,它是按虚拟机的 Encode 来处理,也就是要按照字符集将字节转化为字符。Java 中默认的编码是 Unicode 编码。

字节流和字符流通过 InputStreamReader,OutputStreamWriter 来关联,实际上是通过 byte[ ] 和 String 来关联。在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。在从字节流转化为字符流时,实际上就是 byte[ ] 转化为 String

byte[] bytes = new byte[10];
String charsetName = "UTF-8"
String s1 = new String(bytes, charsetName);

有一个关键的参数字符集编码,通常我们都省略了,而在字符流转化为字节流时,实际上是 String 转化为 byte[ ]

String s = "你好";
byte[] bytes = s.getBytes();

至于其他的流,主要是为了提高性能和使用方便,如:

// 字节流相关
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream // 字符流相关
FileReader
FilterWriter
BufferedReader
BufferedWriter

字节流和字符流的区别,除了类名称的区别,还有就是字符流会使用缓冲区,而字节流没有使用缓冲区。

缓冲区可以简单地理解为一段特殊的内存。某些情况下,如果一个程序频繁地操作一个资源(如文件或数据库),则性能会很低,此时为了提升性能,就可以将一部分数据暂时读入到内存的一块区域之中,以后直接从此区域中读取数据即可,因为读取内存速度会比较快,这样可以提升程序的性能。

在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用缓冲区暂存数据。如果想在不关闭时也可以将字符流的内容全部输出,则可以使用 Writer 类中的 flush() 方法。

字节流和字符流的选择

Reader 类的 read() 方法返回类型为 int,面向的是字符(占两个字节共 16 位),范围在 0 到 65535 之间 ( 0x00 - 0xffff ),如果已到达流的末尾,则返回 -1。

InputStream 的 read() 方法虽然也返回 int,但由于此类是面向字节流的,一个字节占 8 位,所以返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。因此对于不能用 0 - 255 来表示的值就得用字符流来读取,比如说汉字。

字符( Reader 和 Writer ):中文,字符是只有在内存中才会形成的,操作字符,字符数组或字符串。

字节( InputStream 和 OutputStream ):音频文件,图片,歌曲,所有的硬盘上保存文件或进行传输的时候,操作字节和字节数组或二进制对象。

最后来看一个例子,实现拷贝功能。代码只是为了演示操作,合理的写法不该这样。

public static void main(String[] args) throws Exception {
File inFile = new File("D:\\input.txt");
File outFile = new File("D:\\output.txt"); FileInputStream inputStream = new FileInputStream(inFile);
FileOutputStream outputStream = new FileOutputStream(outFile); byte[] content = new byte[1024];
int len;
while ((len = inputStream.read(content)) != -1) {
outputStream.write(content, 0, len);
}
outputStream.flush();
outputStream.close();
inputStream.close();
}

Java 中的 I/O的更多相关文章

  1. java中的锁

    java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...

  2. java中的字符串相关知识整理

    字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...

  3. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

  4. java中Action层、Service层和Dao层的功能区分

    Action/Service/DAO简介: Action是管理业务(Service)调度和管理跳转的. Service是管理具体的功能的. Action只负责管理,而Service负责实施. DAO只 ...

  5. Java中常用集合操作

    一.Map 名值对存储的. 常用派生类HashMap类 添加: put(key,value)往集合里添加数据 删除: clear()删除所有 remove(key)清除单个,根据k来找 获取: siz ...

  6. java中的移位运算符:<<,>>,>>>总结

    java中有三种移位运算符 <<      :     左移运算符,num << 1,相当于num乘以2 >>      :     右移运算符,num >& ...

  7. 关于Java中进程和线程的详解

    一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...

  8. Java中的进程和线程

     Java中的进程与线程 一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是 ...

  9. Java中的进程与线程(总结篇)

    详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...

  10. 初探java中this的用法

    一般this在各类语言中都表示“调用当前函数的对象”,java中也存在这种用法: public class Leaf { int i = 0; Leaf increment(){ i++; retur ...

随机推荐

  1. BZOJ第一页刷题计划

    BZOJ第一页刷题计划 已完成:67 / 90 [BZOJ1000]A+B Problem:A+B: [BZOJ1001][BeiJing2006]狼抓兔子:最小割: [BZOJ1002][FJOI2 ...

  2. caffe的特殊层

    每次写博客都带有一定的目的,在我看来这是一个记录的过程,所以尽量按照循序渐进的顺序逐步写,前面介绍的CNN层应该是非常常用的,这篇博客介绍一下某些特殊的layer,但是由于特殊的layer都带有一定的 ...

  3. 【leetcode 简单】 第八十九题 赎金信

    给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成.如果可以构成,返回 true :否则返回 ...

  4. xampp + windows 配置 memcache流程

    1.运行xampp服务,打开phpinfo查看信息,如下图: 从上到下,依次为 PHP版本:5.6: architecture:x86: TS:线程安全: vc11 2.牢记以上几个横线内容之后去下载 ...

  5. pywin32记录备忘

    项目地址:http://sourceforge.net/projects/pywin32/ 文档地址:http://docs.activestate.com/activepython/2.7/pywi ...

  6. KDE下安装fcitx后终端不能输入中文

    编辑用户的  ~/.profile 文件(或/etc/profile): #fcitx export XIM_PROGRAM=fcitx export XIM=fcitx export GTK_IM_ ...

  7. shell脚本实现监控shell脚本的执行流程及变量的值

    这篇文章主要介绍了shell脚本实现监控shell脚本的执行流程及变量的值本文使用shell完成对执行过程中条件语句中的变量的变化的监控和整个程序的执行流程的观察功能,需要的朋友可以参考下 很多时候, ...

  8. mysql高可用架构 -> MHA主从复制-03

    GTID复制技术说明 GTID的全称为 global transaction identifier ,可以翻译为全局事务标示符,GTID在原始master上的事务提交时被创建.GTID需要在全局的主- ...

  9. linux创建新用户

    服务器只用root账号会有风险,最好是每个人使用一个账号. 1. 添加用户名 adduser linuxidc 2. 给这个用户名设置新密码 passwd linuxidc 3.授权 个人用户的权限只 ...

  10. centos 6.x 部署uwsgi+flask项目

    一.项目背景 1. 公司需求要做一个在线统计页面; 2. 统计在线人数,进行人数数据展示; 3. 类似QQ官网在线人数 二.测试环境 [root@linux-node2 ~]# cat /etc/*r ...