JAVA学习笔记——JAVA中的IO流

本文内容主要根据慕课网的《文件传输基础——JAVA IO流》课程整理而成。(链接:http://www.imooc.com/learn/123


一. 文件的编码

问题:

        新建一个文本文件,输入“联通”后保存,再打开会出现乱码。而输入“联想”再打开时则不会出现乱码。

原因:

        文本文件是字节序列,如果在中文机上直接创建文本文件,该文本文件只认识ANSI编码,输入”联通“时是一种巧合,刚好符合了UTF-8的编码规则,因此打开时为UTF-8编码方式,如果再点击打开选择ANSI编码方式,则不会出现乱码问题。

三种编码类型的字符占用字节数为:

                  中文         英文

gbk              2             1

utf-8             3             1

utf-16be      2             2

因此,当字节序列是由某种方式编码时,也需要用这种方式进行解码,否则可能出现乱码。

使用Eclipse创建项目时默认的编码是gbk编码(可以修改,但一个项目只有一种编码方式),用字节流创建字符时若未指定编码类型则使用默认编码。而utf-16be是JAVA中字符串的编码方式。


二. File类的使用

java.io.File类用于表示文件(目录),File类只用于表示文件(目录)的信息(大小,名称等),不能用于文件内容的访问。

常用方法:exists mkdir  mkdirs  createNewFile  delete  isFile  isDirectory  getAbsolutePath  getName  getParent  等,比较简单,需要时直接查看文档或提示即可。

三. RandomAccessFile类的使用

RandomAccessFile是java提供的对文件内容的访问的类,既可以读文件,也可以写文件。
RandomAccessFile支持随机访问文件,可以访问文件的任意位置。

(1)java文件模型
  在硬盘上的文件是byte byte byte存储的,是数据的集合

(2)打开文件
  有两种模式"rw"(读写)  "r"(只读)
  RandomAccessFile raf = new RandomeAccessFile(file,"rw")
  文件指针,打开文件时指针在开头 pointer = 0;

(3) 写方法
    raf.write(int)--->只写一个字节(后8位),同时指针指向下一个位置,准备再次写入。一个int为4字节,如果用write写一个int要配合移位算符写四次(实际上就是writeInt的底层源码)。
(4)读方法
   int b = raf.read()--->无参数则只读一个字节,传入字节数组可以一起读入到字节数组中。

(5)文件读写完成以后一定要关闭(Oracle官方说明)


四. 字节流 

 1)InputStream、OutputStream

   InputStream抽象了应用程序读取数据的方式

   OutputStream抽象了应用程序写出数据的方式 

 2)EOF = End   读到-1就读到结尾

 3)输入流基本方法
   int  b = in.read();读取一个字节无符号填充到int低八位.-1是 EOF
   in.read(byte[] buf)    读取数据填充到字节数组buf,单个字节读取,不适合读取大文件。
   in.read(byte[] buf,int start,int size)  批量读取。读取从start开始长为size的数据到字节数组buf,返回的是读到字节的个数(可能读不满),批量读取到字节缓冲数组时应用这个方法。批量读取,对大文件而言效率高(缓冲器的大小应适中)。
4)输出流基本方法
  out.write(int b)  写出一个byte到流,b的低8位
  out.write(byte[] buf)将buf字节数组都写入到流
  out.write(byte[] buf,int start,int size)  将字节数组buf中的从start开始长为size的数据写到输出流。
  
 5)FileInputStream

 继承了Inputstream,把文件作为字节流进行读操作,具体实现了在文件上读取数据。读方法与InputStream相同。


 6)FileOutputStream 

  继承了Inputstream,把文件作为字节流进行写操作,实现了向文件中写出byte数据的方法,写方法与OutputStream相同


 7)DataOutputStream/DataInputStream
    对"流"功能的扩展,可以更加方面的读取int,long,字符等类型数据
   DataOutputStream
        writeInt()/writeDouble()/writeUTF()    实际上包装了write方法对不同类型数据的处理,扩展了功能。

 8)BufferedInputStream&BufferedOutputStream

 这两个流类位IO提供了带缓冲区的操作,一般打开文件进行写入
 或读取操作时,都会加上缓冲,这种流模式提高了IO的性能
 从应用程序中把输入放入文件,相当于将一缸水倒入到另一个缸中:
           FileOutputStream--->write()方法相当于一滴一滴地把水“转移”过去
           DataOutputStream-->writeXxx()方法会方便一些,相当于一瓢一瓢把水“转移”过去
           BufferedOutputStream--->write方法更方便,相当于一飘一瓢先放入桶中,再从桶中倒入到另一个缸中,性能提高了。

 视频中给出了三种方法对文件进行复制,速度是2>>3>1,但是3中使用的是单字节读取,实际上使用批量读取的带缓冲的复制对大文件应该是更快的。

在使用时,注意BufferedOutputStream每次读取后都要用flush()对缓冲区进行刷新。(视频中说需要进行刷新,但有看到说不要进行刷新,否则严重印象效率。。有待进一步查阅资料)


五. 字符流

1) 编码问题

 2)认识文本和文本文件
 java的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码)
 文件是byte byte byte ...的数据序列
文本文件是文本(char)序列按照某种编码方案(utf-8,utf-16be,gbk)序列化为byte的存储结果

3)字符流(Reader Writer)

---->操作的是文本文本文件
字符的处理,一次处理一个字符
字符的底层任然是基本的字节序列
字符流的基本实现
   InputStreamReader   完成byte流解析为char流,按照编码解析,在构造函数中没有指定编码时将默认为项目编码,操作文件时应与文件的编码相一致。
   OutputStreamWriter  提供char流到byte流,按照编码处理,同样也要注意编码问题。
   
   FileReader/FileWriter

主要是操作文件,API比较方便,但不能指定编码。
 字符流的过滤器
   BufferedReader   ---->readLine 一次读一行 
   BufferedWriter/PrintWriter   ---->写一行,直接操作时不能识别换行,BufferedWriter可以添加newLine实现换行操作,PrintWriter可以用Println实现换行。 

   
   

六. 对象的序列化,反序列化

1)对象序列化, 就是将Object转换成byte序列,反之叫对象的反序列化 

2)序列化流(ObjectOutputStream),是过滤流----writeObject
   反序列化流(ObjectInputStream)---readObject

3)序列化接口(Serializable)
   对象必须实现序列化接口 ,才能进行序列化,否则将出现异常
   这个接口,没有任何方法,只是一个标准

4) transient关键字

  使用transient关键字后该成员变量不会进行jvm默认的序列化,反序列化后对应的值为默认值,但也可以自己完成这个元素的序列化。

  
    private void writeObject(java.io.ObjectOutputStream s)
       throws java.io.IOException
private void readObject(java.io.ObjectInputStream s)
       throws java.io.IOException, ClassNotFoundException
       
   在writeObject方法中,调用defaultWriteObject能对默认元素进行序列化,对transient的元素可写writeXXX自己来完成序列化。同理,在readObject中也可以完成transient元素的序列化。

    在某些情况下,transient关键字可以提高序列化的性能,分析ArrayList源码中序列化和反序列化的问题,在elementData中用了transient关键字,由于arraylist数组的元素未定,自行根据元素的数目来序列化能避免浪费,因此提高了性能。

5)序列化中 子类和父类构造函数的调用问题

 
 一个类实现了序列化接口,其子类都能进行序列化。子类在构造时会递归调用父类的构造函数,而反序列化时,如果其父类没有实现序列化接口,那么父类的构造函数会被调用。

 
 



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值