[关闭]
@liayun 2016-06-20T15:25:43.000000Z 字数 23093 阅读 2153

Java IO(二)

java基础


File类

File类的简要概述

File类中的常用方法

递归

例,列出指定目录下文件或者文件夹,包含子目录中的内容。也就是列出指定目录下的所有内容。
解:
分析:因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。也就是函数自身调用自身。这种表现形式或者编程手法,称为递归
定义一个列出目录功能的递归函数:

  1. public static void showDir(File dir) {
  2. System.out.println(dir);
  3. File[] files = dir.listFiles();
  4. for(int x = 0; x < files.length; x++) {
  5. if(files[x].isDirectory())
  6. showDir(files[x]);
  7. else
  8. System.out.println(files[x]);
  9. }
  10. }

调用代码:

  1. public static void main(String[] args) {
  2. File dir = new File("C:\\myclass");
  3. showDir(dir);
  4. }

若要分层级列出指定目录下的所有内容,可这样做:

  1. public static String getLevel(int level) {
  2. StringBuilder sb = new StringBuilder();
  3. sb.append("|--");
  4. for (int x = 0; x < level; x++) {
  5. // sb.append("|--");
  6. sb.insert(0, "| ");
  7. }
  8. return sb.toString();
  9. }
  10. public static void showDir(File dir, int level) {
  11. System.out.println(getLevel(level)+dir.getName());
  12. level++;
  13. File[] files = dir.listFiles();
  14. for(int x = 0; x < files.length; x++) {
  15. if(files[x].isDirectory())
  16. showDir(files[x], level);
  17. else
  18. System.out.println(getLevel(level)+files[x]);
  19. }
  20. }

调用代码:

  1. public static void main(String[] args) {
  2. File dir = new File("C:\\myclass");
  3. showDir(dir, 0);
  4. }

递归要注意:

  1. 限定条件。
  2. 要注意递归的次数,尽量避免内存溢出(java.lang.StackOverflowError)

接下来要对递归有更深入的理解,举例说之:
例1. 十进制数转二进制数,代码如下:

  1. public static void toBin(int num) {
  2. if(num > 0) {
  3. toBin(num / 2);
  4. System.out.print(num % 2);
  5. }
  6. }

图解如下:
递归1
例2. 求和,代码如下:

  1. public static int getSum(int n) {
  2. if(n == 1)
  3. return 1;
  4. return n + getSum(n - 1);
  5. }

图解如下:
递归2
思考:删除一个目录的过程是如何进行的?
解:
删除原理:在windows中,删除目录从里面往外删除的。既然是从里往外删除,就需要用到递归。

  1. import java.io.*;
  2. class RemoveDir {
  3. public static void main(String[] args) {
  4. File dir = new File("d:\\java");
  5. removeDir(dir);
  6. }
  7. public static void removeDir(File dir) {
  8. File[] files = dir.listFiles();
  9. for (int x = 0; x < files.length; x++) {
  10. if(!files[x].isHidden() && files[x].isDirectory()) // 避开隐藏文件
  11. removeDir(files[x]);
  12. else
  13. System.out.println(files[x].toString()+":-file-:"+files[x].delete());
  14. }
  15. System.out.println(dir+"::dir::"+dir.delete());
  16. }
  17. }

注意:java删除时是不走回车站的。

  1. 隐藏目录——无法访问就不能删除,返回的files为空,会导致空指针异常。
  2. 系统中的有些文件虽然看上去是一个文件,其实是一个目录,或反之。

练习一:将一个指定目录下的java文件的绝对路径,存到一个文本文件中,建立一个java文件列表清单。
解:
思路:

  1. 对指定的目录进行递归。
  2. 获取递归过程中所有的java文件的路径。
  3. 将这些路径存储到集合中。
  4. 将集合的数据写入到一个文件中。

①②③步骤:对指定的目录进行递归,获取递归过程中所有的java文件的路径,将这些路径存储到集合中。

  1. public static void fileToList(File dir, List<File> list) {
  2. File[] files = dir.listFiles();
  3. for(File file : files) {
  4. if(file.isDirectory())
  5. fileToList(file, list);
  6. else {
  7. if(file.getName().endsWith(".java"))
  8. list.add(file);
  9. }
  10. }
  11. }

④步骤:将集合的数据写入到一个文件中。

  1. public static void writeToFile(List<File> list, String javaListFile) throws IOException {
  2. BufferedWriter bufw = null;
  3. try {
  4. bufw = new BufferedWriter(new FileWriter(javaListFile));
  5. for (File f : list) {
  6. String path = f.getAbsolutePath();
  7. bufw.write(path);
  8. bufw.newLine();
  9. bufw.flush();
  10. }
  11. } catch(IOException e) {
  12. throw e;
  13. } finally {
  14. try {
  15. if(bufw != null)
  16. bufw.close();
  17. } catch(IOException e) {
  18. throw e;
  19. }
  20. }
  21. }

测试代码:

  1. public static void main(String[] args) throws IOException {
  2. File dir = new File("E:\\MyJava\\Java_Basic");
  3. List<File> list = new ArrayList<File>();
  4. fileToList(dir, list);
  5. File file = new File(dir, "javalist.txt");
  6. writeToFile(list, file.toString());
  7. }

Properties

Properties类简要概述

Properties是HashTable的子类,也就是说它具备map集合的特点,而且他里面存储的键值对都是字符串,没有泛型定义。它是集合和IO技术相结合的集合容器。该对象的特点:可以用于键值对形式的配置文件。那么在加载数据时,需要数据有固定格式,通常是键=值。

Properties类常用方法

练习二:用于记录应用程序运行次数,如果使用次数已到,那么给出注册提示。
解:
分析:很容易想到的是计数器,可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增,可是随着该应用程序的退出,该计数器也在内存中消失了,下一次再启动该程序,又重新开始从0计数,这样不是我们想要的。
程序即使结束,该计数器的值也要存在。下次程序启动会先加载该计数器的值,并+1后重新存储起来,所以要建立一个配置文件,用于记录该软件的使用次数,该配置文件使用键值对的形式,这样便于阅读数据,并操作数据。键值对数据是map集合,数据是以文件形式存储,使用io技术,那么map+io ---> properties。配置文件可以实现应用程序数据的共享。

  1. import java.io.*;
  2. import java.util.*;
  3. class RunCount {
  4. public static void main(String[] args) throws IOException {
  5. Properties prop = new Properties();
  6. File file = new File("count.ini");
  7. if(!file.exists())
  8. file.createNewFile();
  9. FileInputStream fis = new FileInputStream(file);
  10. prop.load(fis);
  11. int count = 0;
  12. String value = prop.getProperty("time");
  13. if(value != null) {
  14. count = Integer.parseInt(value);
  15. if(count >= 5) {
  16. System.out.println("您好,使用次数已到,拿钱!");
  17. return;
  18. }
  19. }
  20. count++;
  21. prop.setProperty("time", count+"");
  22. FileOutputStream fos = new FileOutputStream(file);
  23. prop.store(fos, "");
  24. fos.close();
  25. fis.close();
  26. }
  27. }

当然配置文件还有另外一种格式保存,那就是XML,格式如下:

  1. <persons>
  2. <person id="001">
  3. <name>zhangsan</name>
  4. <age>30</age>
  5. <address>bj</address>
  6. </person>
  7. <person id="002">
  8. <name>zhangsan1</name>
  9. <age>31</age>
  10. <address>bj</address>
  11. </person>
  12. </persons>

一个用来解析XML格式的文件的工具:dom4j,即dom for(four的谐音) java

打印流

该流提供了打印方法,可以将各种数据类型的数据都原样打印。

字节打印流——PrintStream

构造函数可以接收的参数类型:

字符打印流——PrintWriter

构造函数可以接收的参数类型:

合并流

SequenceInputStream表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
SequenceInputStream有两个构造函数:

  1. SequenceInputStream(Enumeration<? extends InputStream> e),通过记住参数来初始化新创建的SequenceInputStream,该参数必须是生成运行时类型为InputStream对象的Enumeration型参数。
  2. SequenceInputStream(InputStream s1, InputStream s2),通过记住这两个参数来初始化新创建的SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取s2),以提供从此SequenceInputStream读取的字节。

例,合并文件,将3个文件中的数据合并到1个文件中。
解:

  1. import java.io.*;
  2. import java.util.*;
  3. class SequenceDemo {
  4. public static void main(String[] args) throws IOException {
  5. Vector<FileInputStream> v = new Vector<FileInputStream>();
  6. v.add(new FileInputStream("c:\\1.txt"));
  7. v.add(new FileInputStream("c:\\2.txt"));
  8. v.add(new FileInputStream("c:\\3.txt"));
  9. Enumeration<FileInputStream> en = v.elements();
  10. SequenceInputStream sis = new SequenceInputStream(en);
  11. FileOutputStream fos = new FileOutputStream("c:\\4.txt");
  12. byte[] buf = new byte[1024];
  13. int len = 0;
  14. while((len=sis.read(buf)) != -1) {
  15. fos.write(buf, 0, len);
  16. }
  17. fos.close();
  18. sis.close();
  19. }
  20. }

有合并文件,那么就有切割文件
例,切割一张图片,再合成为一张完整的图片。
解:
先切割文件(一张图片):

  1. public static void splitFile() throws IOException {
  2. FileInputStream fis = new FileInputStream("c:\\DSC_0199.JPG");
  3. FileOutputStream fos = null;
  4. byte[] buf = new byte[1024*1024*2]; // 按照每2MB数据大小来切割图片
  5. int len = 0;
  6. int count = 1;
  7. while((len=fis.read(buf)) != -1) {
  8. fos = new FileOutputStream("c:\\splitFiles\\"+(count++)+".part");
  9. fos.write(buf, 0, len);
  10. fos.close();
  11. }
  12. fis.close();
  13. }

再合并图片:

  1. public static void merge() throws IOException {
  2. // 使用Vector的效率很低下,所以使用List集合
  3. ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
  4. for (int x = 1; x <= 3; x++) {
  5. al.add(new FileInputStream("c:\\splitFiles\\"+x+".part"));
  6. }
  7. // 匿名内部类访问局部变量,所以要用final修饰
  8. final Iterator<FileInputStream> it = al.iterator();
  9. /*
  10. 枚举,匿名内部类
  11. 虽然见过Enumeration,但是从来没这么写过,这是用ArrayList来实现一个Enumeration。
  12. 即把数据存到ArrayList集合里面去(Vector效率低),使用Enumeration取出。
  13. */
  14. Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {
  15. public boolean hasMoreElements() {
  16. return it.hasNext();
  17. }
  18. public FileInputStream nextElement() {
  19. return it.next();
  20. }
  21. };
  22. SequenceInputStream sis = new SequenceInputStream(en);
  23. FileOutputStream fos = new FileOutputStream("c:\\splitFiles\\0.jpg");
  24. byte[] buf = new byte[1024];
  25. int len = 0;
  26. while((len=sis.read(buf)) != -1) {
  27. fos.write(buf, 0, len);
  28. }
  29. fos.close();
  30. sis.close();
  31. }

对象序列化

管道流

管道流的主要作用是可以进行两个线程间的通信。反正管道流结合的是多线程技术,可以查看JDK帮助文档。

RandomAccessFile

DataOutputStream与DataInputStream

可以用于操作基本数据类型的数据的流对象。

内存操作流

用于操作字节数组的流对象。

在流操作规律讲解时:

  1. import java.io.*;
  2. class ByteArrayStream {
  3. public static void main(String[] args) {
  4. // 数据源
  5. ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());
  6. // 数据目的
  7. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  8. int by = 0;
  9. while((by=bis.read()) != -1) {
  10. bos.write(by);
  11. }
  12. System.out.println(bos.size());
  13. System.out.println(bos.toString());
  14. // bos.writeTo(new FileOutputStream("a.txt")); // 也可将数据源写入到一个文件中
  15. }
  16. }

结论:用流的读写思想来操作数组。

字符编码

字符流的出现是为了方便操作字符,更重要是的加入了编码转换,通过两个转换流来完成,InputStreamReaderOutputStreamWriter,在两个对象进行构造的时候可以加入字符集。

转换流的编码应用

可以将字符以指定编码格式存储,可以对文本数据指定编码格式来解读,指定编码表的动作由构造函数完成。
由UTF-8编码,自然要通过UTF-8来解码;同理,由GBK来编码,自然也要通过GBK来解码,否则会出现乱码。

编码表

编码表的由来

计算机只能识别二进制数据,早期由来是电信号来表示,为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。

常见的编码表

编码与解码

综合练习

有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括姓名,三门可成绩),输入的格式:如:zhangsan,30,40,60,计算出总成绩,并把学生的信息和计算出的总分数按高低顺序存放在磁盘文件"stud.txt"中。
解:
分析:

  1. 描述学生对象。
  2. 定义一个可以操作学生对象的工具类。

思路:

  1. 通过获取键盘录入的一行数据,并将该行中的信息取出封装成学生对象。
  2. 因为学生对象有很多,那么就需要存储,使用到集合。因为要对学生的总分进行排序,所以可以使用TreeSet。
  3. 将集合的信息写入到一个文件中。

描述一个学生类,因为学生对象要存储到TreeSet集合中,所以要实现Comparable接口,学生对象也有可能存储到HashSet集合中,所以最好覆写hashCodeequals方法。

  1. class Student implements Comparable<Student> {
  2. private String name;
  3. private int ma, cn, en;
  4. private int sum;
  5. Student(String name, int ma, int cn, int en) {
  6. this.name = name;
  7. this.ma = ma;
  8. this.cn = cn;
  9. this.en = en;
  10. sum = ma + cn + en;
  11. }
  12. public int compareTo(Student s) {
  13. int num = new Integer(this.sum).compareTo(new Integer(s.sum));
  14. if(num == 0)
  15. return this.name.compareTo(s.name);
  16. return num;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public int getSum() {
  22. return sum;
  23. }
  24. public int hashCode() {
  25. return name.hashCode() + sum * 78;
  26. }
  27. public boolean equals(Object obj) {
  28. if(!(obj instanceof Student))
  29. throw new ClassCastException("类型不匹配");
  30. Student s = (Student)obj;
  31. return this.name.equals(s.name) && this.sum == s.sum;
  32. }
  33. public String toString() {
  34. return "student["+name+", "+ma+", "+cn+", "+en+"]";
  35. }
  36. }

定义一个可以操作学生对象的工具类。一个函数功能是通过获取键盘录入的一行数据,并将该行中的信息取出封装成学生对象,存储进集合。一个函数功能是将集合的信息写入到一个文件中。

  1. class StudentInfoTool {
  2. // 使用默认排序
  3. public static Set<Student> getStudents() throws IOException {
  4. return getStudents(null);
  5. }
  6. // 使用传入的比较器排序
  7. public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException {
  8. BufferedReader bufr =
  9. new BufferedReader(new InputStreamReader(System.in));
  10. String line = null;
  11. TreeSet<Student> stus = null;
  12. if(cmp == null)
  13. stus = new TreeSet<Student>(); // 使用默认排序
  14. else
  15. stus = new TreeSet<Student>(cmp); // 使用传入的比较器排序
  16. while((line=bufr.readLine()) != null) {
  17. if("over".equals(line))
  18. break;
  19. String[] info = line.split(",");
  20. Student stu = new Student(info[0], Integer.parseInt(info[1]),
  21. Integer.parseInt(info[2]),
  22. Integer.parseInt(info[3]));
  23. stus.add(stu);
  24. }
  25. bufr.close();
  26. return stus;
  27. }
  28. public static void write2File(Set<Student> stus) throws IOException {
  29. BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
  30. for(Student stu : stus) {
  31. bufw.write(stu.toString()+"\t");
  32. bufw.write(stu.getSum()+""); // 注意有可能写出的是一个整数的最后8位
  33. bufw.newLine();
  34. bufw.flush();
  35. }
  36. bufw.close();
  37. }
  38. }

测试代码:

  1. class StudentInfoTest {
  2. public static void main(String[] args) throws IOException {
  3. Comparator<Student> cmp = Collections.reverseOrder();
  4. Set<Student> stus = StudentInfoTool.getStudents(cmp);
  5. StudentInfoTool.write2File(stus);
  6. }
  7. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注