Java高级编程(二)——I/O流

I/O流

目录

I/O流

体系

字符流

Reader

Writer

字节流

处理文本文件

处理非文本文件

缓冲流

对象流

序列化与反序列化

随机文件存取流

NIO


在Java中,把不同类型的输入、输出源(键盘、文件、网络等)抽象为流 (Stream)

文本文件(.txt、.java、.c、.cpp)使用字符流,非文本文件(.jpg、.mp3、.avi、.doc、.ppt)用字节流

体系

分类

  • 输入流、输出流

  • 字节流(8 bit)、字符流(16 bit)

  • 节点流、过滤器(处理流)

 

字符流不能处理图片等字节数据

节点流(或文件流)

 read(byte[] buffer);       //InputStream
 write(byte[] buffer,0,len);//OutputStream
 read(char[] buffer);       //Reader
 write(char[] buffer,0,len);//Writer

缓冲流(处理流一种)

 read(byte[] buffer);           //BufferedInputStream
 write(byte[] buffer,0,len);    //BufferedOutputStream
 read(char[] buffer);readLine();//BufferedReader
 write(char[] buffer,0,len);    //BufferedWriter

字符流

Reader

File类具有查询文件属性、状态和文件名等功能,但不能访问文件内容

相对路径

 public static void main(String[] args) {
     //C:\Users\HanYiting\IdeaProjects\TestFile\src\hello.txt
     File file=new File("src\\hello.txt");//相对于当前工程
     System.out.println(file.getAbsoluteFile());
 }
 public static void TestFileReader(){
     File file=new File("hello.txt");//相对于当前model
 }

FileReader读入数据

public static void TestFileReader() throws IOException {
     File file=new File("src\\hello.txt");//实例化File类的对象
     FileReader fr=new FileReader(file);//提供具体的流
     int data;//read()返回值为int类型,返回读入的一个字符,没有则返回-1
     while((data=fr.read()) != -1){
         System.out.print((char)data);
     }
     if(fr != null) fr.close();
 }

垃圾回收机制中,对于物理上的连接,如数据库连接、输入输出流连接,Socket连接不能自动关闭,需要手动关闭输入输出流

选中后ctrl+Alt+T快捷键可选择try/catch/finally快速做异常处理 ↓

 public static void TestFileReader(){
     FileReader fr= null;//提供具体的流
     try {
         File file=new File("src\\hello.txt");//实例化File类的对象
         fr = new FileReader(file);
         int data;
         while((data=fr.read()) != -1){
             System.out.print((char)data);
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {}
     try {
         if(fr != null) fr.close();
     } catch (IOException e) {
         e.printStackTrace();
     } finally {}
 }

注意,fr.close()也要做异常处理

read的重载:read(char[] buf)的用法

char[] cbuf=new char[5];
 fr.read(cbuf)//会5个5个读,没有则返回-1
 /*int len;
 char[] cbuf=new char[5];
 while((len = fr.read(cbuf)) != -1){//
     System.out.print(cbuf);
 }helloworld123→helloworld123ld,因为最后只能读3个,没有新的字符可以覆盖后面两个字符*/

所以用的时候用String,然后每次输出注意截取String str=new String(cbuf,0,len)

Writer

public static void TestFileWriter() throws IOException {
     File file=new File("hello1.txt");//没有则会默认创建一个
     FileWriter fw=new FileWriter(file);//构造器还有参数FileWriter(file,true),true就不覆盖,在原有文件后追加
     fw.write("Hei Boy");
     fw.close();
 }

write同样也有多个重载方法,详情去看定义

字节流

处理文本文件

 public static void TestFileInputStream(){
     File file=new File("src\\hello.txt");
     FileInputStream fis=new FileInputStream(file);
     byte[] buffer=new byte[5];
     int len;
     while((len=fis.read(buffer))!=-1){
         String str=new String(buffer,0,len);
         System.out.print(str);
     }
     fis.close();
 }//再做一下异常处理

处理非文本文件

public static void FileStream(){
     FileInputStream fis= null;
     FileOutputStream fos= null;
     try {
         File srcFile=new File("src\\picture.png");
         File destFile=new File("src\\1.png");
         fis = new FileInputStream(srcFile);
         fos = new FileOutputStream(destFile);
         byte[] buffer=new byte[10];
         int len;
         while ((len=fis.read(buffer))!=-1){
             fos.write(buffer,0,len);
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         if(fos!=null){
             try {
                 fos.close();
             } catch (IOException e) {
                 e.printStackTrace();
             } finally {}
         }
         if(fis!=null){
             try {
                 fis.close();
            } catch (IOException e) {
                 e.printStackTrace();
             } finally {}
         }
     }
 }

缓冲流

缓冲是为了提高读写效率

造流先造内层再造外层,关的时候先关外层再关内层。关闭外层时,内层流会自动关闭,所以只写外层的close即可

 public static void BufferedTest() throws IOException {//没改try-catch
         File srcfile=new File("src\\hello.txt");
         File destFile=new File("src\\hei.txt");
         FileInputStream fis=new FileInputStream(srcfile);
         FileOutputStream fos=new FileOutputStream(destFile);
         BufferedInputStream bis=new BufferedInputStream(fis);
         BufferedOutputStream bos=new BufferedOutputStream(fos);
 ​
         byte[] buffer=new byte[10];
         int len;
         while ((len=bis.read(buffer))!=-1){
             bos.write(buffer,0,len);
         }
         bos.close();
         bis.close();
     }

通过时间分别测试缓冲流和普通的字节流复制视频时间,缓冲流明显快

long start=System.currentTimeMillis();
 ...
long end=System.currentTimeMillis();
System.out.println(end-start);//缓冲流157,字节流9391

Buffer内部提供了一个缓冲区也就是说缓冲区大小8192(=1024×8)

 bos.flush();//刷新缓冲区,缓冲区满时会自动清空

可以通过匿名的方式简化

 BufferedReader br=new BufferedReader(new FileReader(new File("src\\hello.txt")));

其他方法

String data;
data=br.eadLine();//BufferedReader,读取一行但不读取换行符,记得自己加\n,没有值时返回null
//bw.newLine();BufferedWriter的方法,换行用

对象流

ObjectInputStream和ObjectOutputStream:用于存储和读取基本数据类型数据或对象的处理流。可以把java中的对象写入到数据源中,也能把对象从数据源中还原出来

  • 序列化:用ObjectOutputStream类保存基本数据类型数据或对象

  • 反序列化:用ObjectInputStream类读取基本类型数据或对象

  • 不能序列化static和transient修饰的成员变量

序列化与反序列化

对象的序列化机制:允许把内存中的java对象转换成语平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网路将这种二进制流传输到另一个网络节点,当其他程序获取了这种二进制流,就可以恢复成原来的java对象

  • 实现了Serializable接口的对象转化为字节数据

  • 序列化是RMI(远程方法调用)过程的参数和返回值都必须实现的机制。RMI是JavaEE的基础,因此序列化是JavaEE平台的基础

  • 如果要让某个对象支持序列化机制,则需要让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现Serializable、Externalizable两接口之一,否则会抛出异常

对String类进行序列化和反序列操作

 import java.io.*;
 ​
 public class Test1 {
     public static void main(String[] args) {
         //testObjectOutputStream();
         testObjectInputStream();
     }
 ​
     public static void testObjectOutputStream(){
         ObjectOutputStream oos= null;
         try {
             oos = new ObjectOutputStream(new FileOutputStream("object.dat"));//造流造对象
             oos.writeObject(new String("Hello world"));//写入
             oos.flush();//刷新操作
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             if(oos != null){
                 try {
                     oos.close();//关闭资源
                 } catch (IOException e) {
                     e.printStackTrace();
                 } finally {
                 }
             }
         }
     }
 ​
     public static void  testObjectInputStream(){
         ObjectInputStream ois=null;
         try {
             ois=new ObjectInputStream(new FileInputStream("object.dat"));
             Object obj=ois.readObject();
             String str=(String) obj;
             System.out.println(str);
         } catch (IOException e) {
             e.printStackTrace();
         } catch (ClassNotFoundException e) {
             e.printStackTrace();
         } finally {
             if (ois != null){
                 try {
                     ois.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 } finally {
                 }
             }
         }
     }
 }

要想一个java对象可序列化的,需要满足相应的要求

 public class Person implements Serializable {
     public static final long serialVersionUID=12344L;//从声明里看到必须提供一个这样的UID
     private String name;
     private int age;
     ...
 }
  • 需要当前类提供一个全局常量serialVersionUID

    • serialVersionUID用来表名类的不同版本的兼容性,目的是以序列化对象进行版本控制,有关各版本序列化时是否兼容。

    • 如果不显示的定义这个静态变量,它的值是由java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID也可能变化。因此最好显示声明

    • 简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性,在反序列化时,JVM会把传来的字节流的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同则认为一致,可以进行反序列化,否则会出现序列化版本不一样的异常(InvalidCastException)

修改上面代码的部分

 oos.writeObject(new Person("Tom",23));//序列化写入
 oos.flush();
 ​
 Person p=(Person) ois.readObject();//反序列化读取
 System.out.println(p);//Person{name='Tom', age=23}

注意,ObjectInputStream和ObjectOutputStream不能序列化static和transient修饰的成员

随机文件存取流

RandomAccessFile类

  • 直接继承与java.long.Object类,并且实现了DataInput、DataOutput这两个接口,也就是说这个类可以读也可以写

public static void test1() throws IOException {
    RandomAccessFile raf1=new RandomAccessFile(new File("123.png"),"r");
    RandomAccessFile raf2=new RandomAccessFile(new File("1234.png"),"rw");
    byte[] buffer=new byte[1024];
    int len;
    while ((len = raf1.read(buffer)) != -1){
        raf2.write(buffer,0,len);
    }
    raf1.close();
    raf2.close();
}

作为输出流时,文件不存在则会自动创建。如果存在,会对原有文件内容进行覆盖(默认从头覆盖)

public static void test2() throws IOException {
     RandomAccessFile raf1=new RandomAccessFile(new File("hello.txt"),"rw");//abcdefghijk
     raf1.write("zxyuv".getBytes());//zxyuvfghijk,前面被覆盖了
     raf1.close();
 }

seek可以改变指针位置

 raf1.seek(3);//将指针调到角标为3的位置,从3开始覆盖
 raf1.write("xyz".getBytes());//abcxyzghijk

想不覆盖就先把后面内容读出来,最后再写回去

public static void test2() throws IOException {
    RandomAccessFile raf1=new RandomAccessFile(new File("hello.txt"),"rw");//abcdefghijk
    raf1.seek(3);//将指针掉到3,保存指针后的数据
    StringBuilder builder=new StringBuilder((int) new File("hello.txt").length());
    byte[] buffer=new byte[20];
    int len;
    while ((len = raf1.read(buffer)) != -1){
        builder.append(new String(buffer,0,len));
    }
    raf1.seek(3);
    raf1.write("xyz".getBytes());
    raf1.write(builder.toString().getBytes());
    raf1.close();
}

NIO

IO是面向流的,NIO是面向缓冲区的。NIO更加高效

Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO

早期java只提供了File类,但是File类功能有限,出错时大部分仅返回失败不提供异常信息。NIO.2引入了Path接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path可以看成是File类的升级版,实际引用的资源也可以不存在

Path path = Paths.get("index.html");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值