17 File
含义:
file是文件和目录路径的抽象表示
File只关注文件本身的信息,而不能操作文件里面的内容
File类 – 表示文件或文件夹,不能对文件里的数据进行操作
对文件里的数据进行操作的是:IO流
17.1 File对象
绝对路径:
File file = new File("D:\\A\\1.txt");
相对路径:
File file = new File("file01\\test.txt");
浅显来看,绝对路径是有C:这样的硬盘路径,而相对路径则没有。
绝对路径是基于整个操作系统来说的,而相对路径则是要相对于某个绝对路径来说(项目的路径)。
注:
17.2 获取文件信息
简单示例:
File file = new File("D:\\A\\1.txt");
System.out.println("获取文件绝对路径:"+file.getAbsolutePath());
System.out.println("获取文件名:"+file.getName());
System.out.println("文件是否可读:"+file.canRead());
System.out.println("文件是否可写:"+file.canWrite());
System.out.println("文件是否隐藏:"+file.isHidden());
System.out.println("获取文件长度:"+file.length());
System.out.println("判断是否是文件:"+file.isFile());
System.out.println("判断是否是文件夹:"+file.isDirectory());
System.out.println("判断文件是否存在:"+file.exists());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("文件最后修改的时间:"+sdf.format(file.lastModified()));
如果相对路径下的文件不存在,可以使用createNewFile();方法创建文件。
File file = new File("file01\\test.txt");
System.out.println("获取文件绝对路径:"+file.getAbsolutePath());
//判断该文件是否存在,如果不存在就创建
if(!file.exists()){
file.createNewFile();//创建文件方法。
}
17.3 创建文件夹和文件
创建文件夹:mkdirs();
创建文件:createNewFile();
示例:(通过路径判断是否存在文件,如果不存在就创建。)
File file = new File("file01\\file02\\test1.txt");
//获取父路径对象
File parentFile = file.getParentFile();
//如果文件夹路径不存在,就创建对应路径的文件夹
if(!parentFile.exists()){
parentFile.mkdirs();
}
//判断文件是否存在,不存在就创建文件
if(!file.exists()){
file.createNewFile();
}
17.4 检索文件目录
17.4.1 遍历文件夹(不考虑子文件夹)
获取当前文件夹下的所有文件(包括文件夹):listFiles();
File file = new File("D:\\Test");
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f.getName()+" -- "+f.canRead());
}
拓展:使用过滤器检索遍历(查询当前文件夹下的txt文件)
File file = new File("D:\\Test");
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File f = new File(dir,name);
if(f.isFile()&&f.getName().endsWith(".txt"))
return true;
else
return false;
}
});
//经过过滤器,在files中装入的都是符合条件的File对象。
for (File f : files) {
System.out.println(f.getName()+" -- "+f.canRead());
}
17.4.2 检索文件(考虑子文件夹)
使用检索文件时,常常需要遍历整个文件夹下的文件(包括子文件目录),此时需要使用递归的方法。
public class Test {
public static void main(String[] args) {
File file = new File("file01");
method(file, ".txt");
}
public static void method(File file,String str){
File[] files = file.listFiles();
for (File f : files) {
//判断是否是文件夹
if(f.isDirectory()){
method(f,str);//如果是文件夹,继续调用method方法。将文件夹传入方法。
}
//如果不是文件夹,则判断是否符合检索条件
else if(f.getName().endsWith(".txt")){
System.out.println(f.getName());
}
}
}
}
17.4 删除文件夹
注:在删除文件夹时,不能直接删除,因为当文件夹中含有其他文件时,无法直接删除,必须要先将文件夹内部的文件逐步删除,使用的递归思想和检索文件类似。
public class Test {
public static void main(String[] args) {
File file = new File("D:\\Test");
method(file);
}
public static void method(File file){
File[] listFiles = file.listFiles();
for (File f : listFiles) {
if(f.isDirectory()){//文件夹
method(f);
f.delete();
}else if(f.isFile()){//文件
f.delete();
}
}
}
}
18 IO流
18.1 IO流的含义
注:站在程序的角度
I:In ,输入流,read读取(读取文件中的数据)
O:Out,输出流,writer写入(向文件里写入数据)
流:一点点的读取或者写入
单位:
1024KB = 1MB
1024MB= 1GB
1024GB = 1TB
1024TB = 1PB
进制:1024
18.2 分流
按照方向分:输入流、输出流
按照单位分:字节流、字符流
按照功能分:节点流/基础流、处理流
18.3 字节流
InputStream 字节输入流的基类(抽象类)
OutputStream 字节输出流的基类(抽象类)
FileInputStream extends InputStream 文件字节输入流
FileOutputStream extends OutputStream 文件字节输出流
示例:
- 使用文件字节输入流(FileInputStream )向文件中添加数据。
//1.创建流对象 //FileOutputStream fos = new FileOutputStream("io.txt"); //设置成在文件末尾追加 FileOutputStream fos = new FileOutputStream("io.txt",true); //2.写入数据 fos.write(97);//写入ASCII fos.write("123abc".getBytes());//写入字节数组 fos.write("123abc".getBytes(),2,3);//写入字节数组,偏移量,写入字节个数 //3.关流 fos.close();
- 使用文件字节输出流(FileOutputStream )从文件中读取数据。
//1.创建流对象 FileInputStream fis = new FileInputStream("io.txt"); //2.读取数据1 //int read = fis.read();//读取第一个字节,返回ASCII //System.out.println(read); //2.读取数据2 //读取数据 read()依次读取单个字节,读取到文件末尾返回-1 //int read; //while((read=fis.read()) != -1){ // System.out.print((char)read); //} //2.读取数据3 byte[] bs = new byte[1024]; int len; while((len = fis.read(bs)) != -1){ System.out.println(new String(bs, 0, len)); } //3.关流 fis.close();
3.使用字节流复制字节文件。
FileInputStream fis = new FileInputStream("test1.txt"); FileOutputStream fos = new FileOutputStream("copy.txt"); byte [] bs=new byte [1024]; int len; while((len=fis.read(bs))!=-1){ fos.write(bs, 0, len); } fis.close(); fos.close(); }
FilterInputStream extends InputStream 过滤器字节输入流
FilterOutputStream extends OutputStream 过滤器字节输出流
BufferedInputStream extends InputStream 带有缓冲区的字节输入流
BufferedOutputStream extends OutputStream 带有缓冲区的字节输出流
默认缓冲区大小:8192字节
示例:
- 使用带有缓冲区的字节输出流BufferedOutputStream 向文件中添加数据
//BufferedOutputStream 带有缓冲区的字节输出流 //1.创建流对象(文件末尾追加) BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test1.txt",true)); //2.写入文件 bos.write("12345".getBytes()); //12345 bos.write("12345".getBytes(),2,3); //12345345 //3.关闭流资源 bos.close();
- 使用带有缓冲区的字节输入流BufferedInputStream 从文件中读取数据
//BufferedInputStream 带有缓冲区的字节输入流 //1.创建流对象 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test1.txt")); //2.读取文件 byte[] bs=new byte[1024]; int len; while((len=bis.read(bs))!=-1){ System.out.println(new String(bs, 0, len)); } //3.关闭流资源 bis.close(); }
- 复制视频(.mp4)文件
BufferedInputStream bis = null; BufferedOutputStream bos = null; try { bis = new BufferedInputStream(new FileInputStream("test1.mp4")); bos = new BufferedOutputStream(new FileOutputStream("copy.mp4")); byte[] bs = new byte[4]; int len; while ((len = bis.read(bs)) != -1) { bos.write(bs, 0, len); } }catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); } finally{ if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } }
注:1.在向文件中添加数据时,若路径下没有文件,将会默认创建文件。
2.使用字节流时,不能出现文字等字符,若需操作含有字符的文件,可以使用字符流。
使用区别:
18.4 字符流
Reader 字符输入流的基类(抽象类)
Writer 字符输出流的基类(抽象类)
InputStreamReader extends Reader 字符输入转换流(字节流转换字符流)
OutputStreamWriter extends Writer 字符输出转换流(字节流转换字符流)
应用场景:获取到的流是字节流,但是要去做字符操作
注意:使用转换流一般都加上编码格式
示例:
- 使用 字符输出转换流(OutputStreamWriter )向文件中添加数据。
//1.创建流对象 //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test1.txt")); //设置编码格式 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test1.txt"),"UTF-8"); //文件末尾写入 //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("test1.txt",true),"UTF-8"); //2.写入数据 osw.write(97);//写入ASCII码 //写入字符数组 osw.write("晚风依旧很温柔~~~".toCharArray(), 2, 5); //写入字符串 osw.write("晚风依旧很温柔~~~"); osw.write("晚风依旧很温柔~~~",7,3); //3.关闭资源 osw.close();
- 使用 字符输入转换流(InputStreamWriter )从文件中读取数据。
//1.创建流对象 InputStreamReader isr = new InputStreamReader(new FileInputStream("test1.txt")); //2.读取数据1,单个读取 int read; while((read = isr.read()) != -1){ System.out.println((char)read); } //2.读取数据2,多个读取 char [] read=new char [1024]; int len; while((len = isr.read(read)) != -1){ System.out.println(new String(read,0,len)); } //3.关闭资源 isr.close();
3.复制文本文件
//创建流对象 InputStreamReader isr = new InputStreamReader(new FileInputStream("test1.txt")); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("copy.txt")); char[] cs =new char[1024]; int len; while((len=isr.read(cs))!=-1){ osw.write(cs,0,len); } isr.close(); osw.close();
FileReader extends InputStreamReader 文件字符输入流
FileWriter extends OutputStreamWriter 文件字符输出流
示例:
- 使用文件字符输出流(FileWriter )向文件中添加字符数据
FileWriter fw = new FileWriter("test1.txt"); fw.write("晚风依旧很温柔~~~"); fw.close();
- 使用文件字符输入流(FileReader )从文件中获取字符数据
FileReader fr = new FileReader("test1.txt"); char[] cs =new char[1024]; int len; while((len=fr.read(cs))!=-1){ System.out.println(new String(cs,0,len)); } fr.close();
- 复制文件
FileReader fr = new FileReader("test1.txt"); FileWriter fw = new FileWriter("copy.txt"); char[] cs =new char[1024]; int len; while((len=fr.read(cs))!=-1){ fw.write(cs,0,len); } fr.close(); fw.close();
BufferedReader extends Reader 带有缓冲区的字符输入流
BufferedWriter extends Writer 带有缓冲区的字符输出流
默认缓冲区大小:8192字符
示例:
- 使用带有缓冲区的字符输出流(BufferedWriter)向文件中添加字符数据
//BufferedWriter 带有缓冲区的字符输出流 //默认缓冲区:8192字符 BufferedWriter bw = new BufferedWriter(new FileWriter("test1.txt")); //自定义缓冲区大小 //BufferedWriter bw = new BufferedWriter(new FileWriter("test1.txt"),1000); //在文件末尾追加 //BufferedWriter bw = new BufferedWriter(new FileWriter("test1.txt",true)); //写入数据 bw.write("BufferedWriter 晚风依旧很温柔~~~"); //关闭资源 bw.close();
- 使用带有缓冲区的字符输入流(BufferedReader )从文件中获取字符数据
//BufferedReader 带缓存区的字符输入流 //创建流对象 BufferedReader br = new BufferedReader(new FileReader("test1.txt")); //读出数据1 char [] cs =new char[1024]; int len; while((len=br.read(cs))!=-1){ System.out.println(new String(cs,0,len)); } //读取数据2:单行读取 String line; while((line=br.readLine())!=null){ System.out.println(line); } //关闭资源 br.close();
- 复制文件1(依次复制会失去原有格式)
BufferedReader br = new BufferedReader(new FileReader("test1.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt")); char[] cs =new char[1024]; int len; while((len=br.read(cs))!=-1){ bw.write(cs,0,len); } br.close(); bw.close();
- 复制文件2(单行的复制)
BufferedReader br = new BufferedReader(new FileReader("test1.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt")); char[] cs =new char[1024]; String line; while((line=br.readLine())!=null){ bw.write(line);//单行添加字符到文件 bw.newLine();//手动加入换行 } br.close(); bw.close();
注:1.使用字符的输入输出转换流时,一般手动加上编码格式,以免读出的字符乱码。
2.在复制文本文件时,如果直接依照字符数组复制,会导致复制后的文件失去原有的格式,所有的字符都在文件第一行中,而显示异常。所有需要单行读取和单行添加,并且要在每次添加之后手动再添加一个换行。(复制的文件会比原来的文件多上一行)
使用区别:
拓展:
以上流的复制文件都是针对单个文件,而对整个文件夹的复制可见java例题
复制整个文件夹到指定文件夹下
18.5 各种流
18.5.1 对象流
ObjectInputStream 对象输入流
ObjectOutputStream 对象输出流
序列化概念:
序列化:将程序中的对象写入到文件中 — 钝化
反序列化:将文件中的对象读取到程序中 — 活化
注:
一个类的对象要想通过对象流写入到文件中,该类就必须实现序列化接口(Serializable)
Serializable叫做序列化接口,该接口没有让我们去实现任何的方法,这种接口叫做标记型接口
transient修饰属性后,该属性不会随着对象写入到文件中
static静态属性也不会随着对象写入到文件中
对象写入文件之后,会特殊编码,只能够通过对象输出流获取,且获取的顺序要和写入一致。
示例1:
写入常见类对象
//ObjectOutputStream 对象输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt")); oos.writeInt(500); oos.writeDouble(20.2); oos.writeObject(new Date()); oos.close();
获取写入的对象
//ObjectInputStream 对象输入流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt")); int readInt = ois.readInt(); //500 double readDouble = ois.readDouble();//20.2 Date date = (Date)ois.readObject();//装入时的时间 System.out.println(readInt); System.out.println(readDouble); System.out.println(date);
示例2:
自定义对象类
import java.io.Serializable; public class Student implements Serializable{//实现序列化接口 //手动设置序列化版本号 private static final long serialVersionUID = 1L; private String name; private String carldId; //构造方法 public Student() { } public Student(String name, String carldId) { this.name = name; this.carldId = carldId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCarldId() { return carldId; } public void setCarldId(String carldId) { this.carldId = carldId; } @Override public String toString() { return "Student [name=" + name + ", carldId=" + carldId + "]"; } }
写入自定义对象
//ObjectOutputStream 对象输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt")); oos.writeObject(new Student("小红","001")); oos.writeObject(new Student("小张","002")); oos.writeObject(new Student("小明","003")); oos.writeObject(null); oos.close();
读取自定义对象
//ObjectInputStream 对象输入流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt")); Student stu; while((stu=(Student)ois.readObject())!=null){ System.out.println(stu); } ois.close();
写入自定义对象注意:
- 在自定义类中必须需要实现序列化接口(implements Serializable)。
- 手动设置序列化版本号(非必须,但若是不手动设置,在写入对象之后,一旦自定义类发生结构发生变化(如增加成员方法…等)都会导致默认的序列化版本号发生变化,从而无法被读出。)
- 小技巧:在写入对象时,可以在末尾写入一个null对象,这样在循环依次获取这个文件中的对象时,可以使用null对象为结束标志。
18.5.2 打印流
18.5.2.1 PrintStream
字节打印流
示例:
//创建流对象 //PrintStream ps = new PrintStream("test1.txt"); //FileOutputStream -> PrintStream (在文件末尾追加) PrintStream ps = new PrintStream(new FileOutputStream("test1.txt",true)); ps.println("输了你,赢了世界又如何~~~~"); ps.close();
18.5.2.2 PrintWriter
字符打印流
示例:
//PrintWriter pw = new PrintWriter("test1.txt"); //FileOutputStream -> PrintWriter (在文件末尾追加) 文件字节流转化为字符打印流 //PrintWriter pw = new PrintWriter(new FileOutputStream("test1.txt",true)); //FileWriter--->PrintWriter 文件字符流转化为字符打印流 PrintWriter pw = new PrintWriter(new FileWriter("test1.txt",true)); pw.println("我想我永远不会懂~~~"); pw.close();
18.5.2.3 重定向
获取系统标准的输入流(方向:控制台->程序):
InputStream in = System.in;
获取系统标准的输出流:(方向:程序->控制台):
PrintStream out = System.out;
知识点:重定向 System.in
//重定向:(方向:文件->程序)
System.setIn(new FileInputStream("test1.txt"));
InputStream in = System.in;
Scanner scan = new Scanner(in);
String next = scan.next();
System.out.println(next);
scan.close();
知识点:重定向 System.out
//重定向:(方向:程序->文件)
System.setOut(new PrintStream(new FileOutputStream("test1.txt")));
PrintStream out = System.out;
out.println("晚风依旧很温柔~~");
18.5.3 随机访问流(RandomAccessFile)
new RandomAccessFile(file, mode);
file:文件对象
mode:操作模式(“r”,“rw”,“rws”,“rwd”)
r 以只读方式打开 rw 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件 rws 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。 rwd 打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。
- 使用随机访问流向文件添加数据
//创建随机访问流对象 RandomAccessFile w = new RandomAccessFile("test2.txt", "rw"); //写入数据 w.write("美丽的泡沫~~".getBytes()); //关流 w.close(); /* test2.txt: 美丽的泡沫~~ */
- 使用随机访问流在文件末尾添加数据
//创建随机访问流对象 File file = new File("test2.txt"); RandomAccessFile w = new RandomAccessFile(file, "rw"); //设置指针在末尾 w.seek(file.length()); //写入数据 w.write("你所有承诺~~~".getBytes()); //关流 w.close(); /* test2.txt: 美丽的泡沫~~你所有承诺~~~ */
- 使用随机访问流在文件内容指定处开始添加数据
//创建随机访问流对象 File file = new File("test1.txt"); /* test.txt: 0123456789 */ RandomAccessFile w = new RandomAccessFile(file, "rw"); //设置指针在位置6 w.seek(6); //写入数据 w.write("aaa".getBytes()); //关流 w.close(); /* test.txt: 012345aaa9 */
- 使用随机访问流从文件中读取数据
//1. 创建流对象 RandomAccessFile r = new RandomAccessFile("test1.txt", "r"); //2.读取数据 byte[] bs = new byte[1024]; int len; while((len = r.read(bs)) != -1){ System.out.println(new String(bs, 0, len)); } //3.关流 r.close();
- 使用随机访问流从文件指定位置开始中读取数据
/* test.txt: 0123456789 */ //1. 创建流对象 RandomAccessFile r = new RandomAccessFile("test2.txt", "r"); //设置指针 r.seek(5); //2.读取数据 byte[] bs = new byte[1024]; int len; while((len = r.read(bs)) != -1){ System.out.println(new String(bs, 0, len)); } //3.关流 r.close(); /* test.txt: 56789 */
注:使用随机访问流在文件内容指定处开始添加数据时会从指针位置依次覆盖,直到添加完。
- 使用随机访问流复制文件,完成断点续传的功能
RandomAccessFile r = new RandomAccessFile("1.mp4", "r"); File file = new File("copy.mp4"); RandomAccessFile w = new RandomAccessFile(file, "rw"); int len=file.length();//以copy.mp4的末指针为随机访问的指针,让文件同步传输。 //设置指针 r.seek(len); w.seek(len); byte[] bs = new byte[1024]; int len; while((len = r.read(bs)) != -1){ w.write(bs, 0, len); } r.close(); w.close();
注:
- 随机访问流并不是随机访问,而是通过seek指针开始读写,而我们可以手动修改seek指针,让文件的读写更加“随机”。
- 通过随机访问流向文件中写入时,文件不可能变小(因为写入的方式为覆盖写入)。
18.5.4 内存流
内存流是建立在程序和内存之间的流,而程序也是运行在内存中,所以内存流一旦创建,将无法关闭。(无法关闭程序和内存的交互)
18.5.4.1 内存输出流
示例:
//创建内存输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//写入数据是将数据放入到内存中
baos.write("java入门基础13".getBytes());
//一次性读取数据
System.out.println(baos.toString());
System.out.println(new String(baos.toByteArray()));
18.5.4.2 内存输入流
示例:
//创建内存输入流(创建流时就将数据放入到内存中)
ByteArrayInputStream bis = new ByteArrayInputStream("java入门基础13".getBytes());
//获取数据
byte[] bs = new byte[1024];
int len;
while((len = bis.read(bs)) != -1){
System.out.println(new String(bs, 0, len));
}
18.5.4.3 内存流的使用场景
内存流是在内存中存取数据,所以效率会远高于其他需要和硬盘交互的流,但数据不能持久化(因为是在内存中存放,持久化需要存入硬盘(文件)中),所有当我们不需要持久化存储数据,而仅仅是需要进行数据中转的时候,就可以使用内存流,来提高效率。