一、什么是流
1.I/O流的基本概念
Java中的流是一个抽象的概念,当程序需要去数据库读取数据的时候,就会开启数据流,数据源可以是各种类型的文件,当想要把某些数据传输到其他存储设备上时,也要用到数据流。存储的数据可以是各种类型的文件,简单来说就是执行了数据的读写操作,Java 的I/ O 流是实现文件读写操作的基础,I/ O 流是一种计算机用语,可以实现文件的输入和输出。在Java中把不同的输入输出源(键盘、 文件、 网络等)抽象表述为“流( stream)”, 对于程序而言,文件的传输是非常重要的, 程序运行需要数据,获取数据要跟外部系统(数据库、 网络、 文件、 其他程序)保持联络,外部系统比较复杂多变,必须要通过某种手段进行数据的获取,屏蔽这个差异, 但是程序在获取数据之后, 一般数据会保存在 JVM上, 但是JVM一旦关闭运行状态,数据就会消失,不能恢复,所以对数据的操作,除了要从外部系统中获取数据以外,还要学会将数据传输到其他存储设备上去, 使数据得到保护,在需要的时候再通过读取的方式获取数据。 2.流的分类 Java 中的流,可以从不同的角度进行分类。 按照数据流的方向不同,可以分为输入流(Input)和输出流(Output)。 按照处理数据单位不同,可以分为字节流(InputStream/OutputStream)和字符流(Readeer/Writer)。 按照实现功能不同,可以分为节点流(低级流: 直接跟输入输出源对接)和处理流(高级流: 建立在低级流的基础上)。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930190502128-265070725.png)
二、File文件操作
1.File类
在 Java 的Java.io 包中有一个类叫File, 一个File 磁盘上的文件或者目录, 可以通过调用File的方法。创建、 删除、 隐藏文件等,File 对象主要用于获取文件的一些信息,如获取文件路径、 文件长度等,数据流可以将数据写入到文件中, 而文件也是数据流最常用的数据媒体。
2.File类的使用
文件是磁盘中的一部分,如果要使用 File 类进行文件的创建, 必须要掌握这个类中一些常用方法,在Java的API文档中可以查看到这些方法,File类的常用方法:
![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930190945265-1384659713.png)
![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930191016251-1867647728.png)
三、什么是字节流
通过前面流的学习,File 文件创建,可以通过File 对象完成指定磁盘下的文件创建,文件创建好之后, 就可以通过代码向文件中完成内容的读写, 要向文件中写入内容ꎬ,需要学习流, 流是数据源传输基础ꎮ,流的分类有三种划分方式:按照数据方向划分, 按照数据单位划分,按照流的功能划分。本任务介绍按照数据单位划分的第一个字节流的字节输 出 流 (OutputStream) 和 输 入 流(InputStream)。它们也是节点流, 这两个类是所有字节输出流和字节输入流的父类。InputStream和OutputStream是抽象类,不能用它创建对象,如果要进行数据的传输,必须依靠子类进行对象实例化,数据 输 入 输 出 的 简 单 操 作 步 骤如下: (1)通过Java.io.File 创建文件对象,指定创建文件的路径,调用方法创建文件或者目录。 (2)通过字节流或者字符流的子类进行对象实例化,指定要写的内容的文件路径。 (3)调用流的读/ 写方法,输入流对应读取操作、 输出流对应写入操作。 (4)调用close()方法,关闭流。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930191715987-652849242.png)
1.OutputStream字节输出流
OutputStream 是所有字节输出流的父类,可以通过它的子类进行对象实例化,完成数据的输出,OutputStream 类的常用方法。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930191923775-1602395032.png)
要想完成数据的输出,必须借助OutputStream的子类才能完成对象的实例化, 因为OutputStream是 抽 象 类,没 有 办 法 创 建 对 象。所 以 接 下 来 要 学 习OutputStream 子 类的Java.io.FileOutputStream,FileOutputStream的常用方法。
三个构造方法的使用:
构造方法 1: public FileOutputStream(File file) throws FileNotFoundException{...}进行创建文件传输流的时候,通过创建 File对象,通过File 对象指定文件传输的路径,但是这个“F:/myfoles”路径一定得存在,不然程序会报错Java.io.FileNotFoundException , 找不到指定的文件。 构造方法 2: public FileOutputStream(File file,Boolean append) throws FileNotFoundException{...}还是通过File 对象指定文件传输路径,在创建文件流的时候, 如果磁盘中存在这个文件,根据后面的布尔值类型判断是否在文件后追加内容,如果布尔值为“true”, 就在原有文件后面追加内容,如果布尔值为“false”, 就不追加,但是文件“F:/myfiles”路径也必须存 在,不然会报错。 构造方法 3: public FileOutput Stream(String name) throws FileNotFoundException{...}创建文件传输流,该文件通过文件系统中的路径名name 指定,如果 1.txt 不存在就创建一个,如果存在就覆盖,如果指定路径比较深,比如“F: /myfiles”, 那这个路径就必须存在。 2.InputStream字节输入流 InputStream是所有字节输入流的父类,InputStream 是抽象类,实现了loseable 接口,Closeable接口继承了AutoClose Able接口, 要执行数据的读操作, 要借助InputStream的子类来进行对象实例化,它有个子类是Java.io.FileInputStream,InputStream 和FileInputStream的常用方法。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930194418389-776989556.png)
四、什么是字符流
字符流和字节流一样都可以完成数据的读写操作,因为数据编码的不同, 有了对字符进行高效操作的流对象,本质是基于字节流读取时,去查指定的编码表。 1.字符编码 计算机是以二进制的方式存储数据,所以其实就是一堆数字,有人会有疑问: 那文字是怎么保存的? 计算机在显示文字的时候,其实和图片一样是以像素的形式显示出来的, 从本质上来讲显示文字就是在显示图像, 但是计算机在保存文字的时候并不是保存的图像,在保存文字时,计算机底层会把文字转化为数字, 然后再保存,而读取文件的时候,则过程相反, 计算机会把数字转化为文字,并绘制到屏幕上,计算机把数字转换为字符的过程, 称为“解码“。常不见同编的码字符对应着不同的数字。 2.常见编码 (1)ASCII最早编码,对应英文字母和英文标点。 (2)GB2312/GBK,对应简体中文,GB2312 是 GBK的一个子集, 相同的中文在 GB2312和GBK 中对应的编码相同,GBK相比GB2312 扩展了很多新字符, GBK是Windows 系统的默认编码。 (3)UTF—8, 国际通用编码, 外文、 简体繁体中文都可以使用,但是UTF—8 中的文字和GBK/GB2312中相同文字的编码不同。 (4)Big5, 台湾省简体繁体中文。 (5)ISO—8859—1, 所有西欧字符和西欧标点,由于多种编码规范存在, 所以在文件解读过程中就很容易出现乱码,假设一个文字的对应编码是 12345,在保存文件的时候使用 GB2312 编码方式, 使用Big5 进行解码, 就会得到一个和最初的文字完全不同的一个字, 由于编码和解码的方式不一样,从而产生乱码。1.Writer字符输出流及其子类FileWriter
Writer是字符流的字符输出流,用于数据的输出, 和OutputStream 的功能一样,主要用来操作文本文件的数据传输,它是一个抽象, 只能通过子类 FileWriter来进行对象实例化,Writer 类和FileWriter 类的常用方法。 三种构造方法的使用:
构造方法 1:public FileWriter(File file) throws IOException{...}方法是通过File 对象指定文件传输的路径,这个路径必须存在,如果不存在, 程序会出错,找不到文件。
构造方法 2: public FileWriter(File file,boolean append ) throws OException{...}方法还是通过File 指定文件输出路径,通过布尔值去判断这个文件存在, 是否在原文件后面追加内容,如果值为“true”就追加, 如果为“false”就不追加, 这个路径也必须存在, 否则程序会报错。 构造方法 3: public FileWriter(String fileName) throws IOException{...}创建字符输出流,通过文件系统中的路径名 name 指定路径, 如果这里指定的路径比较深, 还有目录, 比如“F:/myfiles", 那这个路径就必须存在。 2.字符输入流Reader及其子类FileReader Reader类是字符输入流,也是一个抽象类,不能创建对象,也需要通过它的子类FileReader 来完成对象实例化,通过FileReader 完成文件的读取,Reader 和FileReader 的常用方法。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930200511164-1814039933.png)
3.转换流InputStreamReader和OutputStreamWriter
字符输出流 Winder和字符输入流Reader是字符输出流和字符输入流的父类,由于它们是抽象类。 没有办法创建对象, 所以我们学习了它们的子类 FileWriter 和FileReader, 但是由于编码方式不同,所以在读写文件的时候难免出现乱码的情况,用字节数组读取字节文件,比如文本文件中是字符串时, 读取就会出现问题, 因为FileOutputStream /FileInputStream没有办法指定编解码方式,所以使用字符输出流读取文本文件更加方便。
InputStreamReader和OutputStreamWriter是字节流通向字符流的桥梁, 它们可以根据指定的编码方式, 将字节输入流转化为字符输入流, OutputStreamWriter 和InputStreamReader的常用构造方法。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930201133488-1096098262.png)
五、字节流与字符流的区别
在Java 中对于流的处理,虽然分成了字节流和字符流,但是计算机在传输数据的时候操作的依然是字节数据(所有文件: 比如图片、 视频、 音频等)ꎮ 字符数据是传输到缓冲区中进行处理过后得到的内容。 字节输出流数据操作流程。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930201329802-1391396055.png)
字符输出数据操作流程
六、过滤流
基本输入流提供的读取方法,只能用来读取字节或字符,而过滤流能够读取整数值、 双精度值或字符串。 DateOutputStream和DateInputStream 类是FilterOutputStream 类的子类, 它们分别实现了DateOutputStream和DateInput接口ꎬ 在这两个接口中定义了独立于具体机器的带有格式的读写操作。因此, DateOutputStream 和 DateInputStream 类可以对不同的基本数据类型进行读写操作。 例如: 从文件中读取数据,可以先创建一个FilterInputStream 类的对象,然后把该对象传给 DateInputStream的构造方法。FilterInputStream fi = new FilterInputStream("JZLG.txt" ); DateInputStream di = new DateInputStream(fi); inti = di.readInt(); // 读取整型数据 di.close();同理,把数据写入文件,可以先创建一个 FilterOutputStream类的对象, 然后把该对象传给DateOutputStream的构造方法。
FilterInputStream fo = new FilterInputStream("JZLG.txt" ); DateInputStream do = new DateInputStream(fo); ds.writeBytes("ZNXY"); //写入字符串 ds.close();
七、RandomAccessFile
RandomAccessFile流是 IO中功能比较强大的一个流,它既支持数据的读取,又支持数据的写操作,它可以到文件的任意位置去读写数据,可以任意控制文件指针,所以如果只想访问文件的某一部分内容, 或者在文件中追加内容, 就可以使用
RandomAccessFile流,RandomAccessFile 类包含了一个记录指针, 用以标识当前读写处的位置,当程序新创建一个RandomAccessFile对象时,该对象的文件记录指针位于文件头(也就是 0 处),当读/ 写了 n 个字节后, 文件记录指针将会向后移动 n 个字节,除此之外,RandomAccessFile 可以自由地移动记录指针,既可以向前移动,也可以向后移动,RandomAccessFile 包含了以下两个方法来操作文件的记录指针。
(1)longgetFilePointer(), 返回文件记录指针的当前位置。(2)void seek(long pos), 将文件记录指针定位到 pos位置。
RandomAccessFile类有两个构造方法,同样参数可以是File 对象,也可以是 String参数来指定文件名,除此之外,创建 RandomAccessFile 对象还需要指定一个mode参数, 该参数指定 RandomAccessFile 的访问模式, 有以下 4 个值。 (1)“r” 以只读方式来打开指定文件夹,如果试图对该RandomAccessFile执行写入方法,都将抛出IOException异常。 (2)“rw” 以读写方式打开指定文件,如果该文件尚不存在,则试图创建该文件。 (3)“rws” 以读写方式打开指定文件,相对于“rw” 模式,还要求对文件内容或原数据的每个更新都同步写入到底层设备。 (4)“rwd” 以读写方式打开指定文件,相对于“rw” 模式, 还要求对文件内容每个更新都同步写入到底层设备。RandomAccessFile 类可以完成读写操作,所以它既包含 OutputStream的三个写方法,又包含Input Stream的三个读方法,也有自己的一些读写方法,RandomAccessFile的常用方法。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930203448381-703754541.png)
八、对象序列化和反序列化
1.对象序列化和反序列化
字节流和字符流都可以写字节、 字符类型的数据ꎮ 但是在Java中除了 8 种基本数据类型以外,还可以通过对象来存储数据,除了RandomAccessFile 可以写对象以外,其他都没有办法读写对象类型的数据,但是对象序列化和反序列化刚好可以满足我们的要求。 Java中对象类型的数据,只在 JVM还在运行的时候存在,如果关闭 JVM, 数据就会消失, 但是往往有些数据相对重要,需要就算JVM停止工作, 数据也不会消失,这样再次运行JVM也能重新加载到数据,实现对象的复用, 对象序列化和反序列化就可以完成这个需求, 对象序列化是通过ObjectOutputStream完成,反序列化是通过ObjectInputStream类来完成的。 序列化(Serialization)是将对象的状态信息转化为可以存储或者传输的形式的过程, 一般将一个对象存储到一个储存媒介,在网络传输过程中,可以是字节或者XML等格式,而字节或者XML 格式的可以还原成完全相等的对象, 反序列化就是把序列化的对象数据从一个存储文件或者设备上读取出来,以对象的形式展示出来。 数据在传输过程中都是以字节形式存在,当读取的时候才会转换成我们想要的类型, 对象序列化也是一样,把对象转化为字节形式,当接收到数据的时候, 再通过其他方式转化为对象展示出来,所以序列化机制会把内存中的 Java对象转换成与平台无关的二进制流, 从而永久地保存在磁盘上或是通过网络传输到另一个网络节点。 什么情况下需要序列化 : (1)想把内存中的对象保存到一个文件中或者数据库中的时候。 (2)想用序列化在网络上传送对象的时候。 (3)想通过 RMI(remotemethod invocation)传输对象的时候。 RMI: 远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制,使用这种机制,某一台计算机上的对象可以调用另外一台计算机上的对象来获取远程数据,RMI是 EnterPriseJavaBeans 的支柱,是建立分布式java 应用程序的方便途径。 2.实现Serializable接口 ObjectInputStream和ObjectOutputStream 类,是InputStream 和OutputStream的子类, 除了具备这两个类的读写功能外,它还和过滤流非常相似,也可以接受字节流参数,可以实现8种基本类型和String的读写操作,所以它也可以代替过滤流,但是在写操作对象类型的时候, 并不是所有的对象都可以序列化,要想实现对象序列化,需要遵循一定的规则,ObjectInputStream和ObjectOutputStream 的常用方法。![](/i/l/?n=22&i=blog/2968872/202209/2968872-20220930204615826-1689132314.png)
九、文件拷贝
1.任务描述
本任务主要实现不同文件夹的拷贝,在我们的磁盘上,除了各种类型的文件(图片,音频, 视频, 文本... )。 就是文件夹,我们可以利用流来读写文件内容, 这个代码过程非常简单,但是我们的任务主要是把一个磁盘路径下的文件拷贝到另一个磁盘路径下,把文件夹整个拷贝到另一个文件夹,希望大家通过练习对File 对象,流的操作更加熟悉。2.任务实现步骤
(1)打开 Eclips。 (2)点击“File”→“New”→“Java Project”, 新建一个模块, 模块名字自定义。 (3)点击新建的模块,在 “src” 文件 上点击右键 “New” → “Package”,新建一个包com.demo(包名可以自定义)。 (4)点击新建的包com.demo, “New”→“Class”, 新建一个类FileKB,用于提供拷贝文件的方法。class FileKB{ FilelnputStream fis = null; BufferedlnputStream bis= null; FileOutputStream fos = null; BufferedOutputStream bos = null; public void kaoWenJian( String strl, String str2){ //源文件路径。 File filel = new File(strl); //目标文件路径。 File file2 = new File( str2); //获取原文件的所有文件。 File [] files =filel. listFiles(); //遍历原文件。 for( int i=0;i<files. length;i++) //判断是否是文件,是文件就复制文件 if( files[i]. isFile())! File ywjUrl= files[i]; File mbUrl = new File (file2.toString()+File.separator+files [i]. getName()); //调用拷贝文件方法copyFiles(File filel, File file2); copyFiles ( ywjUrl, mbUrl); } //如果是目录就调用拷贝目录的方法copyDir(File filel, File file2); if( files[i]. isDirectory()) copyDir( files[i] , new File( file2, files[i]. getName())); } } } //拷贝文件 public void copyFiles(File ywjUrl, File mbUrl) { try{ //创建字节输入流和缓冲流。 fis = new FilelnputStream(ywjUrl); bis = new BufferedlnputStream(fis); //创建字节输出流和缓冲流。 fos =new FileOutputStream (mbUd); bos =new BufferedOutputStream (fos); byte[]bs =new byte[1024] ; while(true){ int in =bis. read(bs); if(in==-1) break; //把读到的文件写道到目标文件中。 bos. write(bs, 0, in); } }catch (Exception e){ // TODO Auto-generated catch block e. printStackTrace(); }finally{ try{ //刷新缓冲流。 bos. flush(); //关闭流。 bis, close(); bos. close(); }catch (IOException e){ // TODO Auto-generated catch block e. printStackTrace(); } } } //拷贝目录 public void copyDir( File oldUrl, File newUrl){ //判断在目标文件中有没有img文件夹,没有就创建。 if(! newUrl. exists()){ newUrl. mkdirs(); } //获取原文件的所有文件。 File [] files = oldUrl. listFiles(); //遍历源文件。 for( int i=0;i<files. length; i++){ //如果是文件就拷贝 if (files [i]. isFile ()){ copyFiles ( files[i] , new File (newUrl, files[i]. getName ())); //如果是目录,递归调用copyDir(),目的是找到文件中的子文件和目录。 }else if( files [i]. isDirectory()){ copyDir(files [i] , new File(newUrl, files[i]. getName ())) ; } } } }
(5)主函数,程序运行的入口类。
public class TestKBFile{ public static void main (String[] args){ // TODO Auto-generated method stub String strl ="F:/myfiles" ; String str2="E:/img" ; FilekB filekb = new FilekB(); filekb. ma(strl, str2); } }
标签:文件,字节,字符,对象,编程,File,拷贝,序列化 From: https://www.cnblogs.com/ShiFang0406/p/16745918.html