Java IO深入

IO体系

Java IO 体系种类繁多,感觉很复杂,但其实是 IO 涉及的因素太多了。在进行介绍的时候添加了设计模式等的使用,会让你感觉更加难以理解难以使用这些IO类,在此对java的IO做了一个详细的总结。

IO 类设计出来,肯定是为了解决 IO 相关的操作的,想一想哪里会有 IO 操作?网络、磁盘。网络操作相关的类是在 java.net 包下,不在本文的总结范围内。提到磁盘,你可能会想到文件,文件操作在 IO 中是比较典型的操作。在 Java 中引入了 “流” 的概念,它表示任何有能力产生数据源或有能力接收数据源的对象。数据源可以想象成水源,海水、河水、湖水、一杯水等等。数据传输可以想象为水的运输,古代有用桶运水,用竹管运水的,现在有钢管运水,不同的运输方式对应不同的运输特性。

从数据来源或者说是操作对象角度看,IO 类可以分为:

1、文件(file):FileInputStream、FileOutputStream、FileReader、FileWriter

2、数组([]):

  2.1、字节数组(byte[]):ByteArrayInputStream、ByteArrayOutputStream2.2、字符数组(char[]):CharArrayReader、CharArrayWriter

3、管道操作:PipedInputStream、PipedOutputStream、PipedReader、PipedWriter

4、基本数据类型:DataInputStream、DataOutputStream

5、缓冲操作:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter

6、打印:PrintStream、PrintWriter

7、对象序列化反序列化:ObjectInputStream、ObjectOutputStream

8、转换:InputStreamReader、OutputStreWriter

数据源节点也可以再进行二次处理,使数据更加容易使用,所以还可以划分成节点流和处理流,涉及到设计模式的使用。
在这里插入图片描述

从数据传输方式或者说是运输方式角度看,可以将 IO 类分为:

1、字节流
2、字符流

字节流是以一个字节单位来运输的,比如一杯一杯的取水。而字符流是以多个字节来运输的,比如一桶一桶的取水,一桶水又可以分为几杯水。

字节流和字符流的区别:
字节流读取单个字节,字符流读取单个字符(一个字符根据编码的不同,对应的字节也不同,如 UTF-8 编码是 3 个字节,中文编码是 2 个字节。)字节流用来处理二进制文件(图片、MP3、视频文件),字符流用来处理文本文件(可以看做是特殊的二进制文件,使用了某种编码,人可以阅读)。简而言之,字节是个计算机看的,字符才是给人看的。

字节流和字符流的划分可以看下面这张图。
在这里插入图片描述
不可否认,Java IO 相关的类确实很多,但我们并不是所有的类都会用到,我们常用的也就是文件相关的几个类,如文件最基本的读写类 File 开头的、文件读写带缓冲区的类 Buffered 开头的类,对象序列化反序列化相关的类 Object 开头的类。

IO类和相关方法

IO 类虽然很多,但最基本的是 4 个抽象类:InputStream、OutputStream、Reader、Writer。最基本的方法也就是一个读 read() 方法、一个写 write() 方法。方法具体的实现还是要看继承这 4 个抽象类的子类,毕竟我们平时使用的也是子类对象。这些类中的一些方法都是(Native)本地方法、所以并没有 Java 源代码,下面我对这些常用类进行了总结。

先来看 InputStream 和 OutStream 中的方法简介,因为都是抽象类、大都是抽象方法、所以就不贴源码喽!注意这里的读取和写入,其实就是获取(输入)数据和输出数据。

InputStream 类

读取数据

public abstract int read()

将读取到的数据放在 byte 数组中,该方法实际上是根据下面的方法实现的,off 为 0,len 为数组的长度

public int read(byte b[])

从第 off 位置读取 len 长度字节的数据放到 byte 数组中,流是以 -1 来判断是否读取结束的(注意这里读取的虽然是一个字节,但是返回的却是 int 类型 4 个字节)

public int read(byte b[], int off, int len)

跳过指定个数的字节不读取,想想看电影跳过片头片尾

public long skip(long n)

返回可读的字节数量

public int available()

读取完,关闭流,释放资源

public void close()

标记读取位置,下次还可以从这里开始读取,使用前要看当前流是否支持,可以使用 markSupport() 方法判断

public synchronized void mark(int readlimit)

重置读取位置为上次 mark 标记的位置

public synchronized void reset()

判断当前流是否支持标记流,和上面两个方法配套使用

public boolean markSupported()

OutputStream 类

写入一个字节,可以看到这里的参数是一个 int 类型,对应上面的读方法,int 类型的 32 位,只有低 8 位才写入,高 24 位将舍弃。

public abstract void write(int b)

将数组中的所有字节写入,和上面对应的 read() 方法类似,实际调用的也是下面的方法。

public void write(byte b[])

将 byte 数组从 off 位置开始,len 长度的字节写入

public void write(byte b[], int off, int len)

强制刷新,将缓冲中的数据写入

public void flush()

关闭输出流,流被关闭后就不能再输出数据了

public void close()

再来看 Reader 和 Writer 类中的方法,你会发现和上面两个抽象基类中的方法很像。

Reader 类

读取字节到字符缓存中

public int read(java.nio.CharBuffer target)

读取单个字符

public int read()

读取字符到指定的 char 数组中

public int read(char cbuf[])

从 off 位置读取 len 长度的字符到 char 数组中

abstract public int read(char cbuf[], int off, int len)

跳过指定长度的字符数量

public long skip(long n)

和上面的 available() 方法类似

public boolean ready()

判断当前流是否支持标记流

public boolean markSupported()

标记读取位置,下次还可以从这里开始读取,使用前要看当前流是否支持,可以使用 markSupport() 方法判断

public void mark(int readAheadLimit)

重置读取位置为上次 mark 标记的位置

public void reset()

关闭流释放相关资源

abstract public void close()

Writer 类

写入一个字符

public void write(int c)

写入一个字符数组

public void write(char cbuf[])

从字符数组的 off 位置写入 len 数量的字符

abstract public void write(char cbuf[], int off, int len)

写入一个字符串

public void write(String str)

从字符串的 off 位置写入 len 数量的字符

public void write(String str, int off, int len)

追加吸入一个字符序列

public Writer append(CharSequence csq)

追加写入一个字符序列的一部分,从 start 位置开始,end 位置结束

public Writer append(CharSequence csq, int start, int end)

追加写入一个 16 位的字符

public Writer append(char c)

强制刷新,将缓冲中的数据写入

abstract public void flush()

关闭输出流,流被关闭后就不能再输出数据了

abstract public void close()

下面我们就直接使用他们的子类,在使用中再介绍下面没有的新方法。

1、读取控制台中的输入

import java.io.*;public class IOTest {public static void main(String[] args) throws IOException {// 三个测试方法
//        test01();
//        test02();test03();}public static void test01() throws IOException {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("请输入一个字符");char c;c = (char) bufferedReader.read();System.out.println("你输入的字符为"+c);}public static void test02() throws IOException {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("请输入一个字符,按 q 键结束");char c;do {c = (char) bufferedReader.read();System.out.println("你输入的字符为"+c);} while (c != 'q');}public static void test03() throws IOException {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("请输入一行字符");String str = bufferedReader.readLine();System.out.println("你输入的字符为" + str);}
}

至于控制台的输出,我们其实一直都在使用呢,System.out.println() ,out 其实是 PrintStream 类对象的引用,PrintStream 类中当然也有 write() 方法,但是我们更常用 print() 方法和 println() 方法,因为这两个方法可以输出的内容种类更多,比如一个打印一个对象,实际调用的对象的 toString() 方法。

2、二进制文件的写入和读取
注意这里文件的路径,可以根据自己情况改一下,虽然这里的文件后缀是txt,但该文件却是一个二进制文件,并不能直接查看。

@Testpublic void test04() throws IOException {byte[] bytes = {10,25,37,12,23};FileOutputStream fileOutputStream = new FileOutputStream(new File("").getAbsolutePath()+"/io/test.txt");// 写入二进制文件,直接打开会出现乱码fileOutputStream.write(bytes);fileOutputStream.close();}@Testpublic void test05() throws IOException {FileInputStream fileInputStream = new FileInputStream(new File("").getAbsolutePath()+"/io/test.txt");int c;// 读取写入的二进制文件,输出字节数组while ((c = fileInputStream.read()) != -1) {System.out.print(c);}}

3、文本文件的写入和读取
write() 方法和 append() 方法并不是像方法名那样,一个是覆盖内容,一个是追加内容,append() 内部也是 write() 方法实现的,也非说区别,也就是 append() 方法可以直接写 null,而 write() 方法需要把 null 当成一个字符串写入,所以两者并无本质的区别。需要注意的是这里并没有指定文件编码,可能会出现乱码的问题。

@Testpublic void test06() throws IOException {FileWriter fileWriter = new FileWriter(new File("").getAbsolutePath()+"/io/test.txt");fileWriter.write("Hello,world!\n欢迎进入Java IO\n");fileWriter.write("不会覆盖文件原本的内容\n");
//        fileWriter.write(null); 不能直接写入 nullfileWriter.append("并不是追加一行内容,不要被方法名迷惑\n");fileWriter.append(null);fileWriter.flush();System.out.println("文件的默认编码为" + fileWriter.getEncoding());fileWriter.close();}@Testpublic void test07() throws IOException {FileWriter fileWriter = new FileWriter(new File("").getAbsolutePath()+"/io/test.txt", false); // 关闭追加模式,变为覆盖模式fileWriter.write("Hello,world!欢迎进入Java IO\n");fileWriter.write("我来覆盖文件原本的内容");fileWriter.append("我是下一行");fileWriter.flush();System.out.println("文件的默认编码为" + fileWriter.getEncoding());fileWriter.close();}@Testpublic void test08() throws IOException {FileReader fileReader = new FileReader(new File("").getAbsolutePath()+"/io/test.txt");BufferedReader bufferedReader = new BufferedReader(fileReader);String str;while ((str = bufferedReader.readLine()) != null) {System.out.println(str);}fileReader.close();bufferedReader.close();}@Testpublic void test09() throws IOException {FileReader fileReader = new FileReader(new File("").getAbsolutePath()+"/io/test.txt");int c;while ((c = fileReader.read()) != -1) {System.out.print((char) c);}}

使用字节流和字符流的转换类 InputStreamReader 和 OutputStreamWriter 可以指定文件的编码,使用 Buffer 相关的类来读取文件的每一行。

@Testpublic void test10() throws IOException {FileOutputStream fileOutputStream = new FileOutputStream(new File("").getAbsolutePath()+"/io/test2.txt");OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "GBK"); // 使用 GBK 编码文件outputStreamWriter.write("Hello,world!\n欢迎进入Java IO\n");outputStreamWriter.append("另外一行内容");outputStreamWriter.flush();System.out.println("文件的编码为" + outputStreamWriter.getEncoding());outputStreamWriter.close();fileOutputStream.close();}@Testpublic void test11() throws IOException {FileInputStream fileInputStream = new FileInputStream(new File("").getAbsolutePath()+"/io/test2.txt");InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "GBK"); // 使用 GBK 解码文件BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str;while ((str = bufferedReader.readLine()) != null) {System.out.println(str);}bufferedReader.close();inputStreamReader.close();}

4、复制文件
我进行了一些测试,不使用缓冲对文件复制时间的影响,文件的复制实质还是文件的读写。缓冲流是处理流,是对节点流的装饰。

@Testpublic void  test12() throws IOException {// 输入和输出都使用缓冲流FileInputStream in = new FileInputStream("D:\\我的资料\\大数据可视化\\大数据.mp4");BufferedInputStream inBuffer = new BufferedInputStream(in);FileOutputStream out = new FileOutputStream("大数据.mp4");BufferedOutputStream outBuffer = new BufferedOutputStream(out);int len = 0;byte[] bs = new byte[1024];long begin = System.currentTimeMillis();while ((len = inBuffer.read(bs)) != -1) {outBuffer.write(bs, 0, len);}System.out.println("复制文件所需的时间:" + (System.currentTimeMillis() - begin)); // 平均时间约 200 多毫秒inBuffer.close();in.close();outBuffer.close();out.close();}@Testpublic void  test13() throws IOException {// 只有输入使用缓冲流FileInputStream in = new FileInputStream("D:\\我的资料\\大数据可视化\\大数据.mp4");BufferedInputStream inBuffer = new BufferedInputStream(in);FileOutputStream out = new FileOutputStream("大数据.mp4");int len = 0;byte[] bs = new byte[1024];long begin = System.currentTimeMillis();while ((len = inBuffer.read(bs)) != -1) {out.write(bs, 0, len);}System.out.println("复制文件所需时间:" + (System.currentTimeMillis() - begin)); // 平均时间约 500 多毫秒inBuffer.close();in.close();out.close();}@Testpublic void test14() throws IOException {// 输入和输出都不使用缓冲流FileInputStream in = new FileInputStream("D:\\我的资料\\大数据可视化\\大数据.mp4");FileOutputStream out = new FileOutputStream("大数据.mp4");int len = 0;byte[] bs = new byte[1024];long begin = System.currentTimeMillis();while ((len = in.read(bs)) != -1) {out.write(bs, 0, len);}System.out.println("复制文件所需时间:" + (System.currentTimeMillis() - begin)); // 平均时间 700 多毫秒in.close();out.close();}@Testpublic void test15() throws IOException {// 不使用缓冲FileInputStream in = new FileInputStream("D:\\我的资料\\大数据可视化\\大数据.mp4");FileOutputStream out = new FileOutputStream("大数据.mp4");int len = 0;long begin = System.currentTimeMillis();while ((len = in.read()) != -1) {out.write(len);}System.out.println("复制文件所需时间:" + (System.currentTimeMillis() - begin)); // 平均时间约 160000 毫秒,约 2 分多钟in.close();out.close();}

结论:Java IO 类很多,但是把握住整个体系,掌握关键的方法,学习起来就会轻松很多,看完这篇文章,你是否觉得 Java IO 并没有你想的那么难呢?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/565365.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【OpenCV 例程200篇】222. 特征提取之弗里曼链码(Freeman chain code)

OpenCV 例程200篇 总目录 【youcans 的 OpenCV 例程200篇】222. 特征提取之弗里曼链码(Freeman chain code) 目标特征的基本概念 通过图像分割获得多个区域,得到区域内的像素集合或区域边界像素集合。我们把感兴趣的人或物称为目标&#xff…

【OpenCV 例程200篇】224. 特征提取之提取骨架

OpenCV 例程200篇 总目录 【youcans 的 OpenCV 例程200篇】224. 特征提取之提取骨架 目标特征的基本概念 通过图像分割获得多个区域,得到区域内的像素集合或区域边界像素集合。我们把感兴趣的人或物称为目标,目标所处的区域就是目标区域。 特征通常是针…

inuri .php id=,php常用函数(遇见就补充)

preg_match()preg_match(string pattern, string subject [, array matches [, int flags]])在subject字符串中搜索与 pattern给出的正则表达式相匹配的内容。如果提供了 matches,则会被搜索的结果所填充。$matches[0]将包含于整个模式匹配的文本,$match…

【OpenCV 例程200篇】226. 区域特征之紧致度/圆度/偏心率

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】226. 区域特征之紧致度/圆度/偏心率 特征通常是针对于图像中的某个目标而言的。 我们把感兴趣的人或物称为目标,目标所处的区域就是目标区域。图像分割之后,还要对目标区域进…

Java常见的几种设计模式

单例模式 指一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。 getInstance()的返回值是一个对象的引用,并不是一个新的实例&a…

【OpenCV 例程200篇】227. 特征描述之 LBP 纹理特征算子

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】227. 特征描述之 LBP 纹理特征算子 特征通常是针对于图像中的某个目标而言的。 针对目标所在区域的特征描述符(Region descriptors),称为区域特征描述子。 4.2 纹…

【OpenCV 例程200篇】228. 特征描述之 extendLBP 改进算子

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】228. 特征描述之 extendLBP 改进算子 特征通常是针对于图像中的某个目标而言的。针对目标所在区域的特征描述符(Region descriptors),称为区域特征描述子。 局部…

【OpenCV 例程200篇】229. 特征描述之 LBP 算子比较(skimage)

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】229. 特征描述之 LBP 算子比较(skimage) 局部二值模式(LBP,Local binary patterns)是一种用来描述图像局部纹理特征的算子,它具…

Java多线程的使用

无论在我们的工作中还是在我们的生活中,我们都会用到多线程的知识,今天就给大家讲一下如何使用多线程。 序幕 线程的启动 如何使线程暂停 如何使线程停止 线程的优先级 线程安全相关的问题 我们首先要知道进程和线程分别是什么? 进程 - 进…

oracle 存储过程 db,oracle数据库的存储过程是什么?

oracle数据库的存储过程:一组为了完成特定功能的SQL语句集,经编译后存储在数据库中。存储过程是由流控制和SQL语句书写的过程,这个过程经编译和优化后存储在数据库服务器中,应用程序使用时只要调用即可。存储过程(Stored Procedur…

【OpenCV 例程200篇】230. 特征描述之 LBP 统计直方图

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】230. 特征描述之 LBP 统计直方图 局部二值模式(LBP,Local binary patterns)是一种用来描述图像局部纹理特征的算子,它具有旋转不变性和灰度不变性的优点…

【OpenCV 例程200篇】232. 特征描述之频谱方法

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】232. 纹理特征之频谱方法 4.3 纹理特征之频谱方法 傅里叶谱可以描述图像中的周期性或半周期性二维模式的方向性,因此可以基于傅里叶变换对纹理进行频谱分析。 纹理与图像频谱中的高频分…

java制作oracle程序,Java程序操作Oracle两种方式之简单实现

Java程序操作Oracle两种方式之简单实现1.通过JDBC-ODBC桥连接Oracle数据库(1)创建odbc源,在控制面板->管理工具->数据源(odbc)中添加DSN,比如取名为wangtao,选择一个Service,输入用户名密码,测试连接,若通过说明成功;(注意&…

【OpenCV 例程200篇】233. 区域特征之矩不变量

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】233. 区域特征之矩不变量 4.4 区域特征之矩不变量 矩是概率与统计中的一个概念,是随机变量的一种数字特征。矩函数在图像分析中有着广泛的应用,如模式识别、目标分类、图像编…

【OpenCV 例程200篇】231. 特征描述之灰度共生矩阵(GLCM)

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】231. 特征描述之灰度共生矩阵(GLCM) 4.2.4 灰度共生矩阵(GLCM) 灰度共生矩阵(Gray level co-occurrence matrix,GLCM&#xff…

oracle11 处理器数,11G AWR中%Total CPU与%Busy CPU指标的疑问

ODM FINDING:FROM http://www.os2ora.com/how-to-analyze-awr-report-1/ 推荐 kaya 的这篇文章引用如下:如果关注数据库的性能,那么当拿到一份AWR报告的时候,最想知道的第一件事情可能就是系统资源的利用情况了,而首当其冲的&am…

【OpenCV 例程200篇】237. 基于主成分提取的方向校正(OpenCV)

『youcans 的 OpenCV 例程200篇 - 总目录』 【youcans 的 OpenCV 例程200篇】237. 基于主成分提取的方向校正(OpenCV) 主成分分析(Principal Components Analysis,PCA)是一种基于统计的数据降维方法,又称主…

swoole 捕捉php错误,swoole怎么处理错误

在协程编程中可直接使用try/catch处理异常。但必须在协程内捕获,不得跨协程捕获异常。不仅是应用层throw的Exception,底层的一些错误也是可以被捕获的,如function、class、method不存在下面的代码中,try/catch和throw在不同的协程…

【OpenCV 例程200篇】235. 特征提取之主成分分析(sklearn)

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程 300篇】235. 特征提取之主成分分析(sklearn) 特征提取是指从原始特征中通过数学变换得到一组新的特征,以降低特征维数,消除相关性,减少无用信息…

Linux 进程资源分配,linux 进程管理和内存分配

1、进程相关概念进程:正在运行中的程序内核功用:进程管理、文件系统、网络功能、内存管理、驱动程序、安全功能等Process:运行中的程序的一个副本,是被载入内存的一个指令集合进程 ID(Process ID,PID)号码被用来标记各…