一键读取文件,Java I/O真的太方便了,一文带你了解I/O和设计模式之Decorator Pattern【Java 养成】

Java学习打卡:第十天

Java入门到精通(打卡第十天)


学习内容

Java IO编程


内容管理

I/O 基本介绍

接下来我们来分享一下Java的输入输出编程原理,programmer通常需要操作I/O设备,提供有效的输入/输出操作

不同的I/O设备有不同的特性,such as 键盘、鼠标、显示器、打印机、扫描仪、硬盘etc. 这些I/O设备有不同的接口,之后操作系统根据不同的驱动来实现相关function.

想必大家只要接触过一点点C++的知识就知道其头文件要求是一个iostream,这里的I/O 就是输入输出,那stream是什么? stream是一种抽象的流动。 那Java也是是将I/O抽象为流

  • Java I/O libraries(方法库) use the abstraction of a stream, which resprents(描绘,代表) any data source or sink(下沉,汇集) as an object capable(容许–的) of producing or recieving pieces of data. //就是说JavaI/O库使用一种抽象的数据流动,这种流动可以代表任何的数据资源或者汇集成一个容许生产和接受数据的一个对象。
  • Java通过流的概念封装了很多操作系统底层的操作,所以如果有必要还是去阅读API观察I/O的实现。 The stream hides the details of ehat happens to the data inside the actual(实际的) I/O device(手段).

Java通过封装I/O节省了programmer的很多步骤,Java和C++将输入和输出擦欧总包装成基于字节流(字符流)的操作,输入输出是包含三个方面的内容。通过C语言的学习就知道简单的输入,还有就是读写文件,以及对内存指定空间进行输入输出

标准I/O

对系统指定的标准设备进行输入输出,即从键盘输入数据,输出到显示器屏幕,这也就是我们经常遇到的I/O,简称为标准I/O

文件I/O

对外存磁盘文件为对象进行输入和输出,即从磁盘文件输入数据,数据输出到磁盘文件,以外存为对象的输入和输出就是文件I/O

串I/O

对内存中指定空间进行输入和输出,通常指定一个字符数组作为存储空间(可利用该空间存储任何信息,)这种输入输出为字符串的输入输出称为串I/O

这上面所有实现输入输出的类都封装在Java.IO packages中,我们调用就直接java.io.*,而这些流都是8-bit byte stream,就是8个字节

8位字节流 8-bit byte stream
  • 所有的流分为两个部分,输入流和输出流,InputStream 和 OutputStream两个类
  • 继承该基类的流以字节为单位处理
基本类

8位字节流有两个基类InputStream和OutputStream,需要注意的是这两个类都是抽象类

所以一般by inheritance,kind of stream(Media stream):所以我们通过继承(重写覆盖)上面的抽象类,可以创建与存储介质直接连接的流,表示在哪个界面上发生(介质流)比如FileStream

抽象类InputStream

常用方法

从输入流(数据源)中读取数据:(这是一个字节一个字节读数据)

  • public abstract int read() throws IOException 从数据源中读取一个字节返回(返回的0–255之间的一个字节所对应的编码)
  • public int read(byte[] b)throws IOException 从数据源中读取字节放入字节数组byte中
  • public int read(byte[] b,int off,int len)throws IOException 从数据源读取字节放入b中从那里开始到哪里结束的位置
  • public int available()throws IOException 数据源中还有没有字节可以读取,类似容器ArrayList里的hasnext() method
  • public long skip(long n)throws IOException 数据源中跳过n个字节不读取,这里是long型的,因为字节数很多

关闭输入流

  • public void close()throws IOException 如果不用的话要及时关闭,避免占用内存
Kinds of InputStream

其子类常见的有三种

  • StringBufferedInputStream ----- 它是将String作为数据来源
  • ByteArrayInputStream------ 允许内存的一个缓冲区当输入流使用
  • FileInputStream ----- 把文件当作数据源 public class FileInputStream extends InputStream ,构造函数FileInputStream(String name);可以指定文件的路径

这里我们来举一个例子

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
	private static final String NEWLINE = System.getProperty("line.separator");
	
	public static void main(String[] args) throws IOException {
		FileInputStream readFile = new FileInputStream("FileInputStreamdemo.java");//文件名,这就是文件输入流类,要导入
		int intTemp = readFile.read();
		while(intTemp != -1) //读取结束就是endFile就是为-1时就代表结束了
		{
			System.out.println((char)intTemp);//输出的是字节所对应的编码要进行强制转换
		    intTemp = readFile.read();//这里可以不断读取
		}
		readFile.close();
  }//这里出现了异常,因为我没有让他读入数据
}
/*
Exception in thread "main" java.io.FileNotFoundException: java.java_study.FileInputFileDemo (系统找不到指定的文件)at java.base/java.io.FileInputStream.open0(Native Method)
*/
键盘输入类-----System.in(Kinds of InputStream)

这个我们平时就见到的多了,因为平时我们的属于大部分是用键盘输入,所以用到System.in比较多,这个也是InputStream的子类

  • 它是将键盘输入的数据作为输入流来使用

  • in是类System的静态变量,即public static final InputStream in.

比如我们从键盘输入数据

public class FileInputStreamDemo {
	private static final String NEWLINE = System.getProperty("line.separator");
	
	public static void main(String[] args) throws IOException{
		int in = System.in.read();//这个是一个字节一个字节读,输入65,输出为6
//		while((char)in != '\n');
//		{
			System.out.println((char)in);
//			in = System.in.read();
//		}	
  }
}//这是8位字节输入流
抽象类OutputStream

输出数据到相应位置:(这是一个字节一个字节输出数据)

  • public void write() throws IOException 从数据源中输出一个字节
  • public void write(byte[] b)throws IOException 从数据源中输出字节放入字节数组byte中
  • public void write(byte[] b,int off,int len)throws IOException 从数据源输出字节放入b中从那里开

和刚刚的输入流恰好相反,它还有其他方法比如刷空所有输出流,输出缓存中所有的自己二到相应的输出流

  • public void flush()throws IOException
  • public void close() 关闭相应的输出流
Kinds of OutputStream 恰好与输入流相比较

其子类常见的有l两种(介质流)、、比输入流要少一种String,因为怎么输出到string !!!

  • ByteArrayOutputStream------ 串输出流,把串当作目标去写,分配一块内存当作缓冲区,将所有输出的字节放在该缓冲区中
  • FileOutputStream -----文件输出流,把文件当作目标去写

我们下面还是以FileOutStream来举例,输出流也就是目标介质不同,这里我们就是将字节写入文件中去

public static void main(String[] args) throws IOException{
		FileOutputStream writeFile = new FileOutputStream("out.txt");//将数据写入到指定文件中去,out.txt就是指定的文件名
		String s = "happy every day" + "\n" + "wish you happy";
		for(int i = 0;i < s.length();i++)
		{
			writeFile.write(s.charAt(i));
		}
		writeFile.close();
  }//字节byte = 8位bits boolean也是一字节,char是两字节;这里的数据源就是String

在这里插入图片描述

像输出到显示器上的子类就是我们常见的System.out ,它的介质就是显示器

过滤流Filter—Stream

Java除了基本的InputStream和OutputSream之外,还提供了过滤流来实现附加的功能

类FilterInputStream FilterOutputStream分别加工,提供更多的操作

和Stream的关系
  • extends (继承)类FilterInputStream还是继承了InputStream,过滤流具备输入流的功能

  • 关联关系(过滤输入流的加工对象),FileInputStream维护了一个私有的关联属性

    -inputStream,(UML类图表示的)通过FilteredStream构造方法的参数初始化该私有属性

FilterInputStream

FilterInputStream与InputStream具有继承关系同时又有关联关系,关联属性位inputStream,其下面有几个子类,有BufferedInputSream,DataInputStream,这里的Buffered是过滤流,上面的buffer是普通的输入流

BufferedStream

BufferedInputStream(InputStream in)

位读取的数据申请一份可用的缓冲区,用于提高输入处理的效率,运用它可以避免“每次想要取得数据都得进行实际的读取操作,它所代表的意义是”使用缓存区“,它所特有的操作是mark和reset操作,缓存

DataInputStream

DataInputStream(InputStream in)//之前的流是不具备读取基本数据类型数据的,只能一个字节一个字节读取

从流中读取基本类型(int,char,long)的数据

含有一份完整接口(public方法),让你可以读取各种基本类型的数据(查看API帮助文档)

FilterOutputStream–输出过滤流

这和输入过滤流没有太大的差距,但是还是有一点不同,就是它的子类多了一个PrintStream过滤流

  • BufferedOutputStream,避免每次想要写入数据时都得执行实际的写入动作。代表的是“使用混存取”可以调用flush()来清除缓存区的内容
  • DataOutputStream
  • PrintStream 继承了extends FilterOutputStream

PrintStream(OutputStream out)这个就是我们经常使用的System.out.println();就是将数据显示到显示器上面,各种数据类型都行(比如int,char,float,double,String),就是说它可以产生格式化的输出结果,处理各种数据的显示,可以打印各种基本类型的数据和字符串类型的数据;我们可以查看帮助文档来看它的用法

思考:有了过滤所带来的不同的操作
FileInputStream reaFile = new FileInputStream("Data.txt");//之前通过这个读取data中的数据
DataOutputStream in = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")));//将数据输出到Data文件中

如果我们想要缓存,那我们就将这个FileInputStream作为BufferedInputStream的加工对象(因为我们实际的读取要比处理器慢,那使用缓存就可以解决,协调交互效率),然后我们可以再用DataInputStream给加工一次

DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")));//这里提供了加工的操作,只是看起来繁琐

有了过滤流之后我们的Java I/O就变得繁琐了一些,因为可以层层包装,但我们需要明确最里层一定是一个介质流(什么类型的介质输入流都可以,基本三种和控制台的System.in)

Decorator设计模式(装饰器模式)【strategy、singleton】

decorator pattern 就是解决I/O流的;比如我们的OutPutStream就与FileOutPutStream,ByteArrayOutPutStream有联系,加工的就是FilterOutPutStream,FilterOutPutStream下面又有三个常用的子类,PrintStream,BufferedStream,DataStream,我们这个时候就新建一个子类来组合功能,比如我们的FilterOutStream下面就新建了几个子类

也就是说我们在编程的时候我们有的时候也像I/O一样关联了很多类,且我们想用几个子类的组合功能,就会产生状态爆炸,但是上面的组合太繁琐了,programmer会觉得很烦,所以的Java的I/O程序库的设计就用了decorator模式

应用场景
  • 当为满足各种必要的功能组合,造成单纯的继承动作产生大量的subclasses时,就需要使用decorator模式
Decorator模式介绍
  • 既然叫做装饰器模式就是我们在原有的组件上面增加一些装饰的,比如我们I/O设备的加工,在原有的基础上,没添加一个装饰,就可以增加一种功能
  • 动态地给一个对象添加一些额外的职责(在原有的基础上增加更多的功能),就扩展功能而言,他比子类更灵活
  • 像我们如果不了解decorator模式时我们就更加倾向于生成它的子类,但是我们扩展的功能过多,或者就时扩展的子类太多就会让程序看起来非常的混淆。
  • 这个的模式就像我们的I/O类图一样,我们的Filter—Stream就是我们的装饰器,我们原本的I/O就是原来的类,我们原来的介质流,要给他们加新的功能,这个时候我们就将我们的Decorator与原来的类建立一对一关联关系,并且建立继承关系,关联属性为原类,之后我们新添加功能就直接在装饰器下面添加子类,然后用我们具体的类装饰器去修饰原类,比子类更加灵活,所以我们考虑这些设计模式就要参照我们的标准程序库,去学习设计模式的实现。
16位字符流 16-bit-Unicode stream
  • 有两个基本的类Reader和Writer
  • 继承该基类的流以16位的Unicode编码表示的字符为基本处理单位
抽象类Reader

该类的基本操作也就是和字节输入流类似

  • 从输入流中(数据源中)读取数据

    • public int read() throws IOException 从数据源中读取一个字符返回(返回的0–255之间的一个字节所对应的编码)

    • public int read(char[] b)throws IOException 从数据源中读取字符放入字符数组byte中

    • public int read(char[] b,int off,int len)throws IOException 从数据源读取字符放入b中从那里开始到哪里结束的位置

    • public long skip(long n)throws IOException 数据源中跳过n个字符不读取,这里是long型的,因为字符数很多

    • 关闭输入流 public void close()throws IOException 如果不用的话要及时关闭,避免占用内存

这里我们发现没有available,上面是字节,这个是字符,其他的差距不大

Kinds of Reader
  • FileReader(对比FileInputStream)
  • StringReader (对比StringInputStream)
  • charArrayReader (对比 ByteInputStream)一个字节就是一个byte,一个字符就是一个char,一个char是两个字节,一个字节是8个bit

使用字符有什么好处呢,就是我们的汉字可以正常显示了,之前不可以,所有的语言都可以使用Unicode编码

FileReader readFile = new FileReader("out.txt");
		int charTemp = readFile.read();
		while(charTemp != -1)
		{
			System.out.print((char)charTemp);
			charTemp = readFile.read();
		}
		readFile.close();//一定要记得关闭文件

这样我们直接调上面的文件,输出的就是文件中的话,如果我们那里是中文也可以正常输出

happy every day
wish you happy

字符流的效率更高

抽象类Writer

输出数据到相应位置:(这是一个字符一个字符输出数据)

  • public void write() throws IOException 从数据源中输出一个字符
  • public void write(char[] c)throws IOException 从数据源中输出字符放入字符数组c中
  • public void write(char[] c,int off,int len)throws IOException 从数据源输出字符放入c中从那里开
  • public void write(String str) 从数据源读出数据给string
  • public void write(String str,int off,int len)

和刚刚的输入流恰好相反,它还有其他方法比如刷空所有输出流,输出缓存中所有的自己二到相应的输出流

  • public abstract void flush()throws IOException
  • public abstract void close() 关闭相应的输出流
Kinds of Writer
  • FileWriter(对比FileOutputStream)
  • StringWriter (no class)//因为这里是字符就可以将其放进字符串中,上面的字节流不可以
  • charWriter (对比 ByteOutputStream)
Filter — 字符过滤流

我们还是和之前的字节过滤流进行比较

  • BufferedInputStream ----- BufferedReader

    • public BufferedReader(Reader in)将Reader类型的数据作为基本的加工类型
    • public String readLine() throws IOException 无参(A String containing the contchs of the linc,not including any linc-termiantion charaters,or null if the end of the stream has been reached.)这里可以实现一行一行地读取,提高效率,文件末尾的时候就返回null

那这里我们就可以从键盘上输入的一行一行读入,这可以嘛,不可以,因为我们输入的是字节流,而reader是字符流,那我们这个时候就要使用一个桥接(birdge)类

Java提供的桥接类语序将“byte”转换成“character‘继承体系中的classes搭配运用

桥接类"InputStreamReader"jiang InputStream转换成Reader 如何实现?

public class InputStreamReader extends Reader//先继承

InputStreamReader(InputStream in),通过构造函数(构造方法)将InputStream转换成Reader类

同理Writer一样实现OutputStreamWriter

//从控制台读取一行字符串
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));//过滤流加工的都是介质流,这里的InputStream的子类介质流是System.in
String input = stdIn.readLine();//这里的桥接类我们可以直接使用
while(input != null)
{
    //process input; 将每一行的字符串进行操作
    input = stdIn.readLine();
}
//从文件中读取一行字符串
BufferedReader fileIn = new BufferedReader(new FileReader("out.txt"));//有现成的可以直接使用

>Whenever you want to use readLine(),you should use a BufferedReader.Other than this,DataInputStream is still a "preferred" member of the I/O library.
//我们一旦要使用按行读写,那就必须使用过滤字符流BufferedReader,如果不是我们之际额使用过滤字节流就好了
  • DataInputStream ------ Use DataInputStream(except when you need to use readLine(),you should use a BufferedReader非常的实用)

  • BufferedOutputStream----- BufferedWriter

  • DataOutputStream--------- 无对应类,因为字节使其余的最小,但是字符就是基本类型了

  • PrintStream ----- PrintWriter

    • public class PrintWriter extends Writer
    • 构造方法
      • PrintWriter(Writer out)//直接加工这个对象
      • PrintWriter(Writer out,boolean autoFlush)
      • PrintWriter(OutputStream out)//直接加工字节流也可以
      • PrintWriter(OutputStream out,boolean autoFlush)

    把数据写到控制台

System.out 是一个 PrintStream

  • PrintWriter has a constructor(构造方法) that takes an OutPutStream as an argument.
PrintWriter stdOut = new PrintWriter(System.out,true);//这就是我们常用的
stdOut.println();
//过滤字节流就是Scanner(System.in)

//写文件到指定的文件,字符流的方式
PrintWriter fileOut = new PrintWriter(new FileWitter("out.txt"));
fileOut.println("A line of output");//主要区别就是含参,无参就会覆盖源文件内容,没有文件就会创建文件,这我们是知道的,有参数为true我们就会追加而不是覆盖
//在源文件中追加
PrintWriter fileAppend = new PrintWriter(new FileWitter("out.txt",true));//过滤了一下,就嵌套了
fileAppend.println("A line of output");//写入字符流

主要区别就是含参,无二参就会覆盖源文件内容,没有文件就会创建文件,这我们是知道的,有参数为true我们就会追加而不是覆盖

我们最常用的过滤流就是BufferedReader和PrintWriter,我们常用的控制台读写和文件读写都要使用到

好啦,今天我们分享的Java的I/O就到这里了,只是一个基本的介绍,里面还有很多细节我们以后会详细分析的~~

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值