黑马程序员-IO流

 ---------------------- android培训,java培训、期待与您交流! ----------------------

IO:

IO流是java中的一个重要的部分,主要用于操作数据的.

IO流按照流向来分可以分为输入流和输出流

按照流的操作数据来分可以分为字节流.和字符流

 

流的常用基类:

字节流的抽象基类:

       InportStream   OutputStream

字符流的抽象基类:

       Reader   Worker

 

字符流:

字符流是基于字节流产生的,字符流主要用于操作文本数据,文件的操作无非是写入和读取,字符流有两个基类分别用于文件的写入和读取:

Writer   Reader

 

先看看Writer

Writer类是抽象类,其中对文件的操作的子类是OutputStreamWriter,它有一个直接子类是FileWriter,它没有空参数的构造方法,因为要操作文件首先要初始化一个文件才行,所以要使用带参数的构造函数来初始化.

 

Writer中的主要方法:

append(..)

 将数据添加到此 writer

close()

关闭此流,但要先刷新它。

flush()

刷新该流的缓冲。

write(..)

写入数据。

 

 

FileWriter没有自己独立的方法,全部都是继承OutputStreamWriter.

 

要值得注意的是: FileWriter会创建一个指定名字的文件,这是绝对的,如果目的地已经有了同名的文件,那么FileWriter会创建一个新的文件,把那个同名文件覆盖掉.如果一定要在文件的末尾处继续写数据要怎么办呢? FileWriter有一个构造函数:

FileWriter(String filename, boolean append),第二个参数就是是否允许继续添加文件,如果为true,那么填入的数据就会被添加到文件的末尾处了.

 

小问题一个: WindowsLinux中的换行符不一样,Windows中用\r\n表示换行,而在Linux中用\n表示换行

 

 

Reader

Reader也是一个抽象的类,它的子类中有一个文件读取的子类InputStreamReader

FileReader没有空参数的构造函数,因为要读取文件首先肯定要有一个文件才行,所以要初始化一个文件才行

FileReader中的read:

 

read方法有两个读取方式

第一种:read() 读取单个字符。

它们都返回字符的二进制码,所以需要用char强转,read是一次读取一个字符,每次都接着上一个继续读取,读到末尾没有字符的时候,返回-1

fr=new FileReader("读取.txt");

           intch=0;

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

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

           }

 

 

第二种, read(ch [ ])把数据读入数组

过程:

       首先读取一个文件中的数据,把数据读入一个数组,此时会返回一个数组中元素的个数,当读到数据的末尾的时候,会返回-1,然后通过把数组中的元素变成字符串的形式打印出来

 

fr=new FileReader("读取.txt");

           char[] ch=newchar[1024];

           int buf=0;              //再循环外面定义一个变量,用于记录

           while((buf=fr.read(ch))!=-1){  //当数组里还有元素就进入循环

              System.out.print(new String(ch,0,buf));  //把数组从0buf位置的元素变成字符串

           }

 

缓冲区:

IO流中为了提高流的操作效率,避免过多次读写操作,提供了缓冲区的对象,缓冲区可以存放流对象的资源,当读写的数据达到一定数量的时候,再把数据读写到目的地,实质上,缓冲区内部调用了数组,所以在创建缓冲区之前必须要有流对象.

输入流和输出流各有自己的缓冲区,分别是BufferedWriterBufferedReader

缓冲区没有空参数的构造方法,因为要缓冲,首先要有被缓冲的流

BufferedWriter bufw=new BufferedWriter ( fw);

缓冲区提供了一个跨平台的换行符:newline( )

关闭缓冲区实际上就是在关闭流对象,所以流对象不用单独关闭

 

字符读取流缓冲区:BufferedReader

和输入流缓冲区一样读取流缓冲区也是为了提高读取效率的

BufferedReader提供了一个一次读取一行的方法readLine,方便与读取,当返回null,表示读到文件的末尾

 

 

 

 

字节流:

字符流只能操作文本类型的文件,这样具有很高的局限性, 为了操作其他类型的文件,我们可以使用字节流,因为字节流是操作字节的.

字节流有两个基类分别用于文件的写入和读取:

OutputStream(字节写入流)     InputStream(字节读取流)

和字符流一样,上面两个都是字节流的两个基类,它们都是抽象的.

 

 

先看看OutputStream(字节写入流)

close()

关闭此流,但要先刷新它。

flush()

刷新该流的缓冲。

write(..)

写入数据。

字节流与字符流有一个不同的就是,字节流的写入不需要使用flush()方法刷新

 

 

InputStream(字节读取流):

 int

available() (获取到文件中的字节个数)
          返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。

 void

close()
          关闭此输入流并释放与该流关联的所有系统资源。

 void

mark(int readlimit)
          在此输入流中标记当前的位置。

abstract  int

read()
          从输入流中读取数据的下一个字节。

 int

read(byte[] b)
          从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。

 int

read(byte[] b, int off, int len)
          将输入流中最多 len 个数据字节读入 byte 数组。

 void

reset()
          将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。

 long

skip(long n)
          跳过和丢弃此输入流中数据的 n 个字节。

字节流和字符流一样,都有缓冲技术,同样都是为了提高效率

 

键盘录入:

IO流不仅能操作已有的文件,而且能进行动态的键盘录入,键盘录入使用的是System类中的in.out.但是键盘录入是字节流中的方法,每一次都要一个一个字节的读取和写入,这样非常麻烦,如果可以一次读写一行,这样就非常方便了,但是一次读一行是BufferedReader中的方法,我们知道字节流是不能使用字符流中的方法的, 这怎么办呢?这时,就需要用到字符流中的另一个流对象:转换流:

转换流是字符流和字节流之间的桥梁,通过转换流可以实现字符流和字节流的自由转换.转换流有两个:

第一中转换流: InputStreamReader ,字节流转换成字符流,它初始化需要接收一个字节流对象.

第二种转换流: OutputStreamWriter ,字符流转换成字节流, 它初始化需要接收一个字符流对象.

 

键盘录入最常见的写法:

BufferedWriter bfw=new BufferedWriter(new OutputStreamWriter(System.out));

读取键盘的最常见写法:

BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));

 

File:

IO流可以操作数据,但是遗憾的是,IO流能操作文件夹或文件属性,为了弥补这个不足,JAVA提供了另一个类来专门操作文件的各种信息,这个类就是 File .

 

File(File parent,String child)  参数一:文件地址 参数二:文件名
          根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。

File(String pathname)  参数:文件名
          通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。

File(String parent,String child)  参数一:文件地址 参数二:文件名
          根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

File(URI uri)
          通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。

File的字段摘要:

static String

pathSeparator
          与系统有关的路径分隔符,为了方便,它被表示为一个字符串。

static char

pathSeparatorChar
          与系统有关的路径分隔符。

static String

separator
          与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。

static char

separatorChar
          与系统有关的默认名称分隔符。

这些分隔符是跨平台的

 

File类常见方法:

 

1.       创建:如果地址中已存在这个文件,那么就不再创建

boolean createNeaFile(); 创建文件

boolean mkdir();     创建文件夹

boolean mkdirs();   创建多级文件夹

 

2.       删除:

Boolean  delete();  删除文件

Boolean    deleteOnExit() ;     在程序结束的时候删除文件

 

3.       判断:

Boolean exists();    文件是否存在

           isFile();    是否是文件

           isDirectory是否是目录

          isHidden();是否隐藏     

          在判断是什么类型的文件之前,一定要先判断文件是否存在

 

4.       获取:

getAbsolutePath()    获取绝对路径

getPath()          获取路径

getParent()        获取绝对路径中的父目录,如果是相对路径,就返null

 

File中的List,用于得到指定目录下的文件名称

   File[ ]  listRoots( );返回一个File数组,得到所有可用的盘符

   String [ ]  list( );    返回一个字符串数组,得到所有此路径下的文件,包含隐藏文件 .调用list的对象必须是一个存在的目录

File[]listFiles():返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。

File[]listFiles(FilenameFilter filter):通过过滤器放回指定目录下的指定文件

 

List和listFiles的区别就是:list返回的只是文件名称,而listFiles返回的是文件对象名称,通过这些对象名称可以得到路径,大小等信息

 

 

Properties:

   Propertieshashtable的子类,具备map集合的特点,并且它里面存储的键值对都是字符串.

它是集合中和IO技术相结合的集合容器,它的特点是:可以用于键值对形式的配置文件.

Properties中的常用方法:

获取:

 String

getProperty(String key)
          用指定的键在此属性列表中搜索属性。

 String

getProperty(String key,String defaultValue)
          用指定的键在属性列表中搜索属性。

创建

 Object

setProperty(String key,String value)
          调用 Hashtable 的方法 put。

 

遍历(迭代)

 Set<String>

stringPropertyNames()
          返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。

 

 

加载配置文件信息

 void

load(InputStream inStream)
          从输入流中读取属性列表(键和元素对)。

 void

load(Reader reader)
          按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

 

把改变后的配置文件信息重新写入配置文件

 void

store(OutputStream out,String comments)
          以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。

 void

store(Writer writer,String comments)
          以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。

 

 

打印流:

该流提供了打印方法,可以将各种数据类型的数据都原样打印。

 

字节打印流:

PrintStream

构造函数可以接收的参数类型:

1file对象。File

2,字符串路径。String

3,字节输出流。OutputStream

 

字符打印流:

PrintWriter

构造函数可以接收的参数类型:

1file对象。File

2,字符串路径。String

3,字节输出流。OutputStream

4,字符输出流,Writer

 

 

合并流:SequenceInputStream

一个流对象只能操作一个文件的数据,但是如果要把多个文件的数据合并到一个文件中,IO流就显得有些吃力了,还好IO包中提供了一个专门用于多个流合并成一个流的流对象:SequenceInputStream

SequenceInputStream是一个合并流,可以把多个流合并成一个流

 

构造函数:

 

SequenceInputStream(InputStream s1,InputStream s2)通过记住这两个参数来初始化新创建的SequenceInputStream(将按顺序读取这两个参数,先读取s1,然后读取s2),以提供从此SequenceInputStream读取的字节。

 

SequenceInputStream(Enumeration<? extendsInputStream> e)通过记住参数来初始化新创建的SequenceInputStream,该参数必须是生成运行时类型为InputStream对象的Enumeration型参数。

 

第二种构造函数接收一个Vector集合,因为只有Vector集合才有Enumeration,把每一个流对象存入集合,然后在通过Vector集合得到Enumeration ,传递给合并流.

例:

    Vector v=new Vector();

    v.add(new FileInputStream(“a.txt”));

    v.add(new FileInputStream(“b.txt”));

v.add(new FileInputStream(“b.txt”));

Enumeration  e= v . elements() ;

    SequenceInputStream sis=new SequenceInputStream(e);

    byte [] b =new byte[1024];

    int len=0;

    while((len=sis.read(b))!=-1){

       System.out.println(new String(b,0,len));

}

 

序列化:ObjectInputStreamObjeceOutputStream

IO流可以操作文件数据,但是不能直接操作对象,这样一来,如果程序中的一个对象的信息要持久化保存就不行了,如果要把堆内存中的对象的数据持久化保存起来并且每次运行此程序的时候都要读取这些信息,就必须使用两个类:ObjectInputStreamObjectOutputStream.这两个类要搭配使用,因为用ObjectOutputStream写入的文件一定要用ObjectInputStream才能读取.

他们的构造函数需要传入一个字节流流对象,并且此对象要实现Serializable,这个接口没有抽象方法,所以这是一个标记接口,用于表示:这个类可以序列化

 

要从由 ObjectOutputStream 中的示例写入的流读取:

        FileInputStream fis = new FileInputStream("t.tmp");
        ObjectInputStream ois = new ObjectInputStream(fis);
 
        int i = ois.readInt();
        String today = (String) ois.readObject();
        Date date = (Date) ois.readObject();
 
        ois.close();

 

 

要写入可通过 ObjectInputStream 中的示例读取的对象,请执行以下操作:

        FileOutputStream fos = new FileOutputStream("t.tmp");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
 
        oos.writeInt(12345);
        oos.writeObject("Today");
        oos.writeObject(new Date());
 
        oos.close();

 

 

由于对象的序列号是根据对象中的成员决定的,只要对象中的成员改变了,对象的序列号也随之改变了,如果要在成员改变后依然对这个对象进行序列化,此时就需要手动的给对象添加序列号,这样对象的序列号就可以保持不变了: public static final long serialVersionUID = 42L;

 

静态的成员不能被序列化,也就是说,静态的成员的值只能在内存中改变,但是无法保存在文件中;另一方面,如果一个非静态成员也不需要序列化,那么可以加上一个访问修饰符: transient  (短暂的,片刻的,非持久的)

 

管道流:PipedInputStreamPipedOutputStream

管道流是IO技术和线程技术相结合的流对象,通过管道流,可以直接把数据从输出流输出到输入流,输出流和输入流可以通过构造函数或者connect方法创建关联.通常读取方法和写入方法都分别封装到一个线程中.其实管道流没有什么特别的,只是把读取和写入的方法都定义在不同的线程中,然后把管道连接起来,通过线程来调用读取和写入的方法.下面用一个例子演示管道流的用法:

import java.io.*;

class  Demo{

       PipedInputStream pis=null;

PipedOutputStream pos=null;

try{

       pis=new PipedInputStream();

       pos=new PipedOutputStream();   

       pis.connect(pos);                        //把读取流和写入流相关联

Write w=new Write(pos);             //写入数据的线程

Read r=new Read(pis);                //读取数据的线程

new Thread(w).start();                //写入数据

new Thread(r).start();                  //读取数据

}catch(IOException e){

      

}

}

 

//写入数据的线程

class Write implements Runnable{

       private PipedOutputStream pos;

       Write (PipedOutputStream pos){          //在创建时接受一个管道输出流

              this.pos=pos;

}

       public void run(){

              try{

                     pos.write(“管道流练习”.getBytes());  

}catch(IOException e){

}finally{

       if(pos!=null){

              try{

                     pos.close();

}catch(IOException e2){}

}

}

}

}

 

//读取数据的线程

class Read implements Runnable{

       private PipedInputStream pis;

       Read(PipedInputStream pis){       //在创建时接受一个管道输入流

              This.pis=pis;

}

 

public void run(){

       try{

              byte [] b=new byte[1024];

              int len=0;

              while((len=pis.read(b))!=-1){

                     System.out.println(new String(b,0,len));

}

}catch(IOException e){

}finally{

if(pis!=null){

       try{

              pis.close();

}catch(IOException e2){}

}

}

 

}

}

 

任意访问: RandomAccessFile

RandomAccessFilejava输入输出流中功能最丰富的文件内容访问类,它提供了众多的文件访问方法,包括读取文件数据和向文件输出数据.但是RandomAccessFile却不是IO包中的类,而是直接继承自Object. RandomAccessFile可以从文件的任意位置来读写数据,这也是它比IO流强的一个方面.

 

RandomAccessFile的构造函数:

RandomAccessFile(File file , String mode);

RandomAccessFile(String name , String mode);

第一个参数是要读写的文件名,第二个参数是一个模板,”r”只读, “rw”读写

 

RandomAccessFile的常用方法:

getFilePointer() :返回此文件中的当前偏移量。

read()readInt()等读取基本数据类型的方法

seek(long pos) :设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。

write(int b)writeInt(int v)写入基本数据类型的方法

 

 

数组流:ByteArrayInputStreamByteArrayOutputStream

专门操作数组的流,操作的源和目的都是内存.

ByteArrayInputStream是读取一个字节数组的元素,所以在它的构造函数中要传入一个字节数组

ByteArrayOutputStream是把文件写入一个数组,在内部封装了一个字节数组,所以不需要在构造的时候传递数组.

ByteArrayOutputStream中的方法writeTo(OutputStream out)是把ByteArrayOutputStream中写入元通过一个输出流输出到文件中

 

 

编码表:

GBK
Gbk编码表,一个中文是两个字节

GBK编码是GB2312编码的超集,向下完全兼容GB2312,同时GBK收录了Unicode基本多文种平面中的所有CJK汉字。同 GB2312一样,GBK也支持希腊字母、日文假名字母、俄语字母等字符,但不支持韩语中的表音字符(非汉字字符)。GBK还收录了GB2312不包含的汉字部首符号、竖排标点符号等字符。
GBK
的整体编码范围是为0x8140-0xFEFE(两个字节,16位二进制码),不包括低字节是0×7F的组合。高字节范围是0×81-0xFE,低字节范围是0x40-7E0x80-0xFE

低字节是0x40-0x7EGBK字符有一定特殊性,因为这些字符占用了ASCII码的位置,这样会给一些系统带来麻烦。

有些系统中用0x40-0x7E中的字符(如“|”)做特殊符号,在定位这些符号时又没有判断这些符号是不是属于某个 GBK字符的低字节,这样就会造成错误判断。在支持GB2312的环境下就不存在这个问题。需要注意的是支持GBK的环境中小于0x80的某个字节未必就是ASCII符号;另外就是最好选用小于0×40ASCII符号做一些特殊符号,这样就可以快速定位,且不用担心是某个汉字的另一半。Big5编码中也存在相应问题。

CP936
GBK的有些许差别,绝大多数情况下可以把CP936当作GBK的别名。

 

 

UTF-8

UTF-8编码表,一个中文是三个字节
Unicode Transformation Format-8bit,允许含BOM,但通常不含BOM。是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。UTF-8编码的文字可以在各国支持UTF8字符集的浏览器上显示。如,如果是UTF8编码,则在外国人的英文IE上也能显示中文,他们无需下载IE的中文语言支持包。
GBK
的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBK大。

GBK
GB2312等与UTF8之间都必须通过Unicode编码才能相互转换:

GBK
GB2312--Unicode--
UTF8
UTF8
--Unicode--GBK
GB2312
对于一个网站、论坛来说,如果英文字符较多,则建议使用UTF8节省空间。不过现在很多论坛的插件一般只支持GBK

 

 

 

 

 

----------------------android培训,java培训、期待与您交流! ----------------------

详细请查看
http://edu.youkuaiyun.com/heima

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值