目录
IO流
FILE类
FILE类的一个对象代表一个文件或者一个文件目录
路径分隔符
- 路径中的每级目录之间用一个路径分隔符隔开
- 路径分隔符和系统有关:
- windows和DOS系统默认使用’\’
- UNIX和URL使用’/’
- Java支持跨平台运行,因此路径分隔符要慎用
- 为了解决这个隐患,File类提供了一个常量File.seperator
构造器
@Test
public void test1()
{
// 构造器1
File file = new File("Hello.txt");
File file2 = new File("e:\\STUDY\\java");
// which equals to
File file3 = new File("e:"+File.separator+"STUDY"+File.separator+"java");
File file4 = new File("e:\\STUDY\\java","day01.md");
System.out.println(file4);
File file5 = new File(file3,"day01.md");
System.out.println(file5);
//e:\STUDY\java\day01.md
//e:\STUDY\java\day01.md
}
常用功能
获取相关信息
@Test
public void test2()
{
File file = new File("hello.txt");
System.out.println(file.getAbsoluteFile());
System.out.println(file.getPath());
System.out.println(file.getName());
System.out.println(file.getParent());
System.out.println(file.length());
System.out.println(new Date(file.lastModified()));
//E:\Java\code\JavaSE\senior_java\hello.txt
//hello.txt
//hello.txt
//null -- 只有构造器中是绝对路径的才能看到父目录
//11 -- 文件中的字节长度
//Wed Sep 09 15:30:11 CST 2020 -- 返回时间戳
}
对文件夹的操作
@Test
public void test3()
{
File file = new File("E:\\BUPT\\java");
String[] list = file.list();
for(String str : list)
{
System.out.println(str);
}
File[] files = file.listFiles();
for (File f : files)
{
System.out.println(f);
}
// day1.md
// day2.md
// ...
// E:\BUPT\java\day1.md
// E:\BUPT\java\day2.md
// ...
}
重命名
boolean file1.ranameTo(file2)
注意:file1必须在硬盘中存在,file2必须在硬盘中不存在,否则操作失败,返回false
@Test
public void test4()
{
File file1 = new File("hello.txt");
File file2 = new File("hi.txt");
boolean b = file2.renameTo(file1);
System.out.println(b);
// true if hello.txt exists and hi.txt not
}
判断相关信息
public boolean isDirectory()
:判断是否是目录public boolean isFile()
:判断是否是文件public boolean exists()
:判断是否存在public boolean canRead()
:判断是否可读public boolean canWrite()
:判断是否可写
创建、删除功能
public boolean createNewFile()
:创建新的文件,如果文件存在则不创建,返回falsepublic boolean mkdir()
:创建文件目录,如果目录存在则不创建,如果上层目录不存在也不创建public boolean mkdirs()
:创建文件目录,如果上层文件不存在一并创建public boolean delete()
:删除文件或文件夹。注意:Java删除不走回收站!
IO流原理及流的分类
IO是Input/Output的缩写,用于处理设备之间的数据传输。Java中数据的输入输出以流的方式进行。java.io包下提供了各种 流类和接口,并通过标准的方法输入或输出数据。
流的分类
- 操作数据单位不同分为:字节流(8bit),字符流(16bit)
- 数据量的流向不同分为:输入流、输出流
- 按流的角色的不同分为:节点流、处理流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
深色是比较重要的。主要学习访问文件的四个流,具体的操作方法每个流都差不多,剩下的学习功能即可。
文件流
FileReader
首先注意的几点:
- IO流这些物理上的流JVM无法自行关闭,必须手动关闭!
- read()的理解:返回读入的一个字符,如果达到文件末尾返回-1
- 异常的处理:如果使用throws,发现错误就会理解创建异常对象然后结束,此时有可能IO流对象还未被关闭,所以需要使用try-catch-finally来关闭IO流而非使用throws
- 打开的文件一定要存在,不然会报错
public void test() throws IOException {
FileReader fr = null;
try {
File file = new File("hello.txt");
fr = new FileReader(file);
int data;
while((data = fr.read())!=-1)
{
System.out.print((char)data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fr != null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
重载read()方法——一次读多个字符
public void test2() {
FileReader fr = null;
try {
// 1. File类的初始化
File file = new File("hello.txt");
// 2. io流类的初始化
fr = new FileReader(file);
// 3. 读文件操作
// 这里使用重载的read(char[] buf)
int len;
char[] cbuf = new char[5];
while ((len = fr.read(cbuf)) != -1) {
// System.out.println("错误的写法1");
// for (int i = 0; i < cbuf.length; i++)
// System.out.print(cbuf[i]);
// // helloworld123ld
//
// System.out.println("\n错误的写法2:");
// String str = new String(cbuf);
// System.out.print(cbuf);
// // helloworld123ld
//
// System.out.println("\n正确的写法1");
// for (int i = 0; i < len; i++)
// System.out.print(cbuf[i]);
// // helloworld
System.out.println("\n正确的写法2");
String str2 = new String(cbuf, 0, len);
System.out.print(str2);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4. 关闭IO流
if(fr != null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileWriter
注意:
- 若文件不存在,则创建该文件
- 若文件存在,会覆盖该文件
FileWriter(File file,boolean append)
,如果使用该构造器,append为false则覆盖,为true则添加
public void test3(){
FileWriter fw = null;
try {
// 该文件不存在,则会创建文件
File file = new File("hi.txt");
fw = new FileWriter(file);
fw.write("I hava a dream! ");
fw.write("This is a Test");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileReader和FileWriter实现文件复制
注意:FileReader是字符流形式,不能用于图片、视频的复制,如果想实现后者的复制,必须换成FileOutputStream,FileWriter同理。
结论:对于文本文件(.txt.doc.c.java)用字符流处理,非文本文件用字节流(.jpg.mp4.mp3)处理
@Test
public void test5()
{
FileReader fr = null;
FileWriter fw = null;
try {
File srcfile = new File("hello.txt");
File destfile = new File("hello2.txt");
fr = new FileReader(srcfile);
fw = new FileWriter(destfile);
int len;
char []cbuf = new char[5];
while((len = fr.read(cbuf))!=-1)
{
String str = new String(cbuf,0,len);
fw.write(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fr!=null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fw !=null)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
缓冲流(处理流的一种)
缓冲流是为了提高读写效率
注意:
- 新建缓冲流的前提是新建了一个节点流
- 关闭处理流时,只用关闭外层流,内层流自动关闭
- 使用缓冲流的速度远大于节点流(原因:内部提供缓冲区,大小为8092,等到缓冲区填满了再flush出去)
@Test
public void test6() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
// 1.造文件
File f1 = new File("UI1.png");
File f2 = new File("UI2.png");
// 2.造流
// 2.1 造节点流
FileInputStream fis = new FileInputStream(f1);
FileOutputStream fos = new FileOutputStream(f2);
// 2.2 造处理流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
// 复制
int len;
byte[] bytes = new byte[10];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
转换流
分辨属于什么流看后缀,转换流属于字符流
- InputStreamReader:将一个字节的输入流转换为字符的输入流
- OutputStreamWriter:将一个字符的输出流转换为字节的输出流
- 作用:提供字节流与字符流之间的转换
- 解码:字节、字节数组 —>字符数组、字符串
- 编码:字符数组、字符串 —>字节、字节数组
字符集
-
ASCII:美国标准信息交换码,用一个字节的7位可以表示
-
ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位可以表示
-
GB2312:中国的中文编码表,最多两个字节编码所有字符
-
GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节
-
Unicode:国际校准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码,所有的文字字符都用两个字节表示
-
UTF-8:变长的编码方式,可用1-4个字节来表示一个字符
对象流
序列化机制
允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取了这种二进制流,就可以恢复成原来的Java对象。
对象的序列化
用于存储和读取基本数据类型数据或对象的处理流,可以把Java中的对象写入到数据源中也能还原回来。
ObjectInputStream
和ObjectOutputStream
- 序列化:用
ObjectInputStream
类保存基本类型数据或对象的机制 - 反序列化:用
ObjectOutputStream
类读取基本类型数据或对象的机制
static和transient修饰的成员变量不能被序列化
IO流的框架都是一样的,对象流也差不多
自定义类序列化的注意事项:
- 注意进行序列化的对象所在的类必须要实现Serializable接口,尤其是自定义类
- 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量
private static final long serialVersionUID
- 如果不显式设置这个ID也可,但是系统会隐式给你一个ID,当你修改了类中的变量之后,ID会改变,可能会造成先前序列化后的文件反序列化回来的结果与原来不一致。所以一般自己显示定义一个常量。
- 自定义类的所有内部属性必须可序列化。(默认情况下,基本数据类型可序列化)
public class ObjectStreamTest {
// 序列化
@Test
public void test2()
{
ObjectOutputStream oos = null;
try {
File file = new File("object.dat");
FileOutputStream fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(new User(18,"Bill"));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos != null)
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 反序列化
@Test
public void test1()
{
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = ois.readObject();
User user = (User) obj;
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null)
{
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null)
{
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}