文章目录
IO
主要位于java.io.*
包
File对象
-
File f = new File("C:\\Windows\\notepad.exe")
File
对象可以传入绝对路径和相对路径
-
File
输出路径:getPath()
: 返回构造函数传入的路径getAbsolutePath()
: 返回绝对路径, 可能有..
/.
路径,同File.toString()
getCanonicalPath()
: 返回简洁的规范路径, 简化了..
/.
路径
-
File
对象既可以表示文件, 也可以表示目录:isFile()
: 判断是否为已存在的文件isDirectory()
: 判断是否为已存在的目录
-
File
对象获取一个文件/目录后:boolean canRead()
: 是否可读boolean canWrite()
: 是否可写boolean canExecute()
: 是否可执行, 对目录而言表示能否列出其子目录和文件long length()
: 文件字节大小
-
创建/删除文件:
-
boolean createNewFile()
-
boolean delete()
-
创建临时文件:
File f = File.createTempFile("tmp-", ".txt"); // 临时文件前缀和后缀 f.deleteOnExit(); // 指定JVM退出时自动删除 ...
-
-
遍历文件和目录:
list()
: 返回String[]
相对路径listFiles()
: 返回File[]
listFiles(new FileNameFilter() {public boolean accept(File dir, String name) {return name.endsWith(".exe");}})
: 使用文件过滤器筛选
-
创建/删除目录:
boolean mkdir()
: 创建当前File对象表示的目录boolean mkdirs()
: 创建当前File对象表示的目录, 并在必要时将不存在的父目录也创建出来boolean delete()
: 删除当前File对象表示的目录, 当前目录必须为空才能删除成功
Path对象
java.nio.file.Path
java.nio.file.Paths
Path p1 = Paths.get(String... path); // 构造Path类型, 可以传入相对路径/绝对路径, 支持路径拼接
Path p2 = p1.toAbsolutePath(); // 转换为绝对路径
Path p3 = p2.normalize(); // 转换为规范路径
File f = p3.toFlie(); // 转换为File对象
// Path对象支持for each遍历, 获取每一层目录的相对路径Path对象
InputStream
-
InputStream
是Java标准库提供的最基本的字节输入流, 位于java.io.InputStream
, 是一个抽象类 -
public abstract int read() throws IOException;
是最重要的一个方法- 每次读取输入流的下一个字节, 返回字节表示的int整数(范围0-255), 读到末尾返回-1表示不能再读了
-
所有IO调度都由操作系统完成, 所以在IO输入输出完成后要及时归还资源
InputStream
和OutputStream
都是提供close()
方法来关闭流, 进而释放底层资源
-
与IO操作相关的代码都必须正确的处理可能的
IOException
-
可以使用
try{}finally{}
确保IO流能够正常的关闭- Java编译器优化可以使用
try(getIO){}
的方式简化:
try (InputStream input = new FileInputStream("src/readme.txt")) { int n; while ((n = input.read()) != -1) { System.out.println(n); } }
- 实际上,编译器是通过检查
try()
中的对象是否实现了java.lang.AutoCloseable
接口, 来决定是否补写finally
块
- Java编译器优化可以使用
-
缓冲
int read(byte[] b)
: 读取若干字节并填充到byte[]数组, 返回读取的字节数int read(byte[] b, int off, int len)
: 指定byte[]数组的偏移量和最大填充数- 如果返回-1表示已经没有更多数据
-
IO读写时是阻塞的, 必须到达执行完成才能继续执行下面的代码
-
常见的
FileInputStream
可以从文件获取输入流,ByteArrayInputStream
可以在内存中模拟一个InputStream
输入流
OuputStream
- 与
InputStream
同理,OutputStream
是Java标准库中最基本的输出流 void write(int b)
, 写入低8位(0-255)flush()
, 立刻把缓冲区数据写入write(byte[] b)
, 一次性写入多个字节try(){}
, 处理IOException
并及时关闭资源- 阻塞操作
FileOutputStream
,ByteArrayOutputStream
Filter模式
如果想要给特定的Stream
添加一下额外功能, 如缓冲/加解密/解压缩/数字签名等, 通过继承来实现会无法控制继承规模
Java为了解决继承子类数量失控问题, 提供了具有额外功能的Stream
类, 可以对指定类包装装饰
基本的InputStream和来源:
FileInputStream
ServletInputStream
: HTTPSocket.getInputStream()
: TCP- …
包装类: BufferedInputStream
DigestInputStream
CipherInputStream
GZIPInputStream
- …
称之为Filter/Decorator
构建时传入InputStream
, 返回一个具有新功能的InputStream
FilterInputStream
继承FilterInputStream
来自定义一个:
class CountInputStream extends FilterInputStream {
private int count = 0;
CountInputStream(InputStream in) {
super(in); // 调用父类的构造方法
}
public int getBytesRead() {
return this.count;
}
public int read() throws IOException {
int n = in.read(); // 调用绑定的InputStream的read()方法
if (n != -1) {
this.count ++;
}
return n;
}
public int read(byte[] b, int off, int len) throws IOException {
int n = in.read(b, off, len); // 调用绑定的InputStream的read(...)方法, 自动关联到read()计数
this.count += n;
return n;
}
// read(byte[] b)方法会调用read(byte[] b, int off, int len), 不必重写
}
ZIP
-
ZipInputStream
也是一种FliterInputStream
,JarInputStream
从ZipInputStream
派生, 可以读取jar包的内容 -
ZipInputStream
通常以FileInputStream
作为数据来源:try (ZipInputStream zip = new ZipInputStream(new FileInputStream(...))) { ZipEntry entry = null; while ((entry = zip.getNextEntry()) != null) { // 获取包内实体(压缩文件或目录), 为null表示结束 String name = entry.getName(); if (!entry.isDirectory()) { // 如果是文件, 可以直接读取内容 int n; while ((n = zip.read()) != -1); } } }
-
ZipOutputStream
可以向zip包内写入数据:try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(...))) { File[] files = ... for (File file : files) { zip.putNextEntry(new ZipEntry(file.getName())); zip.write(getFileDataAsBytes(file)); zip.closeEntry(); } }
classpath
Java程序读取配置时, 利用classpath相对路径更方便:
getClass().getResourceAsStream("/settings.properties")
利用Properties
先load
默认配置, 再load
外部用户配置
序列化
- Java对象序列化需要实现
java.io.Serializable
接口 Serializable
接口没有定义任何方法, 是一个空接口(标记接口), 实现了标记接口的类仅仅相当于添加了一个标记, 没有增加任何额外的方法- Java对象序列化需要
ObjectOutputStream
- 反序列化出对象时不会调用构造函数
- 由于Java序列化机制存在安全隐患, 一个特定的byte[]反序列化后可能会执行不安全代码
- 更好的序列化是利用通用的JSON数据结构来序列化
Reader
- 字符流, 以字符char(0-65535)为单位读取
int read()
/int read(char[] c)
FileReader
通过字符流读取文件- 读取时的编码默认与系统有关, 可能会出现乱码
- 在创建时可以指定编码:
var fr = new FileReader("xx.txt", StandardCharsets.UTF_8)
try(){}
正确关闭资源
CharArrayReader
: 把一个char[]
变成Reader
StringReader
: 以String
作为数据源, 与CharArrayReader
类似InputStreamReader
: 把InputStream
转换成Reader
- 除了特殊的
CharArrayReader
和StringReader
, 普通的Reader
实际上是基于InputStream
构造的 - 构造
InputStreamReader
时, 我们需要传入InputStream
, 还需要指定编码, 就可以得到一个Reader
对象
- 除了特殊的
Writer
Reader
是带编码转换器的InputStream
, 它把byte
转换为char
, 而Writer
就是带编码转换器的OutputStream
, 它把char
转换为byte
并输出void write(int c)
void write(char[] c)
void write(String s)
FileWriter
CharArrayWriter
StringWriter
OutputStreamWriter
PrintStream / PrintWriter
-
PrintStream
是一种FilterOutputStream
:print(int)
: 写入intprint(boolean)
: 写入booleanprint(String)
: 写入Stringprint(Object)
: 写入Object, 相当于print(Object.toString())
- 以及对应的
println()
, 自动添加换行符
-
System.out
是系统默认提供的PrintStream
, 表示标准输出 -
System.err
是系统默认提供的标准错误输出 -
PrintStream
不会抛出IOException
, 不必捕获异常 -
PrintStream
输出的是字节流(byte), 而PrintWriter
扩展了Writer
, 输出的是字符流(char)