File和IO
元数据与文件读写Java 的 File 表示文件的元数据。元数据是什么元数据就是描述数据的数据。文件的元数据就是文件名、文件大小、文件路径等信息。要读写文件内容需要用到 Java 的 IO 流而 IO 流需要提供 File 对象。File 的常用方法File 的构造方法需要提供文件的路径支持绝对路径、相对路径以及网络路径不过这个路径可以不存在。boolean exists()判断传入的路径是否对应一个真实存在的文件如果不存在则可以新建文件boolean createNewFile()新建文件也只会创建文件文件可以没有后缀boolean mkdir()创建单级目录boolean mkdirs()创建多级目录如果文件存在可以判断是文件夹还是文件如果文件不存在则这两个方法都返回 falseboolean isFile()boolean isDirectory()获取文件信息String getAbsolutePath()返回文件的绝对路径String getPath()返回构造方法中传入的路径String getParent()返回父级路径字符串File getParentFile()返回父级路径 File 对象String getName()返回文件夹名称、文件名有后缀带后缀long length()返回文件的大小字节如果是文件夹也有返回值但是不正确long lastModified()最近修改的时刻文件权限相关boolean canRead()boolean canWrite()boolean isHidden()list如果 File 对象不存在、File 对象不是文件夹、File 对象需要的权限不足返回 null否则返回文件夹下的所有文件如果是空文件夹则返回一个空数组String[] list()File[] listFiles()危险操作boolean delete()删除文件和空目录不走回收站boolean renameTo(File dest)重命名或者移动文件原文件不会保留基础 IO 流基础 IO 流可以分成字节流、字符流、输入流和输出流。两两组合就得到了四种搭配字节输入流InputStreamint read()每次读取一个字节取值在 [0, 255] 区间读完返回 -1。int read(byte[] b) : 读取是尽量将数组填满返回实际读取的字节数读完返回-1。int read(byte[] b, int off, int len)跟上一个方法一样只不过加了一个偏移量返回实际读取的字节数读完返回-1。void close()字节输出流OutputStreamvoid write(int b)每次写一个字节void write(byte[] b)一个字节一个字节写效率太低每次写一整组的字节数据void write(byte[] b, int off, int len)void flush()void close()字符输入流Readerint read()int read(char[] cbuf)int read(char[] cbuf, int off, int len)void close()字符输出流Writervoid write(int c)void write(char[] cbuf)void write(char[] cbuf, int off, int len)void write(String str)void write(String str, int off, int len)void flush()void close()InputStream、OutputStream、Reader、Writer 都是抽象类都实现了 Closeable 接口输出流额外实现了 Flushable 接口。字符流拥有一个长度为 8192 的字节数组缓冲区充当了内存的作用Java 程序先将数据读入缓冲区之后每次再到缓冲区中读写数据的时候先将数据写到缓冲区中缓冲区写满了就刷盘。输出流的 flush 方法可以主动将缓冲区的数据刷盘。拷贝文件的示例代码如下InputStream、OutputStream、Reader、Writer 都是抽象类需要用到实现类 FileInputStream、FileOutputStream、FileReader、FileWriter。public void test() throws IOException { File source new File(C:\\Users\\admin\\Downloads\\python.md); File dest new File(C:\\Users\\admin\\Documents\\python.md); FileInputStream fis new FileInputStream(source); // 输出流的append参数表示是追加写还是覆盖写默认是false也就是覆盖写 FileOutputStream fos new FileOutputStream(dest, true); int res; while (true) { res fis.read(); if (res -1) break; fos.write(res); } fos.close(); fis.close(); }不基础 IO 流1.缓冲流BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter需要传入基础 IO 流对象。字节缓冲流新增了长度为 8192 的字节数组而字符缓冲流的缓冲区从长度为 8192 的字节数组升级成了字符数据长度不变。字符缓冲流新增了两个方法String readLine()一次读取一行遇到换行符结束读完返回 nullvoid newLine()换行这两个方法可以区分不同操作系统的换行符。2.序列化流将 Java 对象转成字节数组可以在不同设备之间进行传输。只有字节流没有字符流Java 对象所属的类必须实现 Serializable 接口这是一个标记性空接口里面没有抽象方法固定 private static final long serialVersionUID 属性private外部不可见static版本号是跟类相关的属性跟对象没关系final常量不可二次修改long serialVersionUID按英语的说法是固定搭配如果某个属性不想被序列化可以用 transient 瞬态关键字修饰反序列化后得到的值是 0 或 null。public void test() throws IOException, ClassNotFoundException { File file new File(C:\\Users\\admin\\Documents\\1.txt); String str 1234; // 序列化将Java对象转成字节数据 FileOutputStream fos new FileOutputStream(file); ObjectOutputStream oos new ObjectOutputStream(fos); oos.writeObject(str); oos.close(); // 反序列化将字节数据转成Java对象 FileInputStream fis new FileInputStream(file); ObjectInputStream ois new ObjectInputStream(fis); String s (String) ois.readObject(); ois.close(); // 只关序列化流就可以了像这些需要传入IO流对象的实例对象在调用close方法时会自动将传入的IO流对象一起关闭 System.out.println(s); }在我们之前学习的 IO 流中会返回 -1返回 null 表示读完了。但是序列化流不会进行提醒如果写入了 10 个对象但是进行了 11 次读取操作就会直接报错非常粗暴。比较好的做法是将实例对象存入集合中然后将集合对象序列化。3.打印流打印流可以做到原样输出在方法中传递什么参数就直接输出到文件中。如果传入的是一个对象将这个对象的 toString 方法的返回值输出到文件中。打印流的特点是只支持输出流PrintStream、PrintWriter。void print()void println()void printf(String format, Object... args)跟 C 语言的 printf 用法差不多4.转换流InputStreamReader字节输入流 - 字符输入流传入 InputStream但是能调用字符流的方法InputStreamReader(InputStream in)InputStreamReader(InputStream in, String charsetName)OutputStreamWriter字节输出流 - 字符输出流传入 OutputStream但是能调用字符流的方法OutputStreamWriter(OutputStream in)OutputStreamWriter(OutputStream in, String charsetName)将 UTF8 编码的文件读取到程序中用 GBK 编码写入到新文件中。public static void main(String[] args) throws IOException { InputStreamReader isr new InputStreamReader(new FileInputStream(C:\\Users\\admin\\Downloads\\1.txt), UTF8); OutputStreamWriter isw new OutputStreamWriter(new FileOutputStream(C:\\Users\\admin\\Downloads\\2.txt), GBK); char[] arr new char[256]; int len 0; while ((len isr.read(arr)) ! -1) { System.out.println(new String(arr, 0, len)); isw.write(arr, 0, len); } isw.close(); isr.close(); }Unicode 码点ASCII 码英文标点符号一个字节就可以表示。GBK1 英2 中兼容 ASCII 码英文用一个字节表示中文用两个字节表示。UnicodeUnicode 是一个字符集它为世界上所有字符分配了唯一的编号这个编号就叫码点。但是Unicode 并未规定这些字符如何保存和传输需要具体的编码方式来实现。UTF-8Unicode 的一种变长编码方式可以用 1 到 4 个字节来保存字符1 英 3 中。这个时候我想到了 Java 里面的 char 类型char 可以用来表示中文但是 char 类型只占用两个字节这是怎么做到的呢其实 char 类型保存的是字符的码点这个唯一编号并不是保存字符实际的编码如果一个字符的码点无法用 16bit 表示就无法用 char 类型存储。平时我们可以用 char 来表示中文是因为这些字符的码点可以用两个字节表示但是有些字符不能如 emoji 表情。 是一个生僻字就无法用一个 char 表示需要用 String 表示。因为 要用两个 char 表示所以 String 的 length 方法返回的是 2也就是 String 中 char 的个数。如果想真正统计字符串中有多少个字符需要用 String 提供的 codePointCount 方法。