18.IO流

1.File

1.1 File类的概述和构造方法

File:它是文件和目录路径名的抽象表示
● 文件和目录是可以通过File封装成对象的
● 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。
   他可以是存在的,也可以是不存在的。
在这里插入图片描述
构造方法

public class FileDemo {
    public static void main(String[] args) {

        //File(String pathname):通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
        File f1 = new File("E:\\javaFileTest\\java.txt");
        System.out.println(f1);

        //File(String parent, String child):从父路径名字符串和子路径名字符串创建新的 File实例。
        File f2 = new File("E:\\javaFileTest","java.txt");
        System.out.println(f2);

        //File(File parent, String chile):从父抽象路径名和子路径名字符串创建新的 File实例。
        File f3 = new File("E:\\javaFileTest");
        File f4 = new File(f3,"java.txt");
        System.out.println(f4);
    }
}

1.2 File类创建功能

在这里插入图片描述

public class FileDemo {
    public static void main(String[] args) throws IOException {
        //需求1:在E:\\javaFileTest目录下创建一个文件java.txt
        File f1 = new File("E:\\javaFileTest\\java.txt");
        System.out.println(f1.createNewFile());

        //需求2:在E:\\javaFileTest目录下创建一个目录JavaSE
        File f2 = new File("E:\\javaFileTest\\javaSE");
        System.out.println(f2.mkdir());

        //需求3:在E:\\javaFileTest目录下创建一个多级目录JavaWEB\\HTML
        File f3 = new File("E:\\javaFileTest\\javaWEB\\HTML");
        System.out.println(f3.mkdirs());

        //需求4:在E:\\javaFileTest目录下创建一个文件javase.txt
        File f4 = new File("E:\\javaFileTest\\javase\\javase.txt");
//        System.out.println(f4.mkdirs());  //只是创建了一个javase.txt为名字的文件夹
//        在创建一个文件试试
//        System.out.println(f4.createNewFile()); 仍然创建失败,因为即使是文件夹,只要和文件名称一样,就无法创建成功
    }
}

1.3 File类删除功能

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

 
绝对路径和相对路径的区别
● 绝对路径:完整的路径名,不需要任何其他信息就可以定位它所表示的文件。例如:E:\JavaFileTest\java.txt
● 相对路径:必须使用取自其他路径名的信息进行解释。例如:Article18\java.txt

 
删除目录时的注意事项:
● 如果一个目录中有内容(目录,文件),不能直接删除。应该先删除目录中的内容,最后才能删除目录

1.4 File类判断和获取功能

在这里插入图片描述

1.5 递归

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

案例1:递归求阶乘

案例2:遍历目录

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

思路:
① 根据给定的路径创建一个File对象
② 定义一个方法,用于获取给定目录下的所有内容,参数为第1步创建的File对象
③ 获取给定的File目录下所有的文件或者目录的File数组
④ 遍历该File数组,得到每一个File对象
⑤ 判断该File对象是否是目录
      是:递归调用
      不是:获取绝对路径输出在控制台
⑥ 调用方法

public class DiGuiDemo {
    public static void main(String[] args) {
        File f1 = new File("C:\\Users\\Tale\\Desktop\\Duyilin");
        bianli(f1);
    }

    public static void bianli(File file){
        //获取给定的File目录下所有的文件或者目录的File数组
        File[] fileArray = file.listFiles();
        //遍历该File数组,得到每一个File对象
        for(File f : fileArray){
            //判断该File对象是否是目录
            if(f.isDirectory()){
                //是,递归调用
                bianli(f);
            //不是,获取绝对路径输出在控制台
            }else{

//                System.out.println(f);
                System.out.println(f.getAbsolutePath());
            }
        }
    }
}

2.字节流

2.1 IO流概述和分类

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

 
IO流分类:
● 按照数据的流向
      输入流:读数据
      输出流:写数据

● 按照数据类型来分
      字节流
            字节输入流;字节输出流
      字符流
            字符输入流;字符输出流
 
 
一般来说,我们说IO流的分类是按照数据类型来分的
那么这两种流都在什么情况下使用呢?
● 如果数据通过Windows自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流,否则就是使用字节流。如果你不知道该使用哪种类型的流,就使用字节流

2.2 字节流写数据

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

 
FileOutputStream:文件输出流用于将数据写入File
● FileOutputStream(String name):创建文件输出流以指定的名称写入文件

使用字节输出流写数据的步骤:
● 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
● 调用字节输出流对象的写数据方法
● 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("Article18\\fos.txt");
        /*
            做了三件事情:
                A:调用系统功能创建了文件
                B:创建了字节输出流对象
                C:让字节输出流对象指向创建好的文件
        */

        //void write (int b):将指定的字节写入此文件输出流
        fos.write(97); //注意这里写进去的不是"97" ,而是对应asc编码97的a

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

2.3 字节流写数据的3种方式

在这里插入图片描述

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

字节流写数据如何实现换行呢?
● 写完数据后,加换行符
    windows:\r\n
    linux:\n
    mac:\r

 
字节流写数据如何实现追加写入呢?
● public FileOutputStream(String name,boolean append)
● 创建文件输出流以指定的名称写入文件。如果第二个参数为true,则字节将写入文件的末尾而不是开头

2.5 字节流写数据加异常处理

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

try{
	可能出现异常的代码;
} catch(异常类名 变量名){
	异常的处理代码;
} finally{
	执行所有清除操作;
}
public class FileOutputStreamDemo {
    public static void main(String[] args) {
/*
        //前几个例子中,我们都是通过抛出来解决异常的,这次试用try...catch来解决
        try {
            FileOutputStream fos = new FileOutputStream("Article18\\src\\day8\\io12_2_5\\fos.txt");
            fos.write("hello".getBytes()); //如果在这一步出现了IOException,那么fos.close()就会不被执行到。
            //为了保证资源一定能被释放,Java提供了关键字finally
            fos.close();
        } catch (IOException e ){
            e.printStackTrace();
        }*/

        //加入加入finally来实现释放资源
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("Article18\\src\\day8\\io12_2_5\\fos.txt");
            fos.write("hello".getBytes());
        } catch (IOException e ){
            e.printStackTrace();
        } finally {
            if(fos!=null) {  //当创建File对象失败时,fos本就为空,进行fos.close()释放内存会报错空指针异常,所以加一个判断
                try {
                    fos.close();  //这里的fos变红报错,这是因为在try里new的fos只能在try里看到
                } catch (IOException e) {
                    e.printStackTrace();
                }`在这里插入代码片`
            }
        }
    }
}

2.6 字节流读数据

2.6.1 字节流读数据(一次读一个字节数据)

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

FileInputStream:从文件系统中的文件获取输入字节
● FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名

使用字节输入流读数据的步骤:
① 创建字节输入流对象
② 调用字节输入流对象的读数据方法
③ 释放资源

FileInputStream fis = new FileInputStream("Article18\\src\\day8\\io13_2_6\\fos.txt");


/*        //调用字节输入流对象的读数据方法
//int read (): 从该输入流读取一个字节的数据
//第一次读取数据
int by = fis.read();
System.out.println(by);
System.out.println((char)by);
//第二次读取数据
by = fis.read();
System.out.println(by);
System.out.println((char)by);*/
//再读取一次
/* by = fis.read();
System.out.println(by);  //如果达到文件末尾,返回-1
System.out.println((char)by); */
//借由循环读取
int by;
while((by=fis.read()) != -1){
    System.out.print((char)by);
}
//释放资源
fis.close();

案例3:复制文本文件

需求:把"E:\JavaFileTest\priest.txt" 复制到模块目录下的"priest.txt"

分析:
① 复制文本文件,其实就是把文本文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
② 数据源:
      E:\JavaFileTest\priest.txt —读数据— InputStream — FileInputStream
③ 目的地:
      Article18\priest.txt —写数据— OutputStream — FileOutputStream

思路:
① 根据数据源创建字节输入流对象
② 根据目的地创建字节输出流对象
③ 读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)
④ 释放资源

public class Copy {
    public static void main(String[] args) throws IOException {
        //根据数据源创建字节输入流对象
        FileInputStream fis = new FileInputStream("E:\\JavaFileTest\\priest.txt");
        File f1 = new File("Article18\\src\\day8\\case3\\priest.txt");
        f1.createNewFile();
        FileOutputStream fos = new FileOutputStream(f1);
        int by;
        while((by=fis.read()) !=-1){
            fos.write(by);
        }
        fis.close();
        fos.close();
    }
}

2.6.2 字节流读数据(一次读一个字节数组数据)

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

使用字节输入流读数据的步骤:
① 创建字节输入流对象
② 调用字节输入流对象的读数据方法
③ 释放资源

        byte[] bys = new byte[1024]; //这里给的byte数组长度一般为1024的整数倍
        int len;
        while((len=fis.read(bys)) != -1){
            System.out.println(new String (bys,0,len));
        }

具体思路代码在对应的包里

案例4:复制图片

需求:把"E:\JavaFileTest\Timeless.jpg" 复制到case4目录下的"Timeless.jpg"

思路:
① 根据数据源创建字节输入流对象
② 根据目的地创建字节输出流对象
③ 读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
④ 释放资源

public class CopyPicture {
    public static void main(String[] args) throws IOException {
        File f = new File("Article18\\src\\day8\\case4\\Timeless.jpg");
        f.createNewFile();
        FileOutputStream fos = new FileOutputStream(f);
        FileInputStream fis = new FileInputStream("C:\\Users\\Tale\\Desktop\\Duyilin\\乌克兰插画师Alena Aenami\\Timeless.jpg");

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

3.字符流

case:复制Java文件(字符缓冲流改进版)

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

public class CopyJavaFile {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("myCharStream\\src\\day9\\BufferedStreamDemo.java"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Copy.java"));

/*        int ch;
        while((ch=br.read())!=-1){
            bw.write(ch);
        }*/
        char[] chs = new char[1024];
        int len;
        while((len=br.read(chs))!=-1){
            bw.write(chs,0,len);
        }
        br.close();
        bw.close();
    }
}

3.8 字符缓冲流特有功能

BufferedWriter:
● void newLine():写一行行分隔符,行分隔符字符串由系统属性定义

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

public class BufferedStreamDemo2 {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));
        BufferedReader br = new BufferedReader(new FileReader("myCharStream\\Copy.java"));

        for(int i=0;i<10;i++){
            bw.write("Hello" +i);
//            bw.write("\r\n");
            bw.newLine();
        }
        String line;
        while((line=br.readLine()) != null ){
            System.out.println(line);
        }

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

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

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

读写数据,复制文件使用字符缓冲流特有功能实现

public class CopyJavaFile1 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("myCharStream\\src\\day9\\BufferedStreamDemo.java"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Copy.java"));

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

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

3.9 IO流小结

在这里插入图片描述

case:集合到文件

需求:把ArrayList集合中的字符串数据写入到文本文件。要求:每一个字符串元素作为文件中的一行数据

思路:
① 创建ArrayList集合
② 往集合中存储字符串元素
③ 创建字符缓冲输出流对象
④ 遍历集合,得到每一个字符串数据
⑤ 调用字符缓冲输出流对象的方法写数据
⑥ 释放资源

public class ArrayListToFile {
    public static void main(String[] args) throws IOException {
        ArrayList<String> array = new ArrayList<String>();
        array.add("Rarity");
        array.add("Twilight Sparkle");
        array.add("Rainbow Dash");
        array.add("Flutter Shy");
        array.add("Apple Jack");
        array.add("Pinkie Pie");
        array.add("Starlight Glimmer");
        array.add("Sunset Shimmer");

        BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));
/*        for(int i=0;i<array.size();i++){
            bw.write(array.get(i));
            bw.newLine();
        }*/
 /*       Iterator<String> it = array.iterator();
        while(it.hasNext()){
            bw.write(it.next());
            bw.newLine();
        }*/
        for(String s : array){
            bw.write(s);
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}

case:文件到集合

需求:把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个集合元素

case:点名器

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

思路:
① 把文本文件中的数据读取到集合中
② 使用Random产生一个随机数,随机数的范围在 [0,集合长度]

case:集合到文件(改进版)

需求:把ArrayList集合中的学生数据写入到文本文件。要求:每一个学生对象的数据作为文件中的一行数据
       格式:学号,姓名,年龄,居住地         举例:Pony 001,Rarity,15,Pony Valley

思路:
① 定义学生类
② 创建ArrayList集合
③ 创建学生对象
④ 把学生对象添加到集合中
⑤ 创建字符缓冲输入流对象
⑥ 遍历集合,得到每一个学生对象
⑦ 把学生对象的数据拼接成指定格式的字符串
⑧ 调用字符缓冲输出流对象的方法写数据
⑨ 释放资源

public class ArrayListToFile {
    public static void main(String[] args) throws IOException {
        Student s1 = new Student("Pony 001","Rarity",15,"Ponyville");
        Student s2 = new Student("Pony 002","Twilight Sparkle",14,"Canterlot");
        Student s3 = new Student("Pony 003","Rainbow Dash",15,"Ponyville");
        Student s4 = new Student("Pony 004","Flutter Shy",16,"Ponyville");
        Student s5 = new Student("Pony 005","Apple Jack",16,"Ponyville");
        Student s6 = new Student("Pony 006","Pinkie Pie",16,"Ponyville");
        Student s7 = new Student("Pony 007","Starlight Glimmer",17,"Ponyville");
        Student s8 = new Student("Pony 008","Sunset Shimmer",19,"Canterlot");

        ArrayList<Student> arrayList = new ArrayList<Student>();
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        arrayList.add(s4);
        arrayList.add(s5);
        arrayList.add(s6);
        arrayList.add(s7);
        arrayList.add(s8);

        BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\pony.txt"));
        for(Student s : arrayList){
//            String str = s.getId()+","+s.getName()+","+s.getAge()+","+s.getAddress();
            StringBuilder sb = new StringBuilder();
            sb.append(s.getId()).append(",").append(s.getName()).append(",").append(s.getAge()).append(",").append(s.getAddress());
            bw.write(sb.toString());
            bw.newLine();
        }
        bw.close();
    }
}

case:文件到集合(改进版)

在这里插入图片描述

public class FileToArrayList {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("myCharStream\\pony.txt"));
        ArrayList<Student> array = new ArrayList<Student>();

        String s;
        while((s=br.readLine())!=null){
            String[] s1 = s.split(",");
            Student student = new Student(s1[0],s1[1],Integer.parseInt(s1[2]),s1[3]);
            array.add(student);
        }

        br.close();

        for(Student s0 : array){
            System.out.println(s0.getId()+","+s0.getName()+","+s0.getAge()+","+s0.getAddress());
        }
    }
}

case:集合到文件(数据排序改进版)

在这里插入图片描述

public class ArrayListToFileSort {
    public static void main(String[] args) throws IOException {
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num = s2.getSum()-s1.getSum();
                int num1 = num ==0 ? s1.getName().compareTo(s2.getName()) : num;
                return num1;
            }
        });

        for(int i=0;i<3;i++){
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入第"+ (i+1)+"位学生信息:" );
            System.out.println("姓名:");
            String name = sc.nextLine();
            System.out.println("语文成绩:");
            int chinese = sc.nextInt();
            System.out.println("数学成绩:");
            int math = sc.nextInt();
            System.out.println("英语成绩:");
            int english = sc.nextInt();
            Student s = new Student(name,chinese,math,english);
            ts.add(s);
        }

        BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\ponyGrade.txt"));
        for(Student s : ts){
            StringBuilder sb = new StringBuilder();
            sb.append(s.getName()+","+s.getChinese() +","+s.getMath()+","+s.getEnglish());
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}

case:复制单级文件

单级文件夹,即只含有文件不含有文件夹的文件夹在这里插入图片描述

public class CopyFileFolder1 {
    public static void main(String[] args) throws IOException{
        //创建数据源目录File对象,路径是E:\\IT
        File srcFolder = new File("E:\\IT");
        //获取数据源目录File对象的名称
        String srcFolderName = srcFolder.getName();

        //创建目的地目录File对象,路径名是模块名+IT组成(myCharStream\\IT)
        File destFolder = new File("myCharStream",srcFolderName);

        //判断目的地目录对应的File是否存在,如果不存在,就创建
        if(!destFolder.exists()){
            destFolder.mkdir();
        }

        //获取数据源目录下所有文件的File数组
        File[] listFiles = srcFolder.listFiles();

        //遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件
        for(File srcFile : listFiles){
            //数据源文件:E:\\IT\\70.jpg
            //获取数据源文件File对象的名称(70.jpg)
            String srcFileName = srcFile.getName();
            //创建目的地文件File对象,路径名是目的地目录+70.jpg组成(myCharStream\\IT\\70.jpg)
            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[] bys = new byte[1024];
        int len;
        while((len = bis.read(bys))!=-1){
            bos.write(bys,0,len);
        }
        bis.close();
        bos.close();

    }
}

case:复制多级文件夹

需求:把"E:\IT"复制到模块目录下

自写版本

    public static void copyFolder(File srcFolder, File destFolder) throws IOException {
        //在目的地下创建和数据源File名称一样的目录
        destFolder = new File(destFolder, srcFolder.getName());
        if (!destFolder.exists()) {
            destFolder.mkdir();
        }

        //获取数据源File下所有文件或者目录的File数组
        File[] files = srcFolder.listFiles();

        //遍历该File数组,得到每一个File对象
        for (File file : files) {
            if (file.isDirectory()) {
                String folderName = file.getName();
                File newSrcFolder = new File(srcFolder, folderName);
                copyFolder(newSrcFolder, destFolder);
            } else {
                String fileName = file.getName();
                File destFile = new File(destFolder,fileName);
                copyFile(file,destFile);
            }
        }
    }

教学版本

    private static void copyFolder(File srcFile, File destFile) throws IOException{
        //判断数据源File是否是目录
        if(srcFile.isDirectory()){
            //在目的地下创建和数据源File名称一样的目录
            String srcFileName = srcFile.getName();
            File newFolder = new File(destFile,srcFileName);
            if(!newFolder.exists()){
                newFolder.mkdir();
            }
            //获取数据源File下所有文件或者目录的File数组
            File[] files = srcFile.listFiles();

            //遍历该数组,得到每一个File对象
            for(File file : files){
                copyFolder(file,newFolder);
            }
        }else{
            //说明是文件,直接复制,用字节流
            File newFile = new File(destFile,srcFile.getName());
            copyFile(srcFile,newFile);
        }

    }

3.10 复制文件的异常处理

在这里插入图片描述
这几种之间,JDK7的最好,因为不会抛出,代码还较为简洁

    //JDK9改进方案
    public static void method4() throws IOException{
        FileReader fr = new FileReader("fr.txt");
        FileWriter fw = new FileWriter("fw.txt");
        try(fr;fw) {
            char[] chs = new char[1024];
            int len;
            while ((len = fr.read()) != -1) {
                fw.write(chs, 0, len);
            }
        } catch (IOException e){
            e.printStackTrace();
        }
    }

    //JDK7改进方案:
    public static void method3() {
        try(FileReader fr = new FileReader("fr.txt");
            FileWriter fw = new FileWriter("fw.txt");) {
            char[] chs = new char[1024];
            int len;
            while ((len = fr.read()) != -1) {
                fw.write(chs, 0, len);
            }
        } catch (IOException e){
            e.printStackTrace();
        }
    }

    //try...catch...finally
    public static void method2() {
        FileWriter fw = null;
        FileReader fr = null;
        try {
            fr = new FileReader("fr.txt");
            fw = new FileWriter("fw.txt");
            char[] chs = new char[1024];
            int len;
            while ((len = fr.read()) != -1) {
                fw.write(chs, 0, len);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(fw != null) {
                try {
                    fw.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if(fr!=null){
                try{
                    fr.close();
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }

    //抛出处理
    public static void method1() throws IOException {
        FileReader fr = new FileReader("fr.txt");
        FileWriter fw = new FileWriter("fw.txt");

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

4.特殊操作流

4.1 标准输入输出流

System类中有两个静态的成员变量:
● public static final InputStream in :标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源
● public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标

4.1.1 标准输入流

自己实现键盘录入数据:
● BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

写起来过于麻烦,而且是字符流,输入整数时还得使用Integer.parseInt方法
所以Java就提供了一个类实现键盘录入

●Scanner sc = new Scanner(System.in) 可以看出 其本质还是对System.in进行包装

public class SystemInDemo {
    public static void main(String[] args) throws IOException {
/*        //public static final InputStream in:标准输入流
        InputStream is = System.in;  //System.in输入的是字节流
*//*        int by;
        while ((by = is.read())!=-1){
            System.out.print((char)by);
        }*//*
        //使用字节流在控制台输入中文会发生乱码,所以我们使用字符流
        //如何把字节流转换为字符流? 用转换流
        InputStreamReader isr = new InputStreamReader(is);

        //使用字符流能不能实现一次读取一行数据呢?
        //一次读取一行是字符缓存流特有的方法
        BufferedReader br = new BufferedReader(isr);*/


        //InputStream is = System.in;
        //InputStreamReader isr = new InputStreamReader(is);
        //BufferedReader br = new BufferedReader(isr);
        //这三步可以精简为一步
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        System.out.println("请输入一个字符:");
        String line = br.readLine();
        System.out.println("你输入的字符为:" + line);

        System.out.println("请输入一个整数:");  //由于是字符流,需要调用特别方法来获取整数
        int i = Integer.parseInt(br.readLine());
        System.out.println(i);
        br.close();

        //以上是自己实现键盘录入数据,太麻烦了,所以Java就提供了一个类供我们使用

        Scanner sc = new Scanner(System.in);

    }
}

4.1.1 标准输出流

输出语句的本质:是一个标准的输出流
● PrintStream ps = System.out;
● PrintStream类有的方法,System.out都可以使用

通过代码发现,这就是我们一直使用的System.out.println();

public class SystemOutDemo {
    public static void main(String[] args) {
        // public static final PrintStream out:标准输出流
        PrintStream ps = System.out;   //System.out的本质时一个字节输出流

        //能够方便地打印各种数据值
        ps.print("hello");
        ps.println(100);
        ps.println("hello");
        ps.println(100);
        System.out.println();
//        System.out.print(); //不带ln的不能没有参数
    }
}

4.2 打印流

打印流分类:
● 字节打印流:PrintStream
● 字符打印流:PrintWriter

打印流的特点:
● 只负责输出数据,不负责读取护具
● 有自己的特有方法

4.2.1 字节打印流

● PrintStream(String fileName):使用指定的的文件名创建新的打印流
● 使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出

public class PrintStreamDemo {
    public static void main(String[] args) throws IOException {
        PrintStream ps = new PrintStream("myCharStream\\ps.txt");

        //写数据
        //字节输出流有的方法
        ps.write(97);  //输出a

        //使用特有方法写数据,与write不同的是,参数是97那么写进去的就是97,不会进行转码
        ps.print(97);  //输出97
        ps.println(98); //输出97后换行
        ps.print(99);
        ps.close();
    }
}

4.2.2 字符打印流

字符打印流PrintWriter的构造方法:
在这里插入图片描述

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

需求:把myCharStream\src\day9\copyFileFolder\CopyFileFolder3.java 复制到模块目录下的Copy.java

思路:
① 根据数据源创建字符输入流对象
② 根据目的地创建字符输出流对象
③ 读写数据,复制文件
④ 释放资源

public class CopyJavaFile {
    public static void main(String[] args) throws IOException {
        //使用打印流进行改进
        BufferedReader br = new BufferedReader(new FileReader("myCharStream\\src\\day9\\copyFileFolder\\CopyFileFolder3.java"));
        PrintWriter pw = new PrintWriter(new FileWriter("myCharStream\\Copy.java"),true);

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


        /*  未改进版本
         //创建字符输入流对象
        BufferedReader br = new BufferedReader(new FileReader("myCharStream\\src\\day9\\copyFileFolder\\CopyFileFolder3.java"));
        //创建字符输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Copy.java"));

        //读写数据,复制文件
        String line;
        while((line = br.readLine())!=null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

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

4.3 对象序列化和反序列化流

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

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

要实现序列化和反序列化就要使用对象序列化流和对象反序列化流:
1.对象序列化流:ObjectOutputStream
2.对象反序列化流:ObjectInputStream

4.3.1 对象序列化流

对象序列化流:ObjectOutputStream

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

构造方法:
     ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream

序列化对象的方法:
    void writeObject(Object obj):将指定的对象写入ObjectOutputStream

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

public class Student implements Serializable {
    private String name;
    private int age;
    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; }
    public Student() { }
}

NotSerializableException 抛出一个实例需要一个Serializable接口。 序列化运行时或实例的类可能会抛出此异常。 参数应该是类的名称。

    public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
        Student s1 = new Student("Rarity",15);
        //void writeObject (Object obj):将指定的对象写入ObjectOutputStream
        oos.writeObject(s1);
        oos.close();
    }
}

4.3.2 对象反序列化流

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

构造方法:
    ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjInputStream

反序列化对象的方法:
    Object readObject():从ObjectInputStream读取一个对象

public class ObjectInputStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //ObjectInputStream (InputStream in):创建从指定的InputStream读取的ObjectInputStream
        ObjectInputStream  ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
        //Object readObject():从ObjectInputStream读取一个对象
        Object obj = ois.readObject();
        Student s = (Student) obj;
        System.out.println(s.getName()+","+s.getAge());
        ois.close();
    }
}

4.3.3 对象序列化相关问题

用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
     会出问题,抛出InvalidClassException异常
        当序列化运行时检测到类中的以下问题之一时抛出。
            类的串行版本与从流中读取的类描述符的类型不匹配
            该类包含位置的数据类型
            该类没有可访问的无参数构造函数

如果出问题了,如何解决呢?
    给对象所属的类加一个serialVersionUID
        private static final long serialVersionUID = 42L;

如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
    给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
//        write();
        read();
    }

    //反序列化
    private static void read() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
        Object obj = ois.readObject();
        Student s = (Student) obj;
        System.out.println(s.getName() + "," + s.getAge());
        ois.close();
    }
    //序列化
    private static void write() throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
        Student s1 = new Student("Rarity", 15);
        //void writeObject (Object obj):将指定的对象写入ObjectOutputStream
        oos.writeObject(s1);
        oos.close();
    }
}
public class Student implements Serializable {
    private static final long serialVersionUID = 42L;
    private String name;
    private transient int age;
    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; }
    public Student() { }

}

4.4 Properties

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

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

public class PropertiesDemo {
    public static void main(String[] args) {
//        Properties<String,String> prop = new Properties<String,String>(); 这种写法是错误的
        Properties prop = new Properties();

        //存储元素
        prop.put("Pony01","Rarity");
        prop.put("Pony02","Twilight");
        prop.put("Pony03","Starlight");

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

Properties作为集合的特有方法:
在这里插入图片描述

public class PropertiesDemo2 {
    public static void main(String[] args) {
        //创建集合对象
        Properties prop = new Properties();

        //Object setProperties(String key, String value):设置集合的键和值,都是String类型,底层调用Hashtable方法put
        prop.setProperty("Pony 001","Rarity");
        prop.setProperty("Pony 002","TwilightSparkle");
        prop.setProperty("Pony 003","SunsetShimmer");


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

        //Set<String> stringPropertyNames():从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串

        Set<String> names = prop.stringPropertyNames();
        System.out.println(names);

        //使用以上两种方法可以实现遍历properties集合
        for(String key : names){
            String value = prop.getProperty(key);
            System.out.println(key + "," + value);
        }
    }
}

Properties和IO流结合的方法:
在这里插入图片描述

public class PropertiesDemo3 {
    public static void main(String[] args) throws IOException{
        myStore();
        myLoad();
    }

    private static void myLoad() throws IOException{
        Properties prop = new Properties();
        FileReader fr = new FileReader("myOtherStream\\fw.txt");
        prop.load(fr);
        System.out.println(prop);
    }

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

        prop.setProperty("Pony001","Rarity");
        prop.setProperty("Pony002","Twilight Sparkle");
        prop.setProperty("Pony003","Sunset Shimmer");

        FileWriter fw = new FileWriter("myOtherStream\\fw.txt");
        prop.store(fw,"小马国女孩");
        fw.close();

    }
}

case:游戏次数

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

思路
① 写一个游戏类,里面有一个猜数字的小游戏
② 写一个测试类,测试类中有main()方法,main()方法中按照下面步骤完成:
    A:从文件中读取数据到Properties集合,用load()方法实现
        文件已经存在:game.txt
        里面有一个数据值:count = 0
    B:通过properties集合获取到玩游戏的次数
    C:判断次数是否到3次了
        如果到了,给出提示
        如果不到3次:
            玩游戏
            次数+1,重新写回文件,用Properties的store()方法实现

Java学生管理系统中的IO流主要用于读取和写入学生信息到文件中。下面是一个简单的Java学生管理系统的示例代码,演示了如何使用IO流进行学生信息的读取和写入: ```java import java.io.*; import java.util.ArrayList; import java.util.List; // 学生类 class Student implements Serializable { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } // 学生管理系统类 class StudentManagementSystem { private List<Student> students; public StudentManagementSystem() { students = new ArrayList<>(); } // 添加学生信息 public void addStudent(Student student) { students.add(student); } // 保存学生信息到文件 public void saveToFile(String filename) { try { FileOutputStream fileOut = new FileOutputStream(filename); ObjectOutputStream objectOut = new ObjectOutputStream(fileOut); objectOut.writeObject(students); objectOut.close(); fileOut.close(); System.out.println("学生信息已保存到文件:" + filename); } catch (IOException e) { e.printStackTrace(); } } // 从文件中读取学生信息 public void loadFromFile(String filename) { try { FileInputStream fileIn = new FileInputStream(filename); ObjectInputStream objectIn = new ObjectInputStream(fileIn); students = (List<Student>) objectIn.readObject(); objectIn.close(); fileIn.close(); System.out.println("从文件中读取到的学生信息:"); for (Student student : students) { System.out.println("姓名:" + student.getName() + ",年龄:" + student.getAge()); } } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } } // 测试类 public class Main { public static void main(String[] args) { StudentManagementSystem system = new StudentManagementSystem(); // 添加学生信息 system.addStudent(new Student("张三", 18)); system.addStudent(new Student("李四", 20)); system.addStudent(new Student("王五", 22)); // 保存学生信息到文件 system.saveToFile("students.dat"); // 从文件中读取学生信息 system.loadFromFile("students.dat"); } } ``` 运行结果: ``` 学生信息已保存到文件:students.dat 从文件中读取到的学生信息: 姓名:张三,年龄:18 姓名:李四,年龄:20 姓名:王五,年龄:22 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值