File类
- 概述:
- java.io.File类:文件和文件目录路径(即文件夹)的抽象表示形式。File表示的文件或文件夹可以不存在
- File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流
- File对象可以作为参数传递给流的构造器
- 构造器:
-
public File(String pathname)
:以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属user.dir中存储 -
public File(String parent,String child)
:以parent为父路径,child为子路径创建File对象。 -
public File(File parent,String child)
:根据一个父File对象和子文件路径创建File对象
//绝对路径
File file1 = new File("D:\\one");
//相对路径,以当前Java文件所处的工程为起始位置
File file2 = new File("onefile.txt");
//构造器2,表示路径D:\\one\two
File file3 = new File("D:\\one","two");
//构造器3,表示路径D:\\one\two
File file4 = new File(file1,"two");
- 路径分隔符
- windows和DOS系统默认使用“\”来表示,Java中
\
表示转义符,所以用\\
表示 - UNIX和URL使用“/”来表示
- File类提供了一个常量:
public static final String separator
,根据操作系统,动态的提供分隔符
File file5 = new File("d:\\one\\two.txt");
File file6 = new File("d:" + File.separator + "one" + File.separator + "two.txt");
- File获取功能方法
-
public String getAbsolutePath()
:获取绝对路径 -
public String getPath()
:获取路径,即创建该对象时传入构造器的路径 -
public String getName()
:获取所表示的文件或文件夹的名称 -
public String getParent()
:获取上层文件目录路径。若无,返回null,会根据传入构造器的路径来查找,即最后一个路径分隔符之前的路径 -
public long length()
:获取文件长度(即:字节数)。不能获取目录的长度。 -
public long lastModified()
:获取最后一次的修改时间,毫秒值 -
public String[] list()
:获取指定目录下的所有文件或者文件目录的名称数组,如果file所表示的文件夹不存在或是一个文件,则返回null public File[] listFiles()
:获取指定目录下的所有文件或者文件目录的File数组,同上
- File重命名功能方法
-
public boolean renameTo(File dest)
:把文件重命名为指定的文件路径。返回true的条件:file1(调用该方法的File对象)存在,而dest不存在。返回true后,file1不存在,dest存在
- File判断功能方法:
-
public boolean isDirectory()
:判断是否是文件目录 -
public boolean isFile()
:判断是否是文件 -
public boolean exists()
:判断是否存在 -
public boolean canRead()
:判断是否可读 -
public boolean canWrite()
:判断是否可写 -
public boolean isHidden()
:判断是否隐藏
- File创建功能方法:
-
public boolean createNewFile()
:创建该对象对应路径的文件。若文件存在,则不创建,返回false -
public boolean mkdir()
:创建该对象对应路径的文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建 -
public boolean mkdirs()
:创建该对象对应路径的文件目录。如果上层文件目录不存在,一并创建
- File删除功能方法:
-
public boolean delete()
:删除文件或者文件夹删除 - Java中的删除不走回收站
- 要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录
IO流介绍及分类
- 介绍:
- I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等
- Java程序中,对于数据的输入/输出操作以“流(stream)” 的方式进行。java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据
- 输入输出以程序为基准。输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
- 分类
- 按操作数据单位不同分为:字节流(8 bit),一般用于非文本数据;字符流(16 bit),一般用于文本数据
- 按数据流的流向不同分为:输入流,输出流
- 按流的角色的不同分为:节点流,直接从数据源或目的地读写数据;处理流,不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能
- Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的,由这四个类派生出来的子类名称都是以其父类名作为子类名后缀
(抽象基类) | 字节流 | 字符流 |
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
- IO流体系
- InputStream & Reader
- InputStream和 Reader是所有输入流的基类
- InputStream(典型实现:FileInputStream)方法:
-
int read()
:从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。到达流末尾则返回-1 -
int read(byte[] b)
:从输入流中最多将b.length个字节的数据读入byte数组中,不够则有多少读多少。如果到达流末尾则返回-1。否则以int形式返回实际读取的字节数 -
int read(byte[] b, int off, int len)
:从数组的off位置开始,将输入流中最多len个数据字节读入byte数组。不够则有多少读多少。以整数形式返回实际读取的字节数。如果到达末尾则返回 -1 -
close()
:关闭操作
- Reader(典型实现:FileReader)方法:
-
int read()
:读取单个字符。作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff)(2个字节的Unicode码),如果已到达流的末尾,则返回 -1 -
int read(char [] c)
:与InputStream相似,区别在于一个返回的是byte型代表的整数,一个返回的是char类型代表的整数 -
int read(char [] c, int off, int len)
:与InputStream相似,区别在于一个返回的是byte型代表的整数,一个返回的是char类型代表的整数 -
close()
:关闭操作
- 程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源,所以也应该使用try-catch-final而不是throw
- File所代表的文件可以不存在,但是放入流中的File必须存在,否则报错
- OutputStream & Writer
- OutputStream和Writer是所有输出流的基类
- OutputStream方法:
-
void write(int b)
:将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 即写入0~255范围的 -
void write(byte[] b)
:写入byte数组 -
void write(byte[] b, int off, int len)
:将指定 byte 数组中从偏移量 off 开始的len个字节写入此输出流 -
void flush()
:刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标 -
void close()
:需要先刷新,再关闭此流
- Writer方法:
-
void write(int c)
:写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 即写入0 到 65535 之间的Unicode码 -
void write(char[] cbuf)
:写入字符数组 -
void write(char[] buff, int off, int len)
:写入字符数组的某一部分。从off开始,写入len个字符 -
void flush()
:刷新该流的缓冲,则立即将它们写入预期目标 -
void close()
:需要先刷新,再关闭此流 - 因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数
-
void write(String str
:写入字符串 -
void write(String str, int off, int len)
:写入字符串的某一部分。从off开始,写入len长度字符串
- 文件操作的基本四个步骤:
- 实例化Flie类表示计算机上的文件
- 将File对象传入流的实例化
- 利用流对文件进行操作
- 流的关闭
节点流(或文件流)
- FileReader、FileWriter(也是字符流);FileInputStream、FileOutputStream(也是字节流)
- 读(FileReader)
//用read()
public static void main(String[] args) {
//File类实例化
File file1 = new File("D:\\test.txt");
//File传入流的实例化
FileReader fileReader1 = null;
//流的操作
try {
fileReader1 = new FileReader(file1);
int result;
while((result=fileReader1.read()) != -1){
//每一行的末尾都会有一个\n(换行)
//如果文件中有回车手动换行,则每一行末尾有\r \n
System.out.println((char)result);
}
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileReader1!=null) {
try {
fileReader1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//用read(char[] c)
public static void main(String[] args) {
//File类实例化
File file1 = new File("D:\\test.txt");
//File传入流的实例化
FileReader fileReader1 = null;
//流的操作
try {
fileReader1 = new FileReader(file1);
char[] result = new char[5];
int len;
while((len=fileReader1.read(result)) != -1){
//由于最后一次读取很可能读不满,所以用读取的个数来作为判定条件
//第一种
for(int i = 0;i < len;i++){
System.out.print((char)result[i]);
}
//第二种
String resultString = new String(result,0,len);
System.out.print(resultString);
}
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileReader1!=null) {
try {
fileReader1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 写(FileWriter)
public static void main(String[] args) {
File f1 = new File("D:\\test.txt");
FileWriter fileWriter1 = null;
try {
//File文件不存在会自动创建
//默认构造器是在原文件上覆盖
fileWriter1 = new FileWriter(f1);
//表示在原文件上追加
//fileWriter1 = new FileWriter(f1,true);
fileWriter1.write("asd");
}catch (IOException e){
e.printStackTrace();
}finally {
//如果有多个流的关闭,并列写即可,finally中语句一定会被执行
//不必再加finally结构
if (fileWriter1 != null)
try {
fileWriter1.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
- FileInputStream和FileOutputStream
//文件的复制
public static void main(String[] args) {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
File copyFile = new File("D:\\copy.txt");
File pasteFile = new File("D:\\paste.txt");
try {
fileInputStream = new FileInputStream(copyFile);
fileOutputStream = new FileOutputStream(pasteFile);
byte[] result = new byte[1024];
int len = 0;
while((len = fileInputStream.read(result))!= -1){
/**
* 这里读取的是文本文件,如果此时打印文件内容,大概率乱码,因为文件中一个字符很有
* 可能不止一个字节,在读取过程中会出现一个字符被分成多次读取读取。
* 但如果只是复制,则正常,无论分成多少次读取,都会按原先顺序放在一起
*/
//String resultString = new String(result,0,len);
//System.out.print(resultString);
fileOutputStream.write(result,0,len);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(fileInputStream != null){
try {
fileInputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(fileOutputStream != null){
try {
fileOutputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
- 注意点:
- 在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文件将被覆盖;如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖,在文件内容末尾追加内容
- 在读取文件时,必须保证该文件已存在,否则报异常
- 字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt
- 字符流操作字符,只能操作普通文本文件。最常见的文本文件:.txt、.java、.c、.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文本文件
- 字符流处理非文本文件不一定报错,但很可能无法以预期格式打开;
缓冲流(处理流的一种)
- 包括
BufferedInputStream
、BufferedOutputStream
和BufferedReader
、BufferedWriter
- 缓冲流提高了文件读写速度。在内部中会创建一个缓冲区默认大小是8192
- 当读取数据时,数据按块读入缓冲区,直到缓冲区装满了,才重新从文件中读取下一个8192大小数组。其后的读操作则直接访问缓冲区
- 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,缓冲流才会把缓冲区中的数据一次性写到文件里。使用方法
flush()
可以强制将缓冲区的内容全部写入输出流 - 关闭流的顺序和打开流的顺序相反,并且关闭最外层流也会相应关闭内层节点流
-
flush()
方法的使用:手动将buffer中内容写入文件 - 如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷
新缓冲区,关闭后不能再写出
//BufferedInputStream和BufferedOutputStream 复制文件
public static void main(String[] args) {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
File copyFile = new File("D:\\copy.txt");
File pasteFile = new File("D:\\paste.txt");
try {
fileInputStream = new FileInputStream(copyFile);
fileOutputStream = new FileOutputStream(pasteFile);
//处理流作用在文件流之上
bufferedInputStream = new BufferedInputStream(fileInputStream);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
byte[] result = new byte[1024];
int len = 0;
while((len = bufferedInputStream.read(result))!= -1){
bufferedOutputStream.write(result,0,len);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//流的关闭,先关外层,再关内层,即先处理流再文件流
//关闭处理流,处理流中的文件里会被自动关闭
if(bufferedInputStream != null){
try {
bufferedInputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(bufferedOutputStream != null){
try {
bufferedOutputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
//BufferedWriter readLine()方法 复制文件
public static void main(String[] args) {
FileReader fileReader = null;
FileWriter fileWriter = null;
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
/**
*
*/
File copyFile = new File("D:\\copy.txt");
File pasteFile = new File("D:\\paste.txt");
try {
fileReader = new FileReader(copyFile);
fileWriter = new FileWriter(pasteFile);
bufferedReader = new BufferedReader(fileReader);
bufferedWriter = new BufferedWriter(fileWriter);
String data;
while((data = bufferedReader.readLine())!= null){
bufferedWriter.write(data);
//readLine()不读取换行符
//bufferedWriter.write("\n");
bufferedWriter.newLine();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(bufferedReader != null){
try {
bufferedReader.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(bufferedWriter != null){
try {
bufferedWriter.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
转换流
- 包括
InputStreamReader
和OutputStreamWriter
,同时也是字符流、处理流 InputStreamReader
:将字节的输入流按指定字符集转换为字符的输入流(解码)
- 常用构造器:
public InputStreamReader(InputStream in)
,使用默认字符集 -
public InputSreamReader(InputStream in,String charsetName)
,使用指定字符集
OutputStreamWriter
: 将字符的输出流按指定字符集转换为字节的输出流(编码)
- 常用构造器:
public OutputStreamWriter(OutputStream out)
,使用默认字符集 public OutputSreamWriter(OutputStream out,String charsetName)
,使用指定字符集
//用转换流复制文件
public static void main(String[] args) {
File f1 = new File("D:\\test.txt");
File f2 = new File("D:\\test1.txt");
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
InputStreamReader inputStreamReader = null;
OutputStreamWriter outputStreamWriter = null;
try {
inputStream = new FileInputStream(f1);
outputStream = new FileOutputStream(f2);
//解码的字符集要和当时编码的字符集一致
inputStreamReader = new InputStreamReader(inputStream,"utf-8");
//这里以gbk编码,下次解码就需要gbk
outputStreamWriter = new OutputStreamWriter(outputStream,"gbk");
char[] read = new char[10];
int len;
while ((len = inputStreamReader.read(read))!=-1){
//System.out.print(new String(read,0,len));
outputStreamWriter.write(read,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
//只关闭最外层的流即可
if(inputStreamReader != null){
try {
inputStreamReader.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(outputStreamWriter != null){
try {
outputStreamWriter.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
字符编码
- 编码表的由来:计算机只能识别二进制数据,为了计算机可以识别各个国家的文字,就将各个国家的文字用数字来表示,一一对应,形成一张表,即编码表。
- 常见编码表:
- ASCII:美国标准信息交换码,用一个字节的7位可以表示
- ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表示
- GB2312:中国的中文编码表。最多两个字节编码所有字符
- GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
- Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示
- UTF-8:变长的编码方式,可用1-4个字节来表示一个字符
- 说明
- 编码集只是字符表示编号的集合,它不等于具体在计算机内传输的编码方式(在Unicode上体现明显)
- 早期编码集比较简单,计算机容易区别不同的编码集,如GBK等双字节编码方式,用最高位是1表示两个字节一个字符,0表示一个字节一个字符。造器所有的字符集都是和具体编码方案绑定在一起的(即字符集≈编码方式),都是直接将字符和最终字节流绑定死了。
- Unicode比较特殊,如果仿照GBK的编码方式区别两个字节表示的是两个字符还是共同表示一个则会导致表示的字符不够,需要特殊的编码(统称为Unicode编码)方式在计算机上实现
- Unicode字符集与Unicode编码
- Unicode不完美,这里就有三个问题,一个是,英文字母只用一个字节表示就够了,第二个是如何才能区别Unicode和ASCII?计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢?第三个,如果和GBK等双字节编码方式一样,用最高位是1或0表示两个字节和一个字节,就少了很多值无法用于表示字符,不够表示所有字符。因此,Unicode在很长一段时间内无法推广
- 面向传输的众多 UTF(UCS Transfer Format)标准出现了,常见的有UTF-8(每次8个位传输数据)和UTF-16(每次16个位)。以UTF-8为例,在传输过程中,它把两个字节表示一个字符的Unicode字符集变成1-4个字节(后续更新后也有1-6字节),留出一些位置来作为提示计算机的标识位
- 总的来说Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。这些方案也都成为Unicode编码,常见的有UTF-8和UTF-16
- 常见编码集的关系
标准输入、输出流
- 标准输入
System.in
,类型是InputStream
,默认输入设备是键盘 - 标准输出:
System.out
,类型是PrintStream
,是OutputStream
的子类FilterOutputStream
的子类,默认输出设备是显示器 - 重定向:通过System类的setIn,setOut方法对默认设备进行改变
public static void setIn(InputStream in)
public static void setOut(PrintStream out)
//从键盘输入字符串,将读取到的整行字符串转成大写输出。当输入“e”或者“exit”时,退出程序
public static void main(String[] args) {
//借助BufferedReader的readLine方法
//用转换流把字节流转为字符流
InputStreamReader inputStreamReader = null;
BufferedReader reader = null;
try {
inputStreamReader = new InputStreamReader(System.in);
reader = new BufferedReader(inputStreamReader);
while (true){
System.out.println("please input");
String data = reader.readLine();
if("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)){
System.out.println("exit");
break;
}
System.out.println(data.toUpperCase());
}
}catch (IOException e){
e.printStackTrace();
}finally {
if (reader != null){
try {
reader.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
打印流
- 包括
PrintStream
和PrintWriter
,实现将基本数据类型的数据格式转化为字符串输出 -
System.out
返回的是PrintStream
的实例。打印流也通常利用标准输出的重定向功能与System.out
结合使用 - 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
-
PrintStream
和PrintWriter
的输出不会抛出IOException异常 -
PrintStream
和PrintWriter
有自动flush功能 -
PrintStream
打印的所有字符都使用平台的默认字符编码转换为字节
public static void main(String[] args) {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("D:\\test.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
ps = new PrintStream(fos, true);
if (ps != null) {
// 把标准输出流(控制台输出)改成自定义的打印流
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) { // 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50个数据一行
System.out.println(); // 换行
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
}
数据流
- 包括
DataInputStream
和DataOutputStream
,可以操作Java语言的基本数据类型和String的数据 - 数据流有两个类:(用于读取和写出基本数据类型、String类的数据)
-
DataInputStream
,套接在 InputStream子类的流上,常用方法:boolean readBoolean()
、byte readByte()
、char readChar()
、float readFloat()
、double readDouble()
、short readShort()
、long readLong()
、int readInt()
、String readUTF()
、void readFully(byte[] b)
-
DataOutputStream
套接在OutputStream
子类的流上,常用方法:将上述的方法的read改为相应的write即可。 - DataInputStream的读取顺序要与DataOutputStream写入顺序一致
public static void write(){
DataOutputStream dos = null;
try {
// 创建连接到指定文件的数据输出流对象
dos = new DataOutputStream(new FileOutputStream("D:\\test.txt"));
dos.writeUTF("我爱北京天安门"); // 写UTF字符串
dos.flush();//刷新操作,将内存中的数据立即写入文件
dos.writeBoolean(false); // 写入布尔值
dos.flush();
dos.writeLong(1234567890L); // 写入长整数
dos.flush();
System.out.println("写文件成功!");
} catch (IOException e) {
e.printStackTrace();
} finally { // 关闭流对象
try {
if (dos != null) {
// 关闭过滤流时,会自动关闭它包装的底层节点流
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void read(){
DataInputStream dis = null;
try {
dis = new DataInputStream(new FileInputStream("D:\\test.txt"));
//读取的顺序要与写入的顺序一致
String info = dis.readUTF();
boolean flag = dis.readBoolean();
long time = dis.readLong();
System.out.println(info);
System.out.println(flag);
System.out.println(time);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
对象流
- 包括
ObjectInputStream
和OjbectOutputSteam
,用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来 - 序列化:用
ObjectOutputStream
类保存基本类型数据或对象的机制 - 反序列化:用
ObjectInputStream
类读取基本类型数据或对象的机制 -
ObjectOutputStream
和ObjectInputStream
不能序列化static和transient修饰的成员变量 - 对象的序列化机制
- 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
- 序列化是RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是JavaEE 平台的基础
- 对象序列化条件
- 实现如下两个接口之一:
Serializable
或Externalizable
。否则,会抛出NotSerializableException
异常 - 凡是实现
Serializable
接口的类都有一个表示序列化版本标识符的静态变量:private static final long serialVersionUID
,该属性相当于类的标识,在进行反序列化时,JVM会把传来的字节流中的serialVersionUID
与本地相应实体类的serialVersionUID
进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException
)。如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID
可能发生变化导致无法反序列化。故建议,显式声明 - 类中所有熟悉也必须是可序列化的
- 一般来说一个文件只会序列化一种对象,当想要一直读取完文件中全部对象,可以借助try-catch
//简单例子
import java.io.Serializable;
//继承Serializable
public class Student implements Serializable {
//显式声明serialVersionUID
public static final long serialVersionUID = 5132132L;
//如果有属性不想被序列化,可以用static或transient修饰
private String name;
private int age;
//属性必须都可被序列化
private Account account;
public Student(String name, int age, Account account) {
this.name = name;
this.age = age;
this.account = account;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", account=" + account +
'}';
}
}
//属性必须都可被序列化
class Account implements Serializable{
public static final long serialVersionUID = 3131656L;
private String count;
private double money;
public Account(String count, double money) {
this.count = count;
this.money = money;
}
public String getCount() {
return count;
}
public void setCount(String count) {
this.count = count;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"count='" + count + '\'' +
", money=" + money +
'}';
}
}
public static void writObject() {
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("D:\\test.txt")));
objectOutputStream.writeObject(new Student("Tom",8,new Account("621532",100)));
objectOutputStream.flush();
}catch (IOException e){
e.printStackTrace();
}finally {
if (objectOutputStream != null){
try {
objectOutputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
public static void readObject() {
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(new File("D:\\test.txt")));
Object readObject = objectInputStream.readObject();
Student s = (Student)readObject;
System.out.println(s);
}catch (IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (objectInputStream != null){
try {
objectInputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
- 借助try-catch读取完文件中所有对象
public List<E> getFileList(String filename){
File f=new File(filename);
FileInputStream fis = null;
ObjectInputStream ois = null;
List<E>list=new ArrayList<>();
try {
fis = new FileInputStream(f);
ois = new ObjectInputStream(fis);
//用一个死循环不断读取数据,当没数据了,就会报错,
//然后去catch结构中返回list
while (true) {
E e=(E)ois.readObject();
list.add(e);
}
}
catch (IOException e) {
//文件中没数据了就会报错,进入catch结构,在这里返回list
return list;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
随机存取文件流
-
RandomAccessFile
声明在java.io
包下,但直接继承于java.lang.Object
类。并且它实现了DataInput
、DataOutpu
t这两个接口,也就意味着这个类既可以读也可以写 RandomAccessFile
类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件
- 支持只访问文件的部分内容
- 可以向已存在的文件后追加内容
-
RandomAccessFile
对象包含一个记录指针,用以标示当前读写处的位置 RandomAccessFile
类对象可以自由移动记录指针:
-
long getFilePointer()
:获取文件记录指针的当前位置 -
void seek(long pos)
:将文件记录指针定位到 pos 位置
- 构造器:
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
- 创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
r
: 以只读方式打开;rw
:打开以便读取和写入;rwd
:打开以便读取和写入,同步文件内容的更新;rws
:打开以便读取和写入,同步文件内容和元数据的更新 -
rw
模式数据不会被立即写入文件中,rwd
模式数据会被立即写入文件中,如果写入过程出现异常,rw
不会写入任何数据,rwd
已被写入的会保存到文件中 - 如果模式为只读
r
,则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常 - 如果模式为
rw
读写。如果文件不存在则会去创建文件,如果存在则不会创建。当文件存在时,会从头对文件内容进行覆盖,而不是对整个文件覆盖。例如,原文件内容为123456
,此时写入abc
,文件内容会变成abc456
,而不是abc
public static void copyFile(){
RandomAccessFile inputFile = null;
RandomAccessFile outputFile = null;
try {
inputFile = new RandomAccessFile(new File("D:\\copy.jpg"),"r");
outputFile = new RandomAccessFile(new File("D:\\paste.jpg"),"rw");
byte[] data = new byte[1024];
int len;
while ((len = inputFile.read(data)) != -1){
outputFile.write(data,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(inputFile != null){
try {
inputFile.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(outputFile != null){
try {
outputFile.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
public static void insertData(){
RandomAccessFile insertFile = null;
try {
File file = new File("D:\\test.txt");
insertFile = new RandomAccessFile(file,"rw");
//要插入的位置,在此处写入数据
int insertIndex = 3;
//要插入的数据
String insertData = "abc";
//写入数据会覆盖原有数据,所以把角标后的数据要先保存
//插入数据写入后,再写入这个数据
insertFile.seek(insertIndex);
StringBuilder afterInsertData = new StringBuilder((int) file.length());
byte[] data = new byte[10];
int len;
while ((len = insertFile.read(data)) != -1){
afterInsertData.append(new String(data,0,len));
}
//此时文件指针已经到末尾,需要重新回来
insertFile.seek(insertIndex);
insertFile.write(insertData.getBytes());
insertFile.write(afterInsertData.toString().getBytes());
}catch (IOException e){
e.printStackTrace();
}finally {
if(insertFile != null){
try {
insertFile.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
NIO.2中Path、Paths、Files类的使用
Java NIO 概述
- Java NIO(New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作
- Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO
java.nio.channels.Channel
-
FileChannel
:处理本地文件 -
SocketChannel
:TCP网络编程的客户端的Channel -
ServerSocketChannel
:TCP网络编程的服务器端的Channel -
DatagramChannel
:UDP网络编程中发送端和接收端的Channel
- NIO. 2:随着 JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。因为 NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分
Path、Paths和Files核心API
- 早期的Java只提供了一个File类来访问文件系统,但File类的功能比较有限,所提供的方法性能也不高。而且,大多数方法在出错时仅返回失败,并不会提供异常信息
- NIO. 2为了弥补这种不足,引入了Path接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path可以看成是File类的升级版本,实际引用的资源也可以不存在
- 在以前IO操作都是这样写的:
import java.io.File;
File file = new File("index.html");
- 但在Java7 中,我们可以这样写:
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("index.html");
- 同时,NIO.2在java.nio.file包下还提供了Files、Paths工具类,Files包含了大量静态的工具方法来操作文件;Paths则包含了两个返回Path的静态工厂方法
Paths
类提供的静态 get() 方法用来获取 Path 对象:
-
static Path get(String first, String … more)
: 用于将多个字符串串连成路径 -
static Path get(URI uri)
: 返回指定uri对应的Path路径
Path
常用方法:
-
String toString()
:返回调用 Path 对象的字符串表示形式 -
boolean startsWith(String path)
:判断是否以 path 路径开始 -
boolean endsWith(String path)
:判断是否以 path 路径结束 -
boolean isAbsolute()
:判断是否是绝对路径 -
Path getParent()
:返回Path对象包含整个路径,不包含 Path 对象指定的文件路径 -
Path getRoot()
:返回调用 Path 对象的根路径 -
Path getFileName()
:返回与调用 Path 对象关联的文件名 -
int getNameCount()
:返回Path 根目录后面元素的数量 -
Path getName(int idx)
:返回指定索引位置 idx 的路径名称 -
Path toAbsolutePath()
:作为绝对路径返回调用 Path 对象 -
Path resolve(Path p)
:合并两个路径,返回合并后的路径对应的Path对象 -
File toFile()
:将Path转化为File类的对象
Files
(java.nio.file.Files
用于操作文件或目录的工具类)常用方法:
-
Path copy(Path src, Path dest, CopyOption … how)
:文件的复制 -
Path createDirectory(Path path, FileAttribute<?> … attr)
:创建一个目录 -
Path createFile(Path path, FileAttribute<?> … arr)
:创建一个文件 -
void delete(Path path)
:删除一个文件/目录,如果不存在,执行报错 -
void deleteIfExists(Path path)
:Path对应的文件/目录如果存在,执行删除 -
Path move(Path src, Path dest, CopyOption…how)
:将 src 移动到 dest 位置 -
long size(Path path)
:返回 path 指定文件的大小
Files
常用方法:用于判断
-
boolean exists(Path path, LinkOption … opts)
:判断文件是否存在 -
boolean isDirectory(Path path, LinkOption … opts)
:判断是否是目录 -
boolean isRegularFile(Path path, LinkOption … opts)
:判断是否是文件 -
boolean isHidden(Path path)
:判断是否是隐藏文件 -
boolean isReadable(Path path)
:判断文件是否可读 -
boolean isWritable(Path path)
:判断文件是否可写 -
boolean notExists(Path path, LinkOption … opts)
:判断文件是否不存在
Files
常用方法:用于操作内容
-
SeekableByteChannel newByteChannel(Path path, OpenOption…how)
:获取与指定文件的连接,how指定打开方式 -
DirectoryStream<Path> newDirectoryStream(Path path)
:打开path指定的目录 -
InputStream newInputStream(Path path, OpenOption…how)
:获取InputStream对象 -
OutputStream newOutputStream(Path path, OpenOption…how)
:获取OutputStream对象