理解文件
-
文件是相关记录或者放在一起的数据的集合
-
你在windows操作中,经常在硬盘上创建的各种.txt, .doc, .exe, .java, .lib, .mp3等等,都可以称之为文件
-
文件简单的可以理解成,在外设硬盘上面保存数据的一种方式
-
文件一共可以由两部分构成:属性(文件大小,文件名,文件类型等)+内容(就是文件里面放的是什 么)
-
JavaIO ,学什么呢?就学对文件的属性和内容进行操作,而实际写入或者读取的过程,我们称之为IO
File文件操作类
在 java.io 包之中,用 File 类来对文件进行操作(创建、删除、取得信息等)
file类的使用-准备
方法 | 解释 |
---|---|
public File(String pathname) | 创建指定路径文件对象 |
public File(String parent, String child) | 同上,但可指明父路径和子路径 |
为了方便表述,我们先做两方面准备:1. 创建 FileDemo.java , 方便测试, 2. 指定并创建文件测试 路径为 E:\java_code\file
package com.bittech;
public class FileDemo {
public static void main(String[] args){
//注意后的\\,拼接,不是必须的,仅仅为了让同学们理解路径的构成 文件路径+文件名
String path = "E:\\java_code\\file\\";
String name = "demo.txt";
String pathname = path + name;
}
}
File类常用方法-基本文件操作
方法 | 说明 |
---|---|
public boolean exists() | 测试指定路径中文件或者目录是否 存在 |
public boolean isDirectory() | 判定一个文件是目录 |
public boolean isFile() | 判定是否是文件 |
public boolean delete() | 删除文件 |
public boolean createNewFile() throwsIOException | 创建一个新文件 |
实际项目部署环境可能与开发环境不同。那么这个时候路径分隔符是一个很重要的问题。 windows 下使用的是 \ ,而 Unix/Linux 系统下使用的是 / 。所以在使用路径分隔符时都会 采用File类的一个常量 public static final String separator 来描述。
在Java中要进行文件的处理操作是要通过本地操作系统支持的,在这之中如果操作的是同名 文件,就可能出现延迟的问题。(开发之中尽可能避免文件重名问题)
File类常用方法-目录操作
方法 | 解释 |
---|---|
public boolean mkdir() | 创建一个空目录 |
public boolean mkdirs() | 创建目录(无论有多少级父目录,都会创建) |
public String getParent() | 取得父路径 |
public File getParentFile() | 取得父File对象 |
File类常用方法-文件属性操作
方法 | 解释 |
---|---|
public long length() | 取得文件大小(字节) |
public long lastModified() | 最后一次修改日期 |
相对路径/绝对路径
绝对路径:
是指目录下的绝对位置,直接到达目标位置,通常是从盘符开始的路径。完整的描述文件位置的路径就 是绝对路径 。如: E:\javacode\Java8\Test.java 。通常:http://www.sun.com/index.htm也 代表了一个URL绝对路径。
当前目录:
这个参数还可以使用一些常用的路径表示方法,例如 ”.” 或 ”./” 代表当前目录,这个目录也就是 jvm 启动 路径 .所以如下代码能得到当前 .java 文件的当前目录的完整路径:(注意:这个目录也是当前 idea 中的 JVM 的启动路径,我们可以修改这个路径:在 Run->edit Configurations->Working directory 可 以看到)
相对路径:
相对与某个基准目录的路径 。使用相对路径可以为我们带来非常多的便利 。如当前路径为 E:\javacode ,要描述上述路径( E:\javacode\Java8\Test.java ),只需输 入: Java8\Test.java 。此时的路径是相对 E:\javacode 来说的。
File类常用方法-其他操作
方法 | 解释 |
---|---|
public File[] listFiles() | 列出一个目录指定的全部组 |
流
流:
在 Java中所有数据都是使用流读写的。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。
即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象 为各种类,方便更直观的进行数据操作。
- 按照流向分:输入流;输出流
- 按照处理数据的单位分:字节流(8位的字节);字符流(16位的字节)
什么是输入输出流
输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中。
输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。
例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设 备,又可以作为输出设备
什么是字节流,字符流
File类不支持文件内容处理,如果要处理文件内容,必须要通过流的操作模式来完成。 在java.io包中,流分为两种:字节流与字符流
字节流:数据流中最小的数据单元是字节 。
InputStream、OutputStream 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节 。Reader、Writer
字节流
FileInputStream 和 FileOutputStream
public class FileInputStream extends InputStream {}
FileInputStream 从文件系统中的某个文件中获得输入字节。 FileInputStream 用于读取诸如图像数据之类的原始字节流.
方法 | 解释 |
---|---|
FileInputStream(Filefile) | 通过打开与实际文件的连接创建一个 FileInputStream ,该文 件由文件系统中的 File 对象 file 命名 |
FileInputStream(Stringname) | 通过打开与实际文件的连接来创建一个 FileInputStream ,该 文件由文件系统中的路径名 name 命名。 |
public class FileOutputStream extends OutputStream
文件输出流是用于将数据写入到输出流 File 或一个 FileDescriptor 。 文件是否可用或可能被 创建取决于底层平台。
特别是某些平台允许一次只能打开一个文件来写入一个 FileOutputStream (或其他文件写入对 象)。 在这种情况下,如果所涉及的文件已经打开,则此类中的构造函数将失败。
方法 | 解释 |
---|---|
FileOutputStream(File file) | 创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(Stringname) | 创建文件输出流以指定的名称写入文件 |
字节缓冲流 BufferedInputStream 和 BufferedOutputStream
为什么需要有缓冲流?
答:当我们用read()读取文件时,每读一个字节,访问一次硬盘,效率很低 。文件过大时,操作 起来也不是很方便。因此我们需要用到buffer缓存流,当创建buffer对象时,会创建一个缓冲区 数组。当我们读一个文件时,先从硬盘中读到缓冲区,然后直接从缓冲区输出即可,效率会更高。
public class BufferedInputStream extends FilterInputStream
BufferedInputStream 为另一个输入流添加了功能,即缓冲输入和支持 mark 和 reset 方法的功 能。 当创建 BufferedInputStream 时,将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次有多个字 节。 mark 操作会记住输入流中的一点,并且 reset 操作会导致从最近的 mark 操作之后读取的所 有字节在从包含的输入流中取出新的字节之前重新读取。
方法 | 解释 |
---|---|
BufferedInputStream(InputStreamin) | 创建一个 BufferedInputStream 并保存其参数,输 入流 in ,供以后使用。 |
BufferedInputStream(InputStreamin,int size) | 创建 BufferedInputStream 具有指定缓冲区大小,保存其参数,输入流in,供以后使用 |
public class BufferedOutputStream extends FilterOutputStream
该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为 写入的每个字节导致底层系统的调用。
方法 | 解释 |
---|---|
BufferedOutputStream(OutputStreamout) | 创建一个新的缓冲输出流,以将数据写入指定 的底层输出流。 |
BufferedOutputStream(OutputStreamout, int size) | 创建一个新的缓冲输出流,以便以指定的缓冲 区大小将数据写入指定的底层输出流 |
字符流
字符流 FileReader 和 FileWriter
public class FileReader extends InputStreamReader
如果要从文件中读取内容,可以直接使用 FileReader 子类。 FileReader 是用于读取字符流。 要读取原始字节流,请考虑使用 FileInputStream .
方法 | 解释 |
---|---|
FileReader(File file) | 创建一个新的 FileReader ,给出 File 读取。 |
FileReader(String fileName) | 创建一个新的 FileReader ,给定要读取的文件的名称。 |
方法 | 解释 |
---|---|
FileWriter(File file) | 给一个File对象构造一个FileWriter对象。 |
FileWriter(String fileName) | 构造一个给定文件名的FileWriter对象。 |
public class FileWriter extends OutputStreamWriter
如果是向文件中写入内容,应该使用 FileWriter 子类
FileWriter 是用于写入字符流。要编写原始字节流,请考虑使用 FileOutputStream.
方法 | 解释 |
---|---|
FileWriter(File file) | 给一个File对象构造一个FileWriter对象。 |
FileWriter(String fileName) | 构造一个给定文件名的FileWriter对象。 |
字符缓冲流 BufferedReader 和 BufferedWriter
为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率。 BufferedReader 用于加快读取字符的速度, BufferedWriter 用于加快写入的速度。
BufferedReader 和 BufferedWriter 类各拥有 8192个 字符的缓冲区。当 BufferedReader在 读取文 本文件时,会先尽量从文件中读入字符数据并放满缓冲区,而之后若使用read()方法,会先从缓冲区中 进行读取。如果缓冲区数据不足,才会再从文件中读取,使用 BufferedWriter 时,写入的数据并不会 先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。
public class BufferedReader extends Reader
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取
方法 | 解释 |
---|---|
BufferedReader(Reader in) | 创建使用默认大小的输入缓冲区的缓冲字符输入 流。 |
BufferedReader(Reader in, int sz) | 创建使用指定大小的输入缓冲区的缓冲字符输入 流 |
public class BufferedWriter extends Writer
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入
方法 | 解释 |
---|---|
BufferedWriter(Writer out) | 创建使用默认大小的输出缓冲区的缓冲字符输出流。 |
BufferedWriter(Writer out, int sz) | 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。 |
字节流对比字符流
1、字节流操作的基本单元是字节;字符流操作的基本单元为Unicode码元。
2、字节流在操作的时候本身不会用到缓冲区的,是与文件本身直接操作的;而字符流在操作的时候使 用到缓冲区的。
3、所有文件的存储都是字节(byte)的存储,在磁盘上保留的是字节。
4、在使用字节流操作中,即使没有关闭资源(close方法),也能输出;而字符流不使用close方法的 话,不会输出任何内容。
字符字节转换流
有时候我们需要进行字节流与字符流二者之间的转换,因为这是两种不同的流,所以,在进行转换的时 候我们需要用到 OutputStreamWriter 和 InputStreamReader 。
InputStreamReader 是Reader的子类,将输入的字节流转换成字符流。
public class InputStreamReader extends Reader
InputStreamReader 是从字节流到字符流的桥:它读取字节,并使用指定的 charset 将其解码为 字符 。
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
方法 | 解释 |
---|---|
InputStreamReader(InputStream in) | 创建一个使用默认字符集的 InputStreamReader。 |
InputStreamReader(InputStream in,Charset cs) | 创建一个使用给定字符集的 InputStreamReader |
OutputStreamWriter 是Writer的子类,将输出的字符流转换成字节流。
public class OutputStreamWriter extends Writer
OutputStreamWriter 是字符的桥梁流以字节流:向其写入的字符编码成使用指定的字节 charset 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集.
方法 | 解释 |
---|---|
OutputStreamWriter(OutputStream out) | 创建一个使用默认字符编码的 OutputStreamWriter。 |
OutputStreamWriter(OutputStream out,Charset cs) | 创建一个使用给定字符集的 OutputStreamWriter |
小结
流:在 Java中所有数据都是使用流读写的。流是一组有顺序的,有起点和终点的字节集合,是对数据传 输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象 为各种类,方便更直观的进行数据操作。
I/O流的分类
1.按照流向分:输入流;输出流 2.按照处理数据的单位分:字节流(8位的字节);字符流(16位的字节)
3.按照流的功能分: 节点流(低级流):可以从一个特定的IO设备上读/写数据的流。(了解) 处理流(高级流/过滤流):是对一个已经存在的流的连接和封装,通过所封装的流的功能调用实现 数据读/写操作。通常处理流的构造器上都会带有一个其他流的参数。(了解)
流的作用:为数据源和目的地建立一个输送通道。
序列化/反序列化
什么是序列化和反序列化
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
有时候我们想把一些信息持久化保存起来,那么序列化的意思就是把内存里面的这些对象给变成一连串 的字节描述的过程。 常见的就是变成文件
什么时候需要序列化
记住以下几点:
1、把内存中的对象状态保存到一个文件中或者数据库中时候;
2、 用套接字在网络上传送对象的时候;
实现序列化的方式
一定要牢记实现序列化本身是跟语言无关的:
0、Java对象序列化
1、JSON序列化
2、XML
3、Protostuff
4、Hession(它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较 友好。)
5、Dubbo Serialization(阿里dubbo序列化)
6、FST(高性能、序列化速度大概是JDK的4-10倍,大小是JDK大小的1/3左右)
7、自定义协议进行序列化
如何实现序列化(Java对象序列化)
如下代码,我们自己实现一个Person类,在类当中定义成员变量 name , age , sex , stuId , count ,如果要讲Person进行序列化需要实现Serializable接口即可 。
import java.io.Serializable
class Person implements Serializable{
//private static final long serialVersionUID = 1L;
private String name;
private int age;
private String sex;
//transient修饰的变量,不能被序列化
transient private int stuId;
//static修饰的变量,不能被序列化
private static int count = 99;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getSex() {
return sex;
}
public int getStuId() {
return stuId;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setStuId(int stuId) {
this.stuId = stuId;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", stuId=" + stuId +
", count=" + count +
'}';
}
}
public class TestDemo3 {
public static void main(String[] args) throws Exception {
serializePerson();
Person person = deserializePerson();
System.out.println(person.toString());
}
/** * 序列化 */
private static void serializePerson() throws IOException {
Person person = new Person();
person.setName("bit");
person.setAge(10);
person.setSex("男");
person.setStuId(100);
// ObjectOutputStream 对象输出流,将 person 对象存储到E盘的
// person.txt 文件中,完成对 person 对象的序列化操作
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream(new File("e:/person.txt")));
oos.writeObject(person);
System.out.println("person 对象序列化成功!");
oos.close();
}
/** * 反序列化 */
private static Person deserializePerson() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("e:/person.txt")));
Person person = (Person) ois.readObject();
System.out.println("person 对象反序列化成功!");
return person;
}
}
ObjectOutputStream 代表对象输出流: 它的 writeObject(Object obj) 方法可对参数指定的 obj 对象进行序列化,把得到的字节序列写到一个目标输出流中。 ObjectInputStream 代表对象输入流: 它的 readObject() 方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返 回。
person 对象序列化成功!
person 对象反序列化成功!
Person{name='bit', age=10, sex='男', stuId=0, count=99}
1,他实现了对象的序列化和反序列化。
2,transient 修饰的属性,是不会被序列化的 。我们的 stuId 本应是100,可现在为0.注意:内置类型 为对应0值。引用类型为null; 3、静态变量 好像也被序列化了,但是其实并没有,
小结
1、一个类如果想被序列化,那么需要实现一个Serializable接口。 2、类中的静态变量的值是不会被进行序列化的,transient 修饰的属性,是不会被序列化的,内置类型 为对应0值。引用类型为null;
3、在实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值,最好设置为1L,这 个L最好大写来区分,不然小写看起来像是1,不同的 serialVersionUID 的值,会影响到反序列化 .
字符编码
常见的编码有哪些
- GBK、GB2312:表示的是国标编码,GBK包含简体中文和繁体中文,而GB2312只包含简体 中文。也就是说,这两种编码都是描述中文的编码。
- UNICODE编码:java提供的16进制编码,可以描述世界上任意的文字信息,但是有个问 题,如果现在所有的字母也都使用16进制编码,那么这个编码太庞大了,会造成网络传输的 负担。
- ISO8859-1:国际通用编码,但是所有的编码都需要进行转换。
- UTF编码:相当于结合了UNICODE、ISO8859-1,也就是说需要使用到16进制文字使用 UNICODE,而如果只是字母就使用ISO8859-1,而常用的就是UTF-8编码形式。
在以后的开发之中使用的编码只有一个:UTF-8编码 .
乱码产生的本质:编码和解码不统一产生的问题