java IO流 ObjectStream PipedStream 随机访问文件 字符编码--21

ObjectStream

操作对象
ObjectInputStream和ObjectOutputStream
被操作的对象需要实现Serializable(标记接口)

ObjectOutputStream 将 java 对象的基本数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象,通过在流中使用文件可以实现对象的持久存储。

java会根据对象自定义一个UID值将其序列化,随着对象的更改,UID值也会变化,导致前后无法相关联 读不出数据。根据这个情况, 可以自定义UID值,可以保证更改对象后依然能读出数据。

public static final long serialVersionUID = 42L;
另外,静态不能被序列化,因为静态在方法区中先于对象加载
static String country = “CN”;
如果对于非静态成员也不想序列化,可以transient修饰
transient int age;

transient修饰age会打印出age = 0
transient修饰name会打印出name = null

package day21;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectStreamDemo {


    public static void main(String[] args) throws Exception{
        //writeObj();
        readObj();
    }


    public static void readObj()throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));

        Person p = (Person)ois.readObject();

        System.out.println(p);

        ois.close();
    }

    public static void writeObj()throws IOException{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
        oos.writeObject(new Person("lisi",39,"kr"));

        oos.close();
    }
}
package day21;

import java.io.Serializable;

public class Person implements Serializable{

//  自定义UID值,可以保证更改对象后依然能读出
    public static final long serialVersionUID = 42L;

    private String name;
    int age;
    static String country = "CN";

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

    public String toString(){
        return name+"..."+age+"....."+country;
    }

}

这里写图片描述

管道流

管道流分为管道输入流(PipedInputStream)和管道输出流(PipedOutputStream)
管道输入流应该连接到管道输出流,管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个从PipedInputStream对象读取,并由其他线程将其写入到相应的PipedOutputStream
不建议对这两个对象尝试单线程,因为这样可能会死锁线程。要分别用一个线程负责一个管道流,当一个处于等待的情况时, 另一个线程可以运行不会死锁

这是涉及了多线程的IO流
(涉及了集合的IO流是Properties)

package day21;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class PipedStream {


    public static void main(String[] args) throws IOException {

        PipedInputStream pis = new PipedInputStream();
        PipedOutputStream pos = new PipedOutputStream();

        pis.connect(pos);

        Read r = new Read(pis);
        Write w = new Write(pos);

        new Thread(r).start();
        new Thread(w).start();
    }

}

class Read implements Runnable {
    private PipedInputStream pis;

    Read(PipedInputStream pis) {
        this.pis = pis;
    }

    public void run() {
        try {
            byte[] buf = new byte[1024];

            System.out.println("没有数据可以读取,先阻塞");

            int len = pis.read(buf);

            System.out.println("读到数据,阻塞结束");

            String s = new String(buf, 0, len);

            System.out.println(s);

            pis.close();

        } catch (IOException e) {
            throw new RuntimeException("管道读取失败");
        }
    }
}

class Write implements Runnable {
    private PipedOutputStream pos;

    Write(PipedOutputStream pos) {
        this.pos = pos;
    }

    public void run() {
        try {
            System.out.println("等待6s后写入数据");
            Thread.sleep(6000);
            pos.write("piped laile".getBytes());

            pos.close();

        } catch (Exception e) {
            throw new RuntimeException("管道输出失败");
        }
    }
}

这里写图片描述

RandomAccessFile-随机访问文件

该类不是IO中的体系的子类,而是直接继承自Object。但是它是IO包中的成员,因为它具备读和写的功能 内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。

其实完成读写的原理就是内部封装了字节输入流和字节输出流。

通过构造函数,可以看出该类只能操作文件。 而且操作文件还有模式:r或者rw等
如果模式为只读r,不会创建文件,会去读取一个已存在的文件,若该文件不存在,报出异常
如果模式为rw,可以创建文件。该对象的构造函数要操作的文件不存在,会自动创建,如果存在则不会覆盖

应用:多线程的分段下载,每个线程负责不同的区域下载

package day21;

import java.io.IOException;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo {


    public static void main(String[] args) throws IOException {
        //writeFile_2();
        //writeFile();
        readFile();
    }

    public static void readFile() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("ran.txt", "r");

        // 调整指针的位置
        // raf.seek(8);

        // 跳过指定的字节数
        raf.skipBytes(8);

        byte[] buf = new byte[4];

        raf.read(buf);

        String name = new String(buf);

        int age = raf.readInt();

        System.out.println("name=" + name);
        System.out.println("age=" + age);
        System.outprintln(raf.getFilePointer());
        raf.close();
    }

    public static void writeFile_2() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");
        raf.seek(8 * 0);
//      会覆盖原来起始位置上的数据。李四被赵柳覆盖。
        raf.write("赵柳".getBytes());
        raf.writeInt(101);

        raf.close();
    }

    public static void writeFile() throws IOException {
        RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");

        raf.write("李四".getBytes());
        raf.writeInt(97);
        raf.write("王五".getBytes());
        raf.writeInt(99);

        raf.close();

    }

}

这里写图片描述

DataStream:操作基本数据类型的流对象

package day21;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataStreamDemo {

    /**
     * DataInputStream和DataOutputStream 可以用于操作基本数据类型的数据的流对象
     * 
     * 记住:凡是操作基本数据类型,就用DataStream。
     * 
     */
    public static void main(String[] args) throws IOException {
        // readData();
        // writeData();

        // writeUTFDemo();
        readUTFDemo();
    }

    public static void readUTFDemo() throws IOException {
        DataInputStream dis = new DataInputStream(new FileInputStream(
                "utfdata.txt"));
        // 用writeUTF写的文件必须要用readUTF读取。
        // 也必须用readUTF读writeUTF写出的文件。
        // 否则会报出EOFException - 如果此流在读取所有字节之前到达末尾。
        // 因为以打印“你好”为例,utf-8是6个字节,UTF-8 修改版是8个字节。
        // 所以用readUTF读取utf-8格式的文件会导致8个字节没读完就到达末尾。
        String s = dis.readUTF();

        System.out.println(s);

        dis.close();
    }

    public static void writeUTFDemo() throws IOException {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(
                "utfdata.txt"));

        dos.writeUTF("你好");
        // 与普通的UTF-8不同,以打印“你好”为例,utf-8是6个字节,gbk是4个字节,UTF-8 修改版是8个字节
        dos.close();
    }

    public static void readData() throws IOException {

        DataInputStream dis = new DataInputStream(new FileInputStream(
                "data.txt"));

        // 读取的顺序与写入的顺序要一致,否则会乱码
        int num = dis.readInt();
        boolean b = dis.readBoolean();
        double d = dis.readDouble();

        System.out.println(num);
        System.out.println(b);
        System.out.println(d);

        dis.close();

    }

    public static void writeData() throws IOException {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(
                "data.txt"));

        dos.writeInt(2324);
        dos.writeBoolean(true);
        dos.writeDouble(234.65);

        dos.close();
    }

}

这里写图片描述

ByteArrayStream

用于操作字节数组的流对象
ByteArrayInputStream:在构造的时候,需要接受一个数据源,而且数据源是一个字节数组
ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的。

因为两个流对象都操作数组,没有使用系统资源,不用close关闭
这两个类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。

在流操作规律讲解时,
源设备:
键盘System.in,硬盘FileStream,内存ArrayStreeam
目的设备:
控制台System.out,硬盘FileStream,内存ArrayStream

package day21;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class ByteArrayStream {

    public static void main(String[] args) {
        // 数据源
        ByteArrayInputStream bis = new ByteArrayInputStream("abcdef".getBytes());

        // 数据目的
        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        int by = 0;

        while ((by = bis.read()) != -1) {
            bos.write(by);
        }

        System.out.println(bos.size());
        // 把byte数组转成字符串,并变成大写格式
        System.out.println(bos.toString().toUpperCase());
    }
}

这里写图片描述

// 另外还有:
// 操作字符数组:
// CharArrayReader:需要抛出IOException,用的是toCharArray()的方法
CharArrayReader car = new CharArrayReader("abcdef".toCharArray());
// CharArrayWrite:关闭无效,不会抛出IOException
 CharArrayWriter caw = new CharArrayWriter();
 int by = 0;
 while ((by = car.read()) != -1) {
     caw.write(by);
}
//需要toString()后才能使用toUpperCase方法
System.out.println(caw.size());
System.out.println(caw.toString().toUpperCase());
car.close();


//操作字符串:
// StringReader:需要抛出IOException。
 StringReader sr = new StringReader("abcdef");
// StringWriter:关闭无效,不会抛出IOException
StringWriter sw = new StringWriter();
int by = 0;
while ((by = sr.read()) != -1) {
    sw.write(by);
}
//需要toString()后才能使用.length方法,toUpperCase方法
System.out.println(sw.toString().length());
System.out.println(sw.toString().toUpperCase());

sr.close();

字符编码

字符流的出现为了方便操作字符。
更重要的是加入了编码转换。
通过子类转换流来完成
InputStreamReader:是字节流通向字符流的桥梁
OutputStreamWriter:是字符流通向字节流的桥梁
在两个对象构造的时候加入了字符集。

package day21;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class EncodeStream {


    public static void main(String[] args) throws IOException{
        //writeText();
        readText();
    }

    public static void readText()throws IOException{
        InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");
        //      用gbk读取utf-8编写的文件,“你好”打印出了“浣犲ソ”
        //      用utf-8读取gbk编写的文件,“你好”打印出了“??”
        char[] buf = new char[10];

        int len = isr.read(buf);

        String s = new String(buf,0,len);

        System.out.println(s);

        isr.close();
    }

    public static void writeText()throws IOException{
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");

        osw.write("你好");

        osw.close();
    }

}

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

编码与解码

编码:字符串变成字节数组

解码:字节数组变成字符串

String——>byte[]: str.getBytes(charsetName);

byte[]——>String: new String(byte[],charsetName);

package day21;

import java.io.IOException;
import java.util.Arrays;

public class EncodeDemo {


    public static void main(String[] args) throws IOException {

        String s = "你好";

        byte[] b1 = s.getBytes("GBK");

        System.out.println("b1=" + Arrays.toString(b1));

        String s1 = new String(b1, "ISO8859-1");

        System.out.println("s1=" + s1);
//若解码时用错误的表解码错误,可以通过解码所用码表转换回原来的字节数组,再通过这个字节数组按照正确的编码表解码
//gbk与utf-8之间不行,因为utf-8也包含汉字,会出现乱码,变不回来
        byte[] b2 = s1.getBytes("ISO8859-1");

        System.out.println("b2=" + Arrays.toString(b2));

        String s2 = new String(b2, "GBK");

        System.out.println("s2=" + s2);

    }

}

这里写图片描述

联通的现象

package day21;

import java.io.IOException;
import java.util.Arrays;

public class EncodeDemo2 {

    public static void main(String[] args) throws IOException{
        String s = "联通";
        byte[] b1 = s.getBytes();

        System.out.println("by="+Arrays.toString(b1));

        String s1 = new String(b1);

        System.out.println("s1="+s1);

    }

}

这里写图片描述

而在记事本中写入“联通”,重新打开却是“��ͨ”。因为“联通”在gbk码中的表现形式与UTF-8编码规律相同。打开时记事本启用了UTF-8码表,造成乱码。
这里写图片描述
这里写图片描述

附:UTF-8表编码格式

这里写图片描述

终极练习:录入学生成绩

有五个学生,每个学生有三门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩)
输入的格式:张三,30,40,50,计算出总成绩

1,描述学生对象
2,定义一个可操作学生对象的工具类

思想:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生的对象。
2,因为学生有很多,那么就需要存储,使用到集合,因为要对学生的总分排序。 所以可以使用TreeSet
3,将集合信息写入到文件中。

package day21;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class StudentInfoTest {

    public static void main(String[] args) throws IOException{

        Comparator<Student> cmp = Collections.reverseOrder();

        Set<Student> stus = StudentInfoTool.getStudent(cmp);

        StudentInfoTool.write2File(stus);



    }

}

class Student implements Comparable<Student>{
    private String name;
    private int Math,Chinese,English;
    private int sum;
    public Student(String name, int Math, int Chinese, int English) {

        this.name = name;
        this.Math = Math;
        this.Chinese = Chinese;
        this.English = English;

        sum = Math+Chinese+English;

    }

    public int compareTo(Student s){
        int num = new Integer(this.sum).compareTo(new Integer(s.sum));
        if(num==0)
            return this.name.compareTo(s.name);
        return num;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getSum() {
        return sum;
    }
    public void setSum(int sum) {
        this.sum = sum;
    }

    public int hashCode(){
        return name.hashCode()+sum*78;
    }

    public boolean equals(Object obj){
        if(!(obj instanceof Student))
            throw new ClassCastException("输入不是学生");

            Student s = (Student)obj;

            return this.name.equals(s.name)  && this.sum == s.sum;
    }

    public String toString(){
        return "Student["+name+","+Math+","+Chinese+","+English+"]";
    }
}

class StudentInfoTool{

    public static Set<Student> getStudent()throws IOException{
        return getStudent(null);
    }
    public static Set<Student> getStudent(Comparator<Student> cmp)throws IOException{
        BufferedReader bufr  = new BufferedReader(new InputStreamReader(System.in));

        String line = null;

        Set<Student> stus = null;
        if(cmp==null)
            stus = new TreeSet<Student>();
        else
            stus = new TreeSet<Student>(cmp);

        while((line=bufr.readLine())!=null){
            if("over".equals(line))
                break;
            String[] info = line.split(",");

            Student stu = new Student(info[0],Integer.parseInt(info[1]),Integer.parseInt(info[2]),Integer.parseInt(info[3]));

            stus.add(stu);
        }

        bufr.close();

        return stus;
    }

    public static void write2File(Set<Student> stus) throws IOException{
        BufferedWriter bufw = new BufferedWriter(new FileWriter("StudentInfo.txt"));

        for(Student stu:stus){
            bufw.write(stu.toString()+"\t");
            bufw.write(stu.getSum()+"");
            bufw.newLine();
            bufw.flush();
        }

        bufw.close();
    }
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值