type
status
date
slug
summary
tags
category
icon
password
File
IO技术概述
- Output:把内存中的数据存储到持久化设备上这个动作称为输出Output(写)操作
- Input:把持久设备上的数据读取到内存中的这个动作称为输入Input(读)操作
- IO操作:把上面的这种输入和输出动作称为IO操作
概述
- File类是文件和目录路径名的抽象表示形式
- Java中把文件或者目录(文件夹)都封装成File对象
- 要操作硬盘上的文件,或者文件夹只要找到File这个类即可
静态成员变量
- pathSeparator:与系统有关的路径分隔符,为了方便,它被表示为一个字符串
- separator:与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串
构造方法
相对路径和绝对路径
- 绝对路径:绝对路径是一个固定的路径,从盘符开始,在系统中具有唯一性,如:c:\windows\system32
- 相对路径:相对路径相对于某个位置,在eclipse下是指当前项目下,表示路径之间的关系,如:D:\develop\Java\jdk1.7.0_72\bin、D:\develop\Java\jre7,Java父目录是D:\develop,子目录是jdk1.7.0_72;父路径是唯一性,子目录是可以多个
构造方法
- File(String pathname)
- 通过将给定路径名字符串转换为一个File对象,之后可以使用File中的方法
- windows中的路径或文件名不区分大小写
- File(String parent, String child):根据parent路径名字符串和child路径名字符串创建一个新File对象
- File(File parent, String child)
创建文件
public boolean createNewFile():创建文件,如果存在这样的文件,就不创建了。
创建目录
- public boolean mkdir():创建文件夹,如果存在这样的文件夹,就不创建了
- public boolean mkdirs():创建文件夹,如果父文件夹不存在,会创建出来
删除
public boolean delete():删除文件或者文件夹。
获取
- String getName():返回路径中表示的文件或者文件夹名,获取路径中的最后部分的名字
- long length():返回路径中表示的文件的字节数
- String getAbsolutePath():获取绝对路径,返回String对象
- File getAbsoluteFile():获取绝对路径,返回File对象
- String getParent():获取父路径,返回String对象
- File getParentFile():获取父路径,返回File对象
判断
- boolean exists():判断File构造方法中封装路径是否存在,存在返回true,不存在返回false
- boolean isDirectory():判断File构造方法中封装的路径是不是文件夹,如果是文件夹返回true,不是文件返回false
- boolean isFile():判断File构造方法中封装的路径是不是文件,如果是文件返回true,不是文件返回false
list获取
- String[] list():获取到File构造方法中封装的路径中的文件和文件夹名(遍历一个目录),返回只有名字
- File[] listFiles():获取File构造方法中封装的路径中的文件和文件夹名(遍历一个目录),返回的是目录或者文件的全路径
- static File[] listRoots():列出可用的文件系统根
文件过滤器
用来过滤一个目录下的指定扩展名的文件,或者包含某些关键字的文件夹。
- public String[] list(FilenameFilter filter)
- public File[] listFiles(FileFilter filter)
listFiles(FileFilter filter) 也可以接受一个FileFilter过滤器,它和FilenameFilter的区别如下:
- FilenameFilter过滤器中的accept方法接受两个参数,一个当前文件或文件夹所在的路径,一个是当前文件或文件夹对象的名称
- FileFilter 过滤器中的accept方法接受一个参数,这个参数就当前文件或文件夹对象
- 当需要过滤文件名称时就可以使用FilenameFilter这个过滤器,当想对当前文件或文件夹进行过滤,就可以使用FileFilter,比如需要当前目录下的所有文件夹,就可以使用FileFilter 过滤器
原理分析
- listFiles()遍历目录的同时,获取到了文件名全路径,调用过滤器的方法accept,将获取到的路径传递给accept方法的参数pathname
- accept方法接收了参数pathname,参数是listFiles传递来的
- 在accept方法中,进行判断,如果这个路径是Java文件,返回true,走着返回false
- 一旦方法返回了true,listFiles将路径保存到File数组中
字节流
输入和输出
是输入还是输出,都是以Java程序为参照。
- Output:把内存中的数据存储到持久化设备上这个动作称为输出(写)Output操作,程序到文件称为输出
- Input:把持久设备上的数据读取到内存中的这个动作称为输入(读)Input操作,文件到程序称为输入
把上面的这种输入和输出动作称为IO操作,IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的类都在IO包中。
流按流向分为两种:输入流,输出流。
流按操作类型分为两种:
- 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
- 字符流 : 字符流只能操作纯字符数据,比较方便。
IO流常用父类:
- 字节流的抽象父类:
- InputStream
- OutputStream
- 字符流的抽象父类:
- Reader
- Writer
字节输出流OutputStream
IO程序书写:
- 使用前,导入IO包中的类
- 使用时,进行IO异常处理
- 使用后,释放资源
方法介绍
- void close():关闭此输出流并释放与此流有关的所有系统资源
- void write(byte[] b):将b.length个字节从指定的byte数组写入此输出流
- void write(byte[] b, int off, int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流
- abstract void write(int b):将指定的字节写入此输出流
字节输出流FileOutputStream写字节
FileOutputStream:数据文件写入流。
FileOutputStream构造方法:
- 作用:绑定输出的输出目的
- FileOutputStream(File file):创建一个向指定File对象表示的文件中写入数据的文件输出流
- FileOutputStream(File file, boolean append):创建一个向指定File对象表示的文件中写入数据的文件输出流,以追加的方式写入
- FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流
- FileOutputStream(String name, boolean append):创建一个向具有指定name的文件中写入数据的输出文件流,以追加的方式写入
流对象使用步骤:
- 创建流子类的对象,绑定数据目的
- 调用流对象的方法write写
- close释放资源
注意:流对象的构造方法可以创建文件,如果文件存在,直接覆盖。
字节输出流FileOutputStream写字节数组
- void write(byte[] b): 将b.length个字节从指定的byte数组写入此输出流
- void write(byte[] b, int off, int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流
文件的续写和换行符号
- 文件的续写:FileOutputStream构造方法的第二个参数中加入true
- 换行符号:在文件中写入换行符号\r\n,可以写在上一行的末尾,也可以写在下一行的开头
IO中的异常处理
IO流中使用try...catch...finally进行异常处理。
细节:
- 保证流对象变量作用域足够
- catch里面怎么处理异常:
- 输出异常的信息,目的看到哪里出现了问题
- 停下程序,从新尝试
- 如果流对象建立失败了,需要关闭资源吗
- new 对象的时候失败,没有占用系统资源
- 释放资源的时候,对流对象判断null
- 变量不是null,对象建立成功,需要关闭资源
字节输入流InputStream
- abstract int read():从输入流中读取数据的下一个字节
- int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
- int read(byte[] b, int off, int len):将输入流中最多len个数据字节读入byte数组
- void close():关闭此输入流并释放与该流关联的所有系统资源
字节输入流FileInputStream读取字节
- abstract int read():从输入流中读取数据的下一个字节,返回-1表示文件结束
- int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中,读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1
- int read(byte[] b, int off, int len):将输入流中最多len个数据字节读入byte数组
- void close():关闭此输入流并释放与该流关联的所有系统资源
字节输入流FileInputStream读取字节数组
- int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中,读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1
- int read(byte[] b, int off, int len):将输入流中最多len个数据字节读入byte数组
字节输入流FileInputStream读取字节数组的实现原理
文件复制原理
上述代码输入流和输出流之间是通过ch这个变量进行数据交换的。
上述复制文件有个问题,每次都从源文件读取一个,然后在写到指定文件,接着再读取一个字符,然后再写一个,一直这样下去。效率极低。
字节流复制文件读取单个字节
字节流复制文件读取字节数组
字符流
字节流读取字符的问题
通过以下程序读取带有中文的文件:
可以发现,上面程序在读取含有中文的文件时,并没有看到具体的中文,而是看到一些数字。
编码表
生活中字符和计算机二进制的对应关系表就是编码表。
分类:
- ascii:一个字节中的7位就可以表示,对应的字节都是正数,0-xxxxxxx
- iso-8859-1:拉丁码表,用一个字节的8位表示,有负数,1-xxxxxxx
- GB2312:简体中文码表,包含6000-7000中文和符号,用两个字节表示,两个字节第一个字节是负数,第二个字节可能是正数
- GBK:目前最常用的中文码表,2万的中文和符号,用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0
- GB18030:最新的中文码表,目前还没有正式使用
- unicode:国际标准码表,无论是什么文字都用两个字节存储
- Java中的char类型用的就是这个码表,char c = 'a';占两个字节
- Java中的字符串是按照系统默认码表来解析的,简体中文版字符串默认的码表是GBK
- UTF-8:基于unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头加入了编码信息(后期到API中查找)
- 能识别中文的码表:GBK、UTF-8;正因为识别中文码表不唯一,涉及到了编码解码问题
- 对于我们开发而言;常见的编码是GBK、UTF-8和ISO-8859-1
- 文字--->(数字):编码,“abc”.getBytes() byte[]
- (数字)--->文字:解码, byte[] b={97,98,99} new String(b)
字符输出流写文本FileWriter类
- void write(int c):写入单个字符
- void write(String str):写入字符串
- void write(String str, int off, int len):写入字符串的某一部分
- void write(char[] cbuf):写入字符数组
- abstract void write(char[] cbuf, int off, int len):写入字符数组的某一部分
字符输入流读取文本FileReader类
- int read():读取单个字符
- int read(char[] cbuf):将字符读入数组
- abstract int read(char[] cbuf, int off, int len):将字符读入数组的某一部分
flush方法和close方法区别
- flush()方法:用来刷新缓冲区的,刷新后可以再次写出,只有字符流才需要刷新
- close()方法:用来关闭流释放资源的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出
字符流复制文本文件
练习:复制文本文件。
思路:
- 既然是文本涉及编码表,需要用字符流
- 操作的是文件,涉及硬盘
- 有指定码表吗?没有,默认就行
操作的是文件,使用的默认码表,直接使用字符流操作文件的便捷类FileReader和FileWriter。
转换流
概述
- OutputStreamWriter是字符流通向字节流的桥梁,可以使用指定的字符编码表,将要写入流中的字符编码成字节
- 将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去
OutputStreamWriter写文本文件
java.io.OutputStreamWriter继承Writer类,就是一个字符输出流,写文本文件。
构造方法:
- OutputStreamWriter(OuputStream out)接收所有的字节输出流FileOutputStream
- OutputStreamWriter(OutputStream out, String charsetName):String charsetName传递编码表名字 GBK UTF-8
- OutputStreamWriter有个子类FileWriter
OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?
其实在OutputStreamWriter流中维护自己的缓冲区,当调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter缓冲区中,然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。
字节转字符流过程
java.io.InputStreamReader继承Reader,是字符输入流,读取文本文件。
- InputStreamReader(InputStream in)接收所有的字节输入流,可以传递的字节输入流: FileInputStream
- InputStreamReader(InputStream in,String charsetName)传递编码表的名字
InputSteamReader读取文本文件
InputStreamReader是字节流通向字符流的桥梁:它使用指定的字符编码表读取字节并将其解码为字符,它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
转换流子类父类的区别
继承关系:
OutputStreamWriter:
|--FileWriter:
InputStreamReader:
|--FileReader;
区别:
- OutputStreamWriter和InputStreamReader是字符和字节的桥梁,也可以称之为字符转换流,字符转换流原理:字节流+编码表
- FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在,当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
以下三句话功能相同:
注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。
使用子类的条件:
- 操作的是文件
- 使用默认编码
缓冲流
概述
缓冲流可提高IO流的读写速度,分为字节缓冲流与字符缓冲流。
字节流
字节输出流缓冲流BufferedOutputStream
BufferedOuputStream继承OutputStream,可以提高原有输出流的写入效率。
构造方法:
- BufferedOuputStream(OuputStream out)
- 可以传递任意的字节输出流,传递的是哪个字节流,就对哪个字节流提高效率
字节输入流缓冲流BufferedInputStream
BufferedInputStream继承InputStream,是字节输入流的缓冲流。
构造方法BufferedInputStream(InputStream in),可以传递任意的字节输入流,传递是谁就提高谁的效率,可以传递的字节输入流FileInputStream。
字节输入流缓冲流BufferedInputStream
四种文件复制方式的效率比较
流形式 | 耗时 |
字节流读写单个字节 | 125250毫秒 |
字节流读写字节数组 | 193毫秒 |
字节流缓冲区流读写单个字节 | 1210毫秒 |
字节流缓冲区流读写字节数组 | 73毫秒 |
字符流
字符输出流缓冲流BufferedWriter
BufferedWriter继承Writer。
构造方法BufferedWriter(Writer w)传递任意字符输出流,传递谁就高效谁,能传递的字符输出流:FileWriter、OutputStreamWriter。
字符输出流缓冲流BufferedWriter特有方法newLine
void newLine():写换行,方法具有平台无关性,JVM安装的是Windows版本newLine()写的就是\r\n,安装的是Linux版本newLine()写的就是\n。
字符输入流缓冲流BufferedReader
BufferedReader继承Reader,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取
public String readLine()读取一个文本行,包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null。
构造方法BufferedReader(Reader r)可以任意的字符输入流:FileReader、InputStreamReader。
字符流缓冲区流复制文本文件
IO流对象的操作规律
- 明确一:要操作的数据是数据源还是数据目的
- 源:InputStream、Reader
- 目的:OutputStream、Writer
- 先根据需求明确要读,还是要写
- 明确二:要操作的数据是字节还是文本
- 源:
- 字节:InputStream
- 文本:Reader
- 目的:
- 字节:OutputStream
- 文本:Writer
- 明确三:明确数据所在的具体设备
- 源设备:
- 硬盘:文件File开头
- 内存:数组,字符串
- 键盘:System.in
- 网络:Socket
- 目的设备:
- 硬盘:文件File开头
- 内存:数组,字符串
- 屏幕:System.out
- 网络:Socket
- 完全可以明确具体要使用哪个流对象
- 明确四:是否需要额外功能
- 额外功能:
- 转换吗?转换流InputStreamReader、OutputStreamWriter
- 高效吗?缓冲区对象BufferedXXX
- 已经明确到了具体的体系上
Properties类
Properties类继承Hashtable,实现Map接口,表示了一个持久的属性集,Properties可保存在流中或从流中加载,属性列表中每个键及其对应值都是一个字符串。
特点
- Hashtable的子类,map集合中的方法都可以用
- 该集合没有泛型。键值都是字符串
- 它是一个可以持久化的属性集,键值可以存储到集合中,也可以存储到持久化的设备(硬盘、U盘、光盘)上,键值的来源也可以是持久化的设备
- 有和流技术相结合的方法
方法介绍
- load(InputStream inputStream):把指定流所对应的文件中的数据,读取出来,保存到Propertie集合中
- load(Reader reader):按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)
- store(OutputStream outputStream,String commonts):把集合中的数据,保存到指定的流所对应的文件中,参数commonts代表对描述信息
- stroe(Writer writer,String comments):以适合使用 load(Reader) 方法的格式,将此Properties表中的属性列表(键和元素对)写入输出字符
存储键值对
load方法
传递任意的字节或者字符输入流,流对象读取文件中的键值对,保存到集合。
注意:使用字符流FileReader就可以完成文件中的中文读取操作了。
store方法
接收所有的字节或者字符的输出流,将集合中的键值对写回文件中保存。
序列化
对象的序列化
- 对象中的数据,以流的形式,写入到文件中保存过程称为写出对象,对象的序列化
- ObjectOutputStream将对象写道文件中,实现序列化
对象的反序列化
- 在文件中,以流的形式,将对象读出来,读取对象,对象的反序列化
- ObjectInputStream 将文件对象读取出来
ObjectOutputStream流写对象
IO流对象实现对象Person序列化和反序列化
- ObjectOutputStream:写对象,实现序列化
- ObjectInputStream:读取对象,实现反序列化
ObjectInputStream流读取对象
构造方法:ObjectInputStream(InputStream in)传递任意的字节输入流,输入流封装文件,必须是序列化的文件。
静态不能序列化
序列化是把对象数据进行持久化存储,静态的东西不属于对象,而属于类。
transient关键字
被transient修饰的属性不会被序列化,transient关键字只能修饰成员变量。
Serializable接口
给需要序列化的类上加标记,该标记中没有任何抽象方法,只有实现了 Serializable接口的类的对象才能被序列化。
序列化中的序列号冲突问题
当一个类实现Serializable接口后,创建对象并将对象写入文件,之后更改了源代码(比如:将成员变量的修饰符有private改成public),再次从文件中读取对象时会报异常。
序列化中自定义的序列号
定义方式:
这样每次编译类时生成的serialVersionUID值都是固定的。
打印流
打印流添加输出数据的功能,使它们能够方便地打印各种数据值表示形式。
打印流根据流的分类:
- 字节打印流:PrintStream
- 字符打印流:PrintWriter
方法:
- void print(String str):输出任意类型的数据,
- void println(String str):输出任意类型的数据,自动写入换行操作
特点:
- 此流不负责数据源,只负责数据目的
- 为其他输出流,添加功能
- 永远不会抛出IOException,但是可能抛出别的异常
- 两个打印流的方法完全一致
- 构造方法就是打印流的输出目的端
- PrintStream构造方法接收File类型、字符串文件名、字节输出流OutputStream
- PrintWriter构造方法接收File类型、字符串文件名、字节输出流OutputStream、字符输出流Writer
打印流输出目的是File对象
输出语句是char数组
结果分析:
- println数组,只有打印字符数组时只有容,其余均打印数组的地址
- 因为api中定义了打印字符数组的方法,其底层是在遍历数组中的元素
- 而其他打印数组的方法,都是将数组对象编程Object,其底层再将对象编程String,调用了String s = String.valueOf(x);方法
打印流输出目的是String和流对象
打印流开启自动刷新
打印流复制文本文件
Commons-IO
概述
解压缩commons-io-2.4.zip文件:
- commons-io-2.4.jar需要导入到项目中的jar包,里面存放的是class文件
- commons-io-2.4-sources.jar工具类中原代码
- docs是帮助文档
使用
- 导入jar包
- 加入classpath的第三方jar包内的class文件才能在项目中使用
- 创建lib文件夹
- 将commons-io.jar拷贝到lib文件夹
- 右键点击commons-io.jar,Build Path→Add to Build Path
- 学会如何看源代码
IO工具类FilenameUtils
- getExtension(String path):获取文件的扩展名
- getName():获取文件名
- isExtension(String fileName,String ext):判断fileName是否是ext后缀名
IO工具类FileUtils
- readFileToString(File file):读取文件内容,并返回一个String
- writeStringToFile(File file,String content):将内容content写入到file中
- copyDirectoryToDirectory(File srcDir,File destDir):文件夹复制
- copyFile(File srcFile,File destFile):文件复制
- 作者:青山🌊
- 链接:http://cxuan.me/article/java-se-io
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。