一、IO流概述
流:可以理解数据的流动,就是一个数据流。当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中"流"动一样。
流的分类:
1,按数据方向分为:输入流(读)和输出流(写)。
2,按数据类型分为:因为处理的数据不同,分为字节流和字符流。
流的体系因为功能不同,但是有共性内容,不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类:
1,字节流的抽象基流:InputStream(输入)、OutputStream(输出)
2,字符流的抽象基流:Reader(输入)、Writer(输出)
在这四个系统中,它们的子类,都有一个共性特点:子类名后缀都是父类名,前缀名都是这个子类的功能名称。
二、字符流
字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。
Reader:用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和close()。
|---BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。StringreadLine(),当它返回的值是null时,就表示读取完毕。
|---LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
|---InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。这个类不是直接用于输入的
|---FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在FileInputStream 上构造一个 InputStreamReader。
|---CharArrayReader:
|---StringReader:
Writer:写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和close()。
|---BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。写入是要注意写换行符,newLine()写出一个换行符,否则会出现阻塞。
|---OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。这个类不是直接用于输入的。
|---FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在FileOutputStream 上构造一个 OutputStreamWriter。
|---PrintWriter:字符打印流类,
|---CharArrayWriter:
|---StringWriter:
三、字节流
InputStream:是表示字节输入流的所有类的超类。
|---FileInputStream:从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
|---FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
|--- BufferedInputStream:该类实现缓冲的输入流。
|--- DataInputStream:通过流来读取Java基本数据类型,
|---ObjectInputStream:对象输入流,用于读取对象
|---PipedInputStream:管道输入流
OutputStream:此抽象类是表示输出字节流的所有类的超类。
|---FileOutputStream:文件输出流是用于将数据写入 File 或 FileDescriptor的输出流。
|---FilterOutputStream:此类是过滤输出流的所有类的超类。
|--- BufferedOutputStream:该类实现缓冲的输出流。
|--- PrintStream:字节打印流类
|---DataOutputStream:通过流来写入Java基本数据类型,要注意写入顺序和读取顺序相同,否则会将没有分割写入的信息分割不正确,而造成读取出错误的数据。
|---ObjectOutputStream:对象输出流,用于写入对象。(实现对象序列化),对象流是过滤流,需要节点流作参数来构造对象,用于直接把对象写入文件和从文件中读取对象。需要注意的是,只有实现了Serializable接口的类型的对象次啊可以被读写,Serializable是个标记接口,其中没有定义方法。
对象会被序列化成一个二进制代码,文件中保存对象的属性。writeObject(o)、readObject(o)是对象读写操作时用的方法。
|---PipedOutputStream:管道输出流,线程交互的时候使用,PipedInputStream/PipedOutputStream,传送输出流可以连接到传送输入流,以创建通信管道。传送输出流是管道的发送端。通常,数据由某个线程写入PipedOutputStream对象,并由其他线程从连接的PipedInputStream读取。PipedInputStream和PipedOutputStream需要对接。
四、流的操作规律
1,明确源和目的。
数据源:就是需要读取,可以使用两个体系:InputStream、Reader;
数据目的:就是需要写入,可以使用两个体系:OutputStream、Writer;
2,操作的数据是否是纯文本数据?
是:字符流
不是:字节流
3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
4,需要在基本操作上附加其他功能吗?比如缓冲。
如果需要就进行装饰。
五、转换流
凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。
转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。
转换流的子类FileReader、FIleWriter有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。
如果需要制定码表,必须用转换流。
转换流 = 字节流+编码表。
转换流的子类File = 字节流 + 默认编码表。
[java] view plaincopy
public static void main(String[] args) throws IOException //读、写都会发生IO异常
{
FileWriter fw = new FileWriter("demo.txt"); // FileNotFoundException
fw.write("abcde");
fw.flush(); // 刷新缓冲区,将缓冲区中的数据刷到目的地文件中。
fw.close(); // 关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流。
}
FileReader:使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。
[java] view plaincopy
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch = fr.read())!= -1)
{ //条件是没有读到结尾
System.out.println((char)ch); //调用读取流的read方法,读取一个字符。
}
fr.close();
}
}
读取数据的第二种方式:第二种方式较为高效,自定义缓冲区。
[java] view plaincopy
import java.io.*;
class FileReaderDemo2
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt"); //创建读取流对象和指定文件关联。
//因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度都是1024的整数倍。
char[] buf = new char[1024];
int len = 0;
while(( len=fr.read(buf)) != -1)
{
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
通过缓冲区复制文本文件
[java] view plaincopy
//字符写入缓冲流
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符写入流对象。
FileWriter fw = new FileWriter("buf.txt");
//为了提高字符写入流效率。加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1; x<</SPAN>5; x++)
{
bufw.write("abcd"+x);
bufw.newLine();
//只要用到缓冲区,就要记得刷新。
bufw.flush();
}
//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();
}
}
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("buf.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null)
{
System.out.print(line);
}
bufr.close();//操作完毕后一定要关闭流
}
}
六、装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。
根据装饰模式模拟BufferedReader功能:
流:可以理解数据的流动,就是一个数据流。当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中"流"动一样。
流的分类:
1,按数据方向分为:输入流(读)和输出流(写)。
2,按数据类型分为:因为处理的数据不同,分为字节流和字符流。
流的体系因为功能不同,但是有共性内容,不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类:
1,字节流的抽象基流:InputStream(输入)、OutputStream(输出)
2,字符流的抽象基流:Reader(输入)、Writer(输出)
在这四个系统中,它们的子类,都有一个共性特点:子类名后缀都是父类名,前缀名都是这个子类的功能名称。
二、字符流
字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。
Reader:用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和close()。
|---BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。StringreadLine(),当它返回的值是null时,就表示读取完毕。
|---LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
|---InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。这个类不是直接用于输入的
|---FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在FileInputStream 上构造一个 InputStreamReader。
|---CharArrayReader:
|---StringReader:
Writer:写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和close()。
|---BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。写入是要注意写换行符,newLine()写出一个换行符,否则会出现阻塞。
|---OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。这个类不是直接用于输入的。
|---FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在FileOutputStream 上构造一个 OutputStreamWriter。
|---PrintWriter:字符打印流类,
|---CharArrayWriter:
|---StringWriter:
三、字节流
InputStream:是表示字节输入流的所有类的超类。
|---FileInputStream:从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
|---FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
|--- BufferedInputStream:该类实现缓冲的输入流。
|--- DataInputStream:通过流来读取Java基本数据类型,
|---ObjectInputStream:对象输入流,用于读取对象
|---PipedInputStream:管道输入流
OutputStream:此抽象类是表示输出字节流的所有类的超类。
|---FileOutputStream:文件输出流是用于将数据写入 File 或 FileDescriptor的输出流。
|---FilterOutputStream:此类是过滤输出流的所有类的超类。
|--- BufferedOutputStream:该类实现缓冲的输出流。
|--- PrintStream:字节打印流类
|---DataOutputStream:通过流来写入Java基本数据类型,要注意写入顺序和读取顺序相同,否则会将没有分割写入的信息分割不正确,而造成读取出错误的数据。
|---ObjectOutputStream:对象输出流,用于写入对象。(实现对象序列化),对象流是过滤流,需要节点流作参数来构造对象,用于直接把对象写入文件和从文件中读取对象。需要注意的是,只有实现了Serializable接口的类型的对象次啊可以被读写,Serializable是个标记接口,其中没有定义方法。
对象会被序列化成一个二进制代码,文件中保存对象的属性。writeObject(o)、readObject(o)是对象读写操作时用的方法。
|---PipedOutputStream:管道输出流,线程交互的时候使用,PipedInputStream/PipedOutputStream,传送输出流可以连接到传送输入流,以创建通信管道。传送输出流是管道的发送端。通常,数据由某个线程写入PipedOutputStream对象,并由其他线程从连接的PipedInputStream读取。PipedInputStream和PipedOutputStream需要对接。
四、流的操作规律
1,明确源和目的。
数据源:就是需要读取,可以使用两个体系:InputStream、Reader;
数据目的:就是需要写入,可以使用两个体系:OutputStream、Writer;
2,操作的数据是否是纯文本数据?
是:字符流
不是:字节流
3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
4,需要在基本操作上附加其他功能吗?比如缓冲。
如果需要就进行装饰。
五、转换流
凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。
转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。
转换流的子类FileReader、FIleWriter有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。
如果需要制定码表,必须用转换流。
转换流 = 字节流+编码表。
转换流的子类File = 字节流 + 默认编码表。
[java] view plaincopy
public static void main(String[] args) throws IOException //读、写都会发生IO异常
{
FileWriter fw = new FileWriter("demo.txt"); // FileNotFoundException
fw.write("abcde");
fw.flush(); // 刷新缓冲区,将缓冲区中的数据刷到目的地文件中。
fw.close(); // 关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流。
}
FileReader:使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。
[java] view plaincopy
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch = fr.read())!= -1)
{ //条件是没有读到结尾
System.out.println((char)ch); //调用读取流的read方法,读取一个字符。
}
fr.close();
}
}
读取数据的第二种方式:第二种方式较为高效,自定义缓冲区。
[java] view plaincopy
import java.io.*;
class FileReaderDemo2
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt"); //创建读取流对象和指定文件关联。
//因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度都是1024的整数倍。
char[] buf = new char[1024];
int len = 0;
while(( len=fr.read(buf)) != -1)
{
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
通过缓冲区复制文本文件
[java] view plaincopy
//字符写入缓冲流
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符写入流对象。
FileWriter fw = new FileWriter("buf.txt");
//为了提高字符写入流效率。加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1; x<</SPAN>5; x++)
{
bufw.write("abcd"+x);
bufw.newLine();
//只要用到缓冲区,就要记得刷新。
bufw.flush();
}
//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();
}
}
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("buf.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null)
{
System.out.print(line);
}
bufr.close();//操作完毕后一定要关闭流
}
}
六、装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。
根据装饰模式模拟BufferedReader功能: