IO流(下)

上节介绍常规的字节流、字符流,本节介绍特殊的IO流。

数据操作流

数据操作流是操作基本数据类型的流。

public class DataInputStream extends FilterInputStream
public class DataOutputStream extends FilterOutputStream

  • DataInputStream 构造方法

    • public DataInputStream(InputStream in)
  • 特殊方法

    • boolean readBoolean():读取一个输入字节,如果该字节不是零,则返回 true,如果是零,则返回 false。
    • byte readByte():读取并返回一个输入字节
    • char readChar():读取两个输入字节并返回一个 char 值。
    • short readShort() :读取两个输入字节并返回一个 short 值。
    • int readInt():读取四个输入字节并返回一个 int 值。
    • long readLong() :读取八个输入字节并返回一个 long 值。
    • float readFloat():读取四个输入字节并返回一个 float 值。
    • double readDouble():读取八个输入字节并返回一个 double 值
  • DataOutputStream 构造方法

    • public DataOutputStream(OutputStream out)
  • 特殊方法

    • void write(int b)
    • void writeBoolean(boolean v)
    • void writeByte(int v)
    • void writeBytes(String s):将字符串按字节顺序写出到基础输出流中
    • void writeChar(int v)
    • void writeChars(String s):将字符串按字符顺序写入基础输出流。
    • void writeInt(int v)
    • void writeDouble(double v)
public class DataStreamDemo {
    public static void main(String[] args) throws IOException {
        // 先写入,在读取。
        
        // 写
        write();

        // 读
        read();
    }

    private static void read() throws IOException {
        // DataInputStream(InputStream in)
        // 创建数据输入流对象
        DataInputStream dis = new DataInputStream(
                new FileInputStream("E:\\demo\\a.txt"));

        // 读数据
        byte b = dis.readByte();
        short s = dis.readShort();
        int i = dis.readInt();
        long l = dis.readLong();
        float f = dis.readFloat();
        double d = dis.readDouble();
        char c = dis.readChar();
        boolean bb = dis.readBoolean();

        // 释放资源
        dis.close();

        System.out.println(b);
        System.out.println(s);
        System.out.println(i);
        System.out.println(l);
        System.out.println(f);
        System.out.println(d);
        System.out.println(c);
        System.out.println(bb);
    }

    private static void write() throws IOException {
        // DataOutputStream(OutputStream out)
        // 创建数据输出流对象
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(
                "E:\\demo\\a.txt"));

        // 写数据了
        dos.writeByte(10);
        dos.writeShort(100);
        dos.writeInt(1000);
        dos.writeLong(10000);
        dos.writeFloat(12.34F);
        dos.writeDouble(12.56);
        dos.writeChar('a');
        dos.writeBoolean(true);

        // 释放资源
        dos.close();
    }
}

输出:
10
100
1000
10000
12.34
12.56
a
true

数据操作流是操作基本数据类型的流,输出流怎么写入的数据,输入流必须按照顺序读取数据,不然造成读取异常。

内存操作流

内存操作流:用于处理临时存储信息程序结束,数据就从内存中消失

字节数组

public class ByteArrayInputStream extends InputStream
public class ByteArrayOutputStream extends OutputStream

  • ByteArrayInputStream
    • public ByteArrayInputStream(byte[] buf):使用 字节数组 作为其缓冲区数组
    • public ByteArrayInputStream(byte[] buf, int offset, int length)
  • ByteArrayOutputStream
    • public ByteArrayOutputStream()
    • public ByteArrayOutputStream(int size):创建一个新的 byte 数组输出流,指定大小的缓冲区容量
    • public synchronized byte[] toByteArray():将输出流转换成字节数组,用于输入流 ByteArrayInputStream 读取。

字符数组

public class CharArrayReader extends Reader
public class CharArrayWriter extends Writer

  • CharArrayReader
    • public CharArrayReader(char[] buf) :使用 字符数组 作为其缓冲区数组
    • public CharArrayReader(char[] buf, int offset, int length)
  • CharArrayWriter
    • public CharArrayWriter()
    • public CharArrayWriter(int initialSize):创建一个具有指定初始大小的新 CharArrayWriter
    • public char[] toCharArray():将输出流转换成字符数组,用于输入流 CharArrayReader 读取。

字符串

public class StringReader extends Reader
public class StringWriter extends Writer

  • StringReader
    • public StringReader(String s) :使用 字符串 作为其缓冲区
  • StringWriter
    • public StringWriter()
    • public StringWriter(int initialSize)
    • public String toString():将输出流转换成字符串,用于输入流 StringReader 读取。
public class MemoryDemo {

    public static void main(String[] args) throws IOException {
        // 写数据
        // ByteArrayOutputStream()
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        // 写数据
        for (int x = 0; x < 10; x++) {
            baos.write(("hello" + x).getBytes());
        }

        // public byte[] toByteArray()  将字节输出流转换成字节数组!
        byte[] bys = baos.toByteArray();

        // 读数据
        // ByteArrayInputStream(byte[] buf)  转换后的字节数组用于字节输入流的参数!
        ByteArrayInputStream bais = new ByteArrayInputStream(bys);

        int by = 0;
        while ((by = bais.read()) != -1) {
            System.out.print((char) by);
        }
    }
}

输出:
hello0hello1hello2hello3hello4hello5hello6hello7hello8hello9

注:内存操作流无需close()关闭资源。

打印流(常用)

字节打印流:public class PrintStream extends FilterOutputStream
字符打印流:public class PrintWriter extends Writer

  • PrintStream 构造方法
    • PrintStream(File file):创建指定文件对象File和默认编码方式的打印流。不自动刷新。
    • PrintStream(File file, String csn):创建指定文件对象File和字符集的打印流。不自动刷新。
    • PrintStream(String fileName) :创建指定文件名称和默认编码方式的打印流。不自动刷新。
    • PrintStream(String fileName, String csn):创建指定文件名称和字符集的打印流。不自动刷新。
    • PrintStream(OutputStream out):创建指定字节输出流和默认编码方式的打印流。不自动刷新。
    • PrintStream(OutputStream out, boolean autoFlush):创建指定字节输出流和默认编码方式的打印流。指定是否自动刷新。
    • PrintStream(OutputStream out, boolean autoFlush, String encoding):创建指定字节输出流和字符集的打印流。指定是否自动刷新。
  • PrintWriter 构造方法
    • PrintWriter(File file):创建指定文件对象File和默认编码方式的字符打印流。不自动刷新。
    • PrintWriter(File file, String csn):创建指定文件对象File和字符集的字符打印流。不自动刷新。
    • PrintWriter(String fileName):
    • PrintWriter(String fileName, String csn) :
    • PrintWriter(OutputStream out):创建指定字节输出流和默认编码方式的字符打印流。不自动刷新。
    • PrintWriter(OutputStream out, boolean autoFlush) :创建指定字节输出流和默认编码方式的字符打印流。指定是否自动刷新。
    • PrintWriter(Writer out):创建指定字符输出流的字符打印流。不自动刷新。
    • PrintWriter(Writer out, boolean autoFlush) :创建指定字符输出流的字符打印流。指定是否自动刷新。可以给 out 传参OutputStreamWriter,以达到设置文件字符编码。
  • 特有的方法
    • void print(任意类型):写入任意类型的值。
    • void println(任意类型):写入任意类型的值并换行。
    • format(String format, Object … args):格式化字符串写入。
    • printf(String format, Object … args):格式化字符串写入。相当于 C 语言的 sprintf。

如果需要使用带自动刷新的流,第一个参数需要是 OutputStream/Writer ,第二个参数是true。

public class PrintWriterDemo {
    public static void main(String[] args) throws IOException {
        // 作为Writer的子类使用
        PrintWriter pw = new PrintWriter("E:\\demo\\a.txt");
        pw.write("hello");
        pw.write("world");
        pw.println("java");
        pw.println("111");
        pw.close();

        // 创建打印流对象
        PrintWriter pw2 = new PrintWriter(new FileWriter("E:\\demo\\b.txt"), true);
        pw2.println("hello");
        pw2.println(true);
        pw2.println(100);
        pw2.close();

        // public PrintStream(String fileName, String csn) 创建带字符编码的文件
        PrintStream pw3 = new PrintStream("E:\\demo\\gbk1.txt", "GBK");   //文件gbk1.txt生成成功,且文件编码是GBK
        pw3.println("hello");
        pw3.printf("name=%s,age=%d", "小明", 15);
        pw3.close();

        // public PrintStream(OutputStream out, boolean autoFlush, String encoding) 创建带字符编码的文件
        PrintStream pw4 = new PrintStream(
                new FileOutputStream("E:\\demo\\gbk2.txt"), true, "GBK");  //文件gbk2.txt生成成功,且文件编码是GBK
        pw4.println("hello");
        pw4.printf("name=%s,age=%d", "小明", 15);
        pw4.close();

        // public PrintWriter(String fileName, String csn) 创建带字符编码的文件
        PrintWriter pw5 = new PrintWriter("E:\\demo\\gbk3.txt", "GBK");  //文件gbk3.txt生成成功,且文件编码是GBK
        pw5.println("hello");
        pw5.printf("name=%s,age=%d", "小明", 15);
        pw5.close();

        // public PrintWriter (Writer out) 创建带字符编码的文件,OutputStreamWriter设置字符编码
        PrintWriter pw6 = new PrintWriter(
                new OutputStreamWriter(
                        new FileOutputStream("E:\\demo\\gbk4.txt"), "GBK"));  //文件gbk4.txt生成成功,且文件编码是GBK
        pw6.println("hello");
        pw6.printf("name=%s,age=%d", "小明", 15);
        pw6.close();
    }
}

打印流特点:
1、只有写数据的没有读取数据
2、可以操作任意类型的数据
3、该流是可以直接操作文本文件的。

哪些流可以直接操作文本?
FileInputStreamFileOutputStreamFileReaderFileWriterPrintStreamPrintWriter。看API,查流对象的构造方法,如果同时有File类型和String类型的参数,一般来说就是可以直接操作文件的。

:输出流 PrintStream、PrintWriter 可以传参File、String、OutputStream,可以指定字符集,可以指定是否自动刷新,还可以写入任意类型数据

问题:PrintStream、PrintWriter构造函数没有 “是否追加方式写入文件” ,怎么让 PrintStream、PrintWriter 可以追加呢?

// PrintStream
PrintStream pw7 = new PrintStream(
                new FileOutputStream("E:\\demo\\gbk2.txt", true), true, "GBK"); 

// PrintWriter
PrintWriter pw8 = new PrintWriter(
                new OutputStreamWriter(
                        new FileOutputStream("E:\\demo\\gbk4.txt", true), "GBK"));

标准输入输出流

System类中的两个成员变量:
public static final InputStream in    “标准”输入流。
public static final PrintStream out   “标准”输出流。

InputStream is = System.in;
PrintStream ps = System.out;

问题:首先,标准输入流是InputStream,而我们怎么把标准输入一次获取一行?

思路:1、每次获得一行方法BufferedReader.readLine()。
2、BufferedReader构造方法BufferedReader(Reader in)。
3、而标准输入流是InputStream,需要通过转换流 InputStreamReader 转换成 Reader。

标准输入流
public class SystemDemo {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

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

        System.out.println("请输入一个整数:");
        line = br.readLine();
        int i = Integer.parseInt(line);
        System.out.println("你输入的整数是:" + i);
    }
}

请输入一个字符串:
1111
你输入的字符串是:1111
请输入一个整数:
12
你输入的整数是:12
标准输出流
public class SystemDemo {
    public static void main(String[] args) throws IOException {
        System.out.println("helloworld");

        // 获取标准输出流对象
        PrintStream ps = System.out;
        ps.println("helloworld");

        // 使用缓存输出流把数据输出到控制台
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        bw.write("helloworld");
        bw.newLine();
        bw.flush();

        bw.close();
    }
}

输出:
helloworld
helloworld
helloworld

随机访问流

public class RandomAccessFile extends Object

RandomAccessFile类不属于流,是Object类的子类。但它融合了 InputStream 和 OutputStream 的功能,支持对文件的随机访问读取和写入。

RandomAccessFile 构造方法

  • RandomAccessFile(File file, String mode):第一个参数是文件路径,第二个参数是操作文件的模式
  • RandomAccessFile(String name, String mode)

mode 参数指定用以打开文件的访问模式

含意
r以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
rw打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
rws打开以便读取和写入。对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
rwd打开以便读取和写入。对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。
public class SystemDemo {
    public static void main(String[] args) throws IOException {
        write();
        read();
    }

    private static void read() throws IOException {
        // 创建随机访问流对象
        RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");

        int i = raf.readInt();
        System.out.println(i);
        // 该文件指针可以通过 getFilePointer方法读取,并通过 seek 方法设置。
        System.out.println("当前文件的指针位置是:" + raf.getFilePointer());

        char ch = raf.readChar();
        System.out.println(ch);
        System.out.println("当前文件的指针位置是:" + raf.getFilePointer());

        String s = raf.readUTF();
        System.out.println(s);
        System.out.println("当前文件的指针位置是:" + raf.getFilePointer());

        // 我不想重头开始了,我就要读取a,怎么办呢?
        raf.seek(4);
        ch = raf.readChar();
        System.out.println(ch);
    }

    private static void write() throws IOException {
        // 创建随机访问流对象
        RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");

        // 怎么玩呢?
        raf.writeInt(100);
        raf.writeChar('a');
        raf.writeUTF("中国");

        raf.close();
    }
}

输出:
100
当前文件的指针位置是:4
a
当前文件的指针位置是:6
中国
当前文件的指针位置是:14
a
  • void seek(long pos)设置到此文件开头测量到的文件指针偏移量
  • long getFilePointer():返回此文件中的当前偏移量

合并流

public class SequenceInputStream extends InputStream

SequenceInputStream构造方法

  • SequenceInputStream(InputStream s1, InputStream s2)
  • SequenceInputStream(Enumeration<? extends InputStream> e)
public class SequenceInputStreamDemo {
    public static void main(String[] args) throws IOException {
        // SequenceInputStream(InputStream s1, InputStream s2)
        InputStream s1 = new FileInputStream("E:\\demo\\1.txt");
        InputStream s2 = new FileInputStream("E:\\demo\\2.txt");
        SequenceInputStream sis = new SequenceInputStream(s1, s2);
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("E:\\demo\\Copy.txt"));

        // 如何写读写呢,其实很简单,你就按照以前怎么读写,现在还是怎么读写
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = sis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }

        bos.close();
        sis.close();
    }
}

生成的 Copy.txt 文件内容是 1.txt 和 2.txt 合并后内容。

问题:如果有多个文件需要合并呢?

方法一:嵌套使用SequenceInputStream。

InputStream s3 = new FileInputStream("E:\\demo\\3.txt");
SequenceInputStream sis2 = new SequenceInputStream(sis, s3);

如果文件特别多,会使代码臃肿。SequenceInputStream 提供另一种构造方法:

方法二:Vector集合

public class SequenceInputStreamDemo {
    public static void main(String[] args) throws IOException {
        // SequenceInputStream(InputStream s1, InputStream s2)
        InputStream s1 = new FileInputStream("E:\\demo\\1.txt");
        InputStream s2 = new FileInputStream("E:\\demo\\2.txt");
        InputStream s3 = new FileInputStream("E:\\demo\\3.txt");

        Vector<InputStream> vector = new Vector<InputStream>();
        vector.add(s1);
        vector.add(s2);
        vector.add(s3);
        Enumeration<InputStream> elements = vector.elements();

        SequenceInputStream sis = new SequenceInputStream(elements);
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("E:\\demo\\Copy.java"));

        // 如何写读写呢,其实很简单,你就按照以前怎么读写,现在还是怎么读写
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = sis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }

        bos.close();
        sis.close();
    }
}

使用集合vector.elements()方法为 SequenceInputStream(Enumeration<? extends InputStream> e) 提供构造参数。

序列化流

序列化流:把对象按照流一样的方式写入文本文件或者在网络中传输。 对象 – 流数据
public class ObjectOutputStream extends OutputStream

反序列化流读取文本文件中的流对象数据或者网络中的流对象数据还原成对象。 流数据 – 对象
public class ObjectInputStream extends InputStream

  • ObjectOutputStream构造方法
    • protected ObjectOutputStream()
    • public ObjectOutputStream(OutputStream out)
  • 常用方法
    • public final void writeObject(Object obj):将指定的对象写入 ObjectOutputStream。
  • ObjectInputStream构造方法
    • protected ObjectInputStream()
    • public ObjectInputStream(InputStream in)
  • 常用方法
    • public final Object readObject():从 ObjectInputStream 读取对象。
    • public ObjectInputStream.GetField readFields():按名称从流中读取持久字段并使其可用。
      • abstract int get(String name, int val):从持久字段获取指定字段的值。val:name 没有值时使用的默认值。可以多种数据类型
      • abstract short get(String name, short val):从持久字段获取指定字段的值。
      • abstract Object get(String name, Object val):从持久字段获取指定字段的值。

读写单个对象

Stupublic class Stu implements Serializable {
    public static final long serialVersionUID = 1L;

    public String name;
    public int age;

	setter、getter方法
}

public class ObjectDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 序列化
        // write();

        read();
    }

    private static void write() throws IOException {
        // 创建序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\demo\\1.txt"));

        // 创建对象
        Stu p = new Stu("lili", 27);

        // public final void writeObject(Object obj)
        oos.writeObject(p);

        // 释放资源
        oos.close();
    }

    private static void read() throws IOException, ClassNotFoundException {
        // 创建反序列化对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\demo\\1.txt"));

        // 读取对象
        Object obj = ois.readObject();

        // 释放资源
        ois.close();

        // 输出对象
        System.out.println(obj);
    }
}

输出:
Person [name=lili, age=27]

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。Serializable接口居然没有任何方法,类似于这种没有方法的接口被称为标记接口

注:如果一个类需要序列化,其成员变量类也必须实现Serializable接口。

新增成绩类,声明 Score 类为Stu 的成员变量。

public class Score{
    public String course;
    public String grade;

    public Score(String course, String grade) {
        this.course = course;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Score{" + "course='" + course + '\'' + ", grade='" + grade + '\'' + '}';
    }
}

public class Stu implements Serializable {
    public static final long serialVersionUID = 1L;

    public String name;
    public  int age;
    public Score score;

    ...
}

运行结果:

Exception in thread "main" java.io.NotSerializableException: 文件.Score
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at 文件.ObjectDemo.write(ObjectDemo.java:23)
	at 文件.ObjectDemo.main(ObjectDemo.java:10)

报错:Score 类没有实现 Serializable接口。

修改类Score,让其实现 Serializable 接口。(其实只是类声明实现下 Serializable 接口,也不用重写任何方法,因为 Serializable 是个标记接口)

public class Score implements Serializable {
    public static final long serialVersionUID = 1L;  //可加可不加

    public String course;
    public String grade;

    public Score(String course, String grade) {
        this.course = course;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Score{" + "course='" + course + '\'' + ", grade='" + grade + '\'' + '}';
    }
}

运行结果:

Stu{name='lili', age=28, score=Score{course='math', grade='90'}}
Stu{name='中文', age=20, score=Score{course='math', grade='91'}}
Stu{name='jack', age=28, score=Score{course='math', grade='92'}}
serialVersionUID

每次修改 java 文件的内容的时候,class文件的 id 值都会发生改变。如果写入对象后,对象 java 文件有过修改,相应的 class 文件的 id 改变,再读取对象时就会报错。

通过设置 java 文件的 serialVersionUID,可以使 java 文件改变,其对应 class 文件的 id 不改变。

serialVersionUID有两种显示的生成方式:

  • 默认的1L,比如:private static final long serialVersionUID = 1L;
  • 根据包名,类名,继承关系,非私有的方法和属性,以及参数,返回值等诸多因子计算得出的,极度复杂生成的一个64位的哈希字段。基本上计算出来的这个值是唯一的。比如:private static final long serialVersionUID = xxxxL;

一般使用默认的1L,这样可以通过serialVersionUID知道该类的版本号

1、在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
2、在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

transient

transient关键字声明不需要序列化的成员变量

Stupublic class Stu implements Serializable {
    public static final long serialVersionUID = 1L;

    public String name;
    transient public int age;

	setter、getter方法
}

输出:
Person [name=lili, age=0]

从结果看出,age 属性变为了 0,说明被transient关键字修饰之后没有被序列化。底层原理上,serialization就是把对象的状态存储到硬盘上去,等需要的时候就可以再把它读出来使用。而transient修饰变量的生命周期仅存于调用者的内存中,而不会写到磁盘里持久化。

static

静态变量不管有没有被transient关键字修饰,都会被序列化。
因为静态变量在全局区,而我们的序列化是写到磁盘上的,JVM 查找这个静态变量的值,是从全局区查找的,而不是磁盘上。

读写多个对象

public class ObjectDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 序列化
        write();

        read();
    }

    private static void write() throws IOException {
        // 创建序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\demo\\1.txt"));

        // 创建对象
        Stu p = new Stu("lili", 28);

        // public final void writeObject(Object obj)
        oos.writeObject(p);
        oos.writeObject(new Stu("中文",20));
        oos.writeObject(new Stu("jack",28));

        // 释放资源
        oos.close();
    }

    private static void read() throws IOException, ClassNotFoundException {
        // 创建反序列化对象
        FileInputStream fileInputStream = null;
        fileInputStream = new FileInputStream("E:\\demo\\1.txt");
        ObjectInputStream ois = new ObjectInputStream(fileInputStream);

        // 读取对象
        Object obj = null;
        while (fileInputStream.available() > 0) {
            // 输出对象
            obj = ois.readObject();
            System.out.println(obj);
        }

        // 释放资源
        ois.close();
    }
}

输出:
Person [name=lili, age=28]
Person [name=中文, age=20]
Person [name=jack, age=28]

fileInputStream.available() > 0判断是否有可读对象

Properties

public class Properties extends Hashtable<Object,Object>
属性集合类是一个可以和 IO 流相结合使用的集合类可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

  • Properties构造方法
    • Properties():创建一个无默认值的空属性列表。
    • Properties(Properties defaults):创建一个带有指定默认值的空属性列表。
  • 特有方法
    • public Object setProperty(String key, String value):添加元素
    • public String getProperty(String key):获取元素
    • public Set<String> stringPropertyNames():获取所有的键的集合
  • 和 IO 流结合的方法
    • public void load(InputStream inStream)
    • public void load(Reader reader):把键值对形式的文本文件内容加载到集合中
    • public void store(OutputStream out, String comments)
    • public void store(Writer writer, String comments):把集合中的数据存储到文本文件中,comments是配置描述
public class PropertiesDemo {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();

        // 添加元素
        prop.put("it002", "hello");
        prop.put("it001", "world");
        prop.put("it003", "java");

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

        // 添加元素
        prop.setProperty("it003", "30");  //方法内部调用 put(key, value);
        prop.setProperty("it004", "40");
        prop.setProperty("it005", "50");

        // public Set<String> stringPropertyNames():获取所有的键的集合
        Set<String> set2 = prop.stringPropertyNames();
        for (String key : set2) {
            String value = prop.getProperty(key);
            System.out.println(key + "+++" + value);
        }

        myStore();

        myLoad();
    }

    private static void myStore() throws IOException {
        // 创建集合对象
        Properties prop = new Properties();

        prop.setProperty("孙悟空", "27");
        prop.setProperty("猪八戒", "30");
        prop.setProperty("唐僧", "18");

        // public void store(Writer writer,String comments):把集合中的数据存储到文件
        Writer w = new FileWriter("E:\\demo\\1.txt");
        prop.store(w, "西游记");
        w.close();
    }

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

        // public void load(Reader reader):把文件中的数据读取到集合中
        // 注意:这个文件的数据必须是键值对形式
        Reader r = new FileReader("E:\\demo\\1.txt");
        prop.load(r);
        r.close();

        System.out.println("prop:" + prop);
    }
}

输出:
it003---java
it002---hello
it001---world

it005+++50
it004+++40
it003+++30
it002+++hello
it001+++world
prop:{孙悟空=27, 唐僧=18, 猪八戒=30}

文件E:\demo\1.txt内容:

#\u897F\u6E38\u8BB0
#Mon Jun 07 00:44:14 CST 2021
孙悟空=27
唐僧=18
猪八戒=30

从输出看出,key(it003) 的值被覆盖,因为setProperty(key, value)内部调用就是 put(key, value)

文件内容第一行,中文显示有问题,即使使用转换流Writer w = new OutputStreamWriter(new FileOutputStream("E:\\demo\\1.txt"), "utf-8");也不行。查看源码,发现有特殊处理…

而且,store()方法第一个参数是文本内容的编码,只会影响写入文件内容编码,并不会影响写入文件第一行内容。

应用:判断配置文件是否有“lisi”这样的键存在,如果有就改变其实为”100”

public class PropertiesDemo {
    public static void main(String[] args) throws IOException {
        // 把文件中的数据加载到集合中
        Properties prop = new Properties();
        Reader r = new FileReader("user.txt");
        prop.load(r);
        r.close();

        // 遍历集合,获取得到每一个键
        Set<String> set = prop.stringPropertyNames();
        for (String key : set) {
            // 判断键是否有为"lisi"的,如果有就修改其值为"100"
            if ("lisi".equals(key)) {
                prop.setProperty(key, "100");
                break;
            }
        }

        // 把集合中的数据重新存储到文件中
        Writer w = new FileWriter("user.txt");
        prop.store(w, null);
        w.close();
    }
}

小结

  1. 读取文本文件,使用字符缓冲流 BufferedReader.readLine每次读取一行
  2. 写入文本文件,使用 PrintStream 可以传参File、String、OutputStream,可以指定字符集可以指定是否自动刷新还可以写入任意类型数据
  3. 读取图片、视频,使用字节流 BufferedInputStreamBufferedOutputStream

注:使用字节流读取,一定要用字节流写入;使用字符流读取,一定要用字符流写入。

NIO (Non-blocking I/O)

NIO(Non-blocking I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础。已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。

组成

NIO主要有三大核心部分:Channel(通道)Buffer(缓冲区), Selector传统IO基于字节流和字符流进行操作,而NIO基于 Channel 和 Buffer(缓冲区) 进行操作

NIO和传统IO(一下简称IO)之间第一个最大的区别是,IO是面向NIO是面向缓冲区

  • Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。它不能前后移动流中的数据。
  • NIO的数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。

阻塞性

  • IO的各种流是阻塞的。这意味着,当一个线程调用read()write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。
  • NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,直至数据变得可以读取之前,所以该线程可以继续做其他的事情。 非阻塞写也是如此。
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;

public class NIODemo {
    public static void main(String[] args) throws IOException {
        // public static long copy(Path source,OutputStream out)
        // Files.copy(Paths.get("ByteArrayStreamDemo.java"), new
        // FileOutputStream(
        // "Copy.java"));

        ArrayList<String> array = new ArrayList<String>();
        array.add("hello");
        array.add("world");
        array.add("java");
        Files.write(Paths.get("E:\\demo\\1.txt"), array, Charset.forName("GBK"));
        Files.copy(Paths.get("E:\\demo\\1.txt"),new FileOutputStream("E:\\demo\\2.txt"));
    }
}

文件E:\demo\1.txt写入,文件E:\demo\2.txt复制成功。

同步、异步 阻塞、非阻塞

同步、异步 阻塞、非阻塞

JAVA同步、异步、阻塞和非阻塞之间的区别

Java NIO浅析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会叫的狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值