19IO流

本文详细介绍了Java中的IO流,包括文件的创建、读写、删除操作,以及字节流和字符流的区别。重点讲解了FileReader、FileWriter、BufferedReader和BufferedWriter的使用,以及文件拷贝的过程。同时,讨论了节点流和处理流的概念,以及如何使用ObjectInputStream和ObjectOutputStream进行对象的序列化和反序列化。
摘要由CSDN通过智能技术生成

19IO流

1. 文件

在这里插入图片描述


2. 常用的文件操作

2.1 创建文件对象相关构造器和方法

在这里插入图片描述

案例:

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

    }

    //方式1:new File(String pathname)
    @Test//通过Junit测试
    public void create01(){
        String filePath = "e:\\news1.txt";//注意这里的"\\"是因为"\\"前一个是转义符,代码中两个\才表示实际中一个\
        File file = new File(filePath);//到这里只是在内存中创建了一个对象,但是e盘中还没有文件

        try {
            file.createNewFile();//执行了createNewFile(),才会在磁盘中创建该文件
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //方式2:new File(File parent,String child)//根据父目录"文件"+"子路径" 构建
    @Test
    public void create02(){
        File parentFile = new File("e:\\");
        String filePath = "news2.txt";
        File file = new File(parentFile, filePath);

        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //方式3:new File(String parent,String child)//根据父目录+子路径构建
    @Test
    public void create03(){
        String parentPath = "e:\\";//实际上这里这样写也可以"e:/"
        String filePath = "news3.txt";
        File file = new File(parentPath, filePath);

        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 获取文件相关信息

在这里插入图片描述

案例演示:

//获取文件的信息
@Test
public void info(){
    File file = new File("e:\\news1.txt");
    //getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
    System.out.println("文件名称=" + file.getName());
    System.out.println("文件绝对路径=" + file.getAbsolutePath());
    System.out.println("文件的父目录=" + file.getParent());
    System.out.println("文件的大小(字节)=" + file.length());//txt是utf-8,一个字母一个字节,一个汉字三个字节(hello你好一共11字节)
    System.out.println("文件是否存在=" + file.exists());//T
    System.out.println("是不是一个文件=" + file.isFile());//T
    System.out.println("是不是一个目录=" + file.isDirectory());//F
}

在这里插入图片描述

2.3 目录的操作和文件删除

在这里插入图片描述

注意:delete删除目录时,这个目录下面必须为空(不包含任何文件)
在这里插入图片描述

//判断d:\\news1.txt是否存在,如果存在就删除
@Test
public void m1(){
    File file = new File("e:\\news1.txt");
    if (file.exists()){
        if (file.delete()){
            System.out.println(file.getPath() + "删除成功");
        }else {
            System.out.println(file.getPath() + "删除失败");
        }
    }else {
        System.out.println("文件不存在");
    }
}

//判断D:\\demo02是否存在,存在就删除,否则提示不存在
//这里我们要体会到,java编程中,目录也被当做文件
@Test
public void m2(){
    File file = new File("D:\\demo02");
    if (file.exists()){
        if (file.delete()){
            System.out.println(file.getPath() + "删除成功");
        }else {
            System.out.println(file.getPath() + "删除失败");
        }
    }else {
        System.out.println("文件不存在");
    }
}

//判断D:\\demo\\a\\b\\c目录是否存在,如果存在就提示已经存在,否则就创建
@Test
public void m3(){
    String directoryPath = "D:\\demo\\a\\b\\c";
    File file = new File(directoryPath);

    if (file.exists()){
        System.out.println(directoryPath + "已经存在");
    }else {
        if (file.mkdirs()){//创建一级目录使用mkdir()如:"D:\\demo",创建多级目录用mkdirs()
            System.out.println(directoryPath + "创建成功");
        }else {
            System.out.println(directoryPath + "创建失败");
        }
    }
}

3. IO流原理及流的分类

3.1 IO流原理

在这里插入图片描述

在这里插入图片描述

3.2 IO流分类

在这里插入图片描述

注意:

  • 字节流效率比字符流低。但是字节流操作二进制文件(声音、视频、word)可以保证无损操作。字符流操作文本文件比较好。

  • InputStream、OutputStream 和 Reader、Writer 都是抽象类,因此在使用时要创建他们的实现子类才行


4. IO流体系-常用的类

在这里插入图片描述

4.1 InputStream

在这里插入图片描述

在这里插入图片描述

/**
 * 演示InputStream的子类FileInputStream的使用(字节输入流  文件 -> 程序)
 */
public class FileInputStream_ {
    public static void main(String[] args) {

    }

    /**
     * 演示读取文件...
     * .read()单个字节的读取,效率较低
     */
    @Test
    public void readFile01(){
        String filePath = "e:\\hello.txt";
        FileInputStream fileInputStream = null;//创建FileInputStream对象,用于读取文件
        int readData = 0;//用于接收文件数据

        try {
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取一个字节的数据。如果没有输入可用,此方法将终止
            //.read()返回的是一个int类型,这里要转成字符类型。如果返回一个-1就表示读取完毕
            while ((readData = fileInputStream.read()) != -1){
                System.out.print((char)readData);
            }
        } catch (IOException e) {//这里要改成IOException,上面的.read()才不会报异常
            e.printStackTrace();
        }finally {
            //关闭文件流,释放资源
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 使用 read(byte[] b)效率比较高
     */
    @Test
    public void readFile02(){
        String filePath = "e:\\hello.txt";
        FileInputStream fileInputStream = null;//创建FileInputStream对象,用于读取文件
        byte[] buf = new byte[8];//一次读取8个字节
        int readLen = 0;

        try {
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取最多buf.length个字节的数据到字节数组。
            //.read(byte[] buf) 返回的是一个int类型,为实际读取的字节数。如果返回一个-1就表示读取完毕
            while ((readLen = fileInputStream.read(buf)) != -1){
                //new String(字符数组, offset, length),由字符数组的[offset,length)构造一个字符串
                System.out.print(new String(buf, 0, readLen));
            }
        } catch (IOException e) {//这里要改成IOException,上面的.read()才不会报异常
            e.printStackTrace();
        }finally {
            //关闭文件流,释放资源
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

/*注意:如果上面的"e:\\hello.txt"文件里本身是hello,word,如果变为hello,word你好,那么上满的readFile01()方法就会hello,word+6个乱码,因为这里是按照一个字节一个字节读的,但是一个汉字字符占3个字节,所以“你好”占6个字节,而每次只读取一个字节并把它转化为char类型输出,所以是乱码

4.2 OutputStream

在这里插入图片描述

在这里插入图片描述

//以OutputStream的实现子类FileOutputStream为例
public class FileOutputStream01 {
    public static void main(String[] args) {

    }

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

        try {
            //注意:
            //1.new FileOutputStream(filePath) 创建方式,当写入内容时,会覆盖原来的内容
            //2.new FileOutputStream(filePath, true) 创建方式,当写入内容时,是追加到文件后面
            //fileOutputStream = new FileOutputStream(filePath);
            fileOutputStream = new FileOutputStream(filePath,true);
           
            //1.写入一个字节:write(int b) 将指定的字节写入此文件输出流。
            //fileOutputStream.write('H');//执行后创建e:\a.txt,里面的内容为‘H’

            //2.write(byte[] b) 将 b.length个字节从指定的字节数组写入此文件输出流。
            String str = "hsp,world!";
            //fileOutputStream.write(str.getBytes());//str.getBytes()可以把字符串转化为字符数组。此时a.txt内容变为hsp,world!

            //3.write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。
            fileOutputStream.write(str.getBytes(), 0, 3);///执行后创建e:\a.txt,里面的内容为 hsp
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //释放资源
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4.3 文件拷贝

//将e盘下面的图片e:\\bg.png拷贝到d:\\bg.png
public class FileCopy {
    public static void main(String[] args) {
        //完成 文件拷贝,将 e:\\bg.png 拷贝到 d:\\
        //思路分析
        //1.创建文件的输入流,将文件读入到程序
        //2.创建文件的输出流,将读取到的文件数据,写入到指定的文件
        //注意:要边写边读,这是为了防止数据过大,内存装不下
        //特别特别注意:拷贝图片,一定要用字节流,不能用字符流啊

        String srcFilePath = "e:\\bg.png";
        String desFilePath = "d:\\bg.png";
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;

        try {
            fileInputStream = new FileInputStream(srcFilePath);
            fileOutputStream = new FileOutputStream(desFilePath);
            //定义一个字节数组,提高读取效率
            byte[] buf = new byte[1024];
            int readLen = 0;

            while ((readLen = fileInputStream.read(buf)) != -1){
                //边读边写。每次读取1024个字节就通过fileOutputStream写入(注意:最后一次读取可能不是1024个字节,所以想下面要用readLen)
                fileOutputStream.write(buf, 0, readLen);
            }
            System.out.println("拷贝成功...");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭输入输出流,释放资源
            try {
                if (fileInputStream != null){
                    fileInputStream.close();
                }
                if (fileOutputStream != null){
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4.2 FileReader 和 FileWriter

在这里插入图片描述

FileReader 和 FileWriter 是字符流,即按照字符来操作 io

在这里插入图片描述

在这里插入图片描述

  • 应用案例:

在这里插入图片描述

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

    }

    /**
     * 单个字符读取文件
     */
    @Test
    public void readFile01(){
        String filePath = "e:\\story.txt";
        FileReader fileReader = null;
        int data = 0;

        try {
            //创建FileReader对象,按照字符流操作
            fileReader = new FileReader(filePath);
            //fileReader.read() 每次读取单个字符,返回的是一个int类型(一个字符)。
            while ((data = fileReader.read()) != -1){
                System.out.print((char)data);//输出的时候我们要重新转型(char)
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileReader != null){
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 字符数组读取文件
     */
    @Test
    public void readFile02(){
        String filePath = "e:\\story.txt";
        FileReader fileReader = null;
        char[] buf = new char[8];
        int readLen = 0;

        try {
            //创建FileReader对象,按照字符流操作
            fileReader = new FileReader(filePath);
            //fileReader.read(buf) 返回的是实际读取到的字符数,如果返回-1表示读取文件结束
            while ((readLen = fileReader.read(buf)) != -1){//读取到的字符数组存入buf中
                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();
            }
        }
    }
}

在这里插入图片描述

public class FileWriter_ {
    public static void main(String[] args) {
        String filePath = "e:\\note.txt";
        FileWriter fileWriter = null;
        char[] chars = {'a', 'b', 'c'};

        try {
            
            fileWriter = new FileWriter(filePath);//默认是覆盖写入
            //方式1:write(int) 写入单个字符
            fileWriter.write('H');//H
            //方式2:write(char[]) 写入指定数组
            fileWriter.write(chars);//Habc
            //方式3:write(char[], off, len) 写入指定数组的指定部分
            fileWriter.write("韩顺平教育".toCharArray(), 0, 3);//Habc韩顺平
            //方式4:write(string) 写入整个字符串
            fileWriter.write(" 北京你好~");//Habc韩顺平 北京你好~
            //方式5:write(string, off, len) 写入字符串的指定部分
            fileWriter.write("上海天津", 0, 2);//Habc韩顺平 北京你好~上海
            //在数据量大的情况下,可以使用循环操作
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //对于FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件
            try {
                //.close()等价于 .flush() + 关闭
                fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5. 节点流和处理流

5.1 基本介绍

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:

  • 节点流只能针对某一种特定的数据进行操作,比如FileReader/FileWriter只能对文件进行操作(按字符),ByteArrayInputStream / ByteArrayOutputStream只能对数组进行操作(按字节操作)等等,这样就造成了节点流是固定的,这是跟数据源直接相关的,更加底层。灵活性不足。

  • 包装流,以BufferedReader / BufferedWriter为例,这两个类的内部定义了一个他们的父类属性 Reader / Writer,源码如下:

在这里插入图片描述

在这里插入图片描述

意味着这两个 Reader/Writer 内部可以接受一个他们父类的属性或者父类下面的所有子类(向上转型),这一点可以看其构造器如下,导致他们可以对任意数据类型进行操作。比如如果对文件进行操作,那么BufferedReader的Reader属性接收一个FileReader对象,就可以对文件进行读操作,如果想对数组、管道进行操作也同理,更加灵活。

在这里插入图片描述

在这里插入图片描述

这其实是一个修饰器模式:子类里面定义一个父类属性,导致该子类可以通过这个父类属性拥有该父类其他子类的功能。

5.2 节点流和处理流区别于联系

在这里插入图片描述

模拟修饰器设计模式案例:

/**
 * 模拟Reader类
 */
public abstract class Reader_ {//抽象类
    public void stringRead(){}
    public void fileRead(){}
}
/**
 * 模拟节点流,FileReader类
 */
public class FileReader_ extends Reader_{
    public void fileRead(){
        System.out.println("读取文件...");
    }
}
/**
 * 模拟节点流,StringReader类
 */
public class StringReader_ extends Reader_{
    public void stringRead(){
        System.out.println("读取字符串...");
    }
}
/**
 * 模拟处理流(包装流)
 */
public class BufferedReader_ extends Reader_{
    private Reader_ reader_; //属性是Reader_类型

    public BufferedReader_(Reader_ reader_) {
        this.reader_ = reader_; //可以接收 一个Reader_的实现子类
    }

    //可以扩展 fileRead, 多次读取文件,或者加缓冲byte[]
    public void filesRead(int num) {
        for (int i = 0; i < num; i++) {
            reader_.fileRead();
        }
    }

    //可以扩展 stringRead,批量处理字符串数据
    public void stringsRead(int num) {
        for (int i = 0; i < num; i++) {
            reader_.stringRead();
        }
    }
}
//测试
public class Test_ {
    public static void main(String[] args) {
        //我们现在想对文件进行操作
        BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_());
        bufferedReader_.filesRead(10);

        //现在又想对字符串进行操作
        bufferedReader_ = new BufferedReader_(new StringReader_());
        //注意:如果下面变为.filesRead(5),那么也不会报错,只是执行的是父类的.fileRead()5次(空操作)
        bufferedReader_.stringsRead(5);
    }
}

5.3 处理流

在这里插入图片描述

解释上面的关闭:

BufferedReader buffered = new BufferedReader(new FileReader)

实际上buffered对文件的读操作还是靠对象new FileReader实现的,如果我们关闭了buffered(外层流),也就关闭了FileReader对象

5.3.1 字符处理流

BufferedReader 和 BufferedWriter

  • BufferedReader 案例:

在这里插入图片描述

/**
 * 演示BufferedReader 的使用
 */
public class BufferedReader_ {
    public static void main(String[] args) throws IOException {
        String filePath = "e:\\a.java";
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
        //我们调用BufferedReader读取文件,实际上使用的是FileReader读取文件
        String line;
        //.readLine() 按行读取效率高。
        // 1.返回的是一个字符串(文件中的一行数据)
        // 2.当返回null时,表示文件读取完毕
        while ((line = bufferedReader.readLine()) != null){
            System.out.println(line);//注意.readLine()只会返回一行数据,不会读取到换行符所以用.println
        }

        //关闭流,这里注意只需要关闭BufferedReader,因为底层会自动去关闭节点流FileReader
        bufferedReader.close();
        /*
                public void close() throws IOException {
                    synchronized (lock) {
                        if (in == null)
                            return;
                        try {
                            in.close();  //这里这个in就是定义BufferedReader时传入的FileReader
                        } finally {
                            in = null;
                            cb = null;
                        }
                    }
                }
         */
    }
}
  • BufferedWriter 案例:

在这里插入图片描述

/**
 * 演示BufferedWriter 的使用
 */
public class BufferedWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "e:\\a.java";
        //使用BufferedWriter来写文件,实际上还是使用FileWriter来写的
        //1.new FileWriter(filePath) 表示以覆盖的方式写入
        //2.new FileWriter(filePath,true) 表示以追加的方式写入
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));

        bufferedWriter.write(" hello,韩顺平教育");
        bufferedWriter.newLine();//插入一个与系统相关的换行符
        bufferedWriter.write(" hello,韩顺平教育");
        bufferedWriter.newLine();
        //关闭外层流即可,传入的new FileWriter(filePath)会在底层自动关闭
        bufferedWriter.close();
    }
}
  • 拷贝文件:

在这里插入图片描述

public class BufferedCopy_ {
    public static void main(String[] args) {
        //1.BufferedReader 和 BufferedWriter是按照字符操作的,
        // 不要去操作二进制文件(声音、视频、doc、pdf),否则会造成文件损坏

        String srcFilePath = "e:\\a.java";
        String desFilePath = "e:\\b.java";
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        String line;

        try {
            bufferedReader = new BufferedReader(new FileReader(srcFilePath));
            bufferedWriter = new BufferedWriter(new FileWriter(desFilePath));

            while ((line = bufferedReader.readLine()) != null){
                bufferedWriter.write(line);
                bufferedWriter.newLine();//注意这里的.write()要插入一个换行符
            }
            System.out.println("拷贝完毕...");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bufferedReader.close();
                bufferedWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
5.3.2 字节处理流

前面的两个处理流都是按字符操作的字符流,下面讲两个按字节进行操作的字节流——BufferedInputStream 和 BufferedOutputStream

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

/**
 * 演示使用BufferedInputStream 和 BufferedOutputStream,使用他们可以完成二进制文件拷贝
 * 思考:字节流可以操作二进制文件,可以操作文本文件吗?可以,没有乱码
 */
public class BufferedCopy02 {
    public static void main(String[] args) {
//        String srcFilePath = "e:\\bg.png";
//        String desFilePath = "e:\\dx.png";
        //上面是拷贝图片,下面是测试字节处理流拷贝文档会不会有中文乱码
        String srcFilePath = "e:\\a.txt";
        String desFilePath = "e:\\b.txt";

        //创建BufferedInputStream 和 BufferedOutputStream对象
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //因为FileInputStream 是 InputStream 的子类
            bis = new BufferedInputStream(new FileInputStream(srcFilePath));
            bos = new BufferedOutputStream(new FileOutputStream(desFilePath));

            //循环的读取文件,并写入到desFilePath
            byte[] buf = new byte[1024];
            int readLen = 0;
            //当返回-1的时候就读取结束
            while ((readLen = bis.read(buf)) != -1){
                bos.write(buf);
                //System.out.println("buf"); //注意:如果这里输出buf中文会乱码,但是上面的写入文件不会乱码
            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭外层的处理流即可,底层会去关闭关联的节点流
            try {
                if (bis != null){
                    bis.close();
                }
                if (bos != null){
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
5.3.3 对象处理流

在这里插入图片描述

  • 之前讲的都是只保存值,现在不仅要保存值还要保存数据类型。即不仅要保存数据,还要保存数据的数据类型,保存一个Dog对象,那么取出该对象时仍然 是一个Dog类对象的数据。

在这里插入图片描述

  • 上面的两个接口,推荐使用 Serializable

在这里插入图片描述

下面开始介绍:ObjectInputStream 和 ObjectOutputStream:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

上面显示了这两个对象流的构造器,说明了他们也用了 修饰器模式

  • ObjectOutputStream 序列化案例:

    /**
     * 演示ObjectOutputStream 的使用,完成数据的序列化
     */
    public class ObjectOutputStream_ {
        public static void main(String[] args) throws IOException {
            //序列化后,保存的文件格式不是纯文本的,而是按照他的格式来保存
            String filePath = "e:\\data.dat";
    
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
    
            //序列化数据到e:\data.dat
            //这里100会自动装箱 int -> Integer(Integer是实现了Serializable接口的,因此可以序列化)。如果是oos.write(100)那就只是存放了值100,而没有类型Integer
            oos.writeInt(100);
            oos.writeBoolean(true);//boolean -> Boolean (实现了Serializable)
            oos.writeChar('a');//char -> Character (实现了Serializable)
            oos.writeDouble(9.5);//double -> Double (实现了Serializable)
            oos.writeUTF("韩顺平教育");//String (实现了Serializable)
            //保存一个Dog类的对象,该Dog类必须实现了两个接口中的一个,不然会出现运行异常
            oos.writeObject(new Dog("旺财", 10));
    
            oos.close();
            System.out.println("数据保存完毕(序列化形式)");
        }
    }
    
    //如果需要序列化某个类的对象,则该类必须实现Serializable接口或Externalizable接口
    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 +
                    '}';
        }
    }
    //文件打开如下:
    

在这里插入图片描述

  • 演示ObjectInputStream 反序列化案例:

    public class ObjectInputStream02 {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            //指定反序列化的文件
            String filePath = "e:\\data.dat";
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
    
            //读取(反序列化)的顺序一定要和存放(序列化)的顺序一致。否则会出现异常
            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());  //Dog
            System.out.println("dog信息:" + dog);
            
            //注意这里如果我们需要调用Dog类的方法,首先要对上面的dog进行向下转型,因此我们要把Dog类放在一个能访问的位置,可以把Dog类声明为public的
    
            //关闭流,关闭外层流即可,底层会关闭 FileInputStream 流
            ois.close();
        }
    }
    //输出如下:
    

在这里插入图片描述

  • 注意细节

在这里插入图片描述

class Dog implements Serializable {
    private String name;
    private int age;
    //序列化对象时,默认将里面的所有属性都进行序列化,但是除了static或transient修饰的成员
    private static String nation;
    private transient String color;
    //序列化对象时,要求里面属性的类型也需要实现序列化接口
    Master master = new Master();//如果Master没有实现序列化接口,则会报错
	
    //构造器..省略
}
5.3.4 标准输入输出流

在这里插入图片描述

public class InputAndOutput {
    public static void main(String[] args) {
        //1.System.in 是System类的 public final static InputStream in = null;
        //2.System.in 的编译类型 InputStream
        //3.System.in 的运行类型 BufferedInputStream
        //4.System.in 表示标准输入,标准输入其实就是键盘
        System.out.println(System.in.getClass());

        //1.System.out 是System类的 public final static PrintStream out = null;
        //2.System.out 的编译类型 PrintStream
        //3.System.out 的运行类型 PrintStream
        //4.System.out 表示标准输出,标准输出其实就是显示器
        System.out.println(System.out.getClass());
    }
}

在这里插入图片描述

在这里插入图片描述

注意:

  • 上面的 System.out 就表示标准输出(显示器);

  • Scanner scanner = new Scanner(System.in); 就表示给Scanner扫描器传入了一个BufferedInputStream,并且这个扫描器从键盘获取值

5.3.5 转换流
  • 先看一个文件乱码问题,引出学习转换流的必要性
//看一个中文乱码问题
public class CodeQuestion {
    public static void main(String[] args) throws IOException {
        //思路:先创建字符处理流 BufferedReader, 使用 BufferedReader 对象读取 a.txt
        String filePath = "e:\\a.txt";
        BufferedReader br = new BufferedReader(new FileReader(filePath));
        //默认情况下,读取文件是按照 utf-8 编码,但是如果保存文件时没有按照utf-8就会出现中文乱码
        String line = br.readLine();

        System.out.println("读取到的内容:" + line);
        br.close();
    }
}

在这里插入图片描述

在这里插入图片描述

  • 转换流:InputStreamReader 和 outputStreamWriter

在这里插入图片描述

  • InputStreamReader 和 outputStreamWriter 的构造器如下所示:

在这里插入图片描述

InputStreamReader(InputStream, Charset)//可以传入一个InputStream对象,而且可以指定处理的编码

在这里插入图片描述

OutputStreamWriter(OutputStream, Charset)//可以传入一个OutputStream对象,而且可以指定处理的编码

在这里插入图片描述

/*** 
  * 演示使用 InputStreamReader 转换流解决中文乱码问题
  * 将字节流 FileInputStream 转成字符流 InputStreamReader, 指定编码 gbk/utf-8
  */
public class InputStreamReader_ {
    public static void main(String[] args) throws IOException {
        String filePath = "e:\\a.txt";
        //1.把 FileInputStream(字节节点流) 转换成 InputStreamReader(字符处理流),并制定编码gbk(因为a.txt文件是按照gbk保存的)
        InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
        //2.把 InputStreamReader(字符处理流)传入 BufferedReader(处理流)
        BufferedReader br = new BufferedReader(isr);
        //4.读取。用 BufferedReader 进行处理,但是底层还是使用的是 FileInputStream
        String s = br.readLine();
        System.out.println("读取到的内容:" + s);
        //5.关流。还是关外层流即可
        br.close();
    }
}
//输出中文不会乱码了

在这里插入图片描述

/**
 * 演示OutputStreamWriter 使用
 * 把FileOutputStream 字节流,转换成字符流 OutputStreamWriter,指定处理的编码 gbk/utf-8 也可以写成 utf8
 */
public class OutputStreamWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "e:\\hsp.txt";
        String charSet = "gbk";
        //按照"gbk"编码保存"e:\\hsp.txt"文件
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
        osw.write("hi, 韩顺平教育");
        osw.close();
        System.out.println("按照" + charSet + " 保存文件成功~");
    }
}

6. 打印流

  • 注意:PrintStream(字节流) 和 PrintWriter(字符流)。打印流只有输出流,没有输入流。

在这里插入图片描述

6.1 字节打印流

/**
 * 演示 PrintStream 字节打印流
 */
public class PrintStream_ {
    public static void main(String[] args) throws IOException {
        PrintStream out = System.out;
        //在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
        /*
            public void print(String s) {
                if (s == null) {
                    s = "null";
                }
                write(s);
            }
         */
        out.print("john, hello");
        //因为print的底层是write方法,所以我们可以直接调用write进行打印/输出
        //解释为什么上面的write(s)不用转换成字节,因为PrintStream类里面有一个private方法:write(String s)
        out.write("韩顺平,你好".getBytes());
        //out.close();

        //我们可以去修改打印流输出的位置/设备
        //1.输出修改成到"e:\\f1.txt"
        //2."hello, 韩顺平教育~" 就会输出到 e:\\f1.txt
        /*
            public static void setOut(PrintStream out) {
                checkIO();
                setOut0(out);//native方法
            }
         */
        System.setOut(new PrintStream("e:\\f1.txt"));
        System.out.println("hello, 韩顺平教育~");//输出到f1.txt

        System.setOut(out);
        System.out.println("韩顺平!");//又输出到显示器了
        out.close();
    }
}

6.2 字符打印流

/**
 * 演示PrintWriter使用方式
 */
public class PrintWriter_ {
    public static void main(String[] args) throws IOException {
        //PrintWriter printWriter = new PrintWriter(System.out);//输出到显示器
        PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));//输出到 f2.txt
        printWriter.print("hi,你好北京");
        printWriter.close();//如果没有关闭,则上面的内容显示不出来(没有close()就不会刷新,就不会真正写东西进去)
    }
}

7. Properties 类

7.1 先看一个需求

在这里插入图片描述

public class Properties_ {
    public static void main(String[] args) throws IOException {
        //读取mysql.properties文件,并得到ip,user和pwd
        BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
        String line = "";
        while ((line = br.readLine()) != null) {//因为不知道文件有多少行,所以要循环读取
            String[] split = line.split("=");
            //如果我们要求指定得到ip值,就要按照如下方法获取,比较麻烦
            if ("ip".equals(split[0])) {
                System.out.println(split[0] + "值是:" + split[1]);
            }
        }
        br.close();
    }
}

7.2 Properties 类基本介绍

在这里插入图片描述

在这里插入图片描述

7.3 案例

在这里插入图片描述

//案例1:读取properties文件
public class Properties02 {
    public static void main(String[] args) throws IOException {
        //使用Properties 类来读取mysql.properties文件
        //1.创建Properties对象
        Properties properties = new Properties();
        //2.加载指定配置文件。new FileReader以字符的方式读取
        properties.load(new FileReader("src\\mysql.properties"));
        //3.把k-v显示到控制台
        properties.list(System.out);
        //4.根据一个key获取对应的值
        String user = properties.getProperty("user");
        String pwd = properties.getProperty("pwd");
        System.out.println("\n用户名=" + user);
        System.out.println("密码=" + pwd);
    }
}
//案例2:创建properties文件,修改键值对
public class Properties03 {
    //使用Properties 类来创建 配置文件,修改配置文件内容
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        //创建
        //1.如果该文件没有key 就是创建
        //2.如果该文件有 key 就是修改
        //3.Properties 父类是 Hashtable, 底层就是 Hashtable。
        //  和新方法是Hashtable 的 public synchronized V put(K key, V value)
        properties.setProperty("charset", "utf-8");
        properties.setProperty("user", "汤姆");//注意:保存时,是中文的unicode码值
        properties.setProperty("pwd", "abc111");//到这里,现在这个k-v还在内存里面
        properties.setProperty("pwd", "8888888");//修改:abc111 被替换成了 8888888

        //存储k-v到文件里面.store(Writer, null) 后面这个参数是注释,写在配置文件的最上面,一般无要求就置空
        //properties.store(new FileWriter("src\\mysql2.properties"), null);//按字符写入,写入到配置文件中的中文还是中文
        properties.store(new FileOutputStream("src\\mysql2.properties"), null);//按字节写入,写入到配置文件中的中文是unicode码
        System.out.println("保存配置文件成功~");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值