12.IO(一)【IO流概述】【字符流】【字符流缓冲区】【装饰设计模式】【字节流】
来源:互联网 发布:免费的美发软件 编辑:程序博客网 时间:2024/06/05 15:07
一、IO流概述
1. IO,是InputOutput的缩写。
2. 流,是传递数据信息的载体,是程序中的数据所经历的路径。
3. 流分为输入流和输出流。输入流是将数据从数据源传递给程序。输出流是将数据从程序传递到目标的地方,如硬盘,内存,网络等。
4. Java语言操作数据就是通过流的方式。
5. Java中的IO流库提供了大量的流库,均包含在IO包中,要使用这些流类必须先用import语句引入。
6. 根据输入输出的数据类型,流可以分为字节流(Byte)和字符流(Chracter),他们处理信息的基本单位分别是字节和字符。
二、字符流
1. 字符流主要用于操作文本数据,使用字符流读取文字字节数据时,不直接操作,而是先查编码表,获取对应的文字,再进行操作。
简单说,字符流==字节流+编码表。
2. 如果要操作文本数据,优先考虑字符流。
3. 个人理解:Java中的字符流融合了编码表,默认的编码表是当前系统的编码表。中文是Unicode编码表,是双字节的。InputStream处理数据时以字节为基本单位,在处理文本数据时,不是很方便,处理比较慢,所以Java为处理字符提供了一套专门的类,简化编程。
4. 字符流的两个顶层抽象父类:Reader和Writer。每次读取或写入16位字符。Reader是读取输入流,Writer是写出输出流。
5. Writer的常用子类为FileWriter,用于操作文件。Reader常用的操作文件的子类为FileReader。
注意:基类的子类都是以父类作为后缀名,前缀为要实现的功能。如FileWriter,前缀为File,说明该子类专门用于操作文件的写出。
6. 使用字符流读取数据的步骤:
l 创建FileReader对象,与数据相关联。在创建读取流对象时,必须要明确被读取的文件,而且该文件一定是存在的。否则会抛出FileNotFoundException异常。
l 调用流的read()方法读取数据。intread()方法,读取单个字符。在字符可用、发生 I/O错误或者已到达流的末尾前,此方法一直阻塞。如果已达到结尾,返回-1.
read(char[] buf):将字符读入数组。buf,目标缓冲区。
l 关闭流:close();关闭流后,将不能再读取数据。
<span style="font-size:18px">import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;//读取一个文本文件,将读取到的字符打印到控制台。public class FileReaderDemo {public static void main(String[] args) throws IOException {//1.创建读取字符数据的流对象/* * 在创建读取流对象时,必须要明确被读取的文件,一定要确定该文件是存在的。 * * 用一个读取流关联一个已存在文件。 */FileReader fr = new FileReader("demo.txt");int ch = 0;while((ch = fr.read())!=-1){System.out.print((char)ch);}fr.close();}}</span>
例2:读取一个文本文件,将读取到的字符打印到控制台。
<span style="font-size:18px">import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;public class FileReaderDemo2 {public static void main(String[] args) throws IOException {FileReader fr = new FileReader("demo.txt");//文件里的字符为abcdw/* * 使用read(char[]) 读取文本文件数据。 * * 先创建字符数组。 */char[] buf = new char[1024];int len = 0;//因为数量一直在变,所以可以定义变量。while((len=fr.read(buf))!=-1){System.out.println(new String(buf));}//method_1(fr, buf);fr.close();}/** * @param fr * @param buf * @throws IOException */public static void method_1(FileReader fr, char[] buf) throws IOException {int num = fr.read(buf);//将读取到的字符存储到数组中。System.out.println(num+":"+new String(buf));/* * 数组容量为3,所以num=3, */int num1 = fr.read(buf);System.out.println(num1+":"+new String(buf));/* * 文件里剩下两个字符:dw。再次读取时,读取到的dw重新存到数组,覆盖了ab,num1 = 2,同时c没被覆盖。 * 所以这次输出dwc */int num2 = fr.read(buf);System.out.println(num2+":"+new String(buf));/* * 文件里没有字符,返回-1.所以num = -1, * 数组里的字符没有被覆盖,所以输出dwc,和第二次一样。 */}}</span>
7. 使用字符流写出数据的步骤:
l 创建FileWriter对象,与数据相关联。创建该对象时,必须要明确存储数据的目的地。如果文件不存在,则会自动创建。如果文件存在,则会覆盖原来的文件。
l 调用write(String str):写入字符串到目的地,如果要续写,可以写为write(String str ,true),这样就不会覆盖原文件,而且会在文件末尾处添加数据。Write(char[]buf,int off, int len),写入数组的某一部分。
l 调用flush()方法,将缓冲区的数据写入到目的地中。刷新之后还可以继续写数据到目的地。
l close()方法,用于关闭流。关闭资源之前,会刷新缓冲区。关闭之后,不能再写入数据到目的地中,否则会抛出异常。
例子:将一些文字存储到硬盘中的文件。
<span style="font-size:18px">import java.io.FileWriter;import java.io.IOException;public class FileWriterDemo {private static final String LINE_SEPARATOR = System.getProperty("line.separator");public static void main(String[] args) throws IOException {//创建一个可以往文件中写入字符数据的输出流对象。FileWriter fw = new FileWriter("demo.txt");//加入true,续写。fw.write("advasdf"+LINE_SEPARATOR+"lala");//换行fw.write(LINE_SEPARATOR+"haha");/* * 进行刷新,将数据直接写到目的地中。刷新后还可以继续操作流。 *///fw.flush();/* * 关闭流,关闭资源,关闭之前先刷新,其实就是close方法调用了flush方法。关闭之后就不能再操作当前流、 */fw.close();//fw.write("sfeg");//java.io.IOException: Stream closed}}</span>
练习:从E:\eclipse\day20e中复制文件demo.txt的内容到E:\目录的democopy。txt文件中。
思路:
既然是复制文件内容,那么先读取文件。优先考虑字符输入流。FileReader
读取文件后再写入到新文件中。FileWriter
步骤:
1.创建要进行复制的文件对象和保存复制后的文件对象。
new FileReader()
new FileWriter()
2.读取文件内容,一个一个复制过去,或者建立字符数组,一次性读取。
3.频繁读写操作。
4.关闭流。
<span style="font-size:18px">import java.io.FileNotFoundException;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class FileCopyTest_2 {public static void main(String[] args) {FileReader copyBy = null;FileWriter copy = null;try {String readerPath = "IO流_2.txt";//要复制的文件路径。String writerPath = "testcopy_2.txt";//复制到目标文件。copyBy = new FileReader(readerPath);copy = new FileWriter(writerPath);char[] file = new char[1024];//定义字符数组。int len = 0;while((len=copyBy.read(file))!=-1){//读取字符到数组copy.write(file,0,len);//从字符中写入目标文件。}} catch (Exception e) {throw new RuntimeException("读写失败!");}finally{if(copyBy!=null)//被创建成功,需要关闭try {copyBy.close();} catch (IOException e) {e.printStackTrace();}if(copy!=null)try {copy.close();} catch (IOException e) {e.printStackTrace();}}}}</span>
三、字符流缓冲区
概述
字符流缓冲区分为输入流缓冲区和输出流缓冲区,即
BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
可以指定缓冲区大小,或者可以使用默认大小。一般情况下,默认大小就足够了。
提供了readLine()方法,从而实现行的高效读取。
BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
该类提供了newLine()方法,他使用了平台自己的分隔符,从而实现换行。
1. 缓冲区的原理:
假设内存中有一数组buf,缓冲区调用继承来的bufr.read(buf),从硬盘读取一些数据到内存。
从内存中读取数据时,调用:这个read是从缓冲区中的取出的字符数据。所以覆盖了父类中的read方法。这个read本身就是一个高效的read().直接从缓冲区读取,不用到硬盘中读取。
用bufr.read()读取字符后,就可以操作字符了,根据文本的行特点,可以按照行读取,所以进行了下一步的动作,就有了新的方法readLine().
2. readLine()方法的原理:
在缓冲区中,bufr.read()从存储字符的容器读取字符后,不直接操作,而是在缓冲区开辟另一个临时容器,将字符装入,然后继续读取,直到读取换行符\r,终止符\n,并且不包含换行符,这时候才从临时容器中一次性读取存储的有效字符。这就是readLine()原理。临时容器可以是StringBuilder,因为最终返回的是字符串。只是在read()基础上加了一个判断换行标记。
简单说,readLine()方法:使用了缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记,将标记前的缓存数据编程字符串输出。
BufferedWriter示例:
<span style="font-size:18px">import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;public class BufferedWriterDemo {public static final String LINE_SEPARATOR = System.getProperty("line.separator");public static void main(String[] args) throws IOException {FileWriter fw = new FileWriter("buf.txt");//为了提高写入效率,使用字符流的缓冲区。//创建一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联。BufferedWriter bufw = new BufferedWriter(fw);//使用缓冲区的写入方法将数据写入缓冲区中。//bufw.write("你好"+LINE_SEPARATOR+"你好two");//bufw.write("你好!");//bufw.newLine();//bufw.write("我不好!");for (int i = 0; i < 4; i++) {bufw.write("早安世界"+i);bufw.newLine();bufw.flush();}//使用缓冲区的刷新方法将数据刷入目的地中。bufw.flush();//关闭缓冲区,其实就是关闭被缓冲的流对象。bufw.close();}}</span>
BufferedReader示例:
<span style="font-size:18px">import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;public class BufferedReaderDemo {public static void main(String[] args) throws IOException {//demo();FileReader fr = new FileReader("buf.txt");BufferedReader bufr = new BufferedReader(fr);/* * 行的读取。 * readLine():一行一行的读取。 */String line = null;while((line=bufr.readLine())!=null){System.out.println(line);}/*String line1 = bufr.readLine();System.out.println(line1);String line2 = bufr.readLine();System.out.println(line2);String line3 = bufr.readLine();System.out.println(line3);String line4 = bufr.readLine();System.out.println(line4);String line5 = bufr.readLine();System.out.println(line5);*/bufr.close();}/**缓冲字符数组 * @throws FileNotFoundException * @throws IOException */public static void demo() throws FileNotFoundException, IOException {FileReader fr = new FileReader("buf.txt");char[] buf = new char[1024];int len = 0;while((len=fr.read(buf))!=-1){System.out.println(new String(buf,0,len));}fr.close();}}</span>
自定义读取缓冲区:
自定义的读取缓冲区,其实就是模拟一个BufferedReader。
分析:
缓冲区中无非就是封装了一个数组,
并对外提供了更多的方法对数组进行访问。
其实这些方法最终操作的都是数组的角标。
缓冲的原理:
从源中获取一批数据装进缓冲区中。
再从缓冲区不断的取出一个一个数据。
在此次取完后,再从源中继续取一批数据进缓冲区。
当源中的数据取完时,用-1作为结束标记。
<span style="font-size:18px">import java.io.FileReader;import java.io.IOException;public class MyBufferReader {private FileReader r;//定义一个数组作为缓冲区private char[] buf = new char[1024];//定义一个指针,用于操作数组元素,操作到最后一个元素时,指针归0.private int pos = 0;//定义一个计数器,用于记录缓冲区的数据个数,当该数据减到零时,就从源中继续获取数据到缓冲区中。private int count = 0;MyBufferReader(FileReader r){this.r = r;}public int myRead() throws IOException{if(count==0){count = r.read(buf);pos = 0;}if(count<0)return -1;char ch = buf[pos];pos++;count--;return ch;/*//1.从源中获取数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。if(count==0){count = r.read(buf);//每次获取数据到缓冲区后,角标归零。pos = 0;char ch = buf[pos];pos++;count--;return ch;}if(count<0)return -1;else{char ch = buf[pos];pos++;count--;return ch;}*/}public String myReadLine() throws IOException{StringBuilder sb = new StringBuilder();int ch = 0;while((ch=myRead())!=-1){if(ch=='\r')continue;if(ch=='\n')return sb.toString();sb.append((char)ch);}//健壮性判断/* * 因为假如“早安世界3”这一行字符最后没有换行字符“\n”,虽然数据存储到缓冲区中, * 但是没有写入文件。所以要判断。 */if(sb.length()!=0)return sb.toString();return null;}public void close() throws IOException {r.close();}</span>
四、装饰设计模式(wrapper)
1. 概述
当想对一组对象功能进行增强时,可以自定义一个增强功能的类,将已有对象传递给增强功能类的构造函数。
基于已有对象的功能,添加想增强的功能。那么自定义的类称为装饰类。这种设计模式称为装饰设计模式。
2. 装饰的特点:
装饰类和被装饰类都必须所属同一个接口或者父类。
3. 继承和装饰都能实现功能的扩展,两者有什么区别呢?
首先有一个继承体系:
writer
|--TextWriter:用于操作文本。
|--MediaWriter:用于操作媒体。
想要对操作的动作进行效率的提高。按照面向对象,可以通过继承对具体的进行功能的扩展。
效率提高需要加入缓冲技术。
可以通过继承实现功能的扩展。
Writer
|--TextWriter:用于操作文本。
|--BufferedTextWriter:加入了缓冲技术的操作文本的对象。
|--MediaWriter:用于操作媒体。
|--BufferedMediaWriter
但是通过继承不理想:如果这个体系进行功能的扩展,又增加流对象。
那么这个流要提高效率,也要产生子类。就会发现只为提高功能而进行继承,导致继承体系越来越臃肿。不够灵活。
既然加入的都是同一种技术,继承是让缓冲和具体的对象相结合,可不可以将缓冲进行单独封装,哪个对象需要缓冲就将哪个对象和缓冲相关联。
class Buffer{
Buffer(TextWriter w)
{
}
Buffer(MediaWriter w)
{
}
}
class BufferWriter extends Writer{
BufferWriter(Writer w){
}
}
这样,原来的体系就变成了:
writer
|--TextWriter:用于操作文本。
|--MediaWriter:用于操作媒体。
|--BufferWriter:用于提高效率。
总结:
- 装饰比继承更灵活。避免了体系因继承而臃肿,并且降低了类和类之间的耦合。
- 从继承结构转为装饰的组合结构。
- 装饰类要增强已有对象的功能,具备和原来对象的功能,而且又有自己增强的功能。
注意:要增强功能时,多用装饰类,少用继承。装饰类更灵活。
示例:人有吃饭的功能,现在想人多了两项功能:吃饭前喝开胃酒,饭后吃甜点。
<span style="font-size:18px">public class PersonDemo {public static void main(String[] args) {Person p = new Person();p.chifan();//装饰NewPerson p1 = new NewPerson(p);p1.chifan();//继承NewPerson_2 p2 = new NewPerson_2();p2.chifan();}}class Person{void chifan(){System.out.println("吃饭");}}//这个类的出现是为了增强Person而出现的。装饰class NewPerson{private Person p;NewPerson(Person p){this.p = p;}public void chifan(){System.out.println("开胃酒");p.chifan();System.out.println("甜点");}}//通过继承实现功能增强class NewPerson_2 extends Person{public void chifan(){System.out.println("开胃酒");super.chifan();System.out.println("甜点");}}</span>
五、字节流
1. 概述
用于操作字节数据,每次读取或者写入8位。字节流能操作文本数据,也可以操作其他媒体文件。
2. 字节流的两个基类:
InputStream输入流,用于读取数据。
OutputStream输出流,用于写入数据。
3. 由于媒体文件数据都是以字节存储,所以字节流对象可以直接操写入数据到文件,不用再进行刷新动作。
为什么不用进行刷新流的动作呢?
因为字节流操作的是字节,是数据的最小单位,不用像字符流一样需要转换为字节,可直接将字节数据写入到文件。
4. InputStream特有方法:
int available();返回文件的字节大小。
注意:可以调用此方法来获取字节大小,从而创建一个和文件大小一致的数组,从而省去循环判断。
但是如果文件过大,超过了虚拟机分配的默认内存64M,此数组长度所占内存空间就会溢出。
所以,此方法适用于文件较小的时候。
例子:复制图片
<span style="font-size:18px">import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;//复制图片public class copyPic {public static void main(String[] args) {copyPicMethod1();copyPicMethod2();}//avaliable方法public static void copyPicMethod2() {FileInputStream fis = null;FileOutputStream fos = null;try {fis = new FileInputStream("F:\\我的女孩.jpg");fos = new FileOutputStream("F:\\我的女孩1.jpg");//调用avaliable方法,获取字节数大小,创建字节数组byte[] buf = new byte[fis.available()];fis.read(buf);//复制数据fos.write(buf);//黏贴文件到指定路径。} catch (IOException e) {throw new RuntimeException("复制图片失败!");}finally{if(fis!=null){try {fis.close();} catch (IOException e) {throw new RuntimeException("读取图片失败!");}}if (fos!=null) {try {fos.close();} catch (IOException e) {throw new RuntimeException("写入图片失败!");}}}}// 创建字符数组作为缓冲区public static void copyPicMethod1() {FileInputStream fis = null;FileOutputStream fos = null;try {fis = new FileInputStream("F:\\我的女孩.jpg");fos = new FileOutputStream("F:\\我的女孩1.jpg");byte[] buf = new byte[1024];int len = 0;while ((len = fis.read(buf)) != -1) {//复制数据fos.write(buf, 0, len);//粘贴到指定路径}} catch (IOException e) {throw new RuntimeException("复制图片失败!");}finally{if(fis!=null){try {fis.close();} catch (IOException e) {throw new RuntimeException("读取图片失败!");}}if (fos!=null) {try {fos.close();} catch (IOException e) {throw new RuntimeException("写入图片失败!");}}}}}</span>
- 12.IO(一)【IO流概述】【字符流】【字符流缓冲区】【装饰设计模式】【字节流】
- IO流2(字符流缓冲区、字节流、装饰设计模式、转换流)
- Java基础--IO流02(字符缓冲区、字节流、装饰设计模式)
- 黑马程序员_温习 IO流一 (个人笔记) 简要(IO介绍。。字符流。。字符流缓冲区..装饰设计模式)
- 21-IO流-18-IO流(字符流-缓冲区-装饰设计模式)
- 21-IO流-19-IO流(字符流-缓冲区-装饰设计模式和继承的区别)
- 5. 1 IO流(字符流(6个),字节流(4个),(缓冲区装饰类(装饰设计模式)),(转换流))
- IO(IO概述、字符流以及缓冲区)
- IO流:概述、字符流、缓冲区
- IO流-2.【字符流缓冲区BufferedWriter/BufferedReader】【readLine()方法】【装饰设计模式】【LineNumberReader】
- java--IO流缓冲区,装饰设计模式
- IO流,缓冲区,装饰设计模式
- IO(一)字符流,字节流
- IO流(概述字节流字符流)
- 黑马程序员_java的IO流(对第十九课字符缓冲区及装饰设计模式的总结)
- IO流-字符流,设计模式--装饰模式
- 黑马程序员--IO流概述、字符流、字符流缓冲区
- 黑马程序员——Java基础——IO流、字符流、字节流、缓冲区技术、装饰设计模式、LineNumberReader等
- ant 编译android工程
- 关于Activity布局的优化
- linux之sysfs文件系统
- 单调队列
- C++二维数组new几种应用方法点评
- 12.IO(一)【IO流概述】【字符流】【字符流缓冲区】【装饰设计模式】【字节流】
- 手把手教你编写一个具有基本功能的shell(已开源)
- sdk更新被和谐的处理
- Which of the following statements describe the result when standard new CANNOT allocate the requeste
- Documentation/fb/pvr2fb
- php中使用函数
- android 在myeclipse中查看源码
- A child container failed during start
- 指针常量和常量指针