File 类是文件或目录的抽象表示,通过它可以实现对文件或目录信息的操作和管理。File 类能新建、删除、重命名文件和目录,但 File 类不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。想要在 Java 程序中表示一个真实存在的文件或目录,那么必须有一个 File 对象,但是 Java 程序中的一个 File 对象,可能没有一个真实存在的文件或目录。File 对象可以作为参数传递给流的构造器。
File(String pathname)
以 pathname 为路径创建 File 对象,可以是绝对路径或者相对路径,如果 pathname 是相对路径,则默认的当前路径在系统属性 user.dir 中存储。
File(String parent, String child)
以 parent 为父路径,child 为子路径创建 File 对象。
File(File parent, String child)
根据一个父 File 对象和子文件路径创建 File 对象
@Test public void test1() { // 构造器1 File file1 = new File("hello.txt"); // 相当于当前 module File file2 = new File("D:\\document\\he.txt"); // "\\" 第一个为转义字符 // 构造器2 File file3 = new File("D:\\document\\md文档", "Java 学习笔记"); // 构造器3 File file4 = new File(file3, "hi.txt"); }
路径中的每级目录之间用一个路径分隔符隔开。
路径分隔符和系统有关:
注意:Java 程序支持跨平台运行,因此路径分隔符要慎用。为了解决这个隐患,File 类提供了一个常量:public static final String separator。它可以根据操作系统,动态的提供分隔符。
// 下列两种情况效果相同 File file1 = new File("d:\\document\\hello.txt"); File file2 = new File("d:" + File.separator + "document" + File.separator + "hello.txt");
通过 File 类中的方法可以实现对文件和目录的操作和管理。下面是 File 类中常用的方法。
File 类的获取功能:
public String getName():返回文件或目录的名称。
public String getPath():获取路径。
public String getParent():获取上层文件目录路径。若无,返回 null。
public String getAbsolutePath():获取绝对路径。
public long length():获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified():获取最后一次的修改时间,毫秒值。
如下两个方法适用于文件目录:
public String[] list():获取指定目录下的所有文件或者文件目录的名称数组。
public File[] listFiles() :获取指定目录下的所有文件或者文件目录的 File 数组。
File 类的重命名功能:
比如以 file1.renameTo(file2) 为例,想要保证返回 true,需要 file1 在硬盘中是存在的,且 file2 不能在硬盘中存在。
File 类的判断功能:
File 类的创建功能:
注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目路径下。
File 类的删除功能:
注意事项:
Java 中的删除不走回收站。
要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录。
I/O 是 Input/Output 的缩写, I/O 技术是非常实用的技术,用于处理设备之间的数据传输。如:读/写文件,网络通讯等。在 Java 程序中,对于数据的输入/输出操作以 “流 (stream)” 的方式进行。
流 (stream) 是一组有序的数据序列。根据操作的类型,分为输入流和输出流两种。输入流的指向称为源,程序从指向源的输入流中读取源中的数据。当程序需要读取数据时,就会开启一个通向数据源的流,这个数据源可以是文件、内存或是网络连接。而输出流的指向是字节要去的目的地,程序通过向输出流中写入数据把信息传递到目的地。当程序需要写入数据时,就会开启一个通向目的地的流。
Java 本身不包含 I/O 语句,而是通过 Java API 提供的 Java.io 包完成 I/O。java.io 包下提供了各种 “流” 类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
注意:输入输出是相对的,需要清楚是站在谁的角度说明问题。比如:甲借给乙 100 块,对甲来说,甲是输出端;对乙来说,乙是输入端。
按照 流的方向 划分可以分为输入流 (InputStream) 和输出流 (OutputStream) 。
按照 流所关联的是否为最终数据源或目标 来划分,流可以分为节点流 (node stream) 和处理流 (processing stream)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EGtLc6L0-1640706447490)(D:\单宇楠\md文档\md插图\IO流2.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ZUO6c4l-1640706447492)(D:\单宇楠\md文档\md插图\IO流3.png)]
按照 流所操作的数据单元 来划分,流可以分为字节流和字符流。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6CsB6Pci-1640706447493)(D:\单宇楠\md文档\md插图\IO流1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hq0nxSY8-1640706447494)(D:\单宇楠\md文档\md插图\io流4.jpg)]
输入输出流一般分为字节输入流、字节输出流、字符输入流和字符输出流 4 种。
InputStream(典型实现类:FileInputStream):
抽象类 java.io.InputStream 是所有字节输入流的父类,该类定义了读取字节数据的基本方法。下面是 InputStream 类中常用的方法。
OutputStream(典型实现类:FileOutputStream):
抽象类 java.io.OutputStream 是所有字节输出流的父类,该类定义了输出字节数据的基本方法。下面是 OutputStream 类中常用的方法。
注意:
Reader(典型实现类:FileReader):
抽象类 java.io.Reader 是所有字符输入流的父类,该类定义了读取字符数据的基本方法。下面是 Reader 类中常用的方法。
Writer(典型实现类:FileWriter):
抽象类 java.io.Writer 是所有字符输出流的父类,该类定义了输出字符数据的基本方法。下面是 Writer 类中常用的方法。
注意:
因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数。
输入过程:
输出过程:
建立一个流对象,将已存在的一个文件加载进流。
FileReader fr = new FileReader(new File(“Test.txt”));
创建一个临时存放数据的数组。
char[] ch = new char[1024];
调用流对象的读取方法将流中的数据读入到数组中。
fr.read(ch);
关闭资源。
fr.close();
示例:
FileReader fr = null; try { fr = new FileReader(new File("c:\\test.txt")); char[] buf = new char[1024]; int len; while ((len = fr.read(buf)) != -1) { System.out.print(new String(buf, 0, len)); } } catch (IOException e) { System.out.println("read-Exception :" + e.getMessage()); } finally { if (fr != null) { try { fr.close(); } catch (IOException e) { System.out.println("close-Exception :" + e.getMessage()); } } }
创建流对象,建立数据存放文件
FileWriter fw = new FileWriter(new File(“Test.txt”));
调用流对象的写入方法,将数据写入流
fw.write(“atguigu-songhongkang”);
关闭流资源,并将流中的数据清空到文件中。
fw.close();
示例:
FileWriter fw = null; try { fw = new FileWriter(new File("Test.txt")); fw.write("atguigu-songhongkang"); } catch (IOException e) { e.printStackTrace(); } finally { if (fw != null) try { fw.close(); } catch (IOException e) { e.printStackTrace(); } }
1. FileReader 读入数据的基本操作:将 hello.txt 文件内容读入程序中,并输出到控制台。
@Test public void testFileReader() { FileReader fr = null; try { // 1.实例化 File 类的对象,指明要操作的文件 File file = new File("hello.txt"); // 相较于当前 Module // 2.提供具体的流 fr = new FileReader(file); // 3.数据的读入 // read():返回读入的一个字符。如果达到文件末尾,返回 -1 int data; while ((data = fr.read()) != -1) { System.out.print((char)data); } } catch (IOException e) { e.printStackTrace(); } finally { // 4.流的关闭操作 try { if (fr != null) { fr.close(); } } catch (IOException e) { e.printStackTrace(); } } } // FileReader 中使用 read(char[] cbuf) 读入数据 // 对 read() 操作升级:使用 read 的重载方法 @Test public void testFileReader1() throws IOException { FileReader fr = null; try { // 1.File 类的实例化 File file = new File("hello.txt"); // 2.FileReader 流的实例化 fr = new FileReader(file); // 3.读入的操作 // read(char[] cbuf):返回每次读入 cbuf 数组中的字符数。如果达到文件末尾,返回 -1。 char[] cbuf = new char[5]; int len; while ((len = fr.read(cbuf)) != -1) { // 方式一: // for (int i = 0; i < len; i++) { // len 不能写成 cbuf.length // System.out.print(cbuf[i]); // } // 方式二: String str = new String(cbuf, 0, len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (fr != null) { try { // 4.资源的关闭 fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
2. FileWriter 写出数据的操作:从内存中写出数据到硬盘文件里。
/* 说明: 1.输出操作,对应的 File 可以不存在。并不会报异常。 2. File 对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。 File 对应的硬盘中的文件如果存在: 如果流使用的构造器是:FileWriter(file, false) / FileWriter(file):对原有的文件覆盖 如果流使用的构造器是:FileWriter(file, true):不会对原有文件覆盖,二十在原有文件基础上追加内容 */ @Test public void testFileWriter() { FileWriter fw = null; try { // 1.提供 File 类的对象,指明写出到的文件 File file = new File("hello1.txt"); // 2.提供 FileWriter 的对象,用于数据的写出 fw = new FileWriter(file, true); // 3.写出的操作 fw.write("I have a dream!\n"); fw.write("you need to have a dream!"); } catch (IOException e) { e.printStackTrace(); } finally { // 4.资源流的关闭 if(fw != null) { try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3. 使用 FileReader 和 FileWriter 实现文本文件的复制
@Test public void testFileReaderFileWriter() throws IOException { FileReader fr = null; FileWriter fw = null; try { // 1.创建 File 类的对象,指明读入和写出的文件 // 注意:不能使用字符流来处理图片等字节数据 File srcFile = new File("hello.txt"); File destFile = new File("hello2.txt"); // 2.创建输入流和输出流的对象 fr = new FileReader(srcFile); fw = new FileWriter(destFile); // 3.数据的读入和写出操作 char[] cbuf = new char[5]; int len; // 记录每次读入到 cbuf 数组中的字符数 while ((len = fr.read(cbuf)) != -1) { // 每次写出 len 个字符 fw.write(cbuf, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { // 4.关闭流资源 try { if (fw != null) { fw.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (fr != null) { fr.close(); } } catch (IOException e) { e.printStackTrace(); } } }
4. 使用 FileInputStream 不能读取文本文件的测试
@Test // 使用字节流 FileInputStream 处理文本文件,可能出现乱码 public void testFileInputStream() { FileInputStream fis = null; try { // 1.造文件 File file = new File("hello.txt"); // 2.造流 fis = new FileInputStream(file); // 3.读数据 byte[] buffer = new byte[5]; int len; // 记录每次读取的字节个数 while ((len = fis.read(buffer)) != -1) { String str = new String(buffer, 0, len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null) { // 4.关闭资源 try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
5. 使用 FileInputStream 和 FileOutputStream 读写非文本文件
// 实现对图片的复制操作 @Test public void testFileInputOutputStream() { FileInputStream fis = null; FileOutputStream fos = null; try { // File srcFile = new File("image.jpg"); File destFile = new File("image2.jpg"); // fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); // 复制的过程 byte[] buffer = new byte[5]; int len; while ((len = fis.read(buffer)) != -1) { fos.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null) { // try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
6. 使用 FileputStream 和 FileOutputStream 复制文件的方法测试
// 指定路径下文件的复制 public void copyFile(String srcpath, String destpath) { FileInputStream fis = null; FileOutputStream fos = null; try { // File srcFile = new File(srcpath); File destFile = new File(destpath); // fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); // 复制的过程 byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { fos.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null) { // try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void testCopyFile() { long start = System.currentTimeMillis(); String srcPath = "C:\\Users\\Desktop\\01-视频.mp4"; String destPath = "C:\\Users\\Desktop\\02-视频.mp4"; copyFile(srcPath, destPath); long end = System.currentTimeMillis(); System.out.println("复制操作花费的时间为:" + (end - start)); } }
private static int DEFAULT_BUFFER_SIZE = 8192;
缓冲流要 “套接” 在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
BufferedInputStream 和 BufferedOutputStream
BufferedReader 和 BufferedWriter
当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区。
当使用BufferedInputStream读取字节文件时,BufferedInputStream 会一次性从文件中读取 8192 个 (8Kb) 存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个 8192 个字节数组。
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法 flush() 可以强制将缓冲区的内容全部写入输出流。
关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,因为关闭外层流的的同时,内层流也会自动的进行关闭。所以关于内层流的关闭,我们可以省略。
flush() 方法的使用:刷新缓冲区,调用后缓冲区未满也直接写出,清空缓冲区。即:手动将 buffer 中内容写入文件。
如果是带缓冲区的流对象的 close() 方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出。
1. 缓冲流(字节型)实现非文本文件的复制
@Test public void BufferedStreamTest() { BufferedInputStream bis = null; BufferedOutputStream bos = null; try { // 1. 造文件 File srcFile = new File("image.jpg"); File destFile = new File("image3.jpg"); // 2. 造流 // 2.1 造节点流 FileInputStream fis = new FileInputStream(srcFile); FileOutputStream fos = new FileOutputStream(destFile); // 2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); // 3. 复制的细节:读取、写入 byte[] buffer = new byte[10]; int len; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); // bos.flush(); // 刷新缓冲区,调用后缓冲区未满也直接写出,清空缓冲区 } } catch (IOException e) { e.printStackTrace(); } finally { // 4. 资源关闭 // 要求:先关闭外层的流,再关闭内层的流 if(bos != null) { try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } } // 说明:关闭外层流的的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略。 // fos.close(); // fis.close(); }
2. 缓冲流(字符型)实现文本文件的复制
/* 使用 BufferedReader 和 BufferedWriter 实现文本文件的复制(言外之意:不能处理非文本文件) */ @Test public void testBufferedReaderBufferedWriter() { BufferedReader br = null; BufferedWriter bw = null; try { // 创建文件和相应的流:造文件,造节点流,造缓冲流合成一步 br = new BufferedReader(new FileReader(new File("dbcp.txt"))); bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt"))); // 读写操作 // 方式一:使用 char[] 数组 // char[] cbuf = new char[1024]; // int len; // while ((len = br.read(cbuf)) != -1) { // bw.write(cbuf, 0, len); // // bw.flush(); // } // 方式二:使用 String String data; while ((data = br.readLine()) != null) { // 方法一 // bw.write(data + "\n"); // data 中不包含换行符 // 方法二 bw.write(data); bw.newLine(); // 提供换行的操作 } } catch (IOException e) { e.printStackTrace(); } finally { // 关闭资源 if (bw != null) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vv3MI4h6-1640706447495)(D:\单宇楠\md文档\md插图\IO流5.jpg)]
InputStreamReader
实现将字节的输入流按指定字符集转换为字符的输入流。需要和 InputStream “套接”。
构造器:
charsetName 为字符集的名称,如:InputStreamReader isr = new InputStreamReader(fis, “UTF-8”);
OutputStreamWriter
实现将字符的输出流按指定字符集转换为字节的输出流。需要和 OutputStream “套接”。
构造器:
InputStreamReader 的使用,实现字节的输入流到字符的输入流的转换
/* 此时处理异常仍应该使用 try-catch-finally(偷懒写法) */ @Test public void test1() throws IOException { FileInputStream fis = new FileInputStream("dbcp.txt"); // InputStreamReader isr = new InputStreamReader(fis); // 使用系统默认的字符集 // 参数 2 指明了字符集,具体使用哪个字符集取决于文件 dbcp.txt 保存时使用的字符集 InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1) { String str = new String(cbuf, 0, len); System.out.print(str); } isr.close(); }
转换流实现文件的读入和写出,综合使用 InputStreamReader 和 OutputStreamWriter
/* 此时处理异常仍应该使用 try-catch-finally(偷懒写法) */ @Test public void test2() throws Exception { // 1.造文件、造流 File file1 = new File("dbcp.txt"); File file2 = new File("dbcp_gbk.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis, "utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk"); // 2.读写过程 char[] cbuf = new char[20]; int len; while ((len = isr.read(cbuf)) != -1) { osw.write(cbuf, 0, len); } // 3.关闭资源 isr.close(); osw.close(); }
在 java.lang.System 类中,定义了系统标准输入流 in 和 标准输出流 out。系统标准流在 Java 程序运行时会自动提供,标准输入流 System.in 将会读取键盘的输入,标准输出流将数据在控制台窗口中输出。
重定向:通过 System 类的 setIn,setOut 方法对默认设备进行改变。setIn(InputStream is) / setOut(PrintStream ps) 重新指定输入和输出的流。
从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入 “e” 或者 “exit” 时,退出程序。
public static void main(String[] args) { BufferedReader br = null; try { InputStreamReader isr = new InputStreamReader(System.in); br = new BufferedReader(isr); while (true) { System.out.println("请输入字符串:"); String data = br.readLine(); // 这种写法避免空指针 if("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) { System.out.println("程序结束"); break; } String upperCase = data.toUpperCase(); System.out.println(upperCase); } } catch (IOException e) { e.printStackTrace(); } finally { if(br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
实现将基本数据类型的数据格式转换为字符串输出。
打印流:PrintStream 和 PrintWriter
将 ASCII 码打印到 D: \IO\text.txt 文件中。
PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt")); // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区) ps = new PrintStream(fos, true); if (ps != null) {// 把标准输出流(控制台输出)改成文件 System.setOut(ps); } //如果只有 for 循环中的内容,就是在控制台中打印 ASCII 码 for (int i = 0; i <= 255; i++) { // 输出ASCII字符 System.out.print((char) i); if (i % 50 == 0) { // 每50个数据一行 System.out.println(); // 换行 } } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (ps != null) { ps.close(); } }
为了方便地操作 Java 语言的基本数据类型和 String 的数据,可以使用数据流。
数据流有两个类:(用于读取和写出基本数据类型、String类的数据)
DataInputStream 中的常用方法:
DataOutputStream 中的常用方法:
将内存中的字符串、基本数据类型的变量写出到文件中。
@Test public void test3() throws IOException { // 偷懒抛异常 // 1. DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt")); //2. dos.writeUTF("中国人"); // dos.flush(); // 刷新操作,将内存中的数据写入文件 dos.writeInt(18); // dos.flush(); dos.writeBoolean(true); // dos.flush(); //3. dos.close(); }
将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。
@Test public void test4() throws IOException { // 偷懒抛异常 // 注意:读取不同类型的数据的顺序要与当初写入文件时,保存的数据顺序一致。 // 1. DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); // 2. String name = dis.readUTF(); int age = dis.readInt(); boolean isMale = dis.readBoolean(); System.out.println("name = " + name); System.out.println("age = " + age); System.out.println("isMale = " + isMale); // 3. dis.close(); }
对象流包括 ObjectInputStream 和 OjbectOutputSteam 。用于存储和读取基本数据类型或对象的处理流。它的强大之处就是可以把 Java 中的对象写入到数据源中,也能把对象从数据源中还原回来。
序列化与反序列化
注意:ObjectOutputStream 和 ObjectInputStream 不能序列化 static 和 transient 修饰的成员变量。
对象的序列化
对象序列化机制允许把内存中的 Java 对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的 Java 对象。序列化的好处在于可将任何实现了 Serializable 接口的对象转化为字节数据,使其在保存和传输时可被还原。
注意:如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出 NotSerializableException 异常。
凡是实现 Serializable 接口的类都有一个表示序列化版本标识符的静态变量:private static final long serialVersionUID。serialVersionUID 用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象
进行版本控制,有关各版本反序列化时是否兼容。如果类没有显示定义这个静态常量,它的值是 Java 运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,
显式声明。
简单来说,Java 的序列化机制是通过在运行时判断类的 serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体类的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
对象流序列化与反序列化字符串操作
/* 序列化过程:将内存中的 java 对象保存到磁盘中或通过网络传输出去 使用 ObjectOutputStream 实现 */ @Test public void testObjectOutputStream() { ObjectOutputStream oos = null; try { // 1.造流 oos = new ObjectOutputStream(new FileOutputStream("object.dat")); // 2.造对象 oos.writeObject(new String("我爱北京天安门")); oos.flush(); // 刷新操作 } catch (IOException e) { e.printStackTrace(); } finally { if(oos != null) { try { // 3.关闭流 oos.close(); } catch (IOException e) { e.printStackTrace(); } } } /* 反序列化:将磁盘文件中的对象还原为内存中的一个 java 对象 使用 ObjectInputStream 来实现 */ @Test public void testObjectInputStream() { ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("object.dat")); Object obj = ois.readObject(); String str = (String) obj; System.out.println(str); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
自定义类实现序列化与反序列化操作
若某个类实现了 Serializable 接口,该类的对象就是可序列化的:
反序列化:
注意:如果某个类的属性不是基本数据类型或 String 类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化。
Person 类
/** * Person 需要满足如下要求,方可序列化 * 1.需要实现接口:Serializable * 2.当前类提供一个全局常量:serialVersionUID * 3.除了当前 Person 类需要实现 Serializable 接口之外,还必须保证其内部所有属性也是可序列化的。 (默认情况下,基本数据类型是可序列化) 注意:ObjectOutputStream 和 ObjectInputStream 不能序列化 static 和 transient 修饰的成员变量。 */ public class Person implements Serializable { public static final long serialVersionUID = 435345324535L; private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
测试类
// 序列化 @Test public void testObjectOutputStream() { ObjectOutputStream oos = null; try { // 1. oos = new ObjectOutputStream(new FileOutputStream("object.dat")); // 2. oos.writeObject(new String("我爱北京天安门")); oos.flush(); // 刷新操作 oos.writeObject(new Person("张三", 23)); oos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if(oos != null) { try { // 3. oos.close(); } catch (IOException e) { e.printStackTrace(); } } } } // 反序列化 @Test public void testObjectInputStream() { ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("object.dat")); Object obj = ois.readObject(); String str = (String) obj; Person p = (Person) ois.readObject(); System.out.println(str); System.out.println(p); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } }
RandomAccessFile 声明在 java.io 包下,但直接继承于 java.lang.Object 类。并且它实现了 DataInput、DataOutput 这两个接口,也就意味着这个类既可以读也可以写。RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件。
RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。
RandomAccessFile 类对象可以自由移动记录指针:
构造器
创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
如果模式为只读 r。则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。 如果模式为rw读写,如果文件不存在则会去创建文件,如果存在则不会创建。
RandomAccessFile 实现数据的读写操作
/** * RandomAccessFile 的使用 * 1.RandomAccessFile 直接继承于 java,lang.Object 类,实现了 DataInput 和 DataOutput 接口 * 2.RandomAccessFile 既可以作为一个输入流,又可以作为一个输出流 * 3.如果 RandomAccessFile 作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。 * 如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下从头覆盖) * * 4.可以通过相关的操作,实现 RandomAccessFile ”插入“数据的效果 */ @Test public void test1() throws FileNotFoundException { RandomAccessFile rad1 = null; RandomAccessFile rad2 = null; try { // 1. rad1 = new RandomAccessFile(new File("image.jpg"), "r"); rad2 = new RandomAccessFile(new File("image1.jpg"), "rw"); // 2. byte[] buffer = new byte[1024]; int len; while((len = rad1.read(buffer)) != -1) { rad2.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { // 3. if(rad1 != null) { try { rad1.close(); } catch (IOException e) { e.printStackTrace(); } } if(rad2 != null) { try { rad2.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Test public void test2() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hi.txt", "rw"); raf1.write("xyz".getBytes()); raf1.close(); }
RandomAccessFile 实现数据的插入操作
/* 使用 RandomAccessFile 实现数据的插入效果 */ @Test public void test3() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hi.txt", "rw"); raf1.seek(3); // 将指针调到角标为 3 的位置 // 保存指针 3 后面的所有数据到 StringBuilder 中 StringBuilder builder = new StringBuilder((int) new File("hi.txt").length()); // 指定 builder 的长度 byte[] buffer = new byte[20]; int len; while((len = raf1.read(buffer)) != -1) { builder.append(new String(buffer, 0, len)); } // 调回指针,写入 ”xyz“ raf1.seek(3); raf1.write("xyz".getBytes()); // 将 StringBuilder 中的数据写入到文件中 raf1.write(builder.toString().getBytes()); // 先 toString 得到一个字符串 }
编码表的由来
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
常见的编码表
Java NIO (New IO,Non-Blocking IO) 是从 Java 1.4 版本开始引入的一套新的 IO API,可以替代标准的Java IO API。NIO 与原来的 IO 有同样的作用和目的,但是使用的方式完全不同,NIO 支持面向缓冲区的(IO 是面向流的)、基于通道的 IO 操作。NIO 将以更加高效的方式进行文件的读写操作。
Java API 中提供了两套 NIO,一套是针对标准输入输出 NIO,另一套就是网络编程 NIO。
随着 JDK 7 的发布,Java 对 NIO 进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。因为 NIO 提供的一些功能,NIO 已经成为文件处理中越来越重要的部分。
早期的 Java 只提供了一个 File 类来访问文件系统,但 File 类的功能比较有限,所提供的方法性能也不高。而且大多数方法在出错时仅返回失败,并不会提供异常信息。NIO. 2 为了弥补这种不足,引入了 Path 接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path 可以看成是 File类 的升级版本,实际引用的资源也可以不存在。
在以前 IO 操作都是这样写的:
import java.io.File; File file = new File("index.html");
但在 Java 7 中,可以这样写:
import java.nio.file.Path; import java.nio.file.Paths; Path path = Paths.get("index.html");
同时,NIO.2 在 java.nio.file 包下还提供了 Files、Paths 工具类,Files 包含了大量静态的工具方法来操作文件;Paths 则包含了两个返回 Path 的静态工厂方法。
Paths 类提供的静态 get() 方法用来获取 Path 对象:
Path 常用方法:
java.nio.file.Files 用于操作文件或目录的工具类。
Files 常用方法:
Files 常用方法:用于判断
Files 常用方法:用于操作内容
java.nio.file.Files 用于操作文件或目录的工具类。
Files 常用方法:
Files 常用方法:用于判断
Files 常用方法:用于操作内容