JAVA高级编程基础自学笔记---文件/IO流

教学视频:

         https://edu.51cto.com/course/5667.html?source=so

         https://edu.csdn.net/course/play/24612/275043?spm=1002.2009.3001.4024

目录

文件/IO流(文件操作流)

内存流(内存操作流使用不多)

打印流

System类对IO的支持(输出:system.out.print(),输入:system.in)

IO高级应用(重点是Scanner的应用)

对象序列化(序列化与反序列化)

 

文件/IO流

IP操作类的核心五个类:

文件操作类:File

流操作类    :IntputStream、OutputStream、Reader、Writer

一个接口    : Serializable

 

一、File文件

File类是唯一一个与文件本身操作有关的类,即通过File可以实现文件的创建、删除、列表等操作;

java.io.File类用于描述文件和目录路径和信息,可以获取文件名称、大小等属性信息   (一般只对文件操作,不能对文件内容操作)

例子:操作File

File考虑路径的时候要注意,"\"要“\\”,如:e:\\这里要双斜杠

如:

这个要是放到linux下执行回报错,这是windows的分隔符

使用windows和linux下的操作通用写法加上:File.separator代表斜杠

问题二

必须存在目录后才可以创建文件;

exists()这个方法是确认文件是否存在;

mkdir这个方法是适合只有一个父类时,如:e:\\test.txt,如果该目录下存在多级文件夹,那么就用mkdirs方法,如:e:\\hello\\nihao\\test.txt,代码如下:

日后如果要进行文件操作,必须要确保文件的存在;

 

直接看代码例子2:路径下有个图片约16.1M

结果如下:

对于文件大小这样子我们如法读懂,我们需要转换下格式

double放哪个位置都可以,可这样输出结果:这样还是读不懂,我们需要保留两位小数需要这样改写(先记住):

输出的结果就是:

我们再来看看加条该图片修写的日期

输出的结果:这样也看不是很清楚,就需要改写下:

输出结果就显然易见咯:

 

扩展其他知识:

 

以上的操作方法适当了解即可,不用特意去记,查API,

需要记的方法操作有:判断父目录、判断文件夹、判断文件是否存在、删除;

 

列出e盘下的(app文件夹里的所有文件,一有文件夹就打开读取、这里采用递归方法)

运行中会出现空指针,这是因为e盘下可以会有系统的隐藏文件夹、这些文件夹是加密不能打开,所有程序运行时会报错

这需要修改下,再次运行后就不会出错了:

需要记的方法操作有:判断父目录、判断文件夹、判断文件是否存在、删除;

 

二、IO流

I/O:就是Inout+Output的缩写,就是输入输出的意思;

流:像流水一样不间断的读写状态;

一、按照数据读写的基本单位不同分为:字节流+字符流

1.字节流以字节为单位进行读写(硬盘上的文件都是字节为单位),所以可以出来任意类型的文件;

2.字符流以字符(char 2个字节,就是两个字节两个字节的读)为单位进行读写的流,只能处理文本文件,比如记事本里面的都是文字可以处理(把电影放进来不行,路径可以)

为了处理汉字(文本文件),用字节流处理汉字,一次性读半个汉字,字符流一次性处理一个汉字

 

二、按数据流动方向不同:输入流+输出流

站在程序的角度:

1.输入流:把数据输入程序中(数据存在硬盘上,程序运行在内存上)---读文件

2.输出流:把程序中的数据输出到硬盘上--写文件

 

三、按照:字节流+包装流

1.字节流:流直接跟文件接触

2.包装流:流间接跟文件接触

FileOutputStream类(重中之重)

文件输出流:

基本概念:

java.io.FileOutputStream类主要用于将图像数据之类的原始字节流写入输出流中;

编程练习:使用FileOutputStream类实现将字符串“hello”写入到文件a.txt中。(输出就是写入)

常用方法:略!自己查文档

FileIutputStream类(重中之重)

基本概念:

java.io.FileIutputStream类主要用于从输入流中读取图像数据之类的原始字节流;

常用方法:略!

BufferedWriter类

java.io.BufferedWriter类主要用于向输出流中写入单个字符、字符数字以及字符串;

(写入字节找FileOutputStream、写入字符找BufferedWriter

常用方法:略!

BufferedRead类

java.io.BufferedRead类主要用于向输入流中读取单个字符、字符数字以及一行字符串内容;

常用方法:略!

PrintStream类(重点)

java.io.PrintStream类主要终于方便地打印各种数据内容并且自动刷新;

常用方法:略!

ObjectOutputStream类(重点)

对象输出流:是以对象为单位(把对象看成一个整体)写入文件中

java.io.ObjectOutputStream类主要用于将java语言的对象整体写入输出流中

只能支持java.io.Serializable接口的对象写入流中

类通过实现java.io.Serializable接口以启用其序列化功能

(序列化是指将一个对象相关的所有信息有效组织成字节序列的转化过程)

例如:把这个小人图片看做一个对象,

打包成字节文件(也就是我们说的图片,.png格式)--这就是序列化过程

也就是把ObjectOutputStream实现Serializable这个接口,这个接口什么功能都有(比如图片线条的位置、形状、颜色等)

ObjectIutputStream类

java.io.ObjectIutputStream类主要用于从输入流中一次性将一个对象的内容读取出来

实现了从字节序列到对象的转化过程,叫反序列化

常用方法:略!

 

多个对象的写入和读取经验:

  一般最傻的方式是写或读都去判断末尾内容是否到了最后 ,比较麻烦。

  当需要写入多个对象到文件中时,建议先将多个对象放入一个集合对象中,然后将集合对象看做一个整体只需要调用以此wreteObject方法就可以吸入所以内容,此时只需要调用以此readObject方法就可以将所以内容读取出来,这样就避免了通过返回值判断是否读取到文件的末尾。

   这只是一种方式,建议没有特殊要求就用这种方式;

Transient关键字

使用transient关键字修饰成员变量表示该成员不参与序列化操作

private transient String phone;//用于描述手机号码

 

 

 

如果要进行文件内容操作必须依靠数据流,数据流分为两种流:

1.字节流:IntputStream(字节输入流)、OutputStream(字节输出流)

2.字符流:Reader(字符输入流)、Writer(字符输出流)

 

字节流和字符流的区别

这两种的区别就好比数据的BLOB与CLOB的区别?

首先要明确一点,通过任何终端(网络、文件)读取或者输出的数据都一定是字节(可以直接存储在磁盘上),但是字符经过内存处理后的数据(才能存到磁盘上)。

  字符输入:字节(磁盘)>自动转换为>字符(内存);

  字符输出:字符(内存)>自动转换为>字节(磁盘)。

例子:

OutputStream范例代码(在下面),把close()去掉(文件也删除),执行的时候,会新生成文件并写入内容(能直接存储)

Writer范例代码(在下面),把close()去掉(文件也删除),执行的时候,会新生成文件内容为空(不能直接存储)

在字符流输出的时候,所有的内容实际上都只是输出到了缓冲区中(内存)。在使用close()方法会将缓冲区的数据进行输出,如果没有关闭,将无法进行输出,此时可以利用flush()进行强制刷新如:out.flush();

字符使用到了缓冲区,而字节流没有使用缓冲区;

如果处理中文使用字符流,其他任何数据都使用字节流。

 

 

输出流和输入流的区别:

从上面可以知道,无论是字节流还是字符流,输出流执行都是写到文件中去的,输入流执行都是从文件读取内容显示在控制台的。

输出流都是直接输出,输入流是直接读取

 

OutputStream(字节输出流)

字节输出流只要是操作byte数据为主的,首先来观察java.io.OutputStream类定义结构:

画图描述:抽象要斜体表示,最后没有改

 

最早在使用OutputStream类操作的时候还没有Closeable和Flushable,它们在java1.5后才有,所以对于这两个接口基本可以忽略了(出现比较晚),除了close()与flush()之外的两个方法之外,还定义有三个重要的输出:

但是OutpuStream是一个抽象类(1.0就提供了,所以没有这么复杂),那么按照抽象类的基本原则来讲,如果想要取得OutputStream类的实例化对象,那么一定需要依靠子类,如果要进行文件的输出操作则可以使用FileOutputStream。

在这个类中提供有两个常用的构造方法:

 构造方法:覆盖文件

构造方法:追加文件

 

范例:实现文件的输出

执行在这个路径下打开该文件,就可以看到内容:

那么在进行输出也只输出部分内容:

在路径下打开文件,因为2个字节为1个字符,就是5个字,10个字节:

或者还可以使用循环方式进行单个字节的输出,代码如下:

输出的结果:

但是发现每当执行完成之后所以的内容都被覆盖了,所以也可以进行追加操作。

 

范例:追加操作

加上true就是我们的追加操作

结果,:

但是追加是后面追加,我们可以换行

多按几次运行按钮,再次输出结果:

 

IntputStream(字节输入流)

IntputStream可以实现数据的读取操作,在java中IntputStream定义如下:

画图描述:抽象要斜体表示,最后没有改

在IntputStream类中定义有三个数据的读取操作方法:

   |--每次执行此方法将读取单个字节数据(一个一个字节的读取),如果已经读取完成了,最后返回的值是-1

 

   |--读取一组,每次讲数据读取到数据之中,那么会返回一个读取长度的数据,如果没有数据则返回长度为-1,要考虑两种情况:

    1.要读取的内容大于开辟的数组内容,长度就是整个数组的长度(杯子的容积要小于纲的容积,杯子一下装满了,长度就杯子的长度);

     2.要读取的内容小于开辟的数组内容,长度就是权保部最后的内容长度,数组装不满(最后缸一直喝最后只剩一点了,一打水只剩下半碗水了);

 |--每次读取内容到部分字节数组,只允许读取满限制的数组的字节个数,此方法依然会返回读取的数组长度。

IntputStream是一个抽象类,所以要进行文件的读取使用FileIntputStream子类,子类的定义:

    

范例:实现数据的读取

输出:

但是,后面的都是存在很多空格,这是因为1024,杯子大于缸的容量,

需要这么修改,将部分字节内容变为字符串:

这才是对的结果:

IutputStream类中有一个read()方法,这个方法可以实现单个字节数据的读取操作,于是下面利用循环的方式才有此方法实现单个字节数据的读取。

范例:读取单个字节

一个个读取,知道值为-1才知道读取结束。foot ++是因为do……while循环至少循环一次,标识要加1

结果:

以上的do……while循环操作,的确是进行了数据源的读取,但是太麻烦了,所以在实际的开发会利用while循环实现读取操作。

范例:利用while循环修改(日后开发中读取使用最多的循环方法

下面重要步骤要记住:

结果是一样的:

 

Writer(字符输出流)

文件存在磁盘是使用字节,在网络上保存都是使用字节,字符并不是在磁盘上保存的,它是经过处理后才保存的。

Writer是抽象类,是进行字符输出操作的使用的类,java1.1后才出现(字节是1.0就出现),理论上依靠字节就够了,有人觉得不方便才出现字符流,80%不会考虑,什么时候用字符?输出中文,字节也可以,就是比字符麻烦;

之所以会提供一个writer类主要是因为这个类的输出方法有一个特别好用的:

   输出字符串:能直接输出Strinbg,这是最大唯一的好处

Writer要进行文件字符流操作,应该是FileWriter,里面一定有  一个构造方法:

不用查api文档了

范例:使用Writer输出数据

有些逻辑不能动,见红色标记

输出:

虽然Writer类提供有字符数组的输出操作能力,本质上,Writer类就意味着字符串的直接输出,字符流是最适合操作中文的,但并不是意味着字节流就无法操作中文。

 

Reader(字符输入流)

Reader是抽象类,观察定义,没有直接读取String(Writer中有直接输出String),这是为什么?

比如人吃东西,然后拉出来,把拉出来想象的是Writer,吃进去的是Reader,所以不可能提供一下子吃很多,这导致内存会爆掉,要控制输入。

在Reader类中也提供一系列的read()方法

 数据读取:

 

范例:读取数据

结果:

 

三、转换流(非重点)

为此在java中提供两个转换流:

观察这两个类定义的结构以及构造方法:

   或  

 

范例:观察转型(记住)

执行结果:

实际讲解这种转换流目的不在于使用,而是进一步观察类的继承结构,

  

 

保存在磁盘上的数据一定都是字节数据。

总结:

字节流和字符流之间是可以实现互相转换的,字符流一定是在内存中经过处理得来的结果。

 

四、把前面所学的应用:

是模拟dos系统中的copy命令完成。

大案例:编写一个拷贝程序,可以实现任意的文件拷贝操作,通过初始化参数输入拷贝的源文件路径以及拷贝目标文件路径,本程序暂时不考虑类的设计(在main文件写)

   拷贝命令:

如果要想实现这种拷贝的操作,可以有一下两种实现的思路:

1.开辟一个数组,将所需要的拷贝内容读取到数组之中,而后一次性输出到目标文件;

2.采用边读编写方式进行拷贝,不是一次性读取;

第一种的问题在于,如果文件量大小没有问题,5M左右,如果文件量一大,基本上内存就被占满。内存处理

范例:初期实现

做个介绍:

写个拷贝的方法自定义的(就是功能)

初期的实现写完了,接下来拷贝文字!

 在e:\my.txt的文件内容,拷贝到e:\hello.txt

先执行一次,才能配置。右键-->Run As-->Run Configura……如图所示:

结果是:

看下文件是否拷贝

打开hello.txt文件

在试试拷贝图片

看结果实际很慢。

虽然实现了拷贝操作,但是这样的拷贝操作无法忍受。如果每次只拷贝单个字节,这是不可能的事情,利用数组来提升拷贝的性能,可以将数据读取到数组中,然后一次性将数组输出

都是一组一组读,每组8个,就算不满8页可以,读取到最后都是-1,

 

范例:修改拷贝方法

一组组读,满的就是数组长度,不满的读到多少是多少,知道等于-1时结束。

结果:

 

常见字符编码

1.计算机只认识0、1,计算机知识略

乱码产生分析

乱码的本质就在于编码与解码的方式不统一。

 

内存操作流

对于IO操作,目前为止实际上只学习了一种文件流的操作。但是这种操作的特点都是以文件为终端来处理,但是现在开发中如果不希望你产生文件,但依然需要你使用IO操作。那么此时就可以利用内存作为操作流。

内存流分两组:

 1.字节内存流:

 2.字符内存流:

范例:内存流操作

str的内容实现小写操作

内存流也要实行close操作

输出结果:

没有文件产生,发生了IO操作。

范例:两文件合并

结果:

以上操作不能合并大文件;

要大写

 

打印流

 

System类对IO的支持(理解)

数据输出:1.system.out.println()、2.system.err.println(),第2中很少用了,他们的区别是颜色上的区别

数据输入:system.in,这个输入内容大小的不确定性,也没有什么好讲的,Scanner常用

 

IO高级应用

缓冲输入流:BufferedReader

在JDK1.5之前,BufferedReader是一个非常重要的数据读取的操作类,也就是说如果要进行输入流的操作,尤其是文字操作,都建议应该使用BufferedReader类完成,这就是在1.5之前,1.5之后它的地位以及被剥脱了。我们还需要了解它

BufferedReader是Reader的子类,复杂进行缓冲区的读取,由于本身属于字符流,适合输入文字信息。

BufferedReader的继承结构(IO里继承结构很烦人,稍微记下,万一会面试): 

在BufferedReader类中提供两个重要方法:

1.构造方法:

2.数据读取方法:

为什么不用BufferedInputStream,而要用BufferedReader,这是因为BufferedInputStream没有readLine()的方法。

BufferedReader返回String意味着什么,正则验证能使、能向各种类型能转换了吧!String是优先考虑的;

范例:BufferedReader实现数据输入

  输入要使用system.in,而system.in是InputStream类型;

  BufferedReader类的结构需要接受Reader类型,那么要将字节输入流变为字符输入流,使用InputStreamReader

执行:

从这代码可以看出,是不是跟Scanner简直一样啊,所以说BufferedReader都是早期的技术了,我们只需了解下即可,后面几乎都不用它了。

其他的没有太多了解的必要,略!

 

扫描流Scanner(重点知识)

java1.5之后提供一个java.util.Scanner类,称Scanner为扫描流,观察下构造方法:

  构造方法:

当取得Scanner类的实例化对象之后,那么下面可以进行数据读取操作

    

      

Scanner默认最古老方式是利用String来接收数据,但是很多时候内容肯是数据,或者输入的内容还需要我们的验证,那么我们在这种情况下Scanner类发生了一下变化,增加了一推的hasNextXxxx()、nextXxxx()的方法。

  范例:获取键盘输入的数据

运行,输入内容后按回车:

注意:如果以下这么输入,结果:

这是因为Scanner默认情况下会将空格和回车作为读取分割符,有个方法可以设置分割符:

代码如下,设置分割符为回车“\n”:

所以键盘输入空格后不影响结果:

 

Scanner本身支持各种数据类型

范例:设置年龄输入

注意,设置分割符会有个问题,代码如下:

这是因为你输入3的时候,已经带有回车符\n,所以删除分割符那行后就没有问题了

代码如下:

输入非数字试试:

 

范例:要求输入生日

输入错误内容:

输入正常的:

 

以上只是用键盘做了Scanner功能的演示,意义不大;

范例:利用Scanner读取文件

如果println是换行的意思,因为上面有了\n,在换行会多了一个换行而已。

读取文件内容为:

对象序列化(重要)

对象序列化指的是将在内存中保存的对象变为二进制数据流,这就意味着对象可以保存在文件中,或者进行各种传输操作,但是并不是所有的类对象都可以被序列化,如果某一个类的对象需要被序列化,则这个类必须实现java.io.Seralizble接口.但是这个接口没有任何的方法定义,因为其描述的是一种能力,属于标识接口。

什么是序列化?

将对象转换为字节。

什么是反序列化?

将字节转换回对象。

什么时候使用序列化?

当我们想要持久化对象的时候。当我们希望对象存在于JVM的生存期之后。

记住:

序列化对象时,只保存对象的状态,而不保存对象的类文件或方法。

当您序列化一个2字节的对象时,您会看到51个字节的序列化文件。

 

范例:定义可以被序列化定义的类

要记住Seralizble这个单词,其他的没有什么特别

这里出现Person警告,按Ctrl键进行修正,系统提供两种解决方案

这两个是序列版本的UID,这是什么意思呢?它在最早版本只得是你可以在JRE版本使用,意义不大,只是不用这个警告

最常用的是选中下面那种,我们习惯于压制,

就是类似直接添加注解的意思。

以后Person类对象就可以被传输或者保存在文件之中了。

以上的代码除了类的接口implements Serializble外,其他地方没有发生任何的改变,这就是序列化含义所在;

 

序列化操作(利用ObjectOutputStream)

要记住对象序列化不可能有字符流,因为序列化通过二进制传输,二进制都是字节数据,所以依靠这个父类。

范例:序列化对象

 

反序列化利用ObjectInputStream

利用ObjectInputStream输出的文件信息都是二进制数据,但是这个数据大部分看不懂的,所以要将数据还原,那么必须用ObjectInputStream进行反序列化;

范例:反序列化操作

序列化和反序列化都是操作一个文件

输出反序列化内容:

不一定所有的类都有序列化,只有一种百分之百使用序列化操作,那就是简单java类

 

Transient关键字不被序列化(一般不考虑用这个,知道就行)

默认情况下,一个对象的所有竖向都一定要被序列化下来,那么现在如果某些属性不想被序列化,那就使用transient关键字

范例:观察transient关键字的使用

要想不被序列化,就必须加入transient关键字

//

如:

调用验证下:

由于transient关键字作用,所以当数据被反序列化后,此数据为其对应类型的默认值。age=0是默认值

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

测试狂人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值