Java基础学习笔记22——IO流

目录

1、File类

1.1、File概述

1.2、File类的构造方法

1.3、File类的创建功能

1.4、File类的判断和获取功能

1.5、File类的删除功能

2、字节流

2.1、IO流概述

2.2、IO流的分类

2.3、字节流写数据

2.3.1、字节流写数据的3种方式

2.3.2、字节流写数据的两个小问题

2.3.3、字节流写数据加异常处理(finally关键字)

2.4、字节流读数据

2.4.1、一次读一个字节的数据

2.4.2、一次读一个字节数组的数据

2.5、字节缓冲流

3、字符流

3.1、为什么会出现字符流?

3.2、编码表

3.3、字符串的编码/解码问题

3.4、字符流中的编码/解码问题

3.5、字符流写数据的5种方式

3.6、字符流读数据的2种方式

3.7、字符缓冲流

3.7.1、字符缓冲流的特有功能

4、特殊操作流

4.1、标准输入输出流

4.1.1、标准输入流

4.1.2、标准输出流

4.2、打印流

4.2.1、字节打印流

4.2.2、字符打印流

4.3、对象序列化流

4.3.1、对象序列化流(ObjectOutputStream)

4.3.2、对象反序列化流(ObjectInputStream)

4.3.3、对象序列化流的几个问题

4.4、Properties集合类

4.4.1、Properties作为集合的特有方法

4.4.2、Properties和IO流结合的方法


1、File类

1.1、File概述

File类在java.io包下,故使用时需要导包。File是一个具体的类,继承自Object类,实现了Serializable和Comparable<File>接口。

File是文件和目录路径名的抽象表示:

  • 文件和目录是可以通过File封装成对象的;
  • 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已,它可以是存在的,也可以是不存在的,将来是要通过具体的操作把这个路径的内容转换为具体存在的。

绝对路径和相对路径的区别:

  • 绝对路径:完整的路径名,不需要任何其他信息就可以定位它所表示的文件。
  • 相对路径:必须使用取自其他路径名的信息进行解释。

1.2、File类的构造方法

方法名描述
File(String pathname)通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
File(String parent, String child)从父路径名字符串和子路径名字符串创建新的File实例
File(File parent, String child)从父抽象路径名和子路径名字符串创建新的File实例

代码演示:

package FilePackage;

import java.io.File;

public class Test {
    public static void main(String[] args) {
        //File(String pathname):通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
        File f1 = new File("F:\\javaFileTest\\java.txt");
        System.out.println(f1); //File类重写了ToString()方法
        
        //File(String parent, String child):从父路径名字符串和子路径名字符串创建新的File实例
        File f2 = new File("F:\\javaFileTest","java.txt");
        System.out.println(f2);
        
        //File(File parent, String child):从父抽象路径名和子路径名字符串创建新的File实例
        File f3 = new File("F:\\JavaFileTest");
        File f4 = new File(f3,"java.txt");
        System.out.println(f4);

    }
}


结论:
F:\javaFileTest\java.txt
F:\javaFileTest\java.txt
F:\JavaFileTest\java.txt

1.3、File类的创建功能

方法名描述
public boolean createNewFile()

当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件,返回true;

文件已经存在时,就不创建文件,并返回false。

public boolean mkdir()

如果目录不存在,就创建由此对象路径名命名的目录,并返回true;

目录已经存在时,就不创建目录,并返回false。

public boolean mkdirs()

如果多级目录不存在,就创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录;

如果多级目录存在,就不创建多级目录,并返回false。

代码演示:

package FilePackage;

import java.io.File;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        //在F盘的javaFileTest目录下创建一个文件java1.txt
        File f1 = new File("F:\\javaFileTest\\java1.txt");
        System.out.println(f1.createNewFile());

        //在F盘的javaFileTest目录下创建一个目录javaFileTest1
        File f2 = new File("F:\\javaFileTest\\javaFileTest1");
        System.out.println(f2.mkdir());

        //在F盘的javaFileTest目录下创建一个多级目录javaFileTest2\\javaFileTest22
        File f3 = new File("F:\\javaFileTest\\javaFileTest2\\javaFileTest22");
        System.out.println(f3.mkdirs());
    }
}

1.4、File类的判断和获取功能

方法名描述
public boolean isDirectory()测试此抽象路径名表示的File是否为目录
public boolean isFile()测试此抽象路径名表示的File是否为文件
public boolean exists()测试此抽象路径名表示的File是否存在
public String getAbsolutePath()返回此抽象路径名的绝对路径名字符串
public String getPath()将此抽象路径名转换为路径名字符串
public String getName()返回由此抽象路径名表示的文件或目录的名称
public String[] list()返回由此抽象路径名表示的目录中的文件和目录的名称字符串数组
public File[] listFiles()返回此抽象路径名表示的目录中的文件和目录的File对象数组

代码演示:

public class Test {
    public static void main(String[] args) throws IOException {
        File f = new File("F:\\javaFT\\java.txt");

        //public boolean isDirectory():测试此抽象路径名表示的File是否为目录
        System.out.println(f.isDirectory());
        //public boolean isFile():测试此抽象路径名表示的File是否为文件
        System.out.println(f.isFile());
        //public boolean exists():测试此抽象路径名表示的File是否存在
        System.out.println(f.exists());

        //public String getAbsolutePath():返回此抽象路径名的绝对路径名字符串
        System.out.println(f.getAbsolutePath());
        //public String getPath():将此抽象路径名转换为路径名字符串
        System.out.println(f.getPath());
        //public String getName():返回由此抽象路径名表示的文件或目录的名称
        System.out.println(f.getName());

        System.out.println("--------");
        File f1 = new File("F:\\javaFT");
        //public String[] list():返回由此抽象路径名表示的目录中的文件和目录的名称字符串数组
        String[] strArray = f1.list();
        for (String str : strArray) {
            System.out.println(str);
        }
        System.out.println("--------");
        //public File[] listFile():返回此抽象路径名表示的目录中的文件和目录的File对象数组
        File[] fileArray = f1.listFiles();
        for (File file : fileArray) {
            System.out.println(file);
        }
    }
}


结果:
false
true
true
F:\javaFT\java.txt
F:\javaFT\java.txt
java.txt
--------
dir
java.txt
--------
F:\javaFT\dir
F:\javaFT\java.txt

1.5、File类的删除功能

方法名描述
public boolean delete()删除由此抽象路径名表示的文件或目录

删除目录时的注意事项:

  • 如果一个目录中有内容(目录、文件),不能直接删除。应该先删除目录中的内容,最后才能删除目录。

2、字节流

2.1、IO流概述

  • IO:输入/输出(Input/Output)。
  • 流:是一种抽象概念,是对数据传输的总称,也就是说数据在设备间的传输称为流,流的本质是数据传输。

IO流就是用来处理设备间数据传输问题的。常见的应用:文件复制;文件上传;文件下载。

字节流可以复制任意的文件数据,一般采用字节缓冲流一次读写一个字节数组的方式。

2.2、IO流的分类

按照数据的流向:

  1. 输入流:读数据。
  2. 输出流:写数据。

按照数据类型来分(一般情况下IO流的分类按数据类型来分):

  1. 字节流:字节输入流;字节输出流。
  2. 字符流:字符输入流;字符输出流。

这两种流应该在什么情况下使用呢?

如果数据通过Windows自带的记事本软件打开,我们可以读懂里面的内容,就是用字符流,否则使用字节流。如果不知道该使用那种类型的流,就是用字节流。

IO流读写数据的异常处理方案:

//JDK7及之后的改进方案(如IO流)
try(定义流对象) {
    可能出现异常的代码;
}catch(异常类名 变量名){
    异常的处理代码;
}//自动释放资源

//JDK9及之后的改进方案(如IO流)
定义输入流对象;
定义输出流对象;
try(输入流对象;输出流对象) {
    可能出现异常的代码;
}catch(异常类名 变量名){
    异常的处理代码;
}//自动释放资源

2.3、字节流写数据

字节流抽象基类:

  • InputStream:这个抽象类是表示字节输入流的所有类的超类(父类)。
  • OutputStream:这个抽象类是表示字节输出流的所有类的超类(父类)。
  • InputStream和OutputStream的子类名特点:子类名称都是以其父类名作为子类名的后缀。

InputStream类在java.io包下,使用时需要导包。InputStream是一个抽象类,继承自Object类,实现了Closeable接口。这个抽象类是表示字节输入流的所有类的超类。

OutputStream类在java.io包下,使用时需要导包。OutStream是一个抽象类,继承自Object类,实现了Closeable和Flushable接口。这个抽象类是表示字节输出流的所有类的超类。

使用字节输出流写数据的步骤:

  1. 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件);
  2. 调用字节输出流对象的写数据方法;
  3. 释放资源(关闭此文件输出流并释放与此流相关的任何系统资源)。

FileOutputStream:文件输出流用于将数据写入File

//创建文件输出流以指定的名称写入文件
FileOutputStream(String name){}

//创建文件输出流以写入由指定的File对象表示的文件
FileOutputStream(File file){}

代码演示:

package IOPackage;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        //创建字节输出流对象
        //创建文件输出流以指定的名称写入文件:FileOutputStream(String name)
        FileOutputStream fos = new FileOutputStream("F:\\javaFT\\java.txt");
        /*
            做了三件事:
            1、调用系统功能创建了文件;
            2、创建了字节输出流对象;
            3、让字节输出流对象指向创建好的文件
         */

        //void write(int b):将指定的字节写入此文件输出流
        fos.write(65); //输出字符A
        fos.write(97); //输出字符a

        //最后要释放资源(重要)
        //void close():关闭此文件输出流并释放与此流相关联的任何系统资源
        fos.close();
    }
}

2.3.1、字节流写数据的3种方式

方法名描述
void write(int b)将指定的字节写入此文件输出流,一次写一个字节数据
void write(byte[] b)将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
void write(byte[] b, int off, int len)将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流,一次写一个字节数组的部分数据

代码演示:

package IOPackage;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        //创建文件输出流以指定的名称写入文件:FileOutputStream(String name)
        FileOutputStream fos = new FileOutputStream("F:\\javaFT\\java.txt");

        //创建文件输出流以写入由指定的File对象表示的文件:FileOutputStream(File file)
        //File file = new File("F:\\javaFT\\java.txt");
        //FileOutputStream fos1 = new FileOutputStream(file);

        //void write(int b):将指定的字节写入此文件输出流
        for (int i = 0; i < 26; i++) {
            fos.write(65 + i); //输出字符A到Z
        }

        fos.write('\n'); //换行

        //void write(byte[] b)	将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
        byte[] bytes = new byte[26];
        for(int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte)(97 + i); //输出字符a到z
        }
        fos.write(bytes);

        fos.write('\n'); //换行

        //void write(byte[] b, int off, int len):
        // 将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流,一次写一个字节数组的部分数据
        fos.write(bytes,1,bytes.length-1); //输出去掉a和z后的字节流

        //最后要释放资源(重要)
        //void close():关闭此文件输出流并释放与此流相关联的任何系统资源
        fos.close();
    }
}
结果(java.txt中):
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
bcdefghijklmnopqrstuvwxyz

2.3.2、字节流写数据的两个小问题

1、字节流写数据如何实现换行?

  • windows:\r\n
  • Linux:\n
  • mac:\r

2、字节流写数据如何实现追加写入?

//创建文件输出流以指定的名称写入文件
public FileOutputStream(String name, boolean append){}
//第二个参数append为true,则字节将写入文件的末尾而不是开头

范例(实现追加写入):
FileOutputStream fos = new FileOutputStream("F\\java.txt", true);

2.3.3、字节流写数据加异常处理(finally关键字)

finally关键字:在异常处理时提供finally块来执行所有清楚操作。比如说IO流中的释放资源。

finallly特点:被finally控制的语句一定会执行,除非JVM退出。

//使用finally的格式
try{
    可能出现异常的代码;
}catch(异常类名 变量名){
    异常处理代码;
}finally{
    执行所有清除操作;
}

2.4、字节流读数据

需求:把文件java.txt中的内容读取出来在控制台输出。

FileInputStream:从文件系统中的文件获取输入字节。

//通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
FileInputStream(String name)

使用字节输入流读数据的步骤:

  1. 创建字节输入流对象;
  2. 调用字节输入流对象的读数据方法;
  3. 释放资源。

2.4.1、一次读一个字节的数据

代码演示:

package IOPackage;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException, FileNotFoundException {
        //通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名:
        //FileInputStream(String name)
        FileInputStream fis = new FileInputStream("F:\\javaFT\\java.txt");

        //调用字节输入流对象的读数据方法
        //int read():从该输入流读取一个字节的数据,如果读取到文件的末尾,则返回-1
        int by;
        while((by = fis.read()) != -1){
            System.out.print((char)by);
        }

        //最后要释放资源(重要)
        //void close():关闭此文件输出流并释放与此流相关联的任何系统资源
        fis.close();
    }
}

2.4.2、一次读一个字节数组的数据

代码演示:

package IOPackage;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException, FileNotFoundException {
        //通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名:
        //FileInputStream(String name)
        FileInputStream fis = new FileInputStream("F:\\javaFT\\java.txt");

        //调用字节输入流对象的读数据方法
        //int read(byte[] b):从该输入流读取最多b.length个字节的数据到一个字节数组,返回实际读取到的字节数据个数
        byte[] bys = new byte[10];
        int len = fis.read(bys); //len值为实际读取到的字节数据个数
        System.out.println(len);
        //String(byte[] bytes):将byte[]类型数据转换为String类型
        System.out.println(new String(bys, 0, len));

        System.out.println("-----------");

        byte[] bys1 = new byte[1024]; //1024及其整数倍即可
        int len1;
        while((len1 = fis.read(bys1)) != -1){
            System.out.print(new String(bys1,0,len1));
        }

        //最后要释放资源(重要)
        //void close():关闭此文件输出流并释放与此流相关联的任何系统资源
        fis.close();
    }

2.5、字节缓冲流

类名构造方法描述
BufferedOutputStreamBufferedOutputStream(OutputStream out)该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。
BufferedInputStreamBufferedInputStream(InputStream in)创建BufferedInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从做包含的输入流中重新填充,一次很多字节。

为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?

  • 字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作。

代码演示:

package IOPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException, FileNotFoundException {
        //字节缓冲输出流:BufferedOutputStream(OutputStream out)
        FileOutputStream fos = new FileOutputStream("F:\\javaFT\\javaBuffered.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //写数据
        bos.write("Hello\r\n".getBytes());
        bos.write("World\r\n".getBytes());
        //释放资源
        bos.close();

        //字节缓冲输出流:BufferedInputStream(InputStream in)
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("F:\\javaFT\\javaBuffered.txt"));
        //读数据
        //一次读取一个字节数据
//        int by;
//        while ((by = bis.read()) != -1) {
//            System.out.print((char) by);
//        }
        System.out.println("------------");
        //一次读取一个字节数组的数据
        byte[] bys = new byte[1024];
        int len;
        while ((len = bis.read(bys)) != -1) {
            System.out.println(new String(bys, 0, len));
        }
        //释放资源
        bis.close();
    }
}

3、字符流

3.1、为什么会出现字符流?

需求:字节流读文本文件数据。

一个汉字存储:

  • 如果是GBK编码,占用2个字节;
  • 如果是UTF-8编码,占用3个字节。

用字节流读取含有汉字的文本文件数据会乱码。

由于字节流操作中文不是特别的方便,所以Java就提供字符流。字符流=字节流+编码表

用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?

  • 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数。

3.2、编码表

基础知识:

  • 计算机中存储的信息都是用二进制数表示的,我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果;
  • 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号,否则就会导致乱码现象。
  • 字符编码,就是一套自然语言的字符与二进制数之间的对应规则(如ASCII码中A对应65)。

字符集:

  • 是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等;
  • 计算机要准确地存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集由ASCII字符集、GBXXX字符集、Unicode字符集等。

ASCII字符集:

  • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):是基于拉丁字母地一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数据和西文符号);
  • 基本地ASCII字符集,使用7位表示一个字符,一共有128个字符。ASCII地扩展字符集使用8位表示一个字符,共256个字符,方便支持欧洲常用字符,是一个系统支持地所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。

GBXXX字符集:

  • GB2312:简体中文码表。一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名等都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的“全角”字符,而原来在127号以下的那些就叫“半角”字符了。
  • GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
  • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。

Unicode字符集:

  • 为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案:UTF-8、UTF-16和UTF-32.最为常用的是UTF-8编码。
  • UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页以及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用1至4个字节为每个字符编码。编码规则:① 128个US-ASCII字符,只需要1个字节编码;② 拉丁文等字符,需要2个字节编码;③ 大部分常用字(含中文),使用3个字节编码;④ 其他极少使用的Unicode辅助字符,使用4字节编码。

3.3、字符串的编码/解码问题

编码:

//使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes()

//使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName)

解码:

//通过使用平台的默认字符集解码指定的字节数组来构造新的String
String(byte[] bytes)

//通过指定的字符集解码指定的字节数组来构造新的String
String(byte[] bytes, String charsetName)

代码演示:

package IOPackage;

import java.io.*;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) throws IOException {
        //定义一个字符串
        String s = "张三";

        //编码
        //byte[] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
        byte[] bys1 = s.getBytes();
        System.out.println(Arrays.toString(bys1));
        //byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
        byte[] bys2 = s.getBytes("UTF-8");
        System.out.println(Arrays.toString(bys2));
        byte[] bys3 = s.getBytes("GBK");
        System.out.println(Arrays.toString(bys3));

        //解码
        String s1 = new String(bys1);
        System.out.println(s1);
        String s2 = new String(bys1,"UTF-8");
        System.out.println(s2);
        String s3 = new String(bys1,"GBK");
        System.out.println(s3);
    }
}
结果:
[-27, -68, -96, -28, -72, -119]
[-27, -68, -96, -28, -72, -119]
[-43, -59, -56, -3]
张三
张三
寮犱笁

3.4、字符流中的编码/解码问题

字符流抽象基类:

  • Reader:字符输入流的抽象类。
  • Writer:字符输出流的抽象类。

字符流中和编码解码问题相关的两个类:

  • InputStreamReader类(在java.io包下,继承自Reader,有一个子类FileReader(FileReader是用于读取字符文件的便捷类)):是从字节流到字符流的桥梁;它读取字节,并使用指定的charset将其解码为字符;它使用的字符集可以由名称指定,也可以被明确指定,或者可以接收平台的默认字符集。
  • OutputStreamWriter类(在java.io包下,继承自Writer,有一个子类FileWriter(FileWriter是用于写入字符文件的便捷类)):是从字符流到字节流的桥梁;使用指定的charset将写入的字符编码为字节;它使用的字符集可以由名称指定,也可以被明确指定,或者可以接收平台的默认字符集。

代码演示:

package IOPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        //OutputStreamWriter(OutputStream_out):创建一个使用默认字符编码的OutputStreamWriter
        FileOutputStream fos = new FileOutputStream("F:\\javaFT\\IOStream.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
        osw.write("李四");
        osw.close(); //释放资源


        //InputStreamWriter(InputStream_in, String charsetName):创建一个使用命名字符集的InputStreamWriter
        FileInputStream fis = new FileInputStream("F:\\javaFT\\IOStream.txt");
        InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
        //一次读取一个数据
        int ch;
        while((ch=isr.read()) != -1){
            System.out.print((char)ch);
        }
        isr.close();

    }
}


结果:
����

3.5、字符流写数据的5种方式

方法名描述
void write(int c)写一个字符
void write(char[] cbuf)写入一个字符数组
void write(char[] cbuf, int off, int len)写入字符数组的一部分
void write(String str)写一个字符串
void write(String str, int off, int len)写一个字符串的一部分
方法名描述
void flush()刷新流,还可以继续写数据
void close()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

字符流只能复制文本数据,一般采用字符缓冲流的特有功能。

代码演示:

package IOPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("F:\\javaFT\\IOStream.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");

        //void write(int c):写一个字符(需要刷新)
        osw.write('A');
        osw.write(97);
        //void flush():刷新流
        osw.flush();

        //void write(char[] cbuf):写入一个字符数组
        char[] chs = {'\n','1','2','3','4','5'};
        osw.write(chs);

        //void write(char[] cbuf, int off, int len):写入字符数组的一部分
        osw.write(chs,0,3);

        //void write(String str):写一个字符串
        osw.write("\na999999a");

        //void write(String str, int off, int len):写一个字符串的一部分
        osw.write("\n123321",0,4);

        osw.close(); //释放资源之前有一次自动刷新
    }
结果(IOStream.txt中):
Aa
12345
12
a999999a
123

3.6、字符流读数据的2种方式

方法名描述
int read()一次读一个字符数据
int read(char[] cbuf)一次读一个字符数组数据

代码演示:

package IOPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("F:\\javaFT\\IOStream.txt");
        InputStreamReader isr = new InputStreamReader(fis,"UTF-8");

        //int read():一次读一个字符数据
//        int ch;
//        while((ch=isr.read()) != -1){
//            System.out.print((char)ch);
//        }

        //int read(char[] cbuf):一次读一个字符数组数据
        char[] chs = new char[1024];
        int len;
        while((len=isr.read(chs)) != -1){
            System.out.print(new String(chs,0,len));
        }

        isr.close();
    }
}


结果:
Aa
12345
12
a999999a
123

3.7、字符缓冲流

  • BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小,默认值足够大,可用于大多数用途。
  • BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符、数组和行的高效读取,可以指定缓冲区大小,或者可以接受默认大小,默认值足够大,可用于大多数用途。

构造方法:

  • BufferedWriter(Writer out)
  • BufferedReader(Reader in)

代码演示:

package IOPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        //BufferedWriter(Writer out)
        FileWriter fw = new FileWriter("F:\\javaFT\\IOStream.txt");
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write("Hello\r\n");
        bw.write("World\r\n");
        bw.close();

        //BufferedReader(Reader in)
        FileReader fr = new FileReader("F:\\javaFT\\IOStream.txt");
        BufferedReader br = new BufferedReader(fr);
        //一次读取一个字符数据
//        int ch;
//        while((ch=br.read()) != -1){
//            System.out.print((char)ch);
//        }
        //一次读取一个字符数组数据
        char[] chs = new char[1024];
        int len;
        while((len=br.read(chs)) != -1){
            System.out.println(new String(chs,0,len));
        }
        br.close();
    }
}


结果:
Hello
World

3.7.1、字符缓冲流的特有功能

方法名描述

BufferedWriter类中:

void newLine()

写一行行分隔符,行分隔符字符串由系统属性定义。

BufferedReader类中:

public String readLine()

读一行文字,结果包含行的内容的字符串,不包括任何终止字符,如果流的结尾已经到达,则为null。

代码演示:

package IOPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        //创建字符缓冲输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\javaFT\\IOStream.txt"));
        //写数据
        for (int i = 0; i < 10; i++) {
            bw.write("Hello " + i);
            bw.newLine(); //换行,void newLine():写一行行分隔符,行分隔符字符串由系统属性定义。
            bw.flush(); //刷新
        }
        bw.close();

        //创建字符缓冲输入流
        BufferedReader br = new BufferedReader(new FileReader("F:\\javaFT\\IOStream.txt"));
        //public String readLine():读一行文字,结果包含行的内容的字符串,不包括任何终止字符,如果流的结尾已经到达,则为null。
        String line;
        while((line = br.readLine()) != null){
            System.out.print(line);
            System.out.println(); //因为读的内容不包含任何终止字符,所以需要手动换行
        }
        br.close();
    }
}


结果:
Hello 0
Hello 1
Hello 2
Hello 3
Hello 4
Hello 5
Hello 6
Hello 7
Hello 8
Hello 9

4、特殊操作流

4.1、标准输入输出流

System类中有两个静态的成员变量:

  • public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源。
  • public static final PrintStream out:标准输出流。通常该流对应于显式输出或由主机环境或用户指定的另一个输出目标。

4.1.1、标准输入流

自己实现键盘录入数据:

  • BufferReader br = new BufferedReader(new InputStreamReader(System.in));

写起来太麻烦,Java就提供了一个类实现键盘录入:

  • Scanner sc = new Scanner(System.in);

4.1.2、标准输出流

输出语句的本质是一个标准的输出流。

  • PrintStream ps = System.out;
  • PrintStream类有的方法,System.out都可以使用。

代码演示:

package SysPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        //public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源。
        InputStream is = System.in; //多态
        //读取数据1:一次读取一个字节
//        int by;
//        while ((by = is.read()) != -1) {
//            System.out.print((char) by);
//        }
        //读取数据2:
        InputStreamReader isr = new InputStreamReader(is);
        //使用字符流一次读取一行数据
        //一次读取一行数据的方法是字符缓冲输入流的特有方法
        BufferedReader br = new BufferedReader(isr);
        System.out.print("请输入一个字符串:");
        String line = br.readLine();
        System.out.print("你输入的字符串是:" + line);
        br.close();

        System.out.println();

        //public static final PrintStream out:标准输出流。通常该流对应于显式输出或由主机环境或用户指定的另一个输出目标。
        PrintStream ps = System.out; //System.out的本质是一个字节输出流
        ps.print("Hello\n");
        ps.println(100);
    }
}


结果:
请输入一个字符串:rere
你输入的字符串是:rere
Hello
100

4.2、打印流

打印流分类:

  • 字节打印流:PrintStream
  • 字符打印流:PrintWriter

打印流的特点:

  • 只负责输出数据,不负责读取数据;
  • 有自己的特有方法。

4.2.1、字节打印流

PrintStream(String fileName):使用指定的文件名创建新的打印流。

使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出。

package SysPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        //PrintStream(String fileName):使用指定的文件名创建新的打印流。
        PrintStream ps = new PrintStream("F:\\javaFT\\java.txt");

        //写数据
//        //字节输出流有的方法
//        ps.write(97); //写入a
        //使用特有方法写数据
        ps.print(97); //输入什么就写什么,这句会写入97,而不是a

        //释放资源
        ps.close();
    }
}

4.2.2、字符打印流

字符打印流PrintWriter的构造方法:

构造方法描述
PrintWriter(String filename)使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
PrintWriter(Writer out, boolean autoFlush)

创建一个新的PrintWriter

out:字符输出流

autoFlush:一个布尔值,如果为真,则println,printf或format方法将刷新输出缓冲区

代码演示:

package SysPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        //PrintWriter(String filename):使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
        PrintWriter pw = new PrintWriter("F:\\javaFT\\java.txt");
        //写数据1
        pw.write("Hello\n");
        pw.write("World\n");
        pw.flush();
        //写数据2
        pw.println("Java");
        pw.flush();
        pw.close();

        PrintWriter(Writer out, boolean autoFlush):创建一个新的PrintWriter
        PrintWriter pw2 = new PrintWriter(new FileWriter("F:\\javaFT\\java.txt"),true);
        //写数据3
        pw2.println("123");
        pw2.println("abc");
        pw2.close();
    }
}

4.3、对象序列化流

对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象。

这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息。字节序列写到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。

要实现序列化和反序列化就要使用对象序列化流和对象反序列化流:

  • 对象序列化流:ObjectOutputStream
  • 对象反序列化流:ObjectInputStream

4.3.1、对象序列化流(ObjectOutputStream)

将Java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。

构造方法:

  • ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream

序列化对象的方法:

  • void writeObject(Object obj):将指定的对象写入ObjectOutputStream

注意:

  • 一个对象要想被序列化,该对象所属的类必须实现Serializable接口;
  • Serializable是一个标记接口,实现该接口,不需要重写任何方法。

4.3.2、对象反序列化流(ObjectInputStream)

ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象。

构造方法:

  • ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream

反序列化对象的方法:

  • Object readObject():从ObjectInputStream读取一个对象。

代码演示:

package SysPackage;

import java.io.Serializable; //序列化和反序列化接口

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

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.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;
    }
}
package SysPackage;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //通过构造方法创建对象序列化流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\javaFT\\oos.txt"));
        //创建对象
        Student s = new Student("张三",18);
        //void writeObject(Object obj):将指定的对象写入ObjectOutputStream
        oos.writeObject(s); //异常:NotSerializableException
        //释放资源
        oos.close();

        //通过构造方法创建对象反序列化流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\javaFT\\oos.txt"));
        //Object readObject():从ObjectInputStream读取一个对象。
        Object obj = ois.readObject();
        Student s1 = (Student) obj;
        System.out.println(s1.getName()+","+s1.getAge());
        //释放资源
        ois.close();
    }
}


结果:
张三,18

4.3.3、对象序列化流的几个问题

1、用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?

  • 会出问题,会抛出InvalidClassException异常。

2、如果出问题了,如何解决呢?

  • 给对象所属的类加一个serialVersionUID属性并用static、final、long进行修饰,如 private static final long serialVersionUID = 34L 。

3、如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?

  • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程。

4.4、Properties集合类

Properties是一个Map体系的集合类,Properties可以保存到流中或从流中加载。

4.4.1、Properties作为集合的特有方法

方法名描述
Object setProperty(String key, String value)设置集合的键和值,都是String类型,底层调用Hashtable方法put
String getProperty(String key)使用此属性列表中指定的键搜索属性
Set<String> stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串

代码演示:

package SysPackage;

import java.io.*;
import java.util.Properties;
import java.util.Set;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //创建集合对象
        //Properties<String,String> prop = new Properties<String,String>(); //错误的写法
        Properties prop = new Properties(); //默认Object类型

        //Object setProperty(String key, String value):设置集合的键和值,都是String类型,底层调用Hashtable方法put
        prop.setProperty("002", "零零二");
        prop.setProperty("004", "零零四");
        prop.setProperty("006", "零零六");

        System.out.println("--------------");
        //String getProperty(String key):使用此属性列表中指定的键搜索属性
        System.out.println(prop.getProperty("002"));

        System.out.println("--------------");
        //Set<String> stringPropertyNames():从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
        Set<String> names = prop.stringPropertyNames();
        for (String key : names) {
            String value = prop.getProperty(key);
            System.out.println(key + "," + value);
        }


        //遍历集合
        System.out.println("--------------");  
        Set<Object> keySet = prop.keySet();
        for (Object key : keySet) {
            Object value = prop.get(key);
            System.out.println(key + "," + value);
        }
        
        System.out.println("--------------");
        System.out.println(prop);
    }
}


结果:
--------------
零零二
--------------
002,零零二
004,零零四
006,零零六
--------------
002,零零二
004,零零四
006,零零六
--------------
{002=零零二, 004=零零四, 006=零零六}

4.4.2、Properties和IO流结合的方法

方法名描述
void load(InputStream inStream)从输入字节流读取属性列表(键和元素对)
void load(Reader reader)从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments)将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流
void store(Writer writer, String comments)将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流

代码演示:

package SysPackage;

import java.io.*;
import java.util.Properties;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //把集合中的数据保存到文件
        myStore();

        //把文件中的数据加载到集合
        myLoad();
    }

    private static void myStore() throws IOException {
        Properties prop = new Properties();

        prop.setProperty("001","零零一");
        prop.setProperty("002","零零二");
        prop.setProperty("003","零零三");

        //void store(Writer writer, String comments)
        FileWriter fw = new FileWriter("F:\\javaFT\\fw.txt");
        prop.store(fw,null);
        fw.close();
    }

    private static void myLoad() throws IOException {
        Properties prop = new Properties();

        //void load(Reader reader)
        FileReader fr = new FileReader("F:\\javaFT\\fw.txt");
        prop.load(fr);
        fr.close();

        System.out.println(prop);
    }
}


结果:
{001=零零一, 002=零零二, 003=零零三}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值