小白学习Java第十七天

复习

1.理解IO流
IO流:不同设备间的数据的传输。

2.IO流分类
按照数据流向:
输入流:站在内存的角度看,读取数据
输出流:站在内存的角度看,写出数据
按照操作数据类型:
字节流:音频、视频、图片等二进制文件(万能)
字符流:纯文本数据

总结:
学习流?
1.分清楚要做输入还是输出操作?

2.选择字节流还是字符流?
流对象基本上都是成对出现的,有输入必然有输出
字节流: 设备名+父类名
字符流: 设备名+父类名
输入:read() 输出:write()

3.字节流

根类:
   InputStream           OutputStream
子类:
   FileInputStream       FileOutputStream
   BufferedInputStream   BufferedOutputStream

4.字符流

本质:字节流+编码表
根类:
    Reader          Writer
子类:
    FileWriter

5.流异常处理

  try(
    流对象的创建
   ){
      流的读或写操作
 }catch(流异常类型名   变量名){
	    异常处理代码
 }

课程

一.字符流

(一) 字符输入流

  1. 字符流输入流介绍:
    Reader: 用于读取字符流的抽象父类
    FileReader: 用于读取字符流的常用子类

  2. FileReader构造方法
    1)FileReader(File file): 在给定从中读取数据的 File 的情况下创建一个新 FileReader
    2)FileReader(String fileName): 在给定从中读取数据的文件名的情况下创建一个新 FileReader

  3. 读的方法:
    1)int read(): 一次读一个字符数据,返回值结果是int类型, 返回的就是这个字符在编码表中对应的整数结果,如果读到-1,证明文件读取完毕
    2)int read(char[] ch): 一次最多读一个字符数组数据, 将读取出的字符放置在参数ch数组中, 返回值结果int类型, 表示每次从文件中读取到的字符的个数, 如果读取到-1,证明文件读取完毕

import java.io.FileReader;
import java.io.IOException;

public class Demo1 {
    public static void main(String[] args) throws IOException {
        //创建字符输入流,并指定数据源
        FileReader fr = new FileReader("abc.txt");
        //读取数据,一个字符一个字符读取,读取到文件末尾返回-1.
        /*int ch = fr.read();
        while(ch != -1) {
            System.out.print((char) ch);
            ch = fr.read();
        }*/

        //读取数据,采用数组方式。返回的是读取的字符数,读取到末尾返回-1.读取的内容存储到数组中。
        //数组的长度设定:1024的倍数
        char[] chf = new char[1024];
        int num = fr.read(chf);
        while(num != -1) {
                System.out.println(new String(chf,0,num));
        num = fr.read(chf);
    }

        fr.close();
    }

}
/*
* 字符输入流:
*   Reader:
*     抽象类,是根类
*     read():读取一个字符,读取到末尾返回-1.
*     read(char[] ch) :读取的内存存储到ch中,返回读取的字符个数,读取到末尾返回-1
*     read(char[] ch ,int  index, int count) :读取的内存存储到ch一部分中,返回读取的字符个数,读取到末尾返回-1
*     close():关闭流
*   子类:
*     FileReader
*       构造方法: 必须指定数据源
*                 FileReader(File/String  filename)
* */

import java.io.FileReader;
import java.io.IOException;

public class Demo2 {
    public static void main(String[] args) throws IOException {
        //需求:读取当前java文件
        FileReader fr = new FileReader("E:\\0802java\\javase0802\\day18\\src\\Demo2.java");
        /*int ch = 0;
        while((ch = fr.read()) != -1){
            System.out.print((char)ch);
        }*/
        int num = 0;
        char[] cha = new char[512];
        while((num = fr.read(cha)) != -1){
            System.out.print(new String(cha,0,num));
        }

        fr.close();
    }
}

(二) 字符流的拷贝

  1. 字符流复制结论:
    1)字符流,可以复制纯文本文件, 但是效率不高, 因此对于文件的复制,通常采用字节流
    2)字符流,不能复制非纯文本文件

  2. 字符流复制纯文本效率低原理:
    在这里插入图片描述

  3. 字符流不能复制非纯文本文件原理:

在这里插入图片描述

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo3 {
    public static void main(String[] args) throws IOException {
        //复制文本文件   先读后写
        FileReader fr = new FileReader("E:\\0802java\\javase0802\\day18\\src\\Demo3.java");
        FileWriter fw = new FileWriter("a.txt");

       /* int ch = 0;
        while((ch = fr.read()) != -1){
            fw.write(ch);
        }*/

       char[]  chf = new char[1024];
       int len = 0;
       while((len = fr.read(chf)) != -1){
           fw.write(chf,0,len);
       }


        //关闭流
        fw.close();
        fr.close();

    }
}

(三) 字符缓冲流

1、字符缓冲流介绍:
(1) BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途。
(2) BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。

2、构造方法
BufferedWriter(Writer out): 创建字符缓冲输出流对象
BufferedReader(Reader in) : 创建字符缓冲输入流对象

3、高效的原因:
BufferedReader:每次调用read方法,第一次从磁盘中读取了默认数组缓冲大小个字符,存储到该类型对象的缓冲区数组中,将其中一个返回给调用者,再次调用read方法时,就不需要再访问磁盘,直接从缓冲区中拿一个出来即可,效率提升了很多。
BufferedWriter:每次调用write方法,不会直接将字符刷新到文件中,而是存储到字符数组中,等字符数组写满了或者flush刷新缓冲区,才一次性刷新到文件中,减少了和磁盘交互的次数,提升了效率

(四) 高效缓冲字符流的特有方法

1、BufferedReader:
readLine():可以从输入流中,一次读取一行数据,返回一个字符串,如果到达文件末尾,则返回null

2、BufferedWriter:
newLine():写一行 行分隔符.

import java.io.*;

public class Demo4 {
    public static void main(String[] args) throws IOException {
       /* FileWriter fw = new FileWriter("a.txt");
        BufferedWriter bw = new BufferedWriter(fw);

        bw.write("hello ");
        bw.newLine();
        bw.write("world");

        //bw.flush();

        bw.close();*/

        FileReader fr = new FileReader("a.txt");
        BufferedReader br = new BufferedReader(fr);
        String line = br.readLine();
        while(line != null) {
            System.out.print(line);
            line = br.readLine();
        }

        br.close();
    }
}
/*
* 字符缓冲流:
*    BufferedReader
*       高效读取 : 字符、数组、行
*      本质:从内部维护的缓冲区中读取数据
*      新增方法:
*          readLine():读取一行文本,返回一行文本内容,但不包含行终止符,读取到末尾返回null
*    BufferedWriter
*        高效写入 字符、字符数组、字符串数据
*        本质:也是将数据写到缓冲区,需要刷新到目的地
*        新增方法:
*              newLine(): 跨平台的换行符
* */
import java.io.*;

public class Demo5 {
    public static void main(String[] args) throws IOException {
        //使用缓冲区,复制一个java文件
        BufferedReader br = new BufferedReader(new FileReader("E:\\0802java\\javase0802\\day18\\src\\Demo5.java"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("1.txt"));
        String line = null;
        while((line = br.readLine()) != null){
            bw.write(line);
            bw.newLine();
        }

        bw.close();
        br.close();
    }
}

练习

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;

public class Demo6 {
    public static void main(String[] args) throws IOException {
        /*有这样一个文件,格式如下:
         张三
         男
         20
         李四
         女
         30
         ....
         读取该文件中的数据,将数据存储到Person对象中。
         操作对象,将对象存储到集合中,将对象的属性信息输出打印。
        */
        BufferedReader br = new BufferedReader(new FileReader("user.txt"));
        //用于存储文件中的每行数据
        ArrayList<String>  list = new ArrayList<>();
        String line = null;
        while((line = br.readLine()) != null){
            list.add(line.trim());
        }
        //用于存储person对象
        ArrayList<Person>  listPer = new ArrayList<>();
        for (int i = 0 ;i < list.size()/3; i++){
            String name = list.get(3*i+0);
            char  gender = list.get(3*i+1).charAt(0);
            int   age = Integer.parseInt(list.get(3*i+2));
            Person p = new Person(name,gender,age);
            listPer.add(p);
        }

        //遍历listPer
        for (Person per : listPer)
            System.out.println(per);

    }
}

class Person{
    private  String name;
    private  char  gender;
    private  int  age;

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

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

二. 转换流

(一) 使用转换流的原因

针对于不同编码表的文件, 通过字符流进行读写会发生乱码问题, 为了正确文本文件读写过程, 使用转换流
案例: 将GBK编码的文本文件内容, 复制到UTF-8编码的文件中

(二) 转换流的使用

1、OutputStreamWriter:Writer的子类, 字符流到字节流的桥梁,可以指定编码形式
构造方法:OutputStreamWriter(OutputStream os, String charSetName)
创建一个转换流对象,可以把将来方法中接收到的字符,通过指定的编码表charSetName,编码成字节信息,再通过指定的字节流os,将字节信息写出

2、InputStreamReader:Reader的子类, 字节流到字符流的桥梁,可以指定编码形式
构造方法:InputStreamReader(InputStream is, String charSetName)
创建一个转换流对象,可以使用is这个指定的字节流,从磁盘中读取字节信息,通过指定的编码表charSetName,将字节信息解码成字符信息,返回给调用者

3、说明:
无论是读取的时候,还是写出的时候,都需要参考读取文件和目标文件的编码形式
读取源文件时,解码的形式必须和源文件的编码形式一致
写出到目标文件时,编码形式必须和目标文件的编码形式一致

```java
import java.io.*;

public class Demo7 {
    public static void main(String[] args) throws IOException {
       /* BufferedReader br = new BufferedReader( new FileReader("a.txt"));
        String line = br.readLine();
        System.out.println(line);
        br.close();*/

       //向文件中写入数据,指定编码格式为 gbk
        OutputStreamWriter osw =  new OutputStreamWriter(new FileOutputStream("b.txt"),"gbk");
        osw.write("你好");
        osw.close();

        //读取指定编码格式的文件
        InputStreamReader  isr = new InputStreamReader(new FileInputStream("b.txt"),"gbk");
        BufferedReader br = new BufferedReader(isr);
        String line = br.readLine();
        System.out.println(line);
        br.close();
    }
}

/*
* 转换流:
*    是字符流,是Reader和Writer的子类。
*    InputStreamReader:    1.字节输入流转为字符输入流   2.可以指定编码表
*    OutputStreamWriter:   1.字节输出流转为字符输出流   2.可以指定编码表
* */

  1. 转换流的实现原理如下图:
    在这里插入图片描述
import java.io.*;
import java.util.Scanner;

public class Demo8 {
    public static void main(String[] args) throws IOException {
        //数据源:文件
        //此时的数据源是:键盘
        /*InputStream is = System.in;
        int ch = is.read(); //读取的键盘按下的内容
        System.out.println(ch);*/

        //录入一行字母数据,将其转为大写形式输出
        InputStream is = System.in;
        //将字节流转为字符流
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String line = br.readLine();
        System.out.println(line.toUpperCase());

        System.out.println("-------------------------------------------");
        /*
        * Scanner:
        *    扫描器,可以扫描键盘录入的数据,也可以扫描文本文件中的数据,也可以直接扫描字符串数据
        *    只能扫描:基本类型和字符串类型的数据。
        * */
        /*Scanner sc = new Scanner(System.in);
        sc.nextInt();*/

        Scanner sc1 = new Scanner("hello abc 123");
        System.out.println(sc1.next());

        Scanner  sc2 = new Scanner(new File("user.txt"));
        System.out.println(sc2.next());

        Scanner  sc3 = new Scanner(new FileInputStream("user.txt"));
        System.out.println(sc3.next());
    }
}

三. 对象序列化

(一) 对象流概述

  1. 对象序列化介绍:
    (1) 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
    这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息,字节序列写到文件之后,相当于文件中持久保存了一个对象的信息(把对象存储在文件中)
    (2) 对象反序列化: 将序列化的字节序列从文件中读取回来,重构对象(把文件当中存储的对象读取出来)

  2. 对象序列化流: ObjectOutputStream ObjectInputStream

(二)对象序列化流的使用

  1. ObjectOutputStream : 对象序列化流,是OutputStream的子类,将Java对象的原始数据类型和图形写入OutputStream

2.构造方法:
ObjectOutputStream (OutputStream o):
创建一个写入指定的OutputStream的ObjectOutputStream

  1. 常用方法:
    writeObject(Object obj): 将指定的对象写入ObjectOutputStream

  2. 注意事项:
    (1) 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
    (2) Serializable是一个标记接口,实现该接口,不需要重写任何方法

(三)对象反序列化流的使用

  1. ObjectInputStream : 对象反序列化流,是InputStream的子类,就是将持久化保存的对象,按照字节顺序进行读取,ObjectInputStream反序列化前先使用ObjectOutputStream编写的原始数据和对象

  2. 构造方法:
    ObjectInputStream(InputStream in): 创建从指定的InputStream读取的ObjectInputStream

3.常用方法:
readObject(): 从ObjectInputStream读取一个对象, 返回值类型为Object

4.EOFException : End Of File Excepttion 文件到达结束位置,不能再进行对象的获取了

  1. 问题: 反序列时,将对象从文件中读取,不知道文件中有几个对象, 于是获取对象次数多了, 报出EOFException, 文件到达末尾异常
    优化方案: 将多个对象放置到一个集合中, 将这个集合对象写入到文件中,实现序列化过程; 读取文件的时候,就读一次, 将集合对象获取出来, 遍历集合相当于将集合中所有对象都获取到. 如此可以避免掉,不知道文件中有多少对象从而报出的异常问题
import java.io.*;
import java.util.Arrays;
import java.util.List;

public class Demo9 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 基本类型 和常用对象
        int  num = 123;
        float f = 1.23f;
        String s = "helloworld";
        int[] arr = {1,2,3,4};
        List<Integer> list = Arrays.asList(1,2,3);

        //序列化
        //注意:序列化数据的顺序决定了反序列化数据的顺序
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
        oos.writeInt(num);
        oos.writeFloat(f);
        oos.writeUTF(s);
        oos.writeObject(arr);
        oos.writeObject(list);

        oos.close();


        //反序列化
        ObjectInputStream  ois = new ObjectInputStream(new FileInputStream("obj.txt"));
        int x = ois.readInt();
        float fl = ois.readFloat();
        String str = ois.readUTF();
        int[] arrays = (int[])ois.readObject();
        List<Integer> list1 = (List<Integer>)ois.readObject();

        System.out.println(x);
        System.out.println(fl);
        System.out.println(str);
        System.out.println(Arrays.toString(arrays));
        System.out.println(list1);

        ois.close();

    }
}
/*
* 对象序列化:
*    序列化:  将对象进行持久化存储。---写
*              按照字节顺序进行存储的,这是一个字节序列(类型、属性名、属性值)
*    反序列化: 将持久化的对象获取到。---读
* 字节对象流:
*  ObjectInputStream
*  ObjectOutputStream
*         可以操作基本类型和对象。
* */

(四)序列号serialVersionUID和transient关键字

  1. serialVersionUID:
    (1) 序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?会出问题,会抛出InvalidClassException异常
    (2) 如果出问题了,如何解决呢?
    重新序列化
    给对象所属的类加一个serialVersionUID
    格式为:private static final long serialVersionUID = 42L;
  2. transient:
    如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
    给成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程。
import java.io.*;
import java.util.ArrayList;

public class Demo10 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\中公教育\\基础班\\8.10以及以后\\8.25\\编程8.25\\src\\per.txt"));

        oos.writeObject(new Student("zhangsan",20));
        oos.writeObject(new Student("zhangsan",29));


        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student("zhangsna",18));
        list.add(new Student("lisi",18));
        list.add(new Student("wangwu",16));
        oos.writeObject(list);

        /*
         * 反序列化时,容器出现 EOFException异常,没有可反序列的对象,还要反序列化。
         * 解决方案,就是将多个要序列化的对象存储到一个集合中,反序列化这个集合即可。
         * */


        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\中公教育\\基础班\\8.10以及以后\\8.25\\编程8.25\\src\\per.txt"));

        Student stu = (Student) ois.readObject();
        System.out.println(stu);
        Student stu1 = (Student) ois.readObject();
        System.out.println(stu1);
        //Student stu2 = (Student) ois.readObject();// 多反序列化时就会Exception in thread "main" java.io.EOFException,可以用集合
        //System.out.println(stu2);

        ArrayList<Student>  stuList  = (ArrayList<Student>) ois.readObject();//序列化反序列化都要是集合类,否则无法转换
        System.out.println(stuList);

        ois.close();
    }
}

class Student implements Serializable {
    public static final long serialVersionUID = 42L;//设定类的序列版本号一致,否则有异常
    private String name;
    //transient  int age;//加了transient 不序列化。输出初始值
    private   int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
/*
* 标记接口:  接口中没有方法和字段。
* java.io.Serializable
*    只有实现该接口的类对应的对象才可以进行序列化和反序列化。
*    对象可以序列化,其子类对象也可以序列化。
*
* 问题: 某个属性不想要进行序列化怎么办?
*     属性使用static修饰,变成类变量,不属于对象可以做实现效果,但是static修饰变量,要求变量是一个共享资源数据是才可以修饰。
*     专业方案: 使用关键字 transient 修饰属性,修饰后的属性不会进行序列化。
*
* 注意: 与系统底层有关的对象不能进行序列化,如:System
* */

补充

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;

public class Demo11 {
    public static void main(String[] args) throws IOException {
        PrintStream ps = System.out;
        ps.println("abc");
        ps.write("123".getBytes());  //控制台

        ps.println(new char[]{'1','2','3'});//显示数组内的值
        ps.println(new int[]{4,5,6,6});//显示地址值

        System.out.println("\n------------------------------------");
        PrintStream ps1 = new PrintStream("ps.txt");
        ps1.println("abc");
        ps1.write("hahah".getBytes());
        ps1.print("heihei");

        System.out.println("-------------------------------");
        PrintStream ps2 = new PrintStream(new FileOutputStream("p.txt"));
        ps2.println("abc");
        ps2.write("hahah".getBytes());
        ps2.print("heihei");

    }
}
/*
* PrintStream:
*    打印字节流,可以输出任意类型的数据到目的地,扩展了print/println方法
*    System.out:对应的目的地默认是控制台。
*    构造方法:
*      PrintStream(File/String filename)
*      PrintStream(OutputStream  os)
*
*    println(char[] ch)
*    println(Object obj)
* */

import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Demo12 {
    public static void main(String[] args) throws IOException {
        PrintWriter pw = new PrintWriter("pp.txt");
        pw.write("abc");
        pw.print("hahha");
        pw.println("abcd");
        pw.flush();

        System.out.println("-------------------------");
        //true:代表开启自动刷新
        //自动刷新只有对 println  printf format 有效
        PrintWriter  pw1 = new PrintWriter(new FileOutputStream("ppp.txt"),true);
        pw1.println("abcdsfd");
        pw1.print("abcdsfd");

        PrintWriter  pw2 = new PrintWriter(new FileWriter("ppp1.txt"),true);
        pw2.println("abcdsfd");
        pw2.print("abcdsfd");
    }
}
/*
* PrintWriter:
*    打印字符流,该对象与PrintStream用法基本相同。
*    对象创建只能通过构造方法。
*    构造方法:
*      PrintWriter(File/String  filename)
*      PrintWriter(Writer/OutputStream  wo)
* */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值