IO流(二)

news2024/12/28 4:58:46

1.对象流-ObjectInputStream和ObjectOutputStream(处理流包装流)的基本原理

看一个需求:

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

2.将Dog dog=new Dog("小黄",3)这个dog对象保存到文件中,并且能够从文件恢复,恢复的也是dog对象。

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

序列化和反序列化:

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

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

3.需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:

Serializable  //这是一个标记接口(声明性质的,没有方法)

Externalizable (该接口有方法需要实现,因此我们一般实现上面的Serializable接口

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

2.ObjectOutputStream提供序列化功能

 其中OutputStream是一个抽象类

3.ObjectInputStream提供反序列化功能

 我们从构造器里面也可以看出来处理流和包装流的概念,我们发现.ObjectInputStream有一个.构造器InputStream,它可以接收一个InputStream。即只要是InputStream的子类都可以发生进去。即像我们之前所说的修饰器模式。

2.ObjectOutputStream的使用

2.1使用ObjectOutputStream序列化基本数据类型和一个Dog对象(name,age),并保存到a.txt文件中

我们设计的代码如下所示:

package com.rgf.outputStream_;

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

/**
 * 演示ObjectOutputStream的使用,完成数据的序列化
 */
public class ObjectOutStream_ {
    public static void main(String[] args) throws Exception {
        //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
        String filePath="E:\\a.txt";

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

        //序列化数据到E:\\a.txt;
        cos.writeInt(100);//int ->Integer(实现了Serializable接口)
        cos.writeBoolean(true); //boolean ->Boolean(实现了Serializable接口)
        cos.writeChar('a');//char ->Character(实现了Serializable接口)
        cos.writeDouble(9.5); //double->Double (实现了Serializable接口)
        cos.writeUTF("蕾峰编程"); //String
        //保存一个Dog对象
        cos.writeObject(new Dog("旺财",3));

        cos.close();
        System.out.println("数据保存完毕(序列化形式)");

    }

        }
        //如果需要序列化某个类的对象,必须实现Serializable接口
class  Dog implements Serializable {
    private  String name;
    private  int age;

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

}

 

 我们运行之后如下所示:

 虽然是乱码,但是我们也可以看出来。比如Dog,序列化的文件并不会跟着文件的后缀形式而进行保存之后的呈现。而是有自己的表现方式。

2.2使用ObjectInputStream读取a.txt并反序列化恢复数据

我们的代码如下所示:

package com.rgf.fileInputStream;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectInputStream_ {
    public static void main(String[] args) throws Exception{
        //指定反序列化的文件
        String filePath="e:\\a.txt";

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

        //读取
        //1.读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
        //2.否则会出现异常


        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();
        System.out.println("运行类型="+dog.getClass());
        System.out.println("dog信息="+dog);  //底层 将Object会转成Dog.

      //关闭流,关闭外层流即可,底层会关闭  FileInputStream流
        ois.close();

    }
}

我们运行之后如下所示:

我们发现在输出里面,我们没有输出dog的详细信息。为了出现这个详细信息,我们需要重写这个方法。我们重写toString方法如下所示:

package com.rgf.outputStream_;

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

/**
 * 演示ObjectOutputStream的使用,完成数据的序列化
 */
public class ObjectOutStream_ {
    public static void main(String[] args) throws Exception {
        //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
        String filePath="E:\\a.txt";

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

        //序列化数据到E:\\a.txt;
        cos.writeInt(100);//int ->Integer(实现了Serializable接口)
        cos.writeBoolean(true); //boolean ->Boolean(实现了Serializable接口)
        cos.writeChar('a');//char ->Character(实现了Serializable接口)
        cos.writeDouble(9.5); //double->Double (实现了Serializable接口)
        cos.writeUTF("蕾峰编程"); //String
        //保存一个Dog对象
        cos.writeObject(new Dog("旺财",3));

        cos.close();
        System.out.println("数据保存完毕(序列化形式)");

    }

        }
        //如果需要序列化某个类的对象,必须实现Serializable接口
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+
                        '}';
            }
        }


 我们重新执行序列化代码,再执行反序列化代码,如下所示:

 我们发现可以拿到了dog的详细信息,所以我们在反序列化的时候,我们要对这个类进行处理并且拿到。

我们发现如下原因:

我们在dog里面创建get和set方法:
 

package com.rgf.outputStream_;

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

/**
 * 演示ObjectOutputStream的使用,完成数据的序列化
 */
public class ObjectOutStream_ {
    public static void main(String[] args) throws Exception {
        //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
        String filePath="E:\\a.txt";

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

        //序列化数据到E:\\a.txt;
        cos.writeInt(100);//int ->Integer(实现了Serializable接口)
        cos.writeBoolean(true); //boolean ->Boolean(实现了Serializable接口)
        cos.writeChar('a');//char ->Character(实现了Serializable接口)
        cos.writeDouble(9.5); //double->Double (实现了Serializable接口)
        cos.writeUTF("蕾峰编程"); //String
        //保存一个Dog对象
        cos.writeObject(new Dog("旺财",3));

        cos.close();
        System.out.println("数据保存完毕(序列化形式)");

    }

        }
        //如果需要序列化某个类的对象,必须实现Serializable接口
class  Dog implements Serializable {
            private String name;
            private int age;

            public Dog(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;
            }

            @Override
            public String toString() {
                return "Dog{"+
                        "name='" +name + '\''+
                        ",age="+ age+
                        '}';
            }
        }


我们发现在反序列化的时候是无法进行调用dog的get方法的。

我们将Dog类公共化,我们进行修改代码如下所示

package com.rgf.outputStream_;

import java.io.Serializable;



public class  Dog implements Serializable {
        private String name;
        private int age;

        public Dog(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;
        }

        @Override
        public String toString() {
            return "Dog{"+
                    "name='" +name + '\''+
                    ",age="+ age+
                    '}';
        }
    }





我们修改代码如下所示:

package com.rgf.fileInputStream;

import com.rgf.outputStream_.Dog;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectInputStream_ {
    public static void main(String[] args) throws Exception{
        //指定反序列化的文件
        String filePath="e:\\a.txt";

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

        //读取
        //1.读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
        //2.否则会出现异常


        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());
        //dog的编译类型是 Object,dog的运行类型是 Dog
        Object dog = ois.readObject();
        System.out.println("运行类型="+dog.getClass());
        System.out.println("dog信息="+dog);  //底层 将Object会转成Dog.

        //这里是特别重要的细节:
        //1.如果我们希望调用Dog的方法,需要向下转型
        //2.需要我们将Dog类的定义,拷贝到可以引用的位置
         Dog dog2=(Dog)dog;
        System.out.println(dog2.getName());   //旺财

      //关闭流,关闭外层流即可,底层会关闭  FileInputStream流
        ois.close();

    }
}

我们运行之后如下所示:

 节点流和处理流

2.3节点流和处理流的注意事项

(1)读写顺序要一致

当我们改变顺序的时候,我们的代码如下所示:

package com.rgf.fileInputStream;

import com.rgf.outputStream_.Dog;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectInputStream_ {
    public static void main(String[] args) throws Exception{
        //指定反序列化的文件
        String filePath="e:\\a.txt";

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

        //读取
        //1.读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
        //2.否则会出现异常

        System.out.println(ois.readUTF());
        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readChar());
        System.out.println(ois.readDouble());

        //dog的编译类型是 Object,dog的运行类型是 Dog
        Object dog = ois.readObject();
        System.out.println("运行类型="+dog.getClass());
        System.out.println("dog信息="+dog);  //底层 将Object会转成Dog.

        //这里是特别重要的细节:
        //1.如果我们希望调用Dog的方法,需要向下转型
        //2.需要我们将Dog类的定义,拷贝到可以引用的位置
         Dog dog2=(Dog)dog;
        System.out.println(dog2.getName());   //旺财

      //关闭流,关闭外层流即可,底层会关闭  FileInputStream流
        ois.close();

    }
}

我们运行之后会出现如下错误:

(2)要求序列化或反序列化对象,需要实现Serializable

(3)序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性

我们可以在我们设置的dog类进行演示:

package com.rgf.outputStream_;

import java.io.Serializable;



public class  Dog implements Serializable {
        private String name;
        private int age;
        //增加序列化的版本号后,我们增加新的东西只会认为是一个版本的修改,而不会认为是一个新的类。
        private String hobby; 
        //serialVersionUID 序列化的版本号,可以提高兼容性
        private static final long serialVersionUID=1L;

        public Dog(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;
        }

        @Override
        public String toString() {
            return "Dog{"+
                    "name='" +name + '\''+
                    ",age="+ age+
                    '}';
        }
    }





(4)序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员

我们进行示例如下所示:

我们的dog类如下所示:

package com.rgf.outputStream_;

import java.io.Serializable;



public class  Dog implements Serializable {
        private String name;
        private int age;
       //(4)序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
private static String nation;
private transient String color;
        public Dog(String name, int age,String nation,String color) {
            this.name = name;
            this.age = age;
            this.color=color;
            this.nation=nation;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}' +nation ;
    }
}





我们增加了dog类的属性,同时用static和 transient进行修饰,我们进行写入如下所示:

package com.rgf.outputStream_;

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

/**
 * 演示ObjectOutputStream的使用,完成数据的序列化
 */
public class ObjectOutStream_ {
    public static void main(String[] args) throws Exception {
        //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
        String filePath="E:\\a.txt";

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

        //序列化数据到E:\\a.txt;
        cos.writeInt(100);//int ->Integer(实现了Serializable接口)
        cos.writeBoolean(true); //boolean ->Boolean(实现了Serializable接口)
        cos.writeChar('a');//char ->Character(实现了Serializable接口)
        cos.writeDouble(9.5); //double->Double (实现了Serializable接口)
        cos.writeUTF("蕾峰编程"); //String
        //保存一个Dog对象
        cos.writeObject(new Dog("旺财",3,"中国","白色"));

        cos.close();
        System.out.println("数据保存完毕(序列化形式)");

    }

        }


我们进行输出后如下所示:

 

 我们打开文件可以查看到序列化里面只有name和age,而没有color和nation。

 (5)序列化对象时,要求里面属性的类型也需要实现序列化接口

我们创建Master类如下所示:

package com.rgf.outputStream_;

public class Master {

}

我们之后在dog类里面引用这个类:

//序列化对象时,要求里面属性的类型也需要实现序列化接口
    private Master master = new Master();

 我们没有实现接口进行序列化的时候,我们发现出现了报错:

 我们修改master来实现seeializable接口:

package com.rgf.outputStream_;

import java.io.Serializable;

public class Master implements Serializable {

}

我们修改我们的tostring,如下所示:

 public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}' +nation+ ""+master ;
    }
}

 我们运行之后如下所示:

 (6)序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化

3.标准输入输出流

类型默认设备
System.in  标准输入   (System里面的一个属性)InputStream键盘
System.out  标准输出   (System里面的一个属性)PrintStream显示器

我们进行演示如下所示:

package com.rgf.standard;

public class InputAndOutput {
    public static void main(String[] args) {
        //public final static InputStream in = null;
        System.in
    }
}

我们查看源码如下所示:

我们查看他们的类型如下所示:

package com.rgf.standard;

public class InputAndOutput {
    public static void main(String[] args) {
        //System 类 的 public final static InputStream in = null;
        //System.in 编译类型 InputStream
        //System.in 运行类型 BufferedInputStream
        System.out.println(System.in.getClass());
       
    }
}

 我们运行之后如下所示:

 我们查看System.out的类型:

package com.rgf.standard;

public class InputAndOutput {
    public static void main(String[] args) {
        //System 类 的 public final static InputStream in = null;
        //System.in 编译类型 InputStream
        //System.in 运行类型 BufferedInputStream(字节流、处理流)
        //表示标准输入  键盘
        System.out.println(System.in.getClass());


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

    }
}

我们运行之后如下所示:

 我们的案例如下所示:

package com.rgf.standard;

import java.util.Scanner;

public class InputAndOutput {
    public static void main(String[] args) {
        //System 类 的 public final static InputStream in = null;
        //System.in 编译类型 InputStream
        //System.in 运行类型 BufferedInputStream(字节流、处理流)
        System.out.println(System.in.getClass());


        //System 类 的  public final static PrintStream out = null;
        //System.out 编译类型  PrintStream
        //System.out 运行类型  PrintStream
        System.out.println(System.out.getClass());

        System.out.println("hello,蕾峰编程");
        Scanner scanner = new Scanner(System.in); //BufferedInputStream.
        System.out.println("请输入内容");
        String next = scanner.next();
        System.out.println("next="+next);

    }
}

我们运行之后如下所示:

4.转换流  InputStreamReader和OutputStreamWriter

 我们设计的代码如下所示:
 

package com.rgf.transformation;

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

/**
 * 看一个中文乱码问题
 *
 */
public class CodeQuestion {
    public static void main(String[] args) throws IOException {
        //读取e:\\b.txt文件到程序
        //思路
        //1.创建字符输入流 BufferedReader (处理流,处理流里面可以放一个节点流Buffered)
        //2.使用BufferedReader对象读取a.txt
        //3.默认情况下,读取文件是按照UTF-8编码
        String filePath="e:\\b.txt";
        BufferedReader br = new BufferedReader(new FileReader(filePath));
        String s = br.readLine();
        System.out.println("读取到的内容为:"+ s);
        br.close();


    }
}

 我们运行之后如下所示:

我们打开该文件,可以了解到该文件的编码为UTF-8:

当我们转换成不是UTF-8编码的时候,我们发现如下所示:

 

ANSI为国标码,为gbk码。我们进行保存而且替换掉它。

我们继续运行之后如下所示:

我们发现出现乱码了。我们没有指定我们所要读取的文件格式。而字节流是可以指定的。我们通过转换流来解决该问题。 

 介绍:

1.InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)

我们发现里面有一个方法InputStreamReader(InputStream,Charset),InputStream是字节流的抽象父类,而Charset指定他的编码。可以传入一个InputStream对象,而且可以指定处理的编码。 

2.OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装(转换)成Writer(字符流)

 

 我们发现里面有一个方法OutputStreamWriter(OutputStream,Charset),OutputStream是字节流的抽象父类,而Charset指定他的编码。可以传入一个OutputStream对象,而且可以指定处理的编码。 

3.当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流

4.可以在使用时指定编码格式(比如utf-8,gbk,gb2312,ISO8859-1等)

我们利用如下实例来进行熟练:
(1)字节流FileInputStream包装成(转换成)字符流InputStreamReader,对文件进行读取(按照utf-8/gbk格式),进而在包装成BufferedReader

我们设计的代码如下所示:

package com.rgf.transformation;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 演示使用InputStreamReader转换流解决中文乱码问题
 * 将字节流FileInputStream转换成字符流InputStreamReader,指定编码gbk/utf-8
 */
public class InputStreamReader_ {
    public static void main(String[] args) throws IOException {
        String filePath="e:\\b.txt";
        //解读
        //1.把 FileInputStream(filePath)转成 InputStreamReader
        //2.指定编码
        InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath),"gbk");
        //3.把InputStreamReader传入 BufferedReader
        BufferedReader br = new BufferedReader(isr);
        //4.读取
        String s = br.readLine();
        System.out.println("读取内容="+s);
        //5.关闭外层流
        br.close();
    }
}

我们运行之后如下所示:

 我们也可以将代码进行简洁如下所示:

package com.rgf.transformation;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 演示使用InputStreamReader转换流解决中文乱码问题
 * 将字节流FileInputStream转换成字符流InputStreamReader,指定编码gbk/utf-8
 */
public class InputStreamReader_ {
    public static void main(String[] args) throws IOException {
        String filePath="e:\\b.txt";
        //解读
        //1.把 FileInputStream(filePath)转成 InputStreamReader
        //2.指定编码
        //InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath),"gbk");
        //3.把InputStreamReader传入 BufferedReader
        //BufferedReader br = new BufferedReader(isr);
        
        //将2和3合在一起写
        BufferedReader br= new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "gbk"));

        //4.读取
        String s = br.readLine();
        System.out.println("读取内容="+s);
        //5.关闭外层流
        br.close();
    }
}

(2)字节流FileOutputStream包装成(转换成)字符流OutputStreamWriter,对文件进行写入(按照utf-8/gbk格式),进而在包装成BufferedWriter。

我们设计的代码如下所示:
 

package com.rgf.transformation;

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

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

我们运行之后如下所示:

我们打开文件如下所示:

我们查看到此时的编码为ANSI编码,即为对应的gbk码。

 我们将编码修改如下所示:

package com.rgf.transformation;

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

/**
 * 演示OutputStreamWriter 使用
 * 把FileOutputStream 字节流,转成字符流
 * 指定处理的编码 gdk/utf-8/utf8
 */
public class OutputStreamWriter_ {
    public static void main(String[] args) throws IOException {
        String  filePath="e:\\b.txt";
        String charSet="utf-8";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
        osw.write("hello,雷锋编程教育");
        osw.close();
        System.out.println("按照"+charSet+"保存文件成功");
    }
}

 我们运行之后如下所示:

我们打开文件进行查看:

 

我们发现此时为utf-8了。

5. 打印流-PrintStream(字节流)和PrintWriter(字符流)

打印流只有输出流没有输入流

PrintStream:

我们查看他的类图如下所示:

 我们进行查看他的构造方法:

我们来发现PrintStream的用法:

我们发现system.out就是PrintStream

 我们编写如下代码进行查看:

package com.rgf.printStream;

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

/**
 * 演示PrintStream(字节打印流/输出流 )
 */
public class PrintStream_ {
    public static void main(String[] args)  throws IOException{

        PrintStream out=System.out;
        //在默认情况下,打印流PrintStream输出数据的位置是标准输出,即显示器
        out.print("hello,we");
        out.write("woaini".getBytes());
        //因为print底层使用的是write方法,所以我们可以直接调用write方法进行打印/输出。
/*  out.print的源码。
public void print(String s) {
        if (s == null) {
            s = "null";
        }
        write(s);
    }*/

        /*
        我们发现真正的打印输出的是write方法:
        private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(s);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush && (s.indexOf('\n') >= 0))
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }
         */
        out.close();
        //我们也可以去修改打印流输出的位置/设备
        //setOut可以去修改PrintStream,默认为向显示器打印的。

        //1.输出修改成到e:\\a.txt
        //2.“hello,woren”就会输出到e:\\a.txt。
        System.setOut(new PrintStream("e:\\a.txt"));
        //此时文件已经切换到PrintStream里面,然后输出就会输出到里面。
        /*
        setOut的底层代码:
          public static void setOut(PrintStream out) {
        checkIO();
        setOut0(out);
        setOut0(out)的底层代码:
        private static native void setIn0(InputStream in);
        private static native void setOut0(PrintStream out); //native方法,修改了out。
        private static native void setErr0(PrintStream err);
        setOut0(out)是一个native方法。
    }
         */

        System.out.println("hello,woren");
    }
}

我们运行之后如下所示:

我们打开文件进行查看,我们发现成功的输送出来了。

我们通过setOut方法修改了输出的位置。

我们查看PrintWriter

 

 我们查看他的构造方法

 我们设计如下代码:

package com.rgf.printStream;

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

/**
 * 演示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:\\a.txt"));
        printWriter.print("hi,北京你好");
        printWriter.close();
        //printWriter.close( );没有colse就不会刷新。
    }
}

我们运行之后如下所示:

我们发现输送在控制台上面了。

为了输送到文件里面,我们创建如下所示:

package com.rgf.printStream;

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

/**
 * 演示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:\\a.txt"));
        printWriter.print("hi,北京你好");
        printWriter.close(); //flush+关闭流,才会将数据写入到文件。。
       //printWriter.close( );没有colse就不会刷新。
    }
}

我们运行之后如下所示:

我们发现这里没有,我们打开文件如下所示:
 

如果里面的close没有关闭,里面是不会刷新的。我们示例如下所示:

package com.rgf.printStream;

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

/**
 * 演示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:\\a.txt"));
        printWriter.print("hi,北京你好");
        printWriter.close();
        //printWriter.close( );没有colse就不会刷新。flush+关闭流,才会将数据写入到文件。
    }
}

我们运行之后如下苏所示:

 我们发现仍然是:

 因为我们没有close,没有close就不会刷新。我们在创建文件的时候,会首先把光标定在文件的最前面,会首先把文件截断再输出,结果我们没有close,就不会刷新。

我们分析源码来判断出close的作用:

我们debug如下所示:

 

 

 PrintStream主要操作byte流,而PrintWriter用来操作字符流,读取文本文件时一般用后者。

6.Properties类

如果我们将所需要的配置文件写死,改起来会非常困难,所以我们可以将我们需要的文件写为properties文件,然后程序进行读取修改。

 看一个需求:

如下一个配置文件mysql.properties:
ip=192.168.0.13

user=root

pwd=12345

请问编程读取ip、user和pwd的值是多少

分析:

1.传统的方法

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

我们传统的方法为:

传统的方法比较麻烦。

我们创建配置文件如下所示:

 我们设置的代码如下所示:

package com.rgf.properties_;

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

public class Properties01 {
    public static void main(String[] args) throws IOException {
        //读取mysql.properties文件,并得到ip,user和pwd
        BufferedReader br = new BufferedReader(new FileReader("com\\mysql.properties"));
        String line="";
        while ((line=br.readLine())!=null){  //循环读取
            System.out.println(line);

        }
        br.close();
    }
}

我们运行之后如下所示:

 我们修改代码如下所示:

package com.rgf.properties_;

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

public class Properties01 {
    public static void main(String[] args) throws IOException {
        //读取mysql.properties文件,并得到ip,user和pwd
        BufferedReader br = new BufferedReader(new FileReader("com\\mysql.properties"));
        String line="";
        while ((line=br.readLine())!=null){  //循环读取
            String[] split = line.split("=");
            System.out.println(split[0]+"值是:"+split[1]);

        }
        br.close();
    }
}

我们运行之后如下所示:

 如果只打印ip,我们将代码修改如下所示:
 

package com.rgf.properties_;

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

public class Properties01 {
    public static void main(String[] args) throws IOException {
        //读取mysql.properties文件,并得到ip,user和pwd
        BufferedReader br = new BufferedReader(new FileReader("com\\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();
    }
}

我们运行之后如下所示:

我们对Properties类的基本介绍:

 

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

配置文件的格式:

键=值

键=值

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

(3)Properties的常见方法

load:加载配置文件的键值对到Properties对象

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

getProperty(key):根据键获取值

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

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

我们通过案例来进行了解:

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

我们设置的代码如下所示:
 

package com.rgf.properties_;

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

public class Properties02 {
    public static void main(String[] args) throws IOException {
        //使用Properties类完成对mysql.properties的读取
        //1.创建Properties对象
        Properties properties = new Properties();
        //2.加载指定的配置文件
        properties.load(new FileReader("com\\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);

    }
}

运行之后如下所示:

 

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

我们设计的代码如下所示:

package com.rgf.properties_;

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

public class Properties03 {
    public static void main(String[] args) throws IOException {
        //使用Properties类来创建配置文件,修改配置文件内容
        //1.创建Properties对象
        Properties properties = new Properties();
        //2.创建配置文件
        properties.setProperty("charset","utf8");
        properties.setProperty("user","汤姆"); //之一保存时,是中文的unicode码值
        properties.setProperty("pwd","abc111");

        //3.将k-v存储到文件中即可。
        properties.store(new FileOutputStream("com\\mysql2.properties"),null);
        System.out.println("保存配置文件成功");
        //4.加载指定的配置文件
        properties.load(new FileReader("com\\mysql2.properties"));
        //5.把k-v显示到控制台
        properties.list(System.out);
    }
}

我们运行之后如下所示:

我们查看里面的mysql2.properties:

 

我们发现将k-v存储到文件里面的时候,后面的为null,Null的含义为:注释

我们追加进去查看源码,如下所示:

public void store(OutputStream out, String comments)
        throws IOException
    {
        store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
               comments,
               true);
    }

我们将null换成"hello  mysql",

我们发现里面的user为unicode码。我们通过查询看这个unicode码是否为汤姆。

http://tool.chinaz.com/tools/unicode.aspx 

我们发现转换后如下所示:

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

我们设计代码如下所示:

package com.rgf.properties_;

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

public class Properties03 {
    public static void main(String[] args) throws IOException {
        //使用Properties类来创建配置文件,修改配置文件内容
        //1.创建Properties对象
        Properties properties = new Properties();
        //2.创建配置文件
        //如果该文件没有key,就是创建,
        //如果该文件有key,就是修改。
        properties.setProperty("charset","utf8");
        properties.setProperty("user","汤姆"); //之一保存时,是中文的unicode码值
        properties.setProperty("pwd","8888888");

        //3.将k-v存储到文件中即可。
        properties.store(new FileOutputStream("com\\mysql2.properties"),"hello world");
        System.out.println("保存配置文件成功");
        //4.加载指定的配置文件
        properties.load(new FileReader("com\\mysql2.properties"));
        //5.把k-v显示到控制台
        properties.list(System.out);
    }
}

我们运行之后如下所示:

 

我们发现成功的修改过来了。 

我们查看setProperty的源码:

public synchronized Object setProperty(String key, String value) {
        return put(key, value);
    }
    
//hashtable不允许value为null,否则会抛异常。
public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value; //如果key存在,就替换
                return old;
            }
        }

        addEntry(hash, key, value, index); //如果是新的key,就addEntry
        return null;
    }

Properties父类是Hashtable,底层就是Hashtable核心方法,如果hash值相同,则为替换,否则就是增加新的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/164048.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Excel工具类实现——基于poi5.2.3

初衷仅是因为网上的Excel工具类不是太老旧的方法&#xff0c;就是不是很满足我想法&#xff0c;就想要自己搞一个&#xff0c;不过还不支持合并单元格等复杂的操作&#xff0c;后续看看能不能优化&#xff0c;有大神有更好的方法&#xff0c;可以公众号联系我&#xff0c;让我学…

基于jsp+mysql+Spring的SpringBoot美容院预约管理系统设计和实现

基于jspmysqlSpring的SpringBoot美容院预约管理系统设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文…

编制万年历的历程

初入编程之道的学子大都试编过万年历。万年历有二种&#xff1a;一为只有西历的月历&#xff0c;另一为有农历对照的月历或日历。编写万年历程序可以练练手&#xff0c;加深对编程语言的理解。记得我初入此道是在1994年&#xff0c;我那时刚买了486电脑&#xff0c;也刚开始有视…

概率论基本概念

全概率公式 P(B)P(BA1)P(BA2)...P(BAn) P(B) P(B|A1)P(A1) P(B|A2)P(A2) ... P(B|An)P(An)P(BA1)P(BA2)...P(BAn) 事件A1&#xff0c;A2&#xff0c;…构成一个完备事件组且都有正概率 某一个事件概率 这个事件*其他完备事件划分联合概率之和 联合概率 联合概率指的…

线程安全问题(2)

锁的相关知识&#xff1a; 1)这就是类似于说我们ATM机上面有一把锁&#xff0c;同一时刻&#xff0c;如果说人们之间不相互认识&#xff0c;那么通过这把锁就进行限制了说就限制了说一次只能有一个人来进来取钱&#xff0c;我们通过这样的锁&#xff0c;就可以来进行避免上述这…

ESP8266 Arduino开发 搭建web服务器与客户端开发

一、wifi 相关配置 1.1 无线终端 wifi 模式 此模式中&#xff0c;esp8266 会连接到指定 wifi 进行工作。 #include <ESP8266WiFi.h> // 本程序使用ESP8266WiFi库const char* ssid "home"; // 连接WiFi名&#xff08;此处使用home为示例&…

位运算相关

1.与运算 &#xff08;点我&#xff09; 这个题的大概意思&#xff1a;给222个数nnn和xxx&#xff0c;其中满足n&(n1)&(n2)&(n3)...&mxn\&(n1)\&(n2)\&(n3)...\&mxn&(n1)&(n2)&(n3)...&mx,求最小的mmm&#xff0c;只要满足m&g…

【异常】java11提示: Cannot find any provider supporting RSA/ECB/PKCS1Padding的问题

一、背景 项目中需要对敏感字段进行加密&#xff0c;但是加密方法中涉及到比较复杂的加密算法&#xff0c;这些算法都需要一个Provider&#xff0c;主要是用于初始化算法的。 以下是遇到的具体问题 二、报错截图 java.security.NoSuchAlgorithmException: Cannot find any pr…

【软件测试】软件测试模型

1. V模型 需求分析—计划—设计—编码—测试 ● 概要设计&#xff1a;设计整体架构&#xff0c;框架 ● 详细设计&#xff1a;模块和模块之间的详细设计 ● 集成测试&#xff0c;单元测试&#xff1a;通常由开发人员进行 特点&#xff1a; 明确标注了测试的多类型明确标注了测…

Introduction to Multi-Armed Bandits——01 Scope and Motivation

Introduction to Multi-Armed Bandits——01 Scope and Motivation 参考资料 Slivkins A. Introduction to multi-armed bandits[J]. Foundations and Trends in Machine Learning, 2019, 12(1-2): 1-286.项目地址 https://github.com/yijunquan-afk/bandit-learning Bandit…

LeetCode622.设计循环队列

设计循环队列1.题目描述2.思路3.代码实现以及分析3.1 创建结构体3.2创建一个具体的循环队列3.3判断是否为空 和 判断是否为满4. 进队列 和 出队列5.取队首和队尾元素6.释放空间7.总结1.题目描述 设计循环队列 2.思路 环形队列的抽象图 我们这里使用数组模拟实现循环队列&…

TransactionTemplate自动注入,只看这一篇文章就够了

标准的springboot接入mybatis步骤 1.引入了对应的依赖包 2.应用的properties下增加相应配置 3.根据配置进行自动装配 一般我们会配置这些信息&#xff0c;主要包括三类 1.数据库的连接信息 2.指定的数据源类型 3.mybatis的配置信息 配完以后&#xff0c;当你启动SpringBoot的主…

你是真的“C”——详解C语言数组模块知识

详解C语言数组模块知识&#x1f60e;前言&#x1f64c;一维数组的创建和初始化&#x1f64c;1.1 数组的创建&#x1f49e;1.2 数组的初始化&#x1f49e;1.3 一维数组的使用&#x1f49e;1.4 一维数组在内存中的存储&#x1f49e;二维数组的创建和初始化&#x1f64c;1.1 二维数…

【Python百日进阶-数据分析】Day225 - plotly的Ohlc图go.Ohlc()

文章目录一、语法二、参数三、返回值四、实例4.1 简单的OHLC图4.2 隐藏滑块的OHLC图4.3 添加自定义文本和注释4.4 自定义OHLC颜色4.5 带日期时间对象的简单的OHLC图4.6 自定义悬浮文本4.7 Dash中的应用一、语法 ohlc&#xff08;Open-High-Low-Close 的缩写&#xff09;是一种…

【C++逆向】虚表(Virtual table)逆向 | 安卓so虚函数逆向

什么是多态 定义一个虚基类ISpeaker class ISpeaker{ protected:size_t b; public:ISpeaker( size_t _v ): b(_v) {}virtual void speak() 0; };有两个子类&#xff0c;都实现了虚函数speak()&#xff1a; class Dog : public ISpeaker { public:Dog(): ISpeaker(0){}//vir…

1581_AURIX_TC275_SMU故障处理梳理

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 前面为了缓解自己的学习压力&#xff0c;一次学习笔记大概也就是看10页文档整理一下。这一次其实是看了几十页&#xff0c;但是里面过掉了一些信息&#xff0c;而且这部分内容不是很好拆分…

hive在IDEA中debug

一、hive在IDEA中debug 安装hadoop环境&#xff08;1和2替换顺序也可以&#xff09; 注&#xff1a;hadoop环境不需要从源码编译 https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html 按照官网教程编译源码 https://cwiki.apach…

软件工程专业课实验报告

一、结构化分析方法1.1需求描述教务管理子系统的需求描述&#xff1a;教务管理是一项需求周密计划、严谨安排的工作&#xff0c;要依据教师、学生信息进行合理安排。开学阶段&#xff0c;需要教师提交开课申请进行开课&#xff0c;学生根据老师的开课信息&#xff0c;选择课程&…

uview 使用遇到的问题。

uviewuniappvue&#xff0c;uView是uni-app生态专用的UI框架。 1. 注意uview版本&#xff0c;uview 2.0与uview1.0 官方提示&#xff1a;uView2.0是继1.0以来的一次重大更新&#xff0c;2.0已全面兼容nvue。 因此在接手项目的时候首先得看清楚&#xff0c;之前开发的是uview…

【自学Python】Python获取字符串长度

Python获取字符串长度 Python获取字符串长度教程 在 Python 中要想获取 字符串 长度可以使用 len() 函数。 Python len()函数详解 定义 我们将要获取的字符串的长度&#xff0c;传进 len() 函数&#xff0c;即可实现获取字符串的长度。 语法 len(string)参数 参数描述s…