JavaSE必备知识点5-IO相关

JavaSE–IO相关知识

IO介绍

IO就是input/output的简称,被称为输入输出流,流动的东西就是数据。因为我们操作的数据不同,这里面又可以分为字节流和字符流。当然,这里我们只是学习很基础的部分,真正实操的时候,我们会有不同的组件来帮助我们更好的去使用。

IO流:程序与数据源之间的流动,是一连串连续动态的数据集合。数据源可为:文件、内存、网络连接、数据库、设备等。

要点:一切以程序为中心

核心类:

io流核心类 说明
File 文件类
InputStream 字节输入流
OutputStream 字节输出流
Reader 字符输入流
Writer 字符输出流
Closeable 关闭流接口
Flushable 刷新流接口
Serializable 序列化接口

但我们需要从硬盘的文件中读取或者输入数据时,我们并不能够直接与硬盘打交道,而是通过操作系统,而关闭流就是通知操作系统可以关闭该“通道”了,至于关不关,是操作系统说了算。

在Java中,一切皆对象。所以当我们从文件中读取数据的时候,我们需要将其进行序列化操作。

流的分类

  • 流向:输入流 输出流 (以当前程序为中心)。
  • 功能:节点流(直接读写)、处理流(包装流)。没有节点流处理流无法发挥作用。
  • 数据:字节流(按照字节读取数据)、字符流(按照字符读取数据,底层仍是字节流,自动搜寻指定码表)。

节点流与处理流:节点流处于io操作的第一线,所有操作必须通过他们进行;处理流可以对其他流进行处理(可提高效率和提高操作灵活性)。

字节流与字符流:区别在于是按照字节还是字符读取数据,如果文件是字符类文件的话,使用字符流确实可以提高速度,当然,其底层还是基于字节流进行操作,自动对码表进行搜寻。

字符集:GBK、UTF-8、Unicode(Java中采用该编码),我们在进行文件的读取的时候一定要选择相同的字符集,否则会乱码。

File类

java不能够直接操作硬盘,只能通过虚拟机与操作系统进行联系。下面给出代码,说明一下简单的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import java.io.File;
import java.io.IOException;
/**
* 熟悉File文件的主要方法
* @author x1aolin
*
*/
public class TestFile3 {
public static void main(String[] args) throws IOException {
String path = "C:/a/P.jpg";
//1、构建File对象
File src = new File(path);
//2、构建File对象
src = new File("C:/a","P.jpg"); //只要拼出来就可以了
src = new File("C:","a/P.jpg"); //只要拼出来就可以了
//3、构建File对象
src = new File(new File("C:/a"),"P.jpg");

//基本信息
System.out.println("得到名称:"+src.getName());
//以创建文件对象时候的路径为准,返回时仅去除掉文件名;若创建时使用相对目录,这里可能返回null
System.out.println("得到父路径: "+ src.getParent());
System.out.println("得到相对路径: "+ src.getPath()); //有时候相对路径==绝对路径
System.out.println("得到绝对路径: "+ src.getAbsolutePath());
System.out.println("得到父对象的名称: "+ src.getParentFile().getName());

//文件状态 不存在 存在:文件夹 文件 boolean
System.out.println("是否存在:"+src.exists());
System.out.println("是否是文件:"+src.isFile());
System.out.println("是否是文件夹:"+src.isDirectory());

//文件长度 文件的字节数大小 long类型 文件夹字节数大小需要另行编写
System.out.println(src.length());

//创建文件 删除文件
src = new File("C:/a/hh.md");
//不存在才会进行创建 而且只能够创建文件而不是文件夹
boolean flag1 = src.createNewFile(); //这里抛出异常,因为创建文件可能会失败
boolean flag2 = src.delete(); //删除已经存在的文件
System.out.println(flag1);
System.out.println(flag2);

//补充: 操作系统的设备名称相同时不能够正确创建!
src = new File("C:/a/con");
System.out.println("与操作系统设备同名时:"+src.createNewFile());
}
}

文件夹相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.x1aolin.io;

import java.io.File;
/**
* mkdir() //创建一个目录,若上级目录不存在,则创建失败
* mkdirs() //创建一个目录,若上级目录不存在,则连带创建上级目录
* list() //列出下级名称
* listFiles() //列出下级File对象
* listRoots //列出所有盘符
* @author x1aolin
*
*/
public class DirDemo02 {
private long len; //文件夹大小
private String path; //文件夹路径
private File src; //源
private int fileCount; //文件个数
private int directoryCount = -1; //文件夹个数 去掉当前文件夹本身

public DirDemo02(String path){
this.path = path;
this.src = new File(path);
count(this.src);
}

public int getFileCount() {
return fileCount;
}

public long getLen() {
return len;
}

public int getDirectoryCount() {
return directoryCount;
}

//统计大小
private void count(File src){
//获取大小
if(null!=src && src.exists()){
if(src.isFile()){
fileCount++;
len += src.length();
}else{
directoryCount++;
for(File temp:src.listFiles()){
count(temp);
}
}
}
}

//打印子孙级目录及其文件名称
public void printName(File src,int deep){

//控制层次感
for(int i = 0;i<deep;i++){
System.out.print("-");
}
System.out.println(src.getName());
if(src == null || !src.exists()){
System.out.println("DirDemo02.printName()");
return;
}else if(src.isDirectory()){//如果是目录
for(File s:src.listFiles()){
printName(s,deep+1);
}
}
}

public static void main(String[] args) {
DirDemo02 d1 = new DirDemo02("C:/JavaWorkspace/IO");
//d1.printName(d1.src, 0);
System.out.println("当前文件夹大小:"+d1.getLen()+
"字节 文件个数:"+d1.getFileCount()+
" 文件夹个数:"+d1.getDirectoryCount());
System.out.println("--------------------------");
DirDemo02 d2 = new DirDemo02("C:/JavaWorkspace/IO/src");
//d2.printName(d2.src, 0);
System.out.println("当前文件夹大小:"+d2.getLen()+
"字节 文件个数:"+d2.getFileCount()+
" 文件夹个数:"+d2.getDirectoryCount());
}
}

字符集

Java字符使用16位的双字节存储,但是在实际文件存储的数据有各种字符集,需要正确操作,否则就有乱码产生。一个字节是8位二进制编码, 字符和字节不太一样,任何一个文字或符号都是一个字符,但所占字节不一定,不同的编码导致一个字符所占的内存不同。 由字符到字节是编码(encode),由字节到字符是解码(decode)。 一套编码规范可以有多种不同的编码方式,不同的编码方式有不同的适应场景。 例如:UTF-8就是一种编码方式,Unicode是一种编码规范。此外,Unicode还有UTF-16,UTF-32这两种编码方式。不同的编码方式节约的空间不同。

感觉上面那段话有点云里雾里的请点击这里,会由十分详细的解释。

常见的字符集: ASCII码 GBK ISO-8859-1 Unicode等。UTF-8是一种变长字符编码,是属于unicode的一种编码方式。下面展示了编码与解码的方式,二者采用编码方式必须相同,否则就会产生乱码!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.x1aolin.io;
import java.io.UnsupportedEncodingException;
/**
* getBytes();
* getBytes(CharSet charset);
* getBytes(String charsetName);
*/
public class TestDecode {
public static void main(String[] args) throws UnsupportedEncodingException {
String a = "性命生命使命";
//编码
byte[] bs = a.getBytes("GBK");//如果什么都不写默认使用工程的字符集
for(byte temp:bs){
System.out.print(temp+" ");
}
System.out.println();
//解码
String b = new String(bs,0,bs.length,"GBK");
System.out.println(b);

//乱码产生原因
//1.字节数不够
String c = new String(bs,0,bs.length-1,"GBK");
System.out.println(c);
//2.字符集的编码方式不统一
String d = new String(bs,0,bs.length,"UTF-8");
System.out.println(d);
}
}

上述代码运行结果如下:

1
2
3
4
-48 -44 -61 -4 -55 -6 -61 -4 -54 -71 -61 -4 
性命生命使命
性命生命使?
???????????

IO四大抽象类

所有的字符都可以转化成字节,但并不是所有的字符都可以转化成字节,比如音频,视频。平常我们人为看得见的字符可以使用字符流,其余类似音频,视频,word,excel等多用字节流。

站在第一线,直接输入输出的称为节点流,在此基础上,为了提升性能,我们对其进行包装,就称为处理流。所以说,没有节点流,那么处理流将无法发挥作用。

抽象类 说明 常用方法
InputStream 字节输入流的父类,数据单位为字节 - int read() - void close()
OutputStream 字节输出流的父类,数据单位为字节 - void write(int) - void flush() - void close()
Reader 字符输出流的父类,数据单位为字符 - int read() - void close()
Writer 字符输出流的父类,数据单位为字符 - void write(String) - void flush() - void close()

Java虚拟机是通过操作系统与文件打交道的,是无权调用垃圾回收机制的,只能够向操作系统提出“建议”。

IO标准步骤

  • 确定源(创建)
  • 选择流(选择具体子类)
  • 操作(读,还是写)
  • 释放资源(系统资源)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.x1aolin.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class IOTest01 {
public static void main(String[] args) {
//1.创建源
File src = new File("C:/JavaWorkspace/IO/abc.txt");
//2.选择流
InputStream is = null;
try {
is = new FileInputStream(src);
//3.操作
int flag = is.read();
while(flag>0){ //到达文件末尾会返回值 -1,表示没有读到东西
System.out.print((char)flag);
flag = is.read(); //读完一次,再次执行该函数会自动读取该文件的下一个字节
}
//3.操作(分段读取)
byte[] flush = new byte[3];//缓冲容器
int len = -1; //接受长度
while((len=is.read(flush))!=-1){
String str = new String(car,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//4.释放
try {
if(is!=null){ //避免还没创建就报异常,这样is就不用关
is.close();
}

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

文件字节流

您的每一份支持将鼓励我继续创作!
-------------本文结束感谢您的阅读-------------