java学习笔记IO输入与输出——IO流概述及字节流(2)

来源:互联网 发布:淘宝pc端首页焦点图 编辑:程序博客网 时间:2024/06/07 09:58


一、IO(Input Output)流

IO流用来处理设备之间的数据传输。

JAVA对数据的操作就是通过流的方式。

JAVA用于操作流的对象都在IO包中。

流按操作数据分为两种:字节流与字符流。  按流向分为:输入流,输出流。

为什么单独分离字符流?

GB2312中文的映射表升级后扩容了出现了GBK。ASCII英文的映射表。

各国的文字编排,国际标准码表Unicode码表,优化后是UTF-8。

字符流可以指定按照哪一种表处理文字。


二、IO流常用基类

字节流抽象基类:InputStream、OutputStream。

字符流抽象基类:Reader、Writer。

注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

如:InputStream的子类FileInputStream。

   Reader的子类FileReader。

 

字符流和字节流

字符流两个基类:Reader、Writer。

字节流两个基类:InputStream、OutputStream。

既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件。那么先以操作文件为主来显示。需求:在硬盘上创建一个文件并写入一些数据。

找到一个专门用于操作文件的Writer子类对象FileWriter(后缀名是父类名,前缀名是该对象的功能)用于写入字符文件的便捷类。

importjava.io.*;

classFileWriterDemo

{

         public static void main(String[] args)throws IOException

         {

                   // 创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。

                   // 而且该文件会被创建到指定目录下。如果该目录下已有同名文件将被覆盖。

                   // 其实该步就是在明确数据要存放的目的地。

                   FileWriter fw = newFileWriter(“demo.txt”);  // 在运行后会在当前文件的目录下创建一个demo.txt

                   // 调用write方法将字符串写入到流中。

                   fw.write(“abcde”);  

                   // 刷新流对象中的缓冲中的数据

                   // 将数据刷到目的地中。

                   fw.flush();

                   fw.write(“haha”);

                   fw.flush();

                   //  关闭流,关闭之前会执行一次刷新flush()。将数据流刷到目的地中。

                   //  和flush区别在于,flush刷新后,流可以继续使用,close刷新后,会将流关闭。

                   fw.close();  

                   fw.write(“haha”);   // 关闭后在write会出错。

         }

}

 

三、IO异常处理方式

importjava.io.*;

classFileWriterDemo2

{

         public static void main(String[] args)

         {      

                   FileWriter fw = null;  // 使finally中的fw可以有效

                   try

                   {

                            //  此时如果路径不存在会出现找不到路径异常继而出现空指针异常

                            fw = new FileWriter(“demo.txt”);  

                            fw.write(“abcdefg”);

                            // fw.close();

                   }

                   catch(IOException e)

                   {

                            System.out.println(“catch:”+e.toString());

                   }

                   finally

                   {

                            try

                            {

                                      if(fw!=null)  //  避免空指针异常

                                              fw.close();   //  一定要执行close();

                            }

                            catch(IOException e)

                            {

                            System.out.println(e.toString());

                            }

                   }

         }

}

 

文件的续写(原有文件上面续写)

演示对已有文件的数据续写。

importjava.io.*;

classFileWriterDemo3

{

         public static void main(String[] args)throws IOException

         {

                   // 传递一个true参数,代表不覆盖已有文件,并在已有文件的末尾处进行数据续写。

                   FileWriter fw = newFileWriter(“demo.txt”,true);

                   fw.write(“ha\r\nah”);  // 换行追加

                   fw.close();

         }

}

 

四、FileReader类(读流)

FileReader类的Read方法返回整数读取的字符。范围在0到65535,如果已到流的末尾,则返回-1。

第一种方法,一次读一个字符

importjava.io.*;

class FileReaderDemo

{

         public static void main(String[] args)throws IOException

         {

                   // 创建一个文件读取流对象,和指定名称的文件相关联。

                   // 要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException

                   FileReader fr = newFileReader(“demo.txt”);

                   // 调用读取流对象

                   int ch = fr.read();

                   System.out.println(“ch=”+(char)ch);  //  返回97 强转成char输出。

                  

                   // read()方法一次读一个字符,而且会自动往下读。

                   int ch1 = fr.read();

                   System.out.println(“ch=”+(char)ch1);  //  返回97 强转成char输出。

                   // 根据-1返回值循环打印。

                   int ch = 0;

                   while((ch=fr.read())!=-1)//  下面循环的简化。

                   {

                            System.out.println((char)ch);

                   }

                   while(true)

                   {

                            int ch = fr.read();

                            if(ch==-1)

                                     break;

                            System.out.println(“ch=”+(char)ch);

                   }

                   fr.close();

         }

}

 

五、FileReader类(读流)

第二种方法,通过字符数组进行读取。  此种方式比较好。

importjava.io.*;

classFileReaderDemo2

{

         public static void main(String[] args)throws IOException

         {

                   FileReader fr = newFileReader(“demo.txt”);

                   // 定义一个字符数组,用于存储读到字符。

                   // 该read(char[])方法返回的是读到的字符个数。

                   char[] buf = new char[3];   // 数组大小一般定义1024的整数倍。1024即2k

                   int num = 0;

                   while((num=fr.read(buf))!=-1)

                   {

                            System.out.println(newString(buf,0,num));

                   }

                   fr.close();

                   //  下面演示部分,数组长度小分多次输出,逐个读字符。当返回-1时buf读完。

                   int num = fr.read(buf);

                   System.out.println(“num=”+num+”…”+newString(buf));   // 转成字符串输出

                  

         }

}

 

练习示例:读取一个.java文件,并打印在控制台上。

importjava.io.*;

classFileReaderTest

{

         public static void main(String[] args)

         {

                   FileReader fr = newFileReader(“DateDemo.java”);

                   char[] buf = new char[1024];

                   int num = 0;

                   while((num=fr.read(buf))!=-1)

                   {

                            System.out.print(newString(buf,0,num));  // println 如果超过1024会打断。

                   }

                   fr.close();

         }

}

 

练习示例:将c盘一个文本文件复制到d盘。

赋值的原理其实就是将c盘下的文件数据存储到d盘的一个文件中。

步骤:

1、在D盘创建一个文件,用于存储C盘文件中的数据。

2、定义读取流和C盘文件关联。

3、通过不断的读写完成数据存储。

4、关闭资源。

importjava.io.*;

classCopyTest

{

         public static void main(String[] args)

         {

                   copy_1();

                   copy_2();

         }

         public static void copy_1() throwsIOException    // 定义方法用于复制。读一个写一个比较慢

         {

                   // 创建目的地

                   FileWriter fw = newFileWriter(“RuntimeDemo_copy.txt”);

                   // 与已有文件关联

                   FileReader fr = newFileReader(“RuntimeDemo.java”);

                   int ch = 0;

                  while((ch=fr.read())!=-1)

                   {

                            fw.write(ch);

                   }

                   fw.close();

                   fr.close();

         }

         public static void copy_2()    // 定义方法用于复制。读全部后写全部

         {

                   FileWriter fw = null;

                   FileReader fr = null;

                   try

                   {

                            fw = new FileWriter(“RuntimeDemo_copy.txt”);

                            fr = new FileReader(“RuntimeDemo.java”);

                            char[] buf = newchar[1024];

                            int len = 0;

                            while((len=fr.read(buf))!=-1)

                            {

                                     fw.write(buf,0,len);

                           }

                   }

                   catch (IOException e)

                   {

                            throw newRuntimeException(“读取失败”);

                   }

                   finally

                   {

                            if(fr!=null)

                            try

                            {

                                     fr.close();

                            }

                            catch (IOExceptione)

                            {

                            }

                            if(fw!=null)

                            try

                            {

                                     fw.close();

                            }

                            catch (IOExceptione)

                            {

                            }

                   }

         }

}


六、字符流的缓冲区

缓冲区的出现提高了对数据的读写效率。

字符流缓冲区对应类:BufferedWriter、BufferedReader。

缓冲区要结合流才可以使用。

在流的基础上对流的功能进行了增强。

注意:缓冲区的出现是为了提高流的操作效率。所以在创建缓冲区之前,必须要现有流对象。

BufferedWriter:将文本写入字符输出流,缓冲各个字符从而提供单个字符、数组和字符串的高

      效写入。

注:缓冲区有一个newLine()方法。作用是写一个行分隔符。

importjava.io.*;

classBufferedWriterDemo

{

         public static void main(String[] args)throws IOException

         {

                   // 创建一个字符写入流对象。

                   FileWriter fw = newFileWriter(“buf.txt”);

                   // 为了提高字符写入流效率,加入了缓冲技术。 //  其原理就是:对象里面封装了数组

                   // 只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。

                   BufferedWriter bufw = newBufferedWriter(fw);

                   bufw.write(“abcde”);

                   bufw.newLine();   //  调用方法,打印一个换行符(即换行)。

                   for(int x=1; x<5; x++)

                   {

                            bufw.write(“”)

                   }

                   // 记住,只要用到缓冲区,就要记得刷新。

                   bufw.flush();

                   // 其实关闭缓冲区,就是在关闭缓冲区中的流对象,所以fw.close()可以不用写。

                   bufw.close();

                   fw.close();

         }

}

 

七、字符读取流缓冲区

BufferedReader是Reader的子类。从字符流中读取文本,从而实现字符、数组和行的高效读取。

BufferedReader缓冲区提供了一个一次读一行的方法readLine,方便于对文本数据的获取。当返回null时,表示读到文件末尾。注意,此方法时只返回回车符之前的数据,不返回回车符。

importjava.io.*;

classBufferedReaderDemo

{

         public static void main(String[] args)

         {

                   //  创建一个读取流对象和文件相关联。

                   FileReader fr = newFileReader (“buf.txt”);

                   //  为了提高效率,加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。

                   BufferedReader bufr = newBufferedReader (fr);

                   String s1 = bufr.readLine();

                   System.out.println(“s1=”+s1);

                   String s2 = bufr.readLine();

                   System.out.println(“s2=”+s2);  //  一行一行读。简化循环如下

                   String line = null;

                   while((line=bufr.readLine())!=null)   // readLine()方法读到末尾返回null不是-1

                   {

                            System.out.println(line);

                   }

                   bufr.close();   // 省略fr.close();

         }

}

 

练习:通过缓冲区复制一个.java文件

importjava.io.*;

classCopyTextByBuf{

         public static void main(String[] args){

                   BufferedReader bufr = null;  

                   BufferedWriter bufw = null;

                   try

                   {

                            bufr = newBufferedReader(new FileReader(“BufferedWriterDemo.java”));//参数匿名类对象做数

                            bufw = newBufferedWriter(new FileWriter(“bufWriter_Copy.txt”));

                            String line = null;

                            while((line=bufr.readLine())!=null)

                            {

                                     bufw.write(line);

                                     bufw.newLine();

                                     bufw.flush();

                            }

                   }

                   catch (IOException e)

                   {

                            throw newRuntimeException(“读写失败”);

                   }

                   finally

                   {

                            try

                            {

                                     if(bufr!=null)

                                               bufr.close();

                            }

                            catch (IOExceptione)

                            {

                                     throw newRuntimeException(“读取关闭失败”);

                            }

                            try

                            {

                                     if(bufw!=null)

                                               bufw.close();

                            }

                            catch (IOExceptione)

                            {

                                     throw newRuntimeException(“写入关闭失败”);

                            }

                   }

         }

}

readLine()方法的原理:无论是读一行或者读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法,即一次读一个的方法。


八、装饰设计模式

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。

装饰类通常会通过构造方法接受被装饰的对象。并基于被装饰对象的功能提供更强大的功能。

classPerson

{

         public void chifan()

         {

                   System.out.println(“吃饭”);

         }

}

classSuperPerson   //  此类用于对Person进行强化,是一个装饰。

{

         private Person p;

         SuperPerson(Person p)

         {

                   this.p = p;

         }

         public void superChifan()

         {

                   System.out.println(“开胃酒”);

                   p.chifan();         //System.out.println(“吃饭”);

                   System.out.println(“甜点”);

                   System.out.println(“抽烟”);

         }

}

classPersonDemo

{

         public static void main(String[] args)

         {

                   Person p = new Person();

                   // p.chifan();

                   SuperPerson sp = newSuperPerson(p);

                   sp.superChifan();

         }

}

 

装饰和继承的区别(装饰类的前世今生)。设计模式为优化而存在

设计模式为优化而存在

MyReader体系

MyReader// 专门用于读取数据的类。

   | -- MyTextReader

                   | -- MyBufferTextReader     //  功能扩展,通过继承方式,需要有很多个继承造成代码臃肿

   | -- MyMediaReader

                   | -- MyBufferMediaReader

   | -- MyDataReader

                   | -- MyBufferDataReader

classMyBufferReader        //   功能扩展,通过装饰类参数太多,扩展性差

{

         MyBufferReader(MyTextReader text)

         {}

         MyBufferReader(MyMediaReader)

         {}

}

注:上面这个类扩展性很差,找到其参数的共同类型。通过多态形式,可以提高扩展性。

classMyBufferReader extends MyReader   //  功能扩展,装饰类,参数向上抽取用其父类,扩展性好。

{

         private MyReader r;       // 继承结构变成了组合结构。

         MyBufferReader(MyReader mr)

         {}

}

MyReader体系更优方式(装饰设计模式优化后产生的体系)

MyReader

   | -- MyTextReader

   | -- MyMediaReader

   | -- MyDataReader

   | -- MyBufferReader

说明:装饰模式比继承要灵活,避免了继承体系臃肿。而且降低了类与类之间的关系。

           装饰类因为是增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和

           被装饰类通常都是属于一个体系中的。(装饰设计模式的特点)

 

LineNumberReader类继承了BufferedReader:跟踪行号的缓冲字符输入流。此类定义了setLineNumber(int)和getLineNumber()方法。

setLineNumber(int):   //  设置行号从指定数开始

getLineNumber():  // 获取行号,行号默认从0开始。

importjava.io.*;

classLineNumberReaderDemo

{

         public static void main(String[] args)

         {

                   FileReader fr = newFileReader(“PersonDemo.java”);

                   LineNumberReader lnr = newLineNumberReader(fr);

                   Stirng line = null;

                   lnr.setLineNumber(100);    //  设置行号从100开始。

                   while((line=lnr.readLine())!=null)

                   {

                            System.out.println(lnr.getLineNumber()+”:”line);

                   }

                   System.out.println(“HelloWorld!”);

         }

}



0 0