把知识记在小本本上

将零散的知识点放在一个集中的地方,不断递归重构,形成一套为己所用的知识系统。

博客首页 | 小本本首页

File

概述和构造方法

1
2
3
4
5
6
7
8
File类的概述
文件和目录(路径名)的抽象表示形式
仅仅是一个路径的表示,不代码具体的事物一定是存在的。

构造方法
public File(String pathname):根据路径得到 File 对象
public File(String parent,String child):根据一个目录和一个子文件(或目录)得到一个 File 对象
public File(File parent,String child):根据一个父 File 对象和一个子文件(或目录)得到一个File对象

成员方法

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
创建功能
public boolean createNewFile():创建文件,存在则不创建
public boolean mkdir():创建文件夹,存在则不创建,要想在某个目录下创建内容,该目录必须存在。
public boolean mkdirs():创建多级文件夹,父文件夹不存在就创建,存在则不创建。

删除功能
public boolean delete():Java 中的删除不走回收站,还要注意,文件夹里面有东西是删除不了的。

重命名功能
public boolean renameTo(File dest):注意这个和 mv 命令有些像,前后路径不一样,做移动并改名。

判断功能
public boolean isDirectory():判断是否位文件
public boolean isFile():判断是否为文件
public boolean exists():判断是否存在
public boolean canRead():判断是否可读
public boolean canWrite():判断是否可写
public boolean isHidden():判断是否隐藏

基本获取功能
public String getAbsolutePath():获取绝对路径
public String getPath():获取相对路径
public String getName():获取名称
public long length():获取长度,字节数
public long lastModified():获取最后一次修改时间,返回的是毫秒值。

高级获取功能
public String[] list()
public File[] listFiles()

文件过滤器的使用栗子🌰

需求:判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称

实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class FileDemo2 {
public static void main(String[] args) {
// 封装 e:\\ 目录
File file = new File("e:\\");

// 获取该目录下所有文件或者文件夹的 String 数组
// public String[] list(FilenameFilter filter)
String[] strArray = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir, name).isFile() && name.endsWith(".jpg");
}
});

// 遍历输出
for (String s : strArray) {
System.out.println(s);
}
}
}

IO流

概述和分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
概述
IO流用来处理设备之间的数据传输
上传文件和下载文件
Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中

分类(IO流的分类如果没有明确指定,默认指按照数据类型分)
按照数据流向
输入流 读入数据
输出流 写出数据
按照数据类型
字节流
字节输入流
字节输出流
字符流
字符输入流
字符输出流
  • 什么情况下使用哪种流呢?

    如果数据所在的文件通过 windows 自带的记事本打开并能读懂里面的内容,就用字符流。其他用字节流。

    如果你什么都不知道,就用字节流。

    计算机如何识别是中文还是英文呢?

    计算机是如何识别什么时候该把两个字节转换为一个中文呢?
    在计算机中中文的存储分两个字节:

    • 第一个字节肯定是负数。
    • 第二个字节常见的是负数,可能有正数。但是没影响。

    看下面的栗子🌰:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Test {
    public static void main(String[] args) {
    String en = "ANDYOU";
    byte[] en_b = en.getBytes();
    System.out.print("en_b : ");
    for (byte b : en_b) {
    System.out.print(b + " ");
    }

    System.out.println("\n------------");

    String zh = "爱你";
    byte[] zh_b = zh.getBytes();
    System.out.print("zh_b : ");
    for (byte b : zh_b) {
    System.out.print(b + " ");
    }
    }
    }

    输出:

    1
    2
    3
    en_b : 65 78 68 89 79 85 
    ------------
    zh_b : -25 -120 -79 -28 -67 -96

常用基类

1
2
3
4
5
6
7
字节流的抽象基类:
InputStream ,OutputStream。
字符流的抽象基类:
Reader , Writer。
由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader。

字节流

1
2
3
4
5
6
java.lang.Object
|---java.io.InputStream
|---java.io.OutputStream

public abstract class InputStream; //此抽象类是表示字节输入流的所有类的超类。
public abstract class OutputStream; //此抽象类是表示输出字节流的所有类的超类。

字节缓冲流

1
2
3
4
5
6
7
8
9
10
11
java.lang.Object
|---java.io.InputStream
|---java.io.FilterInputStream
|---java.io.BufferedInputStream
|---java.io.BufferedOutputStream

public class BufferedInputStream;
// 在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。

public class BufferedOutputStream;
// 该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。

字符流

1
2
3
4
5
6
java.lang.Object
|---java.io.Reader
|---java.io.Writer

public abstract class Reader; // 用于读取字符流的抽象类。
public abstract class Writer; // 写入字符流的抽象类。

字符缓冲流

1
2
3
4
5
6
7
8
9
10
11
12
13
java.lang.Object
|---java.io.Reader
|---java.io.BufferedReader
|---java.io.BufferedWriter

public class BufferedReader; // 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
public class BufferedWriter; // 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

特殊功能,根据系统来读取或写入换行符
BufferedWriter
void newLine()
BufferedReader
String readLine()

注意事项

1
2
3
4
5
字符流操作要注意的问题
flush()的作用
flush()和close()的区别
clone()关闭流对象,但是要刷新一下缓冲区,关闭后流对象不可用。
flush()刷新流对象,刷新完流对象还可以用。

转换流

转换流出现的原因及思想

1
2
由于字节流操作中文不是特别方便,所以,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
编码表
由字符及其对应的数值组成的一张表
常见编码表
ASCII:美国标准信息交换码。
用一个字节的7位可以表示。

ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。

GB2312:中国的中文编码表,简体中文。

GBK:中国的中文编码表升级,融合了更多的中文文字符号。

GB18030:GBK的取代版本

BIG-5码 :通行于台湾、香港地区的一个繁体字编码方案,俗称“大五码”。

Unicode:国际标准码,融合了多种文字。
所有文字都用两个字节来表示,Java语言使用的就是unicode

UTF-8:最多用三个字节来表示一个字符。

UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:
它将Unicode编码为00000000-0000007F的字符,用单个字节来表示
它将Unicode编码为00000080-000007FF的字符用两个字节表示
它将Unicode编码为00000800-0000FFFF的字符用3字节表示

转换流概述

1
2
3
4
5
6
7
OutputStreamWriter 字符输出流
public OutputStreamWriter(OutputStream out)
public OutputStreamWriter(OutputStream out,String charsetName)

InputStreamReader 字符输入流
public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in,String charsetName)

转换流简化

1
2
3
转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流提供了对应的子类。
FileWriter
FileReader

其他流

数据操作流

1
2
3
操作基本数据类型的类:
DataInputStream
DataOutputStream

内存操作流

1
2
3
4
5
6
7
8
9
10
11
内存操作流一般用于处理临时信息,因为临时信息不需要保存,使用后就可以删除。

操作字节数组
ByteArrayInputStream
ByteArrayOutputStream
操作字符数组
CharArrayReader
CharArrayWrite
操作字符串
StringReader
StringWriter

打印流

1
2
3
4
5
6
7
8
9
打印流概述
字节流打印流 PrintStream
字符打印流 PrintWriter

打印流特点
只能操作目的地,不能操作数据。
可以操作任意类型的数据。
如果启动了自动刷新,能够自动刷新。
可以操作文件的流

标准输入输出流

1
2
3
4
5
System类中的字段:in,out。
它们各代表了系统标准的输入和输出设备。
默认输入设备是键盘,输出设备是显示器。
System.in的类型是InputStream.
System.out的类型是PrintStream是OutputStream的子类FilterOutputStream 的子类.

随机访问流

1
2
3
4
5
6
7
8
RandomAccessFile概述
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。支持对随机访问文件的读取和写入。

访问模式
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。

合并流

1
2
3
4
5
SequenceInputStream类可以将多个输入流串流在一起,合并为一个输入流,因此,该流也被称为合并流。

SequenceInputStream的构造方法
SequenceInputStream(InputStream s1, InputStream s2);
SequenceInputStream(Enumeration<? extends InputStream> e);

序列化流

1
2
3
4
5
6
7
8
9
将对象写入文件,传输。。。
对象序列化是将对象状态转换为可保持或传输的过程。一般的格式是与平台无关的二进制流,可以将这种二进制流持久保存在磁盘上,也可以通过网络将这种二进制流传输到另一个网络结点。
对象反序列化,是指把这种二进制流数据还原成对象。

序列化流
ObjectOutputStream

反序列化流
ObjectInputStream
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
序列化操作问题
为什么要实现序列化?
如何实现序列化?
序列化数据后,再次修改类文件,读取数据会出问题,如何解决呢?

使用transient关键字声明不需要序列化的成员变量
NotSerializableException:未序列化异常

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
该接口居然没有任何方法,类似于这种没有方法的接口被称为标记接口。

java.io.InvalidClassException:
cn.itcast_07.Person; local class incompatible:
stream classdesc serialVersionUID = -2071565876962058344,
local class serialVersionUID = -8345153069362641443

为什么会有问题呢?
Person类实现了序列化接口,那么它本身也应该有一个标记值。
这个标记值假设是100
开始的时候:
Person.class -- id=100
wirte数据: oos.txt -- id=100
read数据: oos.txt -- id=100

现在:
Person.class -- id=200
wirte数据: oos.txt -- id=100
read数据: oos.txt -- id=100
我们在实际开发中,可能还需要使用以前写过的数据,不能重新写入。怎么办呢?
回想一下原因是因为它们的id值不匹配。
每次修改java文件的内容的时候,class文件的id值都会发生改变。
而读取文件的时候,会和class文件中的id值进行匹配。所以,就会出问题。
但是呢,如果我有办法,让这个id值在java文件中是一个固定的值,这样,你修改文件的时候,这个id值还会发生改变吗?
不会。现在的关键是我如何能够知道这个id值如何表示的呢?
不用担心,你不用记住,也没关系,点击鼠标即可。
你难道没有看到黄色警告线吗?

我们要知道的是:
看到类实现了序列化接口的时候,要想解决黄色警告线问题,就可以自动产生一个序列化id值。
而且产生这个值以后,我们对类进行任何改动,它读取以前的数据是没有问题的。

注意:
我一个类中可能有很多的成员变量,有些我不想进行序列化。请问该怎么办呢?
使用transient关键字声明不需要序列化的成员变量

Properties 集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Properties:属性集合类。是一个可以和IO流相结合使用的集合类。
Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
是Hashtable的子类,说明是一个Map集合。

Properties作为Map集合的使用

Properties的特殊功能
public Object setProperty(String key,String value):添加元素
public String getProperty(String key):获取元素
public Set<String> stringPropertyNames():获取所有键的集合

Properties和IO流的结合使用
Properties集合!
public void load(Reader reader):把文件中的数据读取到集合中
public void store(Writer writer,String comments):把集合中的数据存储到文件中

NIO

NIO其实就是新IO的意思。

JDK4出现NIO。新IO和传统的IO有相同的目的,都是用于进行输入输出的,但新IO使用了不同的方式来处理输入输出,采用内存映射文件的方式,将文件或者文件的一段区域映射到内存中,就可以像访问内存一样的来访问文件了,这种方式效率比旧IO要高很多,但是目前好多地方我们看到的还是旧IO的引用,所以我们仍以旧IO为主,知道NIO即可。

JDK7的IO改进

  • Path:与平台无关的路径。

  • Paths:包含了返回Path的静态方法。

    public static Path get(URI uri):根据给定的URI来确定文件路径。

  • Files:操作文件的工具类。提供了大量的方法,简单了解如下方法

    public static long copy(Path source, OutputStream out) :复制文件

    public static Path write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption… options):

    把集合的数据写到文件。