0%

Java-输入/输出笔记

前言

输入/输出(I/O)是我们学习编程的必经之路。往往程序的本质就是对数据进行处理,而一些数据以便持久化而存储于文件中,有些数据用于临时使用存于内存中。换句话说,都知道一般游戏会有存档存储游戏数据,那么了解了这里的I/O,就能实现游戏的存档问题了。

必须说的是,由于Java的输入输出并不是很难,这里简单提一下流的使用,更多是作为笔记,记录各种Java流而已。

流式的输入/输出

流的概念

Java中,数据以流的方式传输。流是从源到目的地的字节的有序序列。流中字节依据先进先出存取数据。
流可以理解为一条单向的河流,一端是源,一头是目的地,数据是通过河流从源流到了目的地。

流的读写过程

读操作:打开流→流中存在数据将其读取出来→关闭流

读操作:打开流→流中存在数据对其写入数据→关闭流

流的分类

Java中的流的各种类操作都在java.io包中。

一般Java中流常指的是字节流。

依据端点分类
  • 节点流:以特定源如磁盘文件,内存某个区域或线程之间的管道(可以同时进行读写操作,进程间通讯)为端点构造的输入/输出流,它是一种最基本的流。
  • 过滤流:以其他已经存在的流为端点构造的输入/输出流(可以在其他流中插入使用),称为过滤流或处理流,它要对其相连的另一个流进行某种转换。
依据传输数据的单位分类
  • 字节流:流数据以8位字节为单位进行读写,以InputStreamOutputStream为基础流。
  • 字符流:流数据以16位字符为单位进行读写,以ReaderWriter为基础类。

流的类层次图

(基本方法就不说了)

1

流的嵌套

往往为了提高流的利用,以及减少流的调用次数,可以嵌套流进行使用。

例如:

  • 输入流嵌套:文件流(FileInputStream)为了提高效率将文件的内容写入缓冲流(BufferedInputStream)然后缓冲流中的数据以数据类型流(DataInputStram)将数据根据基本数据类型写到程序中。
    2
  • 输出流嵌套:从程序中以数据类型流(DataOutputStream)写入缓冲流(BufferedOutputStream)再以文件流(FileOutputStream)将数据写入文件中/节点。
    3

常用输入/输出流类

(这里就写几个比较特别的类)

管道流

管道流可以实现线程间数据的直接连接。管道由管道输出端和管道输入端连接而成,例如以下:
4
其实质是将一个进程的管道输出端连接到另一个进程的管道输入端。

管道流的创建

两种方式:

  • 第一种
    1
    2
    3
    4
    // 创建管道输入流
    PipedInputStream pin = new PipedInputStream();
    // 创建管道输出流,并连接输入流
    PipedOutputStream pout = new PipedOutputStream(pin);
  • 第二种
    1
    2
    3
    PipedInputStream pin = new PipedInputStream(); 			
    PipedOutputStream pout = new PipedOutputStream();
    pin.connect(pout); // 或者 pout.connect(pin);

    文件

    (仅展示比较重要的部分)

创建File对象

  • public File(String pathname):其中的参数可以是绝对路径(例如:"F:\work\sourse\text.txt")也可以是相对路径(一般是相对于当前目录,例如当前目录下的sourse中的text.txt文件:''sourse\text.txt'')
  • 后面的其他参数重构就不提了,主要是要注意,路径的间隔符是\,有时候是\\ 这里需要注意一下。

随机存取文件

RandomAccessFile类概述

5
RandomAccessFile类与同包中的输入/输出不相关。因此一些作用于流的过滤流无法作于RandomAccessFile类,这是它的不便之处。

随机存取类的创建

构造方法:

1
2
3
4
5
public RandomAccessFile(String name,String mode)
throws FileNotFoundException
//以及
public RandomAccessFile(File file,String mode)
throws FileNotFoundException

这里说一下mode的取值:

  • r :以只读方式打开
  • rw:以读写方式打开
  • rwd:以读写方式打开,并要求对文件内容的更新要同步地写到底层存储器
  • rws:与rwd类似,还可以更新文件的元数据(MetaData)

随机存取类的一些操作

随机存取以文件指针来读取文件内容

  • long getFilePointer()
    返回当前文件指针,从文件开头算起 的绝对位置。
  • void seek(long pos)
    将文件指针指到只等位置,pos是相对于文件头的绝对偏移量
  • long length()
  • 返回文件长度。由此可与文件指针长度比较,来判断是否到文件尾
  • int skipBytes(int n)
    从当前位置跳过n个字节,返回实际跳过的字节数

对象的串行化

概念

将java程序中的对象保存在外存中,成为对象永久化。

通过ObjectInputStreamObjectOutputStream对对象读和写。

对象永久化关键在于将其以一种串行格式表示出来,以便后面读取对象能将其重构。所以对java对象的读写过程称为对象序列化(object serialization)。

读写操作

比较简单自行查找API即可。

值得注意的是读操作可以只读取对象的数据,读操作读取对象前,要先构造对象(创建对应的对象)然后用readObject()获取对象,最好进行强制类型转换。

构造可串行化对象的类

也就是需要串行化的类最好实现串行化接口(Serializable),这样其对象就可串行化。

java中的Serializable接口的定义

1
2
3
package java.io;
public interface Serializable{
}; //内部没有东西

实际Serializable是个空接口,目的是标识类为可串行化。

当然可以自行定制扩充,操作就是接口的扩充。

串行化对敏感信息的保护

有时候一些程序会存在一些比较敏感的数据,在串行化的时候可能也会对这些敏感数据进行串行化,这样可能使得数据在流中被截取的风险,或者一些特殊数据如果恢复会造成程序混乱。
为此,可以对这些数据变量定义为private transienttransientstatic是不会进行串行化与反串行化,这样就保证了敏感信息不会写入流中。

串行化的注意事项

  • transient关键字的使用
    对于一些类,存在瞬时状态的变量,则无法保存状态并且这些数据往往没有保存价值。例如一个Tread对象或一个FileInputStream对象,对于这些字段,必须用transient标明,否则编译器报错
  • 串行化对象存储或传输中的安全
    串行化可能涉及存在将对象存在磁盘或网络上发送数据,这样就有安全问题。使用transient关键字来防止串行化。

Java NIO

这是一个针对需要高性能应用而提供的高速I/O操作API。

这个详细,以后有机会等我弄明白了再添加。

参考文献

非常感谢这些文献:
[1]:《Java语言程序设计(第3版)》—郎波—清华大学出版社