Java

Java 21-5 工具流

2019-02-02  本文已影响0人  第二套广播体操

用于解决问题的特有流


文本输入输出流
PrintStream(字节流) PrintWriter(字符流)
特点:打印 不抛出异常
解决问题:
方便的打印各种数据值形式
保证数值的表现形式不变
(写的是什么样子 目的地就是什么样子)
PrintWriter 一样具备打印功能
目的:File对象 字符串路径 字节输出流 字符输出流
System.out.println 利用的就是PrintStream方法


System.in&out

示例1 演示PrintStream方法

public class PrintStream_Test1 {
    public static void main(String[] args) throws FileNotFoundException {
//        1.创建PrintStream对象 目的定位文件
        PrintStream out=new PrintStream("D:\\IO\\Test1.txt");
//       将数据打印到文件中
        out.write(97);
//        对于字节流的write方法 一次只写出一个字节 也就是将一个整数的最低8位写出
        out.println(97);
//        原理 将数值转换成字符串
        out.close();
    }
}

示例2 从键盘输入字母 并转成大写返回到显示器上

public class PrintWriter_Test1 {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        PrintWriter pw=new PrintWriter(System.out,true);
//        有缓冲区 做编码解码转换 true 为自动刷新功能
        String line=null;
        while ((line=br.readLine())!=null){
            if (line.equals("over")) {
                break;
            }
            pw.println(line.toUpperCase());}
        pw.close();
    }
}

序列流:流对象有序的排列 合并
SequenceInputStream
内部封装一个有序集合 将多个源封装成一个源 对应一个目的
将多个输入流 封装成一个输入流
特殊在构造器上一初始化就可以传入多个流
使用场景:对多个文件进行数据合并

用于操作对象的流对象。对象的序列化
ObjectInputStream ObjectOutputStream


构造方法
方法

常用到方法Collections.enumeration();


创建指定集合的枚举
示例1 将指定几个文件 合并成一个文件
public class SequenceInputStream_Test1 {
    public static void main(String[] args) throws IOException {
//        如何获取一个Enumeration呢?vector
        ArrayList<FileInputStream> a1=new ArrayList<>();
//        添加三个输出流对象,和指定的具体文件关联
        for (int i = 1; i <4; i++) {
            a1.add(new FileInputStream("D:\\IO\\"+i+".txt"));
        }
//        创建指定集合枚举
//        没有的功能去Collection中找
        Enumeration<FileInputStream> en= Collections.enumeration(a1);
//        创建序列流对象并添加枚举
        SequenceInputStream sis=new SequenceInputStream(en);
        FileOutputStream fos=new FileOutputStream("D:\\IO\\4.txt");
        byte[] bytes=new byte[1024];
        int len=0;
        while ((len=sis.read(bytes))!=-1)
            fos.write(bytes,0,len);
        sis.close();
        fos.close();
    }
}

随机读取存储流
RandomAccessFile
只能操作文件
即能读又能写 维护了一个byte数组 内部定义了字节流
通过指针的操作可以实现对文件任意位置的读取和写入

特点: 随机写入 需要指针 指针位置与字节有关
功能:getFilePointer seek用于操作文件指针的方法
可以多线程写入
多个线程每个写一部分

该类的实例支持读取和写入随机访问文件。 随机访问文件的行为类似于存储在文件系统中的大量字节。 有一种游标,或索引到隐含的数组,称为文件指针 ; 输入操作读取从文件指针开始的字节,并使文件指针超过读取的字节。 如果在读/写模式下创建随机访问文件,则输出操作也可用; 输出操作从文件指针开始写入字节,并将文件指针提前到写入的字节。 写入隐式数组的当前端的输出操作会导致扩展数组。 文件指针可以通过读取getFilePointer方法和由设置seek方法。

构造方法
String mode为对流的权限操作

mode值的意思是“R”打开仅供阅读。调用结果对象的任何写入方法都将导致引发IOException。”rw“开放阅读和写作。如果该文件不存在,则将尝试创建它。“RWS“与“rw”一样,开放读写,还要求对文件内容或元数据的每次更新都同步写入底层存储设备。”与“rw”一样,rwd“开放读写”,还要求对文件内容的每次更新都同步写入底层存储设备。
示例1 向文件中写入信息

public class RandomAccessFile_Test1 {
    public static void main(String[] args) throws IOException {
        writeFile();
    }

    private static void writeFile() throws IOException {
//     创建一个随机访问文件的对象
//     不存在创建 存在不创建 也不覆盖
        RandomAccessFile raf=new RandomAccessFile("D:\\IO\\random.txt","rw");
//        写入姓名和年龄
        raf.write("张三".getBytes());
        raf.writeInt(97);//保证整数字节的原样性
        raf.write("李四".getBytes());
        raf.writeInt(98);//保证整数字节的原样性
        raf.close();
    }
}

示例2 指针操作 覆盖年龄 及打印

public class RandomAccessFile_Test2 {
    public static void main(String[] args) throws IOException {
        RandomAccessFile raf=new RandomAccessFile("D:\\IO\\random.txt","rw");
        //设置指针位置
        raf.seek(10);
        raf.write("于松江".getBytes());
        raf.writeInt(99);
//        打印指针所在位置
        System.out.println(raf.getFilePointer());
        raf.seek(0);
//       创建缓冲区数组将名字放入字节数组中
        byte[] buf=new byte[6];
//        通过read自动解码
        raf.read(buf);
        String name=new String(buf);
        int age=raf.readInt();
        System.out.println(name+":"+age);
        raf.close();
    }
}

管道流
PipedInputStream PipedOutputStream
PipedReader PipedWriter
读取管道和写入管道可以连接
需要使用多线程技术 单线程死锁

管道输入流应连接到管道输出流; 管道输入流然后提供写入管道输出流的任何数据字节。 典型地,数据被从一个读PipedInputStream对象由一个线程并且数据被写入到对应的PipedOutputStream通过一些其它线程。 不建议尝试从单个线程使用这两个对象,因为它可能会使线程死锁。 管道输入流包含一个缓冲区,在读取操作中将读取操作与限制内的操作相分离。 的管道被认为是broken如果正在提供的数据字节到连接的管道输出流中的线程不再存活。

管道连接方法
方法

示例1 连同管道 并输出

public class PipedStream_Test {
    public static void main(String[] args) throws IOException {
//       创建管道对象
        PipedInputStream pis=new PipedInputStream();
        PipedOutputStream pos=new PipedOutputStream();
//        连接方法
        pos.connect(pis);
//        启动多线程
        new Thread(new Input(pis)).start();
        new Thread(new OutPut(pos)).start();
    }

}
//定义输入任务
class Input implements Runnable{
    private PipedInputStream pis;
//从外界接受一个管道输入流进来
    public Input(PipedInputStream pis) {
        this.pis = pis;
    }

    @Override
    public void run() {
        byte[] bytes=new byte[2014];
        int len= 0;
        try {
//            读入到数组中
            len = pis.read(bytes);
//            将字节数组整合成字符串
            String s=new String(bytes,0,len);
            System.out.println(s);
            pis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
//定义输出任务
class OutPut implements Runnable{
    private PipedOutputStream pos;

    public OutPut(PipedOutputStream pos) {
        this.pos = pos;
    }

    @Override
    public void run() {
        try {
//            写入到缓冲区中
            pos.write("hi,管道来了".getBytes());
            pos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

对象输入输出流 序列化和反序列化
ObjectInputStream
只有支持java.io.Serializable接口的对象才能写入流中。 每个可序列化对象的类被编码,包括类的类名和签名,对象的字段和数组的值以及从初始对象引用的任何其他对象的关闭。

方法writeObject用于将一个对象写入流中。 任何对象,包括字符串和数组,都是用writeObject编写的。 多个对象或原语可以写入流。 必须从对应的ObjectInputstream读取对象,其类型和写入次序相同。

原始数据类型也可以使用DataOutput中的适当方法写入流中。 字符串也可以使用writeUTF方法写入。

类的对象如果需要序列化 就需要实现一个Serializable标记接口
该接口给需要序列化的类提供了一个序列版本号 serialVersionUID
给被序列化的类添加的标识
序列号的目的在于验证序列化的对象和对应的类是否版本匹配
建立自己显示声明

   private static final long serialVersionUID = 291613027204248679L;
 private transient int age;
瞬态关键词 不需要持久化

serialVersionUID:
如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据Java(TM)对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值。 但是, 强烈建议所有可序列化的类都明确声明serialVersionUID值,因为默认的serialVersionUID计算对类详细信息非常敏感,这可能会因编译器实现而异,因此可能会在反InvalidClassException化期间导致InvalidClassException的InvalidClassException。

因此,为了保证不同Java编译器实现之间的一致的serialVersionUID值,一个可序列化的类必须声明一个显式的serialVersionUID值。 还强烈建议,显式的serialVersionUID声明在可能的情况下使用private修饰符,因为这种声明仅适用于立即声明的类 - serialVersionUID字段作为继承成员无效。 数组类不能声明一个显式的serialVersionUID,所以它们总是具有默认的计算值,但是对于数组类,放弃了匹配serialVersionUID值的要求。
示例 将对象存储到文件中

public class ObjectOutputStream_Test1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
//        ObjectWriter();//对象序列化
        ObjectReader();//对象反序列化


    }
    private static void ObjectWriter() throws IOException {
        FileOutputStream fos=new FileOutputStream("D:\\IO\\Person.object");
//        对象储存文件的规范
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        oos.writeObject(new Person("asda",21));
        oos.close();
//        NotSerializableException - 要序列化的某些对象不实现java.io.Serializable接口。
//        实现Serializable实现序列化
    }
                                                             //类没有找到异常
    private static void ObjectReader() throws IOException, ClassNotFoundException {
        FileInputStream fos=new FileInputStream("D:\\IO\\Person.object");
        ObjectInputStream ois=new ObjectInputStream(fos);
      Person obj=(Person) ois.readObject();
        System.out.println(obj.toString());
    }
}
public class Person implements Serializable{
    private static final long serialVersionUID = 291613027204248679L;
//    给类显示名声一个序列版本号
    private String name;
    private int age;
//    private transient int age;
//瞬态关键词 不需要持久化
    public Person(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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
上一篇下一篇

猜你喜欢

热点阅读