1. File类的使用
1.1 File类的简介
- java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关。
- File类中涉及到关于文件或文件夹的新建、删除、重命名、修改时间、文件大小,但并未涉及到写入或读取文件内容的操作。如果需要访问文件内容本身,则需要使用输入/输出流。
- 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
- 后续File对象常常作为参数传递给流的构造器指明读取或写入的“终点”。
1.2 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对象。
//导入的包有:import org.junit.Test;import java.io.File;
public class FileTest {
@Test
public void test1(){
//构造器1
File file1 = new File("hello.txt");
System.out.println(file1);//hello.txt
//构造器2
File file2 = new File("E:\\Java_work","ShangGuiGu");
System.out.println(file2);//E:\Java_work\ShangGuiGu
//构造器3
File file3 = new File(file2,"hi.txt");
System.out.println(file3);//E:\Java_work\ShangGuiGu\hi.txt
}
}
1.3 File类的常用方法
常用方法1
- public String getAbsolutePath():获取绝对路径
- public String getPath():获取路径
- public String getName():获取名称
- public String getParent():获取上层文件目录路径。若无,返回null
- public long length():获取文件长度(即:字节数)。不能获取目录的长度
- public long lastModified():获取最后一次的修改时间,毫秒值
- public String[] list():获取指定目录下的所有文件或者文件目录的名称数组
- public File[] listFiles():获取指定目录下的所有文件或者文件目录的File数组
- public boolean renameTo(File dest):把文件重命名为指定的文件路径
//导入的包有:import org.junit.Test;import java.io.File;
public class FileTest {
@Test
public void test2(){
File file1 = new File("hello.txt");
System.out.println(file1.getAbsoluteFile());//E:\Java_work\ShangGuiGuProject\code08\hello.txt
System.out.println(file1.getPath());//hello.txt
System.out.println(file1.getName());//hello.txt
System.out.println(file1.getParent());//null
System.out.println(file1.length());//0
System.out.println(file1.lastModified());//0
}
@Test
public void test3(){
File file = new File("E:\\Java_work\\ShangGuiGuProject");
String[] list = file.list();
for(String s : list){
System.out.println(s);
}
File[] files = file.listFiles();
for(File f : files){
System.out.println(f);
}
}
@Test
public void test4(){
File file1 = new File("hello.txt");
File file2 = new File("E:\\Java_work\\ShangGuiGuProject\\hi.txt");
//要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在
boolean renameTo = file1.renameTo(file2);
System.out.println(renameTo);
}
}
常用方法2
- public boolean isDirectory():判断是否是文件目录
- public boolean isFile() :判断是否是文件
- public boolean exists() :判断是否存在
- public boolean canRead() :判断是否可读
- public boolean canWrite() :判断是否可写
- public boolean isHidden() :判断是否隐藏
- public boolean createNewFile():创建文件。若文件存在,则不创建,返回false
- public boolean mkdir():创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
- public boolean mkdirs():创建文件目录。如果上层文件目录不存在,一并创建
//导入的包有:import org.junit.Test;import java.io.File;import java.io.IOException;import java.sql.SQLOutput;
public class FileTest {
@Test
public void test5(){
File file = new File("hello.txt");
System.out.println(file.isDirectory());//false
System.out.println(file.isFile());//false
System.out.println(file.exists());//false
System.out.println(file.canRead());//false
System.out.println(file.canWrite());//false
System.out.println(file.isHidden());//false
}
@Test
public void test6() throws IOException {
File file1 = new File("hi.txt");
if(!file1.exists()){//文件不存在
//文件的创建
file1.createNewFile();
System.out.println("创建成功");
}else{//文件存在
//文件删除
file1.delete();
System.out.println("删除成功");
}
}
@Test
public void test7(){
//文件目录的创建
File file1 = new File("E:\\test1");
boolean mkdir = file1.mkdir();
File file2 = new File("E:\\test2");
boolean mkdirs = file1.mkdirs();
}
2. IO流原理及流的分类
2.1 流的分类
- 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
- 按数据流的流向不同分为:输入流,输出流
- 按流的角色的不同分为:节点流,处理流
2.2 流的体系结构
Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
其中,FileIniputStream、FileOutputStream、FileReader、FileWriter是节点流,其余是处理流。
3. 节点流(或文件流)
3.1 FileReader与FileWriter
FileReader:处理字符
-
将文件读入程序中,并输出到控制台:
- read()理解:返回读入的一个字符,用int表示,如果达到文件末尾,返回-1
- 异常处理:为了保证流资源一定可以执行关闭操作,必须使用try-catch-finally
- 读入的文件一定要存在,否则会报异常
//导入的包有:import org.junit.Test;import java.io.File;import java.io.FileReader;import java.io.IOException; public class FileReaderWriterTest { @Test public void test1(){ FileReader fr = null; try { //1.实例化File类的对象,指明要操作的文件 File file = new File("hello.txt"); //2.提供具体的流 fr = new FileReader(file); //3.数据的读入 int data; while ((data = fr.read()) != -1) { System.out.println((char) data); } }catch (IOException e){ e.printStackTrace(); }finally { //4.关闭流操作 try { if(fr != null)//防止文件未创建时就出现异常,导致无法关闭文件 fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
-
对read()的操作升级,使用read()的重载方法:
- 可以一次读取多个字符,并返回多个字符
- 在输出字符时,需要注意循环条件不是字符数组长度,而是最后读取数组长度,因为最后一次读取的可能不是数组长度
//导入的包有:import org.junit.Test;import java.io.File;import java.io.FileReader;import java.io.IOException; public class FileReaderWriterTest { @Test public void test2(){ FileReader fr = null; try{ //1.File类的实例化 File file = new File("hello.txt"); //2.FileReader流的实例化 fr = new FileReader(file); //3.读入的操作 //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数,如果达到文件末尾,返回-1 char[] cbuf = new char[5]; int len; while((len = fr.read(cbuf)) != -1){ for(int i = 0;i < len;i++){ System.out.println(cbuf[i]); } } }catch (IOException e){ e.printStackTrace(); }finally{ if(fr != null){ //4.资源的关闭 try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
FileWriter:处理字符
-
从内存写出数据,到硬盘的文件里
- 输出操作,对应的File可以不存在,并不会报异常
- 异常的处理方式要用try-catch-finally
- File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建次文件夹
-
File对应的硬盘中的文件如果不存在:
- 如果流使用的构造器是:FileWriter(file,false)/FileWriter(file):对原有文件进行覆盖
- 如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容
//导入的包有:import org.junit.Test;import java.io.File;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;
public class FileReaderWriterTest {
@Test
public void test3(){
FileWriter fw = null;
try {
//1.提供File类的对象,指明写出到的文件
File file = new File("hello1.txt");
//2.提供FileWriter的对象,用于数据的写入
fw = new FileWriter(file,false);
//3.写出的操作
fw.write("hello world!");
fw.write("I hava a dream!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭
if(fw != null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileReader与FileWriter:处理字节
-
将一文件中的内容复制到另一文件
- 输出字符时需要注意使用的是write()的重载方法,每次写出读入的字符数量
//导入的包有:import org.junit.Test;import java.io.*;
public class FileReaderWriterTest {
@Test
public void test4() throws {
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File scrFile = new File("hello.txt");
File destFile = new File("hello1.txt");
//2.创建输入流和输出流的对象
fr = new FileReader(scrFile);
fw = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len;//记录每次读入到cbuf数组中的字符个数
while ((len = fr.read(cbuf)) != -1) {
//每次写出len个字符
fw.write(cbuf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流资源
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.2 FileIniputStream与FileOutputStream
复制图片,与复制文本内容类似,只是使用的类不同
//导入的包有:import org.junit.Test;import java.io.*;
public class FileInputOutputTest {
@Test
public void test1() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1.创建File类的对象
File srcFile = new File("001.png");
File destFile = new File("002.png");
//2.创建流对象
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//3.复制过程
byte[] buffer = new byte[5];
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
//4.关闭流
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4. 缓冲流
4.1 BufferedReader与BufferedWriter
BufferedReader与BufferedWriter是字符流,处理流
缓冲流相较于节点流的好处是可以提高效率,因此叫做缓冲流
下面使用缓冲流中的BufferedReader和BufferedWriter来进行文本的复制
因为属于处理流,所以关闭流操作时,先关闭外面的流,再关闭里面的流
//导入的包有:import org.junit.Test;import java.io.*;
public class BufferedTest {
@Test
public void test2(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
//创建文件和相应的流
br = new BufferedReader(new FileReader(new File("hello.txt")));
bw = new BufferedWriter(new FileWriter(new File("hello2.txt")));
//读写操作
char[] cbuf = new char[1024];
int len;
while((len = br.read(cbuf)) != -1){
bw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在读写操作中还可以使用方法readLine()来一行一行的读取文本,但是这一方法不包括换行
如果需要换行,则需手动添加"\n"或使用方法newLine()
//读写操作
String data;
while((data = br.readLine()) != -1){
bw.write(data + "\n");
//bw.newLine();
}
4.2 BufferedInputStream与BufferedOutputStream
BufferedInputStream与BufferedOutputStream是字符流、处理流
下面使用缓冲流中的BufferedInputStream与BufferedOutputStream来进行图片的复制
//导入的包有:import org.junit.Test;import java.io.*;
public class BufferedTest {
@Test
public void test() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.造文件
File srcFile = new File("001.png");
File destFile = new File("003.png");
//2.造流
//2.1 造节点流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//2.2 造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.复制的细节:读取、写入
byte[] buffer = new byte[10];
int len;
while((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
//要求:先关闭外层的流,再关闭内层的流
//说明:关闭外层流的同时,内层流也会自动的关闭,所以可以省略
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//fos.close();
//fis.close();
}
}
}
5. 转换流
5.1 InputStreamReader
InputStreamReader属于字符流,输入流,处理流
InputStreamReader可以将一个字节的输入流转换为字符的输入流,因此叫做转换流
属于解码过程:字节、字节数组------->字符数组、字符串
//导入的包有:import org.junit.Test;import java.io.*;
public class InputTest {
@Test
public void test1() {
InputStreamReader isr = null;
try {
//FileInputStream的构造器中可以直接传入文件名,会自动生成文件对象
FileInputStream fis = new FileInputStream("hello.txt");
//第二个参数指明字符集(取决于文件保存时使用的字符集),默认情况下为UTF-8,可以不写
isr = new InputStreamReader(fis,"UTF-8");
char[] cbuf = new char[20];
int len;
while((len = isr.read(cbuf)) != -1){
String str = new String(cbuf,0,len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(isr != null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
5.2 OutputStreamWriter
OutputStreamWriter属于字符流,输入流,处理流
OutputStreamWriter可以将一个字符的输出流转换为字节的输出流,因此叫做转换流
属于编码过程:字符数组、字符串------->字节、字节数组
OutputStreamWriter的编译方式与InputStreamReader类似
6. 标准输入、输出流
System.in:标准的输入流,默认从键盘输入
System.out:标准输出流,默认从控制台输出
System.in的类型是InputStream
System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream的子类
重定向:通过System类的setIn,setOut方法对默认设备进行改变。
- public static void setIn(InputStream in)
- public static void setOut(PrintStream out)
//导入的包有:import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;
public class OtherStreamTest {
public static void main(String[] args) {
BufferedReader br = null;
String data = null;
try {
InputStreamReader isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
//键盘输入数据,进行读取
data = br.readLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println(data);
}
}
7. 打印流
实现将基本数据类型的数据格式转化为字符串输出
打印流:PrintStream和PrintWriter:
- 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
- PrintStream和PrintWriter的输出不会抛出IOException异常
- PrintStream和PrintWriter有自动flush功能
- PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter类。
- System.out返回的是PrintStream的实例
- 可以使用System.setOut(),来选择不输出在控制台,而是输出在指定文件
//导入的包有:import org.junit.Test;import java.io.*;
public class OtherStreamTest {
@Test
public void test(){
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("hello5.txt"));
//创建打印流,设置为自动刷新模式
PrintStream ps = new PrintStream(fos,true);
if(ps != null){
//将输出位置选择为指定文件
System.setOut(ps);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
8. 数据流
为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。
数据流可以将数据存入文件或从文件中读取已存入的数据
数据流有两个类:(用于读取和写出基本数据类型、String类的数据):
- DataInputStream和DataOutputStream
- 分别“套接”在InputStream和OutputStream子类的流上
9. 对象流
9.1 使用对象流实现序列化、反序列化
ObjectInputStream和OjbectOutputSteam是对象流
用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点
//导入的包有:import org.junit.Test;import java.io.*;
public class StreamTest {
//序列化
@Test
public void test1(){
ObjectOutputStream oos = null;
try {
//1.造文件造流
oos = new ObjectOutputStream(new FileOutputStream("Object.dat"));
//2.具体操作
oos.writeObject(new String("我爱你中国"));
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos != null){
//3.关闭流
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//反序列化
@Test
public void test2(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("Object.dat"));
Object obj = ois.readObject();
String str = (String)obj;
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.2 自定义类实现序列化、反序列化
除了Java所规定的一些类的对象可序列化,我们也可自定义类实现序列化:
如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的
-
为了让某个类是可序列化的,该类必须实现如下两个接口之一:
- Serializable:经常使用之一接口
- Externalizable:不常使用这一接口
-
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
- private static final long serialVersionUID
- serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。
- 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID可能发生变化。因此必须显式声明
要想实现可序列化,除了要实现Serializable外,还必须保证其内部所有属性也必须是可序列化的(默认情况下,基本数据类型可序列化)
static和transient修饰的成员变量不可序列化
10. 随机存取文件流
10.1 RandomAccessFile类
RandomAccessFile 声明在java.io包下,但直接继承于java.lang.Object类。
RandomAccessFile类实现了DataInput、DataOutput这两个接口,也就意味着这个类既可以作为输入流也可以作为输出流。
-
构造器:
- public RandomAccessFile(File file, String mode)
- public RandomAccessFile(String name, String mode)
-
创建 RandomAccessFile类实例需要指定一个 mode 参数,该参数指定RandomAccessFile的访问模式:
- r:以只读方式打开
- rw:打开以便读取和写入
- rwd:打开以便读取和写入;同步文件内容的更新
- rws:打开以便读取和写入;同步文件内容和元数据的更新
如果模式为只读r,则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。
如果模式为rw读写,如果文件不存在则会去创建文件,如果存在则不会创建,写入时会对原有文件内容逐字覆盖。
RandomAccessFile类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件
//导入的包有:import org.junit.Test;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;
public class RandomTest {
@Test
public void test1(){
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
raf1 = new RandomAccessFile(new File("hello.txt"),"r");
raf2 = new RandomAccessFile(new File("hello1.txt"),"rw");
byte[] buffer = new byte[1024];
int len;
while((len = raf1.read(buffer)) != -1){
raf2.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(raf1 != null){
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(raf2 != null){
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}