【程序员养成之路】Java基础篇 8-流进流出的IO流(二)

以下内容若有误,欢迎私信我或在下方留言,谢谢^_−


IO流(二)

1.特殊操作流

1.1 标准流
  • public static final InputStream in:标准输入流,一般用于键盘输入、主机环境或用户指定另一输入源。
  • public static final PrintStream out:标准输出流,一般用于显示输出、主机环境或用户指定另一输出源。

看了上面两个,也许会感觉很陌生,但其实一点都不陌生!比如获取键盘输入的Scanner scanner = new Scanner(System.in);,还有打印输出的System.out.println();

1.2 打印流
  • public class PrintStream:字节打印流
  • public class PrintWriter:字符打印流
/**
 * 使用字符打印流实现复制文件
 */
public class CopyFileDemo {
    public static void main(String[] args) throws IOException {
        // 创建字符输入流对象
        BufferedReader br = new BufferedReader(new FileReader("test.txt"));
        // 创建字符打印流对象,第二个参数代表打开自动刷新(flush())
        PrintWriter pw = new PrintWriter(new FileWriter("test2.txt"), true);
        // 读写数据,复制文件
        String line;
        while ((line = br.readLine()) != null) {
            pw.println(line);
        }
        // 释放资源
        pw.close();
        br.close();
    }
}
1.3 对象序列化流

(1)什么是序列化?

百度百科说:“序列化是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。”

看不懂上面的天文,emmm,没关系!这里简单打个比方,假如你去山上打山泉水,但这水你总得有个瓶瓶罐罐来装吧,所以你就把水装进了瓶子里面,这可以理解为就是一个序列化过程;而当你需要这些水的时候,你就把它们给倒出来,这就可以理解为是一个反序列化过程。

而对象序列化就是将一个对象转换为一串序列,反之,则是对象反序列化。

(2)分类

  • ObjectOutputStream:对象序列化流
  • ObjectInputStream:对象反序列化流
/**
 * 演示对象序列化流和对象反序列化流
 */
public class ObjectStreamDemo {
    public static void main(String[] args) throws Exception {
        // 序列化
        // ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
        // 创建对象
        Student student = new Student("爪哇", 20);
        // void writeObject(Object obj):将指定的对象写入ObjectOutputStream
        oos.writeObject(student);
        // 释放资源
        oos.close();
        
        // 反序列化
        // ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.txt"));
        // Object readObject():从ObjectInputStream读取一个对象
        Object object = ois.readObject();
        // 向下转型
        Student s = (Student) object;
        System.out.println(s.getName() + "\t" + s.getAge());
        // 释放资源
        ois.close();
    }
}

/**
 * 学生类
 * 注意:
 *      一个对象被序列化,必须实现Serializable接口
 *      Serializable是一个标记接口,实现该接口,不需要重写任何方法
 */
class Student implements Serializable {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

(3)InvalidClassException异常

当用对象序列化流对一个对象进行序列化后,假如对所属的类文件进行了修改,然后再读取数据时,会抛出InvalidClassException异常。

如何处理该异常?只需在所属类中添加一个serialVersionUID,具体代码如下:

private static final long serialVersionUID = 42L;

如果一个对象中的某个成员变量的值不想被序列化,那可以给该成员变量添加transient关键字修饰,标记该成员变量不参与序列化。

1.4 Properties

(1)概述

  • Properties是一个Map体系的集合类,继承自Hashtable,是线程安全的。
  • Properties可以保存到流中或从流中加载。

(2)特有方法

方法名说明
Object setProperty(String key, String value)设置集合的键和值,都是String类型,底层调用Hashtable的put()方法
String getProperty(String key)从属性列表中查找指定键的值
Set< String> stringPropertyNames()从属性列表中返回一个不可修改的键集,其中键及其值都是字符串

(3)与IO流结合的方法

方法名说明
void load(InputStream inStream)从输入字节流读取属性列表(键和元素对)
void load(Reader reader)从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(InputStream)方法加载到 Properties表中的格式输出流。
void store(Writer writer, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式输出到输出字符流。

拓展1:比较字节流和字节缓冲流的效率

/*
    比较四种方式实现复制音乐的速度:
        1.基本字节流一次读写一个字节             共耗时56049毫秒。
        2.基本字节流一次读写一个字节数组         共耗时72毫秒。
        3.字节缓冲流一次读写一个字节            共耗时240毫秒。
        4.字节缓冲流一次读写一个字节数组        共耗时14毫秒。
 */

import java.io.*;

public class CopyMusic {
    public static void main(String[] args) throws Exception {
        // 记录开始时间
        long startTime = System.currentTimeMillis();
        // 复制音乐
        // method1();
        // method2();
        // method3();
        method4();
        // 记录结束时间
        long endTime = System.currentTimeMillis();
        // 打印复制所耗费的时间
        System.out.println("共耗时" + (endTime - startTime) + "毫秒。");
    }


    /**
     * 1.基本字节流一次读写一个字节
     */
    private static void method1() throws Exception {
        FileInputStream fis = new FileInputStream("E:\\KuGou\\梦然 - 少年.mp3");
        FileOutputStream fos = new FileOutputStream("梦然 - 少年.mp3");

        int by;
        while ((by = fis.read()) != -1) {
            fos.write(by);
        }
        // 释放资源
        fos.close();
        fis.close();
    }

    /**
     * 2.基本字节流一次读写一个字节数组
     */
    private static void method2() throws Exception {
        FileInputStream fis = new FileInputStream("E:\\KuGou\\梦然 - 少年.mp3");
        FileOutputStream fos = new FileOutputStream("梦然 - 少年.mp3");

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

    /**
     * 3.字节缓冲流一次读写一个字节
     */
    private static void method3() throws Exception {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\KuGou\\梦然 - 少年.mp3"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("梦然 - 少年.mp3"));

        int by;
        while ((by = bis.read()) != -1) {
            bos.write(by);
        }
        // 释放资源
        bos.close();
        bis.close();
    }

    /**
     * 4.字节缓冲流一次读写一个字节数组
     */
    private static void method4() throws Exception {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\KuGou\\梦然 - 少年.mp3"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("梦然 - 少年.mp3"));

        byte[] bys = new byte[1024];
        int len;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
        // 释放资源
        bos.close();
        bis.close();
    }
}

拓展2:编码表

(1)计算机中任何存储的信息都是采用二进制表示的。

(2)编码:按照某种规则,将字符转换为二进制数并存储到计算机中。

(3)解码:按照某种规则,将存储在计算机中的二进制数解析显示出来。

编码和解码使用的编码格式必须相同,否则会出现乱码。

(4)字符编码:字符编码就是一套自然语言的字符与二进制数之间的对应规则,比如ASCII码中A→65、a→97等。

(5)字符集:字符集是一个系统支持的所有字符的集合,包括各个国家的文字、数字、标点符号、图形符号等。常见字符集有ASCII、GBK、UTF-8、Unicode等。

拓展3:String、StringBuffer、StringBuilder的区别

(1)String

String的值是不可变的,即每次对String的操作都会生成新的String对象。不仅效率低,而且会浪费大量有限的内存空间。

(2)StringBuffer

StringBuffer是可变的,即每次对StringBuffer的操作都不会产生新的对象,而是在原来的对象上进行修改。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量;当字符串大小超过容量时,会自动增加容量。StringBuffer是线程安全的,常用于多线程操作字符串缓冲区。

(3)StringBuilder

与StringBuffer一样,StringBuilder也是可变的。然而,它是线程不安全的,但其速度更快,常用于单线程操作字符串缓冲区。


【程序员养成之路】Java基础篇 1-聊聊Java那些事

【程序员养成之路】Java基础篇 2-初学Java必知的基础语法

【程序员养成之路】Java基础篇 3-反手就能写个冒泡排序的数组

【程序员养成之路】Java基础篇 4-从面向对象里找对象

【程序员养成之路】Java基础篇 5-从异常机制认识常见bug

【程序员养成之路】Java基础篇 6-啥都能“装”的集合

【程序员养成之路】Java基础篇 7-流进流出的IO流(一)

【程序员养成之路】Java基础篇 9-认识一下类加载器与反射


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

正则表达式1951

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

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

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

打赏作者

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

抵扣说明:

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

余额充值