(目录)


image-20220217101614829

1、文件API

Q:

File类可以用来做目录操作吗?

A:

可以。 File对象本身可以是目录。 调用file.mkdirs()即可创建目录。


Q:

直接调用file.delete()可以删除目录吗?

A:

如果是文件或者空目录,可以直接删除。 但如果目录中有文件或者子目录,则必须递归删除。

    private static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
       //递归删除目录中的子目录下
            for (int i=0; i<children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空,可以删除
        return dir.delete();
    }

Q:

有哪些方法判断给定路径下文件是否存在?

A:

  1. File类的exists方法: file.exist(string)
File testFile = new File(testFilePath);
if(!testFile .exists()) {...}
  1. File类的静态exist方法, File.exist(Path path)
Path filePath = Paths.get(testFilePath);
if (Files.exists(filePath) {...}

注意静态方法和非静态方法的区别



2、字节输入流InputStream

说一下以下这些特点对应哪些InputStream类

  • 字节数组char[] 作为输入源的InputStream类是————ByteArrayInputStream
  • 用文件作为输入源的InputStream类是?————FileInputStream
  • 用字符串作为输入源的是?————StringBufferInputStream
  • 用于多线程之间管道通信的输入源是————PipeInputStream

Q:

FilterInputStream是什么?

A:

用于装饰上面这些输入流的,可以叠加,每装饰一层就相当于增加了1个功能。

InputStream inputStream = new FilterInputStream(InputStream)

以下这些特点分别对应哪些FilterInputStream?

  • 装饰后,不仅可读字符串,还可读取例如int、long等java基本类型的是————DataInputStream DataInputStream里面会支持readInt、readLong等方法。
  • 装饰后,支持分批缓冲读取读取的是————BufferedInputStream 创建BufferedInputStream时,我们会通过它的构造函数指定某个输入流为参数。BufferedInputStream会将该输入流数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从输入流中读取下一部分的数据。
  • 其他: PushbackInputStream: 具有1个能回退上一个字节的缓冲区 ObjectInputStream : 一般用于反序列化读入 LineNumberInputStream: 可跟踪输入流中的行号

3、字节输出流OutputStream

OutputStream包含 ByteArrayOutputStream 输出到缓冲区 FileOutputStream 写到文件 PipedOutputStream 写入管道 FilterOutputStream

而FilterOutputStream 包含

  • DataOutputStream (可以out.writexxx各种类型的数据,writeDouble, writeUTF, reader也一样,可以读想要的数据类型)、
  • PringtStream (输出到文件用这个, 该类.println(str)即可写入文件)
  • BufferOutputString

FileOutputStream相关

Q:

new FileOutputStream(name, true) 这个构造里的true参数是做什么用的?

A:

是否支持在文件末追加的意思。

image-20220216173935644

默认是false,指的是覆盖整个文本。 如果设置成true,会在要写入的文件后面追加本次写入的内容。


Q:

BufferOutputStream相关概念(其实是考缓冲区是否需要刷新之类的问题)

  • BufferOutputStream里的flush()方法是做什么的?
  • BufferOutputStream调用close后,会触发flush()来刷新缓冲区吗?
  • BufferOutputStream调用close可能会丢数据吗?
  • BufferOutputStream多次调用close会报错吗?

A:

  • flush把缓冲区里的数据写入文件,并刷新缓冲区

image-20220216174029844

  • close关闭此输出流并释放与此相关联的任何系统资源, 会调用flush,除了flushBuffer,还会调用父类的flush。

  • 不会丢数据,因为上面这条原因。

  • 多次调用不会报错。

    image.png


4、Reader和Writer

Q:

Reader/Writer和InputStream/OutputStream的区别?

A:

  • InputStream是表示 字节输入流 的所有类的超类 Reader是用于读取 字符流 的抽象类 InputStream提供的是字节流的读取,而非文本读取,这是和Reader类的根本区别。 即用Reader读取出来的是char数组或者String ,使用InputStream读取出来的是byte数组。
  • Reader/Writer提供兼容Unicode、面向字符的IO功能,为了国际化

  • 用reader读取标准输入: BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
  • 用Writer进行标准输出: BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

设置编码:

InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8");  
BufferedReader read = new BufferedReader(isr);  

5、序列化问题

Q:

对某对象进行序列化时, 如何让里面某个敏感成员不被序列化?

A:

  • 方法一:可使用transient关键字处理那个敏感成员
  • 方法二:可以通过覆盖Serializable接口的writeObject和readObject来实现序列化, 但是方法签名必须是private void writeObject(ObjetOutputStream stream) throw IOException;
  • 方法三: 实现Externalizable接口,可自定义实现writeExternal以及readExternal方法

Q:

Externalizable和Serializable哪个快?

A:

Externalizable更快。


Q:

Externalizable需要产生序列化ID吗?

A:

采用Externalizable无需产生序列化ID(serialVersionUID)~而Serializable接口则需要


序列化集合练习

将存有多个自定义对象的集合序列化操作,保存到list.txt文件中。 反序列化list.txt ,并遍历集合,打印对象信息。

案例分析

把若干学生对象 ,保存到集合中。 把集合序列化。 反序列化读取时,只需要读取一次,转换为集合类型。 遍历集合,可以打印所有的学生信息

案例代码实现

public class SerTest {
	public static void main(String[] args) throws Exception {
		// 创建 学生对象
		Student student = new Student("老王", "laow");
		Student student2 = new Student("老张", "laoz");
		Student student3 = new Student("老李", "laol");

		ArrayList<Student> arrayList = new ArrayList<>();
		arrayList.add(student);
		arrayList.add(student2);
		arrayList.add(student3);
		// 序列化操作
		// serializ(arrayList);
		
		// 反序列化  
		ObjectInputStream ois  = new ObjectInputStream(new FileInputStream("list.txt"));
		// 读取对象,强转为ArrayList类型
		ArrayList<Student> list  = (ArrayList<Student>)ois.readObject();
		
      	for (int i = 0; i < list.size(); i++ ){
          	Student s = list.get(i);
        	System.out.println(s.getName()+"--"+ s.getPwd());
      	}
	}

	private static void serializ(ArrayList<Student> arrayList) throws Exception {
		// 创建 序列化流 
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
		// 写出对象
		oos.writeObject(arrayList);
		// 释放资源
		oos.close();
	}
}


6、打印流

1.何谓打印流

平时我们在控制台打印输出,是调用print方法和println方法完成的,各位用了这么久的输出语句肯定没想过这两个方法都来自于java.io.PrintStream类吧,哈哈。该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。


2.打印流分类:

字节打印流PrintStream,字符打印流PrintWriter


3.打印流特点:

A:只操作目的地,不操作数据源 B:可以操作任意类型的数据 C:如果启用了自动刷新,在调用println()方法的时候,能够换行并刷新 D:可以直接操作文件

这个时候有同学就要问了,哪些流可以直接操作文件呢?答案很简单,如果该流的构造方法能够同时接收File和String类型的参数,一般都是可以直接操作文件的!

PrintStream是OutputStream的子类,PrintWriter是Writer的子类,两者处于对等的位置上,所以它们的API是非常相似的。二者区别无非一个是字节打印流,一个是字符打印流。

4.字节输出打印流PrintStream复制文本文件

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;

public class PrintStreamDemo {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("copy.txt"));
        PrintStream ps=new PrintStream("printcopy.txt");
        String line;
        while((line=br.readLine())!=null) {
            ps.println(line);
        }
        br.close();
        ps.close();
    }
}

5.字符输出打印流PrintWriter复制文本文件

  •  import java.io.BufferedReader;
     import java.io.FileReader;
     import java.io.FileWriter;
     import java.io.IOException;
     import java.io.PrintWriter;
     /**
     
      * 使用打印流复制文本文件
        */
        public class PrintWriterDemo {
        public static void main(String[] args) throws IOException {
            BufferedReader br=new BufferedReader(new FileReader("aa.txt"));
            PrintWriter pw=new PrintWriter("printcopyaa.txt");
            String line;
            while((line=br.readLine())!=null) {
                pw.println(line);
            }
            br.close();
            pw.close();
        }
        }
    

7、Properties概述

java.util.Properties 继承于Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,System.getProperties 方法就是返回一个Properties对象。


Properties类

构造方法

public Properties() :创建一个空的属性列表。

基本的存储方法

  • public Object setProperty(String key, String value) : 保存一对属性。
  • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。
  • public Set<String> stringPropertyNames() :所有键的名称的集合。
public class ProDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 创建属性集对象
        Properties properties = new Properties();
        // 添加键值对元素
        properties.setProperty("filename", "a.txt");
        properties.setProperty("length", "209385038");
        properties.setProperty("location", "D:\\a.txt");
        // 打印属性集对象
        System.out.println(properties);
        // 通过键,获取属性值
        System.out.println(properties.getProperty("filename"));
        System.out.println(properties.getProperty("length"));
        System.out.println(properties.getProperty("location"));

        // 遍历属性集,获取所有键的集合
        Set<String> strings = properties.stringPropertyNames();
        // 打印键值对
        for (String key : strings ) {
          	System.out.println(key+" -- "+properties.getProperty(key));
        }
    }
}

输出结果:

{filename=a.txt, length=209385038, location=D:\a.txt}
a.txt
209385038
D:\a.txt
filename -- a.txt
length -- 209385038
location -- D:\a.txt

与流相关的方法

public void load(InputStream inStream): 从字节输入流中读取键值对。

参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。现在文本数据格式如下:

filename=Properties.txt
length=123
location=C:\Properties.txt

加载代码演示:

public class ProDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 创建属性集对象
        Properties pro = new Properties();
        // 加载文本中信息到属性集
        pro.load(new FileInputStream("Properties.txt"));
        // 遍历集合并打印
        Set<String> strings = pro.stringPropertyNames();
        for (String key : strings ) {
          	System.out.println(key+" -- "+pro.getProperty(key));
        }
     }
}

输出结果:

filename -- Properties.txt
length -- 123
location -- C:\Properties.txt

文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。