File类
IO流操作中大部分都是对文件的操作,我们想要实现IO的操作,就必须知道硬盘上文件的表现形式,所以Java就提供了File类供我们来操作文件。
File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。
在计算机系统中,文件是非常重要的存储方式。Java的标准库
java.io
提供了File
对象来操作文件和目录。
要构造一个File
对象,需要传入文件路径:
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
File f = new File("D:\\Windows\\a.exe");
System.out.println(f);
}
}
构造File对象时,既可以传入绝对路径,也可以传入相对路径。
file类的构造方法有3种
file类的功能:
- 创建功能:
- 删除功能:
- 重命名功能:
- 判断功能:
- 获取功能:
- 高级获取功能;
- 过滤器功能:
IO流:用来处理设备之间的数据传输。(上传和下载文件)
java中IO流的基类
输入流:读取数据;输出流:写出数据。
- 字节输入流:InputStream
- 字节输出流:OutputStream
- 字符输入流:Reader
- 字符输出流:Writer
由于基类都是abstrac类,所以要有具体的子类。
在IO流操作中,最后一定要释放资源(close() ):
在计算机中,类似文件、网络端口这些资源,都是由操作系统统一管理的。应用程序在运行的过程中,如果打开了一个文件进行读写,完成后要及时地关闭,以便让操作系统把资源释放掉,否则,应用程序占用的资源会越来越多,不但白白占用内存,还会影响其他应用程序的运行。
- 让流对象变成垃圾,被垃圾回收站回收;
- 通知系统去释放跟该文件相关的资源(因为是系统帮我们创建的文件)。
小细节:在windows系统中换行是\r\n;Linux中是\n;Mac中是\r。
InputStream
InputStream
就是Java标准库提供的最基本的输入流。它位于java.io
这个包里。java.io
包提供了所有同步IO的功能。要特别注意的一点是,InputStream
并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read().
FileInputStream
是InputStream
的一个子类。FileInputStream
就是从文件流中读取数据。
public static void main(String[] args) throws IOException {
//创建对象
FileInputStream in = new FileInputStream("in.txt");
//读取数据:一次读取一个字节
int b = 0;
while((in.read()) != -1) {
System.out.print((char)b);
}
//一次读取一个字节数组
byte[] by = new byte[1024];
int len = 0;
while((len=in.read(by)) != -1) {
System.out.println(new String(by,0,len));
}
//释放资源
in.close();
}
我们还要注意到在读取或写入IO流的过程中,可能会发生错误,例如,文件不存在导致无法读取,没有写权限导致写入失败,等等,这些底层错误由Java虚拟机自动封装成IOException
异常并抛出。因此,所有与IO操作相关的代码都必须正确处理IOException
。
OutputStream
和
InputStream
相反,OutputStream
是Java标准库提供的最基本的输出流。和InputStream
类似,OutputStream
也是抽象类,它是所有输出流的超类。这个抽象类定义的一个最重要的方法就是void write(int b)
用FileOutputStream
可以从文件获取输出流,这是OutputStream
常用的一个实现类。
实现文件复制:
public class CopyDemo {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream in = new FileInputStream("a.txt");
// 创建字节输出流对象
FileOutputStream out = new FileOutputStream("b.txt");
// 创建byte对象数组
byte[] by = new byte[1024];
// read() 最多读取 by.length 个字节
// 返回的是实际读取的个数
// 返回 -1 的时候表示读到 eof,即文件尾
int len = 0;
while ((len = in.read(by)) != -1) {
out.write(by, 0, len);
}
// 释放资源
in.close();
out.close();
}
}
字节缓冲流
缓冲区类(高效类):
- BufferedInputStream
- BufferedOutputStream
BufferedOutputStream(OutputStream out):为什么不传递一个具体的文件或者文件路径,而是传递一个OutputStream对象呢?原因就是:字节缓冲区流仅仅提供字节缓冲区,为高效而设计的,真正的读写操作还是要靠基本的流对象实现。
public class BufferDemo2 {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("a.txt"));
//读数据
byte[] by = new byte[1024];
int len = 0;
while((len=bis.read(by))!=-1) {
System.out.print(new String(by,0,len));
}
//释放资源
bis.close();
}
}
Reader
Windows系统的默认编码可能是GBK。
Reader
是Java的IO库提供的另一个输入流接口。和InputStream
的区别是,InputStream
是一个字节流,即以byte
为单位读取,而Reader
是一个字符流,即以char
为单位读取。java.io.Reader
是所有字符输入流的超类,它最主要的方法是:
public int read() throws IOException;
这个方法读取字符流的下一个字符,并返回字符表示的int
,范围是0
~65535
。如果已读到末尾,返回-1
。
FileReader
是Reader
的一个子类,它可以打开文件并获取Reader
。
Writer
Reader
是带编码转换器的InputStream
,它把byte
转换为char
,而Writer
就是带编码转换器的OutputStream
,它把char
转换为byte
并输出。
FileWriter
就是向文件中写入字符流的Writer
。
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
//封装数据源
FileReader in = new FileReader("a.txt");
//封装目的地
FileWriter out = new FileWriter("b.txt");
//读写数据
char[] ch = new char[1024];
int len = 0;
while((len=in.read(ch)) != -1) {
out.write(ch, 0, len);
out.flush();
}
//关闭
in.close();
out.close();
}
}
String的编码方式
/*
* String 可以看成一个字符序列,
* 可以指定一个编码方式将它编码为字节序列,
* 也可以指定一个编码方式将一个字节序列解码为 String。
*/
public class Test {
public static void main(String[] args) {
//String -- byte[]
String str1 = "无敌敏";
byte[] by = str1.getBytes();
//byte[] -- String
String str2 = new String(by);
System.out.println(str2);
}
}
字符缓冲流
BufferedReader:readLine():一次读取一行数据;
BufferedWriter:newLine():写入一个行分隔符。
public static void main(String[] args) throws IOException {
//封装数据源
BufferedReader in = new BufferedReader(new FileReader("a.txt"));
//封装目的地
BufferedWriter out = new BufferedWriter(new FileWriter("b.txt"));
String line = null;
while((line=in.readLine()) != null) {
out.write(line);
out.newLine();
out.flush();
}
//关闭
in.close();
out.close();
}
打印流
字节打印流:PrintStream
字符打印流:PrintWriter
PrintStream
最终输出的总是byte数据,而PrintWriter
则是扩展了Writer
接口,它的print()
/println()
方法最终输出的是char
数据。
public static void main(String[] args) throws IOException {
//封装数据源
BufferedReader in = new BufferedReader(new FileReader("a.txt"));
//封装目的地(打印流) true:启动自动刷新
PrintWriter out = new PrintWriter(new FileWriter("b.txt"),true);
String line = null;
while((line=in.readLine()) != null) {
//println等价于out.write(line);out.newline();out.flush();
out.println(line);
}
//关闭
in.close();
out.close();
}
序列化
序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]
数组。
为什么要把Java对象序列化呢?因为序列化后可以把byte[]
保存到文件中,或者把byte[]
通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了。
序列化就是将一个对象转换成字节序列,方便存储和传输。
- 序列化:ObjectOutputStream.writeObject()
- 反序列化:ObjectInputStream.readObject()
不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。
一个Java对象要能序列化,必须实现一个特殊的java.io.Serializable
接口,它只是一个标准,没有任何方法需要实现,但是如果不去实现它的话而进行序列化,会抛出异常。(我们把这样的空接口称为“标记接口”)
private class A implements Serializable{
}
序列化和反序列化实例:
public static void main(String[] args) throws IOException, ClassNotFoundException {
A a1 = new A(123, "abc");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.txt"));
objectOutputStream.writeObject(a1);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("a.txt"));
A a2 = (A) objectInputStream.readObject();
objectInputStream.close();
System.out.println(a2);
}
private static class A implements Serializable {
private int x;
private String y;
A(int x, String y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "x = " + x + " " + "y = " + y;
}
}
transient 关键字可以使一些属性不会被序列化。
ArrayList 中存储数据的数组 elementData 是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。
private transient Object[] elementData;