2、特点:1)用来将文件或文件夹封装成对象
2)方便于对文件与文件夹的属性信息进行操作
3)File类的实例是不可变的;也就是说,一旦创建,File 对象表示的抽象路径名将永不改变
4)File对象可以作为参数传递给流的构造函数
3、构造方法:
1)File(String pathname):根据指定的路径创建File对象。
2)File(String parent, String child):根据指定的父文件夹和子文件或者文件夹创建File对象
3)File(File parent, String child):根据指定的父文件夹对象和子文件或者文件夹创建File对象
4、功能:
1)创建文件:public boolean createNewFile():如果指定的文件不存在,就创建。如果存在,就不创建。
2)创建文件夹:a、public boolean mkdir():创建指定的目录,如果存在,就不创建。
b、public boolean mkdirs():创建指定的目录,如果存储,就不创建。这个时候,如果父目录不存在,它也会自动创建。
注意:a、如果你删除的目录下还有内容,那么,必须先把所有内容删除完毕后,在删除目录。
b、java语言的删除不走回收站
4)判断功能:a、boolean exists():判断file对象是否存在
b、boolean isFile():判断file对象是否是文件
c、boolean isDirectory():判断file对象是否是文件夹
d、boolean isAbsolute():判断file对象是否是绝对路径
e、boolean canRead():判断file对象是否可读
f、 boolean canWrite():判断file对象是否可写
g、boolean isHidden():判断file对象是否隐藏
b、String getPath():相对路径
c、String getName():文件名称
d、long length():文件大小,单位是字节
e、long lastModified():上次修改时间的毫秒值。
6)重要获取功能:a、public static File[] listRoots():列出可用的系统文件根目录
b、public String[] list():返回的是指定目录下所有文件或者文件夹的名称数组
d、public File[] listFiles():返回的是指定目录下所有文件或者文件夹对象数组
5、功能练习:
package Test1;
import java.io.File;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) throws IOException{
// 创建文件
File file1 = new File("d:\\a.txt");
System.out.println("createNewFile:" + file1.createNewFile());
// 创建文件 忘了写路径名称了,以当前项目路径所在路径为父目录
File file2 = new File("a.txt");
System.out.println("createNewFile:" + file2.createNewFile());
// 创建目录
File file3 = new File("aaa");
System.out.println("mkdir:" + file3.mkdir());
// 如果目录过多,这样做就太麻烦。肿么办呢?
File file4 = new File("bbb\\ccc");
System.out.println("mkdirs:" + file4.mkdirs());
// 创建文件
File file5 = new File("b.txt");
System.out.println("mkdir:" + file5.mkdir());
System.out.println("-----------------");
// 判断功能
System.out.println("exists:" + file1.exists());// true
System.out.println("isFile:" + file1.isFile());// true
System.out.println("isDirectory:" + file1.isDirectory());// false
System.out.println("isAbsolute:" + file1.isAbsolute());// true
System.out.println("canRead:" + file1.canRead());// true
System.out.println("canWrite:" + file1.canWrite());// true
System.out.println("isHidden:" + file1.isHidden());// false
System.out.println("-----------------");
//获取功能:
File file6 = new File("bbb\\ccc\\a.txt");
System.out.println("getAbsolutePath:" + file6.getAbsolutePath());
System.out.println("getPath:" + file6.getPath());
System.out.println("getName:" + file6.getName());
System.out.println("length:" + file6.length());
System.out.println("lastModified:" + file6.lastModified());
System.out.println("-----------------");
//删除功能
File file7 = new File("a.txt");
System.out.println("delete:" + file7.delete());
// 需求:我要删除aaa
File file8 = new File("aaa");
System.out.println("delete:" + file8.delete());
// 需求:我要删除bbb
File file9 = new File("bbb");
System.out.println("delete:" + file9.delete());
}
}
运行结果:
二. 递归:
public void show(){
show();
}
a、递归一定要有出口。否则就会死递归。
b、递归的次数不要过多。否则内存溢出。
package Test1;
public class DiGuiTest {
public static void main(String[] args) {
System.out.println(DiGuiTest.jc(5));
}
public static int jc(int n) {
if (n == 1) {
// 出口
return 1;
} else {
// 规律
return n * jc(n - 1);
}
}
}
4、小案例1:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问:第二十个月的兔子对数为多少?
package Test1;
public class DiGuiTest {
public static void main(String[] args) {
System.out.println(DiGuiTest.sum(20));
}
private static int sum(int i) {
if(i ==1){
return 1;
}else if(i==2){
return 1;
}else if(i==3){
return 2;
}else{
return sum(i-3) +sum(i-2)+ sum(i-1);
}
}
}
5、小案例2:删除指定的目录。(目录是带有目录或者文件的)
package Test1;
import java.io.File;
public class DiGuiTest {
public static void main(String[] args) {
File f = new File("G:\\CopyFile");
DiGuiTest.deleteFiles(f);
}
private static void deleteFiles(File file) {
// 第1步封装文件夹
File[] fileArray = file.listFiles();// 1,test_deleteFiles;
// 2.1,aaa_deleteFiles;
// 2.2,bbb_deleteFiles;
if (fileArray != null) {
// 如果封装的文件夹不为空,那么就进行遍历,获得每一个文件或文件夹
for (File f : fileArray) {
if (f.isDirectory()) {
// 如果被封装文件夹的子文件还是个文件夹,那么继续封装起来进行判断
deleteFiles(f);
} else {
// 如果被封装起来的子文件夹正好就是个文件,那么直接删除
System.out.println(f.getName() + "***" + f.delete());
}
}
}
System.out.println(file.getName() + "***" + file.delete());
// 如果文件夹为空,直接删除. 当if语句执行完时,就表示每次封装的目录下的文件被删除完毕。
}
}
运行结果:
(2)字符输入流
①、构造方法总结:
a、FileReader(File file) :在给定从中读取数据的 File[File必须一个明确的文件] 的情况下创建一个新 FileReader
b、FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新FileReder
a、创建字符输入流对象(并且明确你要从哪个文件读数据)
package Test1;
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
// 封装数据源
FileReader fr = new FileReader("FileWriterDemo.java");
// 封装目的地
FileWriter fw = new FileWriter("G:\\Copy.java");
// 读取数据
int ch = 0;
while ((ch = fr.read()) != -1) {
fw.write(ch);
System.out.print((char)ch);
fw.flush();
}
// 释放资源
fw.close();
fr.close();
}
}
运行结果如下:
(3)高效字符缓冲流:BufferedReader,BufferedWriter
BufferedReader,BufferedWriter是更高级的流,二者的源和目的地必须是字符输入流和字符输出流.如果把字符输入流做为BufferedReader流的源,把字符输出流作为BufferedWriter流的目的地,那么BufferedReader,BufferedWriter将有更强的读、写能力。
a、构造函数 BufferedReader(Reader in); BufferedWriter(Writer out);
b、常用方法
int read();
//读取单个字符
int read(char[] cbuf, int off, int len) //将字符读入数组的某一部分
String readLine()//读取一个文本行
void write(int c)//写入单个字符
void write(String s, int off, int len)//写入字符串的某一部分。
void write(char[] cbuf, int off, int len)//写入字符数组的某一部分
void newLine()//写入一个行分隔符
c、使用方法
BufferedReader ino=new BufferedReader(new FileReader("Student.txt"));
//参数是字符流
BufferedWriter outo=new BufferedWriter(new FileWrietr("Student.txt"));
//参数是字符流
d、小案例:
import java.io.*;
//编写一个程序,把指定目录下的所有的带.java文件都拷贝到另一个目录中,
//拷贝成功后,把后缀名是.java的改成.txt。
public class Test4 {
static int count=0;
public static void main(String[] args) throws IOException {
File yfile=new File("F:\\");
File mfile=new File("G:\\CopyFile");
copyFile(yfile,mfile);
System.out.println("共复制了:"+count+" 个文件");
}
public static void copyFile(File yfile,File mfile) throws IOException{
if(yfile==null){
System.out.println("源文件目录为:null.");
return;
}
if(!yfile.exists()){
System.out.println("源文件不存在.");
return;
}
if(!mfile.exists()){
mfile.mkdirs();
System.out.println("目标目录不存在,已经创建");
}
File[] fileList=yfile.listFiles();
if(fileList!=null){
for(File f:fileList){
if(f.isFile()&&f.getName().endsWith(".java")){
String name=f.getName();
String newname=name.replaceAll("\\.java$", ".mp3");
Long time=System.currentTimeMillis();
File mf=new File(mfile,time+newname);
BufferedReader br=new BufferedReader(new FileReader(f));
BufferedWriter bw=new BufferedWriter(new FileWriter(mf));
String strs=null;
while((strs=br.readLine())!=null){
bw.write(strs);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
count++;
}
if(f.isDirectory()){
copyFile(f,mfile);
}
}
}
}
}
运行结果如下:
package Test1;
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos =new FileOutputStream("G:\\CopyFile.txt");
byte[] bys =new byte[1024];
int len;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
System.out.println(new String(bys,0,len));
}
fos.close();
fis.close();
}
}
运行结果:
(3)注意:
(4)带缓冲区的输入出输出流
▪BufferedInputStream是带缓冲区的输入流,默认缓冲区大小是8M,能够减少访问磁盘的次数,提高文件读取性能;
▪BufferedOutputStream是带缓冲区的输出流,能够提高文件的写入效率。
▪ BufferedInputStream与BufferedOutputStream分别是FilterInputStream类和FilterOutputStream类的子类,实现了装饰设计模式。
(5)小案例:
package Test1;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) throws IOException {
//高效字节流一次读写一个字节数组
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\酷狗下载\\信乐团 - 假如.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("love.mp3"));
byte[] bys = new byte[1024];
int len = 0;
while((len=bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bos.close();
bis.close();
System.out.println("复制完成!");
}
}
3.转换流
1:标准的输入输出流
a、public static final InputStream in :“标准”输入流。(System.in)--InputStream(字节输出流)
b、public static final PrintStream out:”标准“输出流 。(System.out)--PrintStream (字节输入流)
2.字节输入流转成字符输入流
a、方法:
字节流通向字符流的桥梁
InputStream ----> InputStreamReader -----> BufferedReader
b、小练习,想自己封装一个键盘录入数据,并存储到文本文件中:
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
// 封装目的地
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
String line = null;
while ((line = br.readLine()) != null) {
// 只要是键盘录入,就的自己定义结束条件
if ("over".equals(line)) {
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
}
理解:字符流读写数据的底层其实还是调用的字节流,只不过把字节流按照编码顺序排成了字符,从而读出来的是字符所以说既然底层调用的是字节流,那么字节流通过InputStreamReader就可以转成字符流,(其实就是把字节加入了一些编码顺序)所以说 万流之根源乃字节流,根源演变成了其他流,所以说 字节流可以转字符流,而反过来就不可以。
3.字节输出流转字符输出流:
a、方法:OutputStream -- > OutputStreamWriter --> BufferedWriter
b、小案例,需求:把文本文件的数据通过标准输出流的方式显示在控制台。
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
// 封装数据源
BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
// 封装目的地
OutputStream os = System.out;
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
// BufferedWriter bw = new BufferedWriter(new
// OutStreamWriter(System.out));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
System.out.println();
bw.close();
br.close();
}
}
理解:这句话 "OutputStreamWriter 是字符流通向字节流的桥梁" 难理解,因为不知道为什么字符流是字节流的桥梁,(按字节的流向,字符流读进来文字,按照字符流写入文件,底层还是调用字节流写入文件的,所以是字符流通向字节流的桥梁)不用管OutputStreamWriter是谁到谁的桥梁,我就说上面的是字节流转字符流还是字符流转字节流?肯定是字节流转字符流,反过来就不可以(万流之根源乃字节流,根源演变成了其他流)
4:编码问题
流的编码问题:字符流=字节流+编码表
如果你想在IO流中使用指定编码下数据,用转换流。
编码问题其实很简单,永远使用同一种编码即可。
FileReader: FileInputStream+GBK
FileWriter:FileOutputStream+GBK
//写入的时候用的字符流的UTF-8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"), "UTF-8"); // 指定编码UTF-8
osw.write("中国");
osw.close();
//那么读出的时候如果用字符流 也应该用 UTF-8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"), "UTF-8");
char[] chs = new char[1024];
int len = isr.read(chs);
String str = new String(chs, 0, len);
System.out.println(str);
如下面代码:
import java.io.*;
public class FileTest {
public static void main(String[] args) throws IOException {
// OutputStreamWriter(OutputStream out, String charsetName)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"), "UTF-8");// 指定编码UTF-8
osw.write("中国你好!");
osw.close();
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"), "UTF-8");
char[] chs = new char[1024];
int len = isr.read(chs);
String str = new String(chs, 0, len);
System.out.println(str);
}
}
5.打印流
a、PrintStream:字节打印流
b、PrintWriter:字符打印流
1、打印流特点:
a、可以写入任意类型的数据。
b、可以自动刷新。必须先启动,并且是使用println,printf及format方法才有效。
c、可以直接对文件进行写入。
d、注意:打印流只有写数据的,没有读取数据的。
2、构造方法:
a、PrintWriter(String fileName) :使用指定文件创建不具有自动行刷新的新 PrintWriter
b、PrintWriter(OutputStream out):根据现有的OutputStream创建不带自动行刷新的新 PrintWriter
c、PrintWriter(OutputStream out,boolean autoFlush):
通过现有的OutputStream创建新的PrintWriter。
d、PrintWriter(Writer out):创建不带自动行刷新的新PrintWriter。
e、PrintWriter(Writerout,boolean autoFlush):创建新PrintWriter
3.PrintWriter的 print() 可以写任意类型数据:
PrintWriter 自动刷新功能(必须开启了自动刷新 然后还得是调用println,printf及format方法,才能自动刷新)。
PrintWriter pw = new PrintWriter(new FileWriter("d.txt"), true);PrintWriter的println()能够自动换行。
4.案例:用打印流复制文本文件
public class FileTest {
public static void main(String[] args) throws IOException {
// 封装数据源
BufferedReader br = new BufferedReader(new FileReader("PrintWriterDemo.java"));
// 封装目的地
PrintWriter pw = new PrintWriter(new FileWriter("Copy.java"), true);
String line = null;
while ((line = br.readLine()) != null) {
pw.println(line);
}
pw.close();
br.close();
}
}
5.注意:
a、读取文件的时候,如果是文件夹,会出现:FileNotFoundException: test (拒绝访问。)
b、写文件的时候,如果没有后缀名,它也会自动生成一个文件,而这个种类型的文件通过记事本类型的软件是可以打开的。
6.序列化流
(1)序列化:把对象按照流一样的方式传输或者存储。
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。
void writeObject(Object obj)
(2)反序列化:把网络中的流数据或者文件中的流数据还原成对象。
ObjectInputStream:ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化
Object readObject()
(3)注意问题:
a、如果类的对象想被序列化流操作,请实现序列化接口。
b、看到了类实现了Serializable接口,如果想解决黄色警告线,请点击鼠标。
c、java.io.NotSerializableException 没有实现序列化接口异常
d、类通过实现 java.io.Serializable 接口以启用其序列化功能。该接口被称为序列化接口,是一个标记接口。
没有任何功能需要实现。类一旦实现了该接口,那么,该类的对象就可以被序列化流进行操作。
(4)为什么要写一个序列号?
对象在被序列化流进行操作的时候,会记录一个序列号值。而这个序列号值跟对象的成员相关。
最开始的时候:
Person类的对象 p的id是100
write写到文件中:id -- 100
read读取数据: id -- 100
后来,我们把write方法给注释了,然后修改了Person类,这个类被修改后,成员就发生了改变。
那么,id也就相应的发生改变
Person类的对象id改为了200
那么,直接读取,文件中的id是100,而我们有的id是200,他们不匹配,所以报错了。
那么,在很多情况下,我们的类中的成员可能会发生改变,不可能说每次改变,我都需要把数据重写入一次。
怎么解决这种情况呢?
先写,在读取。没有问题。
先写,后修改了类,在读取,有问题。
很简单,我们想了这样的一个问题:他们之所以出错,就是因为那个id值不匹配,如果我们直接在类中给一个固定的id值。
那么,无论你怎么修改类的成员,这个id值是不变化的,那这样的话,以后的id永远都是一样的。就不会再因为简单的修改出问题。
问题呢?怎么定义这个id值呢?
private static final long serialVersionUID = 333558257579870816L;
(5)
示例:
- /*
- SequenceInputStream
- 合并流
- 需求:将三个文本文件中的数据合并到一个文本文件中
- 思路:1、创建一个Vector集合,将三个文本文件字节流添加到集合中
- 2、创建Enumeration对象,创建SequnceInputStream对象关联Enumeration
- 3、输出流关联新文本文件
- 4、反复读写操作
- */
- import java.util.*;
- import java.io.*;
- class SequenceInputStreamDemo
- {
- public static void main(String[] args)throws IOException
- {
- Vector<InputStream> ve=new Vector<InputStream>();//创建vector集合,并添加相关流对象
- ve.add(new FileInputStream("1.txt"));
- ve.add(new FileInputStream("2.txt"));
- ve.add(new FileInputStream("3.txt"));
- Enumeration<InputStream> en=ve.elements();//创建枚举对象
- SequenceInputStream sis=new SequenceInputStream(en);//合并流
- FileOutputStream fos=new FileOutputStream("4.txt");//关联写入文件
- //反复读写操作
- byte[] buf=new byte[1024];
- int len=0;
- while((len=sis.read(buf))!=-1)
- {
- fos.write(buf,0,len);
- }
- //关流
- fos.close();
- sis.close();
- }
- }
练习:
- /*
- 切割文件
- 需求:将一个mp3文件按1M大小切割成几部分
- 思路:1、使用文件字节流关联mp3文件
- 2、定义一个容器存储1M大小的数据,当存储满时,写入一个新文件中
- */
- import java.util.*;
- import java.io.*;
- class SplitFile
- {
- public static void main(String[] args) throws IOException
- {
- //指定要切割的文件
- File file=new File("C:\\Users\\asus\\Desktop\\苏芮 - 一样的月光.mp3");
- //将指定文件进行切割
- splitFile(file);
- //指定要合并到的文件
- File file1=new File("E:\\Java Study\\Practice\\day20\\splitFile\\一样的月光.mp3");
- //将部分文件进行合并指定文件中
- merge(file1);
- }
- //接收一个文件,将其按1M大小进行切割
- public static void splitFile(File file)throws IOException
- {
- //关联要切割的文件
- BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
- BufferedOutputStream bos=null;
- //定义1M大小存储容器
- byte[] buf=new byte[1024*1024];
- int len=0,x=0;
- while ((len=bis.read(buf))!=-1)
- {
- //每满1M就写入一个新文件中
- bos=new BufferedOutputStream(new FileOutputStream("E:\\Java Study\\Practice\\day20\\splitFile\\"+(++x)+".part"));
- bos.write(buf,0,len);
- bos.close();//没写完一个文件要记得关流
- }
- //关流
- bis.close();
- }
- //将部分文件合并为一个可执行文件
- public static void merge(File file)throws IOException
- {
- //定义一个集合存储这些部分文件关联路径数据
- ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
- for (int x=1;x<=6 ; x++)
- {
- al.add(new FileInputStream("E:\\Java Study\\Practice\\day20\\splitFile\\"+x+".part"));
- }
- //因为Enumeration是Vector特有的迭代方法,所以这里创建一个Enumeration类型的匿名内部类
- final ListIterator<FileInputStream> it=al.listIterator();
- Enumeration<FileInputStream> en=new Enumeration<FileInputStream>()
- {
- public boolean hasMoreElements()
- {
- return it.hasNext();
- }
- public FileInputStream nextElement()
- {
- return it.next();
- }
- };
- //关联枚举对象
- SequenceInputStream sis=new SequenceInputStream(en);
- //将合并的文件数据写入指定文件中
- FileOutputStream fos=new FileOutputStream(file);
- //定义临时存储数据的数组
- byte[] buf=new byte[1024];
- int len=0;
- while((len=sis.read(buf))!=-1)
- {
- fos.write(buf,0,len);//写数据
- }
- //关流
- fos.close();
- sis.close();
- }
- }