黑马程序员_java IO流

  

IO流概述: IO(Input/Output):输入/输出,泛指对某个设备或环境进行数据的数据或输出。例如对硬盘进行输入输出, 对视频设备进行输入输出,对网络主机进行输入输出等。可以想象,因设备或环境的不同,会有各式各样 的输入输出问题与解决方案。对于输入输出问题,Java的IO通过java.io包下的类和接口来支持,在java. io包下主要包括输入、输出两种流,每一种输入、输出流又分为字节流和字符流两大类。其中字节流以字 节为单位来处理输入、输出操作;字符流则以字符为单位来处理输入、输入操作。在正式了解Java中如何 处理文件的输入输出之前,我们需要了解Java是如何表示一个文件的 File类: 概述: File类是java.io包下代表与平台无关的文件和目录,在程序中操作文件和目录,都可以通过File类完成。 File类能新建、删除、重命名文件和目录,但File类不能访问文件内容,如果要访问文件内容,则需要使 用输入、输出流。 创建File对象: File类的构造方法: 1:File(String pathname) 根据给定的字符串路径创建 2:File(String parent, String child) 根据父目录和子目录创建 3:File(File parent, String child) 根据父目录对象和子目录创建 代码示例: import java.io.*; public class FileDemo{ public static void main(String[] args) { //第一种方式 File file1 = new File(“D:\\a.txt”); //第二种方式 File file2 = new File(“D:\\”, haha); //第三种方式 File file3 = new File(“D:\\”); File file4 = new File(file3, c.txt); //以上三种方式等价 } } File类的常见方法: boolean createNewFile():在指定目录创建文件,如果文件不存在,就创建。否则,不创建。 boolean mkdir():创建单级目录。 boolean mkdirs():创建多级目录,父目录可以不存在。 代码示例: import java.io.*; public class FileTest{ public static void main(String[] args) { //创建文件 File file1 = new File(“a.txt”); System.out.println(“createNewFile():” + file1.createNewFile()); //创建文件夹 File file2 = new File(“AA”); System.out.println(“mkdir():” + file2.mkdir()); //创建多级目录 File file3 = new File(“AAA\\BBB”); System.out.println(“mkdirs():” + file3.mkdirs()); } } boolean delete():删除文件和文件夹,如果文件夹里有文件,不能直接删除,只能一级一级的删除 代码示例: import java.io.*; public class FileTest{ public static void main(String[] args) { //删除文件 File file1 = new File(“a.txt”); System.out.println(“delete():” + file1.delete()); //删除文件夹 File file2 = new File(“AAA\\BBB”); System.out.println(“delete():” + file2.delete()); } } boolean exists:判断文件是否存在 boolean isFile:判断是否是文件 boolean isDirectory:判断是否是文件夹 boolean isAbsolute:判断是否是绝对路径,以盘符开始的是绝对路径,否则是相对路径 boolean canRead:判断是否可读 boolean canWrite:判断是否可写 boolean isHidden:判断是否是隐藏文件 代码示例: import java.io.*; public class FileTest{ public static void main(String[] args) { File file = new File(“a.txt”); //判断文件是否存在 System.out.println(“exists():” + file.exists()); //判断是否是文件 System.out.println(“isFile():” + file.isFile()); //判断是否是文件夹 System.out.println(“isDirectory():” + file.isDirectory()); //判读是否可读 System.out.println(“canRead():” + file.canRead()); //判断是否是绝对路径 System.out.println(“isAbsolute():” + file.isAbsolute()); } } String getAbsolutePath():获取绝对路径 String getPath():获取相对路径 String getName():获取文件名称 long length():获取文件内容长度 long lastModified():获取最后一次的修改时间的毫秒值 代码示例: import java.io.*; public class FileTest{ public static void main(String[] args) { //创建File对象 File file = new File(“a.txt”); System.out.println(“getAbsolutePath:” + file.getAbsolutePath()); System.out.println(“getPath:” + file.getPath()); System.out.println(“getName:” + file.getName()); System.out.println(“length:” + file.length()); System.out.println(“lastModified:” + file.lastModified()); } } static File[] listRoots():获取系统的盘符目录 String[] list():根据指定的目录,返回该目录下所有文件和文件夹的名称数组 File[] listFiles:根据指定的目录,返回该目录下所有文件和文件夹的File对象数组 代码示例一: import java.io.*; /* 需求:获取指定目录中的文件夹及文件名 */ public class FileTest{ public static void main(String[] args) { //封装目录 File file = new File(); /* 方法一 */ String[] fileArr = file.list(); for(String fileItem : fileArr) { //获取的是相对路径 System.out.println(fileItem); } /* 方法二: */ File[] files = file.listFiles(); for(File fileItem : files) { //获取的是绝对路径 System.out.println(fileItem); } } } 文件过滤器: File类的list()、listFiles()方法中可以接收一个FilenameFilter参数,通过该参数可以只列出 符合条件的文件。FilenameFilter接口中有一个accept(File dir, String name)方法,该方法将依次 对指定File的所有子目录或者文件进行迭代,返回值类型为boolean。 代码示例: import java.io.*; /* 需求:获取指定目下的文件,不是文件夹 */ public class FileListTest { public static void main(String[] args) { //封装目录 File file = new File(“I:\\”); File[] files = file.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { //根据父目录对象和子目录创建 File newFile = new File(dir, name); return newFile.isFile(); } }); //循环遍历过滤后的文件 for(File fileItem : files) { System.out.println(fileItem); } } } File类综合示例: import java.io.*; /* 需求获取指定盘符目录下,所有的以.java结尾的文件 分析:如果是文件夹,需要进入文件夹继续遍历,所以要使用递归 */ public class FileDemo{ public static void main(String[] args) { File file = new File(“i:\\”); getFiles(file); } public static void getFiles(File file) { File[] files = file.listFiles(); for(File fileItem : files) { /* 如果是文件夹,递归调用 */ if(fileItem.isDirectory()) { getFiles(fileItem); }else { /* 如果是文件,再判断是否是指定文件 */ if(fileItem.getName().endsWith(“.java”)) { System.out.println(fileItem.getName()); } } } } } IO流的分类: 按照流的流向分: 输入流:只能从中读取数据,不能向其写入数据。java的输入流主要由InputStream和Reader作为基类 输出流:只能向其写入数据,不能从中读取数据。java的输出流主要由OutputStream和Writer作为基类 它们都是抽象基类,无法直接创建实例。 每一种输入、输出流按照操作的数据单元又分为字节流和字符流: 字节流:字节流操作的数据单元是8位的字节,字节流主要由InputStream和OutputStream作为基类 字符流:字符流操作的数据单元是16位的字符,字符流主要由Reader和Writer作为基类 InputStream和Reader: InputStream和Reader所有输入流的抽象基类,本身并不能创建对象,但它俩中的方法是所有输入都可使用的 |Read中的方法 int read():从输入流中读取单个字符,返回所读取的字符数据 int read(char[] chs):从输入流中最多读取chs.length个字符的数据,并将其存储在字符数组中, 返回实际读取的字符数 int read(char[] chs, int off, int len):从输入流中最多读取len个字符的数据,并将其存储在 数组中,放入数组中时,并不是从数组的起点开始,而是从off位置开始,返回实际读取的字符数 OutputStream和Writer: OutputStream和Writer也很相似,两个流都提供了如下3个方法 void write(int by):将指定的字节/字符写入指定输出流中 void write(byte[] bys/ char[] chs):将字节/字符数组中的数据写入指定的输出流中 void write(byte[] bys/ char[] chs, int off, int len):将字节/字符数组从off位置开始,长度为len 的字节/字符写入到输出流中 因为字符流直接以字符作为操作单位,虚拟主机,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。 void write(String str):将字符串写入到指定的输出流中。 字符输出流的使用: 案例体现一: import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; /* * 需求:我往a.txt里面写一句话。 * 分析: * 1:a.txt是一个文本文件,所以采用字符流 * 2:我要写入数据,用输出流 * Writer是一个抽象类。不能直接使用。又因为我们想对File进行操作。 * 所以,我们使用了Writer体系的一个实现类:FileWriter * FileWriter构造方法: * FileWriter(File file) * FileWriter(String fileName) * 往文本文件写入数据的步骤: * 1:创建字符输出流对象。 * 为什么FileWriter没有无参构造?因为你要明确写入的目的地。 * 2:调用字符输出流写数据的方法。 * 3:释放资源,也就是关闭流对象。 * * 为什么需要释放资源? * 让本对象变成垃圾,通知操作系统释放占用的资源。 * * 既然close方法也可以刷新缓冲区,为什么,还要专门有一个flush方法。 * jvm占用 64兆 * 不可能让一个比较大的数据完毕后,才刷新缓冲区。 * * close方法和flush方法的区别? * flush:刷新缓冲区,流对象还可以继续使用。 * close:先刷新缓冲区,然后释放资源。流对象不可以被使用了。 * */ public class FileWriterDemo { public static void main(String[] args) throws IOException { // 创建字符输出流对象 // File file = new File(“d:\\a.txt”); // Writer w = new FileWriter(file); // 多态 // FileWriter fw = new FileWriter(file); FileWriter fw = new FileWriter(“d:\\a.txt”); /* * 创建字符输出流对象做了几件事情: * 1:调用操作系统的资源,创建了一个文本文件 * 2:创建了字符输出流对象 * 3:把字符输出流对象指向这个文本文件 */ //调用字符输出流写数据的方法。 fw.write(“io流,我来了”); /* 字符流是由字节流来的,并且数据最基本的单位是字节。 这个时候,对字符流操作的时候,就会有一个缓冲区。 所以,我们需要对缓冲区进行刷新,否则数据写不到文件。 */ fw.flush(); //释放资源,也就是关闭流对象。 fw.close(); } } 字符输入流的使用: 案例体现一: import java.io.FileReader; import java.io.IOException; /* * 读取文本文件的数据我们最终选择了字符输入流。Reader * 而Reader又是抽象类,我们选择了它的实现类:FileReader * FileReader的构造方法: * FileReader(String fileName) * 字符输入流读取数据的步骤: * 1:创建字符输入流对象。 * 2:调用字符输入流对象的读取方式。 * 3:释放资源。 */ public class FileReaderDemo { public static void main(String[] args) throws IOException { /* 1:创建字符输入流对象。做了什么? * a:查找文本文件是否存在 * b:创建字符输入流对象 * c:把字符输入流对象指向文本文件 */ //Exception in thread “main” java.io.FileNotFoundException: nam.txt (系统找不到指定的文件。) FileReader fr = new FileReader(“name.txt”); // 2:调用字符输入流对象的读取方式。 //public int read():读取单个字符,当返回-1时,表明读取完毕 int ch = 0; //通过循环条件读取数据 while((ch=fr.read())!=-1){ System.out.print((char)ch); } // 3:释放资源。 fr.close(); } } 案例体现二: import java.io.FileReader; import java.io.IOException; import java.util.Arrays; /* * 采用一次读取一个数组的方式: * public int read(char[] cbuf):自定义数组缓冲区,然后从数组中获取数据。 * 返回实际读取长度,当返回-1时,表明读取完毕 * 数组长度一般定义为1024的整数即可。 */ public class FileReaderDemo2 { public static void main(String[] args) throws IOException { // 创建字符输入流对象 FileReader fr = new FileReader(“a.txt”); //自定义缓冲区 char[] chs = new char[1024]; int len = 0; while((len=fr.read(chs))!=-1){ System.out.print(new String(chs,0,len)); } //释放资源 fr.close(); } } 字符输入流与字符输入流的综合使用: 案例体现一: import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /* * 复制文本文件: * 把当前路径下的a.java复制到当前路径下的Copy.java中。 * * 数据源: * a.java FileWriter */ public class CopyFileDemo2 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader(“a.java”); FileWriter fw = new FileWriter(“Copy.java”); //基本的读写操作 char[] chs = new char[1024]; int len = 0; while((len=fr.read(chs))!=-1){ fw.write(chs,0,len); } //释放资源 fw.close(); fr.close(); } } 字节输出流的使用: 案例体现一: import java.io.FileOutputStream; import java.io.IOException; /* * 字节流写入数据: * 1:创建字节输出流对象 * 2:调用写入方法 * 3:释放资源 * * 字节流可以不用刷新,为什么? * 数据的最基本单位就是字节,所以,可以直接把字节数据写入文件。 * * 字节流需要关闭吗?需要 */ public class FileOutputStreamDemo { public static void main(String[] args) throws IOException { // 1:创建字节输出流对象 FileOutputStream fos = new FileOutputStream(“fos.txt”,true); // 2:调用写入方法 fos.write(97); //写一个字节 fos.flush(); byte[] bys = {98,99,100,101,102}; fos.write(bys); fos.flush(); //String.getBytes()方法:将字符串准换为字节数组 fos.write(“haha”.getBytes()); fos.flush(); //3:释放资源 fos.close(); } } 字节输入流的使用: 案例体现一: import java.io.FileInputStream; import java.io.IOException; /* * 字节流读取数据的步骤: * 1:创建字节输入流对象 * 2:调用读取数据的方式 * A:一次读取一个字节 * B:一次读取一个字节数组 * 3:释放资源 */ public class FileInputStreamDemo { public static void main(String[] args) throws IOException { //1:创建字节输入流对象 FileInputStream fis = new FileInputStream(“fos.txt”); //2:调用读取数据的方式,当返回-1时,表明读取完毕 byte[] bys = new byte[1024]; int len = 0; while((len=fis.read(bys))!=-1){ System.out.print(new String(bys,0,len)); } //3:释放资源 fis.close(); } } 字节输入流与字节输出流的综合使用: 案例体现: import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; /* * 需求:把当前项目路径下的fos.txt复制到d盘根目录的copy.txt中。 * * 数据源: * fos.txt 为了演示字节流 InputStream FileOutputStream */ public class CopyFile { public static void main(String[] args) throws IOException { //创建对象 FileInputStream fis = new FileInputStream(“fos.txt”); FileOutputStream fos = new FileOutputStream(“d:\\copy.txt”); //方式1 int by = 0; while((by=fis.read())!=-1){ fos.write(by); //fos.flush(); } //方式2 byte[] bys = new byte[1024]; int len = 0; while((len=fis.read(bys))!=-1){ fos.write(bys,0,len); //fos.flush(); } //释放资源 fos.close(); fis.close(); } } 装饰设计模式: 当想要对已有对象进行功能增强时,可以自定义类,通过自定义类的构造器,将已有对象传入, 基于已有功能,并提供更强的功能,那么这个自定义类就称为装饰类, BufferedReader和BufferedWriter就是装饰设计模式很好的体现,通过传入基本流对象 特点:比继承更灵活,避免了继承的臃肿,降低了类与类之间的关系,装饰类和被装饰类通常存在于同一个体系中 字符缓冲流的特殊方法: BufferedReader: public String readLine():根据终止符读取一行数据,返回的是一个字符串,当返回null时,表明读取完毕 BufferedWriter: public void newLine():会根据系统自动区别换行符 案例体现一: import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; /* * 由于我们自定义缓冲能够提高程序的效率,所以,java在设计类的时候,也考虑到了这个问题。 * 它就提供了带缓冲的字符输入输出流。 * * BufferedWriter:写入数据 * BufferedReader:读取数据 * * 需求:往一个文本文件写入一句话。 * * 构造方法: * public BufferedWriter(Writer out): * * 为什么需要传递一个Writer呢? * 缓冲区流只是提供了缓冲区,让我们的程序高效。真正的读写还是的用基本的流对象。 * * * 写入数据步骤: * 1:创建字符缓冲输出流,需要传递一个基本的流对象。 * 2:调用写入方法。 * 3:释放资源 */ public class BufferedWriterDemo { public static void main(String[] args) throws IOException { //1:创建字符缓冲输出流,需要传递一个基本的流对象。 BufferedWriter bw = new BufferedWriter(new FileWriter(“fw.txt”)); //2:调用写入方法。 bw.write(“hello”); bw.flush(); //3:释放资源 bw.close(); } } 案例体现二: import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; /* * 需求:读取当前项目路径下的a.java显示到控制台。 * * public BufferedReader(Reader r): * * 读取数据步骤: * 1:创建字符缓冲输入流,需要传递一个基本的流对象。 * 2:调用读取方法。 * 三种: * 一次读取一个字符。 * 一次读取一个字符数组。 * 一次读取一行 * 3:释放资源 */ public class BufferedReaderDemo { public static void main(String[] args) throws IOException { //1:创建字符缓冲输入流,需要传递一个基本的流对象。 BufferedReader br = new BufferedReader(new FileReader(“a.txt”)); //2:调用读取方法。 //方式1 int ch = 0; while((ch=br.read())!=-1){ System.out.print((char)ch); } System.out.println(“***********************”); //方式2 char[] chs = new char[1024]; int len = 0; while((len=br.read(chs))!=-1){ System.out.print(new String(chs,0,len)); } //方式3 String line = null; while((line = br.readLine()) != null) { System.out.println(line); } //3:释放资源 br.close(); } } 案例体现三: package com.itheima.buffered; import java.io.FileReader; import java.io.IOException; /* * 自定义类模拟BufferedReader的特殊方法readLine() * */ public class MyBufferedReader { private FileReader fr; public MyBufferedReader(FileReader fr) { this.fr = fr; } /*可以读取一行的方法*/ public String myReadLine() throws IOException { //定义一个临时容器,原BufferedReader封装的一个字符数组 //为了演示方便,定义一个StringBuilder,因为最终还是要将数据定义成字符串 StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = fr.read()) != -1) { if(ch == ‘\r’) continue; if(ch == ‘\n’) return sb.toString(); else sb.append((char)ch); } //这个判断用于解决读取最后一行的数据,因为如果回车符没有换行,那么就无法返回sb中的数据 if(sb.length() != 0) return sb.toString(); return null; } public void myClose() { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } package com.itheima.buffered; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class MyBufferedReaderDemo { public static void main(String[] args) throws IOException { MyBufferedReader mfr = new MyBufferedReader(new FileReader(“G:\\a.txt”)); String line = null; while((line = mfr.myReadLine()) != null) { System.out.println(line); } mfr.myClose(); } } LineNumberReader: 跟踪行号的缓冲字符输入流,该类继承了BufferedReader类,此类定义的特殊方法: int getLineNumber() 获得当前行号。 void setLineNumber(int lineNumber) 设置当前行号。 转换流: 输入/输出体系中提供了两个转换流,这个两个转换流用于实现将字节流转换成字符流。 InputStreamReader:将字节输入流转换成字符输入流,OutputStreamWriter:将字节输出流转换成字符输出流 案例体现一: import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; /* * 数据源: * 键盘录入:System.in代表标准键盘录入,这个标准输入流是InputStream类的实例, * 使用不便,而且键盘录入的内容都是文本内容,所以可以使用InputStreamReader * 将其转换成字符输入流,普通的Reader读取不够高效,所以在此把Reader包装成 * BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行数据 * 目的地: * 文本文件(a.txt) BufferedWriter */ public class SysetmInDemo2 { public static void main(String[] args) throws IOException { // 封装数据源 // InputStream is = System.in; // InputStreamReader isr = new InputStreamReader(is); // BufferedReader br = new BufferedReader(isr); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new FileWriter(“a.txt”)); //基本读写 String line = null; while((line=br.readLine())!=null){ if(“over”.equals(line)){ break; } bw.write(line); bw.newLine(); bw.flush(); } bw.close(); br.close(); } } 案例体现二: import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; /* * 需求:把键盘录入的数据,转成大写通过标准流显示在控制台。 * * 数据源: * System.in InputStreamReader OutputStream BufferedWriter */ public class SystemOutDemo2 { public static void main(String[] args) throws IOException { // 封装数据源 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); //基本读写操作 String line = null; while((line=br.readLine())!=null){ //数据源是键盘录入的时候,一定要自定义结束标记 if(“886”.equals(line)){ //== break; } bw.write(line.toUpperCase()); bw.newLine(); bw.flush(); } bw.close(); br.close(); } } 打印流(PrintStream、PrintWriter): 特点: 1:可以用来写入数据,可以打印任意类型的数据。 Sysetm.out – PrintStream 原来我们的输出语句底层是IO流的操作 2:可以自动刷新数据。 前提:必须是启动了自动刷新,还必须是使用println、printf 或 format等方法的时候才可以。 如何启动自动刷新? 通过它的构造方法:PrintWriter(OutputStream out, boolean autoFlush),把autoFlush设为true 3:它是可以直接操作设备的流对象。 如果判断一个流对象是否可以直接操作设备呢? 看其构造方法,是否有同时传字符串和File对象的构造。如果有,则说明他是可以直接操作设备的。 案例体现一: import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; /* * println方法: * 先启动刷新。通过println方法写入数据,还可以实现自动换行功能。 */ public class PrintWriterDemo3 { public static void main(String[] args) throws IOException { // 创建打印流,并启动刷新 PrintWriter pw = new PrintWriter(new FileWriter(“pw3.txt”), true); // 用println方法写入数据 pw.println(“hello”); pw.println(“world”); pw.println(“真高兴,太还了。世界因你而精彩”); pw.flush(); pw.close(); } } 案例体现二: /* * printf方法: * */ public class PrintWriterDemo4 { public static void main(String[] args) { System.out.printf(“hello”+20+”\n”); //可以直接对数据进行格式化。 //通过看API中的文件,发现格式是:%+特殊意义的字符 System.out.printf(“%s,%d\n”,”hello”,20); System.out.printf(“%d+%d=%d %c”,3,4,7,65); } } 案例体现三: import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; /** * 需求:把i盘目录下的所有.java结尾的文件路径存储到一个文件中,方便查找 * @author xiedong * */ public class ShowPath { public static void main(String[] args) throws IOException { //封装数据 File file1 = new File(“I://”); //创建集合,保存数据 ArrayList<File> al = new ArrayList<File>(); getJavaFile(file1, al); //封装目的地 File file2 = new File(“i://codepath.txt”); getJavaFilePath(al, file2); } /** * 获取java文件的方法 * @param file * @param al * @throws IOException */ private static void getJavaFile(File file, ArrayList<File> al) throws IOException { File[] files = file.listFiles(); for(File fileItem : files) { if(fileItem.isDirectory()) { getJavaFile(fileItem, al); }else { if(fileItem.getName().endsWith(“.java”)) { //将数据存入集合 al.add(fileItem); } } } } /** * 写入数据 * @param al * @param file2 * @throws IOException */ private static void getJavaFilePath(ArrayList<File> al, File file2) throws IOException { PrintWriter pw = new PrintWriter(new FileWriter(file2),true); for(File fileItem : al) { pw.println(fileItem); } pw.close(); } } 对象序列化: 对象的序列化(Serialize)机制允许将实现序列化的Java对象按照流的方式写入文件 对象的反序列化(Deserialize)则是从IO流中恢复该Java对象 如果需要将某个对象保存到磁盘上或者通过网络传输,那么这个类应该实现Serializable接口, 或者Externalizable接口。使用Serializable来实现序列化,只需在目标类Serializable标记接口 即可,无需实现任何方法。 注意:静态不能被序列化,如果非静态成员不想被序列化,加transient 序列化对象的步骤: 1:创建ObjectOutputStream,这个输出流必须建立在其他节点流的基础之上。 2:调用ObjectOutputStream对象的writeObject()方法输出可序列化对象 如果希望从二进制流中恢复Java对象,则需要使用反序列化。 反序列化的步骤: 1:创建一个ObjectInputStream输入流,这个输出流必须建立在其他节点流的基础之上。 2:调用ObjectOutputStream对象的readObject()方法读取流中的对象,该方法返回一个 Object类型的java对象,如果程序知道该java对象的类型,则可以将对象强制转换为 原来的类型。 案例体现: package com.itheima import java.io.Serializable; /* * Exception in thread “main” java.io.InvalidClassException: cn.itcast_03.Student; * local class incompatible: stream classdesc serialVersionUID = 7423040749049938137, * local class serialVersionUID = 5208717516990788757 * * 我们在把对象按照流的方式进行存储的时候,会写一个值,这个值和class文件里面的id值一致。 * 如果实现了序列化接口的时候,我们没有给值,它会有一个默认值。 * 第一次:100 * 这个时候,写到文件中的id值也是100 * 第二次: * 我直接修改了类,这个时候,也会有一个序列化值,假如是200。 * 那么,香港服务器租用,我们直接读取文件,就会有问题。 * 如何解决这个问题呢?使我们在对类做一些简单的变化的时候,id值不变。 * 只要保证class文件中的id值是不变的。 * 又因为class是由java源文件来的,所以,我们在Student源码中固定一个id值即可。 * 麻烦在于这个id值到低怎么表示呢? */ public class Student implements Serializable { private static final long serialVersionUID = 7423040749049938137L; private String name; private int age; //protected int age; public Student() { } public Student(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; } } package com.itheima import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /* * 把对象写入文本文件,从文本文件把对象读取出来。 * java.io.NotSerializableException: */ public class ObjectStreamDemo { public static void main(String[] args) throws Exception { wirte(); read(); } private static void read() throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( “oos.txt”)); Object obj = ois.readObject(); ois.close(); // 类型转换 Student s = (Student) obj; System.out.println(s.getName() + “***” + s.getAge()); } private static void wirte() throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( “oos.txt”)); Student s = new Student(); s.setName(“林青霞”); s.setAge(28); oos.writeObject(s); oos.close(); } } Properties: Properties是Hashtable的子类,它里面存储的键值对都是字符串,是集合中和IO相结合的容器 特点:可以用于键值对的形式配置文件 String getProperty(String key):通过键获取值,类似于Map的get(Object key); String getProperty(String key, String value):通过键获取值,如果键不存在,可以指定默认值 setProperty(String key, String value):设置属性值,类似于Map的put()方法 Set<String> stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。 void load(InputStream is):从属性文件中加载key-value对,把加载到的key-value对追加到Properties里 void store(OutputStream os, String file):将属性文件输出到指定文件 void list(PrintWriter out) 将属性列表输出到指定的输出流。 代码示例: package com.itheima.properties; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Iterator; import java.util.Properties; import java.util.Set; public class PropertiesDemo { public static void main(String[] args) throws Exception { Properties prop = new Properties(); setAndGet(prop); loadDemo(prop); } public static void setAndGet(Properties prop) throws Exception { prop.setProperty(“张三”, “22”); prop.setProperty(“李四”, “23”); String value = prop.getProperty(“haha”, “18”); System.out.println(value); /*遍历集合*/ Set<String> set = prop.stringPropertyNames(); Iterator<String> it = set.iterator(); while(it.hasNext()) { String key = it.next(); String value1 = prop.getProperty(key); System.out.println(key + ” = ” + value1); } /*把键值对输出到指定文件中*/ prop.store(new FileOutputStream(“prop.txt”), “prop”); //在控制台显示集合信息 prop.list(System.out); } public static void loadDemo(Properties prop) throws Exception { /*把属性文件加载到集合中*/ prop.load(new FileInputStream(“prop.txt”)); //控制台显示加载的文件信息 prop.list(System.out); //对集合进行修改 prop.setProperty(“张三”, “28”); //把修改后的数据,重新写入属性文件 prop.store(new FileOutputStream(“prop.txt”), “prop”); prop.list(System.out); } } 运行结果: 18 张三 = 22 李四 = 23 张三=22 李四=23 张三=22 李四=23 张三=28 李四=23 合并流: SequenceInputStream表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾, 接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。 构造方法: SequenceInputStream(Enumeration<? extends InputStream> e) 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。 SequenceInputStream(InputStream s1, InputStream s2) 通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2), 以提供从此 SequenceInputStream 读取的字节。 案例体现: package com.itheima.sequenceinputstream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.util.Enumeration; import java.util.Vector; public class SequenceInputStreamDemo { public static void main(String[] args) throws IOException { Vector<FileInputStream> v = new Vector<FileInputStream>(); v.add(new FileInputStream(“1.txt”)); v.add(new FileInputStream(“2.txt”)); v.add(new FileInputStream(“3.txt”)); Enumeration<FileInputStream> e = v.elements(); SequenceInputStream sis = new SequenceInputStream(e); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(“4.txt”)); byte[] bys = new byte[1024]; int len = 0; while((len = sis.read(bys)) != -1) { bos.write(bys, 0, len); bos.flush(); } bos.close(); sis.close(); } } RandomAccessFile: RandomAccessFile不是IO体系中的子类,而是直接继承Object类,但是它是IO包中的成员, 它同时具备读、写功能。内部封装了一个字节数组,而且通过指针对数组的元素进行操作。 读写原理:其实它的内部封装了字节输入、输出流 局限性:通过构造函数可以看出它只能操作文件,而且操作文件还有模式,该模式的取值: “r” 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 “rw” 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。 “rws” 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。 “rwd” 打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。 构造方法: RandomAccessFile(File file, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。 RandomAccessFile(String name, String mode) 创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 操作文件指针的方法: long getFilePointer():返回文件记录指针的当前位置 void seek(long pos):将指针移动到指定位置 代码示例: package com.itheima.randomaccessfile; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; /* * 功能:实现向文本自定义位置插入内容,不覆盖原有的内容 * */ public class InsertContent { public static void main(String[] args) throws IOException { insert(“G:\\Code\\FileDemo.java”, 50, “这是插入的内容”); } private static void insert(String fileName, int pos, String content) throws IOException { //static File createTempFile(String prefix, String suffix) 在默认临时文件目录中创建一个空文件,香港服务器,使用给定前缀和后缀生成其名称。 File tmp = File.createTempFile(“tmp”, null); //void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。 tmp.deleteOnExit(); RandomAccessFile raf = new RandomAccessFile(fileName, “rw”); //创建一个临时文件来保存插入点后的数据 FileOutputStream fos = new FileOutputStream(tmp); FileInputStream fis = new FileInputStream(tmp); //将指针移动到插入位置 raf.seek(pos); /* * 将插入点后的内容读入临时文件中保存 * */ byte[] bys = new byte[1024]; int len = 0; while((len = raf.read(bys)) != -1) { //将数据写入临时文件 fos.write(bys, 0, len); } /* * 插入内容 * */ //把文件指针重新定位到pos位置 raf.seek(pos); //System.out.println(raf.getFilePointer()); raf.write(content.getBytes()); //System.out.println(raf.getFilePointer()); while((len = fis.read(bys)) != -1) { raf.write(bys, 0, len); } } } 字符编码: 计算机底层是没有文本文件、图片文件之分的,它只记录每个文件的二进制序列。 常见的码表: ASCII:美国标准信息交换码,用一个字节的7位可以表示。 ISO8859-1:拉丁码表。欧洲码表用一个字节的8位表示。 GB2312:中国的中文编码表。 GBK:中国的中文编码表升级,融合了更多的中文文字符号。 Unicode:国际标准码,融合了多种文字。 UTF-8:最多用三个字节来表示一个字符。 字符流的出现方便操作字符,更重要的是加入了编码转换,通过转换流来完成的 InputStreamReader和OutputStreamWriter 举例说明: package cn.itcast_05; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; /* * 基本的字符和字节流是实现不了编码问题的。 * 如果你想实现编码的处理,就必须使用转换流。 * OutputStreamWriter:如果没有指定编码,默认编码是GBK。 * 字符流 = 字节符 + 编码表 * 编码问题的解决方案:采用统一编码表。 */ public class EncodeStream { public static void main(String[] args) throws IOException { // 使用的是默认的编码表 // OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream( // “osw.txt”)); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream( “osw.txt”), “UTF-8”); // GBK,UTF-8 // FileWriter osw = new FileWriter(“osw.txt”); osw.write(“中国”); osw.close(); InputStreamReader isr = new InputStreamReader(new FileInputStream( “osw.txt”),”UTF-8″); char[] chs = new char[20]; int len = isr.read(chs); String text = new String(chs,0,len); System.out.println(text); } }

是不是因为心痛的麻木了,我才笑得最美丽。

黑马程序员_java IO流

相关文章:

你感兴趣的文章:

标签云: