Java基础—IO流(三)
IO流(三) File类
一、概述
- 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");
- 示例:
- 1)File(String FileName)
示例代码:
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.txt
f2:C:\Users\jeremy\Documents\javaTmp\Demo2.txt
f3:C:\Users\jeremy\Documents\javaTmp\Demo3.txt
打印封装文件对象时的绝对路径或相对路径。
- 2.成员方法
- 1)创建操作
- -boolean createNewFile():文件名不存在的情况下创建新文件
-boolean mkdir():创建一级目录
-boolean mkdirs():创建多级目录
- -boolean createNewFile():文件名不存在的情况下创建新文件
- 1)创建操作
- 2)删除操作
- -boolean delete():删除指定文件
-void deleteOnExit():虚拟机退出的时候删除指定文件
- -boolean delete():删除指定文件
- 2)删除操作
- 3)判断操作
- -boolean canExecute():判断文件对象是否可执行
-boolean canRead():判断文件对象是否可读
-boolean canWrite():判断文件对象是否可写
-boolean exists():判断文件对象是否存在
-boolean isDirectory():判断文件对象是否是文件夹,判断之前,必须先判断该文件对象是否存在
-boolean isFile():判断文件对象是否是文件,判断之前,必须先判断该文件对象是否存在
-boolean isHidden():判断文件对象是否是隐藏文件
-boolean isAbsolute():判断文件对象路径是否是绝对路径
-int compareTo(File pathname):比较两个文件对象,以自然顺序排序
- -boolean canExecute():判断文件对象是否可执行
- 3)判断操作
- 4)获取操作
- -String getName():获取文件对象名
-String getParent():获取文件对象的父母路,必须明确指定文件路径
-String getPath():获取文件对象相对路径
-String getAbsolutePath():获取文件对象绝对路径,文件可以存在也可以不存在
-long lastModified():获取文件对象最近一次被修改的时间
-long length():获取文件对象大小
- -String getName():获取文件对象名
- 4)获取操作
- 5)修改操作
- -boolean renameTo(File dest):将文件修改为指定文件名并存入指定目录
- 5)修改操作
- 6)其他重要方法
- -static File[] listRoots():获取有效盘符
-String[] list():获取指定目录中的文件和文件夹,包括隐藏文件;必须是存在目录调用,文件调用会空指针
-String[] list(FilenameFileter filter):获取指定格式的文件
-File[] listFiles()
-File[] listFiles(FileFilter filter)
-File[] listFiles(FilenameFilter filter)
- -static File[] listRoots():获取有效盘符
- 6)其他重要方法
- 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);}elseSystem.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--true
ddd**dir**true
demo - Copy (2) - Copy - Copy - Copy.txt--files--true
demo - Copy (2) - Copy - Copy.txt--files--true
ccc**dir**true
demo - Copy (2) - Copy - Copy.txt--files--true
demo - Copy (2) - Copy.txt--files--true
demo - Copy (2).txt--files--true
demo - Copy (3) - Copy.txt--files--true
aaa**dir**true
demo - Copy - Copy - Copy.txt--files--true
demo - Copy - Copy.txt--files--true
demo - Copy - Copy - Copy.txt--files--true
eee**dir**true
bbb**dir**true
demo - Copy (2) - Copy.txt--files--true
demo - Copy (2).txt--files--true
demo - Copy - Copy (2).txt--files--true
demo - Copy - Copy (3).txt--files--true
demo - Copy - Copy - Copy.txt--files--true
demo - Copy - Copy.txt--files--true
demo - Copy.txt--files--true
demo.txt--files--true
testfile**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));}}
}
程序输出结果:
18
zhangsan::18
wangwu::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
此,提示用户注册。
- 创建配置文件记录程序运行次数,到达
- 3)记录程序运行次数
示例代码:
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
可以直接操作文件对象。
- 1)概述
- 2)常用方法
- -PrintStream(File file):接收File对象
-PrintStream(OutputStream out):接收字节输出流
-PrintStream(String filename):接收字符串路径
-println():打印所有基本数据类型,保持数据原样
- -PrintStream(File file):接收File对象
- 2)常用方法
- 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
方法,可以实现自动换行并直接操作基本数据类型。
- 1)概述
- 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):接收字符串路径
- -PrintWriter(File file):接收File对象
- 2)常用方法
- 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
类标识其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到达到文件末尾;接着从第二个输入流读取,以此类推,直到到达包含的最后一个输入流的文件末尾为止。
- 1)概述
- 2)应用
- 将三个文件的内容使用
SequenceInputStream
写入到一个文件中。
- 将三个文件的内容使用
- 2)应用
示例代码:
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
切割,最后合并,要求保证合并后的文件可以播放。
- 将一个
- 3)切割文件后再合并
示例代码:
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
读取(重构)该对象。该类用于将对象持久化存储在硬盘上,以便程序下次启动的时候能再次加载该对象。
- 1)概述
- 2)常用方法
ObjectOutputStream
具备直接操作基本数据类型的一些列write()
方法及操作对象的writeObject()
方法。以一个示例来演示该类的使用方法及注意事项。
- 2)常用方法
示例代码:
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计算出来的一个固定值。如果读取的时候,该值发生了变化,会抛出异常。下面会用代码演示这一现象及序列化的其他特点。
- 3)Serializable接口
修改上述代码,按要求使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 = 234
b = false
d = 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());}
}
程序运行结果:
7
abcdefg
abcdefg
注意:
和字节数组输入输出流类似的流对象还有:
分类 | 输入 | 输出 |
---|---|---|
字符数组流 | CharArrayReader | CharArrayWriter |
字符串流 | StringReader | StringWriter |
这些类的使用方式和字节数组流相似。
字符编码
一、概述
- 字符编码表是由计算机二进制数
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>();elsestuSet = 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] 210
Student[zhaoliu,60,60,60] 180
Student[wangwu,50,50,50] 150
Student[lisi,40,40,40] 120
Student[zhangsan,30,30,30] 90
Java基础—IO流(三)相关推荐
- Java基础-IO流对象之数据流(DataOutputStream与DataInputStream)
Java基础-IO流对象之数据流(DataOutputStream与DataInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.数据流特点 操作基本数据类型 ...
- # Java基础——IO流
Java基础--IO流 File类的使用(熟悉构造器和方法的使用) File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹) File类的声明在java.io包下 文件和文件目录路径的抽象表示 ...
- Java基础IO流(二)字节流小案例
JAVA基础IO流(一)https://www.cnblogs.com/deepSleeping/p/9693601.html ①读取指定文件内容,按照16进制输出到控制台 其中,Integer.to ...
- Java基础IO流之字符流的使用
☆引言☆ 大家好,我是痛而不言笑而不语的浅伤.IO流分为字节流和字符流,而上一篇文章我们学习了字节流(Java基础IO流之字符流的使用),这篇文章带大家一起来学习字符流吧.对文章中描述错误的希望大家积 ...
- Java基础—IO流
第一讲 IO概述 1. 流的概念 IO流即InputOutput的缩写,在Java中IO流用来处理设备之间的数据传输,Java对数据的操作是通过IO流的方式, 我们可以把IO流抽象的当作一根管道, ...
- 黑马程序员-JAVA基础-IO流之字符流和字符流缓冲区
------- android培训.java培训.期待与您交流!------- Java 的IO 流 是实现输入和输出的基础,Java 中把不同的输入.输出源抽象表述为"流" (S ...
- java基础 io流 字节流 字符流 节点流 包装流 转换流 缓冲流 对象流 打印流 Properties类
目录 1.概念 2.常用的文件操作 2.1 创建文件 2.2 获取文件相关信息 2.3 目录的操作和文件删除 3. IO流原理及流的分类 3.1 流的分类 4.InputStream 字节输入流 4. ...
- JAVA基础 IO流技术学习笔记
目录 一.IO 流技术介绍 1.1 什么是IO? 1.2 流的概念 1.3 数据源 1.3.1 什么是数据源? 1.3.2数据源的分类 二.第一个简单的IO流程序 三.IO流经典写法(适用于任何 ...
- java基础--IO流之File类
一.File类概述 用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数 二.File类常见方法: 1,创建 boolean createN ...
最新文章
- python在word中的应用_不能在worddoc中使用docx python应用表样式
- [架构设计]反向(或者后向)插件系统设计
- Android官方技术文档翻译——Gradle 插件用户指南(7)
- 2018南京网络赛L题 Magical Girl Haze(分层图+优先队列优化的dijkstra)
- R语言观察日志(part8)-RMarkdown之其他语言
- CF1253E Antenna Coverage
- 【PHP入门到精通】:Ch05:字符串处理
- Linux Makefile
- 数据库---事务(二)
- 代码优化四部曲:“拆套”、“解耦”、”封装“、“重构”
- 第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛——I题 买花
- win10系统电脑c盘哪些文件可以删除,c盘可以删除的文件有哪些
- Entity Framework 代码先行之约定配置
- 2017中国之旅系列之十:山西张壁古堡之旅
- Nginx服务器部署 mycat数据库中间件
- 电脑需要装杀毒软件吗?两个理由看完你做决定
- ubuntu 管理员权限 文件操作
- 新颖的_基于web的毕业设计题目50例
- Shopee代贴单对商家有什么好处?星卓越货代系统告诉你
- 抖音表情制作方法 动态GIF怎么玩
热门文章
- farpoint 小数保留4位_Farpoint表格编辑的功能用法总结
- 免费网站功能图标_您应该收藏20个免费的多功能图标集
- 安装zabbix4.4
- Flink - CEP 实时分用户析攻击行为
- 如何应对网络恶意的攻击行为?
- cool-admin框架后端使用-node版本,使用事务装饰器来创建和事务回滚
- 尚高拟收购医疗服务企业翔鹏佑康控股权;罗氏、武田、渤健新药纳入新版医保目录 | 医药健闻...
- mysql-front安装
- 淘宝运营 淘宝试用中心 对店铺权重 商品权重的影响
- 为什么大学生沦为了 天之饺子