【JavaSE】I/O控制流 下 包括:转换流,标准输入输出流,对象流,对象序列化,I/O总结


转换流

转换流提供了在字节流和字符流之间的转换
Java API提供了两个转换流:
      InputStreamReader和OutputStreamWriter
字节流中的数据都是字符时,转成字符流操作更高效。

InputStreamReader用于将字节流中读取到的字节按指定字符集解码成字符。需要和 InputStream“套接”。

构造方法:
InputStreamReader(InputStream in)
public InputSreamReader(InputStream in,String charsetName)
   如: Reader isr = new InputStreamReader(System.in,”ISO5334_1”);

OutputStreamWriter用于将要写入到字节流中的字符按指定字符集编码成字节。 需要和OutputStream“套接”。

构造方法:
public OutputStreamWriter(OutputStream out)
public OutputSreamWriter(OutputStream out,String charsetName)

所有的文件都是有编码格式对于我们来说,TXT和java文件一般来讲有三种编码ISO8859-1,西欧编码,是纯粹英文编码,不适应汉字GBK和UTF-8,这两编码是适用于重要和英文我们一般使用UTF-8编码

import java.io.*;

public class test5 {
    //所有的文件都是有编码格式
    //对于我们来说,TXT和java文件一般来讲有三种编码
    //ISO8859-1,西欧编码,是纯粹英文编码,不适应汉字
    //GBK和UTF-8,这两编码是适用于重要和英文
    //我们一般使用UTF-8编码
    public static void main(String[] args) {
        try {
            test5.testInputStreamReader("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t5.txt","UTF-8");
            test5.testOutputStreamWriter("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t6.txt","你好你好","UTF-8");


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 转换字节输入流为字符输入流
     * 注意,在转换字符流的时候,设置的字符集编码要与读取的文件的数据的编码一致
     * 不然就会出现乱码
     * InputStreamReader
     */

    public static void testInputStreamReader(String path,String mode) throws Exception {
        FileInputStream fs = new FileInputStream(path);
        InputStreamReader is = new InputStreamReader(fs,mode);

        char[] c = new char[1024];
        is.read(c);
        System.out.println(c);
        is.close();
        is.close();
    }

    /**
     * 转换字节输出流为字符输出流
     * 注意,在转换字符流的时候,设置的字符集编码要与读取的文件的数据的编码一致
     * 不然就会出现乱码
     * OutputStreamWriter
     */
    public static void testOutputStreamWriter(String path , String s , String mode) throws Exception {
        OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream(path),mode);
        ow.write(s);
        ow.flush();
        ow.close();
    }
}

标准输入输出流

System.in和System.out分别代表了系统标准的输入和输出设备
默认输入设备是键盘,输出设备是显示器

System.in的类型是InputStream

System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类
通过System类的setIn,setOut方法对默认设备进行改变。

public static void setIn(InputStream in)
public static void setOut(PrintStream out)

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;

public class text6 {
    public static void main(String[] args) {
        try {
            text6.testSystemIn();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 标准的输入流
     * @throws Exception
     */
    public static void testSystemIn() throws IOException {
        //创建一个接收键盘输入数据的输入流,参数只要是继承InputStream接口的都行
        InputStreamReader ir = new InputStreamReader(System.in);

        //放入缓冲流
        BufferedReader br = new BufferedReader(ir);

        String s = "";//定义一个临时接收数据的字符串

        while ((s = br.readLine()) != null){//readLine返回的是字符串
            System.out.println(s);
        }

        br.close();
        ir.close();
    }
}


运用一

把控制台输入的内容写到指定的TXT文件中,当接收到字符串over,就结束程序的运行

//把控制台输入的内容写到指定的TXT文件中,当接收到字符串over,就结束程序的运行

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.InputStreamReader;

public class text7 {
    public static void main(String[] args) {
        try {
            text7.writeTXT("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t7.txt");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void writeTXT(String path) throws Exception{
        //创建一个接收键盘输入数据的输入流
        InputStreamReader inputStreamReader = new InputStreamReader(System.in);
        //将输入流放入缓冲流
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        //缓冲流将数据写入指定位置
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path));

        String str = "";

		//标准写法
        // while((str = bufferedReader.readLine()) != null){
        //     if(str.equals("over")){
        //         break;
        //     }
        //     //读取的每一行都写到指定的TXT文件
        //     bufferedWriter.write(str);
        // }


        while (true){
            str = bufferedReader.readLine();
            if(str.equals("over")){ break;}
                    //读取的每一行都写到指定的TXT文件
            bufferedWriter.write(str);
        }

        //错误示范

        // str = bufferedReader.readLine();
        // while (true){
        //     if (str.equals("over")){
        //         bufferedWriter.flush();
        //         bufferedWriter.close();
        //         bufferedReader.close();
        //         inputStreamReader.close();
        //         break;
        //     }else {
        //         bufferedWriter.write(str);
        //     }
        //
        // }

        bufferedWriter.flush();
        bufferedWriter.close();
        bufferedReader.close();
        inputStreamReader.close();
    }
}

运用二

在一个TXT文件中,写一组用户名和密码,通过控制台输入用户名和密码,与TXT文件中的用户名密码做对比,如果一样就在打印登录成功,如果不一致,就打印用户名密码错误

//在一个TXT文件中,写一组用户名和密码,通过控制台输入用户名和密码,
//与TXT文件中的用户名密码做对比,如果一样就在打印登录成功,如果不一致,就打印用户名密码错误

import java.io.*;

public class text9 {
    public static void main(String[] args) {
        try {
            text9.testPassWord();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void testPassWord() throws Exception {
        //建立输入流
        InputStreamReader ir = new InputStreamReader(System.in);
        InputStreamReader ir2 = new InputStreamReader(new FileInputStream("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t8.txt"),"UTF-8");
        //放入缓冲流
        BufferedReader br = new BufferedReader(ir);
        BufferedReader br2 = new BufferedReader(ir2);
        //临时字符串存放数据
        String str = "";
        String str2 = "";

        while ((str = br.readLine())!= null) {
            str2 = br2.readLine();
            if (str.equals(str2)) {
                System.out.println("登录成功");
            } else {
                System.out.println("用户名密码错误");
            }
        }

      br2.close();br.close();ir2.close();ir.close();

    }
}

对象流

ObjectInputStream和OjbectOutputSteam
用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

序列化(Serialize):用ObjectOutputStream类将一个Java对象写入IO流中
反序列化(Deserialize):用ObjectInputStream类从IO流中恢复该Java对象
ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量

对象的序列化

对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象

序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原

序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
     Serializable
     Externalizable

凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
     private static final long serialVersionUID;

serialVersionUID用来表明类的不同版本间的兼容性
     如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码作了修改,serialVersionUID 可能发生变化。故建议,显示声明
显示定义serialVersionUID的用途

希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID
不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID

import java.io.*;

/**
 * 序列化与反序列化
 * 注意:对象的序列化与反序列化使用的类要严格一致,包名,类名,类机构等等所有都要一致
 * @author lby
 *
 */
public class text10 {
    public static void main(String[] args) {
        try {
            //text10.testSerialize("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t9.txt");
            text10.testDeserialize();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

/**
 * 对象的序列化
 */
    public static void testSerialize(String path) throws Exception {
        //序列化完了之后存放在一个地址里 所以这里要newFile
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(path));

        Preson p = new Preson();
        p.age=10;
        p.name="zhangsan";

        out.writeObject(p);
        out.flush();
        out.close();

    }


    /**
     * 对象的反序列化
     */
    public static void testDeserialize() throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\\\JavaProjects\\\\b站草稿\\\\IO控制流\\\\src\\\\t9.txt"));

        Object obj = in.readObject();
        Preson p = (Preson)obj;

        System.out.println(p.age + p.name);

        in.close();
    }
}

import java.io.Serializable;

/**
 * 可以序列化与反序列化的对象
 * @author lby
 *
 */
public class Preson implements Serializable {
    /**
     * 一个表示序列化版本标识符的静态变量
     * 用来表明类的不同版本间的兼容性
     */
    public static final long serialVersionUID = 1L;

    String name;
    int age;
}

RandomAccessFile 类

RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意 地方来读、写文件
     支持只访问文件的部分内容
     可以向已存在的文件后追加内容

RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。 RandomAccessFile 类对象可以自由移动记录指针:
     long getFilePointer():获取文件记录指针的当前位置
     void seek(long pos):将文件记录指针定位到 pos 位置

构造器
     public RandomAccessFile(File file, String mode)
     public RandomAccessFile(String name, String mode)

创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
     r: 以只读方式打开
     rw:打开以便读取和写入
     rwd:打开以便读取和写入;同步文件内容的更新
     rws:打开以便读取和写入;同步文件内容和元数据的更新

r和rw最常用

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.util.RandomAccess;

public class text11 {
    public static void main(String[] args) {
        try {
            text11.testRandomAccessFileRead();
            text11.testRandomAccessFileWrite();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件的随机读写
     * 程序可以直接跳到文件的任意 地方来读、写文件
     * @author lby
     *
     */
    /**
     * 随机读文件
     */
    public static void testRandomAccessFileRead() throws Exception{
        //RandomAccessFile的构造有两个参数,参数1是读写的文件的路径
        //参数2是指定 RandomAccessFile 的访问模式
        //r: 以只读方式打开
        //rw:打开以便读取和写入
        //rwd:打开以便读取和写入;同步文件内容的更新
        //rws:打开以便读取和写入;同步文件内容和元数据的更新
        //最常用是r和rw

        RandomAccessFile rf = new RandomAccessFile("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t11.txt","r");
        //rf.seek(0);//设置读取文件内容的起始点
        rf.seek(8);//通过设置读取文件内容的起始点,来达到从文件的任意位置读取
        byte[] c = new byte[1024];
        int len;
        StringBuilder sb = new StringBuilder();
        while ((len = rf.read(c)) != -1){
            sb.append(new String(c,0,len));
        }
        rf.close();
        System.out.println(sb);
    }


    /**
     * 随机写
     */
    public static void testRandomAccessFileWrite() throws Exception {
        RandomAccessFile ra = new RandomAccessFile("D:\\JavaProjects\\b站草稿\\IO控制流\\src\\t11.txt","rw");
        //ra.seek(0);//从头写
        ra.seek(ra.length());//末尾追加
        ra.write("你好".getBytes());

        ra.close();
    }
}

总结

流是用来处理数据的。

处理数据时,一定要先明确数据源,与数据目的地
     数据源可以是文件,可以是键盘。
     数据目的地可以是文件、显示器或者其他设备。

而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等。
字节流-缓冲流(重点)
     输入流InputStream-FileInputStream-BufferedInputStream
     输出流OutputStream-FileOutputStream-BufferedOutputStream

字符流-缓冲流(重点)
     输入流Reader-FileReader-BufferedReader
     输出流Writer-FileWriter-BufferedWriter

转换流
     InputSteamReader和OutputStreamWriter

对象流ObjectInputStream和ObjectOutputStream(难点)
     序列化
     反序列化

随机存取流RandomAccessFile(掌握读取、写入)

扫盲重点:read方法详解

public static void testFileInputStream()throws IOException{
            FileInputStream in = new FileInputStream("D:\\JavaProjects\\b站草稿\\IO控制流\\a\\tt1.txt");
            byte[] b = new byte[3];//设置一个byte数组接收读取的文件的内容
            int len = 0;//设置一个读取数据的长度
            while ((len=in.read(b)) != -1){
                System.out.println(new String(b,0,len));
            }
            in.close();//注意。流在使用完毕之后一段要关闭
        }
        

假设我的数据为:abcdefg (长度为7)

首先当read读完之后再读后一位就会返回-1,用这个条件去做while循环,因为read每次只读一个你定义的数组长度大小的数据。
1.read方法里面有一个int read(byte)方法,意思是读出来的数据直接放入byte数组里并且返回一个数据长度,所以这里提前定义了一个byte数组来传进read方法里,in.read(b),read方法会返回有效数据的长度,比如说我的总数据长度是7,那么我定义数组长度为10时new byte[10],read会返回7,这里没什么问题,但如果我定义数组长度为3new byte[3],那么read会返回3次,第一次是3,第二次是3,第三次是1,第四次是-1此时就会结束循环。因为第三次读的时候前面已经读了6位了只剩下一位,所以就读了一位有效长度。另外read三次放入数组的数据是这样的,第一次为abc,第二次为def,但是第三次read方法讲数据放入byte数组时的值为gef,他并不是只有一个g,因为她只读了一个g但是他还是会传入三个数据。
2.那么此时len的作用就体现出来了,在后面的new String(b,0,len)方法里,len保证了每次读取的都是有效值,比如第三次len就是1,那么放入Sting里的数据是0-1位,就是“g”,这样就不会把后面的ef也读进去。
3.另外提一嘴,new String(b,0,len)表示每次读出多少字节就把对应字节转化为字符串

因此我认为这里再加入一个StringBuilder去接收转化出来的字符串会更好,用append方法。这样就会形成一个完成连续的字符串,以后用作比较的时候会更好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值