1. 什么是 I/O
java中 I/O 操作主要指使用 Java 进行输入,输出操作。Java所有的I/O 机制都是基于数据流进行输入输出,这些数据流表示了字符或者字节数据的流动序列。
Java的I/O 流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法。
Java.io 是大多数面向数据流的输入/输出类的主要软件包
此外,Java也对块传输提供支持,在核心库 java.nio 中采用的便是块 IO。
流IO 简单易用但效率较低。
块IO效率很高但编程比较复杂。
java IO模型:
java I/O 的设计使用到了 Decorator(装饰器)模式,按功能划分Stream, 您可以动态装配这些 Steam ,以获得您需要的功能。
比如,你需要一个具有缓冲的文件输入流,则应当组合使用FIleInputStream 和 BufferedInputStream。
2. 数据流的基本概念
数据流是一串连续不断的数据的集合,数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。
对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。
不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。
“ 流是磁盘或其他外围设备中存储的数据的源点或终点。”
在电脑上的数据有三种存储方式,一种是内存,一种是外存,一种是缓存
比如电脑上的硬盘,磁盘,U盘都是外存,在电脑上游内存条,缓存是在CPU里面的。
外存、内存、缓存的比较
存储量(依次递减); 外存 > 内存 > 缓存
读取速度(依次递减), 缓存 > 内存 > 外存
对于内存和外存的理解,我们可以简单的理解为容器,即外存是一个容器,内存是另一个容器。
在 Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:
标准输入输出, 文件的操作,网络上的数据流,字符串流,对象流,zip文件流等等,Java中将输入输出抽象称为流,就好像水管,将两个容器连接起来
将数据从外存中读取到内存中的称为输入流,将数据从内存写入外存中的称为输出流。
流是一个很形象的概念。
- 当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接
- 类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。
基本流:
一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。
输入流:
程序从输入流读取数据源,数据源包括外界(键盘、文件、网络…)即是将数据源读入到程序的通信通道。
输出流:
程序向输出流写入数据,将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。
为什么设计成数据流呢?
采用数据流的目的就是使得输出输入独立于设备,忽视设备种类的不同。
Input Stream 不关心数据来自何种设备(键盘,文件,网络)
Output Stream 不关心数据的目的是何种设备(键盘,文件,网络)
3. I/O 体系结构
字节流: 后面是 Stream。
字符流: 后面是 Reader/Writer。
在整个Java.io包中最重要的是 5 个类和 1 个接口。
- 五个类: File 、 OutputStream、 InputStream、 Writer、Reader;
- 一个接口: Serializable.
3.1 Java I/O 层次——三个部分
- 流式部分——I/O 的主体部分
- 非流式部分 — 主要包含一些辅助流式部分的类, 如 File类,RandomAccessFile 类 和 FileDescriptor等类
- 其他类—文件读取部分的与安全相关的类, 如 SerializablePermission类 ,以及与本地操作系统相关的文件系统的类,如 FileSystem类和 Win32FileSystem类和 WinNTFileSystem类。
3.1.1 流式部分主要类——I/O 主体类
Java中字符是采用Unicode 标准,一个字符是16位,即一个字符使用两个字节来表示。为此,Java中引入了处理字符的流。
- 对文件进行操作
- FileInputStream (字节输入流)
- FIleOutputStream(字节输出流)
- FileReader(字符输入流)
- FileWriter(字符输出流)
- 对管道进行操作:
- PipedInputStream(字节输入流)
- PipedOutStram(字节输出流)
- PipedReader(字符输入流)
- PipedWriter(字符输出流)
PipedInputStream的一个实例要和 PipedOutputStream的一个实例共同使用,共同完成写入操作,主要用于线程操作。
- 字节/字符数组——在内存中开辟一个字节或字符数组
- ByteArrayInputStream
- ByteArrayOutputSteam
- CharArrayReader
- CharArrayWriter
- Buffered缓冲流:
- BufferedInputStream
- BufferedOutputStream
- BufferedReader
- BufferedWriter
是带缓冲区的处理流,缓冲区的作用是避免每次和硬盘打交道,提高数据访问的效率
- 转化流:
- InputStreamReaderr
- OutputStreamWriter
把字节转化成字符
- 数据流:
- DataInputStream,
- DataOutputStream
- 因为我们若是我们平时输出一个 8 个字节的 long类型或 4 个字节的 float 类型,可以一个字节一个字节输出,也可以转换成字符串输出,但是这样转换浪费时间。
数据流可以直接输出 float 类型或 long类型,提高了数据读写的效率。
- 打印流:
- printStream
- printWriter
一般是打印到控制台,可以进行控制打印的地方。
- 对象流:
- ObjectInputStream
- ObjectOutputStream
把封装的对象直接输出,而不是一个个再转换成字符串再输出。
- 序列化流:
- SequenceInputStream
对象序列化,把对象直接转换成二进制,写入介质中。
3.1.2 非流式部分主要类:
File类 (文件特征与管理): 用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。
在Java.io 包中, 由File 类提供了描述文件和目录的操作和管理办法。
但File 类不是 InputStream 、 OutputStream 或 Reader、Writer的子类,因为它不负责数据的输入输出,而专门用来管理磁盘文件与目录。
作用: File 类主要用于命名文件、查询文件属性和处理文件目录。
- File类的构造函数
(1)File ( String pathname )
例: File f1 = new File("FileText1.txt"); //创建了一个文件对象 f1, f1 所指的文件是在当前目录下(默认目录,因为没有给绝对路径)下创建的FileTest1.txt。
(2)File (URI uri)
(3)File (String parent, String child)
例: File f2 = new File(" D:\\ dir1" , " FielTest2.txt") " // 注意 D:\\ dir1 目录必须提前定义好,否则异常。
(4)File (File parent , String child)
例: File f4 = new File(" E:\\ dir3");
File f5 = new File(f4, " FileTest5.txt"); //在如果 E: \\dir3 目录,则需要使用 f4.mkdir() 先创建。
一个对应于某磁盘文件或目录的File 对象一经创建,就可以通过调用它的方法来获得文件或目录的属性。
1)public boolean exists( ) 判断文件或目录是否存在
2)public boolean isFile( ) 判断是文件还是目录
3)public boolean isDirectory( ) 判断是文件还是目录
4)public String getName( ) 返回文件名或目录名
5)public String getPath( ) 返回文件或目录的路径。
6)public long length( ) 获取文件的长度
7)public String[ ] list ( ) 将目录中所有文件名和目录名保存在字符串数组中返回。
8)public File[] listFiles() 返回某个目录下所有文件和目录的绝对路径,返回的是File数组
9)public String getAbsolutePath() 返回文件或目录的绝对路径
....
File类中还定义了一些对文件或目录进行管理、操作的方法,常用的方法有:
1) public boolean renameTo( File newFile ); 重命名文件
2) public void delete( ); 删除文件
3) public boolean mkdir( ); 创建目录
4)public boolean createNewFile(); 创建文件
例子: 输出一个目录的所有文件名(目录可能是多级目录,如 a 目录中有 b、c目录。。。)
——FileUtils.java——
public class FileUtils {
public static void listDir(String dir) throws IOException {
File file = new File(dir);
//传进来的可能不是一个目录
if (!file.isDirectory()) {
throw new IOException(dir+"不是目录");
}
//传进来的可能是一个错误的路径
if (file == null) {
throw new IOException("没有此路径");
}
File[] files = file.listFiles();
for (File f : files) {
//有可能是一个多级目录,递归调用
if (f.isDirectory()) {
listDir(f.getAbsolutePath());
//是文件就直接输出该文件的绝对路径
}else {
System.out.println(f.getAbsolutePath());
}
}
}
}
——Main.java——
public class Main {
public static void main(String[] args) throws IOException {
FileUtils.listDir("E:\\ssh");
}
}