Java中的IO操作和缓冲区
Java中的IO操作和缓冲区
一、简述
Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
根据数据的单位划分: 字节流和字符流。
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。
本文主要介绍Java中的字符流和字节流,以及缓冲区的区别
二、IO流的介绍
什么是流
Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列。和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序列的对象被称为输入流;能够向其写入一个字节序列的对象被称为输出流。
输入输出流的作用范围
三、Java中的字节流和字符流
字节流
Java中的字节流处理的最基本单位为单个字节,它通常用来处理二进制数据。Java中最基本的两个字节流类是InputStream和OutputStream,它们分别代表了组基本的输入字节流和输出字节流。InputStream类与OutputStream类均为抽象类,我们在实际使用中通常使用Java类库中提供的它们的一系列子类。
字符流
Java中的字符流处理的最基本的单元是Unicode码元(大小统一为2字节),它通常用来处理文本数据。所谓Unicode码元,也就是一个Unicode代码单元,范围是0x0000~0xFFFF。在以上范围内的每个数字都与一个字符相对应,Java中的String类型默认就把字符以Unicode规则编码而后存储在内存中。然而与存储在内存中不同,存储在磁盘上的数据通常有着各种各样的编码方式。使用不同的编码方式,相同的字符会有不同的二进制表示。
Java中最基本的两个字符流类是Reader和Writer。
二者的联系
Java提供了以下两种IO类型的相互转化
InputStreamReader(字节到字符)和 OutputStreamReader(字符到字节):
1.InputStreamReader
从字节流到字符流的桥梁:它读入字节,并根据指定的编码方式,将之转换为字符流。
使用的编码方式可能由名称指定,或平台可接受的缺省编码方式
InputStreamReader的read()方法之一的每次调用,可能促使从基本字节输入流中读取一个或多个字节。
2.OutputStreamWriter
将多个字符写入到一个输出流,根据指定的字符编码将多个字符转换为字节
每个 OutputStreamWriter 合并它自己的 CharToByteConverter, 因而是从字符流到字节流的桥梁。
字符流=字节流+Unicode编码
字节流变为字符流=基本单位从一个字节变为两个字节(Unicode编码)
字节流和字符流的区别
读取单位不同
- 字节流:用于读取一个一个的数据字节(8位),每8位当成一个单元
- 字符流:用于读取一个一个的数据字符(16位),每16位当成一个单元
执行效率不同
从执行效率来说,字符流相对于字节流的速度是要快的。因为字符流每次处理是可以处理一个缓冲区的,而字节只能一个一个字节的处理。
使用对象不同
1. 字节流通常用于处理二进制数据,实际上它可以**处理任意类型**的数据,但它不支持直接写入或读取Unicode码元;
2. 字符流通常处理文本数据,它支持写入及读取**Unicode码元**。
缓冲区的使用
- 字节流默认不使用缓冲区;
- 字符流使用缓冲区。
四、效率测试
代码测试
输入测试
package IOOperation;
import java.io.*;
/**
* @Description 输入流的测试
* @Author 林泽鸿
* @Date 2020/5/31 11:27
*/
public class InputTest {
/**
* 用于输入的对象,大小为2.58MB
*/
public static final File inputFile = new File("E:\\RDC\\学习资料\\2.pdf");
// 字节流
public void bytes() {
try {
System.out.println("1.字节流输入");
// 新建文件命名
// 创建输入输出流对象
long s1 = System.currentTimeMillis();// 测试开始,计时
FileInputStream fileInputStream = new FileInputStream(inputFile);
// 读写数据
// 读写数据
while (fileInputStream.read() != -1) {
}
fileInputStream.close();
long s2 = System.currentTimeMillis();// 测试结束,计时
System.out.println("输入文件耗时:" + (s2 - s1) + "ms");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//字符流
public void chars() {
try {
System.out.println("2.字符流输入");
// 新建文件命名
long s1 = System.currentTimeMillis();// 测试开始,计时
// 创建输入输出流对象
FileReader fileReader = new FileReader(inputFile);
// 读写数据
while (fileReader.read() != -1) {
}
fileReader.close();
long s2 = System.currentTimeMillis();// 测试结束,计时
System.out.println("输入文件耗时:" + (s2 - s1) + "ms");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 字节缓冲流
public void bytesBuffer() {
try {
System.out.println("3.字节缓冲流输入");
// 新建文件命名
long s1 = System.currentTimeMillis();// 测试开始,计时
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(inputFile));
// 创建输入输出流对象
// 读写数据
while (bufferedInputStream.read() != -1) {
}
bufferedInputStream.close();
long s2 = System.currentTimeMillis();// 测试结束,计时
System.out.println("输入文件耗时:" + (s2 - s1) + "ms");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 字符缓冲流
public void charsBuffer() {
try {
System.out.println("4.字符缓冲流输入");
// 新建文件命名
long s1 = System.currentTimeMillis();// 测试开始,计时
// 创建输入输出流对象
BufferedReader bufferedReader = new BufferedReader(new FileReader(inputFile));
// 读写数据
while (bufferedReader.read() != -1) {
}
bufferedReader.close();
long s2 = System.currentTimeMillis();// 测试结束,计时
System.out.println("输入文件耗时:" + (s2 - s1) + "ms");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出测试
package IOOperation;
import java.io.*;
/**
* @Description 输出流的测试
* @Author 林泽鸿
* @Date 2020/5/31 11:28
*/
public class OutPutTest {
public static final String PATH =
"E:\\codingEnvironment\\GitRepository\\JAVA\\src\\fileCatalog\\";
int count = 1;
/**
* 用于输出的对象
*/
public static byte[] outputBytes = null;
public static char[] outputChars = null;
public static void before() {
StringBuilder stringBuilder = new StringBuilder("");
for (int i = 0; i < 300000; i++) {
stringBuilder.append("testtesttesttesttesttest");
}
outputBytes = stringBuilder.toString().getBytes();
outputChars = stringBuilder.toString().toCharArray();
}
// 字节流
public void bytes() {
try {
System.out.println("1.字节流输出");
// 新建文件命名
String name = PATH + "字节流输出文件.txt";
File file = new File(name);
// 创建输入输出流对象
FileOutputStream fos = new FileOutputStream(file);
// 读写数据
long s1 = System.currentTimeMillis();// 测试开始,计时
writeBytes(fos);
long s2 = System.currentTimeMillis();// 测试结束,计时
fos.close();
System.out.println("输出文件耗时:" + (s2 - s1) + "ms");
System.out.println("文件大小:" + file.length() / 1024 + "KB");
file.delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 字节流
public void chars() {
try {
System.out.println("2.字符流输出");
// 新建文件命名
String name = PATH + "字符流输出文件.txt";
File file = new File(name);
// 创建输入输出流对象
FileWriter fileWriter = new FileWriter(file);
// 读写数据
long s1 = System.currentTimeMillis();// 测试开始,计时
writeChars(fileWriter);
long s2 = System.currentTimeMillis();// 测试结束,计时
fileWriter.close();
System.out.println("输出文件耗时:" + (s2 - s1) + "ms");
System.out.println("文件大小:" + file.length() / 1024 + "KB");
file.delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 字节缓冲流
public void bytesBuffer() {
try {
System.out.println("3.字节缓冲流输出");
// 新建文件命名
String name = PATH + "字节缓冲流输出文件.txt";
File file = new File(name);
// 创建输入输出流对象
FileOutputStream fileOutputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
// 读写数据
long s1 = System.currentTimeMillis();// 测试开始,计时
writeBytes(bufferedOutputStream);
long s2 = System.currentTimeMillis();// 测试结束,计时
bufferedOutputStream.close();
System.out.println("输出文件耗时:" + (s2 - s1) + "ms");
System.out.println("文件大小:" + file.length() / 1024 + "KB");
file.delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 字符缓冲流
public void charsBuffer() {
try {
System.out.println("4.字符缓冲流输出");
// 新建文件命名
String name = PATH + "字符缓冲流输出文件.txt";
File file = new File(name);
// 创建输入输出流对象
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file));
// 读写数据
long s1 = System.currentTimeMillis();// 测试开始,计时
for (int i = 0; i < count; i++) {
bufferedWriter.write(outputChars);
}
long s2 = System.currentTimeMillis();// 测试结束,计时
bufferedWriter.close();
System.out.println("输出文件耗时:" + (s2 - s1) + "ms");
System.out.println("文件大小:" + file.length() / 1024 + "KB");
file.delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 字节输出
*/
private void writeBytes(OutputStream fos) throws IOException {
for (int i = 0; i < count; i++) {
for (int j = 0; j < outputBytes.length; j++) {
fos.write(outputBytes[j]);
}
}
}
/**
* 字符输出
*/
private void writeChars(Writer writer) throws IOException {
for (int i = 0; i < count; i++) {
for (int j = 0; j < outputChars.length; j++) {
writer.write(outputChars[j]);
}
}
}
}
主测试类
package IOOperation;
/**
* @Description Java中IO效率的比较
* @Author 林泽鸿
* @Date 2020/5/31 10:48
*/
public class Main {
public static void main(String[] args) {
OutPutTest.before();
InputTest inputTest = new InputTest();
System.out.println("========================");
System.out.println("测试输入流(文件大小:2.58MB)");
System.out.println("========================");
inputTest.bytes();
inputTest.chars();
inputTest.bytesBuffer();
inputTest.charsBuffer();
OutPutTest outPutTest = new OutPutTest();
System.out.println("========================");
System.out.println("测试输出流");
System.out.println("========================");
outPutTest.bytes();
outPutTest.chars();
outPutTest.bytesBuffer();
outPutTest.charsBuffer();
}
}
测试结果
========================
测试输入流(文件大小:2.58MB)
========================
1.字节流输入
输入文件耗时:6723ms
2.字符流输入
输入文件耗时:522ms
3.字节缓冲流输入
输入文件耗时:14ms
4.字符缓冲流输入
输入文件耗时:98ms
========================
测试输出流
========================
1.字节流输出
输出文件耗时:19272ms
文件大小:7031KB
2.字符流输出
输出文件耗时:405ms
文件大小:7031KB
3.字节缓冲流输出
输出文件耗时:38ms
文件大小:7031KB
4.字符缓冲流输出
输出文件耗时:45ms
文件大小:7031KB
Process finished with exit code 0
结果分析
有缓冲区的操作都比没有缓冲区的效率高
输入流中,由于输入流的文件对象是.pdf对象,不是文本文件,从而带缓冲区的字节流比带缓冲区的字符流效率更高
查看了StreamDecoder的源码,从而知道FileReader类(字符输入流)默认会有一个DEFAULT_BYTE_BUFFER_SIZE 默认缓冲区大小8192,所以会比字节输入流快。
BufferReader中是将数据放到缓冲区。
FileReader是用来读文件的类,而BufferReader是将IO流转换为Buffer以提高程序的处理速度
BufferedInputStream(带缓冲区的字节输入流)在实现的时候是在自身read方法中提供缓存,是一次取1024或更多字节然后再慢慢读,一个个的返回,它并没有实现读一行的方法
BufferedReader(字符缓冲流输入)在实现时通过提供一个readLine方法,使用数组或者stringBuilder存储一行数据,并一次性返回
五、字节序
字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。其实大部分人在实际的开 发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。
类型
小端格式和大端格式(Little-Endian&Big-Endian)
不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序。
最常见的有两种:
1. Little-endian:将低序字节存储在起始地址(低位编址)
2. Big-endian:将高序字节存储在起始地址(高位编址)
C语言中的二进制和文本文件的读取效率比较
测试代码
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
double txtRead(char* filePath){
FILE* fpRead = fopen(filePath, "r");
clock_t start, finish;
int a=0;
if (fpRead == NULL)
{
printf("文件打开失败");
return 0;
}
start = clock();
while (!feof(fpRead))
{
a = fgetc(fpRead);
}
finish = clock();
double text_duration = (double)(finish - start) / CLOCKS_PER_SEC;
fclose(fpRead);
return text_duration;
}
double binaryRead(char* filePath){
FILE* fbRead = fopen(filePath,"rb");
clock_t start, finish;
int a=0;
if (fbRead == NULL)
{
printf("文件打开失败");
return 0;
}
start = clock();
while (!feof(fbRead))
{
a = fgetc(fbRead);
}
finish = clock();
fclose(fbRead);
double binary_duration = (double)(finish - start) / CLOCKS_PER_SEC;
return binary_duration;
}
int main()
{
char filePath[10] = "D:\\1.txt";
printf("文本格式总耗时:%f 秒\n", txtRead(filePath));
printf("二进制格式总耗时:%f 秒\n", binaryRead(filePath));
return 1;
}
测试结果
[Running] cd "e:\VSCodeConfiguration\Java\" && gcc clock_test.c -o clock_test && "e:\VSCodeConfiguration\Java\"clock_test
文本格式总耗时:3.678000 秒
二进制格式总耗时:3.048000 秒
[Done] exited with code=1 in 7.1 seconds
结果分析
二进制格式的读取要比文本格式的读取快一点
六、思考总结
- 源码中很多都有写明作用,直接看源码效率更高
- 字符流默认有一个缓冲区
- 学会看官方文档,Java的官方文档对IO类都有说明
参考链接
Java 流(Stream)、文件(File)和IO | 菜鸟教程
输入和输出(IO)概述_java_dreamzuora的博客-CSDN博客
(2条消息)java字节流与字符流的区别_java_呼卓宇的博客-CSDN博客
(2条消息)JAVA基础知识之StreamDecoder流_java_咕噜是个胖子-CSDN博客
(2条消息)FileReader和BufferedReader的区别_java_meihuai7538的博客-CSDN博客
C 字符串 | 菜鸟教程
------------恢复内容结束------------
Java中的IO操作和缓冲区的更多相关文章
- java中的IO操作总结
一.InputStream重用技巧(利用ByteArrayOutputStream) 对同一个InputStream对象进行使用多次. 比如,客户端从服务器获取数据 ,利用HttpURLConnect ...
- java中的IO操作
IO流是用来处理设备之间的数据传输,Java对数据的操作是通过流的方式进行,而操作流的对象都封装到java.io包中.根据操作数据的种类可以把IO流分为字节流(InputStream,OutputSt ...
- 【转】Java中的IO操作
在使用io操作之前,先看一下java中的文件类File如何使用.File包括文件和目录,对文件和目录的操作是新建目录mkdir,新建文件createNewFile,删除文件和目录delete,以及其他 ...
- java学习系列(一)Java中的IO操作
Java的IO流是实现输入/输出的基础,它可以方便地实现数据的输入\输出操作,在Java中把不同的输入\输出源抽象为"流",通过流的方式允许Java程序使用相同的方式来访问不同的输 ...
- 第12讲-Java中的IO操作及对象的序列化与反序列化
1.知识点 1.1.课程回顾 1.2.本章重点 1.2.1 io操作 1.2.2 对象的序列化与反序列化 2.具体内容 2.1.Java IO 2.1.1.什么是IO IO其实就是输入.输出 I ...
- java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET
java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了! 社区福利快来领取免费参加MDCC大会机会哦 Tag功能介绍—我们 ...
- Java中的IO流系统详解(转载)
摘要: Java 流在处理上分为字符流和字节流.字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符.字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组. Java ...
- Java中的IO流系统详解
Java 流在处理上分为字符流和字节流.字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符.字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组. Java 内用 U ...
- java中的IO流
Java中的IO流 在之前的时候我已经接触过C#中的IO流,也就是说集中数据固化的方式之一,那么我们今天来说一下java中的IO流. 首先,我们学习IO流就是要对文件或目录进行一系列的操作,那么怎样操 ...
随机推荐
- 铁大树洞app功能演示和使用说明
在观看这篇功能博客之前推荐看一下我们设计软件方案博客,可以更好地理解,博客连接:https://www.cnblogs.com/three3/p/12658897.html首先下载我们软件的安装包,点 ...
- Java 程序员生产神器 IDEA 的常用快捷键、插件及设置
对于 Java 程序员来说,使用 IDEA 集成环境是最称手的.优点不多讲,用过的人都知道.IDEA 虽好,但为了充分利用 IDEA 的优势,我分享一下我常用快捷键.插件和设置. 常用快捷键 Ctrl ...
- java验证工具类(待验证)
/** * <判断对象是否为null或者空> * * @param obj * 需要判断的对象 * @return 如果对象为null或者空则返回true */ public static ...
- 深入探究JVM之方法调用及Lambda表达式实现原理
@ 目录 前言 正文 解析 分派 静态分派 动态分派 单分派和多分派 动态分派的实现 Lambda表达式的实现原理 MethodHandle 总结 前言 在最开始讲解JVM内存结构的时候有简单分析过方 ...
- 1、Java 开发环境配置
Java 开发环境配置 在本章节中我们将为大家介绍如何搭建Java开发环境. Windows 上安装开发环境 Linux 上安装开发环境 安装 Eclipse 运行 Java window系统安装ja ...
- Python 切分数组,将一个数组均匀切分成多个数组
Python 切分数组 将一个数组,均分为多个数组 代码 # -*- coding:utf-8 -*- # py3 def list_split(items, n): return [items[i: ...
- Bytom DAPP 开发流程
从目前已经发布的DAPP来看,DAPP架构大致可以分成3种类型:插件钱包模式.全节点钱包模式和兼容模式. 插件钱包模式是借助封装了钱包的浏览器插件通过RPC协议与区块链节点通信,插件在运行时会将Web ...
- csapp第六章笔记-存储器结构
目录 随机访问存储器(Random-Access-Memory) 静态RAM 动态RAM 增强的DRAM 非易失性存储器 磁盘存储 磁盘构成 磁盘容量 磁盘操作 逻辑磁盘块 访问磁盘和连接I/O设备 ...
- Windows下,配置VS Code的Java开发环境
Windows下,配置VS Code的Java开发环境 前言 最近痴迷于VS Code的开发环境配置,原因就在于它的轻巧和免费,还能当一个非常棒的文本编辑器.如果之前你配置过VS Code并且失败了, ...
- 初识ABP vNext(1):开篇计划&基础知识
目录 前言 开始 审计(Audit) 本地化(Localization) 事件总线(Event Bus) 多租户(multi-tenancy technology) DDD分层 实体(Entity) ...