IO流(三) File类

一、概述

  1. File类用于将文件或文件夹封装成对象,方便对文件和文件夹的属性信息进行操作。该类可以作为参数传递给IO流的构造函数,弥补流对象在操作文件和文件夹上的缺陷。

二、File类的使用

  1. 1.构造方法

    1. 1)File(String FileName)

      1. 示例: File f1 = new File("C:\\abc\\a.txt");
    1. 2)File(Strng, parent, String FileName)

      1. 示例: File f2 = new File("C:\\abc", "b.txt");
        该构造方法的好处在于对文件的操作更加灵活,出现文件目录固定,文件名需要改变的时候,这个构造方法更好
    1. 3)File(File parent, String FileName)

      1. 示例: File d = new File("C:\\abc");

        1. 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.txt
f2:C:\Users\jeremy\Documents\javaTmp\Demo2.txt
f3:C:\Users\jeremy\Documents\javaTmp\Demo3.txt

打印封装文件对象时的绝对路径或相对路径。

  1. 2.成员方法

    1. 1)创建操作

      1. -boolean createNewFile():文件名不存在的情况下创建新文件
        -boolean mkdir():创建一级目录
        -boolean mkdirs():创建多级目录
    1. 2)删除操作

      1. -boolean delete():删除指定文件
        -void deleteOnExit():虚拟机退出的时候删除指定文件
    1. 3)判断操作

      1. -boolean canExecute():判断文件对象是否可执行
        -boolean canRead():判断文件对象是否可读
        -boolean canWrite():判断文件对象是否可写
        -boolean exists():判断文件对象是否存在
        -boolean isDirectory():判断文件对象是否是文件夹,判断之前,必须先判断该文件对象是否存在
        -boolean isFile():判断文件对象是否是文件,判断之前,必须先判断该文件对象是否存在
        -boolean isHidden():判断文件对象是否是隐藏文件
        -boolean isAbsolute():判断文件对象路径是否是绝对路径
        -int compareTo(File pathname):比较两个文件对象,以自然顺序排序
    1. 4)获取操作

      1. -String getName():获取文件对象名
        -String getParent():获取文件对象的父母路,必须明确指定文件路径
        -String getPath():获取文件对象相对路径
        -String getAbsolutePath():获取文件对象绝对路径,文件可以存在也可以不存在
        -long lastModified():获取文件对象最近一次被修改的时间
        -long length():获取文件对象大小
    1. 5)修改操作

      1. -boolean renameTo(File dest):将文件修改为指定文件名并存入指定目录
    1. 6)其他重要方法

      1. -static File[] listRoots():获取有效盘符
        -String[] list():获取指定目录中的文件和文件夹,包括隐藏文件;必须是存在目录调用,文件调用会空指针
        -String[] list(FilenameFileter filter):获取指定格式的文件
        -File[] listFiles()
        -File[] listFiles(FileFilter filter)
        -File[] listFiles(FilenameFilter filter)
  1. 3.递归

    1. 用递归遍历文件夹里的所有内容并以层级结构打印。

示例代码:

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

注意:
递归的使用必须要明确控制条件,便面无限循环;递归要慎重使用,调用次数过多,会造成内存溢出。

  1. 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())
忽略隐藏的文件夹

  1. 5.练习

    1. 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. 1.概述

    1. Properties类是 HashTable的子类。该类具备 Map集合的特点,储存字符串作为键值对。 Properties类是集合中和IO技术相结合的容器。该类可以用于键值对形式的配置文件。配置文件用于保存软件运行时的各项参数。配置完成之后将会被持久化存储,每次运行软件都会加载该配置文件。
  1. 2.应用

    1. 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
    1. 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加载的配置文件必须有固定格式;通常为:键=值

    1. 3)记录程序运行次数

      1. 创建配置文件记录程序运行次数,到达 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. 1.PrintStream类

    1. 1)概述
      PrintStream类是字节打印流,其为其他流添加了功能,使它们能够方便打印各种数据值形式。该类不会抛出 IOException,同时内部有刷新机制,无须手动刷新缓冲区。 PrintStream可以直接操作文件对象。
    1. 2)常用方法

      1. -PrintStream(File file):接收File对象
        -PrintStream(OutputStream out):接收字节输出流
        -PrintStream(String filename):接收字符串路径
        -println():打印所有基本数据类型,保持数据原样
    1. 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类基本相同。

  1. 2.PrintWriter类

    1. 1)概述
      字符打印流可以打印基本舒蕾型。其最强大的功能是 println方法,可以实现自动换行并直接操作基本数据类型。
    1. 2)常用方法

      1. -PrintWriter(File file):接收File对象
        -PrintWriter(File file, String csn):接收File对象,并制定字符集
        -PrintWriter(OutputStream out):接收字节输出流
        -PrintWriter(OutputStream out, boolean autoflush):接收字节输出流,设置自动刷新
        -PrintStream(Writer out):接收字符输出流
        -PrintWriter(String filename):接收字符串路径
    1. 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. 1.SequenceInputStream类

    1. 1)概述
      SequenceInputStream类标识其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到达到文件末尾;接着从第二个输入流读取,以此类推,直到到达包含的最后一个输入流的文件末尾为止。
    1. 2)应用

      1. 将三个文件的内容使用 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();}}}
}
    1. 3)切割文件后再合并

      1. 将一个 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. 1.ObjectOutputStream和ObjectInputStream类

    1. 1)概述
      ObjectOutputStream类可以将Java对象的基本数据类型和图形写入 OutputStream,再利用 ObjectInputStream读取(重构)该对象。该类用于将对象持久化存储在硬盘上,以便程序下次启动的时候能再次加载该对象。
    1. 2)常用方法

      1. 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接口

    1. 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. 1.概述

    1. 管道流能通过结合线程技术,实现输入流和输出流的直接连接。管道输入和输出流必须连接在一起使用。某个线程从 PipedInputStream读取数据,并由另一个线程写入到 PipedOutputStream。这是涉及多线程技术的 IO流
  1. 2.PipedInputStream和PipedOutputStream类

    1. 管道流可以通过构造方法连接,也可以通过调用 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. 1.概述

    1. RandomAccessFile类不是IO体系中的子类,而是直接继承自 Object。但是它仍然是 IO包中的成员,因为它具备读写功能。该类支持对随机访问文件的读取和写入。该类的内部封装了数组,通过文件指针对数据进行操作,可以通过 getFilePointer()方法获取指针位置;通过 seek()方法改变指针的位置。 RandomAccessFile类只能操作文件,并必须设定操作模式。
  1. 2.应用

    1. 演示 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. 1.DataInputStream和DataOutputStream类

    1. 用于操作基本数据类型的流对象。

示例代码:

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
你好
  1. 2.ByteArrayInputStream和ByteArrayOutputStream类

    1. 用于操作字节数组的流对象。该类的对象无须关闭,因为没有调用任何系统底层资源;如果关闭,仍可以调用其方法,同时没有任何 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

这些类的使用方式和字节数组流相似。


字符编码

一、概述

  1. 字符编码表是由计算机二进制数 1010的排列组合和文字的对应关系形成的。它的出现可以让计算机识别各个国家不同的文字和符号。

二、常见字符编码表

  1. 1.ASCII码表

    1. 美国标准信息交换码,用一个字节的 7位标识一个字符,最高位为 0
  1. 2.ISO8859-1

    1. 拉丁码表(欧洲码表),用一个字节的 8位标识一个字符。
  1. 3.GB2312和GBK

    1. GB2312GBK都是中文字符编码表。后者是前者的升级版,囊括更多的中文文字和符号。
  1. 4.Unicode

    1. 国际标准码,融合了全世界多种文字和符号。
  1. 5.UTF-8

    1. Unicode编码表的优化版,最多用 3个字节标识一个字符。

三、乱码问题

  1. 乱码问题的产生,原因在于解码的时候没有使用编码时的编码表。比如,在写入中文数据的时候,指定了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. 1.定义

    1. 编码就是将字符串转换为字节数组的过程;解码就是将字节数组转换为字符串的过程。
  1. 2.解决乱码问题

    1. 实际开发过程中, 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);}
}

程序运行结果:

????
你好

五、联通的问题

  1. 在文本文件中输入“联通”二字,保存退出;第二次打开时会出现乱码。

图中可见“联通”二字的二进制储存形式,符合UTF-8两个字节存储的格式要求;最高位分别为11010。所以当“联通”二字以GBK编码表形式编码存储后,记事本在读取的时候误认为是UTF-8编码表编码,会到UTF-8码表中查找字符,形成乱码。

六、练习

  1. 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流(三)相关推荐

  1. Java基础-IO流对象之数据流(DataOutputStream与DataInputStream)

    Java基础-IO流对象之数据流(DataOutputStream与DataInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.数据流特点 操作基本数据类型 ...

  2. # Java基础——IO流

    Java基础--IO流 File类的使用(熟悉构造器和方法的使用) File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹) File类的声明在java.io包下 文件和文件目录路径的抽象表示 ...

  3. Java基础IO流(二)字节流小案例

    JAVA基础IO流(一)https://www.cnblogs.com/deepSleeping/p/9693601.html ①读取指定文件内容,按照16进制输出到控制台 其中,Integer.to ...

  4. Java基础IO流之字符流的使用

    ☆引言☆ 大家好,我是痛而不言笑而不语的浅伤.IO流分为字节流和字符流,而上一篇文章我们学习了字节流(Java基础IO流之字符流的使用),这篇文章带大家一起来学习字符流吧.对文章中描述错误的希望大家积 ...

  5. Java基础—IO流

    第一讲   IO概述 1. 流的概念 IO流即InputOutput的缩写,在Java中IO流用来处理设备之间的数据传输,Java对数据的操作是通过IO流的方式, 我们可以把IO流抽象的当作一根管道, ...

  6. 黑马程序员-JAVA基础-IO流之字符流和字符流缓冲区

    ------- android培训.java培训.期待与您交流!------- Java 的IO 流 是实现输入和输出的基础,Java 中把不同的输入.输出源抽象表述为"流" (S ...

  7. java基础 io流 字节流 字符流 节点流 包装流 转换流 缓冲流 对象流 打印流 Properties类

    目录 1.概念 2.常用的文件操作 2.1 创建文件 2.2 获取文件相关信息 2.3 目录的操作和文件删除 3. IO流原理及流的分类 3.1 流的分类 4.InputStream 字节输入流 4. ...

  8. JAVA基础 IO流技术学习笔记

    目录 一.IO 流技术介绍 1.1  什么是IO? 1.2  流的概念 1.3  数据源 1.3.1 什么是数据源? 1.3.2数据源的分类 二.第一个简单的IO流程序 三.IO流经典写法(适用于任何 ...

  9. java基础--IO流之File类

    一.File类概述 用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数 二.File类常见方法: 1,创建 boolean createN ...

最新文章

  1. python在word中的应用_不能在worddoc中使用docx python应用表样式
  2. [架构设计]反向(或者后向)插件系统设计
  3. Android官方技术文档翻译——Gradle 插件用户指南(7)
  4. 2018南京网络赛L题 Magical Girl Haze(分层图+优先队列优化的dijkstra)
  5. R语言观察日志(part8)-RMarkdown之其他语言
  6. CF1253E Antenna Coverage
  7. 【PHP入门到精通】:Ch05:字符串处理
  8. Linux Makefile
  9. 数据库---事务(二)
  10. 代码优化四部曲:“拆套”、“解耦”、”封装“、“重构”
  11. 第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛——I题 买花
  12. win10系统电脑c盘哪些文件可以删除,c盘可以删除的文件有哪些
  13. Entity Framework 代码先行之约定配置
  14. 2017中国之旅系列之十:山西张壁古堡之旅
  15. Nginx服务器部署 mycat数据库中间件
  16. 电脑需要装杀毒软件吗?两个理由看完你做决定
  17. ubuntu  管理员权限 文件操作
  18. 新颖的_基于web的毕业设计题目50例
  19. Shopee代贴单对商家有什么好处?星卓越货代系统告诉你
  20. 抖音表情制作方法 动态GIF怎么玩

热门文章

  1. farpoint 小数保留4位_Farpoint表格编辑的功能用法总结
  2. 免费网站功能图标_您应该收藏20个免费的多功能图标集
  3. 安装zabbix4.4
  4. Flink - CEP 实时分用户析攻击行为
  5. 如何应对网络恶意的攻击行为?
  6. cool-admin框架后端使用-node版本,使用事务装饰器来创建和事务回滚
  7. 尚高拟收购医疗服务企业翔鹏佑康控股权;罗氏、武田、渤健新药纳入新版医保目录 | 医药健闻...
  8. mysql-front安装
  9. 淘宝运营 淘宝试用中心 对店铺权重 商品权重的影响
  10. 为什么大学生沦为了 天之饺子