Java笔记(17)-处理流、数据流、对象流、RandomAccessFiled

1. 基本使用InputStreamReader和OutputStreamWriter

在许多的操作中,很多都有可能是字节流,尤其是涉及底层的时候,这时就需要将字节转换成为字符流输出,又或者将字符流转换成字节进行传输;

public class TextInOut {
    public static void main(String[] args) {
        int len = -1;
        //使用的是Try-with-resource关闭流;
        try( InputStreamReader in = new InputStreamReader(System.in);
             OutputStreamWriter out = new OutputStreamWriter(System.out)) {
            while ((len = in.read())!=-1) {
                out.write(len);
                out.flush();//只能在里面强制刷新,否则会无法输出,我也debugger过了,但是没发现哪里错了,好像是无法出去while循环
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

2.尝试复制网页的源代码

通过下面的简单代码就可以复制一个网页的源代码,当然有些代码的编码格式是UTF-8,有些则是GBK,还是要知道你想取到的文件的编码格式,然后才能够对编码的格式进行规定,这样才不会在使用InputStreamReader和OutputStreamWriter的时候出现乱码;

public class TestInternet {
    public static void main(String[] args) {
        String line = null;
        try( BufferedReader in = new BufferedReader(new InputStreamReader(new URL("https://www.163.com/").openStream(),"GBK"));
               BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("wangyi.txt"),"GBK"))) {
            while ((line = in.readLine()) != null) {
                out.write(line);
                out.newLine();
            }
            out.flush();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 数据流DataInputStream和DataOutputStream的使用


public class TextDateIO {
    public static void main(String[] args) throws FileNotFoundException {
        int d = -1;
        //首先确定输入输出;但是因为读取的是文件,所以我就
        try (DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("FileText/src/cn/dxs/TextInOut.java")));
             DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("newcopy.txt")))) {
            while ((d = dis.read()) != -1) {
                dos.write(d);
            }
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4.对象流ObjectInputStream和ObjectOutputStream的使用

  1. 用于存取基本的数据类型或对象的处理流;可以将Java的对象写入数据源中;,还能将数据还原;
  2. 首先实现序列化,需要的是一个继承了Serializable接口的对象才行,否则不可以;至于这个接口,属于一种标识接口,其中没有要实现的方法,就是一个空接口;
  3. 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制;通过output输出去;
  4. 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制;input是从外界读入程序;
public final class String
    implements java.io.Serializable,

下图就是Serializable接口:(仅仅是实现serializable接口是不够的)

使用一个类简单的演示对象流如何使用;

public class TestSirealizeStream {
    public static void main(String[] args) {
        new TestSirealizeStream().TakeOut();//将序列化的信息放入一个文件,当然如果是在网络间传输,可能不需要放入文件;
        new TestSirealizeStream().Deposit();//将文件中的序列化信息还原;
    }

    public void TakeOut() {
        ObjectOutputStream ots = null;
        try {
//            定义一下它的对象流,存放位置;
            ots = new ObjectOutputStream(new FileOutputStream("newfile.txt"));
            //写入文件
            ots.writeObject(new Newtest("神秘的天", 545));
            //必须要的刷新
            ots.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ots != null) {
                try {
                    ots.close();//关闭流操作;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void Deposit() {
        Object out = null;
        try {
            //定义读入的对象流文件路径;
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("newfile.txt")));
            out = ois.readObject();//读入;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            System.out.println(out.toString());//简单的输出演示;
        }
    }
}

运行后的结果:
在这里插入图片描述

自定义的序列化之后的类,就是前面传输的对象

现在又有了新的理解,之前的理解好像不是那么的准确,这次我的想法大概就是类似于,两个客户进行数据的传输,假设A传输数据给B,如果没有使用serialVersionUID对数据进行唯一标识,那么系统也会对它进行一种自动的标识,当然这种默认标识并不是一成不变的;也就是说,A发出一条序列化的消息,一旦修改其中的含有的部分消息之后,下一次B就收不到了,也就是必须要再次重新连接。但是如果用了serialVersionUID,那么就算数据已经序列化,准备从A发到B,也就是已经完成连接了,就会像是一条持久的线路,可以在连接的过程中修改其中包含的信息;

public class Newtest implements Serializable {

    private String name;
    private int age;

  /*  必须要定义一个serialVersionUID,比如在网络中传输的时候,可能有很多正在传输的序列化对象
    如果不设置一个独特的标识,那么就很容易打乱,导致传输的数据错误;*/
    public static final long serialVersionUID = 42L;

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

    @Override
    public String toString() {
        return "Newtest{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在默认情况下,基本数据类型的是可序列化的,当然由于String也实现了Serializable接口,自然也是可序列化的;最重要的所有需要序列化的数据都必须是能够序列化的;

static和transient这两个关键词修饰的成员变量不能被序列化;
简单记录一下自己对static不能序列化的理解,自己尝试了一下,发现还是可以传,百度了一段时间,终于找到一个感觉很有道理的博客,在这里记录一下:https://www.cnblogs.com/zht214/p/8040588.html,

5. RandomAccessFile

  1. RandomAccessFile直接继承于java.lang.Object类,并且实现了Dataout(其中有write()相关的方法),Datainput(其中含有read()相关的方法);所以既可以读入文件,又可以写出文件;
  2. RandomAccessFile这个类有两个构造函数;第一个构造函数,就是可以直接把第一个参数设置成String,然后它自动打包成File对象,在我看来应该是调用了第二个构造函数,我尝试debug了一下,没毛病;如下:

    在这里插入图片描述
  3. 构造方法中的mode参数,主要有四种,一种是r:只能读取;rw:可读可写;rwd:同步文件内容的更新;rws:同步文件内容和元数据(简单来说就是用来描述数据的数据,比如元数据可以描述数据的存储位置之类的)的更新;
  4. 使用RandomAccessFiled对文件进行操作(以下记录的是普通情况下),如果文件中含有数据,再向其中放入数据时,会对其中的数据进行覆盖,但不是对整个文件进行覆盖,比如文件A中数据为aaa,文件B中的数据是b,将B中的值放入A中,A的值就会变为baa;
    基本使用:
public class TestAcessFile {
    public static void main(String[] args) {
        String s = "abcdefg";
        new TestAcessFile().text(s);
    }

    public void text(String str) {
        try {
            //定义一个RondomAccessFile
            RandomAccessFile raf = new RandomAccessFile("thsi", "rw");
            //将获得的字符串放入字节数组输入流中;
            ByteArrayInputStream btais = new ByteArrayInputStream(str.getBytes());
            ByteArrayOutputStream btaout = new ByteArrayOutputStream();
            int length = -1;
            byte[] bytes = new byte[9];
            //由于在文件的“我的”后面插入,所以,开始要将我的后面的东西全部复制下来;
            raf.seek(6);
            while ((length = raf.read(bytes)) != -1) {
                btaout.write(bytes, 0, length);
            }
            /*在这个RandomAccessFile中,只要你调用了文件的内容,那么指针就会发生改变,
            所以指针要用在需要调换指针的位置;
            这里是将指针下调到“我的”后面,UTF-8编码,每个字符占3个字节;
            * */
            raf.seek(6);
            //这里是将字节数组中的值放到文件里面去
            while ((length = btais.read(bytes)) != -1) {
                raf.write(bytes, 0, length);
            }
            //再次矫正指针的位置;
            raf.seek(raf.length());
            //将前面的字节数组输出流中的字节,放入一个普通字节数组;
            byte[] bytes1 = btaout.toByteArray();
            //将这个普通数组中的数据写入文件;
            raf.write(bytes1, 0, bytes1.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

原来文件内容:
在这里插入图片描述
程序运行后:
在这里插入图片描述
这个文件流的读取和写入方法上面都用到了,在此记录一下;

6.总结

在我目前的想法看来,其实我们归根结底使用的还是字节流;唯一不同的是,字节流外面的那一层壳子,到底是被什么样的流包裹;比如数据流DataOutputStream等;

  1. InputStream、OutputStream、Reader、Writer就是java.io中的四个抽象类,之前每一个抽象类都有过记录,不过没放到一起看;

猜你喜欢

转载自blog.csdn.net/dxsdcyy/article/details/105954615