第十五章、 Io流

第十五章、 Io流

15.1 文件

15.1.1 文件的基本使用

  1. 什么是文件

    文件是保存数据的地方,比如:我们经常使用的word文档、txt文件、Excel文件......等等。它即可以是保存一张图片,也可以保存视频和音频等等。

  2. 文件流

    文件在程序中是以流的形式来操作的

流:数据在数据源(文件)和程序(内存)之间经历的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源的路径

15.1.2 常用的文件操作

  1. 创建文件对象相关构造器和方法
    (1). new File(String pathname) //根据路径构建一个File对象
    (2). new File(File parent ,String child) //根据父目录文件+子路径构建一个File对象
    (3). new File(String parent ,String child) //根据父目录+子路径构建一个File对象
    (4). createNewFile 创建新文件
  1. 案例演示 FileCreate.java

请在e盘下,创建文件news1.txt 、news2.txt、news3.txt,用三种不同的方式创建

java 复制代码
package chapter15.file_;

import org.junit.jupiter.api.Test;

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

/**
 * @aim java基础学习
 * @note java笔记
 * 演示创建文件
 */
public class CreateFile_ {
    public static void main(String[] args) {

    }

    // new File(String pathname)  //根据路径构建一个File对象
    @Test
    public void create01(){
        String filePath = "e:\\news1.txt";
        File file = new File(filePath);//只是在内存中创建file这个对象
        try {
            file.createNewFile();//当这个对象执行createNewFile方法时,才会真正在硬盘上创建文件
            System.out.println("文件1创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //new File(File parent ,String child)  //根据父目录文件+子路径构建一个File对象
    // e:\\news2.txt
    @Test
    public void create02(){
        File parentFile = new File("e:\\");
        String fileName = "news2.txt";
        File file = new File(parentFile, fileName);
        try {
            file.createNewFile();
            System.out.println("文件2创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // new File(String parent ,String child)  //根据父目录+子路径构建一个File对象
    @Test
    public void create03(){
        String parentPath = "e:\\";
        String filePath = "news3.txt";
        File file = new File(parentPath, filePath);
        try {
            file.createNewFile();
            System.out.println("文件3创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  1. 获取文件的相关信息
    getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
  1. 案例演示 FileInformation.java

如何获取到文件的大小、文件名、路径,父File,是文件还是目录(目录本质也是文佳,一种特殊的文件),是否存在。

java 复制代码
package chapter15.file_;

import org.junit.jupiter.api.Test;

import java.io.File;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class FileInformation {
    public static void main(String[] args) {

    }
    //获取文件信息
    @Test
    public void info(){
        //创建文件对象,在文件中加入了一些文字(Utf=8):hello你好。(英文一个字节,中文3个字节)
        File file = new File("e:\\news3.txt");
        
        //getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
        System.out.println("文件名字="+file.getName());//文件名字=news3.txt
        System.out.println("文件绝对路径="+file.getAbsolutePath());//文件绝对路径=e:\news3.txt
        System.out.println("文件父级目录="+file.getParent());//文件父级目录=e:\
        System.out.println("文件大小(字节)="+file.length());//文件大小(字节)=14
        System.out.println("文件是否存在="+file.exists());//文件是否存在=true
        System.out.println("是不是一个文件="+file.isFile());//是不是一个文件=true
        System.out.println("是不是一个目录="+file.isDirectory());//是不是一个目录=false
    }
}
  1. 目录的操作和文件删除
    mkdir创建一级目录、mkdir创建多级目录、delete删除空目录或文件
  2. 案例演示
    (1). 判断e:\news1.txt 是否存在,如果存在就删除
    (2). 判断e:\demo02 是否存在,存在就删除,否则提示不存在
    (3). 判断e:\demo\a\b\c目录是否存在,如果存在就提示已经存在,否则就创建
java 复制代码
package chapter15.file_;

import org.junit.jupiter.api.Test;

import java.io.File;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class Directory_ {
    public static void main(String[] args) {

    }

    //判断e:\\news1.txt 是否存在,如果存在就删除
    @Test
    public void m1(){
        String filePath = "e:\\news1.txt";
        File file = new File(filePath);
        if (file.exists()){
            if (file.delete()){
                System.out.println("删除成功...");;
            }else{
                System.out.println("删除失败...");
            }
        }else{
            System.out.println("文件不存在...");
        }
    }
    //判断e:\\demo02 是否存在,存在就删除,否则提示不存在
    //这里需要体会到,在Java编程中,目录也被当做文件
    @Test
    public void m2(){
        String filePath = "e:\\demo02";
        File file = new File(filePath);
        if (file.exists()){
            if (file.delete()){
                System.out.println("删除成功...");;
            }else{
                System.out.println("删除失败...");
            }
        }else{
            System.out.println("目录不存在...");
        }
    }
    //判断e:\\demo\a\b\c目录是否存在,如果存在就提示已经存在,否则就创建
    @Test
    public void m3(){
        String directoryPath = "e:\\demo\\a\\b\\c";
        File file = new File(directoryPath);
        if (file.exists()){
            System.out.println("目录已存在...");
        }else{
            if (file.mkdirs()) {
                System.out.println("目录创建成功...");
            }else{
                System.out.println("目录创建失败...");
            }
        }
    }
}

IO流原理及流的分类

15.2.1 Java IO流原理

  1. I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如:读/写文件,网络通迅等。
  2. Java程序中,对于数据的输入和输出操作以"流(Stream)"的方式进行
  3. Java.io包下提供了各种"流"类和接口,用以获取不同种类的数据,并通过方法输入或输出数据。
  4. 输入Input:读入外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
  5. 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

15.2.2 流的分类

  1. 按操作数据单位不同分为:字节流(8bit),字节流(按字符)
  2. 按数据流的流向不同分为:输出流、输入流
  3. 按流的角色的不同分为:节点流、处理流/包装流
  4. 字节流操作二进制文件(图片等)基本是无损的,字符流操作文本文件,效率更高。
(抽象基类) 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

(1). Java的IO流工涉及40多个类,实际上非常规则,都是从如下四个抽象基类派生出来的。

(2). 有这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

15.2.3 IO流体系图-常用的类

  1. IO流体系图
  1. 文件 Vs 流

在Java中,文件是存储在磁盘上的数据集合,而流则是处理这些数据的方式。文件用于保存数据,流则用于读写数据。流分为输入流(如FileInputStream)和输出流(如FileOutputStream),通过这些流可以读取文件内容或将数据写入文件。

  1. InputStream:字节输入流

    (1). InputStream抽象类是所有类字节输入流的超类

    (2). InputStream常用的子类

    ① FileInputStream:文件输入流

    ② BufferedInputStream:缓冲字节输入流

    ③ ObjectInputStream:对象字节输入流

  2. FileInputStream介绍

    (1). 查看API文档


(2). FileInputStream案例:要求使用FileInputStream读取hello.txt 文件,并将文件内容显示到控制台

java 复制代码
package chapter15.inputstream_;

import org.junit.jupiter.api.Test;

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

/**
 * @aim java基础学习
 * @note java笔记
 * 演示FileInputStream的使用(字节输入流 文件 ---> 程序)
 */
public class FileInputStream_ {
    public static void main(String[] args) {

    }

    /**
     * 演示读取文件
     * read()单个字节的读取,效率比较低
     * ->使用read(byte [] b)
     */
    @Test
    public void readFile01(){
        String filePath="D:\\programjava\\testTile\\chapter15\\hello.txt";
        FileInputStream fileInputStream=null;
        int readData = 0;
        try {
            //从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
            //数据的下一个字节,如果达到文件的末尾, -1 。
            fileInputStream = new FileInputStream(filePath);
            while ((readData =fileInputStream.read()) != -1){
                //这个字符得是1个字节,不然是其他字符(中文)读取时很大可能会出现乱码,因为read()是单个字节读取的
                System.out.print((char)readData);//转成char显示
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭文件流,释放资源
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 使用read(byte [] b) 读取文件,提高效率
     */
    @Test
    public void readFile02(){
        String filePath="D:\\programjava\\testTile\\chapter15\\hello.txt";
        FileInputStream fileInputStream=null;
        int readLen = 0;
        byte[] buf = new byte[8]; //定义字节数组,一次读取8个字节,一个字节对应一个索引位置
        try {
            //从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
            //如果返回-1,表示读取完毕,
            //返回:读入缓冲区(字节数组)的总字节数。(切记是读入)
            fileInputStream = new FileInputStream(filePath);
            while ((readLen = fileInputStream.read(buf)) != -1){
                System.out.print(new String(buf,0,readLen));//转成char显示
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭文件流,释放资源
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
}
  1. FileOutputStream介绍
  2. 查看API文档
    在这里插入代码片
  1. FileOutputStream案例1:要求使用FileOutputStream在a.txt文件中,写入"hello,world.",如果文件不存在,会自动创建文件(注意:前提是目录已经存在)
java 复制代码
package chapter15.outputstream_;

import org.junit.jupiter.api.Test;

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

/**
 * @aim java基础学习
 * @note java笔记
 */
public class FileOutputStream_ {
    public static void main(String[] args) {

    }

    /**
     *演示使用FIleOutputStream 将数据写入到文件中,如果该文件不存在,则创建文件
     */
    @Test
    public void writeFile(){
        //创建FileOutputStream对象
        FileOutputStream fileOutputStream= null;
        String filePath = "D:\\programjava\\testTile\\chapter15\\a.txt";

        try {
            //得到一个FileOutputStream对象
            //1.new FileOutputStream(filePath) 这种创建方式,写入内容是会覆盖原来的内容
            //2.new FileOutputStream(filePath,true) 这种创建方式,写入内容是在文件末尾追加
            fileOutputStream = new FileOutputStream(filePath);
            //1.写入一个字节
            fileOutputStream.write('H');// char 自动转换为 int
            //2.写入字符串
            String str = "hello,world.";
            fileOutputStream.write(str.getBytes());//这里str.getBytes()方法是将字符串变成字节数组
            //3.write(byte[] b, int off, int len)将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。
            fileOutputStream.write(str.getBytes(),0,str.length());


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

(3). FileOutputStream案例2:要求编程完成图片/音乐的拷贝

java 复制代码
package chapter15.outputstream_;

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

/**
 * @aim java基础学习
 * @note java笔记
 */
@SuppressWarnings({"all"})
public class FileCopy {
    public static void main(String[] args) {
        //完成对文件的拷贝,将D:\\programjava\\testFile\\chapter15\\koala.jpg拷贝到D:\\programjava\\testFile\\koala.jpg
        /*
        思路分析:
        1.创建文件的输入流,将文件读入到程序
        2.创建文件的输出流,将读取到文件数据,写入到指定的文件
        3.在完成程序时,应该是读取部分数据,就写入到指定文件,这里使用循环
         */
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        String resourcePath = "D:\\programjava\\testFile\\chapter15\\koala.jpg";
        String aimPath = "D:\\programjava\\testFile\\koala.jpg";
        byte[] resource = new byte[1024];
        int readLen = 0;

        try {
            fileInputStream = new FileInputStream(resourcePath);
            fileOutputStream = new FileOutputStream(aimPath);
            while ((readLen=fileInputStream.read(resource)) != -1){
                /*
                1.这里可能有人想,FileOutputStream构造器没设置true,那文件每次写入不是覆盖l吗?,怎么能正常拷贝呢?个人理解如下:
                2.当使用FileOutputStream去创建对象时,它会判断文件是否存在。
                (1).如果存在,假设文件有一个指针,原本指针默认是在文件数据的末尾。那么这时指向开头,那么在write时就是从头开始写入,
                    每次写入都会覆盖原本数据的内容并且指针也是跟着移动。
                    比如:word文档,按下insert进入覆盖模式,在一段话的开头,每写一个字就会覆盖原本的字,同时指针也在移动。
                (2).如果不存在,就会创建一个文件,指针也是指向开头,那么这时每次write由于没有数据可覆盖,
                    但是指针也是跟着移动的,也就不存在覆盖了,那么这时后你就可以理解是追加
                 */
                fileOutputStream.write(resource,0,readLen);// 写入实际读取的数据长度
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. FileReader
    (1). FileReader是字符流,即按照字符来操作io

(2). FileReader相关方法

① new FileReader(File/String)

② read:每次读取单个字符,返回该字符(int),如果到文件末尾就返回-1

③ read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾就返回-1

(3). 相关API:

① new String(char[]):将char[]转换成String

② new String(char[].off,len):将char[]的指定部分转换成String

  1. FileWriter介绍

    (1). FileWriter是字符流,即按照字符来操作io

    (2). FileWriter常用方法

    ① new FileWriter(File/ String):覆盖模式,相当于流的指针在首端

    ② new FileWriter(File / String, true):追加模式,相当于流的指针在尾端

    ③ write(int):写入单个字符

    ④ write(char[]):写入指定数组

    ⑤ write(char[] ,off, len):写入指定数组的指定部分

    ⑥ write(String): 写入整个字符串

    ⑦ write(String,off,len):写入字符串的指定部分

    (3). 相关API:String类,toCharArray:将String转换成char[]

    (4). 注意:FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!

  2. FileReader和FileWriter案例

    (1). 要求使用FileReader从story.txt读取内容,并显示

java 复制代码
package chapter15.reader_;

import org.junit.jupiter.api.Test;

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

/**
 * @aim java基础学习
 * @note java笔记
 */
public class FileRead_ {
    public static void main(String[] args) {

    }

    /**
     * 单个字符读取文件
     */
    @Test
    public void fileRead01() {
        String fileName = "D:\\programjava\\testFile\\chapter15\\story.txt";
        FileReader fileReader = null;
        int data = 0;
        try {
            //FileReader创建对象
            fileReader = new FileReader(fileName);
            //循环读取 使用read()
            while ((data = fileReader.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 字符数组读取文件
     */
    @Test
    public void fileRead02() {
        System.out.println("============fileRead02=========");
        String fileName = "D:\\programjava\\testFile\\chapter15\\story.txt";
        FileReader fileReader = null;
        char[] buf = new char[8];
        int readLen = 0;
        try {
            //FileReader创建对象
            fileReader = new FileReader(fileName);
            //循环读取 使用read(char[]),返回的是实际读取到的字符数
            //如果返回-1,说明到文件末尾了
            while ((readLen = fileReader.read(buf)) != -1) {
                System.out.print(new String(buf,0,readLen));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

(2). 使用FileWriter将"风雨之后,定见彩虹",写入到note.txt文件中,注意细节。

java 复制代码
(2).	使用FileWriter将"风雨之后,定见彩虹",写入到note.txt文件中,注意细节。
package chapter15.writer_;

import java.io.FileWriter;
import java.io.IOException;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class FileWriter_ {
    public static void main(String[] args) {
        String filePath = "D:\\programjava\\testFile\\chapter15\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter = null;
        char[] chars = {'a','b','c'};
        String str = "风雨之后,定然见彩虹";
        try {
            fileWriter = new FileWriter(filePath);
            //③write(int):写入单个字符
            fileWriter.write('H');
            //④write(char[]):写入指定数组
            fileWriter.write(chars);
            //⑤write(char[] ,off, len):写入指定数组的指定部分
            fileWriter.write("好好学习,天天向上",0,5);
            //⑥write(String): 写入整个字符串
            fileWriter.write(str);
            //⑦write(String,off,len):写入字符串的指定部分
            fileWriter.write(str,0,5);
            //在数据量大的情况下,可以使用循环操作

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileWriter != null){
                    //对应FileWriter,一定要关闭流或者flush才能真正把数据写入到文件
                    /* 查看源码 flush和close底层调用这个方法
                    private void writeBytes() throws IOException {
                        bb.flip();
                        int lim = bb.limit();
                        int pos = bb.position();
                        assert (pos <= lim);
                        int rem = (pos <= lim ? lim - pos : 0);

                            if (rem > 0) {
                        if (ch != null) {
                            if (ch.write(bb) != rem)
                                assert false : rem;
                        } else {
                            out.write(bb.array(), bb.arrayOffset() + pos, rem);
                        }
                        }
                        bb.clear();
                        }
                     */
//                    fileWriter.flush();
                    //关闭文件流 等价于 flush+关闭
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

15.3 节点流和处理流

15.3.1 基本介绍

  1. 节点流是直接从特定数据源读写数据的流,它们是I/O系统中的基础流,提供了最基本的读写功能。节点流通常与具体的数据源(如文件、网络连接等)直接相关联,如FileReader、FileWriter。
分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FlieWriter
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
访问字符串 StringReader StringWriter
  1. 处理流是对节点流的包装,它们提供了更高级的功能,如缓冲、行处理、转换等。处理流通常不直接与数据源相连,而是包装一个已经存在的节点流,为程序提供更为强大的读写功能,也更加灵活,如:BufferedReader、BufferedWriter。
分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
缓冲流 BufferedInputStream BufferedOutputStream BufferReader BufferedWriter
转换流 InputStreamReader OutputStreamWriter
对象流 ObjectInputStream ObjectOutputStream
抽象基类 FileInputStream FileOutputStream FileReader FlieWriter
打印流 PrintStream PrintWriter
推回输出流 PushbackInputStream PushbackReader
特殊流 DataInputStream DataOutputStream

举个例子:说明一下什么是包装类。如下部分代码和解释(这里用BufferedReader类,其他类同理):
由部分源码可知:BufferedReader类中有属性Reader,即可以封装一个节点流,该节点流可以是任意的,只要是Reader的子类就行。在它的构造器中,可以是接收Reader这样的属性。这种设计模式一般称为装饰器模式。

  1. 节点流和处理流的区别和联系

    (1). 节点流是底层流/低级流,直接跟数据源相接

    (2). 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。

    (3). 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。

  2. 处理流的功能主要体现在以下两个方法:

    (1). 性能的提供:主要以增加缓冲的方式来提高输入输出的效率

    (2). 操作的便捷:处理流可以提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。

15.3.2 处理流-BufferedReader和BufferedWriter

  1. BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的
  2. 关闭处理流时,只需要关闭外层流即可
  3. 案例1:使用BufferedReader读取文本文件,并显示在控制台
java 复制代码
package chapter15.reader_;

import java.io.BufferedReader;
import java.io.FileReader;

/**
 * @aim java基础学习
 * @note java笔记
 * 使用BufferedReader演示
 */
public class BufferedReader_ {
    public static void main(String[] args) throws Exception {
        String filePath = "D:\\programjava\\testFile\\chapter15\\a.java";
        //1。创建缓冲对象,套接在指定的节点流基础上
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
        //2.bufferedReader.readLine()是按行读取文件,效率高,当返回null时,表示文件读取完毕
        //底层读取文件实际还是new FileReader(filePath),BufferedReader只是做了处理。
        // n = in.read(cb, dst, cb.length - dst);
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);

        }
        //3.关闭流,这里注意,只需要关闭BufferedReader即可,因为底层会自动的去关闭 节点流(这里的节点流是FileReader)
        /*源码如下:
            public void close() throws IOException {
                synchronized (lock) {  //这里的in就是传进去的 new FileReader(filePath)
                    if (in == null)
                        return;
                    try {
                        in.close();
                    } finally {
                        in = null;
                        cb = null;
                    }
                }
            }
         */
        bufferedReader.close();
    }
}
  1. 案例2:使用BufferedWriter,将"hello,大家好呀",写入到文件中去。
java 复制代码
package chapter15.writer_;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

/**
 * @aim java基础学习
 * @note java笔记
 * 使用BufferedWriter演示
 */
public class BufferedWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "D:\\programjava\\testFile\\chapter15\\abc.txt";
        //创建BufferedWriter对象
        // 1.new FileWriter(filePath,true) 表示以追加的方式写入
        // 2.new FileWriter(filePath)  表示以覆盖的方式写入
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));//追加方法
        bufferedWriter.write("hello,大家好呀");
        //插入一个和系统相关的换行符
        bufferedWriter.newLine();
        bufferedWriter.write("hello,jack");
        //关闭外层流即可。内部传入的节点流会在底层关闭
        bufferedWriter.close();
    }
}
  1. 综合应用案例:使用BufferedReader和BufferedWriter 完成文本文件拷贝,注意文件编码。
java 复制代码
package chapter15.writer_;

import java.io.*;

/**
 * @aim java基础学习
 * @note java笔记
 * 使用BufferedReader和BufferedWriter 完成文本文件拷贝
 */
public class BufferedCopy_ {
    public static void main(String[] args) throws IOException {
        String srcFilePath = "D:\\programjava\\testFile\\chapter15\\a.java";
        String destFilePath = "D:\\programjava\\testFile\\chapter15\\copyA.java";
        String line;
        // 1.创建缓冲流对象, 套接在指定节点流基础上,注意:因为Reader和Writer是按字符读取的,因此不能操作二进制文件(字节文件)
        // 比如:图片、音乐、音频等,会操作读取数据错误。
        BufferedReader bufferedReader = new BufferedReader(new FileReader(srcFilePath));
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(destFilePath));
        //2. 边读边写,write(line);默认是读取一行的内容,没有换行
        while ((line = bufferedReader.readLine()) != null){
            bufferedWriter.write(line);
            bufferedWriter.newLine();
        }
        // 关闭外层流
        bufferedReader.close();
        bufferedWriter.close();
    }
}

15.3.3 处理流-BufferedInputStream和BufferedOutputSteam

  1. 介绍BufferedInputStream


BufferedInputStream是字节流,创建BufferedInputStream时,会创建一个内部缓冲区数组。

  1. 介绍BufferedOutputStream

    BufferedOutputStream是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层系统

3. 应用案例:要求编程完成图片/音乐 的拷贝(要求使用Buffered...流)BufferedCopy02.java

java 复制代码
package chapter15.outputstream_;

import java.io.*;

/**
 * @aim java基础学习
 * @note java笔记
 * 演示使用BufferedInputStream和BufferedOutputStream
 * 使用它们可以完成对二进制文件拷贝。
 * 字节流既可以操作二进制文件,也可以操作文本文件
 */
public class BufferedCopy02_ {
    public static void main(String[] args) {
//        String srcFilePath = "D:\\programjava\\testFile\\chapter15\\koala.jpg";
//        String destFilePath = "D:\\programjava\\testFile\\chapter15\\copy\\koala.jpg";
//        String srcFilePath = "D:\\programjava\\testFile\\chapter15\\video.mp4";
//        String destFilePath = "D:\\programjava\\testFile\\chapter15\\copy\\testVideo.mp4";
        //注意:这里复制文本文件并不会出现乱码,因为编码格式都是一样.之前读取文件的代码FileInputStream_.java那个乱码是因为中文utf-8是三个字节
        //它每读取一次就在控制台输出(即每次都是输出一个字节肯定乱码),这里不会乱码和FileInputStream_.java代码不一样。这里是将读取所有的字节
        //换到另一个文件中去,但是编码格式都是一样,然后字节与字符之间的映射肯定也是一样。最后显示也是正常复制成功。
        String srcFilePath = "D:\\programjava\\testFile\\chapter15\\a.java";
        String destFilePath = "D:\\programjava\\testFile\\chapter15\\copy\\aaa.java";
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //FileInputStream 是 InputStream 子类
            bis = new BufferedInputStream(new FileInputStream(srcFilePath));
            bos = new BufferedOutputStream(new FileOutputStream(destFilePath));

            //循序读取与写入
            int readLen = 0;
            byte[] buff = new byte[1024];
            //bis.read(buff)读取数据,返回读取到数据长度,读取完毕就返回-1,这些之前都是用过的
            while ((readLen = bis.read(buff)) != -1) {
                bos.write(buff, 0, readLen);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭外层流即可。bis.close();底层代码会自动new FileInputStream(srcFilePath).close()
                if (bis != null) {
                    bis.close();
                }
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

15.3.4 对象流-ObjectInputStream和ObjectOutputStream

  1. 看一个需求

    (1). 将int num=100 这个int数据保存到文件中,注意不是100这个数据,而是int 100,能够从文件找那个直接恢复int 100

    (2). 将Dog dog = new Dog("小黑",4) 这个dog对象保存到文件中,并且能够从文件中恢复

    (3). 上面的要求,就是能够将基本数据类型或者对象进行序列化 和 反序列化操作

  2. 序列化和反序列化

    (1). 序列化就是在保存数据时,保存数据的值和数据类型

    (2). 反序列化就是在恢复数据时,恢复数据的值和数据类型

    (3). 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable(这是一个标记接口没有方法)和Externalizable

  3. 基本介绍

    (1). 功能:提供了对基本类型和对象类型的序列化和反序列化的方法

    (2). ObjectOutputStream 提供序列化功能

    (3). ObjectInputStream 提供反序列化功能



  4. 应用案例1 :使用ObjectOutputStream序列化 基本数据类型和一个Dog对象(name,age),并保存到data.dat文件中。ObjectOutputStream_.java

java 复制代码
package chapter15.outputstream_;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @aim java基础学习
 * @note java笔记
 * 演示ObjectOutputStream的使用, 完成数据的序列化
 */
public class ObjectOutStream_ {
    public static void main(String[] args) throws IOException {
        //序列化后,保存的文件的格式,不是纯文本,而是按照它的格式来保存
        String filePath = "D:\\programjava\\testFile\\chapter15\\data.dat";

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));

        //序列化数据到 "D:\\programjava\\testFile\\chapter15\\data.dat"
        oos.writeInt(100);//int 自动装箱 Integer(实现了Serializable)
        oos.writeBoolean(true);//boolean 自动装箱 Boolean(实现了Serializable)
        oos.writeChar('H');//char 自动装箱 Character(实现了Serializable)
        oos.writeDouble(10.5);//double 自动装箱 Double(实现了Serializable)
        oos.writeUTF("hello");//String (实现了Serializable)
        //保存一个Dog对象
        oos.writeObject(new Dog("小黑",4));

        oos.close();
    }
}
class Dog implements Serializable {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  1. 应用案例2:使用ObjectInputStream读取data.dat并反序列化恢复数据
java 复制代码
package chapter15.inputstream_;

import chapter15.outputstream_.Dog;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class ObjectInputStream_ {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //指定反序列化文件
        String filePath = "D:\\programjava\\testFile\\chapter15\\data.dat";

        ObjectInputStream ois  = new ObjectInputStream(new FileInputStream(filePath));

        //1.读取(反序列化)的顺序需要和你保存的数据(序列化)的顺序一致, 否则会出现异常
        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readChar());
        System.out.println(ois.readDouble());
        System.out.println(ois.readUTF());
        Object dog = ois.readObject(); // 底层 Object -> Dog
        System.out.println("运行类型= "+ dog.getClass());
        System.out.println("dog信息= "+dog);

        //注意细节:
        //1.如果希望调用Dog的方法,需要进行向下转型
        //2.这时候就要将Dog类的定义,要移动到可以引用的位置。
        Dog dog1 = (Dog) dog;
        System.out.println(dog1.getName());
        ois.close();

    }
}
  1. 注意事项和细节说明(记)
    (1). 读写顺序要一致
    (2). 要求实现序列化或反序列化对象,需要实现Serializable
    (3). 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
    Private static final long serialVersionUID = 1L;
    (4). 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员,即static或transient修饰的成员是不会添加到文件中去,打印出来也是null。
    (5). 序列化对象时,要求里面属性的类型也需要实现序列化接口,基本数据类型自动装箱为包装类,包装类本身就是可序列化的。而自定义的类必须需要实现序列化接口。
    (6). 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化

15.4 输入流和输出流

15.4.1 标准输入输出流

  1. 基本介绍
类型 默认设备
System.in 标准输入 InputStream 键盘
System.out 标准输出 PrintStream 显示器
  1. 应用案例1

    传统的方法System.out.println(); 是使用out对象将数据输出到显示器

  2. 应用案例2

    传统的方法,Scanner 是从标准输入 键盘接收数据

java 复制代码
package chapter15.standard_;

/**
 * @aim java基础学习
 * @note java笔记
 */
public class InputAndOutput_ {
    public static void main(String[] args) {
        //System 类的属性 public static final InputStream in = null;
        //System.in 编译类型   InputStream
        //System.in 运行类型   BufferedInputStream
        //表示的是标准输入 键盘
        System.out.println(System.in.getClass());//class java.io.BufferedInputStream

        //System.out  public static final PrintStream out = null;
        //编译类型是  PrintStream
        //运行类型是  PrintStream
        //表示标准输出 显示器
        System.out.println(System.out.getClass());//class java.io.PrintStream

    }
}

15.4.2 转换流-InputStreamReader和OutputStreamWriter

  1. 先看一个乱码问题,引出学习转换流必要性。
java 复制代码
package chapter15.transformation_;

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

/**
 * @aim java基础学习
 * @note java笔记
 */
public class CodeQuestion_ {
    public static void main(String[] args) throws IOException {
        //1.读取D:\programjava\testFile\chapter15\a.txt 文件到程序
        //2.创建字符输入流 BufferedReader[处理流]
        //3.默认情况下,读取文件是按照utf-8编码的。若文件不是utf-8则读取文件可能会出现文件乱码问题
        //4.介绍一下ANSI国标码,ANSI不是一种编码,它指的是在不同国家和地区基于ASCII扩展的编码方式。
        //  和操作系统和区域设置有关,在中国地区简体中文默认是GBK.
        String filePath = "D:\\programjava\\testFile\\chapter15\\a.txt";
        BufferedReader br = new BufferedReader(new FileReader(filePath));
        String line = br.readLine();
        System.out.println(line); //Hhello�����ѽ��乱码效果
        br.close();
    }
}
  1. InputStreamReader和OutputStreamWriter介绍
    (1). InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)
    (2). OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)
    (3). 当处理纯文本数据时,如果使用的字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
    (4). 可以在使用时指定编码格式(比如:utf-8、gbk、gb2312、ISO8859-1等)


  1. 应用案例1-InputStreamReader

编程将字节流FileInputStream 包装成(转换成)字符流InputStreamReader,对文件进行读取(按照utf-8/gbk格式),进而再包装成BufferedReader

java 复制代码
package chapter15.transformation_;

import java.io.*;

/**
 * @aim java基础学习
 * @note java笔记
 * 演示使用 InputStreamReader 转换流解决中文乱码问题
 * 将字节流 FileInputStream 转换成字符流 InputStreamReader,指定编码gbk/utf-8
 */
public class InputStreamReader_ {
    public static void main(String[] args) throws IOException {
        String filePath = "D:\\programjava\\testFile\\chapter15\\a.txt";

        //1.把 FileInputStream 转成 InputStreamReader 并指定编码gbk
        //InputStreamReader isr  = new InputStreamReader(new FileInputStream(filePath),"gbk");
        //2.把InputStreamReader 传入 BufferedReader
        //BufferedReader br = new BufferedReader(isr);

        //一般都是将1和2写一起的, 这里是为了方便理解
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath),"gbk"));


        //3.读取
        String line = br.readLine();
        System.out.println(line);//Hhello,你好呀。

        //4.关闭外层流
        br.close();
    }
}
  1. 应用案例2-OutputStreamWriter
    编程将字节流FileOutputStream包装成(转换成)字符流OutputStreamWriter,对文件进行写入(按照gbk格式,可以指定其他,比如:utf-8)
java 复制代码
package chapter15.transformation_;

import java.io.*;

/**
 * @aim java基础学习
 * @note java笔记
 * 演示 OutputStreamWriter 使用,把FileOutputStream字节流,转换成字符流OutputStreamWriter
 * 指定处理的编码 gbk/utf-8/utf8
 */
public class OutputStreamWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "D:\\programjava\\testFile\\chapter15\\b.txt";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");
        osw.write("我是Jack,大家好呀。很高兴认识大家!!!");
        osw.close();
    }
}

15.4.3 打印流-PrintStream 和 PrintWriter

  1. 打印流只有输出流,没有输入流

  2. 打印流-PrintStream
java 复制代码
package chapter15.printstream_;

import java.io.IOException;
import java.io.PrintStream;

/**
 * @aim java基础学习
 * @note java笔记
 * 演示 PrintStream 字节打印流/输出流
 */
public class PrintStream_ {
    public static void main(String[] args) throws IOException {
        PrintStream out = System.out;
        //在默认情况下 PrintStream 输出数据的位置是 标准输出 ,即显示器
        /*
            public void print(String s) {
                write(String.valueOf(s));
            }
            public static String valueOf(Object obj) {
                return (obj == null) ? "null" : obj.toString();
            }
         */
        out.print("Jack , hello.");
        //因为print底层使用的还是writer 所以这里可以直接调用writer进行打印/输出
        out.write("hi,大家好!".getBytes());
        //Jack , hello.hi,大家好!

        /*
            默认打印/输出的位置是显示器,那么也可以修改输出的位置
            比如:修改输出位置改到D:\\programjava\\testFile\\chapter15\\f1.txt
            public static void setOut(PrintStream out) {
                checkIO();
                setOut0(out);//native方法, 修改了out位置
            }
         */
        System.setOut(new PrintStream("D:\\programjava\\testFile\\chapter15\\f1.txt"));
        System.out.println("hello , 大家好。");

        out.close();
    }
}
  1. 打印流- PrintWriter
java 复制代码
package chapter15.printstream_;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @aim java基础学习
 * @note java笔记
 * 演示 PrintWriter 的使用
 */
public class PrintWriter_ {
    public static void main(String[] args) throws IOException {
        //输出到显示器
//        PrintWriter printWriter = new PrintWriter(System.out);
//        printWriter.println("hi,大家好。");
//        printWriter.close();

        //输出到D:\\programjava\\testFile\\chapter15\\f2.txt
        PrintWriter printWriter = new PrintWriter(new FileWriter("D:\\programjava\\testFile\\chapter15\\f2.txt"));
        printWriter.println("hi,大家好。");
        printWriter.close();//真正写入文件的语句 等价于 flush + 关闭流
    }
}

15.5 Properties

  1. 看一个需求
    如下一个配置文件 mysql.properties
    ip=195.168.0.13
    user=root
    pwd=123456
    请问编程读取ip、user和pwd的值是什么?
    (1). 传统的方法如下:
java 复制代码
package chapter15.properties_;

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

/**
 * @aim java基础学习
 * @note java笔记
 */
public class Properties01 {
    public static void main(String[] args) throws IOException {
        //传统的方法:读取mysql.properties 文件, 并得到ip、user、pwd
        BufferedReader br = new BufferedReader(new FileReader("src\\chapter15\\mysql.properties"));
        String line = "";
        while ((line = br.readLine()) != null){
            String[] split = line.split("=");
            System.out.println(split[0]+"值是:"+split[1]);
        }
        //ip值是:195.168.0.13
        //user值是:root
        //pwd值是:123456
        //问题来了,如果是要修改呢,也要重新创建一个对象去修改,麻烦
    }
}

(2). 使用Properties类可以方便实现

  1. Properties类的基本介绍

(1). 专门用于读写配置文件的集合类

配置文件的格式如下:

键=值

键=值

......

(2). 注意:键值对不需要有空格,值不需要用引号一起来。默认类型是String

  1. Properties的常见方法

    (1). load:加载配置文件的键值对到Properties对象

    (2). list:将数据显示到指定设备/流对象

    (3). getProperty(key):根据键获取值

    (4). setProperty(key,value):设置键值对到Properties对象

    (5). store:将Properties中的键值对存储到配置文件,在Idea中,保存信息到配置文件,如果含有中文,会存储为unicode码

  2. 应用案例

    (1). 使用Properties类完成对mysql.properties 的读取

java 复制代码
package chapter15.properties_;

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

/**
 * @aim java基础学习
 * @note java笔记
 * 演示 Properties 的使用
 */
public class Properties02 {
    public static void main(String[] args) throws IOException {
        //1.创建 Properties 对象
        Properties properties = new Properties();
        //2.加载指定配置文件
        properties.load(new FileReader("src\\chapter15\\mysql.properties"));
        //3.把k-v显示到控制台
        properties.list(System.out);
        //4.根据key获取对应的值
        String user = properties.getProperty("user");
        String pwd = properties.getProperty("pwd");
        System.out.println("用户名= "+user);
        System.out.println("密码是= "+pwd);
    }
}
/*
-- listing properties --
ip=195.168.0.13
pwd=123456
user=root
用户名= root
密码是= 123456
 */

(2). 使用Properties类添加key-val到新文件mysql2.properties中

(3). 使用Properties类完成对mysql.properties的读取,并修改某个key-val

java 复制代码
package chapter15.properties_;

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

/**
 * @aim java基础学习
 * @note java笔记
 */
public class Properties03 {
    public static void main(String[] args) throws IOException {
         //使用Properties 类来创建配置文件,并修改配置文件内容
        Properties properties = new Properties();
        //创建键值对
        //1.如果该文件没有key,就是创建
        //2.如果该文件有key,就是修改
        //3.Properties 父类就是 Hashtable ,底层就是Hashtable 核心方法一致
        properties.setProperty("charset","utf8");
        properties.setProperty("user","杰克");//注意:保存时,若有中文,则中文是unicode码值
        properties.setProperty("pwd","123456");
        
        //将k-v存储到文件中去
        properties.store(new FileOutputStream("src\\chapter15\\mysql2.properties"),"hello");
    }
}

15.6 本章作业

  1. 编程题 Task01.java

    (1). 在判断D:\programjava\testFile\chapter15\下是否存在文件夹mytemp,如果没有就创建mytemp

    (2). 在D:\programjava\testFile\chapter15\mytemp目录下,创建文件hello.txt

    (3). 如果hello.txt已经存在,提示该文件已经存在,就不要再重复创建了。

  2. 编程题 Task01.java

    要求使用BufferedReader读取一个文本文件,为每行加上行号,再连同内容一并输出到屏幕上。

  3. 编程题 Task03.java

    (1). 要编写一个dog.properties

    Name=tom

    Age=4

    Color=rea

    (2). 编写Dog类(name,age,color)创建一个dog对象,读取dog.properties用相应的内容完成属性初始化,并输出。

相关推荐
神仙别闹28 分钟前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭1 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫1 小时前
泛型(2)
java
超爱吃士力架1 小时前
邀请逻辑
java·linux·后端
南宫生1 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石1 小时前
12/21java基础
java
李小白662 小时前
Spring MVC(上)
java·spring·mvc
sanguine__2 小时前
Web APIs学习 (操作DOM BOM)
学习
冷眼看人间恩怨2 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
GoodStudyAndDayDayUp2 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea