JAVA之旅(二十六)——装饰设计模式,继承和装饰的区别,LineNumberReader,自定义LineNumberReader,字节流读取操作,I/O复制图片

简介: JAVA之旅(二十六)——装饰设计模式,继承和装饰的区别,LineNumberReader,自定义LineNumberReader,字节流读取操作,I/O复制图片一.装饰设计模式 其实我们自定义readLine就是一种装饰模式当想要对已有的对象进行功能增强...

JAVA之旅(二十六)——装饰设计模式,继承和装饰的区别,LineNumberReader,自定义LineNumberReader,字节流读取操作,I/O复制图片


一.装饰设计模式

其实我们自定义readLine就是一种装饰模式

  • 当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,并且提供加强功能,那么自定义的该类就称为装饰类
package com.lgl.hellojava;

public class HelloJJAVA {
    public static void main(String[] args) {

        Person p = new Person();
        p.eat();
        // 开始进行增强
        superPerson p1 = new superPerson(p);
        p1.superEat();
    }
}

class Person {
    public void eat() {
        System.out.println("吃饭");
    }
}

class superPerson {

    private Person p;

    public superPerson(Person p) {
        this.p = p;
    }

    public void superEat() {
        System.out.println("小菜+吃饭");
    }
}

这里的逻辑就是当我们吃饭这个功能需要增强的时候,我们应该装饰他

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

二.继承和装饰的区别

你现在知道了装饰模式,那你一定会疑问,和继承的道理类似,对吧,我们现在来说下他们的区别

这里我们就不写代码了,我们看注释

package com.lgl.hellojava;

public class HelloJJAVA {
    public static void main(String[] args) {
        /**
         * MyReader:专门用于读取数据的类
         * MyTextReader:专门读取文本 两个向上抽取,形成继承体系
         */

        /**
         * 想实现更多的功能 
         * MyBufferReader 
         * myBufferTestReader
         */

        /**
         *谁需要加强就传谁进来
         * class MyBufferReader{
         * }
         */

    }
}

这个逻辑大概是这样的,我们有两个功能,一个读取文件,一个读取文本,他们其实是有共性的,你就把他们共性部分抽取出来,可是我现在在读取文本的时候我顺便想读取图片呢?其实,我们就是这样才产生的装饰者模式

  • 装饰者模式比继承要灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系
  • 装饰类因为增强已有对象,具备功能和已有的想相同,只不过提供了更强的功能,所以装饰类和被装饰类通常属于一个体系中的

三.LineNumberReader

这也是一个子类

这里写图片描述

他也是一个包装类,我们看例子

package com.lgl.hellojava;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

public class HelloJJAVA {
    public static void main(String[] args) {
        FileReader fr;
        try {
            fr = new FileReader("test.txt");
            LineNumberReader lnr = new LineNumberReader(fr);
            String line = null;
            while((line = lnr.readLine()) != null){
                System.out.println(lnr.getLineNumber()+":"+line);
            }
            lnr.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

他输出的结果

这里写图片描述

他可以获取和设置行号

四.自定义LineNumberReader

我们可以根据他的原理自己也来实现一个,仔细看注释

package com.lgl.hellojava;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class HelloJJAVA {
    public static void main(String[] args) {
        try {
            FileReader fr = new FileReader("test.txt");
            MyLineNumberReader my = new MyLineNumberReader(fr);
            String line = null;
            while ((line = my.MyReadLine()) != null) {
                System.out.println(my.getLineReader() + ":" + line);
            }
            my.MyClose();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class MyLineNumberReader {
    // 读取
    private Reader r;
    // 行号
    private int lineReader;

    // 构造方法
    public MyLineNumberReader(Reader r) {
        this.r = r;
    }

    // 提供对外方法
    public String MyReadLine() {
        // 行号自增
        lineReader++;
        StringBuilder sb = new StringBuilder();
        int ch = 0;
        try {
            while ((ch = r.read()) != -1) {
                if (ch == '\r')
                    continue;
                if (ch == '\n')
                    return sb.toString();
                else
                    sb.append((char) ch);
            }
            if (sb.length() != 0)
                return sb.toString();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    public int getLineReader() {
        return lineReader;
    }

    public void setLineReader(int lineReader) {
        this.lineReader = lineReader;
    }

    public void MyClose() {
        try {
            r.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

这个思路是不是很清晰,实际上和LineNumberReader是类似的

五.字节流读取操作

字符流我们讲的差不多了,我们接着说字节,其实他们类似的,知识他操作的是字节而已

  • inputStream:读
  • outputStream:写

我们还是从例子开始

package com.lgl.hellojava;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class HelloJJAVA {
    public static void main(String[] args) {
        writeFile();

    }

    // 写文件
    public static void writeFile() {
        try {
            FileOutputStream fo = new FileOutputStream("demo.txt");
            fo.write("test".getBytes());
            fo.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

这里我们可以看到,他写入数据不需要刷新,现在还没有涉及到缓存区,我们继续看,写已经写好了,现在我们开始读,对于读取数据,我们开头用到的两种方法

// 字符读数据
    public static void readFile() {
        try {
            FileInputStream fs = new FileInputStream("demo.txt");
            int ch = 0;
            while ((ch = fs.read()) != -1) {
                System.out.println((char) ch);
            }
            fs.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    // 字节读取
    public static void readFile1() {
        try {
            FileInputStream fs = new FileInputStream("demo.txt");
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = fs.read(buf)) != -1) {
                System.out.println(new String(buf, 0, len));
            }
            fs.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

现在我们有了专门处理的字节流,我们可以这样做

public static void readFile2() {
        try {
            FileInputStream fs = new FileInputStream("demo.txt");
            int num = fs.available();
            System.out.println(num);
            fs.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

我们发现直接用available就可以拿到字节了,原理其实是这段代码

public static void readFile2() {
        try {
            FileInputStream fs = new FileInputStream("demo.txt");
            byte[] buf = new byte[fs.available()];
            fs.read(buf);
            System.out.println(new String(buf));
            fs.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

六.I/O复制图片

ok,这里算是一个小练习,复制一张图片,我们理顺下思路

  • 1.用字节读取流和图片关联
  • 2.用字节流写入流对象创建一个图片文件,存储数据
  • 3.通过循环读写,完成数据存储
  • 4.关闭流

OK,我们用代码说话

package com.lgl.hellojava;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class HelloJJAVA {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        FileInputStream fis = null;

        try {
            // 复制
            fos = new FileOutputStream("copy_img.png");
            // 原图
            fis = new FileInputStream("img.png");

            byte[] buf = new byte[1024];

            int len = 0;

            while ((len = fis.read(buf)) != -1) {
                fos.write(buf, 0, len);
            }

            fis.close();
            fos.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

这样。我们图片就拷贝过来了

这里写图片描述

好的,知识点今天就到这里

有兴趣的可以加群:555974449,咱们一起学习,一起进步!

目录
相关文章
|
24天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
48 1
|
24天前
|
Java
在 Java 中,如何自定义`NumberFormatException`异常
在Java中,自定义`NumberFormatException`异常可以通过继承`IllegalArgumentException`类并重写其构造方法来实现。自定义异常类可以添加额外的错误信息或行为,以便更精确地处理特定的数字格式转换错误。
30 1
|
2天前
|
Java
java实现从HDFS上下载文件及文件夹的功能,以流形式输出,便于用户自定义保存任何路径下
java实现从HDFS上下载文件及文件夹的功能,以流形式输出,便于用户自定义保存任何路径下
54 34
|
17天前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
39 8
|
1月前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
52 14
|
1月前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
22 3
|
2月前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
2月前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
39 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
33 2