流的概念

Stream

  • 流入与流出以内存为参照

    • 内存---->存储设备:流出(output)

    • 存储设备---->内存:流入(input)

  • 指的是通道

流的分类

方向

  • 输入流(InputStream)

  • 输出流(OutputStream)

单位

  • 字节流:以字节(Byte, FF)为单位, 可读写所有数据(包括图片,音频,所有格式数据)

  • 字符流:以字符(char, FFFF)为单位, 只能读写文本数据

功能

  • 节点流:具有实际传输数据的读写功能

  • 过滤流:在节点流的基础上的增强功能

字节流

字节流抽象类

InputStream:字节输入流

OutputStream:字节输出流

文件字节流

java.io.FileInputStream 和 java.io.FileOutputStream

  • 字节流的读取单位是Byte,参照ASCII码表。(即每调用一次read只读取一个字节)

  • read返回值为其读取的数据,到达文件末尾时,返回值-1

FileInputStream的使用

  • 读取中文,sout时会乱码

  • 若自写缓冲区小于3Byte仍会乱码

  1. 创建FileInputStream对象

  2. 读取文件

  3. 关闭

// 1.创建FileInputStream,将fis与文件建立连接
FileInputStream fis = new FileInputStream("d:\\aaa.txt");

// 2.读取文件(数据)
System.out.println(fis.read()); // 读取单个Byte(字节)

// 2.1 读一个打印
int data = 0;
while ((data=fis.read())!=-1) {
    System.out.print((char)data);  // 转换为char类型
}

// 2.2 读取到数组(缓冲区)中再打印
byte[] buf = new byte[3];

int count = 0;
while ((count=fis.read(buf))!=-1) {
    System.out.println(new String(buf,0,count));  // [,)
}

// fis.read(buf);  // fis.read(buf)将fis的文件读入buf,返回值是读入缓冲区(buf)的总字节数

// new String(buf);  // 构造一个String对象,将数组传入该对象


// 3.关闭
fis.close();

FileOutputStream的使用

  1. 创建FileOutputStream对象

  2. 写入文件(数据)

  3. 关闭

new FileOutputStream会新创一个文件

// 1.创建文件字节输出流对象
FileOutputStream fos = new FileOutputStream("d:\\bbb.txt",true);  // 默认false,false时写操作会覆盖(append开关)

// 2.写入文件
fos.write(97);  // 写入时会转成 'a',使用的unicode编码
fos.write('97')
fos.write('b');
fos.write('c');

String str = "helloworld";

fos.write(str.getBytes());  // getBytes()方法,返回值是byte数组,使用平台默认编码

// 3.关闭
fos.close();

文件字节流复制文件

// 1.创建流
// 1.1文件输入流
FileInputStream fis = new FileInputStream("d:\\30.jpg");
// 1.2文件输出流
FileOutputStream fos = new FileOutputStream("d:\\copy1.jpg");

// 2. 边读边复制
byte[] buf = new byte[1024];
int count = 0;
while ((count=fis.read(buf))!=-1) {
    fos.write(buf,0,count);
}

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

字节缓冲流(过滤流)

java.io.BufferedInputStream / java.io.BufferedOutputStream

  • 需要传入文件字节流
  • 自带缓冲区(buf)

BufferedInputStream的使用

  • 使用字节缓冲流,读取中文sout时仍会乱码

  • 若自写缓冲区,则可正常sout中文

  1. 创建FileInputStream对象,再传入前者创建BufferedInputStream对象

  2. 读取

  3. 关闭

// 1. 创建
FileInputStream fis = new FileInputStream("d:\\aaa.txt");
//  传入字节流,创建BufferedInputStream,用来增强字节流
BufferedInputStream bis = new BufferedInputStream(fis);

// 2.1 读取
int data = 0;
// 直接从内置缓冲区(buf)读取,buf大小默认8kb。
while ((data=bis.read())!=-1) {
    System.out.print((char)data);
}

// 2.2 可以读取中文,2.1不行
byte[] buf = new byte[1024];
int count = 0;
while ((count=bis.read(buf))!=-1) {
    System.out.print(new String(buf,0,count));
}

// 3. 关闭(内置关闭fis的方法)
bis.close();

BufferedOutputStream的使用

  1. 创建流(类似BufferedInputStream)

  2. 写入文件

  3. 关闭

// 1.创建字节输出缓冲流
FileOutputStream fos = new FileOutputStream("d:\\buffer.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);

// 2.写入文件
for (int i=0; i<10; i++) {
    bos.write("helloWorld\n".getBytes());  // 写入8k缓冲区
    bos.flush();  // 刷新到硬盘(从内存冲刷到硬盘)
}

// 3. 关闭(内部调用flush方法)
bos.close();

对象流

java.io.ObjectOutputStream / java.io.ObjectInputStream

  • 增强了缓冲区功能

  • 增强了8种数据类型和字符串功能(write方法可传入的类型增多)

  • 增强了读写对象的功能

被序列化的Student类

  1. 序列化对象要实现Serializable接口

  2. 序列化类中属性(类)要求实现Serializable接口

  3. serialVersionUID:序列化版本号ID,保证序列化和反序列化的类是同一个类,会有个默认值

  4. 使用transient(短暂的)修饰属性,该属性不能被序列化

  5. static属性也不能被序列化

  6. 序列化多个对象可以借助集合来实现

public class Student implements Serializable {
    private static final long serialVersionUID = 100L;
    private String name;
    private transient int age;
    private static String county;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

ObjectOutputStream的使用

  1. 创建

  2. 序列化(写操作)

  3. 关闭

// 1. 创建对象流(代理)
FileOutputStream fos = new FileOutputStream("d:\\stu.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);

// 2. 序列化(写操作)
// 首先创建Object
Student s1 = new Student("zhangsan", 18);
Student s2 = new Student("lisi", 20);

// 2.1单个序列化
oos.writeObject(s1);

// 2.2批量序列化
// 将两个对象放入ArrayList当中,以实现批量序列化
ArrayList<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);

oos.writeObject(list);

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

ObjectInputStream的使用

  1. 创建对象流

  2. 读取文件

  3. 关闭

readObject方法返回的是一个readObject对象

// 1.创建一个对象流
FileInputStream fis = new FileInputStream("d:\\stu.bin");
ObjectInputStream ois = new ObjectInputStream(fis);

// 2.1 读取文件(反序列化)
Student s = (Student)ois.readObject();  // 强制转换成Student对象

// 2.2 批量反序列化
ArrayList<Student> list = (ArrayList<Student>)ois.readObject();  // 强制转换成ArrayList<>对象

// 3.关闭
ois.close();

System.out.println(s.toString());
System.out.println(list.toString());
编码方式

字符编码

编码解码方式不一致时,会出现乱码

ANSI编码,代表国家或地区当地的编码方式,中国ANSI即为GBK

  • ISO-8859-1:1字节,ASCII升级版,使用了首位
  • UTF-8:可变长度1~3字节,使用Unicode码表
  • GB2312:1~2字节,简中
  • GBK:1~2字节,国标扩
  • BIG5 台湾:繁中
字符流

字符流抽象类

Reader字符输出流

Writer字符输入流

文件字符流

FileReader的使用

  1. 创建流

  2. 读取

  3. 关闭

java.io.FileReader

1 char = 2 byte = 16 bit

字符 字节 位

read方法, 返回值是byte(字节)还是char(字符)取决于类 --> 多态

FileReader read方法返回的是字符数(char)

FileInputStream read方法返回的是字节数(byte)

// 1. 创建FileReader流 文件字符输入流
FileReader fr = new FileReader("d:\\hello.txt");

// 2. 读取
// 2.1 单个字符读取
// int data = 0;
// while ((data=fr.read())!=-1) {
//     System.out.println((Integer.toHexString(data)));  转换成16进制
//    }

// 2.2 多个字符
char[] buf = new char[2];  // 通常设置为1024
int count = 0;
while ((count=fr.read(buf))!=-1) {
    System.out.println(new String(buf, 0, count));

}

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

FileWriter的使用

java.io.FileWriter

  • read方法可传参类型:char,char[],String
  • append方法,可追加写入
// 1.创建一个FileWriter对象
FileWriter fw = new FileWriter("d:\\fileWriter.txt");

// 2. 写入
for (int i = 0; i < 10; i++) {
    fw.write("这是字符输入流" + i + "\n");
    fw.flush();
}

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

文件字符流复制文件

  • 使用FileReaderFileWriter复制文本文件, 不能复制图片或二进制文件
  • 读入内存时(即read时)会将图片二进制文件先转成字符编码
// 1. 创建写流和读流
FileReader fr = new FileReader("d:\\fileWriter.txt");
FileWriter fw = new FileWriter("d:\\fileWriter.copy.txt");

// 2. 复制

// 加缓冲区,效果一致
// char[] buf = new char[1024];
// int count = 0;
// while ((count=fr.read(buf))!=-1) {
//     fw.write(buf,0,count);
// }

int data = 0;
while ((data=fr.read())!=-1) {
    fw.write(data);
}

// 关闭
fw.close();
fr.close();

字符缓冲流(过滤流)

字符缓冲流:BufferedReader / BufferdWriter

  • 高效读写

  • 支持输入换行符

  • 可一次写一行, 读一行

BufferdReader的使用

readline方法,读取一行

// 1. 创建缓冲流
FileReader fr = new FileReader("d:\\fileWriter.txt");
BufferedReader br = new BufferedReader(fr);

// 2. 读取
// 2.1 第一种方式
// char[] buf = new char[1024];
// int count = 0;
// while ((count=br.read(buf))!=-1) {
//      System.out.print(new String(buf,0,count));
// }

// 2.2 第二种方式
String line = null;
while ((line=br.readLine())!=null) {  //读一行文字。 一行被视为由换行符('\ n'),回车符('\ r')中的任何一个或随后的换行符终止
    System.out.println(line);
}

// 3. 关闭
br.close();

BufferedWriter的使用

newLine方法:写入一个换行符,Linux \rwindows \n \r

// 1. 创建BufferedWriter对象
FileWriter fw = new FileWriter("d:\\bufWriter.txt");
BufferedWriter bw = new BufferedWriter(fw);

// 2.写入
for (int i = 0; i < 10; i++) {
    bw.write("这是缓冲字符流输入" + i);
    bw.newLine();
    bw.flush();
}

// 3. 关闭
bw.close();

打印流

java.io.PrintWriter

不会进行转换,写入97,文本内显示是97

// 1. 创建打印流
PrintWriter pw =  new PrintWriter("d:\\print.txt");

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

// 3. 关闭
pw.close();
转换流

java.io.InputStreamReader / java.io.OutputStreamWriter

  • 字节流字符流相互转换
  • 可设置字符编码方式

InputSreamReader的使用

// 1. 创建InputStreamReader对象
FileInputStream fis = new FileInputStream("d:\\fileWriter.txt");
InputStreamReader isr = new InputStreamReader(fis, "gbk");  // 解码方式

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

// 3. 关闭
isr.close();

OutputStreamWriter的使用

// 1. 创建OutputStreamWriter
FileOutputStream fos =  new FileOutputStream("d:\\oaw.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");  // 写入的编码格式

// 2. 写入
for (int i = 0; i < 10; i++) {
    osw.write("OutputStreamWriter转换流" + i + "\n");
    osw.flush();
}

// 3.关闭
osw.close();
File类

表示文件或文件夹的路径

分隔符

File.pathSeparator  // ;
File.separator  // \

文件操作

  • 创建文件 :File file = new File(name)

  • 删除文件:file.delete()

  • 获取文件信息

  • 判断

// 1. 创建文件
File file = new File("d:\\fileOpe\\file.txt");
// System.out.println(file.toString());
if (!file.exists()) {
    boolean b = file.createNewFile();
    System.out.println("创建结果:" + b);
}

// 2. 删除文件

// 2.1 直接删除
//System.out.println("删除结果:" + file.delete());

// 2.2 使用jvm退出时删除
//file.deleteOnExit();
//Thread.sleep(5000);

// 3. 获取文件信息
System.out.println("获取文件的绝对路径:" + file.getAbsolutePath());
System.out.println("获取路径:" + file.getPath());  // 创建时是什么路径就是什么路径
System.out.println("获取文件名称:" + file.getName());
System.out.println("获取父目录:" + file.getParent());
System.out.println("获取文件长度:" + file.length());
System.out.println("文件创建时间:" + new Date(file.lastModified()).toLocaleString());


// 4. 判断 erw
System.out.println("是否可写:" + file.canWrite());
System.out.println("是否是文件:" + file.isFile());
System.out.println("是否隐藏:" + file.isHidden());

文件夹操作

  • 创建文件夹 :File dir = new File(name) ; dir.mkdirs();

  • 删除文件夹:dir.delete() 删除时,只删除最底层空目录

  • 获取文件信息

  • 判断

  • 遍历: list返回的是String类listFile返回的是File类

// 1. 创建文件夹
File dir = new File("d:\\aaa\\bbb\\ccc");
// System.out.println(dir.toString());
if (!dir.exists()) {
    //dir.mkdir();  // 只能创建单级目录
    System.out.println("创建结果:" + dir.mkdirs());
}

// 2. 删除文件夹
// 2.1 直接删除
//System.out.println("删除的结果是" + dir.delete());

// 2.2 使用jvm删除
//dir.deleteOnExit();  // 删除效果同2.1
//Thread.sleep(5000);

// 3. 获取文件夹信息
System.out.println("获取绝对路径:" + dir.getAbsolutePath());
System.out.println("获取路径:" + dir.getPath());
System.out.println("获取文件夹名称:" + dir.getName());
System.out.println("获取父目录:" + dir.getParent());
System.out.println("获取创建时间:" + new Date(dir.lastModified()).toLocaleString());


// 4. 判断
System.out.println("是否是文件夹" + dir.isDirectory());
System.out.println("是否隐藏" + dir.isHidden());


// 5. 遍历

File dir2 = new File("d:\\");
// list方法,将文件夹传入数组
String[] files = dir2.list();
for (String str : files) {
    System.out.println(str);
}

FileFilter的使用

类似linux的管道

// 创建文件类
File dir2 = new File("d:\\");
// 匿名内部类
File[] file2 = dir2.listFiles(new FileFilter() {

    @Override
    public boolean accept(File pathname) {
        // 判断文件是否以.jpg结尾
        if (pathname.getName().endsWith(".jpg")) {
            return true;  // 表示accept(即过滤成功)
        }
        return false;  // 表示过滤失败
    }
});

for (File file : file2) {
    System.out.println(file);
}

递归遍历

public static void listDir(File dir) {
    // 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件(或文件夹)
    File[] f1 = dir.listFiles();

    // 输出dir的绝对路径
    System.out.println(dir.getAbsoluteFile());

    // f1数组不为null且 f1数组的长度>0
    // != null表示MyFile存在, length>0表示MyFile文件夹内有文件(或文件夹)
    if (f1 != null && f1.length > 0) {
        // 开始遍历
        for (File file : f1) {
            // 判断是否为文件夹
            if (file.isDirectory()) {
                // 递归调用listDir,传入file
                listDir(file);
            } else {
                System.out.println(file.getAbsolutePath());
            }

        }

    }

}

递归删除

因为删除目录(文件夹)时,只删除最底层空目录,故需使用递归删除

public static void deleteDir(File dir) {
    File[] f1 = dir.listFiles();

    if (f1 != null && f1.length > 0) {
        for(File file : f1) {
            if (file.isDirectory()) {
                deleteDir(file);
            } else {
                // 删除文件
                System.out.println("删除" + file.getAbsoluteFile() + "结果:" + file.delete());
            }
        }
    }
    // 删除 dir(可能为目录)
    System.out.println("删除" + dir.getAbsoluteFile() + "结果:" + dir.delete());

}
Properties

和流有关的方法

list(PrintStream out)方法:将此属性列表打印到指定的输出流

store方法:通过fos实现

load方法:通过fis实现

// 1. 创建集合
Properties properties = new Properties();

// 2. 添加数据
properties.setProperty("usrName", "张三");
properties.setProperty("age", "20");

// ====list方法====
PrintWriter pw = new PrintWriter("d:\\PropertiesPrint.txt");
properties.list(pw);
pw.close();

// ====store方法====(保存)
FileOutputStream fos = new FileOutputStream("d:\\store.properties");
// 调用 store方法,通过fos实现,进行保存
properties.store(fos,"comments");
fos.close();


// ====load方法====(加载)用的较多
Properties properties2 = new Properties();
FileInputStream fis = new FileInputStream("d:\\store.properties");

// 加载前
System.out.println(properties2.toString());
// 加载
properties2.load(fis);
// 加载后
System.out.println(properties2.toString());

// 关闭
fis.close();