黑马程序员——Java IO (第十篇)

-----------android培训java培训、java学习型技术博客、期待与您交流!------------


流:可以理解数据的流动,就是一个数据流。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():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。


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

 

FileWriter写入数据的细节:

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

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

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


public static void main(String[] args) {  
              FileWriter fw = null;  
              try {  
                     fw= new FileWriter("demo.txt",true);  
                     fw.write("abcde");  
              }  
              catch (IOException e ){  
                     System.out.println(e.toString()+"....");  
              }  
              finally{  
                     if(fw!=null)  
                            try{  
                                   fw.close();  
                            }  
                            catch (IOException e){  
                                   System.out.println("close:"+e.toString());  
                            }  
              }  
}  



自定义缓冲区。

import java.io.*;  
class FileReaderDemo2 {  
       public staticvoid main(String[] args) throws IOException {  
              FileReader fr = newFileReader("demo.txt"); //创建读取流对象和指定文件关联。  
              //因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度都是1024的整数倍。  
              char[] buf = new char[1024];  
              int len = 0;  
              while(( len=fr.read(buf)) != -1) {  
                     System.out.println(newString(buf,0,len));  
              }  
              fr.close();  
       }  
}  



中的使用到了一个设计模式:装饰设计模式。

装饰设计模式解决:对一组类进行功能的增强。

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。

class Person  
{  
       public voidchifan()  
       {  
              System.out.println("吃饭");  
       }  
}  
class SuperPerson  
{  
       private Person p;          //定义成员变量的引用  
       SuperPerson(Personp)  //定义一个构造函数  
       {  
              this.p = p;  
       }  
       public voidsuperChifan()  
       {  
              System.out.println("开胃酒");  
              p.chifan();  
              System.out.println("甜点");  
              System.out.println("来一根");  
       }  
}  
class  PersonDemo  
{  
       public staticvoid main(String[] args)  
       {  
              Person p = new Person();  
   
              //p.chifan();  
   
              SuperPerson sp = new SuperPerson(p);  
              sp.superChifan();  
       }  
}  

缓冲区是提高效率用的,给谁提高呢?

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();  


Stringline = null;  
while((line=bufr.readLine())!=null){  
    if("over".equals(line))  
        break;  
    bufw.write(line.toUpperCase());//将输入的字符转成大写字符输出  
    bufw.newLine();  
    bufw.flush();  
    }  
bufw.close();  
bufr.close();  


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

流的操作规律:


转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。

转换流的最强功能就是基于 字节流 + 编码表 。没有转换,没有字符流。

发现转换流有一个子类就是操作文件的字符流对象:

       |--FileReader

       |--FileWrier

想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。

但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。

FileReader fr = new FileReader("a.txt");

InputStreamReader isr = new InputStreamReader(new  FileInputStream("a.txt"),"gbk");

以上两句代码功能一致,

如果仅仅使用平台默认码表,就使用FileReader fr =new FileReader("a.txt"); //因为简化。

凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。


Java.util.Properties:

Properties是hashtable的子类。

也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。是集合中和IO技术相结合的集合容器。

该特点:可以用于键值对形式的配置文件。那么在加载数据时,需要数据有固定格式:键=值。一个可以将键值进行持久化存储的对象。Map--Hashtable的子类。

       |--Hashtable

              |--Properties用于属性配置文件,键和值都是字符串类型。

特点:1:可以持久化存储数据。2:键值都是字符串。3:一般用于配置文件。

|-- load():将流中的数据加载进集合。

原理:其实就是将读取流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过 = 对该行数据进行切割,左边就是键,右边就是值,将键、值存储到properties集合中。

|-- store():写入各个项后,刷新输出流。

|-- list():将集合的键值数据列出到指定的目的地。


//name=JJ
//Weight=4444
//Height=3333
//<span style="font-family: 宋体; font-size: 15.454545021057129px; line-height: 24.545454025268555px;">随便新建一个配置文件(Test.properties)</span>
 public class getProperties {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        Properties pps = new Properties();
        pps.load(new FileInputStream("Test.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
       while(enum1.hasMoreElements()) {
            String strKey = (String) enum1.nextElement();
            String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
         }
     }
 }


以下介绍IO包中扩展功能的流对象:基本都是装饰设计模式。

PrintStream打印流

1:提供了更多的功能,比如打印方法。可以直接打印任意类型的数据。

2:它有一个自动刷新机制,创建该对象,指定参数,对于指定方法可以自动刷新。

3:它使用的本机默认的字符编码.

4:该流的print方法不抛出IOException。


PrintStream可以操作目的:1:File对象。2:字符串路径。3:字节输出流。

前两个都JDK1.5版本才出现。而且在操作文本文件时,可指定字符编码了。

当目的是一个字节输出流时,如果使用的println方法,可以在printStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新,而不是等待缓冲区满了再刷新。最终print方法都将具体的数据转成字符串,而且都对IO异常进行了内部处理。

既然操作的数据都转成了字符串,那么使用PrintWriter更好一些。因为PrintWrite是字符流的子类,可以直接操作字符数据,同时也可以指定具体的编码。


PrintWriter:具备了PrintStream的特点同时,还有自身特点:

该对象的目的地有四个:1:File对象。2:字符串路径。3:字节输出流。4:字符输出流。

开发时尽量使用PrintWriter。

方法中直接操作文件的第二参数是编码表。

直接操作输出流的,第二参数是自动刷新。

//读取键盘录入将数据转成大写显示在控制台.

BufferedReader  bf  = new BufferedReader(new InputStreamReader(System.in));//源:键盘输入

//目的:把数据写到文件中,还想自动刷新。

PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);//设置true后自动刷新  
Stringline = null;  
while((line=bufr.readLine())!=null){  
    if("over".equals(line))  
        break;  
    out.println(line.toUpperCase());//转大写输出  
}  

  //注意:System.in,System.out这两个标准的输入输出流,在jvm启动时已经存在了。随时可以使用。当jvm结束了,这两个流就结束了。但是,当使用了显示的close方法关闭时,这两个流在提前结束了。

out.close();

bufr.close();


SequenceInputStream:序列流,作用就是将多个读取流合并成一个读取流。实现数据合并。

表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。


合并原理:多个读取流对应一个输出流。

切割原理:一个读取流对应多个输出流。


对象的序列化:目的:将一个具体的对象进行持久化,写入到硬盘上。

注意:静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。

如何将非静态的数据不进行序列化?transient 关键字修饰此变量即可。

Serializable:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUIDid号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。


importjava.io.*;  
classObjectStreamDemo {  
    public static void main(String[] args)throws Exception{  
        writeObj();  
        readObj();  
    }  
    public static void readObj()throwsException{  
        ObjectInputStreamois = new ObjectInputStream(new FileInputStream("obj.txt"));  
        Objectobj = ois.readObject();//读取一个对象。  
        System.out.println(obj.toString());  
    }  
    public static void writeObj()throwsIOException{  
        ObjectOutputStreamoos = new ObjectOutputStream(new FileOutputStream("obj.txt"));  
        oos.writeObject(newPerson("lisi",25)); //写入一个对象。  
        oos.close();  
    }  
}  
classPerson implementsSerializable{  
    private static final long serialVersionUID = 42L;  
    private transient String name;//用transient修饰后name将不会进行序列化  
    public int age;  
    Person(String name,int age){  
        this.name= name;  
        this.age= age;  
    }  
    public String toString(){  
        returnname+"::"+age;  
    }  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值