JavaSE基础——(21)IO流&字符流&递归

目录

一、字符流

1.1字符流概述

1.2FileReader

1.3FileWriter

1.4字符流的拷贝

1.5字符流使用情况

1.6字符流是否可以拷贝非纯文本文件

1.7带缓冲的字符流

1.7.1缓冲区字符流拷贝

1.7.2 readLine()和newLine()方法

1.8将文本反转

1.9LineNumberReader

二、装饰设计模式

三、使用指定码表读写文件

四、试用版软件功能实现

五、递归


一、字符流

1.1字符流概述

字符流是可以直接读写字符的IO流,

字符流读取字符,就是先读取到字节数据,然后转为字符,

如果要写出字符,需要把字符转为字节再写出。

1.2FileReader

FileReader是用于读取字符流的类,如果要读取原生的字节流,一般使用FileInputStream,

FileReader主要有以下几个构造方法,

FileReader(File file)//创建一个新的FileReader,给出File读取
FileReader(String fileName)//创建一个新的FileReader,给定要读取的文件的名称。

我们来看看具体如何使用,

import java.io.*;

public class IOTest{
    public static void main(String[] args) throws IOException {
        FileReader fr=new FileReader("data.txt");
        int x;
        while((x=fr.read())!=-1){
            System.out.print((char)x);
        }
        fr.close();
    }
}

1.3FileWriter

FileWriter类中的write()方法可以自动把字符转为字节写出,其主要构造函数如下,

FileWriter(File file)//给一个File对象构造一个FileWriter对象
FileWriter(File file, boolean append)//给一个File对象构造一个FileWriter对象,append为true时从文件末尾写入而不是文件开头
FileWriter(String fileName)//构造一个给定文件名的FileWriter对象
FileWriter(String fileName, boolean append)//构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据

我们看看具体如何使用该对象写入字符,

FileWriter fw=new FileWriter("data.txt",true);
fw.write("\r\nFileWriter中文写入测试");
fw.close();

1.4字符流的拷贝

字符流的拷贝也是一样,一个字符一个字符的读取到程序中,然后逐个写入,

        FileReader fr=new FileReader("data.txt");
        FileWriter fw=new FileWriter("dataCopy.txt");

        int c;
        while((c=fr.read())!=-1){
            fw.write(c);
        }

        fr.close();
        fw.close();

我们可以看到内容已经拷贝到目标文件中去了,

注意这里必须要关闭fw字符流对象,因为FileWriter类在写字符的时候,底层调用的是缓冲区写入,缓冲区大小为2k,

如果内容没有装满缓冲区,就不会刷新到文件中,所以我们要调用close方法将缓冲区刷新,将内容写入到目标的文件中。

1.5字符流使用情况

字符流可以拷贝文本文件,但是不推荐使用,

因为读取时会把字节转为字符,写出时还要把字符转回字节,直接使用字节流更方便快速,

当程序需要读取一段文本,或者需要写出一段文本的时候可以使用字符流,

读取的时候是按照字符的大小读取的,不会出现半个中文;写出的时候可以直接将字符串写出,不用转换为字节数组。

1.6字符流是否可以拷贝非纯文本文件

字符流不可以拷贝非纯文本文件,比如图片视频等文件,

在读的时候会将两个字节转换为字符,转换过程中可能找不到对应的字符,就会用?代替,

写出的时候会将字符转换成字节写出去,如果是?直接写出,那么文件就会乱码。

1.7带缓冲的字符流

同样的,在字符流中也有带缓冲的读写类,分别为BufferedReader和BufferedWriter,缓冲区大小都为16k,

BufferedReader的read()方法读取字符时会一次读取若干个字符到缓冲区,然后逐个返回给程序,降低读取文件的次数,提高效率,

BufferedWriter的write()方法写出字符时会先写到缓冲区,缓冲区写满时才会写道文件,降低写文件的次数,提高效率。

1.7.1缓冲区字符流拷贝

我们来看看具体如何使用read()和write()方法进行拷贝,

import java.io.*;

public class IOTest{
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("data.txt"));
        BufferedWriter bw=new BufferedWriter(new FileWriter("dataCopy.txt"));

        int c;
        while((c=br.read())!=-1){
            bw.write(c);
        }
        
        br.close();
        bw.close();
    }
}

1.7.2 readLine()和newLine()方法

BufferedReader的readLine()方法可以读取一行字符(不包括换行符号),

返回值为字符串,如果到达文件末尾则返回一个null,

BufferedWriter的newLine()方法可以输出一个跨平台的换行符号,

(在windows平台下换行符号为"\r\n",在linux平台下换行符号为"\n",在mac平台下换行符号为"\r")。

        BufferedReader br=new BufferedReader(new FileReader("data.txt"));
        BufferedWriter bw=new BufferedWriter(new FileWriter("dataCopy.txt"));

        String str;
        while((str=br.readLine())!=null){
            bw.write(str);//写入一行文本
            bw.newLine();//换行
        }

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

1.8将文本反转

要求将一个文本文档中的文本反转,第一行和最后一行内容交换,第二行和倒数第二行内容交换,以此类推,

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

public class IOTest{
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("data.txt"));
        BufferedWriter bw=new BufferedWriter(new FileWriter("dataReverse.txt"));

        ArrayList<String> list=new ArrayList<>();
        String line;
        while((line=br.readLine())!=null){
            list.add(line);
        }

        for (int i = list.size()-1; i >=0 ; i--) {
            bw.write(list.get(i));
            bw.newLine();
        }

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

1.9LineNumberReader

LineNumberReader是BufferedReader的子类,具有相同的功能,

并且可以统计行号,默认情况下行号从0开始,

  • getLineNumber()获取当前行号
  • setLineNumber()设置当前行号

注意这里的setLineNumber()方法设置的行号并不会更改实际的行位置,更改的是getLineNumber()方法返回的值。

        LineNumberReader lr=new LineNumberReader(new FileReader("data.txt"));

        String line;
        lr.setLineNumber(100);//设置初始行号为100
        while((line=lr.readLine())!=null){
            System.out.println(lr.getLineNumber()+":"+line);//输出行号和内容
        }

二、装饰设计模式

装饰模式又名包装(Wrapper)模式,

装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,

public class WrapTest{
    public static void main(String[] args) throws IOException {
        ChineseDog cd=new ChineseDog(new Dog());
        cd.ability();
    }
}

interface Animal {
    public void ability();
}

class Dog implements Animal{
    @Override
    public void ability() {
        System.out.println("吃骨头");
    }
}

class ChineseDog implements Animal{
    private Dog d;
    public ChineseDog(Dog d){
        this.d=d;
    }
    @Override
    public void ability() {
        d.ability();
        System.out.println("看门");
    }
}

我们可以用继承实现这样的功能,但是继承关系耦合性太强了,父类修改了子类也会跟着改变,

而使用装饰模式就没有那么强的耦合性,两个类都是独立的,被装饰类的变化和装饰类的变化无关。

三、使用指定码表读写文件

FileReader使用默认码表读取文件,如果需要使用指定码表读取,则可以使用InputStreamReader(字节流,编码表),

FileWriter使用默认码表写出文件,如果需要使用指定码表写出,则可以使用OutputStreamReader(字节流,编码表)。

        //读取uft-8编码格式的文本,将内容以gbk格式写入另一个文本中
        InputStreamReader isr=new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8");
        OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");

        int c;
        while((c=isr.read())!=-1){
            osw.write(c);
        }

        isr.close();
        osw.close();

四、试用版软件功能实现

当我们使用一款使用软件时,要求一共有10次试用的机会,每运行一次程序试用机会就会减少,

我们将使用机会次数存储到文件中,每次运行都会读取剩余试用机会,当使用完试用机会后,提示用户购买正版软件,

        BufferedReader br=new BufferedReader(new FileReader("trialTimes.txt"));

        String str=br.readLine();
        int times=Integer.parseInt(str);
        br.close();

        if(times>0){
            System.out.println("您的剩余使用次数为:"+ --times);
            FileWriter fw=new FileWriter("trialTimes.txt");
            fw.write(times+"");//以字符串的形式写出
            fw.close();
        }else{
            System.out.println("试用次数已用完,请购买正版软件!");
        }

五、递归

递归就是方法不断调用自己本身,很多递归都可以用for循环实现,

但是在有些不明确循环次数的时候,使用递归就非常简单了,我们举个例子来看看如何使用递归,

我们算一下10的阶乘,

    public static void main(String[] args) throws IOException {
        System.out.println(factorial(10));
    }

    public static int factorial(int num){
        if(num ==1){
            return 1;
        }else{
            return num * factorial(num-1);
        }
    }

使用递归有个很明显的缺点,就是使用递归时方法会不断压栈,并且不会释放内存空间,

如果调用的次数过多,很有可能会导致栈内存溢出。注意:构造方法中不能使用递归!否则就会一直在构造中死循环。

猜你喜欢

转载自blog.csdn.net/weixin_39478524/article/details/112958303
今日推荐