IO总结之ByteArray,DataOutput,Object(序列化),PrintStream(三)

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxzxzx0119/article/details/79948480

目录

  • ByteArrayInputStream、ByteArrayOutputStream 字节数组处理流
  • DataInputStream、DataOutputStream(数据类型处理流 )
  • ObjectInputStream、ObjectOutputStream序列化,反序列化,对象存到文件(Serilizable)
  • 工具类关闭流
  • PrintStream以及System中的三个常量 : err,in,out
  • 装饰者模式
  • 随机流(RandomAccessFile类)和文件分割与合并SequenceInputStream

字节数组处理流(节点流)

这里写图片描述
- 就是将内容写到字节数组中(使用ByteArrayOutputStream),然后用ByteArrayInputStream读取字节数组中的内容;
- 注意输出流和文件输出流有点不同,而且当子类有新增的方法的时候不要使用多态,且可以用缓冲流提高访问速度;
- 注意输出流中的toByteArray()方法,将输出流中的内容读到字节数组中;

import java.io.*;
/**
 * 字节数组
 * 1.字节数组 字节  结点流
     输入流 : ByteArrayInputStream  read(byte[] b,int off,int len) + close()(空实现,可以不关闭)
     输出流 : ByteArrayOutputStream  write(byte[] b int off,int len) + toByteArray()(新方法)(不要使用多态)
 */
public class ByteArrayTest {

    public static void main(String[] args) throws IOException {
        read(write());
    }

    /**
     * 输入流,操作与文件输入流一致
     * @throws IOException
     */
    public static void read(byte[] src) throws IOException { //传入一个字节数组
        InputStream is = new BufferedInputStream(
                new ByteArrayInputStream(src)
        );
        byte[] flush = new byte[1024];
        int len = 0;
        while(-1 != (len = is.read(flush))) {
            System.out.println(new String(flush,0,len));
        }
        is.close();
    }

    /**
     * 输出流: 操作与文件输出流有些不同,有新增的方法,不能使用多态
     * @throws IOException
     */
    public static byte[] write() throws IOException { //返回值
        ByteArrayOutputStream os = new ByteArrayOutputStream(); //不能加上缓冲  有新增的方法
        String msg = "操作与文件输出流有些不同";
        byte[] info = msg.getBytes(); //得到字节数组
        os.write(info,0,info.length);

        //获取数据  得到结点流中的字节数组
        byte[] dest = os.toByteArray();
        os.close();
        return dest;
    }

}

使用字节数组流拷贝文件

  • 文件拷贝:
    1).文件内容内容不能太大
    2).文件 -程序-> 字节数组 使用 文件输入流 + 字节数组输出流
    3).字节数组 -程序-> 文件 使用 字节数组输入流 + 文件输出流
import java.io.*;
/**
 *  2.
    字节数组的长度有限,数据量不会很大
    文件拷贝:
    1)文件内容内容不能太大
    2)文件 -程序-> 字节数组   使用  文件输入流  + 字节数组输出流
    3)字节数组 -程序-> 文件   使用  字节数组输入流 + 文件输出流
 */
public class ByteArray2Test {

    public static void main(String[] args) throws IOException {
        byte[] data = getByteFromFile("/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/in.txt"); //不只是文件,也可以是图片等等

        System.out.println(new String(data));

        toFileFromByteArray(data,"/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/out.txt");
    }

    /**
     * 文件到字节数组    文件输入流 + 字节数组输出流
     * @param path
     * @throws IOException
     */
    public static byte[] getByteFromFile(String path) throws IOException {
        File src = new File(path);
        InputStream is = new BufferedInputStream(new FileInputStream(src)); //文件输入流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();  //字节数组输出流不能使用多态
        int len = 0;
        byte[] flush = new byte[1024];  //先读到字节数组中
        while(-1 != (len = is.read(flush))) {
            bos.write(flush,0,len); //将字节数组中的内容写到输出流中
        }
        byte[] dest = null;
        dest = bos.toByteArray();  //从输出流中获取字节数组
        bos.close();
        is.close();   //FileUtil.closeAll(bos,is);
        return dest;
    }

    /**
     *字节数组到文件
     */
    public static void toFileFromByteArray(byte[] src,String destPath) throws IOException {
        //目的地
        File dest = new File(destPath);
        OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));  //写到输出流中

        //字节数组输入流
        InputStream is = new BufferedInputStream(new ByteArrayInputStream(src)); //这个字节数组输入流可以使用多态
        //文件输出流
        int len = 0;
        byte[] flush = new byte[1024];
        while(-1 != (len = is.read(flush))) {
            os.write(flush,0,flush.length);
        }

        os.flush();//记得刷一下
        os.close();
        is.close();
    }
}

DataInputStream、DataOutputStream(数据类型处理流)

这里写图片描述
输入流 : DataInputStream,输出流 : DataOutputStream
方法:readXxx(), WriteXxx()
注意其中可能出现的异常:java.io.EOFException : 已经达到文件的末尾,没有读取到文件
还有要注意:读取的顺序和写出一致,必须存在才能读取

import java.io.*;

/**
 * DataStream(数据类型处理流)流,主要处理基本类型和String
 * 输入流 : DataInputStream,输出流 : DataOutputStream
 * readXxx(), WriteXxx()
 * java.io.EOFException : 已经达到文件的末尾,没有读取到文件
 */
public class DataTest {
    public static void main(String[] args) throws IOException {
        write("/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/in.txt");
        read("/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/in.txt");
    }

    /**
     * 写入数据类型到文件
     */
    public static void write(String destPath) throws IOException {
        double a = 2.3;
        long b = 567L;
        String str = "数据类型";

        File dest = new File(destPath);
        // 数据类型处理流 DataInputStream(使用新增的方法,不要使用上转型和多态)
        DataOutputStream os = new DataOutputStream(
                new BufferedOutputStream( //缓冲
                        new FileOutputStream(dest)
                )
        );
        os.writeDouble(a);
        os.writeLong(b);
        os.writeUTF(str);

        os.flush();
        os.close();
    }

    /**
     * 从文件读取中读取数据+ 类型
     * 读取的顺序和写出一致,必须存在才能读取
     */
    public static void read(String srcPath) throws IOException {
        DataInputStream is = new DataInputStream(
                new BufferedInputStream(new FileInputStream(new File(srcPath)))
        );
        //读取的顺序和写出一致,必须存在才能读取
        double num = is.readDouble();
        double num2 = is.readLong();
        String str = is.readUTF();

        System.out.println(num);
        System.out.println(num2);
        System.out.println(str);
    }
}

也可以使用这个写到字节数组中:

import java.io.*;
public class Data2Test {

    public static void main(String[] args) throws IOException {
        byte[] data = new byte[1024];
        System.out.println(new String(data = write())); //这个是看不懂的(乱码机器识别)
        read(data);
    }

    /**
     * 读到字节数组
     */
    public static byte[] write() throws IOException {
        double a = 2.3;
        long b = 567L;
        String str = "数据类型";
        byte[] dest = null;
        // 数据类型处理流 DataInputStream(使用新增的方法,不要使用上转型和多态)
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream os = new DataOutputStream(
                new BufferedOutputStream(
                        bos
                )
        );
        os.writeDouble(a);
        os.writeLong(b);
        os.writeUTF(str);
        os.flush();
        os.close();

        dest = bos.toByteArray();
        bos.close();
        return dest;
    }
    /**
     * 从字节数组读取
     */
    public static void read(byte[] src) throws IOException {
        ByteArrayInputStream ios = new ByteArrayInputStream(src);

        DataInputStream is = new DataInputStream(// 数据类型处理流 DataInputStream(使用新增的方法,不要使用上转型和多态)
                new BufferedInputStream(
                        ios
                )
        );

        double num = is.readDouble();
        double num2 = is.readLong();
        String str = is.readUTF();
        is.close();
        System.out.println(num);
        System.out.println(num2);
        System.out.println(str);
    }
}

效果: (第一行是字节,读不懂)
这里写图片描述


ObjectInputStream、ObjectOutputStream序列化,反序列化,对象存到文件(Serilizable)(引用类型)

  • 先序列化后反序列化;
  • 序列化就是对象存到文件 : 引用类型的存储 保留数据类型
  • 反序列化就是从文件中读取对象
  • 反序列化 : 输入流 ObjectInputStream : readObject()
  • 序列化 : 输出流 ObjectOutputStream : writeObject()

序列化的时候要注意:

  • 注意序列化的对象和反序列化的顺序要一致 ;
  • 不是所有的对象都可以序列化 : 要实现Serilizable
  • 不是所有的属性都需要序列化(对象中的某个属性存到文件就叫做对象的某个属性被序列化,如果没有被序列化就存到文件,读取的时候是Null,不需要序列化的属性用transient标记)

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

import java.io.*;
import java.util.Arrays;

public class ObjectTest {

    private static class Employee implements java.io.Serializable{  //要实现序列化必须是实现这个接口,必须实现,不然报错
        private transient String name;  //不需要序列化
        private double salary;      //只序列化 工资

        public Employee(String name, double salary) {
            super();
            this.name = name;
            this.salary = salary;
        }

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

        public double getSalary() {
            return salary;
        }

        public void setSalary(double salary) {
            this.salary = salary;
        }

    }


    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        seriwrite("/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/in.txt");
        serread("/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/in.txt");
    }

    /**
     * 序列化 : 序列化employee中的salary
     */
    public static void seriwrite(String destPath) throws FileNotFoundException, IOException {
        Employee emp = new Employee("zx", 10000);
        int[] arr = {1,2,3,4};
        File dest = new File(destPath);
        ObjectOutputStream os = new ObjectOutputStream(
                new BufferedOutputStream(
                        new FileOutputStream(dest)
                )
        );
        os.writeObject(emp);
        os.writeObject(arr);
        os.close();
    }

    /**
     * 反序列化
     */
    public static void serread(String srcPath) throws FileNotFoundException, IOException, ClassNotFoundException {

        File src = new File(srcPath);
        ObjectInputStream is = new ObjectInputStream(
                new BufferedInputStream(
                        new FileInputStream(src)
                )
        );

        Object obj = is.readObject();  //可以强转
        if(obj instanceof Employee) {
            Employee emp = (Employee)obj;
            System.out.println(emp.getName()); //输出为null,名字没有序列化(没有存到文件中)
            System.out.println(emp.getSalary());
        }

        int[]arr = (int[])is.readObject();
        System.out.println(Arrays.toString(arr));
        is.close();
    }
}

效果
这里写图片描述


工具类关闭流


import java.io.*;
public class FileCloseUtil {
    /**
     * 注意可变参数的使用 : 只能位于形参的最后一个位置,使用方法类似数组
     */
    public static void close(Closeable ... io) {
        for(Closeable temp : io) {
            if(null != temp)
                try {
                    temp.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    /**
     * 使用泛型
     */
    public static <T extends Closeable>void closeAll(T ... io) {
        for(Closeable temp : io) {
            if(null != temp) {
                try {
                    temp.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

PrintStream以及System中的三个常量 : err,in,out

这里写图片描述
PrintStream和IO的关系,可以从控制台读入输出,也可以从文件读入输出。

import java.io.*;

public class PrintStreamTest {

    public static void main(String[] args) throws FileNotFoundException {
        //输出到屏幕
        PrintStream out = System.out;
        out.println("test");

        //输出到文件
        File file = new File("/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/in.txt");
        out = new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); //写到文件
        out.println("io is so easy");
        out.close(); //一定要记得及时关闭
    }
}

效果:
这里写图片描述

知识:

  • InputStream从控制台输入
  • Scanner从文件解析读取
  • 重定向setIn(),setOut(),setErr()方法
import java.io.*;
import java.util.Scanner;

public class SystemTest {

    public static void main(String[] args) {
//        test1();
        try {
//          test2();
            test3();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static void test1() {
        System.out.println("out");
        System.err.println("err");
    }

    /**
     * Input从控制台输入和从文件解析
     */
    public static void test2() throws FileNotFoundException {
        InputStream is = System.in;    //输入流的父类
        Scanner sc = new Scanner(is);  //这个就是键盘输入
        System.out.println(sc.nextLine());

        //使用了多态  从文件读取 : 相当于Scanner解析吧
        String path = "/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/in.txt";
        InputStream re = new BufferedInputStream(
                new FileInputStream(path));
        sc = new Scanner(re);
        System.out.println(sc.nextLine());
    }

    /**
     * 重定向   setIn()  setOut()  setErr()方法
     */
    public static void test3() throws FileNotFoundException {
        //输出到文件
        String path = "/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/out.txt";
        System.setOut(new PrintStream(
                new BufferedOutputStream(
                        new FileOutputStream(path)), true)); //后面的true  代表的是自动刷新
        System.out.println("zxzxin");  //不需要手动的flush

        //然后回到控制台控制重写把out变成控制台输出
        //也要借助FileOutputStream文件流(控制台也是一个文件)
        System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)), true)); //后面的true 代表的是自动刷新
        System.out.println("back....");
    }
}

分别测试test1(),test2(),test3()的效果
这里写图片描述
这里写图片描述
这里写图片描述

使用InputStream和BufferedReader封装一个输入:
import java.io.*;

public class BufferedInTest {
    public static void main(String[] args) throws IOException {
        InputStream is = System.in;
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String msg = br.readLine();
        System.out.println(msg);
    }
}

装饰者模式

类与类之间的关系: 
  1.依赖 : 形参,局部变量
  2.关联 : 属性
        聚合 :  整体与部分 : 不一致的生命周期 : 人与手
        组合 :  整体与部分 : 一致的生命周期   : 人于大脑
  3.继承 : 父子关系
  4.实现 : 接口与实现类的关系

IO流的设计使用了装饰者设计模式,详细可以看这篇博客


随机流(RandomAccessFile类)和文件分割与合并SequenceInputStream

测试使用RandomAccessFile类:

import java.io.*;
public class RandAccessFileTest {
    public static void main(String[] args) throws IOException {
        RandomAccessFile rnd = new RandomAccessFile(
                new File("/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/in.txt"),
                "r");
        rnd.seek(3);      //从某个位置开始读

        byte[] flush = new byte[1024];
        int len = 0;
        while(-1 != (len = rnd.read(flush))) {
            if(len >= 4) {
                System.out.println(new String(flush,0,4));
            }else  {
                System.out.println(new String(flush,0,len));
            }
        }
        rnd.close();
    }
}

效果:
这里写图片描述

  • 再来看文件分割,意思就是传入一个源文件src.txt,将这个文件分割成几块,并放到指定的destPath中的某个目录中,分割成的每个文件都有一个名字,文件分割用到了RandomAccessFile。
  • 文件合并函数mergeFile将分割的几个文件合并到一个文件中,文件合并可以直接追加到文件尾,也可以将之前的那些流合并到SequenceInputStream中(先合并),然后再一起写到指定的文件中。

这里写图片描述

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

public class SplitFile {

    private String filePath;  //文件的路径
    private String fileName;  //文件的名字
    private long len;         //整个文件的长度
    private int size;         //快数
    private long blockSize;   //每块的大小
    private String destPath;  //分割后的存放目录
    private List<String> blockPath;   //存放每一个快的路径

    public SplitFile() {
        this.blockPath = new ArrayList<String>();
    }
    public SplitFile(long blockSize) { //  每一块的大小
        this();
        this.blockSize = blockSize;
    }
    public SplitFile(String path,long blockSize,String destPath) {
        this(blockSize);
        this.destPath = destPath;
        this.filePath = path;
        init();
    }

    /**
     * 初始化操作
     */
    public void init() {
        File src = null;
        if(null == filePath || !((src = new File(filePath)).exists())) {
            return ;
        }
        if(src.isDirectory()) return ;
        //文件名
        this.fileName = src.getName();
        this.len = src.length();

        //修正实际的块的大小
        if( blockSize > len ) {
            this.blockSize = len;
        }
        size = (int) Math.ceil(1.0*len/this.blockSize); //取大于这个数的

        //确定文件的路径
        for(int i = 0; i < size; i++) {
            this.blockPath.add(destPath + "/" + i + this.fileName);
        }
    }



    /**
     * 分割整体
     */
    public void split() {
        long beginPos = 0;
        long actualBlockSize = blockSize;
        for(int i = 0; i < size; i++) {
            if(i == size - 1) {
                actualBlockSize = len - beginPos;
            }
            splitDetail(i,beginPos,actualBlockSize);
            beginPos += actualBlockSize;
        }

    }

    /**
     * 分割细节
     * @param idx  第几块
     * @param beginPos  每一块的开始位置
     * @param actualBlockSize  每一块的实际大小
     */
    public void splitDetail(int idx,long beginPos,long actualBlockSize) {  //实际的整数(最后一块不一定有这么多)
        File src = new File(this.filePath);
        File dest = new File(this.blockPath.get(idx));  //这个是目的地的文件名//在初始化的时候设置为destpath+fileName+part+i了
        RandomAccessFile raf = null;
        BufferedOutputStream bos = null;
        try {
            raf = new RandomAccessFile(src, "r");//只读模式
            bos = new BufferedOutputStream(new FileOutputStream(dest));
            raf.seek(beginPos);
            byte[] flush = new byte[1024];
            int alen = 0;
            while(-1 != (alen = raf.read(flush))) {
                if(actualBlockSize > len) {
                    bos.write(flush,0,alen);
                    actualBlockSize -= alen;
                }else {
                    bos.write(flush,0,(int)actualBlockSize); //注意要转换一下成为int
                    break;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {   //关闭流
            if(null != bos) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(null != raf) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 方法一: 将刚刚分解的那些文件直接合并到destPath
     */
    public void mergeFile(String destPath) {
        File dest = new File(destPath);
        BufferedOutputStream bos = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(dest,true)); //等下就是追加的类型
            for(int i = 0; i < blockPath.size(); i++) {
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(blockPath.get(i))));
                byte[] flush = new byte[1024];
                int alen = 0;
                while(-1 != (alen = bis.read(flush))) {
                    bos.write(flush,0,alen);
                }
                bos.flush();
                bis.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 方法二 :先合成一个流  SequenceOutputStream
     */
    public void mergeFile2(String destPath) {
        File dest = new File(destPath);
        Vector<InputStream> ve = new Vector<InputStream>();
        BufferedOutputStream bos = null;
        SequenceInputStream sis = null;
        try {
            for(int i = 0; i < blockPath.size(); i++) {
                ve.add( new BufferedInputStream(new FileInputStream(new File(blockPath.get(i)))) );
            }
            sis = new SequenceInputStream(ve.elements()); //把这些流合并成一个流
            bos = new BufferedOutputStream(new FileOutputStream(dest,true)); //等下就是追加的类型
            byte[] flush = new byte[1024];
            int alen = 0;
            while(-1 != (alen = sis.read(flush))) {
                bos.write(flush,0,alen);
            }
            bos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                sis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //for test
    public static void main(String[] args) {
        //每一块的大小,后面那个目录参数是为了等下mergeFile
        String destPath = "/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/split";
        String mergePath = "/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/merge/";
        SplitFile sp = new SplitFile("/home/zxzxin/Java_Maven/Java8/src/main/java/JavaPrimary/IO/io2/testsplit.xml",
                30,
                destPath);

        //System.out.println(sp.size);
//        sp.split();
//        sp.mergeFile(mergePath + "merge.xml");
        sp.mergeFile2(mergePath + "merge.xml");
    }
}

这里写图片描述

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页