开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
File类
- java.io.File类:文件和文件目录路径的抽象表示形式
- File不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流
- 想要在Java中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录
路径分隔符:
- windows和DOS系统默认使用“ \ ”来表示
- UNIX和URL使用“ / ”来表示
构造方法
-
public File(String pathname):可以是绝对路径或者相对路径- main()方法是相对于工程
- 测试方法则是相对于当前module
-
public File(String parent,String child):以parent为父路径,child为子路径创建File对象 -
public File(File parent,String child):根据一个父File对象和子文件路径创建File对象
常用方法
获取:
public String getAbsolutePath():获取绝对路径public String getPath():获取路径public String getName():获取名称public String getParent():获取上层文件目录路径。若无,返回nullpublic long length():获取文件长度(即:字节数)。不能获取目录的长度public long lastModified():获取最后一次的修改时间,毫秒值public String[] list():获取指定目录下的所有文件或者文件目录的名称数组public File[] listFiles():获取指定目录下的所有文件或者文件目录的File数组(绝对路径)
重命名:
public boolean renameTo(File dest):把文件重命名为指定的文件路径,dest不能在硬盘中
判断:
public boolean isDirectory():判断是否是文件目录public boolean isFile():判断是否是文件public boolean exists():判断是否在硬盘中存在public boolean canRead():判断是否可读public boolean canWrite():判断是否可写public boolean isHidden():判断是否隐藏
创建:
public boolean createNewFile():创建文件。若文件存在,则不创建,返回falsepublic boolean mkdir():创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建public boolean mkdirs():创建文件目录。如果上层文件目录不存在,一并创建
删除:
public boolean delete():删除文件或者文件夹,要删除一个文件目录,该文件目录内不能包含文件或者文件目录
知识加油站
字符集和字符编码
字符集是很多个字符的集合,例如 GB2312 是简体中文的字符集,它收录了六千多个常用的简体汉字及一些符号,数字,拼音等字符。
字符编码是字符集的一种实现方式,把字符集中的字符映射为特定的字节或字节序列,它是一种规则。
比如:Unicode 只是字符集,UTF-8、UTF-16、UTF-32 才是真正的字符编码规则
Unicode
Unicode 是国际标准字符集,它将世界各种语言的每个字符定义一个唯一的编码,以满足跨语言、跨平台的文本信息转换。
Unicode 字符集的编码范围是 0x0000 - 0x10FFFF , 可以容纳一百多万个字符, 每个字符都有一个独一无二的编码,也即每个字符都有一个二进制数值和它对应,这里的二进制数值也叫码点 , 比如:汉字 "中" 的 码点是 0x4E2D, 大写字母 A 的码点是 0x41, 具体字符对应的 Unicode 编码可以查询 Unicode字符编码表。
Unicode 字符存储
Unicode 是一个符号集, 它只规定了每个符号的二进制值,但是符号具体如何存储它并没有规定。
由于编码范围是 0x0000 - 0x10FFFF,因此需要 1 到 3 个字节来表示。
那么,对于三个字节的 Unicode字符,计算机怎么知道它表示的是一个字符而不是三个字符呢 ?
如果所有字符都用三个字节表示,那么对于那些一个字节就能表示的字符来说,有两个字节是无意义的,对于存储来说,这是极大的浪费。
因此,Unicode 出现了多种存储方式,常见的有 UTF-8、UTF-16、UTF-32,它们分别用不同的二进制格式来表示 Unicode 字符。UTF-8、UTF-16、UTF-32 中的 "UTF" 是 "Unicode Transformation Format" 的缩写,意思是"Unicode 转换格式",后面的数字表明至少使用多少个bit来存储字符, 比如:UTF-8 最少需要8个bit也就是一个字节来存储,对应的, UTF-16 和 UTF-32 分别需要最少2个字节和4个字节来存储。
UTF-8
UTF-8: 是一种变长字符编码,将码点编码为 1 至 4 个字节,具体取决于码点数值中有效二进制位的数量
编码规则
-
对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的, 所以 UTF-8 能兼容 ASCII 编码,这也是互联网普遍采用 UTF-8 的原因之一。
-
对于 n 字节的符号( n > 1),第一个字节的前 n 位都设为 1,第 n + 1 位设为 0,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位(x),全部为这个符号的 Unicode 码
下表是Unicode编码对应UTF-8需要的字节数量以及编码格式:
例如:查询 "中" 的UTF-8编码
- 先查询 "中" 字的 Unicode 码 0x4E2D,对应范围的第三种,需要三个字节
- 0x4E2D对应的二进制为 0100 1110 0010 1101
- 然后从二进制的最后一位开始,从后向前依次填入格式中的x
- 得到 ”中“的UTF-8编码为11100100 10111000 10101101
更多见“编码”文章。。。。。。
IO流
流的分类:
-
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
- 对于文本文件(.txt,.java,.c),一般使用字符流处理
- 对于非文本文件(.doc,.jpg,.mp3),使用字节流处理
-
按数据流的流向不同分为:输入流,输出流
-
按流的角色的不同分为:节点流(文件流),处理流
IO流体系:
操作步骤:
- 实例化File类
- 实例化流
- 读入 / 写出操作(以下就是针对不同的操作进行说明)
- 流的关闭
节点流
FileReader & FileWriter(字符流)
-
Java中字符是采用Unicode字符集
-
非文本文件不能用字符流,原因如下:
- java通过字符流读写文件,默认是按照UTF-8读写的
- UTF-8的编码格式有4种,当文件中的字节格式不符合时(详情见知识加油站),字符流会将其转换为FFFD (unicode码点),然后再将FFFD转换为UTF-8,导致文件读取的有问题。
- 简单来说就是字符流想读取一个字符,然后发现不认识这个字符,就会转为FFFD。
- 程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以要显式关闭文件IO资源
常用方法:
int read():读取单个字符。返回范围在 0 到 65535 之间,如果已到达流的末尾,则返回 -1int read(char[] cbuf):读取多个字符,并将字符覆盖cbuf数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。
输出操作,对应的File可以不存在。
-
如果不存在,会自动创建
-
如果存在:
FileWriter(file, false)/FileWriter(file):覆盖原有的文件FileWriter(file, ture):在原有的文件基础上追加内容
FileInputStream & FileOutputStream(字节流)
- 通常作用于非文本
-
可以复制文本,但不能在程序中读取文本,可能会出现乱码。
- 原因:程序中读取文本可能出现中文,而中文占多个字节,导致被切割(即
read())) 成乱码。
- 原因:程序中读取文本可能出现中文,而中文占多个字节,导致被切割(即
常用方法:
int read():从输入流中读取数据的下一个字节。返回 0 到 255 。如果到达流末尾而没有可用的字节,则返回值 -1int read(byte[] b):同FileReadervoid write(byte[] b, int off, int len):将byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
处理流
- 在节点流外面包了一层流,套在相应的节点流之上。
- 只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流。
缓冲流
-
提高读写的速度,因为内部提供了一个缓冲区。
-
当读取数据时,数据先读入缓冲区,其后的读操作则直接访问缓冲区
-
当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
-
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法
flush()可以手动将缓冲区的内容全部写入输出流
-
转换流(属于字符流)
-
转换流提供了在字节流和字符流之间的转换
- InputStreamReader:将一个字节的输入流转换为字符的输入流。解码
- OutputStreamWriter:将一个字符的输出流转换为字节的输出流。编码
-
使用转换流来处理文件乱码问题。实现编码和解码的功能
public InputSreamReader(InputStream in,String charsetName):charsetName:指定字节输入流的编码规则public OutputSreamWriter(OutputStream out,String charsetName):charsetName:指定字符输出流的编码规则
对象流
ObjectInputStream和OjbectOutputSteam,用于存储和读取基本数据类型数据或对象的处理流,可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
writeObject(obj):输出可序列化对象,输出一次,操作flush()一次
readObject():读取流中的对象
序列化和反序列化
对象序列化机制允许把内存中的Java对象转换二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
对象支持序列化前提:
-
实现Serializable接口 或 Externalizable接口(不常用)
-
在类中声明serialVersionUID静态常量,表示序列化对象版本。不加的话由Java运行时自动生成,如果修改类的成员,serialVersionUID也会跟着变化,从而导致反序列化异常。
-
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同
就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
-
-
类的属性也必须是可序列化的(默认情况基本数据类型可序列化)
-
ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员