2

IO流学习总结(java)

 2 years ago
source link: https://blog.csdn.net/hbdhaj/article/details/119250038
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、File

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

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

1.1 File类的构造方法

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

public class FileDemo01 {
    public static void main(String[] args) {
        File file1 = new File("E:\\java\\java.txt");
        System.out.println(file1);

        File file2 = new File("E:\\java", "java.txt");
        System.out.println(file2);

        File file3 = new File("E:\\java");
        File file4 = new File(file3, "java.txt");
        System.out.println(file4);
    }
}

//输出结果:
//    E:\java\java.txt
//    E:\java\java.txt
//    E:\java\java.txt

1.2 File类的创建功能

方法名说明public boolean createNewFile()当具有该名称的文件不存在时,创建一个由该抽象路径命名的新空文件夹public boolean mkdir()创建由此抽象路径命名的目录public boolean mkdirs()创建由此抽象路径命名的目录,包括任何必须但不存在的父目录
public class FileDemo02 {
    public static void main(String[] args) throws IOException {
        //需求1:我要在E:\java目录下创建一个文件java.txt
        File file1 = new File("E:\\java\\java.txt");
        System.out.println(file1.createNewFile());

        //需求2:我要在E:\java目录下创建一个目录JavaEE
        File file2 = new File("E:\\java\\JavaEE");
        System.out.println(file2.mkdir());

        //需求3:我要在E:\java目录下创建一个多级目录JavaWEB\HTML
        File file3 = new File("E:\\java\\JavaWEB\\HTML");
        System.out.println(file3.mkdirs());
    }
}

1.3 File类删除功能

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

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

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

删除目录时的注意事项:

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

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对象 数组

1.5 递归

递归概述:以编程的角度来看,递归指的是方法定义中调用方法本身的现象

递归解决问题的思路:

把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解

递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算

递归解决问题要找到两个内容:

  • 递归出口:否则会出现内存溢出
  • 递归规则:羽缘文体想死的规模较小的问题

1.6 案例:遍历目录

需求: 给定一个路径,通过递归完成遍历该目录下的所有内容,并把所有文件的绝对路径输出在控制台

思路:

  • 根据给定的路径创建一个File对象
  • 定义一个方法,用于获取给定目录下的所有内容,参数为第1步创建的File对象
  • 获取给定的File目录下所有的文件或者目录的File数组
  • 遍历该File对象是否是目录
    • 是:递归调用
    • 不是:获取绝对路径输出在控制台
public class FileDemo {
    public static void main(String[] args) throws IOException {
        File srcFile = new File("E:\\Excel");

        getAllFilePath(srcFile);
    }

    //定义一个方法,用于获取给定目录下的所有内容,参数为第1步创建的File对象
    public static void getAllFilePath(File srcFile){
        //获取给定的File目录下所有的文件或者目录的File数组
        File[] files = srcFile.listFiles();
        if (files != null){
            for (File file :
                    files) {
                if (file.isDirectory()){
                    getAllFilePath(file);
                }else {
                    System.out.println(file.getAbsolutePath());
                }
            }
        }
    }
}

二、IO流概述

IO流概述:

  • IO:输入/输出(Input/Output)
  • 流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间传输成为流,流的本质是数据传输
  • IO流就是用来处理设备间数据传输的问题
    • 常见应用:文件复制、文件上传、文件下载

IO流分类:

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

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

那么这两种流都是在什么情况下使用呢:

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

三、字节流

3.1 字节流写数据

字节流抽象基类

  • InputStream:这个抽象类是表示字节输入流的所有类的超类
  • OutputStream:这个周响雷是表示字节输出流的所有类的超类
  • 子类名特点:子类名都是以其付雷鸣作为子类的后缀

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

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

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

  • 创建字节输出流对象(调用系统功能创建文件,创建字节输出流对象,让字节输出流对象指向文件)
  • 调用字节输出流对象的写数据方法
  • 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream outputStream = new FileOutputStream("fos.txt");
        outputStream.write(54);

        outputStream.close();
    }
}

3.1.1 字节流写数据的三种方式

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

public class FileOutputStreamDemo01 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("fos.txt");
        fos.write(97);
        fos.write(98);
        fos.write(99);

        FileOutputStream fos1 = new FileOutputStream("fos1.txt");
        byte[] bytes = {97,98,99,100,'a'};
        fos1.write(bytes);

        FileOutputStream fos2 = new FileOutputStream("fos2.txt");
        fos2.write(bytes,1,2);

        fos.close();
        fos1.close();
        fos2.close();
    }
}

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

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

public class FileOutputStreamDemo02 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("fos.txt");

        for (int i = 0; i < 10; i++) {
            fos.write("hello\n".getBytes());
//            fos.write("\n".getBytes());
        }
        fos.close();
    }
}

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

  • public FileOutputStream(String name,boolean append)
  • 创建文件输出流以指定的名称写入文件。如果第二个参数为true,则字节将写入文件的末尾
public class FileOutputStreamDemo02 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("fos.txt",true);

        for (int i = 0; i < 10; i++) {
            fos.write("hello\n".getBytes());
        }
        fos.close();
    }
}

3.2 字节流读数据

3.2.1一次读一个字节数据

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

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

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

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

  • 创建字节输入流对象
  • 调用字节输入流对象的读取数据的方法
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("fos.txt");

        //读取一个字节数据
//        int read = fis.read();
//        System.out.println((char) read);
//        int read1 = fis.read();
//        System.out.println((char) read1);

        //遍历输出
        int by = fis.read();
        while (by != -1){
            System.out.print((char) by);
            by = fis.read();
        }

        fis.close();
    }
}

3.2.2 一次读取一个字节数组的数据

需求: 把文件fos.txt众人内容读取出来再控制台输出

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

  • 创建字节输入流对象
  • 调用字节输入流对象的读数据方法
import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("fos.txt");

//        byte[] bys = new byte[5];
//        int len = fis.read(bys);    //读取到的数据的长度
//        System.out.println(new String(bys,0,len));

        byte[] bys = new byte[1024];    //这里一般写1024及其它的整数倍
        int len;
        while ((len=fis.read(bys))!=-1){
            System.out.print(new String(bys,0,len));
        }

        fis.close();
    }
}

3.2.3 案例:复制图片

需求: 把一张图片复制到模块目录下的"tp.jpg"

思路:

  • 根据数据源创建字节输入流对象
  • 根据目的地创建字节输出流对象
  • 读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyJpgDemo {
    public static void main(String[] args) throws IOException {
        long startTime = System.currentTimeMillis();

        FileInputStream fis = new FileInputStream("E:\\图片\\psc (1).jpg");
        FileOutputStream fos = new FileOutputStream("tp.jpg");

        byte[] bys = new byte[1024];
        int len;
        while ((len=fis.read(bys))!=-1){
            fos.write(bys,0,len);
        }
        fis.close();
        fos.close();

        long endTime = System.currentTimeMillis();
        System.out.println("共耗时:"+ (endTime-startTime)/1000.0 +"秒");
    }
}

3.3 字节缓冲流

字节缓冲流:

  • BufferOutputStream:该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
  • BufferInputStream:创建BufferInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所含的输入流中重新填充,一次很多字节

构造方法:

  • 字节缓冲输出流:BufferOutputStream(OutoutStream out)
  • 字节缓冲输入流:BufferInputStream(InputStream in)

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

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

public class HuanChongLiuDemo {
    public static void main(String[] args) throws IOException {
        //字节缓冲输出流
//        FileOutputStream fos = new FileOutputStream("fos.txt");
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("fos.txt"));
        //写数据
        bos.write("hello\nworld".getBytes());
        bos.close();

        //字节缓冲输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("fos.txt"));
        byte[] bys = new byte[1024];
        int len;
        while ((len=bis.read(bys))!=-1) {
            System.out.print(new String(bys,0,len));
        }
        bis.close();
    }
}

四、字符流

由于字节流操作中文不是特别方便,所以Java就提供字节流

  • 字符流 = 字节流 + 编码表

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

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

4.1 编码表

基础知识:

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

字符集:

  • 是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
  • 计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。
  • 常见的字符集有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和UTF32。最为常用的是UTF-8编码。
  • UTF-8编码: 可以用来表示Unicode标准中任意字符,他是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
  • 编码规则:
    • 128个US-ASCII字符,只需要一个字节编码
    • 拉丁文等字符,需要两个字节编码
    • 大部分常用字(含中文),使用三个字节编码
    • 其他极少使用的Unicode辅助字符,使用四字节编码

4.2 字符串中的编码解码问题

编码:

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

解码:

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

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

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

        byte[] bytes = s.getBytes();    //[-26, -75, -114, -27, -115, -105]
        byte[] bytes1 = s.getBytes("UTF-8");    //[-26, -75, -114, -27, -115, -105]
        byte[] gbks = s.getBytes("GBK");    //[-68, -61, -60, -49]

        System.out.println(Arrays.toString(bytes));
        System.out.println(Arrays.toString(bytes1));
        System.out.println(Arrays.toString(gbks));

        String s1 = new String(bytes);
        String s2 = new String(bytes, "UTF-8");
        String s3 = new String(bytes, "GBK");
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);

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

        String s4 = new String(gbks);
        String s5 = new String(gbks, "UTF-8");
        String s6 = new String(gbks, "GBK");
        System.out.println(s4);
        System.out.println(s5);
        System.out.println(s6);
    }
}

4.3 字符流中的编码解码问题

字符流抽象基类:

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

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

  • InputStreamReader:是从字节流到字符流的桥梁
    • 它读取字节,并使用指定的编码将其解码为字符
    • 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
  • OutputStreamWriter:是从字符流到字节流的桥梁
    • 使用指定的编码将写入的字符编码为字节
    • 它使用的字符集可以由名称来指定,也可以被明确指定,或者可以接受平台的默认字符集
import java.io.*;

public class ConversionStreamDemo {
    public static void main(String[] args) throws IOException {
        //写
        //OutputStreamWriter(OutputStream out)              创建一个使用默认字符编码的OutputStreamWriter。
        //OutputStreamWriter(OutputStream out, Charset cs)  创建一个使用给定字符集的OutputStreamWriter。
        FileOutputStream fos = new FileOutputStream("osw.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        osw.write("济南");
        osw.close();

        //读
        //InputStreamReader(InputStream in)                 创建一个使用默认字符集的InputStreamReader。
        //InputStreamReader(InputStream in, Charset cs)     创建一个使用给定字符集的InputStreamReader。
        InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"));
        int ch;
        while ((ch=isr.read())!=-1){
            System.out.print((char) ch);
        }
        isr.close();
    }
}

4.4 字符流写数据的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)写一个字符串的一部分

注意:执行完 write() 方法后并不会立即写入到文件中,需要执行刷新流方法 flush() 后才可以在文件中看到写入的数据

4.5 字符流读数据的2种方式

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

4.6 案例:复制Java文件

需求: 把模块目录下的 “ceshi.md” 复制到模块目录下的 “Copy.java”

思路:

  • 根据数据源创建字符输入流对象
  • 根据目的地创建字符输出流对象
  • 读写数据,复制文件
import java.io.*;

public class CopyDemo01 {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("ceshi.md"));
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("Copy.md"));

        char[] chs = new char[1024];
        int len;
        while ((len = isr.read(chs)) != -1) {
            osw.write(chs,0,len);
        }

        isr.close();
        osw.close();
    }
}

4.7 复制Java文件(改进版)

需求: 把模块目录下的 “ceshi.md” 复制到模块目录下的 “Copy.java”

分析:

  • 转换刘的名字比较长,而我们常见的操作都是按照本地默认编码,所以,为了简化书写,转换流提供了对应的子类
  • FileReader:用于读写字符文件的便捷类
    • FileReader(String fileName)
  • FileWriter:用于写字符文件的便捷类
    • FileWriter(String fileName)
public class CopyDemo02 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("ceshi.md");
        FileWriter fw = new FileWriter("Copy.md");

        char[] chs = new char[1024];
        int len;
        while ((len = fr.read(chs)) != -1) {
            fw.write(chs);
        }

        fr.close();
        fw.close();
    }
}

4.8 字符缓冲流

字符缓冲流:

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

构造方法:

  • BufferedWriter(Writer out)
  • BufferedReader(Reader in)
import java.io.*;

public class CollectionDemo {
    public static void main(String[] args) throws IOException {
        //写
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
        bw.write("helloWorld");
        bw.close();

        //读
        BufferedReader br = new BufferedReader(new FileReader("bw.txt"));

        char[] chars = new char[1024];
        int len;
        while ((len=br.read(chars))!=-1){
            System.out.print(new String(chars,0,len));
        }
    }
}

4.9 字符缓冲流特有功能

BufferedWriter:

  • void newLine():写一行行分隔符,行分隔符字符串由系统属性定义(根据不同系统自适应)

BufferedReager:

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

4.10 案例:复制Java文件(字符缓冲流特有功能改进版)

需求: 把模块目录下的 “ceshi.md” 复制到模块目录下的 “Copy.java”

思路:

  • 根据数据源创建字符缓冲输入流对象
  • 根据目的地创建字符缓冲输出流对象
  • 读写数据,复制文件
    • 使用字符缓冲流特有的功能实现
import java.io.*;

public class CopyDemo {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("arrays.md"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.md"));

        String s = null;
        while ((s=br.readLine())!=null){
            bw.write(s);
            bw.newLine();
            bw.flush();
        }

        br.close();
        bw.close();
    }
}

4.11 案例:集合到文件

需求: 把ArrayList集合中的字符串数据写入到文本文件。

要求: 每个字符串元素作为文件中的一行数据

思路:

  • 创建ArrayList集合
  • 往集合中存储字符串元素
  • 创建字符缓冲输出流对象
  • 遍历集合,得到每一个字符串数据
  • 调用字符缓冲数据流对象的方法写数据
import java.io.*;
import java.util.ArrayList;

public class ArrayListAndBufferedWriterDemo {
    public static void main(String[] args) throws IOException {
        ArrayList<String> list = new ArrayList<>();

        list.add("hello");
        list.add("world");
        list.add("java");

        BufferedWriter bw = new BufferedWriter(new FileWriter("list.txt"));

        for (String i :
                list) {
            bw.write(i);
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}

4.12 案例:点名器

需求: 我有一个文件里面存储了班级同学的姓名,每个姓名占一行,要求通过程序实现随机点名器

思路:

  • 创建字符缓冲输入流对象
  • 创建ArrayList集合对象
  • 调用字符缓冲输入流对象的方法读数据
  • 把读取到的字符串数据存储到集合中
  • 使用Random产生一个随机数,随机数范围在:[0,集合的长度)
  • 把第六步产生的随机数作为索引到ArrayList集合中获取值
  • 把第七步得到的数据输出在控制台
import java.io.*;
import java.util.ArrayList;
import java.util.Random;

public class CallNameDemo {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("names.txt"));
        ArrayList<String> list = new ArrayList<>();

        String line;
        while ((line=br.readLine())!=null){
            list.add(line);
        }
        br.close();

        Random random = new Random();
        int i = random.nextInt(list.size());

        String s = list.get(i);
        System.out.println(s);
    }
}

4.13 案例:集合到文件(改进版)

需求: 把ArrayList集合中的学生数据写入到文本文件。

要求: 每个学生对象的数据作为文件中的一行数据

格式: 学号,姓名,年龄,居住地

举例: 001,张三,25,济南

思路:

  • 定义学生类
  • 创建ArrayList集合
  • 创建学生对象
  • 把学生对象添加到集合中
  • 创建字符缓冲输出流对象
  • 遍历集合,得到每一个学生对象
  • 把学生对象的数据拼接成指定格式的字符串
  • 调用字符缓冲输出流对象的方法写数据
import java.io.*;
import java.util.ArrayList;

public class ArrayListToFileDemo {
    public static void main(String[] args) throws IOException {
        Student s1 = new Student("001", "张三", 18, "济南");
        Student s2 = new Student("002", "李四", 19, "德州");
        Student s3 = new Student("003", "王五", 23, "临邑");
        Student s4 = new Student("004", "赵六", 32, "商河");

        ArrayList<Student> list = new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);

        BufferedWriter bw = new BufferedWriter(new FileWriter("student.txt"));
        String str = null;
        for (Student s :
                list) {
            str = s.getSid() + "," + s.getName() + "," + s.getAge() + "," + s.getAddress();
            bw.write(str);
            bw.newLine();
        }
        bw.close();
    }
}

4.14 案例:文件到集合(改进版)

需求: 把文本中的数据读取到集合中,并遍历集合。

要求: 文件中每一行数据是一个学生对象的成员变量值

思路:

  • 定义一个学生类
  • 创建字符缓冲输入流对象
  • 创建ArrayList集合对象
  • 调用字符缓冲输入流对象的方法读数据
  • 把读取到的字符串数据用split()进行分割,得到一个字符串数组
  • 创建学生对象
  • 把字符数组中的每一个元素取出来对应的赋值给学生对象的成员变量值
  • 把学生对象添加到集合
import java.io.*;
import java.util.ArrayList;

public class FileToArrayListDemo {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("student.txt"));
        ArrayList<Student> list = new ArrayList<>();
        String line;
        while ((line=br.readLine())!=null){
            String[] split = line.split(",");
            Student student = new Student();
            student.setSid(split[0]);
            student.setName(split[1]);
            student.setAge(Integer.parseInt(split[2]));
            student.setAddress(split[3]);
            list.add(student);
        }
        
        br.close();
        
        for (Student s :
               list ) {
            System.out.println(s.getSid()+","+s.getName()+","+s.getAge()+","+s.getAddress());
        }
    }
}

4.15 案例:复制单级文件夹

需求: 把 “E:\Excel” 这个文件夹复制到模块目录下

思路:

  • 创建数据源目录对象,路径是 “E:\Excel”
  • 获取数据源目录File对象的名称(Excel)
  • 创建目的地目录File对象,路径名(Excel)
  • 判断目的地目录对应的File是否存在,如果不存在,就创建
  • 获取数据源目录下所有文件的File数组
  • 遍历File数据,得到每一个File对象,该File对象,其实就是数据源文件
  • 获取数据源文件File对象的名称
  • 创建目的地文件File对象,路径名是目的地目录+文件名 组成
  • 复制文件
    • 由于文件不仅仅是文本文件,还有图片,视频等文件,所以采用字节流
import java.io.*;

public class CopyFolderDemo {

    public static void main(String[] args) throws IOException {
        //创建数据源目录File对象,路径是 "E:\Excel"
        File srcFolder = new File("E:\\Excel");
        //获取数据源目录File对象的名称(最终目录名(Excel))
        String srcFolderName = srcFolder.getName();
        //创建目的地目录File对象,路径名是项目目录下的Excel
        File destFolder = new File(srcFolderName);
        //判断目的地对应的File是否存在,如果不存在就创建
        if (!destFolder.exists()) {
            destFolder.mkdir();
        }
        //获取数据源目录下的所有文件的File数组
        File[] listFiles = srcFolder.listFiles();
        //遍历File数组,得到每一个File对象,该File对象就是数据源文件
        for (File srcFile :
                listFiles) {
            if (srcFile.isFile()) {
                //获取数据源文件的File对象的名称
                String srcFileName = srcFile.getName();
                //创建目的地文件的File对象,路径名是目的地目录+文件名
                File destFile = new File(destFolder, srcFileName);
                //复制文件
                copyFile(srcFile, destFile);
            }
        }
    }

    private static void copyFile(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bis.close();
        bos.close();
    }
}

4.16 案例:复制多级文件夹

需求: 把 “E:\Excel” 这个文件夹复制到模块目录下

思路:

  • 创建数据源File对象,路径是E:\Excel
  • 创建目的地File对象,路径是项目路径
  • 些方法实现文件夹的复制,参数为数据源File对象和目的地File对象
  • 判断数据源File是否是目录
    • 是:
      • A:在目的地下创建和数据源File名称一样的目录
      • B:获取数据源File下所有文件或者目录的File数组
      • C:遍历该File数组,得到每一个File对象
      • D:把该File作为数据源File对象,递归调用复制文件夹的方法
    • 不是:说明这是个文件,直接复制,用字节流
import java.io.*;

public class CopyFoldersDemo {

    public static void main(String[] args) throws IOException {
        //创建数据源File对象,路径是E:\Excel
        File srcFolder = new File("E:\\Excel");
        //创建目的地File对象,路径是项目路径
        File destFolder = new File("copyDest");
        if (!destFolder.exists()){
            destFolder.mkdir();
        }

        copyFolder(srcFolder, destFolder);

    }
    //复制文件夹
    private static void copyFolder(File srcFolder, File destFolder) throws IOException {
        //判断数据源File是否是目录
        if (srcFolder.isDirectory()) {
            //在目的地下创建和数据源File名称一样的目录
            String srcFolderName = srcFolder.getName();
            File newFolder = new File(destFolder, srcFolderName);
            if (!newFolder.exists()) {
                newFolder.mkdir();
            }
            //获取数据源File下所有文件或者目录的File数组
            File[] fileArray = srcFolder.listFiles();
            //遍历该File数组,得到每一个File对象
            for (File file :
                    fileArray) {
                //把该File作为数据源File对象,递归调用复制文件夹的方法
                copyFolder(file, newFolder);
            }
        } else {
            //说明这是个文件,直接复制,用字节流
            File newFolder = new File(destFolder, srcFolder.getName());
            copyFile(srcFolder, newFolder);
        }
    }
    //复制文件
    private static void copyFile(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bis.close();
        bos.close();
    }
}

4.17 复制文件异常处理

在这里插入图片描述

五、IO流小结

在这里插入图片描述

在这里插入图片描述

六、特殊操作流

6.1 标准输入输出流

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

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

标准输入流

自己实现键盘录入数据:

  • BufferedReader br = new BufferedReader(new InputStreamBeader(System.in));

Java提供了一个类实现键盘录入

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

标准输出流

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

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

6.2 打印流

打印流分类:

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

打印流的特点:

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

字节打印流

  • PrintStream(String fileName):使用指定的文件名创建新的打印流
  • 使用继承父类的方法写数据,查看的时候会转码;使用自己特有的方法写数据,查看的数据原样输出
  • 特有方法:println() 和 print()
import java.io.*;
public class PtintStreamDemo{
    public static void main(String[] args)throws Exception{
        PrintStream ps = new PrintStream("bd.txt");
        ps.println(97);
        ps.print(98);
    }
}

字符打印流

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

方法名说明PrintWriter(String fileName)使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新PrintWriter(Writer out, boolean autoFlush)创建新的PrintWriter,out:字符输出流,aotoFlush:一个布尔值,如果为真,则println,printl,或format方法将刷新输出缓冲区

案例:复制Java文件(打印流改进版)

需求: 把模块目录下的 “ceshi.md” 复制到模块目录下的 “Copy.java”

思路:

  • 根据数据源创建字符输入流对象
  • 根据目的地创建字符输出流对象
  • 读写数据,复制文件
import java.io.*;
public class PrintWriterDemo{
    public static void main(String[] args)throws Exception{
        BufferedReader br = new BufferedReader(new FileReader("ceshi.md"));
        PrintWriter pw = new PrintWriter(new FileWriter("Copy.md"), true);

        String line;
        while ((line=br.readLine())!=null){
            pw.println(line);
        }
        pw.close();
        br.close();
    }
}

6.3 对象序列化流

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

这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息

字节序列写到文件之后,相当于文件中持久保存了一个对象的信息

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

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

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

对象序列化流:ObjectOutputStream

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

构造方法:

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

序列化对象的方法:

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

注意:

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

public class ObjectOutputStreamDemo {

    public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));

        Student student = new Student("001", "张三", 18, "济南");
        oos.writeObject(student);
        oos.close();
    }
}

class Student implements Serializable {
    ......
}

对象反序列化流:ObjectInputStream

对象反序列化流:ObjectInputStream

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

构造方法:

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

反序列化对象的方法:

  • Object readObject():从ObjectInputStream读取一个对象
import java.io.*;

public class CollectionDemo {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt"));
        Object object = ois.readObject();
        Student s = (Student)object;   		
        System.out.println(s.getSid()+","+s.getName()+","+s.getAge()+","+s.getAddress());
        ois.close();

    }
}

class Student implements Serializable {
    ......
}

对象序列化流的问题

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

    • 会出问题,抛出InvalidClassException异常
  • 如果出了问题,如何解决呢?

    • 给对象所属的类加一个serialVersionUID
      • private static final long serialVersionUID = 42L; (值可以改)
  • 如果一个对象中的某一个成员变量的值不想被序列化,又该如何实现呢?

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

6.4 Properties

Properties概述:

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

Properties作为Map集合使用:

import java.util.Properties;
import java.util.Set;

public class PropertiesDemo {
    public static void main(String[] args) {
        //创建一个人集合对象
        Properties properties = new Properties();
        properties.put("001","张三");
        properties.put("002","李四");
        properties.put("003","王五");

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

Properties作为集合的特有方法:

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

public class PropertiesDemo {
    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.setProperty("001","张三");
        properties.setProperty("002","李四");
        properties.setProperty("003","王五");

        System.out.println(properties);
        System.out.println(properties.getProperty("001"));
        Set<String> strings = properties.stringPropertyNames();
        for (String key :
                strings) {
            System.out.println(key+","+properties.getProperty(key));
        }
    }
}

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.FileWriter;
import java.io.IOException;
import java.util.Properties;

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

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

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

        FileReader fr = new FileReader("fw.txt");
        properties.load(fr);
        fr.close();

        System.out.println(properties);
    }

    private static void myStore() throws IOException {
        Properties properties = new Properties();
        properties.setProperty("001","张三");
        properties.setProperty("002","李四");
        properties.setProperty("003","王五");

        FileWriter fw = new FileWriter("fw.txt");
        properties.store(fw,null);
        fw.close();
    }
}

案例:游戏次数

需求: 请写程序实现猜数字小游戏只能试玩3此,如果还想玩,提示:游戏试玩结束,想玩请充值

思路:

  • 写一个游戏类,里面有一个猜数字的小游戏
  • 写一个测试类,测试类中有main()方法,main()方法中按照下面步骤完成:
    • A:从文件中读取数据到Properties集合,用load()方法
      • 文件已经存在:game.txt
      • 里面有一个数据值:count=0
    • B:通过Properties集合获取到玩游戏的次数
    • C:判断次数是否到3此了
      • 如果到了,给出提示:游戏试玩结束,想玩请去充值
      • 如果不到三次:
        • 次数加一,重新协会文件,用Properties的store()方法实现
import java.util.Random;
import java.util.Scanner;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class PropertiesTest {
    public static void main(String[] args) throws IOException {
        //从文件中读取数据到Properties集合,用load()方法
        Properties prop = new Properties();
        prop.load(new FileReader("game.txt"));

        String count = prop.getProperty("count");
        int number = Integer.parseInt(count);
        if (number>=3){
            System.out.println("游戏试玩结束,请充值后继续");
        }else {
            GuessNumber.start();
            number++;
            prop.setProperty("count",String.valueOf(number));
            prop.store(new FileWriter("game.txt"),null);
        }

    }
}

class GuessNumber {
    private GuessNumber(){

    }
    public static void start(){
        //要完成猜数字游戏,首先要有一个数字,使用随机数生成该数字,范围1到100
        Random random = new Random();
        int number = random.nextInt(100)+1;

        while (true){
            //实用程序实现猜数字,每次均要输入猜测的数字值,需要使用键盘录入实现
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入你要猜的数字:");
            int i = sc.nextInt();

            //比较输入的数字和系统产生的随机数,根据不同情况进行猜测结果显示
            if (i>number){
                System.out.println("你猜的数字大了");
            }else if (i<number){
                System.out.println("你猜的数字小了");
            }else {
                System.out.println("恭喜你猜中了");
                break;
            }
        }
    }
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK