当前位置: 首页 > news >正文

上海的网站建设酒店如何做网络推广

上海的网站建设,酒店如何做网络推广,wordpress 选择用户登录,设计平台市场分析Java I/O流讲解 每博一文案 谁让你读了这么多书#xff0c;又知道了双水村以外还有一个大世界#xff0c;如果从小你就在这个天地里#xff0c;日出而作#xff0c;日落而息。 那你现在就会和众乡亲抱同一理想#xff1a;经过几年的辛劳#xff0c;像大哥一样娶个满意的…Java I/O流讲解 每博一文案 谁让你读了这么多书又知道了双水村以外还有一个大世界如果从小你就在这个天地里日出而作日落而息。 那你现在就会和众乡亲抱同一理想经过几年的辛劳像大哥一样娶个满意的媳妇生个胖儿子加上你的体魄 会成为一名出色的庄稼人。不幸的是你知道的太多了思考的太多了因此才有了这种不能为周围人所理解的苦恼。—————— 《平凡的世界》 人生是这样的不可预测没有永恒的痛苦也没有永恒的幸福生活就像流水一般 有时是那么平展有时又是那么曲折。 世界上有些人因为忙而感到生活的沉重也有些人因为闲而活得压抑人啊都有自己一本难念的经 可是不同处境的人又很难理解别人的苦处。 细想过来每个人的生活也同样是一个世界即使是最平方的人也要为他那个世界的存在而战斗。—————— 《平凡的世界》 文章目录Java I/O流讲解每博一文案1. File 类1.1 File 类中构造器1.2 File 类中路径分隔符1.3 File 类中常用方法1.4 实用案例:2. I/O 流的概述2.1 I/O的分类和体系结构2.1.1 输入流 和 输出流2.1.2 字节流 和 字符流2.1.3 节点流 和 处理流(包装流)2.1.4 流的概念模型2.1.5 I/O 的体系结构2.2 字符流2.2.1 java.io.FileReader 字符输入流2.2.2 java.io.FileWriter 字符输出流2.2.3 实例文本文件的拷贝2.3 字节流2.3.1 FileInputStream 字节输入流2.3.2 FileOutputStream 字节输出流2.3.3 实例图片文件的拷贝2.3.4 实例对图片的简单加密3.1 缓冲流3.1.1 BufferedReader (字符输入缓冲流) / BufferedWriter (字符输出缓冲流)3.1.2 BufferedInputStream(字节输入缓冲流) / BufferedOutputStream (字节输出缓冲流)4.1 转换流4.2 标准输入\输出流4.3 数据流4.4 对象流 序列化 ObjectOutputStream反序列化 ObjectInputStream4.4.1 对象的序列化4.4.2 对象的反序列化4.4.3 不显式定义 serialVersionUID 值的问题4.4.3.1 设置不被序列化的类型5. 实用案例:6. 文件 I/Onio.27. 总结10. 最后1. File 类 java.io.File 类文件和文件目录路径的抽象表示形式与平台无关。 File 能新建删除重命名文件和目录但File 不能访问文件内容本身。如果需要访问文件内容本身则需要使用 输入/输出 流。 想要在Java 程序中表示一个真实存在的文件或目录那么必须有一个 File 对象但是 Java 程序中的一个 File 对象可能没有一个真实存在的文件或目录。 File 对象可以作为参数传递给流的构造器。 1.1 File 类中构造器 public File(String pathname); // 过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。如果给定字符串是空字符串那么结果是空抽象路径名 public File(String parent,String child); // 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。以parent为父路径child为子路径创建File对象 public File(File parent, String child); // 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。根据一个父File对象和子文件路径创建File对象 // 路径可以是绝对路径也可以是相对路径绝对路径 是一个固定的路径,从盘符开始。 相对路径 是相对于某个位置开始。IDEA中默认相对路径是从Project项目(路径)下和同级的 src 的路径开始的注意不是模块开始的Module的 。如下图所示src 和 Module 模块是同级的。 1.2 File 类中路径分隔符 路径中的每级目录之间用一个路径分隔符隔开。 路径分隔符和系统有关 windows和DOS系统默认使用“\”来表示需要注意的是在 java 中 \ 具有转义的意思所以想要表示真正的 “\” 需要两个 \\ 来转义回来表示一个斜杆。 UNIX和URL使用“/”来表示。 Java程序支持跨平台运行因此路径分隔符要慎用。 为了解决这个隐患File类提供了一个常量 public static final String separator。// 根据操作系统动态的提供分隔符。File file1 new File(E:\\Test\\info.txt);File file2 new File(E: File.separator Test File.separator info.txt);File file3 new File(E:/Test);// 这三者表示的路径是一样的。只是表示方式不同而已。举例: package blogs.blog9;import java.io.File;public class FileTest {/*** File 构造器的使用*/public static void main(String[] args) {// 构造器一:// 绝对路径: 带盘符File file new File(E:\\Java\\JavaRebuilt\\src\\blogs\\blog9); // 双右斜杆表示一个 \ (转义)// 相对路径: IDEA默认是Project的根目录不是模块Module的根目录// 也可以使用左斜杆表示路径分隔符System.out.println(file);File file2 new File(src/blogs/blog9); // 这里是在src的包下(src 和 Module 模块是同级的)System.out.println(file2);// 构造器二:// 第一个参数是第二个参数的父路径第二个参数是子路径File file3 new File(E:\\Java\\JavaRebuilt\\src\\blogs,blog9);System.out.println(file3);// 构造器三:// 第一个参数是 File 类对象(这里是第二个参数的父路径的File对象)第二个参数是子路径File file4 new File(file3,blog9);System.out.println(file4);} } 1.3 File 类中常用方法 获取文件属性的信息的方法 getAbsoluteFile() : 返回此File对象中路径的绝对路径。返回的是 File 对象 public File getAbsoluteFile(); // 返回此抽象路径名的绝对路径名形式。getAbsolutePath() : 返回此抽象路径名的绝对路径名字符串 public String getAbsolutePath(); // 返回此抽象路径名的绝对路径名字符串getPath() 返回此抽象路径名以字符串的形式。 public String getPath(); // 返回此抽象路径名getName() : 获取名称 public String getName(); // 返回由此抽象路径名表示的文件或目录的名称。该名称是路径名名称序列中的最后一个名称。如果路径名名称序列为空则返回空字符串getParent() : 获取上层文件目录路径(也就是父路径)。若无返回null public String getParent(); // 返回此抽象路径名父目录的路径名字符串如果此路径名没有指定父目录则返回 null。length() : 返回文件长度即(字节数) public long length(); // 获取文件长度即字节数。不能获取目录的长度lastModified() : 获取最后一次的修改时间毫秒值。 public long lastModified(); // 获取最后一次的修改时间毫秒值list() : 获取指定目录下的所有文件或者文件目录的名称数组 public String[] list(); // 返回一个字符串数组这些字符串指定此抽象路径名表示的目录中的文件和目录。listFiles() : 获取指定目录下的所有文件或者文件目录的 File 数组 public File[] listFiles(); // 返回一个抽象路径名数组这些路径名表示此抽象路径名表示的目录中的文件。举例: import java.io.File; import java.text.SimpleDateFormat; import java.util.Date;public class FileTest {public static void main(String[] args) {File file new File(src\\blog9\\hello.txt); // 注意转义以及文件后缀File file2 new File(src/blog9/hello3.txt); // 左斜杆也是可以的String absolutePath file.getAbsolutePath(); // 返回绝对路径,以String的形式返回System.out.println(absolutePath);File absoluteFile file.getAbsoluteFile(); // 返回绝对路径,以File 对象的形式返回System.out.println();System.out.println(file.getPath()); // 返回此路径名/目录名System.out.println(file.getName()); // 返回该文件名/目录名System.out.println(file.getParent()); // 返回该上层文件/目录名称System.out.println(file.length()); // 返回文件长度即文件的大小(字节)long l file.lastModified(); // 返回该文件的最后一次修改的时间值(毫秒值)时间戳// 将时间戳转换为Date再转换为 指定格式的字符串Date date new Date(l);SimpleDateFormat simpleDateFormat new SimpleDateFormat(yyyy-MM-dd hh:ss:mm SSS);String format simpleDateFormat.format(date);System.out.println(format);} }举例: import java.io.File; import java.text.SimpleDateFormat; import java.util.Date;public class FileTest {/*** File 文件目录*/public static void main(String[] args) {File file new File(src/blogs/blog9); // 也可以使用 左斜杆String[] list file.list(); // 返回获取指定目录下的所有文件或者文件目录的名称数组for (String s : list) {System.out.println(s);}File[] files file.listFiles();for(File f : files) {System.out.println(f);}} }File类的重命名功能 renameTo(File dest) 把文件重命名为指定的文件路径。换句话说就是剪切附加对文件的重命名 的意思。 public boolean renameTo(File dest); // 重新命名此抽象路径名表示的文件。 注意 这里的剪切效果有一定的要求就是比如file.renameTo(dest) 想要将 file 剪切到 dest 位置路径上。要保证 file 剪切的文件实际在硬盘中存在并且 dest 不能在硬盘文件中存在(仅仅当一个路径)。如果不满足会失败返回 false 举例 import java.io.File;public class FileTest {public static void main(String[] args) {File file new File(src\\blogs\\blog9\\hello.txt);File dest new File(E:\\临时文件\\temp\\test.txt);boolean b file.renameTo(dest); // 将file文件剪切到 dest 中并重命名System.out.println(b);} }失败原因是dest 中的 test.txt 是在硬盘中实际存在的。 将test.txt去了就没事了 再重新剪切就可以了。 File 类的判断功能 isDirectory() 判断是否是文件目录 public boolean isDirectory(); // 测试此抽象路径名表示的文件是否是一个目录。isFile() 判断是否是文件 public boolean isFile(); // 当且仅当此抽象路径名表示的文件存在且 是一个标准文件时返回 true否则返回 false exists() 判断该文件/目录是否存在 public boolean exists(); // 测试此抽象路径名表示的文件或目录是否存在canRead() 判断该文件是否可读的 public boolean canRead(); // 当且仅当此抽象路径名指定的文件存在且 可被应用程序读取时返回 true否则返回 false canWrite() 判断该文件是否可写的 public boolean canWrite(); // 当且仅当文件系统实际包含此抽象路径名表示的文件且 允许应用程序对该文件进行写入时返回 true否则返回 false.isHidden() : 判断该文件是否隐藏的 public boolean isHidden(); // 当且仅当此抽象路径名表示的文件根据底层平台约定是隐藏文件时返回 true 举例 import java.io.File;public class FileTest {public static void main(String[] args) {File file new File(src\\blogs\\blog9\\hello.txt); // 注意转义以及文件后缀(该文件实际存在的)File file2 new File(src/blogs/blog9/hello3.txt); // 左斜杆也是可以的 (该文件不存在的)System.out.println(file.isDirectory()); // 判断是否是文件目录System.out.println(file.isFile()); // 判断是否为文件System.out.println(file.exists()); // 判断该文件/目录是否实际存在System.out.println(file.canRead()); // 判断该我呢见是否可读的System.out.println(file.canWrite()); // 判断该文件是否是可写的System.out.println(file.isHidden()); // 判断该文件是否隐藏的System.out.println(*************************** file2 *************************);System.out.println(file2.isDirectory()); // 判断是否是文件目录System.out.println(file2.isFile()); // 判断是否为文件System.out.println(file2.exists()); // 判断该文件/目录是否实际存在System.out.println(file2.canRead()); // 判断该我呢见是否可读的System.out.println(file2.canWrite()); // 判断该文件是否是可写的System.out.println(file2.isHidden()); // 判断该文件是否隐藏的} }File 类的创建功能 **createNewFile() ** 创建文件。若文件存在则不创建返回false public boolean createNewFile() throws IOException // 如果指定的文件不存在并成功地创建则返回 true如果指定的文件已经存在则返回 false mkdirs() 创建文件目录。创建文件目录。如果上层文件目录不存在一并创建 public boolean mkdirs(); // 创建此抽象路径名指定的目录包括所有必需但不存在的父目录。注意此操作失败时也可能已经成功地创建了一部分必需的父目录。mkdir() 创建文件目录。如果此文件目录存在就不创建了。如果此文件目录的上层目录不存在也不创建。 public boolean mkdir(); // 创建此抽象路径名指定的目录。 注意事项如果你创建文件或者文件目录没有写盘符路径那么默认在项目路径下。 举例 import java.io.File; import java.io.IOException;public class FileTest {public static void main(String[] args) {File file new File(src/blogs/blog9/hello.txt);boolean b false;try {b file.createNewFile(); // 文件存在不创建不存在文件创建} catch (IOException e) {e.printStackTrace();}System.out.println(b);} }创建目录 使用 mkdir import java.io.File;public class FileTest {public static void main(String[] args) {File file new File(src/blogs/blog9/test/test2/);boolean b file.mkdir(); // 如果对应的 test2目录的上级目录test不存在则目录都不创建System.out.println(b);} }使用 mkdirs() import java.io.File; import java.io.IOException;public class FileTest {public static void main(String[] args) {File file new File(src/blogs/blog9/test/test2/);boolean b file.mkdirs(); // 如果对应的 test2目录的上级目录test不存在则目录一并都创建System.out.println(b);} }File类的删除功能 delete() : 删除文件或者文件夹 public boolean delete(); // 删除此抽象路径名表示的文件或目录。如果此路径名表示一个目录则该目录必须为空才能删除。删除注意事项Java中的删除不走回收站。要删除一个文件目录请注意该文件目录内不能包含文件或者文件目录。如果含有无法删除的。 举例 import java.io.File;public class FileTest {public static void main(String[] args) {File file new File(src/blogs/blog9/test);boolean b file.delete(); // test 目录下不能有文件/目录有的话无法删除System.out.println(b);} }小结 1.4 实用案例: 判断指定目录下是否有后缀名为.jpg的文件如果有就输出该文件名称 package com.atguigu.exer2;import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter;import org.junit.Test; /** 判断指定目录下是否有后缀名为.jpg的文件如果有就输出该文件名称*/ public class FindJPGFileTest {Testpublic void test1(){File srcFile new File(d:\\code);String[] fileNames srcFile.list();for(String fileName : fileNames){if(fileName.endsWith(.jpg)){System.out.println(fileName);}}}Testpublic void test2(){File srcFile new File(d:\\code);File[] listFiles srcFile.listFiles();for(File file : listFiles){if(file.getName().endsWith(.jpg)){System.out.println(file.getAbsolutePath());}}}/** File类提供了两个文件过滤器方法* public String[] list(FilenameFilter filter)* public File[] listFiles(FileFilter filter)*/Testpublic void test3(){File srcFile new File(d:\\code);File[] subFiles srcFile.listFiles(new FilenameFilter() {Overridepublic boolean accept(File dir, String name) {return name.endsWith(.jpg);}});for(File file : subFiles){System.out.println(file.getAbsolutePath());}}} 遍历指定目录所有文件名称包括子文件目录中的文件。 拓展1并计算指定目录占用空间的大小 拓展2删除指定文件目录及其下的所有文件 package com.atguigu.exer2;import java.io.File; /*** 3. 遍历指定目录所有文件名称包括子文件目录中的文件。拓展1并计算指定目录占用空间的大小拓展2删除指定文件目录及其下的所有文件* author shkstart 邮箱shkstart126.com* version 创建时间2019年2月23日 上午1:55:31**/ public class ListFilesTest {public static void main(String[] args) {// 递归:文件目录/** 打印出指定目录所有文件名称包括子文件目录中的文件 */// 1.创建目录对象File dir new File(E:\\teach\\01_javaSE\\_尚硅谷Java编程语言\\3_软件);// 2.打印目录的子文件printSubFile(dir);}public static void printSubFile(File dir) {// 打印目录的子文件File[] subfiles dir.listFiles();for (File f : subfiles) {if (f.isDirectory()) {// 文件目录printSubFile(f);} else {// 文件System.out.println(f.getAbsolutePath());}}}// 方式二循环实现// 列出file目录的下级内容仅列出一级的话// 使用File类的String[] list()比较简单public void listSubFiles(File file) {if (file.isDirectory()) {String[] all file.list();for (String s : all) {System.out.println(s);}} else {System.out.println(file 是文件);}}// 列出file目录的下级如果它的下级还是目录接着列出下级的下级依次类推// 建议使用File类的File[] listFiles()public void listAllSubFiles(File file) {if (file.isFile()) {System.out.println(file);} else {File[] all file.listFiles();// 如果all[i]是文件直接打印// 如果all[i]是目录接着再获取它的下一级for (File f : all) {listAllSubFiles(f);// 递归调用自己调用自己就叫递归}}}// 拓展1求指定目录所在空间的大小// 求任意一个目录的总大小public long getDirectorySize(File file) {// file是文件那么直接返回file.length()// file是目录把它的下一级的所有大小加起来就是它的总大小long size 0;if (file.isFile()) {size file.length();} else {File[] all file.listFiles();// 获取file的下一级// 累加all[i]的大小for (File f : all) {size getDirectorySize(f);// f的大小;}}return size;}// 拓展2删除指定的目录public void deleteDirectory(File file) {// 如果file是文件直接delete// 如果file是目录先把它的下一级干掉然后删除自己if (file.isDirectory()) {File[] all file.listFiles();// 循环删除的是file的下一级for (File f : all) {// f代表file的每一个下级deleteDirectory(f);}}// 删除自己file.delete();}} 2. I/O 流的概述 一个 I / O流 代表输入源或输出目的地。流可以表示许多不同种类的源和目的地包括磁盘文件设备其他程序和存储器阵列。 流支持许多不同类型的数据包括简单字节原始数据类型本地化字符和对象。一些流简单地传递数据; 其他人以有用的方式操纵和转换数据。 I/O 其中的 I 是 Input 的缩写O 是 Output 的缩写。I/O 技术是非常实用的技术用于处理设备之间的数据传输。如读/写文件网络通讯等。 Java 程序中对于数据的输入/输出操作以 流(stream) 的方式进行。 java.io 包下提供了各种 “流”类和接口用以获取不同种类的数据并通过方法输入或输出数据。 无论内部工作如何所有流都会使用与使用它们的程序相同的简单模型 流是一系列数据。程序使用 输入流 从源中读取数据input 输入流以内存为参考对象(将文件中的数据内容写入到内存当中) 以及 Read (以文件为参考对象将读取文件中的数据到内存当中)。这两个都是将意思都是一样的将文件中的数据读取出来写入到内存当中。 程序使用 输出流 将数据写入目的地。Output 输出流以内存为参考对象(将内存中的数据内容输出到硬盘文件当中) 以及 Write (以文件为参考对象将内存中的数据到写入到硬盘文件当中)。这两个都是将意思都是一样的将内存中的数据输出到硬盘文件当中。 2.1 I/O的分类和体系结构 按照不同的分类方式 可以将流分为不同的类型。 2.1.1 输入流 和 输出流 按照流的流向来分 可以分为输入流和输出流 输入流 只能从中读取数据 而不能向其写入数据。 输出流 只能向其写入数据 而不能从中读取数据。 此处的输入、 输出涉及一个方向问题 对于如图 1 所示的数据流向 数据从内存到硬盘 通常称为输出流——也就是说 这里的输入、 输出都是从程序运行所在内存的角度来划分的。 对于如图 2 所示的数据流向 数据从服务器通过网络流向客户端 在这种情况下 Server 端的内存负责将数据输出到网络里 因此 Server 端的程序使用输出流 Client 端的内存负责从网络里读取数据 因此 Client 端的程序应该使用输入流。 2.1.2 字节流 和 字符流 按操作数据单位不同分为字节流(8 bit)字符流(16 bit)。 字节流和字符流的用法几乎完全一样 区别在于字节流和字符流操作的数据单元的不同字节流是 8 位的字节 而字符流操作的数据单元是 16 位的字符。其中还有一点不同的就是 字符流只能读取操作文本文件因为字符流读取的是文件中的 char 字符信息。 .c.java.c.txt 等等这些都是文本文件不仅仅只是 txt文件而特别注意的是 .wrod 不是文本文件wrod中的文字是经过特殊处理的存在一定的规范格式不是纯文本文件。字节流可以操作任何的文件因为字节流读取的是二进制信息读取1个字节byte,等同于一次读取8个二进制这种流是万能的什么类型的文件都可以读取到因为文件都是有二进制组成的。包括: 文本文件图片声音文件。 2.1.3 节点流 和 处理流(包装流) 按流的角色的不同分为节点流处理流。 节点流 所谓的节点流就是最基本的一个流到底目的的流向其中的流没有被其它的流所包含住。直接从数据源或目的地读写数据。如下图所示 处理流 处理流又称为包装流 处理流对一个己存在的流进行连接或封装/包装 通过封装后的流来实现数据读/写功能。不直接连接到数据源或目的地而是“连接”在已存在的流节点流或处理流之上通过对数据的处理为程序提 供更为强大的读写功能。如下图所示 注意 节点流和包装流是相对的有时候相对于不同的流的一个节点流变成了是另一个流的包装流。一个包装流变成了另一个流的节点流 。如下图所示 区分一个流是节点流还是包装流大家可以以谁包装谁作为参考被包装的流就是节点流包装了其它的流的就是包装流当然注意这是相对的。 2.1.4 流的概念模型 Java 把所有设备里的有序数据抽象成流模型 简化了输入/输出处理 理解了流的概念模型也就了解了Java IO。 通过使用处理流 Java 程序无须理会输入/输出节点是磁盘、 网络还是其他的输入/输出设备 程序只要将这些节点流包装成处理流 就可以使用相同的输入/输出代码来读写不同的输入/输出设备的数据。 2.1.5 I/O 的体系结构 Java的IO流共涉及40多个类实际上非常规则都是从如下 Java Io 流四大家族 个 抽象基类派生的。 由以下这四个类派生出来的子类。其名称都是以其父类名作为子类名后缀。这样用于我们辨认。 Java IO流四大家族 java.io.InputStream 字节输入流类名是以 stream 结尾的。 public abstract class InputStream implements Closeable {}java.io.OutputStream 字节输出流类名是以 stream 结尾的。 public abstract class OutputStream implements Closeable, Flushable {}java.io.Reader 字符输入流类名是以 Reader/Writer结尾的。 public abstract class Reader implements Readable, Closeable {}java.io.Writer 字符输出流类名是以以 Reader/Writer结尾的。 public abstract class Writer implements Appendable, Closeable, Flushable {}上述四大家族的首领都是抽象类 abstract class。这四个类都实现了 java.io.Closeable 接口都是可以关闭的都有 close() 方法。流毕竟是一个管道这个内存和硬盘之间的通道用完之后一定要关闭。不然会耗费很多资源因为Java打开的资源是有限的当你打开过多的资源超过限制时就无法打开其它的资源了。养成好习惯用完之后一定要关闭(必须关闭)。 这四个类都实现了java.io.Flushable 接口都是可刷新 的都是有 flush() 方法的养成一个好习惯输出流(将内存当中的数据输出到硬盘文件当中)在最终(输出完)之后一定要记得 flush() 刷新一下这个刷新的作用就是清空管道(强制将内存当中的数据输出到文件中)。为什么要清空管道呢因为如果没有 flush() 可以会导致内存中一部分的数据并没有全部输出到硬盘当中从而导致一部分的数据丢失。注意在Java中只要 类名是以 stream 结尾的都是字节流以 Reader/Writer结尾的都是字符流。 java.io包下需要掌握的流有 16个文件专属: java.io.FileInputStream java.io.FileOutputStream 字节流无法读取到 文件中的空格的 java.io.FileReader java.io.FileWriter 字符流可以读取到文件中的空格的转换流: (将字节流转换字符流) java.io.InputStreamReader java.io.OutputStreamWriter缓冲流专属: java.io.BufferedReader java.io.BufferedWriter java.io.BufferedInputStream java.io.BufferedOutputStream java.io.BufferedOutputStream数据流专属: java.io.DataInputStream java.io.DataOutputStream标准输出流: java.io.PrintWtiter java.io.PrinStream对象专属流 java.io.ObjectInputStream java.io.ObjectOutputStream2.2 字符流 2.2.1 java.io.FileReader 字符输入流 关于字符输入流的类都是继承了java.io.Writer(字符输出流)/ java.io.Reader (字符输入流) 来使用的但是这个两个类是抽象类是无法 new 对象来使用的。所以我们就需要使用其实现的子类对于文件字符输入流比较常用的就是 java.io.FileReader 这个子类了。 字符流 只能读取文本文件不能读取其它格式的文件文本文件不仅仅是 .txt 后缀的文件.c.java.c 都是文本文件注意 word 不是文本文件因为 word 中的文本字符是有一个规范格式设置的。不是纯的文本文件。字符流操作字符只能操作普通文本文件。最常见的文本文件.txt.java.c.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文本文件。 在读取文件时必须保证该文件已存在否则报异常。 其中继承的 InputStreamReader 是个转换流继承了 Reader 抽象类的。 FileReader的构造器 public FileReader(File file) throws FileNotFoundException; // 在给定从中读取数据的 File 的情况下创建一个新 FileReader对象 public FileReader(String fileName) throws FileNotFoundException; // 根据文件的相对路径名/绝对路径创建一个新 FileReader对象举例 import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader;public class FileReaderTest {public static void main(String[] args) {File file new File(E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\hello.txt); // 绝对路径try {FileReader fileReader new FileReader(file);} catch (FileNotFoundException e) {e.printStackTrace();}try {FileReader fileReader2 new FileReader(src/blogs/blog9/hello.txt); // 相对路径} catch (FileNotFoundException e) {e.printStackTrace();}} } 如下是 FileReader 继承 java.io.InputStreamReader 继承的方法 read() : 读取单个字符。作为整数读取的字符范围在 0 到 65535 之间 (0x00-0xffff)2个字节的Unicode码如果已到达流的末尾则返回 -1。 public int read() throws IOException; // 读取单个字符。 int read(char[] cbuf) : 将字符读入数组。如果已到达流的末尾则返回 -1。否则返回本次读取的字符数。 public int read(char[] cbuf) throws IOException; // 将文件中的数据读取到char[] cbuf的字符数组当中返回读取到的个数。int read(char[] cbuf,int off,int len) : 将字符读入数组的某一部分。存到数组cbuf中从off处开始存储最多读len个字 符。如果已到达流的末尾则返回 -1。否则返回本次读取的字符数。 public int read(char[] cbuf,int offset, int length) throws IOException; // 将字符读入数组中的某一部分.public void close() throws IOException 关闭此输入流并释放与该流关联的所有系统资源。 public void close() throws IOException; // 关闭此输入流并释放与该流关联的所有系统资源。**需要明白**对于 read() 读取文件内容的方式是一个一个字符的读取的每调用一次 read()对于的文件中的光标就会往后移动一下。对于特殊的 read(char[ ] cbuf) 读取的个数是 char[] 数组的长度往后移动光标的位置也是 char[] 数组的长度。以及返回的是对于字符的编码值。 设文件 file1.txt 采用字符流的话是这样读的a中国bo张三第一次读: ‘a’字符第二次读: ‘中’字符举例: import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;public class FileReaderTest {public static void main(String[] args) {FileReader fileReader null; // 相对路径try {fileReader new FileReader(src/blogs/blog9/hello.txt);int read fileReader.read(); // 返回的是编码值System.out.println(read);read fileReader.read();System.out.println(read);read fileReader.read();System.out.println(read);read fileReader.read();System.out.println(read);read fileReader.read();System.out.println(read);read fileReader.read();System.out.println(read);read fileReader.read();System.out.println(read);} catch (IOException e) {e.printStackTrace();} finally {// fileReader 防止 null引用if(fileReader ! null) {try {fileReader.close();} catch (IOException e) {e.printStackTrace();}}}} }举例 使用 while() 循环处理 import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;public class FileReaderTest {public static void main(String[] args) {FileReader fileReader null; // 相对路径try {fileReader new FileReader(src/blogs/blog9/hello.txt);int len 0;// 当read()读取到 文件末尾返回 -1跳出循环while((len fileReader.read()) ! -1) {System.out.println(len);}} catch (IOException e) {e.printStackTrace();} finally {// fileReader 防止 null引用if(fileReader ! null) {try {fileReader.close();} catch (IOException e) {e.printStackTrace();}}}} }举例 使用 int read(char[] cbuf) : 将字符读入数组。如果已到达流的末尾则返回 -1。否则返回本次读取的字符数 import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;public class FileReaderTest {public static void main(String[] args) {FileReader fileReader null; // 相对路径try {fileReader new FileReader(src/blogs/blog9/hello.txt);int len 0;char [] chars new char[4];// read(chars) 一次性读取数组长度个字符返回读取到的字符个数。到达文件末尾返回-1while((len fileReader.read(chars)) ! -1) {// 将char[] 数组转换为字符串System.out.println(new String(chars));}} catch (IOException e) {e.printStackTrace();} finally {// fileReader 防止 null引用if(fileReader ! null) {try {fileReader.close();} catch (IOException e) {e.printStackTrace();}}}} }read(char[]) 读取数据时的覆盖效果的讲解 举例 去除 read(char[] cduf) 的覆盖效果我们读取多到了多少个字符就 new String 转换多少个字符 import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;public class FileReaderTest {public static void main(String[] args) {FileReader fileReader null; // 相对路径try {fileReader new FileReader(src/blogs/blog9/hello.txt);int len 0;char [] chars new char[4];// read(chars) 一次性读取数组长度个字符返回读取到的字符个数。到达文件末尾返回-1while((len fileReader.read(chars)) ! -1) {// 将char[] 数组转换为字符串// 这里我们 读取到了多少个字符就将 chars数组中的前多少个转换为字符串System.out.println(new String(chars,0,len));}} catch (IOException e) {e.printStackTrace();} finally {// fileReader 防止 null引用if(fileReader ! null) {try {fileReader.close();} catch (IOException e) {e.printStackTrace();}}}} }2.2.2 java.io.FileWriter 字符输出流 于字符流输出的类都是继承了java.io.Writer(字符输出流)/ java.io.Reader (字符输入流) 来使用的但是这个两个类是抽象类是无法 new 对象来使用的。所以我们就需要使用其实现的子类对于文件字符输出流比较常用的就是 java.io.FileWriter 这个子类了。 其中继承的 OutputStreamWriter 是个转换流继承了 Writer 抽象类的。 OutputStreamWriter 的构造器 public FileWriter(File file) throws IOException; // 根据给定的 File 对象构造一个 FileWriter 对象 public FileWriter(String fileName) throws IOException; // 根据给定的文件名构造一个 FileWriter 对象。 public FileWriter(File file,boolean append) throws IOException; // 根据给定的 File 对象构造一个 FileWriter 对象。如果第二个参数为 true则将字节写入文件末尾处而不是写入文件开始处。 // file - 要写入数据的 File 对象 // append - 如果为 true则将字节写入文件末尾处而不是写入文件开始处 ,默认是 false,不写的话也是 false举例 package blogs.blog9;import java.io.File; import java.io.FileWriter; import java.io.IOException;public class FileWriterTest {public static void main(String[] args) {File file new File(E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\hello.txt); // 绝对路径try {FileWriter fileWriter new FileWriter(file);} catch (IOException e) {e.printStackTrace();}try {FileWriter fileWriter2 new FileWriter(src/blogs/blog9/hello.txt); // 相对路径} catch (IOException e) {e.printStackTrace();}try {FileWriter fileWriter3 new FileWriter(src/blogs/blog9/hello.txt,true);} catch (IOException e) {e.printStackTrace();}} } 如下是 FileWriter 继承 java.io.OutputStreamWriter继承的方法 void write(int c) : 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中16 高位被忽略。 即写入0 到 65535 之间的Unicode码。 public void write(int c) throws IOException; // 写入单个字符。 void write(char[] cbuf) : 将字符数组的内容写入到对应的硬盘文件中去 public void write(char[] cbuf) throws IOException; // 将字符数组的内容写到文件中void write(char[] cbuf,int off,int len) 写入字符数组的某一部分。从off开始写入len个字符。 public void write(char[] cbuf,int off,int len) throws IOException // 写入字符数组的某一部分。void write(String str) 将字符串的内容写入到对应的硬盘文件中去 public void write(Stirng str) throws IOExceptionvoid write(String str,int off,int len) 写入字符串的某一部分。从off开始写入len个结束 public void write(String str,int off,int len) throws IOExceptionvoid flush() 刷新该流的缓冲立即内存中的数据写入预期目标硬盘文件中去。 public void flush() throws IOException; // 刷新该流的缓冲。public void close() 关闭此输出流并释放与该流关联的所有系统资源 public void close() throws IOException; // 关闭此流但要先刷新它。在关闭该流之后再调用 write() 或 flush() 将导致抛出 IOException。关闭以前关闭的流无效。注意 如果写入到的文件不存在是会自动创建的。如果文件已经存在了在创建FileWriter 对象时没有设置为 true 的话是会将原本文件中已经存在的内容覆盖的写入新的内容。 举例 文件不存在自动创建。 import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;public class FileReaderTest {public static void main(String[] args) {FileReader fileReader null; // 相对路径try {fileReader new FileReader(src/blogs/blog9/hello.txt);int len 0;char [] chars new char[4];// read(chars) 一次性读取数组长度个字符返回读取到的字符个数。到达文件末尾返回-1while((len fileReader.read(chars)) ! -1) {// 将char[] 数组转换为字符串// 这里我们 读取到了多少个字符就将 chars数组中的前多少个转换为字符串System.out.println(new String(chars,0,len));}} catch (IOException e) {e.printStackTrace();} finally {// fileReader 防止 null引用if(fileReader ! null) {try {fileReader.close();} catch (IOException e) {e.printStackTrace();}}}} }举例 文件已经存在写入的信息覆盖原本文件的全部内容 import java.io.File; import java.io.FileWriter; import java.io.IOException;public class FileWriterTest {public static void main(String[] args) {FileWriter fileWriter null; // 相对路径try {// 1. 创建输出流对象: FileWriterfileWriter new FileWriter(src/blogs/blog9/hello2.txt);// 2. 将内存当中的内容写入到文件中fileWriter.write(你好世界);// 3. 刷新将内存中没有输出到文件中的内容强制全部写入到文件中fileWriter.flush();} catch (IOException e) {e.printStackTrace();} finally {// 防止 null 引用if(fileWriter ! null) {// 4. 关闭IO资源try {fileWriter.close();} catch (IOException e) {e.printStackTrace();}}}} }举例 创建 **FileWriter ** 对象时设置 true 将内存当中的信息写入到文件的末尾去不会覆盖原本文件中的内容 import java.io.File; import java.io.FileWriter; import java.io.IOException;public class FileWriterTest {public static void main(String[] args) {FileWriter fileWriter null; // 相对路径try {// 1. 创建输出流对象: FileWriter,并设置 true 将写入的内容追加到文件的末尾中去fileWriter new FileWriter(src/blogs/blog9/hello2.txt,true);// 2. 将内存当中的内容写入到文件中fileWriter.write(\n); // 换行fileWriter.write(Hello World);// 3. 刷新将内存中没有输出到文件中的内容强制全部写入到文件中fileWriter.flush();} catch (IOException e) {e.printStackTrace();} finally {// 防止 null 引用if(fileWriter ! null) {// 4. 关闭IO资源try {fileWriter.close();} catch (IOException e) {e.printStackTrace();}}}} }2.2.3 实例文本文件的拷贝 将同目录中的 hello.txt 文件的内容拷贝到 同目录中的 hello2.txt 中去 思路: import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException;public class FileWriterTest {public static void main(String[] args) {FileWriter descFile null;FileReader srcFile null; // 注意文件后缀try {// 1. 创建hello.txt 文件的字符输入流对象以及 hello2.txt文件的字符输出流对象descFile new FileWriter(src/blogs/blog9/hello2.txt);srcFile new FileReader(src/blogs/blog9/hello.txt);// 2. 一边读一边写int len 0;char[] chars new char[10];// 读取hello.txt的数据信息while((len srcFile.read(chars)) ! -1) {// 将读取到的内容写入到hello2.txt文件中descFile.write(chars,0,len);}// 3. 刷新:将内存中遗留没有写入到文件中的信息全部强制写入到文件中去descFile.flush();} catch (IOException e) {e.printStackTrace();} finally {// 5. 关闭IO资源// 分开 try,如果两个一起try的话其中一个出现异常了后面的一个IO就无法关闭了if(srcFile ! null) { // 防止 null引用try {srcFile.close();} catch (IOException e) {e.printStackTrace();}}if(descFile ! null) { // 防止 null引用try {descFile.close();} catch (IOException e) {e.printStackTrace();}}}} }2.3 字节流 2.3.1 FileInputStream 字节输入流 关于字节输入流的类都是继承了InputStream(字节输入流) 来使用的但是个类是抽象类是无法 new 对象来使用的。所以我们就需要使用其实现的子类对于文件字符输入流比较常用的就是 **java.io.FileInputStream ** 这个子类了。 字节流 可以操作任何的文件因为字节流读取的是二进制信息读取1个字节byte,等同于一次读取8个二进制这种流是万能的什么类型的文件都可以读取到因为文件都是有二进制组成的。包括: 文本文件图片声音文件。再比如比如.mp3.avi.rmvbmp4.jpg.doc.ppt等文件。 FileInputStream的构造器 public FileInputStream(File file) throws FileNotFoundException; // 通过打开一个到实际文件的连接来创建一个 FileInputStream该文件通过文件系统中的 File 对象 file 指定。 public FileInputStream(String name) throws FileNotFoundException; // 通过打开一个到实际文件的连接来创建一个 FileInputStream该文件通过文件系统中的路径名 name 指定。和 字符流中的 FileReader 基本上是一样的。 举例: import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException;public class FileInputStreamTest {public static void main(String[] args) {File file new File(E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\test.png); // 绝对路径try {FileInputStream fileInputStream new FileInputStream(file);} catch (FileNotFoundException e) {e.printStackTrace();}try {FileInputStream fileInputStream2 new FileInputStream(src/blogs/blog9/test.png); // 相对路径} catch (FileNotFoundException e) {e.printStackTrace();}} }如下是 FileInputStream 继承 java.io.InputStream 继承的方法 字节流和字符流的用法几乎完全一样 区别在于字节流和字符流操作的数据单元的不同字节流是 8 位的字节 而字符流操作的数据单元是 16 位的字符。方法是上的使用也是一样的。 read() 读取文件的一个字节返回值是: 读取到字节本身到达文件末尾返回 -1。 public int read() throws IOException ; // 从此输入流中读取一个数据字节。如果没有输入可用则此方法将阻塞。 read(byte[] b) : 读取文件中 byte[] 数组长度的字节个数返回读取的的字节个数到位文件末尾返回 -1。 public int read(byte[] b) throws IOException; // 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。在某些输入可用之前此方法将阻塞。read(byte[] b int off, int len) 将字节读入数组的某一部分。存到数组b中从off处开始存储最多读len个字 符。如果已到达流的末尾则返回 -1。否则返回本次读取的字符数。 public int read(byte[] b, int off, int len) throws IOException; // 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。如果 len 不为 0则在输入可用之前该方法将阻塞否则不读取任何字节并返回 0。public void close() throws IOException 关闭此输入流并释放与该流关联的所有系统资源。 public void close() throws IOException; // 关闭此输入流并释放与该流关联的所有系统资源。举例 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException;public class FileInputStreamTest {public static void main(String[] args) {FileInputStream fileInputStream null;try {// 1. 创建字节流对象fileInputStream new FileInputStream(src/blogs/blog9/hello.txt);int len 0;// 2.读取文件信息while((len fileInputStream.read()) ! -1) {System.out.println(len);}} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭资源// 防止null引用if (fileInputStream ! null) {try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }举例 使用 byte[] 字节数组 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException;public class FileInputStreamTest {public static void main(String[] args) {FileInputStream fileInputStream null;try {// 1. 创建字节流对象fileInputStream new FileInputStream(src/blogs/blog9/hello.txt);// 2.读取文件信息int len 0;byte[] bytes new byte[1024]; // 1KB// read(bytes) 一次性读取byte[]数组大小的字节个数并存储到 bytes 数组中返回读取的字节个数while((len fileInputStream.read(bytes)) ! -1) {// 将 bytes 数组转换为字符串读取多少转换多少String s new String(bytes,0,len);System.out.println(s);}} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭IO资源if (fileInputStream ! null) {try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }2.3.2 FileOutputStream 字节输出流 关于字节输出流的类都是继承了OutputStream(字节输出流) 来使用的但是个类是抽象类是无法 new 对象来使用的。所以我们就需要使用其实现的子类对于文件字符输出流比较常用的就是 java.io.FileOutputStream 这个子类了。 FileOutputStream的构造器 public FileOutputStream(File file) throws FileNotFoundException // 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。创建一个新 FileDescriptor 对象来表示此文件连接。 public FileOutputStream(String name) throws FileNotFoundException // 创建一个向具有指定名称的文件中写入数据的输出文件流。创建一个新 FileDescriptor 对象来表示此文件连接 public FileOutputStream(File file,boolean append)throws FileNotFoundException // 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。如果第二个参数为 true则将字节写入文件末尾处而不是写入文件开始处。创建一个新 FileDescriptor 对象来表示此文件连接。 // file - 为了进行写入而打开的文件。 // append - 如果为 true则将字节写入文件末尾处而不是写入文件开始处举例: import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream;public class FileOutputStreamTest {public static void main(String[] args) {File file new File(E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\test.png); // 绝对路径try {FileOutputStream fileOutputStream new FileOutputStream(file);} catch (FileNotFoundException e) {e.printStackTrace();}try {FileOutputStream fileOutputStream2 new FileOutputStream(src/blogs/blog9/test.png); // 相对路径} catch (FileNotFoundException e) {e.printStackTrace();}}}如下是 FileOutputStream 继承 java.io.OutputStream继承的方法 void write(int c) : 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中16 高位被忽略。 即写入0 到 65535 之间的Unicode码。 public void write(int b) throws IOException; // 写入单个字符。 void write(byte[] b) : 将字符数组的内容写入到对应的硬盘文件中去 public void write(byte[] cbuf) throws IOException; // 将字符数组的内容写到文件中void write(byte[] b,int off,int len) 写入字符数组的某一部分。从off开始写入len个字符。 public void write(byte[] b,int off,int len) throws IOException // 写入字符数组的某一部分。void flush() 刷新该流的缓冲立即内存中的数据写入预期目标硬盘文件中去。 public void flush() throws IOException; // 刷新该流的缓冲。public void close() 关闭此输出流并释放与该流关联的所有系统资源 public void close() throws IOException; // 关闭此流但要先刷新它。在关闭该流之后再调用 write() 或 flush() 将导致抛出 IOException。关闭以前关闭的流无效。注意 如果写入到的文件不存在是会自动创建的。如果文件已经存在了在创建 FileOutputStream 对象时没有设置为 true 的话是会将原本文件中已经存在的内容覆盖的写入新的内容。 举例: 创建 FileOutputStream 对象时没有设置为 true 的在文件的末尾添加信息不会覆盖原来文件的信息。 import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;public class FileOutputStreamTest {public static void main(String[] args) {File file new File(src/blogs/blog9/hello.txt);FileOutputStream fileOutputStream null;try {// 1. 创建FileOutputStream对象fileOutputStream new FileOutputStream(file,true);// 2. 写入信息到文件中byte[] bytes new byte[]{A,B} ;fileOutputStream.write(bytes);// 3. 刷新:将遗留在内存当中没有写入到文件的信息强制全部写入到文件中fileOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 4. 关闭IO资源if (fileOutputStream ! null) {try {fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }2.3.3 实例图片文件的拷贝 思路: import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException;public class FileOutputStreamTest {public static void main(String[] args) {FileOutputStream fileOutputStream null;FileInputStream fileInputStream null;try {// 1.创建 test2.png 文件的输入字节流对象fileOutputStream new FileOutputStream(src/blogs/blog9/test2.png);// 2.创建 test.png 文件的输出字节流对象fileInputStream new FileInputStream(src/blogs/blog9/test.png);// 2. 一边读一边写int len 0;byte[] bytes new byte[1024 * 1024]; // 1MB// 读while ((len fileInputStream.read(bytes)) ! -1) {// 读取多少写入多少fileOutputStream.write(bytes, 0, len);}// 3. 刷新fileOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 4.关闭IO资源// 分开 try 防止如果一起try的话其中一个出现了异常后面的IO资源就无法关闭了。if (fileInputStream ! null) { // 防止null引用try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}if (fileOutputStream ! null) {try {fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }2.3.4 实例对图片的简单加密 思路 加密这里我们通过字节流获取到图片中的每个 byte 字节信息再对获取到的每个 byte 字节信息进行 ^ 5 运算加密新生成一个加密后的图片(这个图片是加密了的是无法打开的)。 解密同样获取到图片中每个 byte 字节信息再对获取到的每个 byte 字节信息进行 ^ 5 运算解密。新生成一个解密后的图片(这个图片就可以正常打开了) 核心 就是利用 对于一个数^ 两个异或同一个数值返回原来的数值例如6 ^ 2 44 ^ 2 6 举例: import org.junit.Test;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;/*** 图片的加密解密操作*/ public class ImageEncryDecry {/*** 对图片中的每个像素点中的 byte 进行 ^ 5 的加密*/Testpublic void test() {FileInputStream fileInputStream null; // 注意文件后缀FileOutputStream fileOutputStream null; //try {fileInputStream new FileInputStream(src/day27/test2.jpg);fileOutputStream new FileOutputStream(src/day27/test3.jpg);// 一边读一边加密一边写byte[] bytes new byte[20];int len 0;// 读取while((len fileInputStream.read(bytes)) ! -1) {// 读取多少加密多少,注意了不是 bytes数组的长度因为存在重复的覆盖效果for (int i 0; i len; i) {bytes[i] (byte)(bytes[i] ^ 5); // 加密}// 加密完后写入到文件中:读取多少写入多少fileOutputStream.write(bytes,0,len);}// 刷新fileOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 关闭:if(fileInputStream ! null) {try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}if(fileOutputStream ! null) {try {fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }package day27;import org.junit.Test;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;/*** 图片的加密解密操作*/ public class ImageEncryDecry {/*** 对加密 ^ 5 的文件解密*/Testpublic void test2() {FileInputStream fileInputStream null; // 注意文件后缀FileOutputStream fileOutputStream null; //try {fileInputStream new FileInputStream(src/day27/test3.jpg);fileOutputStream new FileOutputStream(src/day27/test4.jpg);// 一边读一边加密一边写byte[] bytes new byte[20];int len 0;// 读取while((len fileInputStream.read(bytes)) ! -1) {// 读取多少加密多少,注意了不是 bytes数组的长度因为存在重复的覆盖效果for (int i 0; i len; i) {bytes[i] (byte)(bytes[i] ^ 5); // 加密}// 加密完后写入到文件中:读取多少写入多少fileOutputStream.write(bytes,0,len);}// 刷新fileOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 关闭:if(fileInputStream ! null) {try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}if(fileOutputStream ! null) {try {fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}}} 3.1 缓冲流 为了提高数据读写的速度Java API 提供了带缓冲区功能的流类。在使用这些流类时会创建一个内部缓冲区数组缺省使用 8192个字节(8kb)的缓冲区 。 缓冲流要 ”套接“ 在相应的节点流之上根据数据操作单位可以把缓冲流分为 字节缓冲流 java.io.BufferedInputStream 字节输入缓冲流 java.io.BufferedOutputStream 字节输出缓冲流 字符缓冲流 java.io.BufferedReader : 字符输入缓冲流java.io.BufferedWriter 字符输出缓冲流 缓冲流的使用当读取数据时数据按块读入缓冲区 其和的读操作则直接访问缓冲区。 当 BufferedInputStream 读取字节文件时BufferedInputStream 会一次性从文件中读取 8192 个 ( 1024 * 8 8KB) 存在缓冲区中直到缓冲区装满了才重新从文件中读取下一个 8192 个字节数组。 向流中写入字节时不会直接写到文件中先写道缓冲区中直到缓冲区写满BufferedOutputStream 才会把缓冲区中的数据一次性写到文件中里。使用方法 flush() 可以强制将缓冲区的内容全部写入输出流。 关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可关闭最外层流也会相应关闭内层节点流。因为从源码中可以看出。 BufferedOutputStream.close() 关闭的同时会将其中对应的节点流关闭。如下源码 3.1.1 BufferedReader (字符输入缓冲流) / BufferedWriter (字符输出缓冲流) BufferedReader的构造器 public BufferedReader(Reader in); // 创建一个使用默认大小输入缓冲区的缓冲字符输入流。 public BufferedReader(Reader in,int sz); // 创建一个使用指定大小输入缓冲区的缓冲字符输入流。BufferedReader 中的方法和FileReader是一样的因为都是继承了 Reader的抽象类的方法的 所以这里就不多介绍说明了。 举例 使用字符缓冲区读取文件信息 import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException;public class BufferedReaderWriterTest {public static void main(String[] args) {BufferedReader bufferedReader null;try {FileReader fileReader new FileReader(src/blogs/blog9/hello.txt);// 1. 创建字符输入缓冲区流对象// 参数是Reader 抽象类这里我们使用 FileReader 同样也是 Reader 的子类作为参数bufferedReader new BufferedReader(fileReader);// 2. 读取文件信息int len 0;char [] chars new char[3];while((len bufferedReader.read(chars))! -1) {// 将 char 转换为字符串// 读多少转换多少String s new String(chars,0,len);System.out.println(s);}} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭IO资源if (bufferedReader ! null) {try {// 只需要关闭外层缓冲区的资源就可以内层的会自动一起关闭bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}}} } BufferedWriter的构造器 public BufferedWriter(Writer out); // 创建一个使用默认大小输出缓冲区的缓冲字符输出流。 public BufferedWriter(Writer out,int sz); // 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 BufferedWriter 中的方法和FileReader是一样的因为都是继承了 Writer的抽象类的方法的 所以这里就不多介绍说明了。 举例 使用字符缓冲区写入文件信息 import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException;public class BufferedReaderWriterTest {public static void main(String[] args) {BufferedWriter bufferedWriter null;try {FileWriter fileWriter new FileWriter(src/blogs/blog9/hello.txt,true);// 1. 创建字符输出缓冲流对象// 参数是Writer 抽象类这里我们使用 BufferedWriter 同样也是 Writer 的子类作为参数bufferedWriter new BufferedWriter(fileWriter);// 2. 写入文件信息char[] chars new char[]{H,H};bufferedWriter.write(\n); // 换行bufferedWriter.write(chars);} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭IO资源这里不用flush()因为缓冲流会自动刷新if (bufferedWriter ! null) {try {bufferedWriter.close();} catch (IOException e) {e.printStackTrace();}}}} }3.1.2 BufferedInputStream(字节输入缓冲流) / BufferedOutputStream (字节输出缓冲流) 这里字节缓冲流和上面的字符缓冲流是一样的这里就不多说明了。 BufferedOutputStream / BufferedInputStream 字节的构造器 public BufferedInputStream(InputStream in); //创建一个 BufferedInputStream 并保存其参数即输入流 in以便将来使用。创建一个内部缓冲区数组并将其存储在 buf 中。 public BufferedInputStream(InputStream in,int size); // 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数即输入流 in以便将来使用。创建一个长度为 size 的内部缓冲区数组并将其存储在 buf 中。 public BufferedOutputStream(OutputStream out); // 创建一个新的缓冲输出流以将数据写入指定的底层输出流。 public BufferedOutputStream(OutputStream out, int size); // 创建一个新的缓冲输出流以将具有指定缓冲区大小的数据写入指定的底层输出流。举例 使用BufferedInputStream 输入缓冲流读取文件的信息 import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException;public class BufferedReaderWriterTest {public static void main(String[] args) {BufferedInputStream bufferedInputStream null;try {FileInputStream fileInputStream new FileInputStream(src/blogs/blog9/hello.txt);// 1. 创建字节输入缓冲流对象bufferedInputStream new BufferedInputStream(fileInputStream);// 2. 读取文件信息int len 0;byte[] bytes new byte[3];while ((len bufferedInputStream.read(bytes)) ! -1) {// 将 bytes 转换为字符串读取了多少转换为多少String s new String(bytes,0,len);System.out.println(s);}} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭IO资源// 关闭IO资源这里只需要关闭外层缓冲区的资源就可以内层的会自动一起关闭if (bufferedInputStream ! null) {try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }举例 使用BufferedOutputStream 输出缓冲流。 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException;public class BufferedReaderWriterTest {public static void main(String[] args) {BufferedOutputStream bufferedOutputStream null;try {FileOutputStream fileOutputStream new FileOutputStream(src/blogs/blog9/hello.txt,true);// 1.创建字节输出缓冲流对象bufferedOutputStream new BufferedOutputStream(fileOutputStream);// 2. 写入信息byte[] bytes new byte[]{K,K};bufferedOutputStream.write(bytes);} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭IO资源这里不用flush()因为缓冲流会自动刷新if (bufferedOutputStream ! null) {try {bufferedOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }4.1 转换流 转换流提供了在字节流和字符流之间的转换 Java API 提供了两个转换流 java.io.InputStreamReader 将 InputStream 转换为 Reader Java.io.OutputStreamWriter : 将 OutputStream 转换为 Writer 字节流中的数据都是字符时转成字符流操作更高效。 很多时候我们使用转换流来处理文件乱码问题。实现编码和 解码的功能。 InputStreamReader 实现将字节的输入流按指定字符集转换为字符的输入流。 需要和 InputStream ”套接“ InputStreamReader 构造器 public InputStreamReader(InputStream in); // 创建一个使用默认字符集的 InputStreamReader。 public InputStreamReader(InputStream in,String charsetName) throws UnsupportedEncodingException; //创建使用指定字符集的 InputStreamReader。举例: import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.FileNotFoundException; public class StreamWriterTest {public static void main(String[] args) {FileInputStream fileInputStream null;try {fileInputStream new FileInputStream(src/blogs/blog9/hello.txt);} catch (FileNotFoundException e) {e.printStackTrace();}// 将 FileInputStream 字节输入流转换为 InputStreamReader 字符输入流InputStreamReader inputStreamReader new InputStreamReader(fileInputStream);} }OutputStreamWriter 实现将字符的输出流按指定字符集转换为字节的输出流。 需要和OutputStream“套接”。 OutputStreamWriter的构造器 public OutputStreamWriter(OutputStream out); // 创建使用默认字符编码的 OutputStreamWriter public OutputStreamWriter(OutputStream out,String charsetName) throws UnsupportedEncodingException; // 创建使用指定字符集的 OutputStreamWriter。 举例 import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStreamWriter;public class StreamWriterTest {public static void main(String[] args) {FileOutputStream fileOutputStream null;try {fileOutputStream new FileOutputStream(src/blogs/blog9/hello.txt);} catch (FileNotFoundException e) {e.printStackTrace();}// 将 FileOutputStream 字节输出流转换为 OutputStreamWriter 字符输出流OutputStreamWriter outputStreamWriter new OutputStreamWriter(fileOutputStream);}4.2 标准输入\输出流 System.in 和 System.out 分布代表了系统标准的输入和输出设备。默认输入设备时键盘输出设备时显示器System.in 实际上是一个 InputStream 字节输入流。将控制台的数据读取到。一个System 类中的静态属性 System.out 的类型实际上是 PrintStream 字节输出流其是OutputSteam 的子类FilterOutputStream 的子类。重定向通过 System 类的 setIn ,setOut 方法对默认设备进行改变。 public static void setIn(InputStream in); //重新分配“标准”输入流。 首先如果有安全管理器则通过 RuntimePermission(setIO) 权限调用其 checkPermission 方法查看是否可以重新分配“标准”输入流。 public static void setOut(PrintStream out); //重新分配“标准”输出流。 首先如果有安全管理器则通过 RuntimePermission(setIO) 权限调用其 checkPermission 方法查看是否可以重新分配“标准”输出流 举例 将 System.out 输出的内容不显示在控制台中而是写入到文件中制作一个日志文件信息 package blogs.blog9;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; import java.text.SimpleDateFormat; import java.util.Date;public class PrintWriterStreamTest {public static void main(String[] args) {log(调用了System类的gc()方法建议启动垃圾回收);log(调用了UserService的doSome()方法);log(用户尝试进行登录验证失败);}public static void log(String msg) {// 1. 创建一个输出的字节的文件对象用于 System的重定向PrintStream printStream null;try {printStream new PrintStream(new FileOutputStream(src/blogs/blog9/log.txt,true));} catch (FileNotFoundException e) {e.printStackTrace();}// 2.改变 System.out()的输出方向使用:SetOut()方法改为重定向到 printSteam的文件中System.setOut(printStream);// 3. 设置输入的日期时间Date date new Date(); // 获取当前系统的时间(毫秒值)时间戳SimpleDateFormat simpleDateFormat new SimpleDateFormat(yyyy-MM-dd hh:mm:ss SSS);// 将 Date 转换为规定格式的字符串String strTime simpleDateFormat.format(date);System.out.println(strTime : msg);// 4. 关闭IO资源System.out.close();} } 举例 从键盘输入字符串要求将读取到的整行字符串转成大写输出然后继续进行输入操作。 直至当输入 “e” 或者 “exit” 时退出程序方法一: 使用Scanner 实现调用next()返回一个字符串方法二使用System.in 实现System.in — 转换流 — BufferedReader 的readLine 这里我们使用方法二的方式 import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.text.SimpleDateFormat; import java.util.Date;public class PrintWriterStreamTest {public static void main(String[] args) {// System.in 就是 public final static InputStream in null;InputStreamReader isr new InputStreamReader(System.in); // System.in 控制台// 转换为该字符输出流可以读取一行的信息BufferedReader bufferedReader new BufferedReader(isr);while(true) {String data null;try {data bufferedReader.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();}}if(bufferedReader ! null) {try {bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}} }4.3 数据流 数据流支持原始数据类型值的二进制 I / O 操作boolean、char、byte、short、int、 long、float and double 以及字符串值。所有数据流都实现 DataInput 接口或 DataOutput 接口。 为了方便地操作Java语言的基本数据类型和String的数据可以使用数据流。 数据流有两个类(用于读取和写出基本数据类型、String类的数据 DataInputStream 和 DataOutputStream 分别“套接”在 InputStream 和 OutputStream 子类的流上 DataInputStream中的方法 boolean readBoolean() byte readByte() char readChar() float readFloat() double readDouble() short readShort() long readLong() int readInt() String readUTF() void readFully(byte[] b)DataOutputStream 中的方法 将上述的方法的read改为相应的即可。 boolean writeBoolean() byte writeByte() char writeChar() float writeFloat() double writeDouble() short writeShort() long writeLong() int writeInt() String writeUTF() void writeFully(byte[] b)需要特别注意的是 输入流判断是否结束不是按普通流那样判断一个返回值而是通过EOFException 异常。 此异常主要被数据输入流用来表明到达流的末尾。注意其他许多输入操作返回一个特殊值表示到达流的末尾而不是抛出异常。 write 和 read 的取值顺序一定要匹配。 输入流由简单的二进制数据组成没有指示单个值的类型或者它们在流中开始的位置。 该示例使用一个非常糟糕的编程技术它使用浮点数来表示货币值。一般来说浮点对精确值是不利的。它对于小数分数特别不利因为常用值如 0.1不具有二进制表示形式。 举例: DataOutputStream的使用 import java.io.DataOutputStream; import java.io.FileOutputStream; import java.io.IOException;public class DataInputStreamWriterTest {public static void main(String[] args) {DataOutputStream dataOutputStream null;try {FileOutputStream fileOutputStream new FileOutputStream(src/blogs/blog9/hello.txt);// 1.创建对应的 DataOutputStream对象dataOutputStream new DataOutputStream(fileOutputStream);byte b 100;short s 200;int i 300;long l 400;float f 2.0f;double d 3.14;boolean bool true;char c A;String str hello;// 2. 写数据将 内存当中的数据写入到文件中dataOutputStream.writeByte(b);dataOutputStream.writeShort(s);dataOutputStream.writeInt(i);dataOutputStream.writeLong(l);dataOutputStream.writeFloat(f);dataOutputStream.writeDouble(d);dataOutputStream.writeBoolean(bool);dataOutputStream.writeChar(c);dataOutputStream.writeChars(str);dataOutputStream.writeChars(str);// 3.刷新dataOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 4.关闭IO资源if (dataOutputStream ! null) {try {dataOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} } 举例: DataInputStream 读取被 DataOutputStream 写入到文件中的信息 注意 write 和 read 的取值顺序一定要匹配。 DataOutputStream 依次写入到文件的 类型是什么顺序后面的 DataInputStream 读取文件的类型的顺序就是什么要的必须保持一致性不然 读取获取到的数据是错误的。 import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;public class DataInputStreamWriterTest {public static void main(String[] args) {DataInputStream dataInputStream null;try {FileInputStream fileInputStream new FileInputStream(src/blogs/blog9/hello.txt);// 1.创建对应的DataInputStream 对象dataInputStream new DataInputStream(fileInputStream);// 2. 读取其中文件的类型信息注意write 和 read 的取值顺序一定要匹配byte b dataInputStream.readByte();System.out.println(b);short s dataInputStream.readShort();System.out.println(s);int i dataInputStream.readInt();System.out.println(i);long l dataInputStream.readLong();System.out.println(l);float f dataInputStream.readFloat();System.out.println(f);double d dataInputStream.readDouble();System.out.println(d);boolean bool dataInputStream.readBoolean();System.out.println(bool);char c dataInputStream.readChar();System.out.println(c);String str dataInputStream.readLine();System.out.println(str);} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭IO资源if (dataInputStream ! null) {try {dataInputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }4.4 对象流 序列化 ObjectOutputStream反序列化 ObjectInputStream 就像数据流支持原始数据类型的 I / O对象流支持 I / O 的对象。大部分但不是全部标准类支持对象的序列化。 都实现了标记接口 Serializable 对象流类是 java.io.ObjectInputStream java.io.OjbectOutputSteam 用于存储和读取基本数据类型数据对象的处理流。它的强大之处就是可以把 Java中的对象写入到硬盘当中也能把对象从硬盘当中还原回来。 序列化 用 ObjectOutputStream 类将基本数据类型或对象类型存储保存到硬盘文件的当中。 反序列化 用ObjectInputStream类 读取存储到硬盘文件当中的基本数据类型或对象类型还原回来。存储到内存当中。 对象序列化机制 允许把内存中的Java对象转换成平台无关的二进制流从而允许把这种二进制流持久地保存在磁盘上或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流就可以恢复成原来的 Java对象 。 序列化是 **RMIRemote Method Invoke – 远程方法调用**过程的参数和返回值都必须实现的机制而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础。 Java的序列化与反序列化的图示如下 什么时候需要用到序列化和反序列化呢? 在本地 JVM 里运行下 Java 实例这个时候是不需要什么序列化和反序列化的 但当我们需要将内存中的对象持久化到磁盘数据库中时 当我们需要与浏览器进行交互时或者当我们需要实现 RPC 时 这个时候就需要序列化和反序列化了。 只要我们对 JVM 堆内存中的对象进行持久化或网络传输 这个时候都需要序列化和反序列化。 序列化的好处 在于可将任何实现了 Serializable 接口的对象转化为 字节数据 使其在保存和传输时可被还原。 JSON 格式实际上就是将一个对象转化为字符串 所以服务器与浏览器交互时的数据格式其实是字符串我们来看来 String 类型的源码: String 类型实现了 Serializable 接口并显示指定 serialVersionUID 的值。也就是说我们使用Json进行传输字符串数据的时候, JVM已经将字符串数据序列化了 4.4.1 对象的序列化 如果需要让某个对象支持序列化机制则必须让对象所属的类及其属性是可序列化的为了让某个类是可序列化的该类必须实现如下两个接口之一。 否则会抛出NotSerializableException异常。 java.io.Serializable 接口一般使用这个接口序列化 java.io.Externalizable 凡是实现了 Serializable 接口的类都有一个表示序列化版本标识符的静态变量 private static final long serialVersionUID;Java当中所有的包装类以及 String 都实现了 java.io.Serializable 接口。所以一般要实现该接口的都是我们自定义的类。 serialVersionUID用来表明类的不同版本间的兼容性。简言之其目的是以序列化对象 进行版本控制有关各版本反序列化时是否兼容。在 Java 中实现了 Serializable 接口后 JVM 在类加载的时候就会发现我们实现了这个接口, 然后在初始化实例对象的时候就会在底层帮我们实现序列化和反序列化 如果一个类实现了了 java.io.Serializable 接口但是却没有定义这个 serialVersionUID 静态变量以及也没有从父类中继承这个静态变量。那么它的值会由 Java运行时环境根据类的内部细节自动生成。这个自动生成的值我们是看不到的导致我们无法手动修改而且每次生成的都不太一样。若类中的实例变量做了修改那么 **serialVersionUID ** 可能会发生变化就不是原来的了。故建议手动显式定义该静态变量不要让Java自动生成。 简单来说Java的序列化机制是通过在运行时判断类的 serialVersionUID 来验证版本是否一致性的在进行反序列化时JVM会把传来的字节流中的 serialVersionUID 与本地相应实体类的 serialVersionUID 进行比较如果相同就认为是一致的可以进行反序列化否则就会出现序列化版本不一致的异常。(InvalidCastException) Java 序列化机制采用了一种特殊的序列化算法 其算法内容如下 所有保存到磁盘中的对象都有一个序列化编号。 当程序试图序列化一个对象时 程序将先检查该对象是否己经被序列化过 只有该对象从未(在本次虚拟机中 被序列化过 系统才会将该对象转换成字节序列并输出。 如果某个对象已经序列化过 程序将只是直接输出一个序列化编号 而不是再次重新序列化该对象。 **举例**使用ObjectOutputStream 类 将自定义类序列化将类对象类型存储到硬盘文件中该类没有实现 java.io.Serializable这个 接口的错误无法序列化的错误演示 package blogs.blog9;import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectOutputStream objectOutputStream null;try {FileOutputStream fileOutputStream new FileOutputStream(src/blogs/blog9/temp);// 1. 创建 ObjectOutputStream 序列化输出流对象// 这里的参数是 OutputStream ,而 FileOutputSteam 实现了该接口objectOutputStream new ObjectOutputStream(fileOutputStream);Person person new Person(Tom,99);// 2. 将自定义的Person 对象序列化:存储到硬盘文件当中去.objectOutputStream.writeObject(person);// 3. 刷新objectOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 4 关闭IO资源if (objectOutputStream ! null) {try {objectOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }// 该自定义类没有实现 java.io.Serializable 接口无法序列化 class Person {String name;int age;public Person() {}public Person(String name, int age) {this.name name;this.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;}Overridepublic String toString() {return Person{ name name \ , age age };} } 举例 自定义类 Person 实现了 Serailaizable 接口但是没有显式定义 serialVersionUID 静态属性而是由Java自动生成的。 package blogs.blog9;import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectOutputStream objectOutputStream null;try {FileOutputStream fileOutputStream new FileOutputStream(src/blogs/blog9/temp);// 1. 创建 ObjectOutputStream 序列化输出流对象// 这里的参数是 OutputStream ,而 FileOutputSteam 实现了该接口objectOutputStream new ObjectOutputStream(fileOutputStream);Person person new Person(Tom,99);// 2. 将自定义的Person 对象序列化:存储到硬盘文件当中去.objectOutputStream.writeObject(person);// 3. 刷新objectOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 4 关闭IO资源if (objectOutputStream ! null) {try {objectOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }// 该自定义类没有实现 java.io.Serializable 接口无法序列化 class Person implements Serializable {String name;int age;public Person() {}public Person(String name, int age) {this.name name;this.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;}Overridepublic String toString() {return Person{ name name \ , age age };} } 4.4.2 对象的反序列化 举例 使用 ObjectInputStream 类读取存储到对象的序列化文件 temp 。 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectInputStream objectInputStream null;try {FileInputStream fileInputStream new FileInputStream(src/blogs/blog9/temp); // 注意文件后缀// 1. 创建反序列化输入对象// 注意: 这里的参数是: InputSteam 而FileInputStream 实现了该接口objectInputStream new ObjectInputStream(fileInputStream);// 2.读取其中序列化对象信息// Object object objectInputStream.readObject();// 这里因为我们知道该文件中序列化的对象是 Person类型的所以可以直接进行强制转化Person person (Person) objectInputStream.readObject();System.out.println(person);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {// 3. 关闭IO资源if (objectInputStream ! null) {try {objectInputStream.close();} catch (IOException e) {e.printStackTrace();}}}}}// 该自定义类实现 java.io.Serializable 接口 class Person implements Serializable {String name;int age;public Person() {}public Person(String name, int age) {this.name name;this.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;}Overridepublic String toString() {return Person{ name name \ , age age };} }4.4.3 不显式定义 serialVersionUID 值的问题 不指定serialVersionUID出现的问题 如果我们自定义的类想要序列化实现了 java.io.Serializable 接口但是却没有显式定义 serialVersionUID 静态属性而是由Java自动 生成的。但是该自动生成的 serialVersionUID我们无法修改当我们对应的类中是属性发生了改变时由于我们没有自行显式定义 serialVersionUID静态属性导致Java会重新生成一个新的 serialVersionUID 属性值该属性值与原先就的属性值的版本不一致导致再次想序列化对象时会出错。如果我们在不同的电脑上都有一个Person类, 我们想通过网络进行传输, 那就必须现在A电脑实现序列化, 在B电脑实现反序列化, 那么如果我们不指定serialVersionUID就就有可能反序列化失败在实例开发过程中, 我们的类会经常改变, 如果我们使用JVM帮我们自动生成的serialVersionUID, 那么如果这个类已经有一些序列化对象, 那我们一旦修改了这个类,这些对象反序列化的时候就都会报错。 为什么要显式定义serialVersionUID的值? 如果不显示指定 serialVersionUID JVM 在序列化时会根据属性自动生成一个 serialVersionUID 然后与属性一起序列化再进行持久化或网络传输。 在反序列化时JVM 会再根据属性自动生成一个新版 serialVersionUID然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较如果相同则反序列化成功 否则报错. 如果显示指定了 serialVersionUID JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID 但值为我们显示指定的值, 就会进行serialVersionUID值的覆盖这样在反序列化时新旧版本的 serialVersionUID 就一致了。 举例 在没有自定义 serialVersionUID 的值的前提这里我们修改了Person类中是属性多加一个 int id 属性后反序列化读取存储序列化文件时的报错提示java.io.InvalidClassException: package blogs.blog9;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectInputStream objectInputStream null;try {FileInputStream fileInputStream new FileInputStream(src/blogs/blog9/temp); // 注意文件后缀// 1. 创建反序列化输入对象// 注意: 这里的参数是: InputSteam 而FileInputStream 实现了该接口objectInputStream new ObjectInputStream(fileInputStream);// 2.读取其中序列化对象信息// Object object objectInputStream.readObject();// 这里因为我们知道该文件中序列化的对象是 Person类型的所以可以直接进行强制转化Person person (Person) objectInputStream.readObject();System.out.println(person);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {// 3. 关闭IO资源if (objectInputStream ! null) {try {objectInputStream.close();} catch (IOException e) {e.printStackTrace();}}}}}// 该自定义类实现 java.io.Serializable 接口 class Person implements Serializable {String name;int age;// 多加一个属性int id;public Person() {}public Person(String name, int age, int id) {this.name name;this.age age;this.id id;}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;}public int getId() {return id;}public void setId(int id) {this.id id;}Overridepublic String toString() {return Person{ name name \ , age age , id id };} } 显式定义serialVersionUID 的值 定义的 serialVersionUID 的值是 private 私有化的static 静态的所以对象共用final 常量不可修改的值是 Long 类型的 private static final long serialVersionUID -6849794470754667710L;Java比较判断版本号时是在同一个项目中先判断类名是否相等类名相等的前提下再比较判断对应的类名的版本号。所以版本号可以不用设置的太大 1L 也是可以的。注意静态属性名是serialVersionUID 这是固定的。不要修改了。 private static final long serialVersionUID 1L;举例 在显式定义 serialVersionUID 的值的前提这里我们修改了Person类中是属性多加一个 int id 属性后反序列化读取存储序列化文件时的。读取正常。注意 要先将我们已经显式定义的 serialVersionUID 的值先序列化一下再添加 int id 属性后在反序列化。不然你就是还是用的是 serialVersionUID 由Java自行生成的版本号。还是会报错的。 package blogs.blog9;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectInputStream objectInputStream null;try {FileInputStream fileInputStream new FileInputStream(src/blogs/blog9/temp); // 注意文件后缀// 1. 创建反序列化输入对象// 注意: 这里的参数是: InputSteam 而FileInputStream 实现了该接口objectInputStream new ObjectInputStream(fileInputStream);// 2.读取其中序列化对象信息// Object object objectInputStream.readObject();// 这里因为我们知道该文件中序列化的对象是 Person类型的所以可以直接进行强制转化Person person (Person) objectInputStream.readObject();System.out.println(person);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {// 3. 关闭IO资源if (objectInputStream ! null) {try {objectInputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }// 该自定义类实现 java.io.Serializable 接口 class Person implements Serializable {String name;int age;// 多加一个属性int id;// 显式定义了 serialVersionUID 的版本号值private static final long serialVersionUID 1L;public Person() {}public Person(String name, int age, int id) {this.name name;this.age age;this.id id;}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;}public int getId() {return id;}public void setId(int id) {this.id id;}Overridepublic String toString() {return Person{ name name \ , age age , id id };}} 4.4.3.1 设置不被序列化的类型 在一些特殊的场景下 如果一个类里包含的某些实例变量是敏感信息 例如银行账户信息等 这时不希望系统将该实例变量值进行序列化 或者某个实例变量的类型是不可序列化的 因此不希望对该实例变量进行递归序列化 以避免引java.io.NotSerializableException 异常。 通过在实例变量前面使用 transient 关键字修饰 可以指定 Java 序列化时无须理会该实例变量。 如下 Person 类与前面的 Person 类几乎完全一样 只是它的 age 使用了 transient 关键字修饰。 简单的说就是被 transient 关键字修饰的属性不会被序列化到文件中更不会被反序列化读取到 举例 将 Person 类中的 age 被 transizent 修饰不会被序列化。 transient int age;先序列化一下将对象存储到文件中 package blogs.blog9;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectOutputStream objectOutputStream null;try {FileOutputStream fileOutputStream new FileOutputStream(src/blogs/blog9/temp);// 1. 创建 ObjectOutputStream 序列化输出流对象objectOutputStream new ObjectOutputStream(fileOutputStream);Person person new Person(Tom, 99,001);//2. 将自定义的Person 对象序列化:存储到硬盘文件当中去.objectOutputStream.writeObject(person);// 3. 刷新objectOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 4 关闭IO资源if (objectOutputStream ! null) {try {objectOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }// 该自定义类实现 java.io.Serializable 接口 class Person implements Serializable {String name;transient int age; // 被transient 修饰不会序列化// 多加一个属性int id;// 显式定义了 serialVersionUID 的版本号值private static final long serialVersionUID 1L;public Person() {}public Person(String name, int age, int id) {this.name name;this.age age;this.id id;}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;}public int getId() {return id;}public void setId(int id) {this.id id;}Overridepublic String toString() {return Person{ name name \ , age age , id id };}} 再反序化查看效果 package blogs.blog9;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectInputStream objectInputStream null;try {FileInputStream fileInputStream new FileInputStream(src/blogs/blog9/temp); // 注意文件后缀// 1. 创建反序列化输入对象// 注意: 这里的参数是: InputSteam 而FileInputStream 实现了该接口objectInputStream new ObjectInputStream(fileInputStream);// 2.读取其中序列化对象信息// Object object objectInputStream.readObject();// 这里因为我们知道该文件中序列化的对象是 Person类型的所以可以直接进行强制转化Person person (Person) objectInputStream.readObject();System.out.println(person);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {// 3. 关闭IO资源if (objectInputStream ! null) {try {objectInputStream.close();} catch (IOException e) {e.printStackTrace();}}}}}// 该自定义类实现 java.io.Serializable 接口 class Person implements Serializable {String name;transient int age;// 多加一个属性int id;// 显式定义了 serialVersionUID 的版本号值private static final long serialVersionUID 1L;public Person() {}public Person(String name, int age, int id) {this.name name;this.age age;this.id id;}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;}public int getId() {return id;}public void setId(int id) {this.id id;}Overridepublic String toString() {return Person{ name name \ , age age , id id };}} 注意同样的被 static 修饰为静态的属性也不会被序列化 static 属性为什么不会被序列化? 因为序列化是针对实例对象而言的而 static 属性优先于对象存在 随着类的加载而加载 所以不会被序列化. 是不是有人会问 serialVersionUID 也被 static 修饰 为什么 serialVersionUID 会被序列化 其实 serialVersionUID 属性并没有被序列化 JVM 在序列化对象时会自动生成一个 serialVersionUID 然后将我们显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID。 补充 一次性序列化多个对象可以可以将对象放到集合当中序列化集合。如下 创建多个 Person 对象并存储到 List 集合中并反序列化到 temp 文件中。 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectOutputStream objectOutputStream null;try {FileOutputStream fileOutputStream new FileOutputStream(src/blogs/blog9/temp);// 1. 创建 ObjectOutputStream 序列化输出流: 注意构造器的参数是 OutputStreamobjectOutputStream new ObjectOutputStream(fileOutputStream);// 2. 创建多个 Person 对象类型存储到 List 集合中Person person1 new Person(Tom,18,001);Person person2 new Person(zhangsan,28,002);Person person3 new Person(lisi,20,003);// Person泛型限定存储对象类型ListPerson list new ArrayListPerson();// 添加元素list.add(person1);list.add(person2);list.add(person3);// 3. 序列化:将存储到 List集合中的元素写入到 temp 硬盘文件中objectOutputStream.writeObject(list);// 4. 刷新:将遗留在内存中没有写入到文件的信息强制全部写入到文件中防止数据丢失objectOutputStream.flush();} catch (IOException e) {e.printStackTrace();} finally {// 5. 关闭IO资源if (objectOutputStream ! null) {try {objectOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }// 该自定义类实现 java.io.Serializable 接口 class Person implements Serializable {String name;transient int age;// 多加一个属性int id;// 显式定义了 serialVersionUID 的版本号值private static final long serialVersionUID 1L;public Person() {}public Person(String name, int age, int id) {this.name name;this.age age;this.id id;}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;}public int getId() {return id;}public void setId(int id) {this.id id;}Overridepublic String toString() {return Person{ name name \ , age age , id id };}} 读取文件中的多个序列化对象 将我存储到 List 集合中的多个对象从文件中读取到内存当中 package blogs.blog9;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List;public class ObjectOutputStreamTest {public static void main(String[] args) {ObjectInputStream objectInputStream null;try {FileInputStream fileInputStream new FileInputStream(src/blogs/blog9/temp);// 1.创建 ObjectInputStream 反序列化输入流对象构造器参数是 InputStreamobjectInputStream new ObjectInputStream(fileInputStream);// 2. 读取存储序列信息的文件到内存当中// 因为这里我们知道该文件中存储的是 ListPerson 集合类型的所以可以直接强制转化ListPerson list (ListPerson)objectInputStream.readObject();// 遍历集合for (Person person : list) {System.out.println(person);}} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {// 3. 关闭IO资源if (objectInputStream ! null) {try {objectInputStream.close();} catch (IOException e) {e.printStackTrace();}}}} }// 该自定义类实现 java.io.Serializable 接口 class Person implements Serializable {String name;transient int age;// 多加一个属性int id;// 显式定义了 serialVersionUID 的版本号值private static final long serialVersionUID 1L;public Person() {}public Person(String name, int age, int id) {this.name name;this.age age;this.id id;}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;}public int getId() {return id;}public void setId(int id) {this.id id;}Overridepublic String toString() {return Person{ name name \ , age age , id id };}} 5. 实用案例: 拷贝目录以及目录下的所有文件 package day26;import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;/*** 拷贝目录以及目录下的所有文件*/ public class CopyAll {/*** 拷贝目录下的所有文件* param srcFile* param destFile*/private static void copyDir(File srcFile, File destFile) {// 递归结束条件。(是文件是最后一层了不用再递归下去了)// 判断该拷贝对象是否是文件if(srcFile.isFile()) {// srcFile如果是文件的话将文件拷贝完就返回了递归结束因为文件都是最后的东西的// 是文件拷贝一边读一边写FileInputStream in null;FileOutputStream out null;try {in new FileInputStream(srcFile);String path destFile.getAbsolutePath().endsWith(\\) ? destFile.getAbsolutePath() :\\ srcFile.getAbsolutePath().substring(3);out new FileOutputStream(destFile);// 一边读一边写byte[] bytes new byte[1024*1024]; // 一次复制1mbint readCout 0;while((readCout in.read(bytes)) ! -1) {out.write(bytes,0,readCout); // 读到多少返回多少}// 刷新out.flush();} catch (IOException e) {e.printStackTrace();} finally {try {in.close();} catch (IOException e) {e.printStackTrace();}try {out.close();} catch (IOException e) {e.printStackTrace();}}return;}// 获取被拷贝的目录下面的子目录该目录下的目录和文件File[] files srcFile.listFiles();for (File file : files) {// 判断该文件是否在同一个目录下if(file.isDirectory()) {// D:/curse/02-javaSe/ 被拷贝的目录// C:/curse/02-javaSe/ 到的目录 两者之间的目录盘必须是一样的// 获取所有文件的(包括目录和文件)绝对路径String srcDir file.getAbsolutePath();String destDir file.getAbsolutePath().endsWith(\\) ? destFile.getAbsolutePath() :\\ srcDir.substring(3);File newFile new File(destDir);// 判断该 目录是否存在不存在创建if(!newFile.exists()) {newFile.mkdirs(); // 多重目录的创建}}// 递归调用copyDir(file,destFile); // 将其中的文件/目录拷贝到 destFile目录中}} } 6. 文件 I/Onio.2 关于这部分内容大家可以移步至 https://zq99299.github.io/java-tutorial/essential/io/fileio.html 观看学习。 7. 总结 File 类中的方法的使用。 I/O 流的分类输入流将硬盘文件的数据信息写入到内存当中输出流将内存当中的信息写入到硬盘文件中。 Java的IO流共涉及40多个类实际上非常规则都是从如下 Java Io 流四大家族 个 抽象基类派生的。 I/O流的四大首领java.io.InputStream java.io.OutputStreamjava.io.Readejava.io.Writer。 在Java中只要 类名是以 stream 结尾的都是字节流以 Reader/Writer结尾的都是字符流。 字节流和字符流的用法几乎完全一样 区别在于字节流和字符流操作的数据单元的不同字节流是 8 位的字节 而字符流操作的数据单元是 16 位的字符。其中还有一点不同的就是 字符流只能读取操作文本文件因为字符流读取的是文件中的 char 字符信息。 .c.java.c.txt 等等这些都是文本文件不仅仅只是 txt文件而特别注意的是 .wrod 不是文本文件wrod中的文字是经过特殊处理的存在一定的规范格式不是纯文本文件。字节流可以操作任何的文件因为字节流读取的是二进制信息读取1个字节byte,等同于一次读取8个二进制这种流是万能的什么类型的文件都可以读取到因为文件都是有二进制组成的。包括: 文本文件图片声音文件。 灵活使用字节流字符流缓冲流转换流标准输入、输出流数据流 序列化把对象转换为字节序列的过程称为对象的序列化。将对象写入到硬盘文件中 反序列化把字节序列恢复为对象的过程称为对象的反序列化。将存储到硬盘文件中的对象读取到内存当中。 如果某个类的属性不是基本数据类型或 String 类型而是另一个 引用类型那么这个引用类型必须是可序列化的否则拥有该类型的Field 的类也不能序列化。 如果需要让某个对象支持序列化机制则必须让对象所属的类及其属性是可序列化的为了让某个类是可序列化的该类必须实现如下两个接口java.io.Serializable(常用)java.io.Externalizable之一。 否则会抛出NotSerializableException异常。 建议显式定义 serialVersionUID 版本值。 private static final long serialVersionUID -6849794470754667710L;被 transient 关键字修饰的属性不会被序列化 static 属性也不会被序列化. 10. 最后 ✏️✏️✏️✏️✏️✏️✏️✏️✏️✏️ 感谢以下大佬提供的参考资料 ✏️✏️✏️✏️✏️✏️✏️✏️✏️✏️ 【1】 https://fighter3.blog.csdn.net/article/details/103554407 【2】https://zq99299.github.io/java-tutorial/essential/io/streams.html 【3】https://blog.csdn.net/Shangxingya/article/details/113744323 限于自身水平其中存在的错误希望大家给予指教韩信点兵——多多益善谢谢大家后会有期江湖再见
http://www.hkea.cn/news/14474944/

相关文章:

  • wordpress固定连接白云怎样优化网站建设
  • 深圳做棋牌网站建设哪家公司收费合理英文网站建设口碑好
  • 广州市网站建设 乾图信息科技免费手机网站空间
  • 福州专业制作网站免费的企业品牌策划公司
  • 网站外链是友情连接吗百度技术培训中心
  • 笑傲网站建设江苏网站建设开发
  • 推广网站的方法有搜索引擎成立中英文网站建设工作领导小组
  • 高州市荷花镇网站建设网站建设流程简图
  • 网站开发背景图模板做网站推广员
  • 网站静态和动态寻花问柳-一个专做男人的网站
  • 上海成品网站网站制作价格上海
  • 企业营销型网站的内容wordpress打包小程序
  • jsp做物流网站免费做链接的网站吗
  • 家庭宽带做网站设计院
  • 做试管的网站app软件制作器
  • 工信部网站备案流程大学网站建设策划书
  • 网站制作行业越来越难做微信怎么弄自己的商城
  • 阿里云网站建设的实训报告运维难还是开发难
  • 楚雄建网站贵阳网站建设公司排名
  • 网站有什么模块太原市建站外包公司
  • 做一个公司网站一般多少钱自己做网站还有出路吗
  • 小说网站做编辑微信公众号如何发布wordpress
  • 网站怎么做子页吉利汽车网站开发环境分析
  • 厦门景观绿环建设行业协会网站长沙大的建网站公司
  • 烟台高新区网站网上智慧团建系统入口
  • 江西正东建设工程有限公司网站a wordpress
  • 重庆知名做网站的公司景点网站应该怎么做
  • 凡科建站是不是免费的可信的专业网站建设
  • 网站开发 技术问题wordpress导航图标
  • 网站建设怎么更换图片网页设计技术学什么