Java IO流
文章目录
- Java IO流
- 1.文件相关基础普及
- 1.1 常用文件操作
- 1.3 目录的操作和文件删除
- 2.IO流原理及流的分类
- 2.1 字节流
- 2.1.1 InputStream:字节输入流
- 2.1.2 OutputStream
- 2.2 字符流
- 2.2.1 Reader
- 2.2.1 Writer
- 2.3 节点流和处理流
- 2.3.1节点流
- 2.3.2 处理流
- 2.3.2.1 处理流设计模式
- 2.3.2.2 缓冲流
- 2.3.2.2.1 字节缓冲输入流
- 2.3.2.2.1 字符缓冲流
- 2.3.3 对象处理流
- 2.3.3.1序列化与反序列化
- 2.3.3.1 OjectOutputStream与OjectOutputStream
- 2.3.3.2 对象处理流使用细节
- 2.4 转换流
- 2.4.1 InputStreamReader :
- 2.4.1 OutputStreamWriter
1.文件相关基础普及
创建文件对象相关构造器和相关方法:
new File(String pathname)//根据路径构建一个File对象
new File(File parent, String child),/根据父目录文件+子路径构建
new File(String parent,String child)//根据父目录+子路径构建
createNewFile(); 创建新文件
接下来我们通过一个简单的例子,实现文件创建
packageio;importjava.io.File;importjava.io.IOException;publicclassioTest01{publicstaticvoidmain(String[]args){StringfilePath="/Users/Zhuanz1/Desktop/大三下Java就业/java网络/netTest/src/io/test.txt";//文件具体路径Filefile=newFile(filePath);try{file.createNewFile();System.out.println("创建成功!");}catch(IOExceptione){e.printStackTrace();}}}在这个例子中,我们使用绝对路径/Users/Zhuanz1/Desktop/大三下Java就业/java网络/netTest/src/io/test.txt将文件创建出来。
1.1 常用文件操作
文件操作中,常用的关键词有以下几个:getName,getAbsolutePath,getParent,length,exists,isFile,isDirectory。包含获取文件名,获取文件的绝对路径,文件的父目录,长度等。
1.3 目录的操作和文件删除
mkdir创建一级目录、mkdirs创建多级目录、delete删除空目录或文件
文件创建案例:
packagecom.example;importjava.io.File;publicclassFileTest01{publicstaticvoidmain(String[]args){try{Filefile=newFile("src/main/resources/test");file.mkdir();System.out.println("创建成功!");}catch(Exceptione){e.printStackTrace();};}}在此案例中,我们根据指定创建了一个test文件夹。
2.IO流原理及流的分类
IO 即
Input/Output,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。
Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
2.1 字节流
2.1.1 InputStream:字节输入流
InputStream用于从源头(通常是文件)读取数据(字节信息)到内存中,java.io.InputStream抽象类是所有字节输入流的父类。
packagecom.example;importjava.io.*;importjava.nio.charset.StandardCharsets;importjava.util.Scanner;publicclassInputStreamTest01{publicstaticvoidmain(String[]args){StringfileAddress="src/main/resources/file1.txt";Filefile=newFile(fileAddress);// 确保父目录存在FileparentDir=file.getParentFile();if(parentDir!=null&&!parentDir.exists()){parentDir.mkdirs();// 创建必要的父目录System.out.println("已创建目录:"+parentDir.getPath());}// 使用 try-with-resources 管理资源try(Scannerscanner=newScanner(System.in);OutputStreamoutputStream=newFileOutputStream(file);InputStreaminputStream=newFileInputStream(file)){// 检查文件是否存在if(file.exists()){System.out.println("文件已经存在!");}else{System.out.println("文件创建成功!");}// 获取用户输入并写入文件System.out.println("你可以记录的所感所想:");Stringrecord=scanner.nextLine();outputStream.write(record.getBytes(StandardCharsets.UTF_8));// 指定 UTF-8 编码outputStream.flush();// 确保数据写入文件// 读取文件内容并输出byte[]buffer=newbyte[1024];intlen;while((len=inputStream.read(buffer))!=-1){System.out.print(newString(buffer,0,len,StandardCharsets.UTF_8));}}catch(IOExceptione){System.err.println("操作文件时发生错误:"+e.getMessage());e.printStackTrace();}}}这个 Java 程序的功能是通过用户输入记录文本到文件,并读取文件内容显示出来。
在资源管理中使用 try-with-resources 语句初始化 Scanner(用于用户输入)、FileOutputStream(用于写文件)和 FileInputStream(用于读文件),确保资源自动关闭。
实现文件成功创建,并且将我们的输入的字段输出出来。
不过,一般我们是不会直接单独使用FileInputStream,通常会配合BufferedInputStream(字节缓冲输入流,后文会讲到)来使用。
2.1.2 OutputStream
OutputStream用于将数据(字节信息)写入到目的地(通常是文件),java.io.OutputStream抽象类是所有字节输出流的父类。
通过这个例子:使用OutputStream在文件“file1.txt"中写入“i love you so”,若文件不存在,则创建
packagecom.example;importjava.io.FileOutputStream;importjava.io.IOException;publicclassOutputSteamTest01{publicstaticvoidmain(String[]args)throwsIOException{StringfileAdress="src/main/resources/file1.txt";//操作目标文件路径intlength=0;byte[]buf=newbyte[1024];//缓冲区FileOutputStreamfileInputStream=null;try{fileInputStream=newFileOutputStream(fileAdress,true);//读取文件 加了true 实现文件不覆盖写入Stringstr="i love you so";fileInputStream.write(str.getBytes());//getBytes()String方法有将str转为byte类型System.out.println("写入成功!");}catch(Exceptione){e.printStackTrace();}finally{fileInputStream.close();}}}结果如下:成功写入~
类似于FileInputStream,FileOutputStream通常也会配合BufferedOutputStream(字节缓冲输出流,后文会讲到)来使用。
FileOutputStreamfileOutputStream=newFileOutputStream("output.txt");BufferedOutputStreambos=newBufferedOutputStream(fileOutputStream)2.2 字符流
不管是文件读写还是网络发送接收,信息的最小存储单元都是字节。那为什么 I/O 流操作要分为字节流操作和字符流操作呢?
个人认为主要有两点原因:
- 字符流是由 Java 虚拟机将字节转换得到的,这个过程还算是比较耗时。
- 如果我们不知道编码类型就很容易出现乱码问题。
乱码问题这个很容易就可以复现,我们只需要将上面提到的FileInputStream代码示例中的input.txt文件内容改为中文即可,原代码不需要改动。
可以很明显地看到读取出来的内容已经变成了乱码。
因此,I/O 流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
字符流默认采用的是Unicode编码,我们可以通过构造方法自定义编码。
Unicode 本身只是一种字符集,它为每个字符分配一个唯一的数字编号,并没有规定具体的存储方式。UTF-8、UTF-16、UTF-32 都是 Unicode 的编码方式,它们使用不同的字节数来表示 Unicode 字符。例如,UTF-8 :英文占 1 字节,中文占 3 字节。
2.2.1 Reader
Reader用于从源头(通常是文件)读取数据(字符信息)到内存中,java.io.Reader抽象类是所有字符输入流的父类。
Reader用于读取文本,InputStream用于读取原始字节。
Reader常用方法:
read(): 从输入流读取一个字符。read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组cbuf中,等价于read(cbuf, 0, cbuf.length)。read(char[] cbuf, int off, int len):在read(char[] cbuf)方法的基础上增加了off参数(偏移量)和len参数(要读取的最大字符数)。skip(long n):忽略输入流中的 n 个字符 ,返回实际忽略的字符数。close(): 关闭输入流并释放相关的系统资源。
InputStreamReader是字节流转换为字符流的桥梁,其子类FileReader是基于该基础上的封装,可以直接操作字符文件。
FileReader代码示例:
try(FileReaderfileReader=newFileReader("input.txt");){intcontent;longskip=fileReader.skip(3);System.out.println("The actual number of bytes skipped:"+skip);System.out.print("The content read from file:");while((content=fileReader.read())!=-1){System.out.print((char)content);}}catch(IOExceptione){e.printStackTrace();}输出:
The actual number of bytes skipped:3 The content read from file:我是Guide。2.2.1 Writer
Writer用于将数据(字符信息)写入到目的地(通常是文件),java.io.Writer抽象类是所有字符输出流的父类。
Writer常用方法:
write(int c): 写入单个字符。write(char[] cbuf):写入字符数组cbuf,等价于write(cbuf, 0, cbuf.length)。write(char[] cbuf, int off, int len):在write(char[] cbuf)方法的基础上增加了off参数(偏移量)和len参数(要读取的最大字符数)。write(String str):写入字符串,等价于write(str, 0, str.length())。write(String str, int off, int len):在write(String str)方法的基础上增加了off参数(偏移量)和len参数(要读取的最大字符数)。append(CharSequence csq):将指定的字符序列附加到指定的Writer对象并返回该Writer对象。append(char c):将指定的字符附加到指定的Writer对象并返回该Writer对象。flush():刷新此输出流并强制写出所有缓冲的输出字符。close():关闭输出流释放相关的系统资源。
FileWriter代码示例:
try(Writeroutput=newFileWriter("output.txt")){output.write("你好,我是Guide。");}catch(IOExceptione){e.printStackTrace();}2.3 节点流和处理流
2.3.1节点流
1.节点流可以从一个特定的数据源读写数据,如:FileReader、FileWriter;
如上图,就是javaIO中所有的的节点流与处理流,包括字节和字符类型。
例如下图,当我们需要操作一个文件时,那么我们就可以通过节点流,利用file文件类型,使用fileReader实现文件的读取。
同理,当我们需要操作别的类型时,就调用别的类型的节点流,即可实现不用数据类型的内容读取。
例如我们在上一章节使用过的一个例子,我们需要通过操作目标文件实现写入。我们使用了FileOutputStream节点流处理文件类型数据,实现文件读取功能。
package com.example; import java.io.FileOutputStream; import java.io.IOException; public class OutputSteamTest01 { public static void main(String[] args) throws IOException { String fileAdress = "src/main/resources/file1.txt";//操作目标文件路径 int length = 0; byte[] buf = new byte[1024];//缓冲区 FileOutputStream fileInputStream = null; try { fileInputStream = new FileOutputStream(fileAdress,true);//读取文件 加了true 实现文件不覆盖写入 String str = "i love you so"; fileInputStream.write(str.getBytes());//getBytes()String方法有将str转为byte类型 System.out.println("写入成功!"); } catch (Exception e) { e.printStackTrace(); }finally { fileInputStream.close(); } } }由此我们可以得出,针对不同数据源,我们就选择相对应的节点流。直接操作数据源,这种方式也相对底层,无法对数据源进行修改删除操作。只能实现简单读取功能。
2.3.2 处理流
2.3.2.1 处理流设计模式
2.处理流(包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写能力,如:BufferedReader、BufferedWriter;
简单来说,就无须管文本输入输出类型,直接操作数据源。
例如我们后面会介绍到的BufferWriter,我们可以通过BufferWriter操作各种数据类型。更加灵活!
2.3.2.2 缓冲流
2.3.2.2.1 字节缓冲输入流
IO 操作是很消耗性能的,缓冲流将数据加载至缓冲区,一次性读取/写入多个字节,从而避免频繁的 IO 操作,提高流的传输效率。
字节缓冲流这里采用了装饰器模式来增强InputStream和OutputStream子类对象的功能。
举个例子,我们可以通过BufferedInputStream(字节缓冲输入流)来增强FileInputStream的功能。只
// 新建一个 BufferedInputStream 对象BufferedInputStreambufferedInputStream=newBufferedInputStream(newFileInputStream("input.txt"));字节流和字节缓冲流的性能差别主要体现在我们使用两者的时候都是调用write(int b)和read()这两个一次只读取一个字节的方法的时候。由于字节缓冲流内部有缓冲区(字节数组),因此,字节缓冲流会先将读取到的字节存放在缓存区,大幅减少 IO 次数,提高读取效率。
我使用write(int b)和read()方法,分别通过字节流和字节缓冲流复制一个524.9 mb的 PDF 文件耗时对比如下
使用缓冲流复制PDF文件总耗时:15428 毫秒 使用普通字节流复制PDF文件总耗时:2555062 毫秒两者耗时差别非常大,缓冲流耗费的时间是字节流的 1/165。
测试代码如下:
@Testvoidcopy_pdf_to_another_pdf_buffer_stream(){// 记录开始时间longstart=System.currentTimeMillis();try(BufferedInputStreambis=newBufferedInputStream(newFileInputStream("深入理解计算机操作系统.pdf"));BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("深入理解计算机操作系统-副本.pdf"))){intcontent;while((content=bis.read())!=-1){bos.write(content);}}catch(IOExceptione){e.printStackTrace();}// 记录结束时间longend=System.currentTimeMillis();System.out.println("使用缓冲流复制PDF文件总耗时:"+(end-start)+" 毫秒");}@Testvoidcopy_pdf_to_another_pdf_stream(){// 记录开始时间longstart=System.currentTimeMillis();try(FileInputStreamfis=newFileInputStream("深入理解计算机操作系统.pdf");FileOutputStreamfos=newFileOutputStream("深入理解计算机操作系统-副本.pdf")){intcontent;while((content=fis.read())!=-1){fos.write(content);}}catch(IOExceptione){e.printStackTrace();}// 记录结束时间longend=System.currentTimeMillis();System.out.println("使用普通流复制PDF文件总耗时:"+(end-start)+" 毫秒");}在结束时,我们只需要关闭外层处理流即可,底层会去关闭节点流。
2.3.2.2.1 字符缓冲流
BufferedReader(字符缓冲输入流)和BufferedWriter(字符缓冲输出流)类似于BufferedInputStream(字节缓冲输入流)和BufferedOutputStream(字节缓冲输入流),内部都维护了一个字节数组作为缓冲区。不过,前者主要是用来操作字符信息。
2.3.3 对象处理流
既可以保存数据的值,也可以保存数据的数据类型;使得数据被保存到文件中后,还能够从文件中恢复。
作用 :
eg1 : 一个基本类型数据 int i = 5; ,对象处理流可以将i变量的值5和它的数据类型int都保存到文件中;eg2 : 一个引用类型数据 Cat cat = new Cat("bubble", 5, "white");,对象处理流可以将cat变量的值String-"bubble", int-5, String-"white"都保存下来,还可以记录下它的数据类型是Cat类型。2.3.3.1序列化与反序列化
序列化——*保存数据的值和数据类型到文件中*。
反序列化——*将文件中保存的数据(包含值和数据类型)重新恢复成数据本身*。
如下图所示 :
2.3.3.1 OjectOutputStream与OjectOutputStream
ObjectOutputStream继承自OutputStream类;它可以实现数据的序列化。ObjectOutputStream类同样使用了“修饰器模式”,即该类中有一个OutputStream的引用变量(存在于OutputStream类的静态内部类BlockDataOutputStream中)。
ObjectInputStream类继承自InputStream类;它可以实现数据的反序列化。ObjectInputStream类同样使用了“修饰器模式”,即该类中有一个InputStream的引用变量(存在于InputStream类中的静态内部类PeekInputStream中)。
2.3.3.2 对象处理流使用细节
关于序列化的一些细节:
1° 序列化或者反序列化的对象必须实现实现Serializable接口;并且序列化和反序列化时写入数据与读取数据的顺序必须一致。 2° 序列化类中,建议添加serialVersionUID,可以提高版本的兼容性。 3° 序列化对象时,默认要将里面所有属性都进行序列化,但不包括static和transient修饰的成员。 4° 序列化对象时,要求里面的属性的类型也要实现序列化接口。(eg : String, Integer等类已实现了序列化) 5° 序列化具备可继承性,即如果某个类实现了序列化,默认其所有子类也实现了序列化。(eg : Number ---> Serializable)2.4 转换流
若在txt文件中出现中文,并且该文件没有使用UTF-8形式编码;在使用IO流读取文件时就会出现乱码问题。
转换流可以解决乱码问题,转换流可以将字节流转成字符流,同时指定读取文件时按照怎样的编码方式。
常见转换流有两个 ——InputStreamReader和OutputStreamWriter。
2.4.1 InputStreamReader :
*InputStreamReader是Reader抽象类的一个子类,因此它本身属于字符流范畴*。完成转换所调用的构造器如下,需要传入一个InputStream类的子类对象,以及一个编码类型。
packagecsdn.knowledge.api_tools.iocurrent.transformation;importjava.io.*;importstaticjava.nio.charset.StandardCharsets.UTF_8;publicclassInputStreamReader_Demo{publicstaticvoidmain(String[]args)throwsIOException{InputStreamReaderinputStreamReader=newInputStreamReader(newFileInputStream("D:\\JAVA\\IDEA\\file\\3.txt"),UTF_8);BufferedReaderbufferedReader=newBufferedReader(inputStreamReader);Stringdata;while((data=bufferedReader.readLine())!=null){System.out.println(data);}bufferedReader.close();}}2.4.1 OutputStreamWriter
*OutputStreamWriter类继承自Writer类,转换方法与InputStreamWriter同理****。 以OutputStreamWriter_Demo类为演示类,代码如下 :
packagecsdn.knowledge.api_tools.iocurrent.transformation;importjava.io.FileNotFoundException;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.OutputStreamWriter;/** * @author : Cyan_RA9 * @version : 21.0 */publicclassOutputStreamWriter_Demo{publicstaticvoidmain(String[]args)throwsIOException{OutputStreamWriteroutputStreamWriter=newOutputStreamWriter(newFileOutputStream("D:\\JAVA\\IDEA\\fileEX\\1.txt"),"UTF-16");outputStreamWriter.write("Space-X, 星舰发射!");outputStreamWriter.close();}}以上便是Java IO流中的全部内容,欢迎站中大佬指出不足,笔者编写不宜,恳求一枚点赞收藏评论!!