Java——IO流

1 字节缓冲流

BufferInputStream 将创建一个内部的缓冲区数组,内部缓冲区数组将根据需要从包含的输入流中重新填充,一次可以读取多个字节

BufferOutputStream 该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用

构造方法

方法作用
BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size)创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
  • BufferedInputStream(InputStream in) 创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
    BufferedInputStream(InputStream in, int size) 创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。
   public static void main(String[] args) throws IOException {
        // 创建字节缓冲输出流
        OutputStream os = new FileOutputStream("day_13/dir/bos.txt");
        BufferedOutputStream bos = new BufferedOutputStream(os);
        //写数据
        bos.write("hello\r\n".getBytes(StandardCharsets.UTF_8));
        bos.write("hello\r\n".getBytes(StandardCharsets.UTF_8));
        /*
            BufferedOutputStream  内部带有缓冲区  写数据的时候  先写出到缓冲区,缓冲区写满的时候 ,才会将缓冲区的内容写出到磁盘
            调用flush方法 只刷新缓冲流 但不释放资源
            close方法  在关闭流 释放资源之前 会先刷新缓冲流
         */
       // bos.flush();//刷新缓冲输出流
        bos.close();
        // 字节缓冲输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day_13/dir/bos.txt"));
        byte[] bytes = new byte[1024];
        int len;
        while((len=bis.read(bytes))!= -1){
            System.out.println(new String(bytes,0,len,StandardCharsets.UTF_8));
        }
        bis.close();
    }

为什么缓冲流的构造方法中需要的是一个字节流,而不是具体的文件或者路径呢?

字节缓冲流仅仅提供缓冲区,而真正的读写数据还的移开基本的字节流对象进行操作。

  • 使用字节缓冲流完成视频的复制
public class BufferedStreamDemo2 {
    public static void main(String[] args) throws IOException {
        // 1 创建字节输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\IO\\mp4.mp4"));
        // 2 创建字节输出流
        BufferedOutputStream  bos = new BufferedOutputStream(new FileOutputStream("day_13/dir/aa.mp4"));
        // 3 创建缓冲数组
        byte[] buff  = new byte[1024*1024];
        // 每次读取的字节数
        int  len;
        while((len = bis.read(buff))!= -1){
            bos.write(buff,0,len);
        }
        bos.close();
        bis.close();
    }
}

2 字符流

2.1 为什么出现字符流

由于字节流操作中文不是特别方便,所以就出现了字符流

​ 字符流 = 字节流 +字符集

中文字节存储方式

​ 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因就是最终底层操作会自动的进行字节拼接成中文。

如何识别中文?

​ 汉字在存储的时候 无论使用那种编码存储 第一个字节都是负数。

2.2. 字符串中的编码和解码的问题

编码就指的是将字符转换成字节

    • byte[]getBytes() 使用平台的默认字符集将此 String编码为字节序列,将结果存储到新的字节数组中。
    • byte[]getBytes(String charsetName) 使用命名的字符集将此 String编码为字节序列,将结果存储到新的字节数组中。

解码: 将字节数组转换为字符

    • String(byte[] bytes, String charsetName) 构造一个新的String由指定用指定的字节的数组解码charset
    • String(byte[] bytes, String charsetName) 构造一个新的String由指定用指定的字节的数组解码charset
    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "河南加油";
        // 编码 采用不同的字符集进行编码
        byte[] b1 = s.getBytes();// 默认的编码  UTF-8
        byte[] b2 = s.getBytes("GBK");
        for(byte b : b1){
            System.out.print(b +"  ");
        }
        System.out.println();
        for(byte b : b2){
            System.out.print(b +"  ");
        }
        System.out.println();
        // 解码 编码和解码必须使用相同的码表 否则会出现中文乱码
        String str1 = new String(b1);//使用默认编码  UTF-8
        System.out.println(str1);
        String str2 = new String(b2,"gbk");
        System.out.println(str2);
    }

2.3. 字符流中的编码问题

字符流的抽象基类:

​ Reader 字符输入流的抽象基类 编码

​ Writer 字符输出流的抽象基类 解码

字符流中和编码相关的类:

  • InputStreamReader是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。

  • OutputStreamWriter是字符的桥梁流以字节流:向其写入的字符编码成使用指定的字节charset 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。

构造方法:

  • OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。

  • OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter。

  • InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。

  • InputStreamReader(InputStream in, String charsetName) 创建一个使用命名字符集的InputStreamReader。

    使用字符流完成对于中文的写和读

    public class RWDemo1 {
        public static void main(String[] args) throws IOException {
            // 写数据
            OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("day_13/dir/writer.txt"),"UTF-8");
            writer.write("中国");
            writer.close();
            // 读数据
            InputStreamReader  reader = new InputStreamReader(new FileInputStream("day_13/dir/writer.txt"));
            int ch;
            while((ch = reader.read())!= -1){
                System.out.println((char) ch);
            }
            reader.close();
        }
    }
    

    2.4 字符流读写的方式

    写的方式

  • voidwrite(char[] cbuf, int off, int len) 写入字符数组的一部分。
    voidwrite(int c) 写一个字符
    voidwrite(String str, int off, int len) 写一个字符串的一部分。
  • voidwrite(String str) 写一个字符串
  • voidwrite(char[] cbuf) 写入一个字符数组。

刷新和关闭

  • voidclose() 关闭流,先刷新。
    voidflush() 刷新流。

读的方式

  • intread() 读一个字符
    intread(char[] cbuf, int offset, int length) 将字符读入数组的一部分。
  • intread(char[] cbuf) 将字符读入数组。

使用字符流完成文件的复制

public class RWDemo2{
    public static void main(String[] args) throws IOException {
        Reader  r = new InputStreamReader(new FileInputStream("day_13/dir/窗里窗外.txt"));
        Writer w = new OutputStreamWriter(new FileOutputStream("day_13/dir/aa.txt"));
        char[] buff = new char[1024];
        int len;
        while((len = r.read(buff)) != -1){
            w.write(buff,0,len);
        }
        r.close();
        w.close();
    }
}

字符流自带缓冲区

2.5 字符流操作的便捷类

FileReader 是InputStreamReader的简洁形式

FileWriter 是OutputStreamReader的简洁形式

public class RWDemo3 {
    public static void main(String[] args) throws IOException {
        Reader r = new FileReader("day_13/src/cn/lanqiao/charac/RWDemo2.java");
        Writer w = new FileWriter("day_13/dir/demo.java");
        char[] cbuff = new char[1024];
        int len;
        while((len=r.read(cbuff))!= -1){
            w.write(cbuff,0,len);
        }
        r.close();
        w.close();
    }
}

2.6 字符缓冲流

BufferedReader

  • 从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。可以指定缓冲区大小,或者可以使用默认大小。

    BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。

      • StringreadLine() 读一行文字。

BufferedWriter

  • 将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。

    可以指定缓冲区大小,或者可以接受默认大小。

    BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。

  • voidnewLine() 写一行行分隔符。
public class RWDemo4 {
    public static void main(String[] args) throws IOException {
        Reader r = new FileReader("day_13/src/cn/lanqiao/charac/RWDemo2.java");
        Writer w = new FileWriter("day_13/dir/demo1.java");
        BufferedReader br = new BufferedReader(r);
        BufferedWriter bw = new BufferedWriter(w);
        String line;
        while((line = br.readLine())!=null){// 每次读取一行  当为null的时候  说明到达文件末尾
            bw.write(line);
            bw.newLine();//写行分隔符
            bw.flush();
        }
        br.close();
        bw.close();
    }
}

2.7 IO流小结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4QRODti-1627558360018)(assest/image-20210729105134952.png)]

字节流可以赋值文件数据,有四种方式一般采用字节缓冲流一次去屑一个字节数组的形式。

2.8 练习:

点名器:

我有一个文件里面存储了班级同学的姓名,每个姓名占一行,要求通过程序实现随机点名器

思路:1 使用字符缓冲输入流 特有的方法 readLine()

​ 2 将每一个同学姓名作为List集合的一个元素

​ 3 采用Random产生一个随机数 (1–28);

​ 4 获取随机数所对应的姓名。

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

public class RandomSelect {
    public static void main(String[] args) throws IOException {
        //创建缓冲字符输入流对象
        BufferedReader reader = new BufferedReader(new FileReader("day_13\\namelist.txt"));
        //定义list数组存放读取的名字
        List<String> names = new ArrayList<>();
        //定义字符串保存每次读取的名字
        String name;
        //使用缓冲字符流特有的readline方法 每次读取一个名字,并存入list中,当读取结束,即读取到的字符串为空时,结束
        while((name = reader.readLine()) != null){
            names.add(name);
        }
        System.out.println(names);
        //创建Random对象 生成随机数
        Random random = new Random();
        int select = random.nextInt(names.size());
        //输出这个数对应索引的名字
        System.out.println(names.get(select));
    }
}

案例: 复制单级目录

需求:把D://IO这个文件夹复制到E盘

分析:

​ 1 数据源是 D:\IO

​ 2 目的地:E:

​ 3 判断数据源是目录还是文件

​ 是文件 直接复制 字节流

​ 是目录

​ 在目的地创建该目录

​ 遍历源目录中的所有的文件 获得File数组,得到的每一个File对象 在回到3继续 (递归)

   public static void main(String[] args) throws IOException {
        // 创建数据源目录的File对象
        File  srcDir = new File("day_13/dir");
        //获取数据源目录的名称
        String srcDirName = srcDir.getName();
        // 创建目的地File对象
        File  destDir = new File("E:\\",srcDirName);
        //判断目录是否存在
        if(!destDir.exists()){
            destDir.mkdirs();
        }
        // 遍历源目录  得到File数组
        File[] listFiles = srcDir.listFiles();
        //遍历数组
        for(File srcFile : listFiles){
            //获取列表中文件的名称
            String  srcFileName = srcFile.getName();
            // 创建目的地文件的File对象
            File destFile = new File(destDir,srcFileName);
            //复制文件
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
            byte[] buff = new byte[1024];
            int len;
            while((len = bis.read(buff))!= -1){
                bos.write(buff,0,len);
            }
            bis.close();
            bos.close();
        }
    }

案例: 复制多级目录

    public static void main(String[] args) throws IOException {
        // 创建数据源目录的File对象
        File srcDir = new File("D:\\IO");
        // 创建目的地File对象
        File  destDir = new File("E:\\");
        copyDir(srcDir,destDir);
    }

    public static  void copyDir(File  srcFile,File destFile) throws IOException {
        // 判断 数据源是目录还是文件
        if(srcFile.isDirectory()){
            // 在目的地创建目录
            String srcFileName = srcFile.getName();
            File newDilr = new File(destFile,srcFileName);
            //判断目的地是否存在该目录
            if(!newDilr.exists()){
                newDilr.mkdirs();
            }
            //获取数据源下的所有的File列表
            File[] listFiles = srcFile.listFiles();
            // 遍历列表
            for(File file : listFiles){
                copyDir(file,newDilr);
            }
        }else{
            //说明此时File对象表示的是文件  直接使用字节流复制
            File newFile = new File(destFile,srcFile.getName());
            //复制文件
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile));
            byte[] buff = new byte[1024];
            int len;
            while((len = bis.read(buff))!= -1){
                bos.write(buff,0,len);
            }
            bis.close();
            bos.close();
        }
    }

练习:删除多级目录 删除其中所有的子目录及文件 包括IO根目录都要删除(删除无用目录 因为删除不经过回收站,删除之后 不可找回)

3 标准输入输出流

  • static PrintStreamerr “标准”错误输出流。
    static InputStreamin “标准”输入流。 默认指的键盘
    static PrintStreamout “标准”输出流。 默认是屏幕

3.1.标准输入流

    public static void main(String[] args) throws IOException {
        //字节流
        InputStream is = System.in;// 字节输入流的数据来源来自标准输入设备键盘
//        int by;
//        while((by = is.read())!= -1){
//            System.out.println((char)by);
//        }
        // 将字节流转换为字符流
        InputStreamReader isr = new InputStreamReader(is);//字符流的数据来源来自标准输入设备
        BufferedReader br = new BufferedReader(isr);
        System.out.println("请输入一个字符串:");
        String line = br.readLine();
        System.out.println("您输入的字符串为:" + line);
        // 让用户输入一个整数
        System.out.println("请输入一个整数:");
        int i= Integer.parseInt(br.readLine());
        System.out.println("您输入的整数为:"+(i + 1));
        //以上代码就是自己实现了键盘录入字符串和整数的方法   这样写起来有点麻烦,因此Java提供了
        Scanner sc = new Scanner(System.in);
    }

3.2. 标准输出流

    public static void main(String[] args) throws IOException {
        //PrintStream  本质就是字节输出流
        PrintStream ps = System.out;
        ps.println("hello");
        ps.println(100);
    }

4 打印流

打印流分为:

  • 字节 打印流 PrintStream
  • 字符打印流 PrintWtriter

打印流的特点:

​ 只负责数据的输出 不能读取数据

​ 有一些特有的方法

    public static void main(String[] args) throws FileNotFoundException {
        PrintStream ps = new PrintStream("day_13/dir/ps.txt");
        ps.println("aaaa");
        ps.println(23);
        ps.println(true);
        //释放资源  
        ps.close();
    }
    public static void main(String[] args) throws FileNotFoundException {
        PrintWriter pw = new PrintWriter("day_13/dir/pw.txt");
        pw.print("hello");
        pw.println("aaaaaaaaaaaaaaaaaa");
        pw.close();
    }

使用打印流实现文件的复制

public class PrintStreamDemo {
    public static void main(String[] args) throws IOException {
       // 创建一个字符输入流
        BufferedReader br = new BufferedReader(new FileReader("day_13/dir/demo.java"));
        // 创建一个字符输入流
       // BufferedWriter bw = new BufferedWriter(new FileWriter("day_13/dir/pw.java"));
        PrintWriter pw = new PrintWriter(new FileWriter("day_13/dir/pw.java"));
        //读写数据
        String  line;
        while((line =br.readLine())!=null){
            pw.println(line);
        }
        br.close();
        pw.close();
    }

5 对象的序列化流

对象序列化: 就是将对象保存到磁盘或者在网络中传输对象 为了对象保存的正确性 和传输的安全性,需要对对象进行编码处理,那么把这种处理方式称为对象的序列化

反序列化:将序列化的对象解析为原对象的过程 就称为反序列化

  • ObjectOutputStream

  • ObjectInputStream

对于对象传输 对象必须实现java.io.Serializable接口

   public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day_13/dir/oos.txt"));
        //创建学生对象
        Student stu1 = new Student("张三",22);
        oos.writeObject(stu1);//写出一个对象
        oos.close();
    }

反序列化

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day_13/dir/oos.txt"));
        //创建学生对象
        Student stu1 = new Student("张三",22);
        oos.writeObject(stu1);//写出一个对象
        oos.close();
        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day_13/dir/oos.txt"));
       Object obj =  ois.readObject();
       Student stu = (Student) obj;
        System.out.println(stu);
    }

6 Porperties

Porperties是Map集合的一个实现类

Porperties可以保存到流中或者从流中加载。 属性列表中的键及其对应的值都是字符串

  public static void main(String[] args) {
        Properties prop = new Properties();
        //存储元素
        prop.put("0001","张三");
        prop.put("0002","李四");
        prop.put("0003","王五");
        // 遍历集合
        Set<Map.Entry<Object,Object>>    set =    prop.entrySet();
        for(Map.Entry<Object,Object>  entry : set){
            System.out.println(entry.getKey() +"--"+ entry.getValue());
        }
    }

Porperties和IO流结合的方法

  • voidload(InputStream inStream) 从输入字节流读取属性列表(键和元素对)。
    voidload(Reader reader) 以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。
  • voidstore(OutputStream out, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法加载到 Properties表中的格式输出流。
    voidstore(Writer writer, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式输出到输出字符流。
public class ProPertiesDemo {
    public static void main(String[] args) throws IOException {
       // myLoad();
        myStore();
    }
    public static void myLoad() throws IOException {
        Properties prop = new Properties();
        Reader r = new FileReader("day_13/dir/test.properties");
        prop.load(r);
        r.close();
        System.out.println(prop.get("username"));
    }
    public static void myStore() throws IOException {
        Properties prop = new Properties();
        prop.setProperty("001","张三");
        prop.setProperty("002","李四");
        prop.setProperty("003","王五");
        prop.setProperty("004","admin");
        FileWriter fw = new FileWriter("day_13/dir/test.properties");
        prop.store(fw,"这是存储一些对象");
        fw.close();
    }
}

练习:游戏次数

需求:请编写程序实现猜数字小游戏,只能试完3次,如果达到3次,还想继续玩,提示:游戏试完已结束,想继续,请充值(www.lanqiao.cn)

分析;

1 编写一个游戏类,里面存储一个猜数字的小游戏。

2 写一个测试类,在测试类中调用猜数字游戏

3 玩过几次游戏,记录次数,用文件保存,而且文件应该一开始就提供,初始次数0

4 每次玩的时候,先从文件中读取,看看你玩了几次

​ 如果达到3次 给出提示

​ 如果没有达到3次 把次数+1,重新写会文件,开始玩游戏

public class GueesNumberTest {
    public static void main(String[] args) throws IOException {
        //从文件中读取数据到Properties集合
        Properties  prop= new Properties();
        FileReader fr = new FileReader("day_13/dir/game.properties");
        prop.load(fr);
        fr.close();
        //获取玩过的次数
        String count = prop.getProperty("count");
        int number = Integer.valueOf(count);
        if(number > 3){
            System.out.println("游戏试完结束,请充值(www.lanqiao.cn)");
        }else{
            GuessNumber.start();
            number++;
            prop.setProperty("count",String.valueOf(number));
            FileWriter fw = new FileWriter("day_13/dir/game.properties");
            prop.store(fw,null);
            fw.close();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值