day21-Java IO流(字符流/装饰设计模式)

21.01_字符流FileReader

  • 1.字符流是什么

    • 字符流是可以直接读写字符的IO流
    • 字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
  • 2.FileReader

    • FileReader类的read()方法可以按照字符大小读取
FileReader fr = new FileReader("xo.txt");  //可能FileNotFoundException

int c ;
while( (c = fr.read()) != -1) {  //通过项目默认的码表,一次读取一个字符
    System.out.print((char)c);
}
 
fr.close(); //可能IOException

21.02_字符流FileWriter

  • FileWriter类的write()方法可以自动把字符转为字节写出
FileWriter fw = new FileWriter("xo.txt", true);  // true 是否追加到文本末尾

fw.write("--我会追加文本末尾--");
fw.write(97);  // 97 就是字母a
fw.close();

21.03_IO流(字符流的拷贝)

FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");

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

fr.close();
fw.close();   // Writer类中有2K的缓冲区,如果不关流,就有可能出现缓冲区的内容没有写到文件里

21.04_什么情况下使用字符流

  • 字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
  • 程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流
  • Reader读取的时候是按照字符的大小读取的,不会出现半个中文
  • Writer写出的时候可以直接将字符串写出,不用转换为字节数组

21.05_字符流是否可以拷贝非纯文本的文件

  • 不可以拷贝非纯文本的文件
  • 因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去
  • 如果是?,直接写出,这样写出之后的文件就乱了,看不了了

21.06_自定义字符数组的拷贝

// 自定义小数组
FileReader fr = new FileReader("xo.txt");
FileWriter fw=  new FileWriter("a.txt");

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

21.07_IO流(带缓冲的字符流)

  • BufferedReaderread()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率
  • BufferedWriterwrite()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率
BufferedReader br = new BufferedReader(new FileReader("xo.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
int c ;
while( (c = br.read()) != -1) {  //read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量ch
    bw.write(c); //write一次,是将数据装到字符数组,装满后再一起写出去
}

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

21.08_readLine()和newLine()方法

  • BufferedReaderreadLine()方法可以读取一行字符(不包含换行符号)
  • BufferedWriternewLine()可以输出一个跨平台的换行符号\r\n
// 带缓冲区的特殊方法: readLine()   newLine()
BufferedReader br = new BufferedReader(new FileReader("xo.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));

String line ;

while( (line = br.readLine()) != null) {  //读取一行,以 \r 或 \n 为换行标识
    bw.write(line);
    // bw.write("\r\n");  这个也是换行,但是只支持windows系统
    bw.newLine(); // 这个换行是跨平台的
}

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

21.09_将文本反转

  • 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
  • 资源的读取,最好符合 晚开早关 原则
// 文本反转
BufferedReader br = new BufferedReader(new FileReader("xo.txt"));
ArrayList<String> list = new ArrayList<>();
// 将读取的数据存储到集合中
String line ;
while( (line = br.readLine()) != null) {
    list.add(line);
}
br.close();

// 倒着遍历集合,将数据写到文件上
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt",true));
for (int i = (list.size() - 1); i >= 0; i--) {
    bw.write(list.get(i));
    bw.newLine();
}
bw.close();

21.10_LineNumberReader

  • LineNumberReaderBufferedReader的子类, 具有相同的功能, 并且可以统计行号
    • 调用getLineNumber()方法可以获取当前行号
    • 调用setLineNumber()方法可以设置当前行号
LineNumberReader lr = new LineNumberReader(new FileReader("xo.txt"));
lr.setLineNumber(100);  //设置行号,那么后续的行号,将是此值的递增
String line;
while((line = lr.readLine()) != null) { 
    System.out.println(lr.getLineNumber() + " " +line);  // getLineNumber获取行号,从101开始
}

lr.close();

21.11_装饰设计模式

23种设计模式之一,英文叫Decorator Pattern,又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。(Java IO流是典型的装饰设计模式)

  • 装饰对象和真实对象有相同的接口
  • 装饰对象包含一个真实对象的引用(reference)
interface Coder {
    public void coder();  // 编码技能
}

class Student implements Coder {
    public void coder() {
        System.out.println("学生学会了编码技能...");
    }
}

class PeiXunStudnet implements Coder {  //培训过的学生 ,不是通过继承实现的扩展
    private Student s ;
    
    public PeiXunStudnet(Student s) {  //装饰模式,一般通过构造方法获取被装饰的类
        this.s = s ;
    }
    
    // getter 和 setter 方法
    public Student getS() { return s;}
    public void setS(Student s) { this.s = s;}

    // 对学生进行升级
    public void coder() {
        s.coder();  //学生已经学会的技能 , 下面对学生进行技能扩展..
        System.out.println("学生学会了 ssh");
        System.out.println("学生学会了 高级数据库编程");
    }
}

21.11_装饰设计模式 和 继承的区别

装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。

装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。

而继承是通过成为某个类的子类,实现功能的扩展。
所以:装饰模式比继承要灵活。避免了继承体系臃肿(降低了继承造成的耦合)。而且降低了类于类之间的关系。
装饰模式的缺点: 导致程序中出现许多小类,过度使用会使程序变的复杂。

21.12_使用指定的码表读写字符)

  • FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)
  • FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)
// 指定码表读写字符,注意,后面的码表,不分大小写
InputStreamReader ir = new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8");
OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");

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

// 使用包装类,带缓存和指定编码表的读写
BufferedReader br = new BufferedReader(new InputStreamReader(
        new FileInputStream("utf-8.txt"), "UTF-8"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream("gbk.txt"),"GBK"));
int c ;
while( (c = br.read()) != -1) {
    bw.write(c);
}       
br.close();
bw.close();

21.13_转换流图解

转换流.png

其实这个地方应该很好理解的,读写文件有字节流和字符流。我想使用字节流读写文件,但是写文件时候,我需要另外一种编码格式,所以肯定需要一个 字节流和字符流互相转换的类,既然互相转换,所以肯定得有个 编码表 来依照参考。InputStreamReaderOutputStreamWriter就是互相转换字节流和字符流的类,因为二者继承于 ReaderWriter,所以属于字符流体系下。

21.14_获取文本上字符出现的次数

  • 获取一个文本上每个字符出现的次数,将结果写在times.txt上
BufferedReader br = new BufferedReader(new FileReader("xo.txt"));
TreeMap<Character, Integer> map = new TreeMap<>();

int c ;
while( (c = br.read()) != -1) {   //注意这里的强制类型转换
    map.put((char) c, map.containsKey((char)c) ? map.get((char)c)+1 : 1);
}
br.close();

BufferedWriter bw = new BufferedWriter(new FileWriter("times.txt"));
// 双列集合Map的遍历
for (Character key : map.keySet()) {
    switch (key) {  //注意下面的转义的使用
    case '\t':
        bw.write("\\t" + "-->" + map.get(key) + "次");  
        break;
    case '\n':
        bw.write("\\n" + "-->" + map.get(key) + "次");  
        break;
    case '\r':
        bw.write("\\r" + "-->" + map.get(key) + "次");  
        break;
    case ' ':
        bw.write("空格" + "-->" + map.get(key) + "次");  
        break;
    default:
        bw.write(key + "-->" + map.get(key) + "次");
        break;
    }
    bw.newLine();
}
bw.close();

21.15_IO流(试用版软件)

当我们下载一个试用版软件,没有购买正版的时候,每执行一次就会提醒我们还有多少次使用机会用学过的IO流知识,模拟试用版软件,试用10次机会,执行一次就提示一次您还有几次机会,如果次数到了提示请购买正版

核心代码:
BufferedReader br = new BufferedReader(new FileReader("config.txt"));
String line = br.readLine();
br.close();
int num = Integer.parseInt("10"); //将数字字符串转为int 
// 注意,下面这句,不能写到readLine()之前,因为构造Writer会清空源文件内容
BufferedWriter bw = new BufferedWriter(new FileWriter("config.txt"));
bw.write("8");
bw.close();   // close()千万千万不能忘记!!!!

21.16_递归,方法自己调用自己

递归通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

注意

  • 递归可能造成栈内存溢出 ,因为方法不弹栈,一直在栈里循环调用,超过栈内存限制就会内存崩溃了。
  • 构造方法不能递归调用,造成死循环,堆内存溢出。
  • 递归方法不一定非有返回值
阶乘递归
public int fun(int num) {  //三目不好理解,可以转为if
    return num == 1 ? 1 : num * fun(num - 1);
}

整数递减求和
public int sum(int x) { //三目不好理解,可以转为if
    return x > 0 ? x + sum(x-1) : x ;
}

21.17_File类(递归练习)

  • 需求:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名
/**
 * 递归获取 文件夹及其子目录下的java文件
 * @param file 文件夹路径
 */
public static void printJavaFile(File file) {
    File list[] = file.listFiles();
    for (File subFile : list) {
        if (subFile.isFile() && subFile.getName().endsWith(".java")) {
            System.out.println(subFile);
        }else if (subFile.isDirectory()){  //是文件夹,就进入递归
            printJavaFile(subFile);
        }
    }
}

21.18_IO流(总结)

  • 1.会用BufferedReader读取GBK码表和UTF-8码表的字符
  • 2.会用BufferedWriter写出字符到GBK码表和UTF-8码表的文件中
  • 3.会使用BufferedReader从键盘读取一行(readLine())

END。
我是小侯爷。
在魔都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
如果读完觉得有收获的话,记得关注和点赞哦。
非要打赏的话,我也是不会拒绝的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,612评论 5 471
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,345评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,625评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,022评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,974评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,227评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,688评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,358评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,490评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,402评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,446评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,721评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,802评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,013评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,504评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,080评论 2 341

推荐阅读更多精彩内容

  • ①IO流(字符流FileReader) 1.字符流是什么字符流是可以直接读写字符的IO流字符流读取字符, 就要先读...
    Y小圆脸阅读 510评论 0 2
  • 概述: 1、IO流:即Input Output的缩写。 2、特点:1)IO流用来处理设备间的数据传输。2)Java...
    玉圣阅读 1,238评论 0 3
  • 1 IONo18 1.1IO框架 【 IO:Input Output 在程序运行的过程中,可能需要对一些设备进...
    征程_Journey阅读 943评论 0 1
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,537评论 18 399
  • 现在孩子让我开始给他讲故事已经有段时间了,如果自己肚子里没有什么故事,那真是很难满足孩子,所以,当妈的,要努力攒故事啊
    hotmother阅读 122评论 0 0