跳到主要内容

认识“流”

流是内存和存储设备之间传输数据的通道。

流的分类

  • 按方向:
    • 输入流:将存储设备中的内容读入到内存中;
    • 输出流:将内存中的内容写入到存储设备中。
  • 按单位:
    • 字节流:以字节为单位,可以读写所有数据;
    • 字符流:以字符为单位,只能读写文本数据。
  • 按功能:
    • 节点流:具有实际传输数据的读写功能;
    • 过滤流:在节点流的基础上增强功能。

字节流

字节流的父类(抽象类):

  • InputStream:字节输入流
    • 主要方法:read()及其重载方法
  • OutputStream:字节输出流
    • 主要方法:write()及其重载方法

文件字节流

文件字节流(实现类):

  • FileInputStream:
    • public int read(byte[] b),从流中读取多个字节,将读到内容存入数组b中,返回实际读到的字节数;如果达到文件的尾部,则返回-1。
  • FileOutputStream:
    • public void write(byte[] b),一次写多个字节,将数组b中所有字节,写入输出流。

文件输入流基本用法:

// 1. 创建文件输入流
FileInputStream fileInputStream = new FileInputStream("/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/111.txt");
// 2. 读取文件
// 创建缓冲区
byte[] buf = new byte[3];
// 将字节读到缓冲区
int count = 0;
while ((count = fileInputStream.read(buf)) != -1) {
System.out.println(new String(buf, 0, count));
}
// 3. 关闭流
fileInputStream.close();

文件输出流基本用法。

// 1. 创建FileOutputStream对象
FileOutputStream fileOutputStream = new FileOutputStream("/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/111.txt");
// 2. 写入文件
String s = "Hello World";
fileOutputStream.write(s.getBytes());
// 3. 关闭流
fileOutputStream.close();

小栗子:通过文件输入输出流复制一张图片。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 创建文件输入流
FileInputStream fis = new FileInputStream(basePath + "001.jpg");
// 创建文件输出流
FileOutputStream fos = new FileOutputStream(basePath + "002.jpg");

// 创建缓冲区
byte[] buf = new byte[1024];

// 边读边写
int count = 0;
while ((count = fis.read(buf)) != -1) {
fos.write(buf, 0, count);
}

// 关闭流
fis.close();
fos.close();

字节缓冲流

缓存流(实现类):

  • BufferedInputStream
  • BufferedOutputStream

作用:

  • 提高I/O效率,减少访问磁盘次数;
  • 数据存储在缓冲区中,flush是将缓冲区的内容写入文件中,也可以直接close。

缓冲输入流基本用法。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 1. 创建BufferedInputStream对象,传入一个节点流(BufferedInputStream继承自过滤流,过滤流的作用就是用来增强节点流的)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(basePath + "111.txt"));

// 2. 读取文件
int data = 0;
while ((data = bis.read()) != -1) {
System.out.println((char) data);
}

// 3. 关闭流(内部调用节点流的close)
bis.close();

缓冲流继承自过滤流,过滤流是用来增强节点流的。

缓冲流在内部实现了一个缓冲区,当我们调用缓冲流的read()方法时,其内部实际不是从磁盘一个一个字节读取,而是读取多个字节到缓冲区(默认缓冲区大小是8K),然后我们再从缓冲区一个一个字节读取。这样做大大降低了对磁盘的访问次数,从而提高I/O效率。

缓冲输出流基本用法。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 1. 创建BufferedOutputStream对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(basePath + "111.txt"));

// 2. 写入文件
String s = "HelloWorld";

bos.write(s.getBytes()); // 写入到缓冲区
bos.flush(); // 刷新到硬盘

// 3. 关闭流(会调用其flush方法)
bos.close();

对象流

对象流:

  • ObjectInputStream
  • ObjectOutputStream

作用:

  • 增强了缓冲区功能;
  • 增强了读写8种基本数据类型和字符串功能;
  • 增强了读写对象的功能:
    • readObject(),从流中读取一个对象(反序列化)
    • writeObject(Object obj),向流中写入一个对象(序列化)

使用流传输对象的过程称之为序列化、反序列化。

对象流基本用法。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 1. 创建ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(basePath + "111.txt"));

Student student = new Student("001", "张三", 20);
// 2. 序列化
oos.writeObject(student);

// 3. 关闭流
oos.close();

// 4. 创建ObjectInputStream对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(basePath + "111.txt"));

// 5. 反序列化
Student stu = (Student) ois.readObject();
System.out.println(stu); // Student{id='001', name='张三', age=20}

// 6. 关闭流
ois.close();

注意事项:

  • 要序列化的类要实现序列化接口Serializable
  • 要序列化的类中的对象属性也要实现序列化接口Serializable
  • private static final long serialVersionUID = 1L;序列化类中serialVersionUID常量是为了保证序列化和反序列化的类是同一个类;
  • 使用transient关键字修饰的属性,该属性不参与序列化;
  • 静态属性不参与序列化。

字符流

字符编码:

  • ISO-8859-1,收录除ASCII外,还包括西欧、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号;
  • UTF-8,针对Unicode码表的可变长度字符编码;
  • GB2312,简体中文
  • GBK,简体中文、扩充
  • BIG5台湾,繁体中文

字符流的父类(抽象类):

  • Reader:字符输入流
    • public int read()
    • public int read(char[] c)
    • public int read(char[] b, int off, int len)
  • Writer:字符输出流
    • public void write(int n)
    • public void write(String str)
    • public void write(char[] c)

文件字符流

  • FileReader

    public int read(char[] c) 从流中读取多个字符,将读到的内容存入c数组,返回实际读到的字符数;如果达到文件的尾部,则返回-1。

  • FileWriter

    public void write(String str) 一次写多个字符,将数组中所有字符写入到输出流。

文件字符输入流基本使用。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 1. 创建FileReader对象
FileReader fr = new FileReader(basePath + "111.txt");

// 2. 读取文件
int data = 0;
while ((data = fr.read()) != -1) {
System.out.println((char) data);
}

// 3. 关闭流
fr.close();

文件字符输出流基本使用。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 1. 创建FileWriter对象
FileWriter fw = new FileWriter(basePath + "111.txt");

// 2. 写入文件
String s = "好好学习";

fw.write(s);
fw.flush();

// 3. 关闭流
fw.close();

字符缓冲流

缓冲流:BufferedReader/BufferedWriter

  • 高效读写
  • 支持输入换行符
  • 可一次写一行、读一行

字符缓冲输入流基本使用。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 创建BufferedReader对象
BufferedReader br = new BufferedReader(new FileReader(basePath + "111.txt"));

// 读取文件
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}

// 关闭流
br.close();

字符缓冲输出流基本使用。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 创建BufferedWriter对象
BufferedWriter bw = new BufferedWriter(new FileWriter(basePath + "222.txt"));

// 写文件
for (int i = 0; i < 10; i++) {
bw.write("好好学习,天天向上");
bw.newLine();
bw.flush();
}

// 关闭流
bw.close();

打印流

PrintWriter

打印流基本用法。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 创建PrintWriter对象
PrintWriter pw = new PrintWriter(basePath + "333.txt");

// 打印
pw.println(97);
pw.println(true);
pw.println(3.14);
pw.println('a');

// 关闭流
pw.close();

转换流

桥转换流:InputStreamReader/OutputStreamWriter

  • 前者可将字节流转换为字符流,后者可将字符流转换为字节流;
  • 可设置字符的编码方式。

InputStreamReader基本使用。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 创建InputStreamReader对象
InputStreamReader isr = new InputStreamReader(new FileInputStream(basePath + "222.txt"), "UTF-8");

// 写文件
int data = 0;
while ((data = isr.read())!= -1){
System.out.println((char) data);
}

// 关闭流
isr.close();

OutputStreamWriter基本使用。

String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

// 创建OutputStreamWriter对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(basePath + "444.txt"), "UTF-8");

// 写文件
for (int i = 0; i < 10; i++) {
osw.write("好好学习,天天向上\r\n");
osw.flush();
}

// 关闭流
osw.close();

File 类

代表物理磁盘中的一个文件或者文件夹。

常用方法

方法作用
createNewFile()创建一个新文件
mkdir()创建一个新目录
delete()删除文件或目录
exists()判断File对象所代表的对象是否存在
getAbsolutePath()获取文件的绝对路径
getName()取得名字
getParent()获取文件/ 目录所在的目录
isDirectory()是否是目录
isFile()是否是文件
length()获得文件的长度
listFiles()列出目录中的所有内容
renameTo()修改文件名为

FileFilter 接口

@FunctionalInterface
public interface FileFilter {

/**
* Tests whether or not the specified abstract pathname should be
* included in a pathname list.
*
* @param pathname The abstract pathname to be tested
* @return <code>true</code> if and only if <code>pathname</code>
* should be included
*/
boolean accept(File pathname);
}

当调用File类中的listFiles()方法时,支持传入FileFilter接口实现类,对获取文件进行过滤,只有满足条件的文件才会被listFiles()方法返回。

递归遍历

递归遍历文件夹,打印所有文件路径。

package com.shiguangping.day1;

import java.io.File;

/**
* @author liyan
*/
public class StreamTest {

private static final String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

public static void main(String[] args) {

File file = new File(basePath + "doc");

listDir(file);
}

private static void listDir(File dir) {
File[] files = dir.listFiles();

if (files != null && files.length > 0) {
for (File file : files) {
if (file.isDirectory()) {
listDir(file);
} else {
System.out.println(file.getAbsolutePath());
}
}
}
}

}

递归删除目录。

package com.shiguangping.day1;

import java.io.File;

/**
* @author liyan
*/
public class StreamTest {

private static final String basePath = "/Users/liyan/Documents/workspace/JavaStudy/javase-study/src/com/shiguangping/day1/";

public static void main(String[] args) {

File file = new File(basePath + "doc");

delDir(file);
}

private static void delDir(File dir) {
File[] files = dir.listFiles();

if (files != null && files.length > 0) {
for (File file : files) {
if (file.isDirectory()) {
delDir(file);
} else {
System.out.println("当前" + file.getAbsolutePath() + "文件删除:" + file.delete());
}
}
System.out.println("当前" + dir.getAbsolutePath() + "目录删除:" + dir.delete());
}
}

}