【JavaSE学习】04-2Java高级(文件处理-IO流)

JavaSE(B站黑马)学习笔记

01Java入门
02数组、方法
03面向对象&Java语法
04-1Java高级(Stream流、异常处理、日志技术)
04-2Java高级(文件处理-IO流)
04-3Java高级(多线程、网络编程)
04-4Java高级(单元测试、反射、注解、动态代理、XML)
05-1常用API
05-2常用API(集合)



前言

JavaSE(B站黑马)学习笔记 04-2Java高级(文件处理-IO流)


04-2Java高级(文件处理-IO流)

文件处理

关于File、IO流,需要学会什么

File

File类概述

  • File类在java.io.File包下,代表操作系统的文件对象(文件、文件夹)。
  • File类提供了诸如:创建文件对象代表文件,获取文件信息(大小、修改时间)、删除文件、创建文件(文件夹)等功能。

创建File对象

File类创建对象

注意

  • File对象可以定位文件和文件夹
  • File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的

绝对路径和相对路径

  • 绝对路径:从盘符开始

  • 相对路径:不带盘符,默认直接到当前工程下的目录寻找文件

相对路径小技巧:相对到工程下可以打印System.getProperty(“user.dir”)(即定位到工程根目录下)查看有没有带工程名称,有的话直接写src即可,没有就要 工程名称/src 如:

File类的常用API

File类的判断文件类型、获取文件信息功能

File类创建文件的功能

File类删除文件的功能

注意

  • delete方法默认只能删除文件和空文件夹,delete方法直接删除不走回收站,文件在占用也直接删(有点猛)

File类的遍历功能(遍历文件夹)

listFiles方法注意事项:

  • 当文件不存在时或者代表文件时,返回null(区分文件 文件夹)
  • 当文件对象代表一个空文件夹时,返回一个长度为0的数组
  • 当文件对象是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
  • 当文件对象是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
  • 当没有权限访问该文件夹时,返回null

方法递归

递归的形式和特点

什么是方法递归?

  • 方法直接调用自己或者间接调用自己的的形式称为方法递归( recursion)。

  • 递归做为一种算法在程序设计语言中广泛应用。

递归的形式

  • 直接递归:方法自己调用自己。
  • 间接递归:方法调用其他方法,其他方法又回调方法自己。

方法递归存在的问题?

  • 递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出现象。

递归的算法流程、核心要素

递归解决问题的思路:

  • 把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

递归算法三要素大体可以总结为:

  • 递归的公式: f(n) = f(n-1) * n;
  • 递归的终结点:f(1)
  • 递归的方向必须走向终结点:

递归的经典案例(猴子吃桃问题)

非规律化递归问题:文件搜索、啤酒问题

文件搜索



啤酒问题

传入初始金额,定义三个静态变量记录 总瓶数 空瓶 盖子,初始金额除以2得到购买的瓶数,累计加上购买的瓶数得到总瓶数,将空瓶和盖子加上当前购买的瓶数得到当前有多少空瓶和盖子。将当前得到空瓶和盖子换算成钱(注意换成钱后要将没换成钱的空瓶和盖子重新放回剩余空瓶和剩余盖子里等待下次换钱)。然后金额大于2就进行递归

IO流前置内容:字符集

常见字符集介绍

字符集基础知识:

  • 计算机底层不可以直接存储字符的,计算机中底层只能存储二进制(0、1)

  • 二进制是可以转化成十进制的

  • 字符集(Character Set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有:

    • ASCII字符集
    • GBK字符集
    • Unicode(UTF-8)字符集等。

结论:计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集

ASCII字符集:

  • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号。
  • ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于表示英文、数字来说是够用的。(英文就26个字母,加上大小写和标点符号也就那么点,一个字节存储一个字符,一个字节是8位二进制,8位二进制总共可以表示2^8位字符,去掉负数总共可以表示128个字符信息,对于表示英文、数字来说是够用的)
  • ASCII详解:菜鸟教程:ASCII 表 | 字符集和字符编码(Charset & Encoding)

GBK:

  • GBK是中国的码表,window中文系统默认的码表。兼容ASCII码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字。但不包含世界上所有国家的文字
  • GBK编码中一个中文字符以两个字节的形式存储。英文还是按照ASCII码用一个字符存储

Unicode码表:

  • unicode(又称统一码、万国码、单一码)是计算机科学领域里的一项业界字符编码标准。
  • 容纳世界上大多数国家的所有常见文字和符号。
  • 由于Unicode会先通过UTF-8,,UTF-16,以及UTF-32的编码成二进制后再存储到计算机,其中最为常见的就是UTF-8

注意

  • Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储
  • UTF-8也要兼容ASCII编码表。
  • 技术人员都应该使用UTF-8的字符集编码。
  • 编码前和编码后的字符集需要一致, 否则会出现中文乱码。(同一个字在GBK编码和UTF-8编码不一致,解析处理对应的字不一样就会导致乱码)

注意字符解码时使用的字符集和编码时使用的字符集必须一致,否则会出现乱码。

汉字存储和展示过程解析

注意:英文和数字在任何国家的编码中都不会乱码

字符集的编码、解码操作

String编码

String解码

IO流(一)

IO流概述

  • IO流也称为输入、输出流,就是用来读写数据的

  • I表示intput,把硬盘文件中的数据读入到内存的过程,称之输入,负责读。

  • O表示output,把内存中的数据写出到硬盘文件的过程,称之输出,负责写。

IO流的分类

总结流的四大类

  • 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。
  • 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。
  • 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。
  • 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流。

字节流的使用

字节输入流

文件字节输入流:FileInputStream

  • 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。

每次读取一个字节

读取一个字节存在问题

  • 性能较慢
  • 读取中文字符输出无法避免乱码问题。

每次读取一个字节数组


注意:当文件内容为ab3abccd,一次读取长度为3的字节数组(就是一次读3个字节),读第3次期待结果应该是 [cd] 但结果是[cdc] 原因有上一数组的残留第三个字节c还在里面 所以是[cdc]。解决办法是用public String(byte bytes[], int offset, int length)构造器,读多少倒读多少 如:

上面效率过低,进行改进,使用循环每次读取一个字节数组

上面我们看到中文也顺利打印了,因为一次打印3个字节,刚好utf-8中一个中文字符用3个字节存储,[ab3] [abc] [我] [爱] [你] [中] [国] [cd] 刚刚好,但是!看下图 加了个d 读取结果变成了 [ab3] [abc] [d???] d后面又加了3个字节,变成 [d我] 里面又变成四个字节,强行拆“我”,还没读完就输出所以后面跟着乱码了。

读取一个字节数组存在问题

  • 读取的性能得到了提升
  • 读取中文字符输出无法避免乱码问题。

读取文件的全部字节

1.如何使用字节输入流读取中文内容输入不乱码呢?

  • 定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。

2.直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?

  • 如果文件过大,字节数组可能引起内存溢出(假设一个文件几十G,内存才16G的话怎么可能一次读完)。

其实字节流并不是处理文本最好的方法,字符流才是,我们只是拿文本文件来了解字节流的使用和其API使用。

方式一

  • 自己定义一个字节数组与文件的大小一样大,然后使用读取字节数组的方法,一次性读取完成。


方式二

  • 官方为字节输入流InputStream提供了如下API可以直接把文件的全部数据读取到一个字节数组中(注意:JDK9开始才有 👊

字节输出流

写字节数据到文件

文件字节输出流:FileOutputStream

  • 作用:以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流。

文件字节输出流(FileOutputStream)写数据出去的API

流的关闭与刷新

因为数据是缓存在内存中,有内存缓冲区,再往文件里写数据。如果不刷新的话数据可能还在缓冲区(还在流里) 没写到文件里,直接把程序干掉了数据就作废了,导致写入数据不成功。刷新就是把缓存在内存的数据赶紧写进文件中,这样就不会丢失,否则容易丢失数据。刷新后流还能继续使用

FileOutputStream管道(流)是一个资源,它的内存和磁盘文件是耦合的,磁盘文件速度比较慢,内存速度快。使用完不关闭会拖累内存速度,所以用完后及时关闭节省内存空间出来,不然拖累cpu性能。注意:关闭包含了刷新,一旦关闭就不能继续使用流了

上面会发现存在一个问题,每次运行都把前面的数据覆盖了,因为我们创建的是覆盖管道,需要追加要换成追加管道

字节输出流如何实现写出去的数据能换行

  • os.write(“\r\n”.getBytes())

如何让写出去的数据能成功生效?

  • flush()刷新数据
  • close()方法是关闭流,关闭包含刷新,关闭后流不可以继续使用了。

文件拷贝



字节流适合做一切文件数据的拷贝吗?

  • 任何文件的底层都是字节,拷贝是一字不漏的转移字节,只要前后文件格式、编码一致没有任何问题。
  • 总体来看字节流适合做数据的拷贝,不适合做读取中文内容输出

资源释放的方式

我们原先对资源释放的方式是直接使用close()方法关闭流,这样存在一个问题,就是代码如果在次之前出现问题就无法关闭。

try-catch-finally

  • finally:在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源
  • 特点:被finally控制的语句最终一定会执行, 除非JVM退出
  • 作用:一般用于进行最后的资源释放操作(专业级做法)

try-catch-finally格式

即使在try的时候return了也拽回来执行finally(很猛)

try-with-resource

  1. finally虽然可以用于释放资源,但是释放资源的代码过于繁琐?

  2. 有没有办法简化?

JDK 7和JDK9中都简化了资源释放操作

注意

  • JDK 7 以及 JDK 9的try()中只能放置资源对象,否则报错
  • 什么是资源呢?
  • 资源都是实现了Closeable/AutoCloseable接口的类对象

建议使用JDK7的方式

字符流的使用

字符输入流

  • 字节流读取中文输出会存在乱码,或者内存溢出的问题。
  • 读取中文输出,字符流更合适,最小单位是按照单个字符读取的。
  • 字节流和字符流结合来看,有些相同的内容字节流那有更详细的解释

文件字符输入流:Reader

  • 作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去。

一次读取一个字符

字符流的好处。每次读取一个字符存在什么问题?

  • 读取中文字符不会出现乱码(如果代码文件编码一致
  • 性能较慢(一个一个读)

一次读取一个字符数组

每次读取一个字符数组的优势?

  • 读取的性能得到了提升
  • 读取中文字符输出不会乱码。

提示:读多少倒多少和上面一次读一个字节数组很像,上面有详细解释,只是把字节换成字符了

字符输出流

文件字符输出流:FileWriter

  • 作用:以内存为基准,把内存中的数据以字符的形式写出到磁盘文件中去的流。

文件字符输出流(FileWriter)写数据出去的API

流的关闭与刷新

关闭和刷新上面字节流更详细,一样的意思

字符输出流如何实现写出去的数据能换行

  • fw.write(“\r\n”) (比字节流方便,字符流直接接受字符不用编码)

如何让写出去的数据能成功生效?

  • flush()刷新数据
  • close()方法是关闭流,关闭包含刷新,关闭后流不可以继续使用了。

字节流、字符流的使用场景总结

  • 字节流适合做一切文件数据的拷贝(音视频,文本)
  • 字节流不适合读取中文内容输出
  • 字符流适合做文本文件的操作(读,写)

IO流(二)

关于IO流(二)需要学会什么

缓冲流

缓冲流概述

  • 缓冲流也称为高效流、或者高级流。之前学习的字节流可以称为原始流。
  • 作用:缓冲流自带缓冲区、可以提高原始字节流、字符流读写数据的性能

(简单来说就像家里楼顶的水箱,没有水箱单靠水管的话水流特别小还慢,有水箱就能装更多水,流的跟快,直接从水箱拿水)

字节缓冲流

跟之前字节流的用法(功能)一样,该怎么写怎么写 该怎么读怎么读 主要是创建对象不同

字节缓冲流性能优化原理:

  • 字节缓冲输入流自带了8KB缓冲池,以后我们直接从缓冲池读取数据,所以性能较好。
  • 字节缓冲输出流自带了8KB缓冲池,数据就直接写入到缓冲池中去,写数据性能极高了。

构造器

  • 字节缓冲输入流:BufferedInputStream,提高字节输入流读取数据的性能,继承自字节输入流,功能上并无变化
  • 字节缓冲输出流:BufferedOutputStream,提高字节输出流读取数据的性能,继承自字节输出流,功能上并无变化
  • 将原始的字节流包装成字节缓冲流

字节缓冲流的性能分析

推荐使用哪种方式提高字节流读写数据的性能?

  • 建议使用字节缓冲输入流、字节缓冲输出流,结合字节数组的方式,目前来看是性能最优的组合。

字符缓冲流

字符缓冲输入流

  • 字符缓冲输入流:BufferedReader。
  • 作用:提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能。

字符缓冲输入流新增功能

字符缓冲输出流

  • 字符缓冲输出流:BufferedWriter。
  • 作用:提高字符输出流写取数据的性能,除此之外多了换行功能

字符缓冲输出流新增功能

注意:做追加还是要在原始管道写true,缓冲流只是提高原始流的性能

字符缓冲流为什么提高了操作数据的性能?

  • 字符缓冲流自带8K缓冲区
  • 可以提高原始字符流读写数据的性能

案例



转换流

问题引出:不同编码读取乱码问题

1、之前我们使用字符流读取中文是否有乱码?

  • 没有的,因为代码编码和文件编码都是UTF-8。

2、如果代码编码和文件编码不一致,使用字符流直接读取还能不乱码吗?

  • 会乱码。
  • 文件编码和读取的编码必须一致才不会乱码。

代码是utf-8 文件是utf-8 不会乱码

代码是utf-8 文件是GBK 会乱码

小tips:一般创建文本文件都是utf-8的,转成GBK编码方法如下:文件另存为ANSI编码(标准编码)相当于GBK编码。

字符输入转换流

如何解决代码编码和文件编码不一致导致读取乱码呢?

  • 使用字符输入转换流
  • 可以提取文件(GBK)的原始字节流,原始字节不会存在问题。,然后把字节流以指定编码转换成 字符输入流,这样字符输入流中的字符就不乱码了(字符转换输入流的原理)

字符输入转换流

  • 字符输入转换流:InputStreamReader,可以把原始的字节流按照指定编码转换成字符输入流。

字符输出转换流

如果需要控制写出去的字符使用的编码,怎么办?

  • 可以把字符以指定编码获取字节后再使用字节输出流写出去:“我爱你中国”.getBytes(编码)
  • 也可以使用字符输出转换流实现。

字符输出转换流

  • 字符输入转换流:OutputStreamWriter,可以把字节输出流按照指定编码转换成字符输出流。

序列化对象

对象序列化

  • 作用:以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化。
  • 使用到的流是对象字节输出流:ObjectOutputStream

对象序列化:

  • 使用到的流是对象字节输出流:ObjectOutputStream

ObjectOutputStream序列化方法

注意:直接序列化存储对象会报错。对象要序列化,它的类必须实现Serializable序列化接口。进入Serializable源码会发现里面什么也没有,那为什么还要实现这个接口呢?它的思想是-既然这个对象要存到文件中,那存在文件中就要有规矩,不能瞎存。存成什么样子的格式是由Java定的,Java事先就要规定对象存的格式。Java就规定 如果这个对象要通过序列化的方式存进文件中就要实现Serializable接口,当对这个对象进行序列化(真正存这个对象)的时候,Java虚拟机就知道这个对象是要保存到文件,虚拟机就会根据这个信号触发虚拟机里一些提取装载好的程序,它会帮我们把对象内的数据解析出来再存到文件中。(简单来说就是通知虚拟机以既定的方式把对象以序列化的方式进行存储,把实现了这个接口的对象当作一种信号)

序列化存完后打开文件会发现文件跟乱码一样,这并不是乱码,这个文件并不是给人看的,它的目的是要给计算机看,让对象数据在下次能够恢复

细节:有时不希望对全部内容序列化,例如密码,为防止它人截取到文件产生隐患,希望该字段不参与序列化,可在该字段前添加 ,这样该字段就不会参与序列化,当反序列化恢复时该字段就会为null

对象反序列化

对象反序列化:

  • 使用到的流是对象字节输入流:ObjectInputStream
  • 作用:以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象,称为对象反序列化。

ObjectInputStream序列化方法

打印流

PrintStream、PrintWriter

打印流

  • 作用:打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类。
  • 可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。

PrintStream

PrintWriter

PrintStream和PrintWriter的区别

  • 打印数据功能上是一模一样的,都是使用方便,性能高效(核心优势)
  • PrintStream继承自字节输出流OutputStream,支持写字节数据的方法。
  • PrintWriter继承自字符输出流Writer,支持写字符数据出去。

PrintStream的wirter方法只支持写字节,PrintWriter的writer方法支持写字符

输出语句的重定向(了解,没啥用)

  • 属于打印流的一种应用,可以把输出语句的打印位置改到文件。(就是把输出控制台语句直接打进文件)

补充知识:Properties

为后面学习框架知识做准备,以后我们程序中会有一个.properties结尾的属性文件
image-20230324114852239

怎么用程序生成这个属性文件、怎么读取这个属性文件里面的内容,就是下面学习内容

Properties属性集对象

  • 其实就是一个Map集合,但是我们一般不会当集合使用,因为HashMap更好用。虽然Properties属于Map集合家族的,但它是一个另类的存在。

Properties核心作用:

  • Properties代表的是一个属性文件,可以把自己对象中的键值对信息存入到一个属性文件中去。
  • 属性文件:后缀是.properties结尾的文件,里面的内容都是 key=value,后续做系统配置信息的。

Properties的API:

  • Properties和IO流结合的方法:


补充知识: IO框架

commons-io概述

  • commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率。
  • commons-io工具包提供了很多有关io操作的类。有两个主要的类FileUtils, IOUtils
  • commons-io官网:https://commons.apache.org/proper/commons-io/

commons-io jar包下载:打开上方网址选择版本

下载后的压缩包解压后主要用它的核心jar包,如图

进入docs文件夹后打开index.html可以看到api文档,主要使用的FileUtils, IOUtils,里面有很多功能都包装好成静态方法,直接拿来用就行,这里只简单介绍几个的,不用刻意去记,后续了解其它方法需要用到某些功能可以查看文档

FileUtils主要有如下方法:



注:

该内容是根据B站黑马程序员学习时所记,相关资料可在B站查询:Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.hexiang.utils; import java.io.*; /** * FileUtil. Simple file operation class. * * @author BeanSoft * */ public class FileUtil { /** * The buffer. */ protected static byte buf[] = new byte[1024]; /** * Read content from local file. FIXME How to judge UTF-8 and GBK, the * correct code should be: FileReader fr = new FileReader(new * InputStreamReader(fileName, "ENCODING")); Might let the user select the * encoding would be a better idea. While reading UTF-8 files, the content * is bad when saved out. * * @param fileName - * local file name to read * @return * @throws Exception */ public static String readFileAsString(String fileName) throws Exception { String content = new String(readFileBinary(fileName)); return content; } /** * 读取文件并返回为给定字符集的字符串. * @param fileName * @param encoding * @return * @throws Exception */ public static String readFileAsString(String fileName, String encoding) throws Exception { String content = new String(readFileBinary(fileName), encoding); return content; } /** * 读取文件并返回为给定字符集的字符串. * @param fileName * @param encoding * @return * @throws Exception */ public static String readFileAsString(InputStream in) throws Exception { String content = new String(readFileBinary(in)); return content; } /** * Read content from local file to binary byte array. * * @param fileName - * local file name to read * @return * @throws Exception */ public static byte[] readFileBinary(String fileName) throws Exception { FileInputStream fin = new FileInputStream(fileName); return readFileBinary(fin); } /** * 从输入读取数据为二进制字节数组. * @param streamIn * @return * @throws IOException */ public static byte[] readFileBinary(InputStream streamIn) throws IOException { BufferedInputStream in = new BufferedInputStream(streamIn); ByteArrayOutputStream out = new ByteArrayOutputStream(10240); int len; while ((len
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值