Java基础07—IO流(输入/输出)

本文是学习Java时所记录的学习笔记,本节包含了IO流的概念、具体的写法、以及NIO、NIO2的IO流相关知识。参考了《疯狂Java讲义》和网上视频教程。欢迎留言、私信交流~~

IO流简介

什么是IO流?

Java的IO流是实现输入/输出的基础。掌握IO流就是掌握数据读取、存储数据。Java的IO部分,需要理解各个类型流之间的关系。

IO流分类概述
  • java.io包下主要包括输入、输出流两种IO流。
  • 每种输入、输出流又可分为字节流和字符流两大类。
  • 字节流以字节为单位来处理输入、输出操作。
  • 字符流以字符来处理输入、输出操作。
  • Java的IO流使用了装饰器设计模式,将IO流分成底层节点流和上层节点流。
  • 节点流用于和底层的物理存储节点直接关联。
  • 包装流用于把不同物理节点流包装成统一的处理流。
IO流分类表
  • java.io.*;
    分类           类名           说明
    文件处理File类用于操作文件和目录的类,可以新建、删除、重命名文件和目录。但File不能访问文件内容本身,如需要访问内容,则需要使用输入流/输出流。
    输入流/抽象基类InputStream类属于字节输入流类,本身不能创建实例,但他是输入流的模板,它的子类通常会使用它的输入流方法(read方法)。
    输入流/抽象基类Reader类属于字符流类,本身不能创建实例,但他是输入流的模板,它的子类通常会使用它的输入流方法(read方法)。
    输入流/节点流FileInputStream类字节流,该类会直接和指定文件关联,可以进行数据读取。
    输入流/节点流FileReader类字符流,该类会直接和文件关联,可以进行数据读取。
    输出流/抽象基类OutputStream类属于字节输出流类,本身不能创建实例,但他是输出流的模板,它的子类通常会使用它的输出流方法(write方法)。
    输出流/抽象基类Writer类属于字符输出流类,本身不能创建实例,但他是输出流的模板,它的子类通常会使用它的输出流方法(write方法)。
    输出流/节点流FileOutputStream类字节流,该类会直接和文件关联,可以进行数据写入(输出)。
    输出流/节点流FileWrite类字节流,该类会直接和文件关联,可以进行数据写入(输出)。
    处理流/打印流PrintStream类字节输出流。
    处理流/打印流PrintWriter类字符输出流。
    处理流ObjectInputStream类字节输入流,主要负责对象输入。
    处理流ObjectOutputStream类字节输出流,主要负责对象输出。
    转换流InputStreamReader类负责将字节输入流转换成字符输入流。
    转换流OutputStreamWriter类负责将字节输出流转换成字符输出流。
    包装流BufferedReader类该类具有缓冲功能,可以一次读取一行文本(以换行符为标志),如果没有读到换行符,则程序堵塞,等到读到换行符为止。
    推回输入流PushbackInputStream类字节输入流,unread方法将字节数组内容推回到缓冲区里,允许重复读取刚刚读取的内容。
    推回输入流PushbackReader类字符输入流,unread方法将字符数组内容推回到缓冲区里,允许重复读取刚刚读取的内容。
    输入流/输出流RandomAccessFile类该类是输入/输出流体系中功能最丰富的文件内容访问类,与其他输入/输出流不同的是,它可以直接跳转到文件任意的地方读写数据。如果只需要访问文件部分内容,推荐使用该类。(该类包含了一个记录指针,用于标识当前度写出的位置)

IO流的具体实现

File类
File tmp = File.createTempFile("tmp",null);  //创建一个临时文件(可以设置该临时文件将在JVM退出时被删除)
tmp.deleteOnExit();  //指定该临时文件在JVM退出时被删除
基本输出流实现流程
FileWriter写数据
FileWriter fw = new FileWriter("e:\\test\\a.txt");  //新建输入流
FileWriter fw = new FileWriter("e:\\test\\a.txt",true);  //true表示追加内容的模式,在a.txt后面追加内容。
基本输入流实现流程
FileReader读数据
FileReader fr = new FileReader("e:\\fr.txt");
基本输入流方法说明
int read()一次读取一个字符,返回int值
int read(char[] cbuf)读取内容到cbuf中,返回的是实际读取的字符个数
ObjectInputStream/ObjectOutputStream类使用方法
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(a.txt));  //创建一个ObjectOutputStream输出流
oos.writeObject(stu1);  //将stu1对象写入输出流。
ObjectInputStream ois = new ObjectInputStream(FileInputStream(a.txt));  //创建一个ObjectInputStream输入流
Person p = (Person)ois.readObject();  //从输入流中读取一个Java对象,并将其类型强制转换为Person类
RandomAccessFile类使用方法
RandomAccessFile raf = newRandomAccessFile("a.txt","r");  //以只读的方式打开相对路径中的a.txt文件。
raf.seek(300);  //设置raf的文件记录指针位置为第300字节处,后面讲从这个位置进行读写。
raf.write("追加的内容!\r\n".getBytes());  //追加内容。注意:需要将文件指针后面的内容存入一个临时文件中,插入新内容后,再把临时文件的内容追加到后面。
基本输入输出案例1
//复制a.txt的内容到b.txt
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class shurushuchu {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader("a.txt"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
		
		String str1;
		while ((str1 = br.readLine())!=null)
		{
			bw.write(str1);
			bw.newLine();
			bw.flush();
		}
		bw.close();
		br.close();
	}
}
基本输入输出案例2
//复制图片
import java.io.*;
class  filedemo
{
    public static void main(String[] args) throws IOException
    {
        FileInputStream i1 = new FileInputStream("filedemo\\atat.jpg");
        FileOutputStream o1 = new FileOutputStream("filedemo\\btbt.jpg");
        int len;
        byte[] str = new byte[1024];
        while ((len=i1.read(str))!=-1)
        {
            o1.write(str);
        }
        i1.close();
        o1.close();
    }
}
基本输入输出案例3
//列出*.java
import java.io.*;
class  filedemo1
{
    public static void main(String[] args) throws IOException
    {
        File f1 = new File("filedemo");
        File[] str = f1.listFiles();
        for (File a : str )
        {
            if (a.isFile())
            {
                if (a.getName().endsWith(".java"))
                {
                    System.out.println(a.getName());
                }
            }
        }
    }
}
标准输入输出流案例1
//用字节输入流输入,通过转换流输出字符。
import java.io.*;
class outdemo 
{
    public static void main(String[] args) throws IOException
    {
        BufferedReader br =  new BufferedReader(new FileReader("test.java"));
        Writer w = new OutputStreamWriter(System.out);

        String line;
        while ((line = br.readLine())!=null)
        {
            w.write(line);
            w.write("\r\n");
        }

        w.close();
        br.close();
    }
}
对象操作流案例1
class shurudemo1
{
    public static void main(String[] args) throws IOException,ClassNotFoundException
    {
        FileOutputStream fos = new FileOutputStream("filedemo\\a.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        ArrayList<students> list = new ArrayList<students>();
        list.add(new students("h1",18));
        list.add(new students("h2",19));
        list.add(new students("h3",20));
        list.add(new students("h4",21));
        oos.writeObject(list);
        fos.close();        
        oos.close();      
        
        FileInputStream fis = new FileInputStream("filedemo\\a.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object a1 = ois.readObject();
        ArrayList<students> list1 = (ArrayList<students>)a1;
        for (students b:list1)
        {
            System.out.println(b);
        }
        fis.close();
        ois.close();
    }
}

IO流相关知识

对象序列化
什么是对象序列化?
  • 序列化机制允许将实现序列化的Java对象转换成字节序列,这些字节序列可以保存在磁盘上,或通过网络传输。序列化机制使得对象可以脱离程序的运行而独立存在。
  • Serializable接口:对象的序列化,指将Java对象写入IO流中。
  • Deserialize接口:对象的反序列化,指从IO流中恢复Java对象。
  • Externalizable接口:对象的序列化,该方式由程序员决定存储和恢复对象数据。
Serializable接口

下面程序定义了一个Person类,这个Person类就是一个普通的Java类,只是实现了Serializable接口,该接口标识该类的对象是可序列化的。

public class Person implements java.io.Serializable{
    private String name;
    private int age;
    public Person(String name,int age){
        System.out.println("有参数的构造器");
        this.name = name;
        this.age = age;
    }
    //省略get、set方法等
}
Externalizable接口
  • 该接口有两个需要实现的方法:void readExternal(ObjectInput in)和void writeExternal(ObjectOutput out)
  • 当使用Externalizable时,程序会先使用public的无参数构造器创建实例,然后才执行readEternal方法进行反序列化。因此必须提供public的无参数构造器。
对象序列化相关知识
  • 如果想要类是可序列化的,该类必须试下两个接口之一:Serializable/Externalizable
  • Serializable:标记接口,实现该接口无需实现任何方法,它只表明该类的实例是可序列化的。
  • 使用反序列化恢复Java时,必须提供该Java对象所属类的class文件,否则会引发ClassNotFoundException异常。
  • 当一个可序列化类有多个父类时,父类要么有无参数的构造器,要么也是可序列化的。否则反序列化时将抛出InvalidClassException异常。
  • 如果类的成员变量类型不是基本类或String类,而是另一个引用类型。那么该引用类型必须是可序列化的,否则拥有该类型成员变量的类也是不可序列化的。
  • 如果多次序列化同一个对象,系统不会重复生成,只会在第二次及以后输出一个序列化编号。
  • 使用序列化机制向文件写入多个Java对象,使用反序列化机制恢复对象时必须按实际写入顺序读取。
  • 只有第一次调用writeObject()方法来输出对象时才会将对象转换成字节序列,并写入到ObjectStream。后面进程中即使该对象的实例变量发生改变,重新写入也不会产生新的序列化编号。
  • transient关键字修饰:用于修饰实例变量,可以指定Java序列化时无需理会该实例变量。
  • 为了安全性,可以将实例变量包装成StringBuffer,并将其字符序列反转后写入。在读取时也要进行相应动作。
序列化版本控制
  • 通过以下方法保证两个class文件的兼容性(防止项目升级后class文件不兼容)
    • Java序列化机制允许为序列化类提供一个private static final的serialVersionUID值。
    public class Test{
        //为该类指定一个serialVersionUID类变量值
    	private static final long serialVersionUID = 512L;
    }
    
    • 可以通过JDK安装路径下的bin目录下的serialver.exe工具获得类的serialVersionUID类变量的值:
    serialver Person。(加上-show可以显示图形化界面)
    //输出:
    //Person: static final long serialVersionUID = -2595800114629327570L;
    
NIO
  • JDK1.4开始,Java提供了一系列盖紧的输入/输出处理的新功能,这些功能被统称为新IO(New IO,简称NIO)。
  • 两个核心功能:Channel(通道)/Buffer(缓冲)
    • Channel与传统的InputStream和OutputStream最大区别在于它提供了一个map()方法。通过map()方法,可以直接将“一块数据”映射到内存中。可以说NIS是面向块的处理。
    • Buffer可以理解成一个容器,本质是一个数组。发送到Channel的对象必须先放到Buffer中。
  • 新IO还提供了:Charset类(Unicode字符映射成字节序列以及逆映射)、Selector类(支持非阻塞式输入/输出)
Buffer类
  • 抽象类Buffer类(三个重要概念:容量(capacity)、界限(limit)和位置(position))
  • 常用子类ByteBuffer类:可以在底层字节数组上进行get/set操作。
  • 常用子类CharBuffer类:可以在底层char数组上进行get/set操作。
  • 不常用子类XxxBuffer类:和ByteBuffer类似,对应于不同的基本数据。
  • Buffer类有三个重要概念:容量(capacity)、界限(limit)和位置(position)。
    • 容量(capacity):缓冲区的容量(capacity)表示该Buffer的最大数据容量。
    • 界限(limit):第一个不应该被读出或者写入的缓冲区位置索引。limit后面的数据不可读也不能写。
    • 位置(position):用于指明下一个可以被读出的或者写入的缓冲区位置索引。(类似于IO流中的指针)
  • 例子
    CharBuffer buff = new CharBuffer.llocate(8);  //创建容量为8的CharBuffer对象
    buff.capacity();  //返回buff的容量大小。
    buff.limit();  //返回buff的界限的位置。
    buff.position();  //返回buff中的position值。
    buff.put('a');  //放入元素'a'。
    buff.flip();  //进入输出数据状态(可以理解为输入数据完成)。该方法会将limit设置为position所在位置,并将position设为0。
    buff.get();  //取出第一个元素。
    buff.clear();  //再次进入输入状态(可以裂解为读取数据完成)。该方法会将position设置为0,limit设置为capacity。
    
Channel类
  • 抽象类Channel类:类似于传统的流对象,但是Channel可以将指定文件的部分或者全部直接映射成Buffer。必须通过Buffer进行读写,该类只和Buffer进行交互。
  • 常用类FileChannel类:文件操作的Channel。
  • 不常用类XxxChannel类:和FileChannel类似,Xxx按功能来命名,例如SocketChannel、ServerSocketChannel和DatagramChannel等等。
  • 常用方法
    方法名说明
    造器Channel是通过传统的节点InputStream、OutputStream的getChannel()方法来返回对应的Channel。
    appedByteBuffer map(FileChannel.MapMode mode,long position,long size)第一个参数是映射时的模式(只读、读写等),第二个、第三个参数用于控制将Channel的哪些数据映射成ByteBuffer。
    ock()试图锁定文件,如果无法得到文件锁,程序将一直阻塞。(带参数的可以设定锁定的内容范围,以及是否共享锁)
    ryLock()试图锁定文件,如果不堵塞则获得文件锁,否则返回null。(带参数的可以设定锁定的内容范围,以及是否共享锁)
  • 例子
    File f = new File("a.txt");
    FileChannel inChannel = new FileInputStream(f).getChannel();  //创建FileInputStream,以该文件输入流创建FileChannel。
    FileChannel outChannel = new FileOutputStream("b.txt").getChannel();  //创建FileOutputStream,以该文件输出流创建FileChannel。
    MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY,0,f.length());  //将FileChannel里的全部数据映射成ByteBuffer。
    Charset charset = Charset.forName("GBK");  //使用GBK的字符集来创建解码器。	
    outChannel.write(buffer);  //将buffer的数据输出。
    buffer.clear();  //恢复buffer的limit、positon位置。
    CharsetDecoder decoder = charset.NewDecoder();  //创建解码器对象。
    CharBuffer charBuffer = decoder.decode(buffer);  //将ByteBuff转换成CharBuffer
    
字符集和Charset
  • Charset类
    方法名说明
    vailableCharsets()该方法可以获取当前JDK所支持的所有字符集。
    ystem类的getProperties()该方法可以访问本地系统的文件编码格式。
    harset cs = Charset.forName(“GBK”);创建Charset对象。
    ewDecoder()返回CharsetDecoder对象,进行解码,把字节序列转换成字符序列。
    ewEncoder()返回CharsetEncoder对象,进行加码,把其他序列转换成字节序列。
    用decode()、encode()可以直接进行转换。
  • 常用字符串别名
    别名说明
    BK简体中文字符集。
    IG5繁体中文字符集。
    SO-8859-1ISO拉丁字母表No.1,也叫做ISO-LATIN-1。
    TF-88位UCS转换格式。
    TF-16BE16位UCS转换格式,Big-endian(最低地址存放高位字节)字节顺序。
    TF-16LE16位UCS转换格式,Little-endian(最高地址存放低位字节)字节顺序。
    TF-1616位UCS转换格式,字节顺序由可选的字节顺序表示来标识。
文件锁
  • FileLock类-用于锁定文件,控制其他进程修改文件。
    用方法说明
    sShared()判断获得的锁是否为共享锁。
    elease()释放文件锁。
Java7的NIO.2
  • 新增Files(操作文件相关)、Paths(平台路径相关)两个工具类。
  • 新增Files类下的方法可以方便的遍历指定目录下的所有文件和子目录。
    walkFileTree(Path start,FileVisitor<? super Path> visitor)  //遍历start下的所有文件和目录。FileVisitResult参数是一个枚举,代表访问之后的后续行为。
    
  • 新增Paths类提供了一个方法监听文件系统的变化。
    regiter(WatchService watcher,WatchEvent.Kind<?>... events)  //用watcher监听该path代表的目录下的变化。events参数指定要监听哪些类型的事件。
    
  • javanio.file.attribute包下提供了大量的工具类。通过这些工具类,可以非常简单地读取、修改文件属性。
  • 例子
    Pths.get("C:/").regiter(watchService,StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_MODIFY);
    //为C:盘根路径注册监听
    
    • WatchService类代表一个文件系统监听服务。
      WatchService watchService = FileSystems.getDefault().newWatchService();  //获取文件系统的WatchService对象
      WatchKey poll()  //获取下一个WatchKey,如果没有WatchKey发生就立即返回null。
      WatchKey take()  //获取下一个WatchKey,如果没有WatchKey发生就一直等待。
      reset()  //重设WatchKey
      
    • WatchKey类:事件相关的类。
      方法名称说明
      ontext()返回产生事件的文件名称。
      ind()返回具体发生的事件名称。

其他

参考资料
  • 《疯狂Java讲义(第4版)》 李刚
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值