JAVA之旅(二十六)——装饰设计模式,继承和装饰的区别,LineNumberReader,自定义LineNumberReader,字节流读取操作,I/O复制图片
一.装饰设计模式
其实我们自定义readLine就是一种装饰模式
-
当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,并且提供加强功能,那么自定义的该类就称为装饰类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 1package com.lgl.hellojava;
2
3public class HelloJJAVA {
4 public static void main(String[] args) {
5
6 Person p = new Person();
7 p.eat();
8 // 开始进行增强
9 superPerson p1 = new superPerson(p);
10 p1.superEat();
11 }
12}
13
14class Person {
15 public void eat() {
16 System.out.println("吃饭");
17 }
18}
19
20class superPerson {
21
22 private Person p;
23
24 public superPerson(Person p) {
25 this.p = p;
26 }
27
28 public void superEat() {
29 System.out.println("小菜+吃饭");
30 }
31}
32
这里的逻辑就是当我们吃饭这个功能需要增强的时候,我们应该装饰他
- 装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能提供更强的功能
二.继承和装饰的区别
你现在知道了装饰模式,那你一定会疑问,和继承的道理类似,对吧,我们现在来说下他们的区别
这里我们就不写代码了,我们看注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 1package com.lgl.hellojava;
2
3public class HelloJJAVA {
4 public static void main(String[] args) {
5 /**
6 * MyReader:专门用于读取数据的类
7 * MyTextReader:专门读取文本 两个向上抽取,形成继承体系
8 */
9
10 /**
11 * 想实现更多的功能
12 * MyBufferReader
13 * myBufferTestReader
14 */
15
16 /**
17 *谁需要加强就传谁进来
18 * class MyBufferReader{
19 * }
20 */
21
22 }
23}
24
25
这个逻辑大概是这样的,我们有两个功能,一个读取文件,一个读取文本,他们其实是有共性的,你就把他们共性部分抽取出来,可是我现在在读取文本的时候我顺便想读取图片呢?其实,我们就是这样才产生的装饰者模式
- 装饰者模式比继承要灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系
- 装饰类因为增强已有对象,具备功能和已有的想相同,只不过提供了更强的功能,所以装饰类和被装饰类通常属于一个体系中的
三.LineNumberReader
这也是一个子类
他也是一个包装类,我们看例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 1package com.lgl.hellojava;
2
3import java.io.FileNotFoundException;
4import java.io.FileReader;
5import java.io.IOException;
6import java.io.LineNumberReader;
7
8public class HelloJJAVA {
9 public static void main(String[] args) {
10 FileReader fr;
11 try {
12 fr = new FileReader("test.txt");
13 LineNumberReader lnr = new LineNumberReader(fr);
14 String line = null;
15 while((line = lnr.readLine()) != null){
16 System.out.println(lnr.getLineNumber()+":"+line);
17 }
18 lnr.close();
19 } catch (FileNotFoundException e) {
20 // TODO Auto-generated catch block
21 e.printStackTrace();
22 } catch (IOException e) {
23 // TODO Auto-generated catch block
24 e.printStackTrace();
25 }
26 }
27}
28
29
他输出的结果
他可以获取和设置行号
四.自定义LineNumberReader
我们可以根据他的原理自己也来实现一个,仔细看注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 1package com.lgl.hellojava;
2
3import java.io.FileNotFoundException;
4import java.io.FileReader;
5import java.io.IOException;
6import java.io.Reader;
7
8public class HelloJJAVA {
9 public static void main(String[] args) {
10 try {
11 FileReader fr = new FileReader("test.txt");
12 MyLineNumberReader my = new MyLineNumberReader(fr);
13 String line = null;
14 while ((line = my.MyReadLine()) != null) {
15 System.out.println(my.getLineReader() + ":" + line);
16 }
17 my.MyClose();
18 } catch (FileNotFoundException e) {
19 // TODO Auto-generated catch block
20 e.printStackTrace();
21 }
22 }
23}
24
25class MyLineNumberReader {
26 // 读取
27 private Reader r;
28 // 行号
29 private int lineReader;
30
31 // 构造方法
32 public MyLineNumberReader(Reader r) {
33 this.r = r;
34 }
35
36 // 提供对外方法
37 public String MyReadLine() {
38 // 行号自增
39 lineReader++;
40 StringBuilder sb = new StringBuilder();
41 int ch = 0;
42 try {
43 while ((ch = r.read()) != -1) {
44 if (ch == '\r')
45 continue;
46 if (ch == '\n')
47 return sb.toString();
48 else
49 sb.append((char) ch);
50 }
51 if (sb.length() != 0)
52 return sb.toString();
53 } catch (IOException e) {
54 // TODO Auto-generated catch block
55 e.printStackTrace();
56 }
57 return null;
58 }
59
60 public int getLineReader() {
61 return lineReader;
62 }
63
64 public void setLineReader(int lineReader) {
65 this.lineReader = lineReader;
66 }
67
68 public void MyClose() {
69 try {
70 r.close();
71 } catch (IOException e) {
72 // TODO Auto-generated catch block
73 e.printStackTrace();
74 }
75 }
76
77}
78
79
这个思路是不是很清晰,实际上和LineNumberReader是类似的
五.字节流读取操作
字符流我们讲的差不多了,我们接着说字节,其实他们类似的,知识他操作的是字节而已
- inputStream:读
- outputStream:写
我们还是从例子开始
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 1package com.lgl.hellojava;
2
3import java.io.FileNotFoundException;
4import java.io.FileOutputStream;
5import java.io.IOException;
6
7public class HelloJJAVA {
8 public static void main(String[] args) {
9 writeFile();
10
11 }
12
13 // 写文件
14 public static void writeFile() {
15 try {
16 FileOutputStream fo = new FileOutputStream("demo.txt");
17 fo.write("test".getBytes());
18 fo.close();
19 } catch (FileNotFoundException e) {
20 // TODO Auto-generated catch block
21 e.printStackTrace();
22 } catch (IOException e) {
23 // TODO Auto-generated catch block
24 e.printStackTrace();
25 }
26 }
27}
28
29
这里我们可以看到,他写入数据不需要刷新,现在还没有涉及到缓存区,我们继续看,写已经写好了,现在我们开始读,对于读取数据,我们开头用到的两种方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 1// 字符读数据
2 public static void readFile() {
3 try {
4 FileInputStream fs = new FileInputStream("demo.txt");
5 int ch = 0;
6 while ((ch = fs.read()) != -1) {
7 System.out.println((char) ch);
8 }
9 fs.close();
10 } catch (FileNotFoundException e) {
11 // TODO Auto-generated catch block
12 e.printStackTrace();
13 } catch (IOException e) {
14 // TODO Auto-generated catch block
15 e.printStackTrace();
16 }
17 }
18
19 // 字节读取
20 public static void readFile1() {
21 try {
22 FileInputStream fs = new FileInputStream("demo.txt");
23 byte[] buf = new byte[1024];
24 int len = 0;
25 while ((len = fs.read(buf)) != -1) {
26 System.out.println(new String(buf, 0, len));
27 }
28 fs.close();
29 } catch (FileNotFoundException e) {
30 // TODO Auto-generated catch block
31 e.printStackTrace();
32 } catch (IOException e) {
33 // TODO Auto-generated catch block
34 e.printStackTrace();
35 }
36 }
37
现在我们有了专门处理的字节流,我们可以这样做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 1public static void readFile2() {
2 try {
3 FileInputStream fs = new FileInputStream("demo.txt");
4 int num = fs.available();
5 System.out.println(num);
6 fs.close();
7 } catch (FileNotFoundException e) {
8 // TODO Auto-generated catch block
9 e.printStackTrace();
10 } catch (IOException e) {
11 // TODO Auto-generated catch block
12 e.printStackTrace();
13 }
14 }
15
我们发现直接用available就可以拿到字节了,原理其实是这段代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 1public static void readFile2() {
2 try {
3 FileInputStream fs = new FileInputStream("demo.txt");
4 byte[] buf = new byte[fs.available()];
5 fs.read(buf);
6 System.out.println(new String(buf));
7 fs.close();
8 } catch (FileNotFoundException e) {
9 // TODO Auto-generated catch block
10 e.printStackTrace();
11 } catch (IOException e) {
12 // TODO Auto-generated catch block
13 e.printStackTrace();
14 }
15 }
16
六.I/O复制图片
ok,这里算是一个小练习,复制一张图片,我们理顺下思路
- 1.用字节读取流和图片关联
- 2.用字节流写入流对象创建一个图片文件,存储数据
- 3.通过循环读写,完成数据存储
- 4.关闭流
OK,我们用代码说话
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 1package com.lgl.hellojava;
2
3import java.io.FileInputStream;
4import java.io.FileNotFoundException;
5import java.io.FileOutputStream;
6import java.io.IOException;
7
8public class HelloJJAVA {
9 public static void main(String[] args) {
10 FileOutputStream fos = null;
11 FileInputStream fis = null;
12
13 try {
14 // 复制
15 fos = new FileOutputStream("copy_img.png");
16 // 原图
17 fis = new FileInputStream("img.png");
18
19 byte[] buf = new byte[1024];
20
21 int len = 0;
22
23 while ((len = fis.read(buf)) != -1) {
24 fos.write(buf, 0, len);
25 }
26
27 fis.close();
28 fos.close();
29 } catch (FileNotFoundException e) {
30 // TODO Auto-generated catch block
31 e.printStackTrace();
32 } catch (IOException e) {
33 // TODO Auto-generated catch block
34 e.printStackTrace();
35 }
36 }
37}
38
39
这样。我们图片就拷贝过来了
好的,知识点今天就到这里