【Java】IO流详解

🌟个人博客:www.hellocode.top🌟
⭐所有文章均在上方博客首发,其他平台同步更新
🔥本文专栏:Java零基础指南
🌞更多相关内容请点击前往 Java零基础指南 查看
⚡如有问题,欢迎指正,一起学习~~



File

概述

  • IO可以对硬盘中的文件进行读写
  • File表示要读写的文件在哪,也可以对文件进行创建,删除等操作

IO流是什么

  • 可以将数据从本地文件中读取出来
  • 可以将数据从内存保存到本地文件

File类是什么

  • 在读写数据时告诉虚拟机要操作的文件/文件夹在哪
  • 对文件/文件夹本身进行操作,包括创建、删除等
File

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

  • 文件和目录可以通过File封装成对象
  • File封装的对象仅仅是一个路径名。它可以是存在的,也可以是不存在的

构造方法

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

Q:为什么要把字符串表示形式的路径变为File对象?

A:就是为了使用File类里面的方法

绝对路径和相对路径
  • 绝对路径:从盘符开始(完整的路径)
  • 相对路径:相对当前项目下的路径
相应操作

File类创建功能

方法名说明
public boolean createNewFile()创建一个新的空的文件
public boolean mkdir()创建一个单级文件夹(了解)
public boolean mkdirs()创建一个多级文件夹

createNewFile 只能创建文件; 如果文件存在,创建失败,返回false; 如果文件不存在,创建文件,返回true; 要求文件所在文件夹必须存在

mkdir 只能创建单级文件夹; 只能创建文件夹;

mkdirs 可以创建单击文件夹,也可以创建多级文件夹

创建文件夹的方法,mkdir只需要了解,掌握mkdirs即可

File类删除功能public boolean delete() 删除由此抽象路径名表示的文件或目录

删除后不进回收站,不能恢复; 可以删除文件,也可以删除文件夹; 如果删除的是文件,直接删除;如果删除的是文件夹,只能删除空文件夹

File类判断和获取功能

方法名说明
public boolean isDirectory()测试此抽象路径名表示的File是否为目录
public boolean isFile()测试此抽象路径名表示的File是否为文件
public boolean exists()测试此抽象路径名表示的File是否存在
public String getName()返回由此抽象路径名表示的文件或目录的名称

getName方法:如果调用者是文件,那么获取的是文件名和后缀名;如果调用者是文件夹,那么获取的是文件夹的名字

File类高级获取功能public File[] listFiles():返回此抽象路径名表示的目录中的文件和目录的File对象数组

  • 进入文件夹,获取这个文件夹里面所有的文件和文件夹的File对象,并把这些File对象都放在一个数组中返回
  • 包括隐藏文件和隐藏文件夹,都可以获取出来

当调用者是一个文件或不存在时,会返回一个null

当调用者是一个空文件夹时,会返回一个长度为0的数组

当调用者是一个有权限才能进入的文件夹时,会返回一个null

练习

练习一:在当前模块下的aaa文件夹中创建一个a.txt文件

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

public class Demo1 {
    public static void main(String[] args) throws IOException {
        File file = new File("File\\aaa");
        if (!file.exists()) {        // 如果文件夹不存在,创建文件夹
            file.mkdirs();
        }
        File newFile = new File(file, "a.txt");
        newFile.createNewFile();        // 要保证文件所在的文件夹必须存在
    }
}

练习二:删除一个多级文件夹

import java.io.File;

public class Demo2 {
    public static void main(String[] args) {
        File src = new File("C:\\Users\\lihao\\Desktop\\cs");
        deleteDir(src);
    }

    private static void deleteDir(File src) {
        // 递归思路
        // 套路:
        // 1.进入----得到src文件夹里面所有内容的File对象
        File[] files = src.listFiles();
        // 2.遍历----得到src文件夹里面每一个文件和文件夹的File对象
        for (File file : files) {
            // 3.判断----如果遍历到的File对象是一个文件就直接删除
            if (file.isFile()) {
                file.delete();
            } else {        // 4.判断----如果遍历到的File对象是一个文件夹,递归
                deleteDir(file);            // 参数一定要是src文件夹里面的文件夹File对象
            }
        }
        src.delete();
    }
}

练习三:统计一个文件夹中每种文件的个数并打印
打印格式如下:
txt:3个
doc:4个

public class Demo3 {
    public static void main(String[] args) {
        File file = new file("File");
        HashMap<String, Integer> hm = new HashMap<>();
        getCount(hm, file);
        System.out.println(h)
    }

    private static void getCount(HashMap<String, Integer> hm, File file) {
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isFile()) {
                String fileName = f.getName();
                String[] fileNameArr = fileName.split("\\.");
                if (fileNameArr.length == 2) {
                    String fileEndName = fileNameArr[1];
                    if (hm.containsKey(fileEndName)) {
                        // 已经存在
                        // 将已经出现的次数获取出来
                        Integer count = hm.get(fileEndName);
                        // 该文件再次出现
                        count++;
                        hm.put(fileEndName, count);
                    } else {
                        // 不存在
                        // 当前文件是第一次出现
                        hm.put(fileEndName, 1);
                    }
                }
            } else {
                getCount(hm, f);
            }
        }
    }
}

字节流

IO流的概述和分类

其中,I表示input,是数据从硬盘进内存的过程,称之为读
O表示output,是数据从内存到硬盘的过程,称之为写

  • IO的数据传输,可以看作是一种数据的流动,按照流动的方向,以内存为参照物,进行读写操作

简单来说:内存在读,内存在写

  • 按流向分:输入流、输出流
  • 按数据类型分:字节流、字符流

字节流:操作所有类型的文件(包括音频、视频、图片等)
字符流:只能操作纯文本文件(包括Java文件、txt文件等)

一般来说,我们说的IO流的分类是按照数据类型来分的

纯文本文件:用windows自带的记事本打开能读得懂的文件

字节流写数据

步骤

  • 创建字节输出流对象(FileOutputStream fos = new FileOutputStream("a.txt"))
  • 写数据(fos.write(数据))
  • 释放资源(fos.close();)

注意点

  • 如果文件不存在,会自动创建出来;如果文件存在,会把文件清空
  • 给write方法传递一个整数时,写入的数据是这个整数在ASCII码表中对应的字符
  • 释放资源指令,表示告诉操作系统,已经不再使用这个文件了。每次使用完流必须要释放资源

字节流写数据的3种方式

方法名说明
void write(int b)一次写一个数据
void write(byte[] b)一次写一个字节数组数据
void write(byte[] b, int off, int len)一次写一个字节数组的部分数据

两个小问题

  • 字节流写完数据如何实现换行

写完数据后,加换行符:\r \n getBytes()

  • 字节流写数据如何实现追加写入呢(保留原数据)?

public FileOutputStream(String name, boolean append) true表示续写开关,保留原数据;默认为false

捕获异常
try{
    FileOutputStream fos = new FileOutputStream("a.txt");
    fos.write(97);
    fos.close();
}catch (IOException e){
    e.printStackTrace();
}

上述方法在fos.write(97);出异常后,close方法将无法执行。那么如何操作才能让close方法一定执行呢?

  • finally:在异常处理时提供finally块来执行所有清除操作。比如说IO流中的释放资源
  • 特点:被finally控制的语句一定会执行,除非JVM退出

异常处理标准格式try...catch...finally

FileOutputStream fos=null;
try{
	fos=new FileOutputStream("a.txt");
	fos.write(97);
}catch(IOException e){
	e.printStackTrace();
}finally{
	//finally 中的代码一定会执行
	if(fos!=null){
		try{
			fos.close();
		}catch(IOException e){
			e.printStackTrace();
		}
	}
}
字节流读数据

步骤

  • 创建字节输入流对象(FileInputStream fis = new FileInputStream("a.txt");)

如果文件存在,不会报错;如果文件不存在,那么直接报错

  • 读数据(int read = fis.read();)

一次读取一个字节,返回值就是本次读到的那个字节数据
也就是字符在码表中对应的那个数字
如果想要看到的是字符数据,一定要强转成char

  • 释放资源(fis.close();)
  • 读取多个字节数据

当读取到文件结束时,会返回-1

int b;
while((b=fis.read())!=-1){
	System.out.println((char)b);
}
fis.close();
复制文件案例

需求:把"E:\\study\\a.txt" 复制到当前模块下

复制文件,其实就是把文件中的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)

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

public class Demo2 {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        FileInputStream fis = new FileInputStream("E:\\study\\test.txt");
        FileOutputStream fos = new FileOutputStream("test.txt");
        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b);
        }
        fis.close();
        fos.close();
    }
}
定义小数组拷贝

如果操作的文件过大,那么速度就会很

为了解决速度问题,字节流通过创建字节数组,可以一次读写多个数据

一次读一个字节数组的方法

  • public int read(byte[] b):从输入流读取最多b.length个字节的数据
  • 返回的是读入缓冲区的总字节数,也就是实际的读取字节个数
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo3 {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        FileOutputStream fos = new FileOutputStream("a.test");
        FileInputStream fis = new FileInputStream("E:\\study\\test.txt");
        byte[] bytes = new byte[1024];
        int len;        // 本次读取到的有效字节个数-----这次读了几个字节
        while ((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, len);
        }
        fis.close();
        fos.close();
    }
}

缓冲流

字节缓冲流:提高读和写的效率

  • BufferedOutputStream:字节缓冲输出流
  • BufferedInputStream:字节缓冲输入流
构造方法
  • 字节缓冲输出流:BufferedOutputStream(OutputStream out)
  • 字节缓冲输入流:BufferedInputStream(InputStream in)

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

import java.io.*;

public class Demo1 {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        // 创建字节缓冲输入流
        // 在底层创建了一个默认长度为8192的字节数组
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\study\\test.txt"));
        // 创建字节缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.txt"));

        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }
        // f
        bis.close();
        bos.close();
    }
}

字符流&字符缓冲流

编码表

基础知识

  • 计算机中储存的信息都是用二进制数表示的
  • 按照某种规则,将字符变成二进制,再存储到计算机中,称为编码
  • 按照同样的规则,将存储在计算机中的二进制数解析显示出来,称为解码
  • 编码和解码的方式必须要一致,否则会导致乱码

ASCII字符集

  • ASCII(美国信息交换标准代码):包括了数字,大小写字符和一些常见的标点符号。
    注意:ASCII码表中是没有中文的
  • GBK:windows系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字。

注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。

  • Unicode码表:由国际组织ISO制定,是统一的万国码,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号

但是因为表示的字符太多,所有Unicode码表中的数字不是直接以二进制的形式存储到计算机的
会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及UTF-32进行编码,再存储到计算机,其中最常见的就是UTF-8

注意:Unicode是万国码,以UTF-8编码后一个中文以三个字节的形式存储

重点:Windows默认使用码表为GBK,一个字符个字节
idea和以后工作默认使用Unicode的UTF-8编解码格式,一个中文个字节

字符串中的编码解码问题

编码

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

解码

  • String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
  • String(bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Demo1 {

	public static void main(String[] args) throws UnsupportedEncodingException {
		// TODO Auto-generated method stub
		String s = "好好学习,天天向上";
		// 利用默认的GBK将中文编码为一系列的字节
		byte[] bytes1 = s.getBytes();
		System.out.println(Arrays.toString(bytes1));
		// 指定为UTF-8编码格式
		byte[] bytes2 = s.getBytes("UTF-8");
		System.out.println(Arrays.toString(bytes2));
		byte[] bytes3 = {-70, -61, -70, -61, -47, -89, -49, -80, -93, -84, -52, -20, -52, -20, -49, -14, -55, -49};
		byte[] bytes4 = {-27, -91, -67, -27, -91, -67, -27, -83, -90, -28, -71, -96, -17, -68, -116, -27, -92, -87, -27, -92, -87, -27, -112, -111, -28, -72, -118};
		String s1 = new String(bytes3);
		String s2 = new String(bytes4,"UTF-8");
		System.out.println(s1);
		System.out.println(s2);
	}
}

为什么字节流读取文本文件,可能出现乱码

因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所有就会出现乱码问题

字符流读取中文的过程

字符流 = 字节流 + 编码表

基础知识

  • 不管是在哪张码表中,中文的第一个字节一定是负数
  • 想要进行拷贝,一律使用字节流或者字节缓冲流
    想要把文本文件中的数据读到内存中,请使用字符输入流
    想要把内存中的数据写到文本文件中,请使用字符输出流
  • GBK码表一个中文两个字节,UTF-8编码格式一个中文三个字节
写出数据

步骤

  • 创建字符输出流对象
    • 如果文件不存在,就创建。但要保证父级路径存在
    • 如果文件存在就清空
  • 写数据
    • 如果写出int类型的整数,实际写出的是整数在码表中对应的字母(如果要写整数,用字符串写)
    • 写出字符串数据,是把字符串本身原样写出
  • 释放资源(每次操作完最后必须释放资源)

字符流写数据的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)写一个字符串的一部分
import java.io.FileWriter;
import java.io.IOException;

public class Demo2 {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub


        // 创建字符输出流对象
        FileWriter fw = new FileWriter("src\\CharStream\\a.txt");
        // FileWriter fw = new FileWriter(new File("CharStream\\a.txt"));


        // 写出数据(一次一个字符)
        // method1(fw);

        // 写一个字符数组
        // method2(fw);

        // 写出字符数组的一部分
        // method3(fw);

        // 写出一个字符串[重点]
        // method4(fw);

        // 写出一个字符串的一部分(了解)
        // method5(fw);

        // 释放资源
        fw.close();


    }

    private static void method5(FileWriter fw) throws IOException {
        fw.write("好好学习,天天向上", 3, 4);
    }

    private static void method4(FileWriter fw) throws IOException {
        String line = "好好学习,";
        fw.write(line);
        fw.write("天天向上");
    }

    private static void method3(FileWriter fw) throws IOException {
        char[] chars = {97, 98, 99, 100, 101, 102, 103};
        fw.write(chars, 3, 4);
    }

    private static void method2(FileWriter fw) throws IOException {
        char[] chars = {97, 98, 99, 100, 101};
        fw.write(chars);
    }

    private static void method1(FileWriter fw) throws IOException {
        fw.write(97);
        fw.write(98);
        fw.write(99);
    }
}
flush和close方法
方法名说明
flush()刷新流,还可以继续写数据
close()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

在写数据后,必须执行刷新流才能将内容最终写入文件种(close会在关闭流前刷新流)

字符流读数据的过程

步骤

  • 创建字符输入流对象:FileReader fr = new FileReader("a.txt"); FileReader fr = new FileReader(new File("a.txt"));
  • 读取数据:fr.read();
  • 释放资源:fr.close();

一次读取多个字符

import java.io.FileReader;
import java.io.IOException;

public class Demo3 {
    public static void main(String[] args) throws IOException {
        // 创建对象
        FileReader fr = new FileReader("src\\CharStream\\a.txt");

        // 创建数组
        char[] chars = new char[1024];
        int len;
        while ((len = fr.read(chars)) != -1) {
            System.out.println(new String(chars, 0, len));
        }

        // 释放资源
        fr.close();
    }
}
练习

保存键盘录入的数据

需求:将用户键盘录入的用户名和密码保存到本地实现永久化存储。
要求用户名独占一行,密码独占一行。

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;

public class Demo4 {
	public static void main(String[] args) throws IOException{
		Scanner sc = new Scanner(System.in);
		System.out.println("【用户注册】");
		System.out.print("请输入用户名(6位):");
		String userName = sc.next();
		System.out.print("请输入密码(6位):");
		String passWord = sc.next();
		FileWriter fw = new FileWriter("user.txt");
		fw.write(userName + "\n" + passWord);
		fw.close();
		System.out.println("注册成功!");
		System.out.println("【用户登录】");
		System.out.print("请输入用户名:");
		String user = sc.next();
		System.out.print("请输入密码:");
		String pass = sc.next();
		FileReader fr = new FileReader("user.txt");
		char[] chars = new char[1024];
		int len;
		while((len = fr.read(chars)) != -1){
			userName = new String(chars, 0, 6);
			passWord = new String(chars, 7 , 6);
		}
		if(user.equals(userName) && pass.equals(passWord)){
			System.out.println("登录成功!");
		}else{
			System.out.println("密码错误,登录失败!");
		}
	}
}
字符缓冲流
  • BufferedWriter(字符缓冲输出流):可以将数据高效的写出
  • BufferedReader(字符缓冲输入流):可以将数据高效的读取到内存
  • 需要传入FileWriter或者FileReader对象,不能直接传入字符串地址或者File对象

字符缓冲流特有方法

  • BufferedWriter
    • void newLine():写一行行分隔符,行分隔符字符串由系统属性定义(回车换行)
  • BufferedReader
    • public String readLine():读一行文字。结果包含行内的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null
练习

需求:读取文件中的数据,排序后再次写到本地文件

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;

public class Demo5 {
    public static void main(String[] args) throws IOException {
        // 创建字符缓冲输入流对象
        BufferedReader br = new BufferedReader(new FileReader("sort.txt"));
        String s = br.readLine();        // 8 7 5 2 1 3 4 9 10 6
        String[] n = s.split(" ");
        int[] num = new int[n.length];
        for (int i = 0; i < n.length; i++) {
            int number = Integer.parseInt(n[i]);
            num[i] = number;
        }
        Arrays.sort(num);
        br.close();
        // 写入
        BufferedWriter bw = new BufferedWriter(new FileWriter("sort.txt"));
        for (int i = 0; i < num.length; i++) {
            bw.write(num[i] + " ");
            bw.flush();
        }
        bw.close();
    }
}
小结
  • 字节流用来拷贝文件
  • 字符流用来进行文件的读写
  • 缓冲流用来提高效率

转换流&对象操作流&Properties

转换流
  • 输入流:InputStreamReader,字节流到字符流的桥梁,把字节流转换为字符流
  • 输出流:OutputStreamWriter,字符流到字节流的桥梁,把字符流转换为字节流

使用场景

  • 在JDK11以前,可用于指定编码读写InputStreamReader("a.txt","utf-8");
  • 在JDK11之后,字符流新推出了一个构造,也可以指定编码表

FileReader fr = new FileReader("a.txt",charset.forName("UTF-8"));

对象操作流

特点:可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中

对象操作流分为两类

  • 对象操作输出流(对象序列化流):ObjectOutputStream,就是将对象写到本地文件中,或者在网络中传输对象
  • 对象操作输入流(对象反序列化流):ObjectInputStream,把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
// User类
package Streamio;

import java.io.Serializable;

//	如果想要这个类的对象能被序列化,那么这个类就必须实现一个接口
public class User implements Serializable{
	
	//	成员变量
	private String userName;
	private String passWord;
	
	//	构造方法
	public User() {
		super();
	}
	public User(String userName, String passWord) {
		super();
		this.userName = userName;
		this.passWord = passWord;
	}
	//	成员方法
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassWord() {
		return passWord;
	}
	public void setPassWord(String passWord) {
		this.passWord = passWord;
	}
	@Override
	public String toString() {
		return "User [userName=" + userName + ", passWord=" + passWord + "]";
	}	
}

Serializable接口的意义:标记性接口,里面没有任何抽象方法,只要一个类实现类这个Serializable接口,那么就表示这个类的对象可以被序列化

// 实现类	对象操作输出流
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Demo1 {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		User user = new User("zhangsan","qwer");
		// 需求:把这个用户信息保存到本地文件
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
		oos.writeObject(user);
		oos.close();
	}
}
// 实现类  写:对象操作输入流
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Demo2 {
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		// TODO Auto-generated method stub
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
		User o = (User) ois.readObject();
		System.out.println(o);
		ois.close();
	}
}
注意点
  • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类(对象实现类),读取数据会不会出问题?
    • serialVersionUID序列号:如果没有手动定义,虚拟机会根据类中的信息自动计算出一个序列号
    • 如果我们修改了类中的信息,虚拟机会再次计算出一个序列号
  • 如果出问题了,如何解决?
    • 不让虚拟机计算序列号,自己手动给出一个不变的序列号

    private static final long serialVersionUID = 1L;
    1L可以自定义,只要不超出long的范围即可

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

使用关键字transient修饰,该关键字标记的成员变量不参与序列化过程

练习

案例:用对象操作流读写多个对象

// 需求:创建多个JavaBean类对象写到文件中,再次读取到内存
// Student类

import java.io.Serializable;

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

    public Student() {
        super();
    }

    public Student(String name, int age) {
        super();
        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;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
}
// 实现类

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Demo3 {
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        Student s1 = new Student("张三", 23);
        Student s2 = new Student("李四", 24);
        Student s3 = new Student("王五", 25);
        // 写入
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.txt"));
        oos.writeObject(s1);
        oos.writeObject(s2);
        oos.writeObject(s3);
        oos.close();
        // 读出
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.txt"));
        while (true) {
            try {
                Student stu = (Student) ois.readObject();
                System.out.println(stu);
            } catch (IOException e) {
                break;
            }
        }
        ois.close();
    }
}
Properties

概述

  • 是一个Map体系的集合类
  • Properties中有跟IO相关的方法
  • 键值对的数据类型基本都定义为字符串

练习:Properties作为Map集合的使用

package Properties;

import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

public class Demo1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Properties prop = new Properties();
        // 增
        prop.put("唐三", "小舞");
        prop.put("戴沐白", "朱竹青");
        prop.put("奥斯卡", "宁荣荣");
        // 删
        prop.remove("戴沐白");
        // 改
        prop.put("奥斯卡", "朱竹青");
        // 查
        Object value = prop.get("唐三");
        // 遍历
        Set<Object> keys = prop.keySet();
        for (Object key : keys) {        // 所有的键
            Object r = prop.get(key);
            System.out.println(key + "=" + r);
        }
        // 所有的键值对对象
        Set<Entry<Object, Object>> entries = prop.entrySet();
        for (Entry<Object, Object> entry : entries) {
            Object key = entry.getKey();
            Object val = entry.getValue();
            System.out.println(key + "---" + val);
        }
    }
}

作为集合的特有方法

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

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)方法的格式写入输出字符流
// 读取

import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class Demo2 {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        Properties prop = new Properties();
        FileReader fr = new FileReader("src\\Properties\\prop.properties");
        // 调用完了load方法之后,文件中的键值对数据已经在集合中了
        prop.load(fr);
        fr.close();
        System.out.println(prop);
    }
}
// 保存

import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class Demo3 {

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        Properties prop = new Properties();
        prop.put("zhangsan", "123");
        prop.put("lisi", "456");
        prop.put("wangwu", "789");
        FileWriter fw = new FileWriter("src\\Properties\\prop.properties");
        prop.store(fw, "注释");        // 注释不填可以填写null
        fw.close();
    }
}

推荐阅读:【Java】多线程详解

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九思のJava之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值