您的位置:首页 > 职场人生

黑马程序员-- IO(一)-IO流以及流的操作规律

2015-03-01 00:50 447 查看
-----------android培训java培训、java学习型技术博客、期待与您交流!------------ 

IO流

IO流:★★★★★,用于处理设备上数据。

流:可以理解数据的流动,就是一个数据流。IO流最终要以对象来体现,对象都存在IO包中。

流也进行分类:

1:输入流(读)和输出流(写)。

2:因为处理的数据不同,分为字节流和字符流。 

 

字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

 

那么为什么要有字符流呢?因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。

 

注意:流的操作只有两种:读和写。

 

流的体系因为功能不同,但是有共性内容,不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类。

 

字节流:InputStream  OutputStream

字符流:Reader  Writer

 

在这四个系统中,它们的子类,都有一个共性特点:子类名后缀都是父类名,前缀名都是这个子类的功能名称。

--------------------------------------------------------------------------------------------------------------------

public static void main(String[] args) throws IOException { //读、写都会发生IO异常

/*

1:创建一个字符输出流对象,用于操作文件。该对象一建立,就必须明确数据存储位置,是一个文件。

2:对象产生后,会在堆内存中有一个实体,同时也调用了系统底层资源,在指定的位置创建了一个存储数据的文件。

3:如果指定位置,出现了同名文件,文件会被覆盖。

*/

FileWriter fw = new FileWriter("demo.txt"); // FileNotFoundException

/*

调用Writer类中的write方法写入字符串。字符串并未直接写入到目的地中,而是写入到了流中,(其实是写入到内存缓冲区中)。怎么把数据弄到文件中?

*/

fw.write("abcde");

fw.flush(); // 刷新缓冲区,将缓冲区中的数据刷到目的地文件中。

fw.close(); // 关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流。

}

 

close()和flush()的区别:

flush():将缓冲区的数据刷到目的地中后,流可以使用。

close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。

练习:

package pack;
// IO流常用基类,
// 字节流的抽象基类:
// InputStream,OuputStream
// 字符流的抽象基类:
// Reader,writer
// 先学习字符流的特点。
// 既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件。
// 那么先以操作文件为主来演示。
// 需求:在硬盘上创建一个文件并写入一些数据。
// 找到一个专门用于操作文件的Writer子类对象,
// FileWriter。后缀名是父类名,前缀名是该流对象的功能。
import java.io.IOException;
import java.io.FileWriter;
class FileWriterDemo{
public static void main(String[] args)throws IOException{
// 创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。
// 而且该文件会被创建到指定的目录下,如果该目录下已有同名文件,将被覆盖。
// 其实该步就是在明确数据要存放的目的地。
FileWriter fw=new FileWriter("");
// 调用write方法将字符串写入到流中,
fw.write("");
// 刷新流对象中的缓冲中的数据,将数据刷到目的地中。
fw.flush();
// 关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
// 将数据刷到目的地中,和flush区别:flush刷新后流可以继续使用,close刷新后,会将流关闭。
fw.close();
}
}

--------------------------------------------------------------------------------------------------------------------

io异常的处理方式:io一定要写finally;

示例:

package pack;
//IO异常的处理方式,
import java.io.IOException;
import java.io.FileWriter;
class FileWriterDemo2{
public static void main(String[] args){
FileWriter fw=null;
try{
fw=new FileWriter("");
fw.write("");
}
catch(IOException e){
e.printStackTrace();
}
finally{
try{
if(fw!=null)
fw.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}
}

 

FileWriter写入数据的细节:

1:window中的换行符:\r\n两个符号组成。 linux:\n。

2:续写数据,只要在构造函数中传入新的参数true。

3:目录分割符:window \\  /

示例:

package pack;
// 演示对已有文件的续写,
import java.io.IOException;
import java.io.FileWriter;
class FileWriterDemo2{
public static void main(String[] args){
FileWriter fw=null;
try{
fw=new FileWriter("",true);
fw.write("");
}
catch(IOException e){
e.printStackTrace();
}
finally{
try{
if(fw!=null)
fw.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}
}

--------------------------------------------------------------------------------------------------------------------
FileReader:使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。
package pack;
import java.io.FileReader;
import java.io.IOException;
class FileReaderDemo1{
<span style="white-space:pre">	</span>public static void main(String[] args){
<span style="white-space:pre">		</span>FileReader fr=null;
<span style="white-space:pre">		</span>try{
<span style="white-space:pre">			</span>// 创建一个文件读取流对象,和指定名称的文件相关联。
<span style="white-space:pre">			</span>// 要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
<span style="white-space:pre">			</span>fr=new FileReader("");
<span style="white-space:pre">			</span>int a=fr.read();
<span style="white-space:pre">			</span>System.out.println((char)a);
<span style="white-space:pre">			</span>int b=0;
<span style="white-space:pre">			</span>while((b=fr.read())!=-1){
<span style="white-space:pre">				</span>System.out.println((char)b);
<span style="white-space:pre">			</span>}
<span style="white-space:pre">			</span>while(true){
<span style="white-space:pre">				</span>int c=fr.read();
<span style="white-space:pre">				</span>if(c==-1)
<span style="white-space:pre">					</span>break;
<span style="white-space:pre">				</span>System.out.println((char)c);
<span style="white-space:pre">			</span>}
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>catch(IOException e){
<span style="white-space:pre">			</span>e.printStackTrace();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>finally{
<span style="white-space:pre">			</span>try{
<span style="white-space:pre">				</span>if(fr!=null)
<span style="white-space:pre">					</span>fr.close();
<span style="white-space:pre">			</span>}
<span style="white-space:pre">			</span>catch(IOException e){
<span style="white-space:pre">				</span>e.printStackTrace();
<span style="white-space:pre">			</span>}
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}
}

--------------------------------------------------------------------------------------------------------------------
读取数据的第二种方式:第二种方式较为高效,自定义缓冲区。
package pack;
// 第二种方式:通过字符数组进行读取。
import java.io.FileReader;
import java.io.IOException;<span style="white-space:pre">	</span>
class FileReaderDemo2{
<span style="white-space:pre">	</span>public static void main(String[] args){
<span style="white-space:pre">		</span>FileReader fr=null;
<span style="white-space:pre">		</span>try{
<span style="white-space:pre">			</span>fr=new FileReader("");
<span style="white-space:pre">			</span>char[] chs=new char[1024];
<span style="white-space:pre">			</span>int a=fr.read(chs);
<span style="white-space:pre">			</span>System.out.println(a+": "+new String(chs,0,a));
<span style="white-space:pre">			</span>int b=0;
<span style="white-space:pre">			</span>while((b=fr.read(chs))!=-1){
<span style="white-space:pre">				</span>System.out.println(new String(chs,0,b));
<span style="white-space:pre">			</span>}
<span style="white-space:pre">			</span>while(true){
<span style="white-space:pre">				</span>int c=fr.read(chs);
<span style="white-space:pre">				</span>if(c==-1)
<span style="white-space:pre">					</span>break;
<span style="white-space:pre">				</span>System.out.println(new String(chs,0,c));
<span style="white-space:pre">			</span>}
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>catch(IOException e){
<span style="white-space:pre">			</span>e.printStackTrace();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>finally{
<span style="white-space:pre">			</span>try{
<span style="white-space:pre">				</span>if(fr!=null)
<span style="white-space:pre">					</span>fr.close();
<span style="white-space:pre">			</span>}
<span style="white-space:pre">			</span>catch(IOException e){
<span style="white-space:pre">				</span>e.printStackTrace();
<span style="white-space:pre">			</span>}
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>}
}

--------------------------------------------------------------------------------------------------------------------
IO中的使用到了一个设计模式:装饰设计模式。
装饰设计模式解决:对一组类进行功能的增强。
包装:写一个类(包装类)对被包装对象进行包装;
 * 1、包装类和被包装对象要实现同样的接口;
 * 2、包装类要持有一个被包装对象;
 * 3、包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现;
package pack;
// 装饰设计模式:当想要对已有的对象进行功能增强时,
// 可以定义类,将已有对象传入,基于已有对象的功能,并提供加强功能。
// 那么自定义的该类就称为装饰类。
// 装饰类通常会通过构造方法接收被装饰的对象。
// 并基于被装饰的对象的功能,提供更强的功能。
class Person{
public void haveameal(){
System.out.println("吃饭");
}
}
class Student{
private Person p;
Student(Person p){
this.p=p;
}
public void haveameal(){
p.haveameal();
System.out.println("甜点");
System.out.println("香烟");
}
}
class PersonDemo{
public static void main(String[] args){
Person p=new Person();
p.haveameal();
System.out.println();
Student s=new Student(p);
s.haveameal();
}
}

// MyReader:专门用于读取数据的类。
// |--MyTextReader
// |--MyBuufferTextReader
// |--MyMediaReader
// |--MyBufferMediaReader
// |--MyDateReader
// |--MyBufferDateReader
class MyBufferReader extends MyReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMedianReader media)
{}
}
// 上面这个类扩展性很差。
// 找到其参数的共同类型,通过多态的形式,可以提高扩展性。
class MyBufferReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
// MyReader:专门用于读取数据的类。
// |--MyTextReader
// |--MyMediaReader
// |--MyDateReader
// 装饰模式比继承要灵活,避免了继承体系臃肿。
// 而且降低了类于类之间的关系。
// 装饰类因为曾强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
// 所以装饰类和被装饰类通常是都属于一个体系中的。



--------------------------------------------------------------------------------------------------------------------
字符流:
Reader:用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。
     |---BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
        |---LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
     |---InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
        |---FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
     |---CharArrayReader:
     |---StringReader:
-------------------------------------------------
Writer:写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。
     |---BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
     |---OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
        |---FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
     |---PrintWriter:
     |---CharArrayWriter:
     |---StringWriter:
---------------------------------
字节流:
InputStream:是表示字节输入流的所有类的超类。
     |--- FileInputStream:从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
     |--- FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
        |--- BufferedInputStream:该类实现缓冲的输入流。
        |--- Stream:
     |--- ObjectInputStream:
     |--- PipedInputStream:
-----------------------------------------------
OutputStream:此抽象类是表示输出字节流的所有类的超类。
     |--- FileOutputStream:文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。
     |--- FilterOutputStream:此类是过滤输出流的所有类的超类。
        |--- BufferedOutputStream:该类实现缓冲的输出流。
        |--- PrintStream:
        |--- DataOutputStream:
     |--- ObjectOutputStream:
     |--- PipedOutputStream:
--------------------------------
缓冲区是提高效率用的,给谁提高呢?
BufferedWriter:是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。
FileWriter fw = new FileWriter("bufdemo.txt");
BufferedWriter bufw = new BufferedWriter(fw);//让缓冲区和指定流相关联。
for(int x=0; x<4; x++){
bufw.write(x+"abc");
bufw.newLine(); //写入一个换行符,这个换行符可以依据平台的不同写入不同的换行符。
bufw.flush();//对缓冲区进行刷新,可以让数据到目的地中。
}
bufw.close();//关闭缓冲区,其实就是在关闭具体的流。

BufferedReader:
FileReader fr = new FileReader("bufdemo.txt");
BufferedReader bufr  = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){  //readLine方法返回的时候是不带换行符的。
System.out.println(line);
}
bufr.close();
//记住,只要一读取键盘录入,就用这句话。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//输出到控制台
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());//将输入的字符转成大写字符输出
bufw.newLine();
bufw.flush();
}
bufw.close();
bufr.close();

流对象:其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律。
设计模式解决:对一组类进行功能的增强。

流的操作规律:

1,明确源和目的。
数据源:就是需要读取,可以使用两个体系:InputStream、Reader;
数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer;
2,操作的数据是否是纯文本数据?
如果是:数据源:Reader
    数据汇:Writer 
如果不是:数据源:InputStream
      数据汇:OutputStream
3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
4,需要在基本操作上附加其他功能吗?比如缓冲。
如果需要就进行装饰。
 
转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。
 
转换流的最强功能就是基于 字节流 + 编码表 。没有转换,没有字符流。
 
发现转换流有一个子类就是操作文件的字符流对象:
InputStreamReader
|--FileReader
OutputStreamWriter
|--FileWrier
 
想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。
 
但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。
FileReader fr = new FileReader("a.txt");
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");
以上两句代码功能一致,
如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt"); //因为简化。
 
如果需要制定码表,必须用转换流。
转换流 = 字节流+编码表。
转换流的子类File = 字节流 + 默认编码表。
 
凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐