Java基础—IO流(三)
来源:互联网 发布:linux查看svn账号密码 编辑:程序博客网 时间:2024/05/18 04:53
一、概述
- File类用于将文件或文件夹封装成对象,方便对文件和文件夹的属性信息进行操作。该类可以作为参数传递给IO流的构造函数,弥补流对象在操作文件和文件夹上的缺陷。
二、File类的使用
- 1.构造方法
- 1)File(String FileName)
- 示例:
File f1 = new File("C:\\abc\\a.txt");
- 2)File(Strng, parent, String FileName)
- 示例:
File f2 = new File("C:\\abc", "b.txt");
该构造方法的好处在于对文件的操作更加灵活,出现文件目录固定,文件名需要改变的时候,这个构造方法更好
- 3)File(File parent, String FileName)
- 示例:
File d = new File("C:\\abc");
File f3 = new File(d, "c.txt");
示例代码:
package com.heisejiuhuche.io;import java.io.File;public class FileDemo { public static void main(String[] args) { /* 使用File类的不同构造方法封装文件对象 */ File f1 = new File("Demo1.txt"); File f2 = new File("C:/Users/jeremy/Documents/javaTmp/", "Demo2.txt"); File d = new File("C:/Users/jeremy/Documents/javaTmp/"); File f3 = new File(d, "Demo3.txt"); /* 打印各个文件对象 */ System.out.println("f1:" + f1); System.out.println("f2:" + f2); System.out.println("f3:" + f3); }}
程序输出结果:
f1:Demo1.txtf2:C:\Users\jeremy\Documents\javaTmp\Demo2.txtf3:C:\Users\jeremy\Documents\javaTmp\Demo3.txt
打印封装文件对象时的绝对路径或相对路径。
- 2.成员方法
- 1)创建操作
- -boolean createNewFile():文件名不存在的情况下创建新文件
-boolean mkdir():创建一级目录
-boolean mkdirs():创建多级目录
- 2)删除操作
- -boolean delete():删除指定文件
-void deleteOnExit():虚拟机退出的时候删除指定文件
- 3)判断操作
- -boolean canExecute():判断文件对象是否可执行
-boolean canRead():判断文件对象是否可读
-boolean canWrite():判断文件对象是否可写
-boolean exists():判断文件对象是否存在
-boolean isDirectory():判断文件对象是否是文件夹,判断之前,必须先判断该文件对象是否存在
-boolean isFile():判断文件对象是否是文件,判断之前,必须先判断该文件对象是否存在
-boolean isHidden():判断文件对象是否是隐藏文件
-boolean isAbsolute():判断文件对象路径是否是绝对路径
-int compareTo(File pathname):比较两个文件对象,以自然顺序排序
- 4)获取操作
- -String getName():获取文件对象名
-String getParent():获取文件对象的父母路,必须明确指定文件路径
-String getPath():获取文件对象相对路径
-String getAbsolutePath():获取文件对象绝对路径,文件可以存在也可以不存在
-long lastModified():获取文件对象最近一次被修改的时间
-long length():获取文件对象大小
- 5)修改操作
- -boolean renameTo(File dest):将文件修改为指定文件名并存入指定目录
- 6)其他重要方法
- -static File[] listRoots():获取有效盘符
-String[] list():获取指定目录中的文件和文件夹,包括隐藏文件;必须是存在目录调用,文件调用会空指针
-String[] list(FilenameFileter filter):获取指定格式的文件
-File[] listFiles()
-File[] listFiles(FileFilter filter)
-File[] listFiles(FilenameFilter filter)
- 3.递归
- 用递归遍历文件夹里的所有内容并以层级结构打印。
示例代码:
package com.heisejiuhuche.io;import java.io.File;public class RecursionDemo { public static void main(String[] args) { File dir = new File("C:/Users/jeremy/Documents/javaTmp/testfile"); recur(dir, 0); } /* 目录层级结构 */ private static String getLevel(int level) { StringBuilder sb = new StringBuilder(); /* 树形结构图形 */ sb.append("|--"); for(int x= 0; x < level; x++) { /* 根据层级的不同在树形结构图形前补上制表符 */ sb.insert(0, "\t"); } return sb.toString(); } /* 递归遍历文件夹,并打印所有文件和文件夹 */ private static void recur(File dir, int level) { /* 列出所有文件到文件数组 */ File[] files = dir.listFiles(); /* 打印文件夹的名称 */ System.out.println(getLevel(level) + dir.getName()); /* 打印完文件夹的名称,层级自增1 */ level++; /* 遍历所有文件,如果是文件夹,则递归遍历里面的文件和文件夹 */ for(int x = 0; x < files.length; x++) { if(files[x].isDirectory()) { recur(files[x], level); } else System.out.println(getLevel(level) + files[x].getName()); //如果不是文件夹,打印文件 } }}
程序输出结果:
|--testfile |--aaa |--ccc |--ddd |--h.txt |--ddd - Copy (2).txt |--ddd - Copy - Copy.txt |--ddd - Copy.txt |--ddd.txt |--ccc - Copy (2).txt |--ccc - Copy - Copy.txt |--ccc - Copy.txt |--ccc.txt |--aaa - Copy (2).txt |--aaa - Copy - Copy.txt |--aaa - Copy.txt |--aaa.txt |--bbb |--eee |--f.txt |--eee - Copy (2).txt |--eee - Copy - Copy.txt |--eee - Copy.txt |--eee.txt
注意:
递归的使用必须要明确控制条件,便面无限循环;递归要慎重使用,调用次数过多,会造成内存溢出。
- 4.删除带内容目录
示例代码:
package com.heisejiuhuche.io;import java.io.File;public class DelDirWithContentDemo { public static void main(String[] args) { File dir = new File("C:/Users/jeremy/Documents/javaTmp/testfile"); recur(dir); } /* 遍历所有文件夹,删除文件,并删除文件夹 */ private static void recur(File dir) { File[] files = dir.listFiles(); for(int x = 0; x < files.length; x++) { if(files[x].isDirectory()) recur(files[x]); else /* 打印文件名及文件删除结果 */ System.out.println(files[x].getName() + "--files--" + files[x].delete()); } /* 打印文件夹名及文件夹删除结果 */ System.out.println(dir.getName() + "**dir**" + dir.delete()); }}
程序输出结果:
demo - Copy (2) - Copy.txt--files--trueddd**dir**truedemo - Copy (2) - Copy - Copy - Copy.txt--files--truedemo - Copy (2) - Copy - Copy.txt--files--trueccc**dir**truedemo - Copy (2) - Copy - Copy.txt--files--truedemo - Copy (2) - Copy.txt--files--truedemo - Copy (2).txt--files--truedemo - Copy (3) - Copy.txt--files--trueaaa**dir**truedemo - Copy - Copy - Copy.txt--files--truedemo - Copy - Copy.txt--files--truedemo - Copy - Copy - Copy.txt--files--trueeee**dir**truebbb**dir**truedemo - Copy (2) - Copy.txt--files--truedemo - Copy (2).txt--files--truedemo - Copy - Copy (2).txt--files--truedemo - Copy - Copy (3).txt--files--truedemo - Copy - Copy - Copy.txt--files--truedemo - Copy - Copy.txt--files--truedemo - Copy.txt--files--truedemo.txt--files--truetestfile**dir**true
注意:
Java无法访问windows中的隐藏目录,所以,最好在for循环中判断的时候加上:
if(!files[x].isHidden() && files[x].isDirectory())
忽略隐藏的文件夹
- 5.练习
FileWriter
接收File
的构造方法。示例代码:
package com.heisejiuhuche.io;/** * 接收键盘录入,然后写入指定目录下的文件中 * 为了测试FileWriter接收File类型数据作为构造方法参数 */import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.io.InputStreamReader;public class FileWriterTest { public static void main(String[] args) { try { writeToFile(createFile("C:\\Users\\jeremy\\Documents\\FileWriter.txt")); } catch(IOException e) { e.printStackTrace(); } } private static File createFile(String filepath) throws IOException { File file = new File(filepath); if(!file.exists()) file.createNewFile(); return file; } private static void writeToFile(File file) { BufferedReader bufr = null; BufferedWriter bufw = null; try { bufr = new BufferedReader(new InputStreamReader(System.in)); bufw = new BufferedWriter(new FileWriter(file)); String line = null; while((line = bufr.readLine()) != null) { if(line.equals("over")) break; bufw.write(line); bufw.newLine(); bufw.flush(); } } catch(IOException e) { e.printStackTrace(); } finally { try { if(bufr != null) bufr.close(); } catch(IOException e) { e.printStackTrace(); } try { if(bufw != null) bufw.close(); } catch(IOException e) { e.printStackTrace(); } } }}
一、Properties类
- 1.概述
Properties
类是HashTable
的子类。该类具备Map
集合的特点,储存字符串作为键值对。Properties
类是集合中和IO技术相结合的容器。该类可以用于键值对形式的配置文件。配置文件用于保存软件运行时的各项参数。配置完成之后将会被持久化存储,每次运行软件都会加载该配置文件。- 2.应用
- 1)设置并打印键值对
示例代码:
package com.heisejiuhuche.io;import java.util.Properties;import java.util.Set;public class PropertiesDemo2 { public static void main(String[] args) { /* 创建Property对象 */ Properties prop = new Properties(); /* 设置键值对 */ prop.setProperty("zhangsan", "18"); prop.setProperty("wangwu", "20"); /* 通过键获取值,并打印 */ System.out.println(prop.getProperty("zhangsan")); /* 将键都返回并装进Set */ Set<String> value = prop.stringPropertyNames(); /* 遍历集合并打印键值 */ for(String str : value) { System.out.println(str + "::" + prop.getProperty(str)); } }}
程序输出结果:
18zhangsan::18wangwu::20
- 2)从文件加载配置并修改
示例代码:
package com.heisejiuhuche.io;import java.io.BufferedReader;import java.io.FileOutputStream;import java.io.FileReader;import java.io.IOException;import java.io.OutputStreamWriter;import java.util.Properties;public class PropertiesLoadDemo { public static void main(String[] args) throws IOException { Properties prop = new Properties();// loadProp(); /* 创建缓冲输入流对象并关联配置文件 */ BufferedReader bufr = new BufferedReader(new FileReader( "C:/Users/jeremy/Documents/javaTmp/info.txt")); /* 调用prop独享的load方法加载配置文件 */ prop.load(bufr); /* 在控制台列出所有键值对 */ prop.list(System.out); /* 修改键值对 */ prop.setProperty("李四", "50"); /* 调用store方法修改配置文件并保存 */ prop.store(new OutputStreamWriter(new FileOutputStream( "C:/Users/jeremy/Documents/javaTmp/info.txt"), "GBK"), "Test"); prop.list(System.out); } /* 方法一:读取文件,将每一行按=号分割,将键值对存入Properties对象 */ private static void loadProp() { BufferedReader bufr = null; Properties prop = null; try { bufr = new BufferedReader(new FileReader("C:/Users/jeremy/Documents/javaTmp/info.txt")); prop = new Properties(); String line = null; while((line = bufr.readLine()) != null) { String[] kv = line.split("="); prop.setProperty(kv[0], kv[1]); } } catch(IOException e) { e.printStackTrace(); } finally { try { if(bufr != null) bufr.close(); } catch(IOException e) { e.printStackTrace(); } } System.out.println(prop); }}
程序输出结果:
-- listing properties --王五=19张三=20李四=90-- listing properties --王五=19张三=20李四=50
注意:
-#都是注释,不会被Properties加载
-Properties加载的配置文件必须有固定格式;通常为:键=值
- 3)记录程序运行次数
- 创建配置文件记录程序运行次数,到达
5
此,提示用户注册。示例代码:
package com.heisejiuhuche.io;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.util.Properties;public class PropertiesTest { public static void main(String[] args) throws IOException { try { checkAuth(); } catch(IOException e) { throw new RuntimeException("配置文件加载异常..."); } } private static void checkAuth() throws IOException { /* 创建配置文件对象 */ File file = new File("C:/Users/jeremy/Documents/javaTmp/auth.txt"); /* 如果文件不存在,创建文件,避免异常 */ if(!file.exists()) file.createNewFile(); /* 创建输出流对象 */ FileInputStream fis = new FileInputStream(file); /* 创建Properties对象 */ Properties prop = new Properties(); /* 加载配置文件 */ prop.load(fis); String val = null; int count = 0; /* 如果读取键值为空,说名是第一次运行,那么将计数器count+1,然后和键time一起存入prop对象 * 并写入配置文件 */ if((val = prop.getProperty("time")) != null) { /* 如果取到了值,说明不是第一次运行,那么让计数器count等于time键所对应的值 * 再+1,然后和time键一起再次存入配置文件 */ count = Integer.parseInt(val); /* 判断,如果count = 5,说明使用次数已到,结束程序,提示注册 */ if(count >= 5) { System.out.println("请注册..."); return; } } count++; prop.setProperty("time", count + ""); /* 输出流不能在load语句前声明,否则将会覆盖配置文件,导致配置信息清空 */ FileOutputStream fos = new FileOutputStream(file); /* 将配置文件更新 */ prop.store(fos, "CheckAuth"); fis.close(); fos.close(); }}
二、打印流
- 1.PrintStream类
- 1)概述
PrintStream
类是字节打印流,其为其他流添加了功能,使它们能够方便打印各种数据值形式。该类不会抛出IOException
,同时内部有刷新机制,无须手动刷新缓冲区。PrintStream
可以直接操作文件对象。- 2)常用方法
- -PrintStream(File file):接收File对象
-PrintStream(OutputStream out):接收字节输出流
-PrintStream(String filename):接收字符串路径
-println():打印所有基本数据类型,保持数据原样
- 3)PrintStream示例
示例代码:
package com.heisejiuhuche.io;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintStream;public class PrintStreamDemo { public static void main(String[] args) throws IOException { /* 创建输入流和PrintStream对象并关联文件 */ BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); PrintStream ps = new PrintStream("C:/Users/jeremy/Documents/javaTmp/PrintStream.txt"); String len = null; /* 接收键盘输入并写入文件 */ while((len = bufr.readLine()) != null) { if(len.equals("over")) break; ps.println(len); ps.flush(); } bufr.close(); ps.close(); }}
该类的使用方法和下面介绍的PrintWriter
类基本相同。
- 2.PrintWriter类
- 1)概述
字符打印流可以打印基本舒蕾型。其最强大的功能是
println
方法,可以实现自动换行并直接操作基本数据类型。- 2)常用方法
- -PrintWriter(File file):接收File对象
-PrintWriter(File file, String csn):接收File对象,并制定字符集
-PrintWriter(OutputStream out):接收字节输出流
-PrintWriter(OutputStream out, boolean autoflush):接收字节输出流,设置自动刷新
-PrintStream(Writer out):接收字符输出流
-PrintWriter(String filename):接收字符串路径
- 3)PrintWriter示例
示例代码:
package com.heisejiuhuche.io;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;public class PrintWriterDemo { public static void main(String[] args) throws IOException { /* 创建输入流对象和打印流对象 */ BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw = new PrintWriter(System.out, true); String len = null; while((len = bufr.readLine()) != null) { /* 如果len等于over,就停止程序 */ if(len.equals("over")) break; /* 调用println方法,在打印的时候自动换行 */ pw.println(len.toUpperCase()); } bufr.close(); pw.close(); }}
注意:
在PrintWriter构造方法中设置true,就无须调用flush()刷新缓冲。
三、序列流
- 1.SequenceInputStream类
- 1)概述
SequenceInputStream
类标识其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到达到文件末尾;接着从第二个输入流读取,以此类推,直到到达包含的最后一个输入流的文件末尾为止。- 2)应用
- 将三个文件的内容使用
SequenceInputStream
写入到一个文件中。示例代码:
package com.heisejiuhuche.io;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.SequenceInputStream;import java.util.ArrayList;import java.util.Enumeration;import java.util.Iterator;public class SequenceInputStreamDemo { public static void main(String[] args) { write(); } private static void write() { SequenceInputStream sis = null; ArrayList<FileInputStream> list = null; FileOutputStream fos = null; try { /* 创建集合对象 */ list = new ArrayList<FileInputStream>(); /* 将输入流对象存入集合 */ for(int x = 1; x <= 3; x++) { list.add(new FileInputStream( "C:\\Users\\jeremy\\Documents\\javaTmp\\mp\\" + x + ".txt")); } Iterator<FileInputStream> it = list.iterator(); /* 得到装有输入流对象的Enumeration */ Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() { public boolean hasMoreElements() { return it.hasNext(); } public FileInputStream nextElement() { return it.next(); } }; /* 创建序列流对象和输出流对象 */ sis = new SequenceInputStream(en); fos = new FileOutputStream("C:\\Users\\jeremy\\Documents\\javaTmp\\mp\\4.txt"); byte[] buf = new byte[1024]; int len = 0; /* 合并文件 */ while((len = sis.read(buf)) != -1) { fos.write(buf, 0, len); fos.flush(); } } catch(IOException e) { e.printStackTrace(); } finally { try { if(sis != null) sis.close(); } catch(IOException e) { e.printStackTrace(); } try { if(fos != null) fos.close(); } catch(IOException e) { e.printStackTrace(); } } }}
- 3)切割文件后再合并
- 将一个
MP3
文件按1M
切割,最后合并,要求保证合并后的文件可以播放。示例代码:
package com.heisejiuhuche.io;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.SequenceInputStream;import java.util.ArrayList;import java.util.Enumeration;import java.util.Iterator;public class SplitMp3Test { public static void main(String[] args) { split(); merge(); } private static void merge() { /* 创建集合,用于存放输入流对象 */ ArrayList<FileInputStream> list = new ArrayList<FileInputStream>(); SequenceInputStream sis = null; FileOutputStream fos = null; try { /* 将四个文件输入流对象存入集合 */ for(int x = 1; x <= 4; x++) { list.add(new FileInputStream("C:\\Users\\jeremy\\Documents\\javaTmp\\mp\\" + x + ".part")); } Iterator<FileInputStream> it = list.iterator(); /* 拿到有输入流对象的Enumeration */ Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() { public boolean hasMoreElements() { return it.hasNext(); } public FileInputStream nextElement() { return it.next(); } }; /* 创建序列流对象 */ sis = new SequenceInputStream(en); /* 创建输出流对象,并关联文件 */ fos = new FileOutputStream("C:\\Users\\jeremy\\Documents\\javaTmp\\mp\\back.mp3"); byte[] buf = new byte[1024]; int len = 0; /* 合并文件 */ while((len = sis.read(buf)) != -1) { fos.write(buf, 0, len); fos.flush(); } } catch(FileNotFoundException e) { throw new RuntimeException("文件不存在..."); } catch(IOException e) { throw new RuntimeException("合并失败..."); } finally { try { if(sis != null) sis.close(); } catch(IOException e) { throw new RuntimeException("资源关闭失败..."); } try { if(fos != null) fos.close(); } catch(IOException e) { throw new RuntimeException("资源关闭失败..."); } } } private static void split() { File file = null; FileInputStream fis = null; FileOutputStream fos = null; try { /* 创建输入流对象 */ file = new File("C:\\Users\\jeremy\\Documents\\javaTmp\\Backseat.mp3"); fis = new FileInputStream(file); byte[] buf = new byte[1024 * 1024]; int count = 1; int len = 0; /* 将文件按1M大小分割,分为count个文件 */ while((len = fis.read(buf)) != -1) { fos = new FileOutputStream("C:\\Users\\jeremy\\Documents\\javaTmp\\mp\\" + count++ + ".part"); fos.write(buf, 0, len); fos.flush(); } } catch(IOException e) { throw new RuntimeException("分割失败..."); } finally { try { if(fis != null) fis.close(); } catch(IOException e) { throw new RuntimeException("输入资源关闭失败"); } try { if(fos != null) fos.close(); } catch(IOException e) { throw new RuntimeException("输出资源关闭失败"); } } }}
四、对象流
- 1.ObjectOutputStream和ObjectInputStream类
- 1)概述
ObjectOutputStream
类可以将Java对象的基本数据类型和图形写入OutputStream
,再利用ObjectInputStream
读取(重构)该对象。该类用于将对象持久化存储在硬盘上,以便程序下次启动的时候能再次加载该对象。- 2)常用方法
ObjectOutputStream
具备直接操作基本数据类型的一些列write()
方法及操作对象的writeObject()
方法。以一个示例来演示该类的使用方法及注意事项。示例代码:
package com.heisejiuhuche.io;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class ObjectOutputInputStreamDemo { static final File file = new File("C:/Users/jeremy/Documents/javaTmp/person.txt"); public static void main(String[] args) throws Exception { writeObj(); } private static void readObj() throws Exception { /* 创建对象输入流对象,传入一个字节输入流,并关联文件 */ ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); /* 读取Person对象,向下转型为Person类型 */ Person p = (Person)ois.readObject(); /* 打印Person对象 */ System.out.println(p); /* 关闭资源 */ ois.close(); } private static void writeObj() throws Exception { /* 创建对象输出流对象,传入一个字节输出流,并关联文件 */ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); /* 调用writeObject方法将Person对象写入文件 */ oos.writeObject(new Person("zhangsan", 28)); /* 写入文件后读取并打印在控制台 */ readObj(); /* 关闭资源 */ oos.close(); }}class Person { private String name; private int age; Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return this.name + "::" + this.age; }}
注意:
上面的代码报出异常:
Exception in thread “main” java.io.NotSerializableException: com.heisejiuhuche.io.Person
在使用ObjectOutputStream写入对象的时候,被写入的对象必须实现Serializable接口
- 3)Serializable接口
Serializable
接口是一个标记接口,它没有任何方法。该接口给对象定义一个固定的数字标识,将该对象类序列化。使用该标识作为对象存储和读取是否一致的判断标准。这个数字标识叫做SerialVersionUID
,是根据每个对象的成员,由Java计算出来的一个固定值。如果读取的时候,该值发生了变化,会抛出异常。下面会用代码演示这一现象及序列化的其他特点。修改上述代码,按要求使Person
类实现Serializable
接口。
示例代码:
class Person implements Serializable { private String name; private int age; Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return this.name + "::" + this.age; }}
程序运行结果:
zhangsan::28
此时,如果将Person
类中name
成员的private
关键字去掉,在写入和读取时,就会发生UID
不匹配的情况,抛出异常。
示例代码:
class Person implements Serializable { /* 去掉了private关键字 */ String name; private int age; Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return this.name + "::" + this.age; }}
程序运行结果:
Exception in thread "main" java.io.InvalidClassException这证明了UID是根据成员的数字标识算出来的一个固定值。
如果要实现更改成员之后还能读取该类,只需手动指定UID
。下面的代码仍然去掉了private
关键字,但是读取无误。
示例代码:
package com.heisejiuhuche.io;import java.io.Serializable;class Person implements Serializable { /* 手动指定UID */ private static final long serialVersionUID = 37L; String name; private int age; Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return this.name + "::" + this.age; }}
程序运行结果:zhangsan::28
注意:
-已被赋值的静态成员,在序列化后,值不能修改;如,static String country = “cn”;之后再修改country的值,读取的时候,还是”cn”;
-不想序列化某非静态成员,可以加上transient关键字:transient private int age;
五、管道流
- 1.概述
- 管道流能通过结合线程技术,实现输入流和输出流的直接连接。管道输入和输出流必须连接在一起使用。某个线程从
PipedInputStream
读取数据,并由另一个线程写入到PipedOutputStream
。这是涉及多线程技术的IO流
。- 2.PipedInputStream和PipedOutputStream类
- 管道流可以通过构造方法连接,也可以通过调用
connect()
方法连接。示例代码:
package com.heisejiuhuche.io;import java.io.IOException;import java.io.PipedInputStream;import java.io.PipedOutputStream;public class PipedStreamDemo { public static void main(String[] args) throws IOException { /* 创建管道流对象并连接 */ PipedInputStream pis = new PipedInputStream(); PipedOutputStream pos = new PipedOutputStream(); pis.connect(pos); /* 启动线程 */ new Thread(new Read(pis)).start(); new Thread(new Write(pos)).start(); }}class Read implements Runnable { private PipedInputStream pis; Read(PipedInputStream pis) { this.pis = pis; } public void run() { /* 等待管道输出流写入数据,读到输出流数据前,处于阻塞状态 */ try { byte[] buf = new byte[1024]; System.out.println("等待读取..."); int len = pis.read(buf); System.out.println("读取结束..."); System.out.println(new String(buf, 0, len)); } catch(IOException e) { throw new RuntimeException("读取失败"); } }}class Write implements Runnable { private PipedOutputStream pos; Write(PipedOutputStream pos) { this.pos = pos; } public void run() { /* 往管道输入流中写入数据,写入先等待6秒,模拟写入过程 */ try { System.out.println("数据写入中..."); Thread.sleep(6000); pos.write("数据来啦!!!!!".getBytes()); pos.close(); } catch(Exception e) { throw new RuntimeException("写入失败"); } }}
程序运行结果:
等待读取...数据写入中...读取结束...数据来啦!!!!!
六、RandomAccessFile类
- 1.概述
RandomAccessFile
类不是IO体系中的子类,而是直接继承自Object
。但是它仍然是IO包
中的成员,因为它具备读写功能。该类支持对随机访问文件的读取和写入。该类的内部封装了数组,通过文件指针对数据进行操作,可以通过getFilePointer()
方法获取指针位置;通过seek()
方法改变指针的位置。RandomAccessFile
类只能操作文件,并必须设定操作模式。- 2.应用
- 演示
RandomAccessFile
的基本方法及特点。示例代码:
package com.heisejiuhuche.io;import java.io.IOException;import java.io.RandomAccessFile;public class RandomAccessFileDemo { public static void main(String[] args) throws IOException { write(); read(); } private static void write() throws IOException { /* 创建RnadomAccessFile对象 */ RandomAccessFile raf = new RandomAccessFile("C:/Users/jeremy/Documents/javaTmp/random.txt", "rw"); raf.write("李四".getBytes()); /* write()方法的特点是,只写出数据的最低8位,会造成数据丢失,出现乱码 */// raf.write(258); /* 保证数据不丢失,要调用writeInt()方法,写入4个8位 */ raf.writeInt(97); raf.write("王五".getBytes()); raf.writeInt(99); /* 让指针回到文件起始位置 */ raf.seek(0); /* 写入数据,那么李四的数据将被修改 */ raf.write("赵六".getBytes()); raf.writeInt(103); raf.close(); } private static void read() throws IOException { RandomAccessFile raf = new RandomAccessFile("C:/Users/jeremy/Documents/javaTmp/random.txt", "rw"); /* 创建4个字节的数组 */ byte[] buf = new byte[4]; /* 如果想要直接取王五的数据,调用seek方法让指针指向第二个8位即可 */ raf.seek(8); /* 如果想要直接取王五的数据,调用skepBytes方法跳过前8位 */ raf.skipBytes(8); /* 读取4个字节到数组中 */ raf.read(buf); /* 用readInt读取下4个字节 */ int age = raf.readInt(); System.out.println(new String(buf) + age); }}
注意:
-write()方法只写数据的最低8位,会造成数据丢失;在写入int类型数据时,调用writeInt()方法;
-skipBytes()方法只能往前跳过指定字节数,不能往回跳转;
-RandomAccessFile对象不仅能写如数据,还能修改该数据;
-RandomAccessFile对象在创建时,如果模式为”r”,就不会创建文件;如果该被读取文件不存在,会抛出异常;模式为”rw”,会自动创建该文件;如果文件已存在,则不会覆盖,继续添加内容;
-RandomAccessFile类可以结合多线程,实现数据的分段写入,提高写入效率
七、其他流对象
- 1.DataInputStream和DataOutputStream类
- 用于操作基本数据类型的流对象。
示例代码:
package com.heisejiuhuche.io;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class DataStreamDemo { public static void main(String[] args) throws IOException { write(); read(); writeUTF(); readUTF(); } private static void readUTF() throws IOException { DataInputStream dos = new DataInputStream(new FileInputStream( "C:/Users/jeremy/Documents/javaTmp/utf-8.txt")); System.out.println(dos.readUTF()); } private static void writeUTF() throws IOException { DataOutputStream dos = new DataOutputStream(new FileOutputStream( "C:/Users/jeremy/Documents/javaTmp/utf-8.txt")); /* 使用UTF-8修改版字符集写入数据 */ dos.writeUTF("你好"); dos.close(); } private static void read() throws IOException { /* 创建DataInputStream对象并关联文件 */ DataInputStream dos = new DataInputStream(new FileInputStream( "C:/Users/jeremy/Documents/javaTmp/data.txt")); /* 读取基本数据类型 */ int num = dos.readInt(); boolean b = dos.readBoolean(); double d = dos.readDouble(); System.out.println("num = " + num); System.out.println("b = " + b); System.out.println("d = " + d); } private static void write() throws IOException { /* 创建DataOutputStream对象并关联文件 */ DataOutputStream dos = new DataOutputStream(new FileOutputStream( "C:/Users/jeremy/Documents/javaTmp/data.txt")); /* 写入基本数据类型 */ dos.writeInt(234); dos.writeBoolean(false); dos.writeDouble(124.135252); dos.close(); }}
程序运行结果:
num = 234b = falsed = 124.135252你好
- 2.ByteArrayInputStream和ByteArrayOutputStream类
- 用于操作字节数组的流对象。该类的对象无须关闭,因为没有调用任何系统底层资源;如果关闭,仍可以调用其方法,同时没有任何
IO异常
。ByteArrayInputStream
在初始化的时候需要接收一个字节数组;ByteArrayOutputStream
内部的缓冲区,会随着数据的不断写入而自动增长。这两个类用流的读写思想来操作数组,用于往内存中暂时写入数据。示例代码:
package com.heisejiuhuche.io;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;public class ByteArrayStreamDemo { public static void main(String[] args) { readWrite(); } private static void readWrite() { /* 创建字节数组流对象并关联文件 */ ByteArrayInputStream bis = new ByteArrayInputStream("abcdefg".getBytes()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int ch = 0; /* 循环读取所有字节并写去字节数组输出流对象 */ while((ch = bis.read()) != -1) { bos.write(ch); } /* 打印数组大小,并打印内容 */ System.out.println(bos.size()); System.out.println(new String(bos.toByteArray())); System.out.println(bos.toString()); }}
程序运行结果:
7abcdefgabcdefg
注意:
和字节数组输入输出流类似的流对象还有:
这些类的使用方式和字节数组流相似。
一、概述
- 字符编码表是由计算机二进制数
1010
的排列组合和文字的对应关系形成的。它的出现可以让计算机识别各个国家不同的文字和符号。二、常见字符编码表
- 1.ASCII码表
- 美国标准信息交换码,用一个字节的
7位
标识一个字符,最高位为0
。- 2.ISO8859-1
- 拉丁码表(欧洲码表),用一个字节的
8位
标识一个字符。- 3.GB2312和GBK
GB2312
和GBK
都是中文字符编码表。后者是前者的升级版,囊括更多的中文文字和符号。- 4.Unicode
- 国际标准码,融合了全世界多种文字和符号。
- 5.UTF-8
Unicode
编码表的优化版,最多用3个
字节标识一个字符。三、乱码问题
- 乱码问题的产生,原因在于解码的时候没有使用编码时的编码表。比如,在写入中文数据的时候,指定了GBK编码表,但是在读取数据的时候却用了UTF-8编码表。GBK是两个字节表示一个字符,UTF-8是三个字节表示一个字符。如果输入的字符是“你好”,对应的GBK编码表上的数字是【-60,-29,-70,-61】;读取的时候误用了UTF-8编码表,那么会读取【-60,-29,-70】这三个字节,到UTF-8码表里面寻找有没有对应的字符,如果没有,返回?;接着拿【-61】去找,如果没有匹配的字符,返回?。反过来的过程相同。“你好”对应的UTF-8的数字是【-28,-67,-96,-27,-91,-67】,如果在读取时误用了GBK码表,那么就拿每两个数字去GBK表中查找对应字符,因此出现乱码。
四、编码解码
- 1.定义
- 编码就是将字符串转换为字节数组的过程;解码就是将字节数组转换为字符串的过程。
- 2.解决乱码问题
- 实际开发过程中,
web服务器
端通常默认使用ISO8859-1
的编码表。如果出现使用GBK
编码,而数据传到服务器端之后出现乱码的问题,只需要将乱码的字符串再次用ISO8859-1
进行编码,在用GBK
解码即可。示例代码:
package com.heisejiuhuche.io;public class EncodeDemo { public static void main(String[] args) throws Exception { String s1 = "你好"; /* 使用gbk编码 */ byte[] b1 = s1.getBytes("GBK"); /* 使用iso8859-1解码 */ String s2 = new String(b1, "ISO8859-1"); /* 出现乱码???? */ System.out.println(s2); /* 是同iso8859-1再次编码 */ byte[] b2 = s2.getBytes("ISO8859-1"); /* 使用gbk解码 */ String s3 = new String(b2, "GBK"); /* 还原字符串成功 */ System.out.println(s3); }}
程序运行结果:
????你好
五、联通的问题
- 在文本文件中输入“联通”二字,保存退出;第二次打开时会出现乱码。
图中可见“联通”二字的二进制储存形式,符合UTF-8
两个字节存储的格式要求;最高位分别为110
和10
。所以当“联通”二字以GBK
编码表形式编码存储后,记事本在读取的时候误认为是UTF-8
编码表编码,会到UTF-8
码表中查找字符,形成乱码。
六、练习
- 有
5
个学生,每个学生有3
们课程的成绩。从键盘录入以上数据,格式为:姓名,数学成绩,语文成绩,英语成绩;并计算出总成绩。将最后的学生信息和计算出的总分按从高到低的顺序存入文件“stud.txt”
中。示例代码:
package com.heisejiuhuche.io;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.io.InputStreamReader;import java.util.Collections;import java.util.Comparator;import java.util.TreeSet;public class StudentInfoToFileTest { public static void main(String[] args) { /* 反转比较器 */ Comparator<Student> student = Collections.reverseOrder(); /* 获得学生对象并写入文件 */ StudentTools.infoToFile(StudentTools.getStudentInfo(student)); }}class StudentTools { public static TreeSet<Student> getStudentInfo(Comparator<Student> comp) { BufferedReader bufr = null; try { /* 创建TreeSet对象,用于储存学生对象并排序 */ TreeSet<Student> stuSet = null; /* 接收键盘录入 */ bufr = new BufferedReader(new InputStreamReader(System.in)); /* 判断有无比较器参数,分别创建有比较器和没有比较器的两个TreeSet对象 */ if(comp == null) stuSet = new TreeSet<Student>(); else stuSet = new TreeSet<Student>(comp); String tmp = null; /* 不断接收键盘按固定格式的录入,如果收到over,结束程序 */ while((tmp = bufr.readLine()) != null) { if(tmp.equals("over")) break; /* 将字符串按指定格式切割 */ String[] info = tmp.split(","); /* 将每段信息传入学生对象封装 */ Student stu = new Student(info[0], Integer.parseInt(info[1]), Integer.parseInt(info[2]), Integer.parseInt(info[3])); /* 将封装号的学生对象存入集合以便排序 */ stuSet.add(stu); } return stuSet; } catch(IOException e) { throw new RuntimeException("文件读入失败..."); } finally { try { if(bufr != null) bufr.close(); } catch(IOException e) { throw new RuntimeException("输入流关闭失败..."); } } } public static void infoToFile(TreeSet<Student> stuSet) { File file = new File("C:/Users/jeremy/Documents/javaTmp/StudentInfo.txt"); BufferedWriter bufw = null; try { bufw = new BufferedWriter(new FileWriter(file)); /* 将集合中的数据写入文件 */ for(Student stus : stuSet) { bufw.write(stus.toString() + "\t"); bufw.write(stus.getSum() + ""); bufw.newLine(); bufw.flush(); } } catch(IOException e) { throw new RuntimeException("文件写入失败..."); } finally { try { if(bufw != null) bufw.close(); } catch(IOException e) { throw new RuntimeException("输入流关闭失败..."); } } }}/* 创建学生类 */class Student implements Comparable<Student> { private String name; private int math, cn, en, sum; Student(String name, int math, int cn, int en) { this.name = name; this.math = math; this.cn = cn; this.en = en; sum = math + cn + en; } public void setName(String name) { this.name = name; } public void setMath(int math) { this.math = math; } public void setCn(int cn) { this.cn = cn; } public void setEn(int en) { this.en = en; } public String getName() { return name; } public int getMath() { return math; } public int getCn() { return cn; } public int getEn() { return en; } public int getSum() { return sum; } /* 复写hashCode方法 */ public int hashCode() { return this.name.hashCode() + this.sum * 37; } /* 复写equals方法 */ public boolean equals(Object obj) { if(!(obj instanceof Student)) throw new ClassCastException("类型不匹配"); Student stu = (Student)obj; return this.name.equals(stu.name) && this.sum == stu.sum; } /* 复写compareTo方法 */ public int compareTo(Student stu) { int flag = new Integer(this.sum).compareTo(new Integer(stu.sum)); if(flag == 0) return this.name.compareTo(stu.name); return flag; } /* 复写toString方法 */ public String toString() { return "Student[" + this.name + "," + math + "," + cn + "," + en + "]"; }}
程序运行结果:
Student[zhouqi,70,70,70] 210Student[zhaoliu,60,60,60] 180Student[wangwu,50,50,50] 150Student[lisi,40,40,40] 120Student[zhangsan,30,30,30] 90
- Java基础—IO流(三)
- Java基础--IO流(三)
- Java基础——IO流(三)
- 黑马程序员——Java基础--------IO流(三)
- Java基础——Java重点基础之IO流(三)
- Java基础---IO流三(字节流& 字符流)
- 黑马程序员——Java基础---IO流(三)
- java基础整理二十一(IO流三)
- 黑马程序员 Java基础--IO流(三)
- 黑马程序员--Java基础--IO流(三)
- 黑马程序员-----java基础IO流<三>
- 黑马程序员---Java基础---IO流(三)
- 黑马程序员-----Java基础-----IO流(三)
- 《文件传输基础----Java IO流---《三》》
- 黑马程序员——Java基础--IO(三)
- JAVA基础------IO操作(三)
- java基础巩固之IO(三)
- Java基础进阶——“Java IO流之三:IO实例”
- Servlet之请求和响应
- https://leetcode.com/problems/pascals-triangle/
- Android 深入理解Android中的自定义属性
- 四种生成和解析XML文档的方法详解
- 图表插件制作双轴图
- Java基础—IO流(三)
- phpstudy不同版本切换时遇到Mysql数据data直接copy需要注意的问题
- [bzoj 1049] HAOI2006数字序列
- Thread的中断机制(interrupt)
- OpenGL ES2.0 iPhone开发指引
- Android 碎片Fragment讲解
- iOS新浪微博客户端开发(1)——新特性界面
- storm文档----配置文件说明
- 进程通信-管道 read & write