Java总结IO篇之其他IO流对象

零、前言:

本篇涉及到的流: 1.PrintWriter:字符打印输出流 2.PrintStream:字节打印输出流 3.SequenceInputStream :合并多个字节输入流 4.RandomAccessFile:随机操作文件 5.ObjectOutputStream与ObjectInputStream :对象的序列化流 6.DataInputStream与DataOutputStream :基本数据类型操作流 7.ByteArrayInputStream与ByteArrayOutputStream:字节数组操作流

一、PrintWriter:字符打印输出流

PrintWriter.png

1.构造方法
File file 文件
File file, String csn 文件,字符集 
String fileName 文件名 
String fileName, String csn 文件名,字符集
OutputStream out 字节输出流
OutputStream out, boolean autoFlush 字节输出流,是否自动刷新缓冲区
Writer out 字符输出流
Writer out, boolean autoFlush 字符输出流,是否自动刷新缓冲区  

无论是文件也好,字符串也好,字节输出流,字符输出流也好,总之一句话: 给我一个输出流,还你一个PrintWriter

使用:键盘录入,控制台输出大写
public class PrintStreamTest {
    public static void main(String[] args) throws IOException {
        BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
        //使用控制台输出流创建一个自动刷新缓冲区的PrintWriter对象
        PrintWriter pw = new PrintWriter(System.out,true);
        String line = null;
        while ((line = bfr.readLine()) != null) {
        //pw.write(line.toUpperCase());//不带换行
        //pw.flush();
            pw.println(line.toUpperCase());//自带换行,自带刷新
        }
        bfr.close();
        pw.close();
    }
}
使用:键盘录入,输出大写到文件

想要将键盘录入保存到文件中,只要将控制台输出流换成文件输出流即可 其他部分同上

String path = "I:\\Java\\Base\\Thinking\\src\\IOTest\\PrintWriter.txt";
PrintWriter pw = new PrintWriter(new FileWriter(path), true);

PrintWriter输出到文件.png


二、PrintStream:字节打印输出流

PrintStream.png

File file 文件
File file, String csn 文件,字符集
String fileName 文件名
String fileName, String csn 文件名,字符集
OutputStream out 字节输出流
OutputStream out, boolean autoFlush  字节输出流,是否自动刷新缓冲区
OutputStream out, boolean autoFlush, String encoding 字节输出流,是否自动刷新缓冲区,字符集

无论是文件也好,字符串也好,字节输出流,总之一句话: 给我一个字节输出流,还你一个PrintStream

public class PrintWriterTest {
    public static void main(String[] args) {
        try {
            int a = Integer.parseInt("a");
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
    }
}
java.lang.NumberFormatException: For input string: "a"
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.base/java.lang.Integer.parseInt(Integer.java:652)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)
    at top.toly.IO.io.其他流.PrintWriterTest.main(PrintWriterTest.java:12)

所有异常继承自:Throwable 类 其中有个不起眼的方法printStackTrace(),一般放着也没人管 但它有的重载的方法void printStackTrace​(PrintStream s) 可以自定义输出流

public class PrintStreamTest {
    public static void main(String[] args) throws FileNotFoundException {
        try {
            int a = Integer.parseInt("a");
        } catch (NumberFormatException e) {
            e.printStackTrace();//默认是输出到控制台:即System.out流
            //将信息打印到F:\log.txt文件中
            e.printStackTrace(new PrintStream("F:\\log.txt"));
        }
    }
}

输出错误日志到文件.png

也可以加入异常的时间

//将信息打印到F:\log.txt文件中
PrintStream ps = new PrintStream("F:\\log.txt");
ps.println(new SimpleDateFormat(" G yyyy/MM/dd 星期--EE a hh:mm:ss ").format(new Date().getTime()));
e.printStackTrace(ps);//默认是输出到控制台:即System.out流

三、SequenceInputStream :合并多个字节输入流

InputStream s1, InputStream s2  两个字节流对象,先读s1,再s2
(Enumeration<? extends InputStream> e)  
1.将两个文件的数据合并到一个文件
public class SISTest {
    public static void main(String[] args) throws Exception {
        FileInputStream fis1 = new FileInputStream("I:\\Java\\Base\\Thinking\\src\\IOTest\\FileInputStream.txt");
        FileInputStream fis2 = new FileInputStream("I:\\Java\\Base\\Thinking\\src\\IOTest\\TxtInfo.ini");
        //使用Vector获取Enumeration对象
        Vector<InputStream> vec = new Vector<>();
        vec.add(fis1);
        vec.add(fis2);
        SequenceInputStream sis = new SequenceInputStream(vec.elements());//合并输入流
        //创建输出流
        FileOutputStream fos = new FileOutputStream("I:\\Java\\Base\\Thinking\\src\\IOTest\\SequenceInputStream.txt");

        int len = 0;
        byte[] buf = new byte[1024];
        while ((len = sis.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        sis.close();
        fos.close();
    }
}

SequenceInputStream.png

2.文件切分

当一个文件过大时,可以分割成多个小块 比如将一个1GB的电影分割成10份,每份100+M,由于字节不完整,导致无法播放 所以别人也不知道是什么电影 想看时用合并流合并一下,就能正常播放了。 可以搞个切合播放器,关闭播放器将电影切割,需要打开时碎片合并,然后就神不知鬼不觉。

目标文件Activity.md --7.34 KB (7,521 字节),按3KB大小切

public class SplitFile {
    public static void main(String[] args) throws Exception {
        String pathS = "I:\\Java\\Base\\Thinking\\src\\IOTest\\Activity.md";
        File fileS = new File(pathS);
        FileInputStream fis = new FileInputStream(pathS);
        //获取待切分文件名,以它作文文件夹,放入切分后的
        File parent = new File(fileS.getParentFile().getAbsolutePath()
                + File.separator + fileS.getName().split("\\.")[0]);
        parent.mkdir();
        int count = 0;
        int len = 0;
        byte[] buf = new byte[1024 * 3];//每份3kb,最后一份小于或等于3kb
        while ((len = fis.read(buf)) != -1) {
            File fileT = new File(parent, (count++) + ".temp");
            FileOutputStream fos = new FileOutputStream(fileT);
            fos.write(buf, 0, len);
            fos.close();
        }
        fis.close();
    }
}

切割文件.png

合并

public class SISTest {
    public static void main(String[] args) throws Exception {
        FileInputStream fis1 = new FileInputStream("I:\\Java\\Base\\Thinking\\src\\IOTest\\Activity\\0.temp");
        FileInputStream fis2 = new FileInputStream("I:\\Java\\Base\\Thinking\\src\\IOTest\\Activity\\1.temp");
        FileInputStream fis3 = new FileInputStream("I:\\Java\\Base\\Thinking\\src\\IOTest\\Activity\\2.temp");
        //使用Vector获取Enumeration对象
        ArrayList<InputStream> list = new ArrayList<>();
        list.add(fis1);
        list.add(fis2);
        list.add(fis3);
        //基于ArrayList合并流:需自定义Enumeration
        final Iterator<InputStream> it = list.iterator();
        Enumeration<InputStream> en = new Enumeration<>() {
            @Override
            public boolean hasMoreElements() {
                return it.hasNext();
            }
            @Override
            public InputStream nextElement() {
                return it.next();
            }
        };
        SequenceInputStream sis = new SequenceInputStream(en);//合并输入流
        //创建输出流
        FileOutputStream fos = new FileOutputStream("I:\\Java\\Base\\Thinking\\src\\IOTest\\Activity\\Activity.md");
        int len = 0;
        byte[] buf = new byte[1024];
        while ((len = sis.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        sis.close();
        fos.close();
    }
}

四、对象的序列化流

使用ObjectOutputStream将对象序列化成为数据输出-->将对象持久存储 使用ObjectInputStream进行读取序列化的数据-->恢复先前对象 只能序列化堆中的对象,static修饰的成员变量不能被序列化 transient修饰的成员变量,即使在堆内存中也不会被序列化

1、ObjectOutputStream :对象的序列化输出流
 private static void writeObject() throws IOException {
     String path = "I:\\Java\\Base\\Thinking\\src\\IOTest\\ObjectOutputStream.txt";
     ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
     //Person需要序列化implements Serializable:否则ERROR----NotSerializableException
     Person toly = new Person("捷特", 24);
     oos.writeObject(toly);
     oos.close();
 }
public class Person implements Serializable {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //get()、set()、toSring()省略
}

将对象序列化到文件中.png

2、ObjectInputStream :对象的序列化输入流

相当于给个文件给你,你直接读出来一个对象,创建,赋值什么的都已经搞定了 对于十分复杂的对象序列化还是很方便的,但由于是IO,相对比较耗时

private static void readObject() throws IOException, ClassNotFoundException {
    String path = "I:\\Java\\Base\\Thinking\\src\\IOTest\\ObjectOutputStream.txt";
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
    Person toly = (Person) ois.readObject();
    System.out.println(toly);//Person{name='捷特', age=24}

五、RandomAccessFile:随机操作文件

1.该类不是算是IO体系中子类。而是直接继承自Object。 2.但是它是IO包中成员。因为它具备读和写功能,内部封装字节输入流和输出流。 3.内部封装数组,通过指针对数组的元素进行操作,getFilePointer获取指针位置,通过seek改变指针的位置 4.只能操作磁盘文件,

构造方法:
File file, String mode     rw :读写模式   r : 只读 
String name, String mode    

1.文件写入
public class RAF_Test {
    public static void main(String[] args) throws IOException {
        String path = "I:\\\\Java\\\\Base\\\\Thinking\\\\src\\\\IOTest\\RandomAccessFile.txt";

        RandomAccessFile raf = new RandomAccessFile(path, "rw");
        raf.write("张风捷特烈".getBytes());
        raf.write(38);
        raf.write(527654);
        raf.close();
    }
}

RandomAccessFile.png

可见38和527654两个int值以&字符展现出来,utf-8码表第38为是&这情有可原,527654怎么也来插一脚 众所周知,一个int占4个字节,一个字节是8位,也就是一个int占32位,转换成二进制即下面:

3366
0000 0000 0000 1000 0000 1101 0010 0110
38
0000 0000 0000 0000 0000 0000 0010 0110
RandomAccessFile写入时int只写入低8位(因为字符写入,一次只能写一个字节即8位),即0010 0110

解决方法:将一个int分为4次来读,每次读一个字节(8位),写入文件

raf.writeInt(527654);//RandomAccessFile内部已经封装

RandomAccessFile2.png

2.读取
RandomAccessFile rafR = new RandomAccessFile(path, "r");
        byte[] buf = new byte[9];//一个utf-8汉字占三个字节,这里一次读三个汉字
        rafR.read(buf);
        System.out.println(new String(buf));//张风捷
        //这里用8,因为两个汉字3*2=6字节,加上2个&&一共8个字节。
        byte[] buf2 = new byte[8];
        rafR.read(buf2);
        System.out.println(new String(buf2));//特烈&&
        //读取int值:如果上面不是8,而是9,那么527654的字节就不完整,会报错
        System.out.println(rafR.readInt());//527654

字节显示.png

3.seek调节指针读取
RandomAccessFile rafR = new RandomAccessFile(path, "r");
rafR.seek(3);//将读取的指针移到第4个字节
byte[] buf = new byte[3];
rafR.read(buf);
System.out.println(new String(buf));//风
4.seek调节指针写出
String path = "I:\\\\Java\\\\Base\\\\Thinking\\\\src\\\\IOTest\\RandomAccessFile.txt";
RandomAccessFile rafRW = new RandomAccessFile(path, "rw");
rafRW.write("张风捷特".getBytes());
rafRW.write(38);
rafRW.write(527654);
rafRW.writeInt(527654);
rafRW.seek(40);
rafRW.write("巫缨".getBytes());
rafRW.close();

移动指针写数据.png


六、DataInputStream与DataOutputStream:基本数据类型操作流

DataXXStream.png


1.写出操作
private static void write() throws IOException {
    DataOutputStream dos = new DataOutputStream(new FileOutputStream(path));
    dos.writeBoolean(true);//1字节
    dos.writeInt(3366);//4字节
    dos.writeFloat(3.14f);//4字节
    dos.close();
}

DataOutputStream.png

2.读取操作
DataInputStream dis = new DataInputStream(new FileInputStream(path));
//注意按添加的顺序读取
System.out.println(dis.readBoolean());//true
System.out.println(dis.readInt());//3366
System.out.println(dis.readFloat());//3.14

七:ByteArrayInputStream与ByteArrayOutputStream:字节数组操作流

ByteArrayInputStream :在构造的时候,需要数据源:一个字节数组,缓冲区会随数据自动增长。 ByteArrayOutputStream: 在构造的时候,该对象中已经内部封装了可变长度的字节数组,是数据目的地。

public class BAIS_BAOS_Test {
    public static void main(String[] args) throws IOException {
        ByteArrayInputStream bais = new ByteArrayInputStream("张风捷特烈".getBytes());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        System.out.println(baos.size());//0
        int by = 0;
        while ((by = bais.read()) != -1) {
            baos.write(by);
        }
        System.out.println(baos.size());//15 = 3 * 5
        //写到控制台
        baos.writeTo(System.out);//张风捷特烈
        //写到文件
        String path = "I:\\Java\\Base\\Thinking\\src\\IOTest\\ByteArrayOutputStream.txt";
        baos.writeTo(new FileOutputStream(path));
    }
}

其他几个操作类似,顺便提一下

IO

流类型

操作数据

ByteArrayInputStream

输入流I

字节流

字节数组

ByteArrayOutputStream

输出流O

字节流

字节数组

CharArrayReader

输出流I

字符流

字符数组

CharArrayWriter

输出流O

字符流

字符数组

StringReader

输出流I

字符流

字符串

StringWriter

输出流O

字符流

字符串


本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个会写诗的程序员的博客

Kotlin 与 Java 对比Kotlin 开发者社区

1352
来自专栏技术小黑屋

探究Kotlin的局部方法

在Kotlin中,定义方法很有趣,不仅仅因为方法的关键字是fun(function前几个字符),还是因为你会惊奇的发现,它允许我们在方法中定义方法。如下

1443
来自专栏程序员与猫

[译]C#和.NET中的字符串

原文地址:Jon Skeet:Strings in C# and .NET System.String 类型(在C#语言中对应的别名是string)是.NET最...

23510
来自专栏Kotlin源码阅读

kotlin源码阅读——基础数据类型

基本和所有编程语言一样,基本数据类型有Byte/Short/Int/Long/Float/Double,且和Java一样都是有符号性的。

2973
来自专栏CDA数据分析师

那些容易被忽略的Python编程方式

Python 之禅 The Zen of Python, by Tim Peters Beautiful is better than ugly. 优美胜于丑陋...

21610
来自专栏机器学习算法与Python学习

Python: 早点知道这些就不会这样了

现在在Python 2的代码中都用import from future来导入Python 3的输出和除法。现在用到的几乎所有库都支持Python 3,因此会很快...

2754
来自专栏lonelydawn的前端猿区

lambda+reduce的一句艰深代码

一句话一脸懵逼 某天晚上看到一句lambda+reduce 组合的代码,看的头都炸了,愣是没看懂,不过也可能因为稀疏的脑神经经过一天的摧残已经运转不动了,这两天...

2008
来自专栏码匠的流水账

使用kotlin改善java代码

本文只是举了kotlin可以改善java代码的几个例子,kotlin太强大了,目标是要替代java。其中很多设计可以看到scala的影子,但是黑魔法也比较多,学...

751
来自专栏Android知识点总结

看得见的数据结构Android版之表的数组实现(数据结构篇)

831
来自专栏magicsoar

关于字符编码的那些事

一、编码是什么 编码为了某种目的把信息从一种形式集合转换为另一种形式集合的过程,古时的鸣金收兵,从某种意义上讲也是一种编码,将帅发出了退兵的命令,为了让更多的人...

2026

扫码关注云+社区

领取腾讯云代金券