File可以代表一个目录或者一个文件,并不能代表文件的内容 文件和流的区别:File关注的是文件本身的特征,如名称、路径、修改时间、大小。 流关注的是文件的内容。
常见构造方法
| File(String pathname) | 建立一个以pathname为路径的File对象, pathname可以是相对路径,也可以是绝对路径。 | | ———————————————— | —————————————————————————————— | | File(String parent,String child) | 建立一个以parent加上child为路径的File对象 | | File(File parent,String child) | 建立一个以parent加上child为路径的File对象 |
注意:文件的路径如果考虑跨平台,就使用File.seperator
相对路径:javase程序中,相对参照点为项目目录 src/test.项目目录下的src/test 绝对路径:e:/test
创建文件操作
mkdir:创建单层目录 dir1/dir2:必须保证dir1是存在的,才能成功创建dir2 mkdirs:创建层级目录 dir1/dir2:如果dir1不存在,会先创建dir1,再创建dir2 createNewFile:创建文件 dir1/a :必须保证dir1是存在的,否则,会报系统找不到指定的路径的异常
文件的常用读操作(文件名称、文件名称)
exists() //文件或者目录是否存在 isFile() //是否是文件 isDirectory() //是否是目录 getName() //取文件或者目录的名字 getPath() //取文件或者目录的路径 getAbsolutePath() //取文件绝对路径 lastModified() //最后修改日期 length() //文件或者目录的字节大小 list()//获得当前File对象所表示目录下的文件和目录名称,返回String[]类型的数据。 listFiles()//获得当前File对象所表示目录下的文件和目录,返回File[]类型的数据。
代码实例
修改文件名称
注意:修改的新文件名需要是不存在的 ,如果修改前后的目录一致,那就是重命名的效果,如果修改前后目录不一致,相当于会删除旧目录中的文件,以新的名称出现在新目录中
// File file = new File(baseDir,"11.txt"); // file.renameTo(new File(baseDir,"bbb/111.txt"));
删除文件
File file = new File(baseDir,"2.txt"); file.delete();
删除文件夹
递归:
1、自己调用自己
2、必须要一个条件
private static void myDelete(File dir) { // TODO Auto-generated method stub if(dir.isDirectory()) { File[] listFiles = dir.listFiles(); for (File file : listFiles) { if(file.isDirectory()) { myDelete(file); } file.delete(); } dir.delete(); } }
得有一个明确的递归出口(什么情况下不递归了)
递归算法代码简洁,但是效率比较低
1到n的累加和执行过程:
| n=1 | 0x01 return 1 | fun(1) =1 | | —— | ———————- | ———————————- | | n=2 | 0x02 2+fun(1) | fun(2) =2+fun(1)=2+1=3 | | n=3 | 0x02 :3+fun(2) | fun(3)= 3+fun(2)=3+3=6 | | n=4 | 0x02 :4+fun(3) | fun(4)=4+fun(3)=4+6=10 | | n=5 | 0x02: 5+fun(4) | fun(5)=5+fun(4)=5+10=15 | | 栈底 | | |
入栈:记录信息
出栈:根据入栈记录的信息计算结果 fun(1) fun(2) ….. fun(5)
public static int fun(int n){ if(n<=1){ return 1; //0x01 }else{ return n+fun(n-1); //0x02} }
public static void deleteFile(File f){ if(!f.exists()){ return; } //如果是一个文件 if(f.isFile()){ f.delete();//删除文件 System.out.println("删除文件"+f.getName()); } //如果是一个目录 if(f.isDirectory()){ File fs[]=f.listFiles(); //满足这个条件,不是空目录 if(fs.length>0){ for(File subFile:fs){ deleteFile(subFile); } } //删除空目录 f.delete(); System.out.println("删除目录"+f.getName()); } }
File类关心的是磁盘上存储的文件位置和名称等,而流是指程序运行中的数据通信信道,当然流类所关心的是文件的内容。
根据流向分为:输入流,输出流
根据传输单位:字节流,字符流
根据是否和数据源可以直接交互:节点流,处理流
节点流:可以和数据源直接交互,FileInputStream,FileOutPutStream,FileWriter,FileReader
处理流:需要连接嵌套其它的流使用,BufferedReader,BufferedWriter
InputStream(字节输入流)
FileInputStream:节点流,字节输入流,按照字节为单位读文件,文件复制
ObjectInputStream:处理流,对象输入流,需要嵌套一个字节输入流进行使用,如FileInputStream,反序列化
OutputStream(字节输出流)
FileOutputStream:节点流,字节输出流,按照字节为单位写,文件复制
ObjectOutPutStream:处理流,对象输出流,需要嵌套一个字节输出流,如FileOutputStream进行使用,序列化
Reader(字符输入流)
FileReader:节点流,针对文件的字符输入流,以字符为单位读文件,存在乱码问题,不能直接按行进行读
BufferedReader:处理流,带缓冲区的字符输入流,以行为单位读文件 readLine:返回值为null表示读取结束
InputStreamReader:处理流,转换流(可以将字节流转换为字符流),需要嵌套节点流使用,读文件的指定字符集编码,解决乱码问题
Writer
FileWriter:节点流,针对文件的字符输出流,以字符为单位写文件,存在乱码问题,不能直接按行写
BufferedWriter :处理流,带缓冲区的字符输出流,按照行写字符,newLine
OutPutStreamWriter:处理流,转换流(可以将字节流转换为字符流),需要嵌套节点流使用,写文件的时候指定字符集编码,解决乱码问题
FileInputStream
常用api
FileInputStream是InputStream的子类。 FileInputStream是读取文件的字节输入流。 FileInputStream常用的构造方法: FileInputStream(File file) FileInputStream(String filename) FileInputStream覆盖实现了父类如下方法: int available() void close()
字节输入流的代码结构
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; public class StreamDemo { public static void main(String[] args) throws IOException { //字节流 //1、建立文件源(数据源的一种) File file = new File("src/1.txt"); //2、建立流链接文件源 FileInputStream input = new FileInputStream(file); //3、读取数据 int read = input.read(); byte[] bs = new byte[1024]; int len = input.read(bs); int read2 = input.read(bs, 2, 5); //使用数据 //4、关闭流 input.close(); } }
int read():一次读取1个字节,返回值表示读取的一个字节的数据(一个字节的数据不一定表示一个字符的编码),返回值为-1表示读取结束
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class StreamDemo { public static void main(String[] args) { //字节流 //public abstract class InputStream File file = new File("src/1.txt"); try { //如果是输入流,文件必须存在 FileInputStream fileInputStream = new FileInputStream(file); int n; //read()读取流中的数据 while((n=fileInputStream.read())!=-1) { System.out.println(n); } //关闭流 fileInputStream.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
int read(byte[ ] b):将文件中读到的数据放到数组中,返回值为读取到的字节长度,返回值为-1表示读取结束
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; public class StreamDemo { public static void main(String[] args) { //字节流 //public abstract class InputStream File file = new File("src/1.txt"); try { //如果是输入流,文件必须存在 FileInputStream fileInputStream = new FileInputStream(file); byte[] bs = new byte[1024]; //int read(byte b[]) 将文件中的所有数据读到字节数组中(bs) //返回值是读取了多少个字节 int len = fileInputStream.read(bs); // System.out.println(Arrays.toString(bs)); // System.out.println(len); String str = new String(bs); System.out.println(str.trim()); //关闭流 fileInputStream.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
int read(byte[] b,int off,int len):将文件中读到的数据放到数组中的指定位置,指定长度,返回值为读取到的字节长度,返回值为-1表示读取结束
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; public class StreamDemo { public static void main(String[] args) { //字节流 //public abstract class InputStream File file = new File("src/1.txt"); try { //如果是输入流,文件必须存在 FileInputStream fileInputStream = new FileInputStream(file); // int n; // //read()读取流中的数据,每次读取一个字节 // while((n=fileInputStream.read())!=-1) { // System.out.println(n); // } byte[] bs = new byte[1024]; //int read(byte b[]) 将文件中的所有数据读到字节数组中(bs) //返回值是读取了多少个字节 //第一个参数bs存了读取的数据 //第二个参数1设置了bs数组从下标1的位置开始存 //第三个参数3设置了从文件中读取几个字节 int len = fileInputStream.read(bs,1,3); System.out.println(Arrays.toString(bs)); System.out.println(len); String str = new String(bs); System.out.println(str); //关闭流 fileInputStream.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
注意:read()和int read(byte[ ] b)方法返回值代表的意义
FileOutputStream
FileOutputStream是OutputStream的子类 FileOutputStream是写入文件的字节输出流 FileOutputStream常用的构造方法: FileOutputStream(File file) FileOutputStream(String filename) FileOutputStream(File file,boolean append) FileOutputStream(String filename,boolean append) FileOutputStream覆盖实现了父类如下方法: void close() void flush() void write(int b) :写入一个字节的数据,参数就是要写入的数据的编码
void write(byte[] b) :将数组中的数据写到文件中,数组中存放的是字符的编码
void write(byte[] b,int off,int len):将数组指定位置指定长度的数据写入的文件中
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class OutputStreamDemo { public static void main(String[] args) throws IOException { //FileOutputStream //按照单位,字节流 //按照方向,输出流 //按照是否跟源进行关联,节点流 //1、声明文件源 File file = new File("src/output.txt"); //2、创建字节输出流 FileOutputStream fos = new FileOutputStream(file); //3、写出数据 String str = "abcdefg"; // for(int i=0;i
文件复制
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class CopyDemo { public static void main(String[] args) throws IOException { //文件复制 File sourceFile = new File("G:\\video\\20220530-1-File概念.mp4"); File targetFile = new File("f:\\64\\1.mp4"); FileInputStream input = new FileInputStream(sourceFile); FileOutputStream output = new FileOutputStream(targetFile); //缓存可以减少io次数,可以大大的提升速度 byte[] bs = new byte[1024*8]; int len; while((len=input.read(bs))!=-1) { output.write(bs); bs = new byte[1024*8]; } input.close(); output.close(); sourceFile.delete(); } }
字符流:适合读写文本字符(按照字符读取,不能直接按照行读取)
FileReader
常用API:
int read() :一次读一个字符,返回值是读到的一个字符的编码,-1表示读取结束
int read(char[] cbuf) :返回值表示读取到字符的个数,参数表示存放读取到的数据的数组
int read(char[] cbuf,int off,int len):返回值表示读取到字符的个数,参数表示存放读取到的数据的数组
void close()
FileWriter
常用API:
void write(int ch) :写入单个字符到调用的输出流。
void write(String str) :写入str到输出流。
void write(String str, int off, int len),写入字符串的某一部分。
void write(char[] cbuf)
void write(char[] cbuf,int off,int len):写入字符数组的某一部分。
void flush() :清空输出缓冲区。
void close() :关闭输出流。
import java.io.File; import java.io.FileReader; import java.io.IOException; public class CharIODemo { public static void main(String[] args) throws IOException { //Reader 字符输入流的父类 //FileReader 文件字符输入流 FileReader fr = new FileReader(new File("src/1.txt")); int n; while((n=fr.read())!=-1) { System.out.println((char)n); } char[] cs = new char[1024]; while(fr.read(cs,1,3)!=-1) { System.out.println(new String(cs).trim()); cs = new char[1024]; } fr.close(); //FileWriter 文件字符输出流 // FileWriter fw = new FileWriter(new File("src/1.txt")); fw.write("你吃了么?".toCharArray()); fw.write(25105); fw.write((int)'我'); // //参数为String fw.write("人情味儿发我放散阀"); // fw.close(); } }
流对象被其他处理流的多次连接,称为流的嵌套。
直接与数据源相连接的流是节点流
处理流连接节点流,提供附加性能
处理流可以被其它处理流继续连接,以获取需要的其它性能。
处理流不可以与节点(数据源)直接连接
默认带了8192个字符的缓冲区,是为了提高性能,减少频繁和硬盘进行io的次数,
BufferedReader:处理流,字符输入流,是Reader的子类提供了readLine方法,很实用
import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; public class BufferedReaderDemo { public static void main(String[] args) throws IOException { // BufferedReader //生成数据源 File sourceFile = new File("src/1.txt"); //生成节点流 FileReader fr = new FileReader(sourceFile); //生成处理流 缓冲流 BufferedReader br = new BufferedReader(fr); //boolean ready()返回true,则可以读 while(br.ready()) { //String readLine()可以读出文件中的一行内容,如果没有则返回null System.out.println(br.readLine()); } br.close(); } }
获得所有学生,并返回集合
import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class StuDemo { public static void main(String[] args) throws IOException { //文件中读取内容,先封装成学生对象,再封装到集合中 ListstuList = new ArrayList (); BufferedReader br = new BufferedReader(new FileReader(new File("src/stu.txt"))); while(br.ready()) { //1,张三,18 String stuStr = br.readLine(); //{"1","张三","18"} String[] arr = stuStr.split(","); Stu stu = new Stu(Integer.parseInt(arr[0]),arr[1],Integer.parseInt(arr[2])); stuList.add(stu); } br.close(); for (Stu stu : stuList) { System.out.println(stu); } } }
BufferedWriter:处理流,字符输出流,是Writer的子类,提供了newLine() write(str)方法
写入一个学生
import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class BufferedWriterDemo { public static void main(String[] args) throws IOException { //BufferedWriter File file = new File("src/stu.txt"); //第二个参数为是否追加,true为追加写,false为覆盖写 FileWriter fw = new FileWriter(file,true); BufferedWriter bw = new BufferedWriter(fw); Stu stu = new Stu(5,"田七",23); String str = stu.getSid()+","+stu.getSname()+","+stu.getSage(); bw.write(str); bw.newLine(); bw.close(); } }
对象流
对象输入流(ObjectInputStream):
ObjectInputStream in=newObjectInputStream(输入流对象);
相应的方法:in.readObject();
对象输出流(ObjectOutputStream):
ObjectOutputStream out=new ObjectOutputStream(输出流对象);
相应的方法:out.writeObject(Object obj);
序列化
序列化:就是将对象的内容分解成字节流,以便存储在文件中或在网络上传输。
属性的要求:非transient 非静态 是可序列化类型(Serializable)
反序列化:就是打开并读取字节流,且从该流中恢复该对象
没有执行过反序列化的类型对应的代码块及构造方法
能够序列化的对象必须是Serializable类型,对应的类型必须实现Serializable接口
序列化类型中一般会要求提供一个serialVersionUID,作用是保证读(反序列化)和写(序列化)的过程中类型版本的一致性。如 写的时候版本id为1 那么读的时候版本id也必须是1,才能成功
public static void write1() throws IOException{ //处理流 ObjectOutputStream writer=new ObjectOutputStream(new FileOutputStream(new File("src/data"))); for (int i = 0; i < 10; i++) { Goods goods=new Goods("00"+i, "商品"+i, i+10.1f); writer.writeObject(goods); } writer.flush(); writer.close(); } public static void read1() throws IOException, ClassNotFoundException{ //处理流 ObjectInputStream reader=new ObjectInputStream(new FileInputStream(new File("src/data"))); while(true){ try { Object obj=reader.readObject(); System.out.println(obj); } catch (EOFException e) { //EOFException:表示文件读取到末尾了 // TODO Auto-generated catch block System.out.println("读取结束"); break; } } reader.close(); }
注意:使用EOFException判断文件是否读取到末尾
问题描述: 用类ObjectOutputStream向文件写读对象时,碰到一个问题:新建一个文件,用输出流ObjectOutputStream向文件连续写几个对象,关闭输出流,然 后读取,这些对象都可以读出;这时在向该文件增加对象,新写的对象就读不出了
问题出现的原因: ObjectOutputStream建立后第一次写入一个对象时, 会在对象数据前写入一些标志的数据“AC ED 00 05”(用二进制方式查看打开),应该是流相关的信息。当你关闭 ObjectOutputStream 后再重新打开往文件里面写对象时(append方式),就会再一次把“AC ED 00 05”写入文件,而这些信息并不是你写入对象的数据,所以当你用ObjectInputStream来读取对象时,流会将除第一个“AC ED 00 05”以外的数据当作各个对象的数据,造成无法解析,所以读取时有一个java.io.StreamCorruptedException出现。 这个可以通过编辑Info.dat来验证,只要将“AC ED 00 05”删除(第一个“AC ED 00 05”保留)就可以正常读出后来加入的对象。 给出一个比较笨的解决方法: 在以后要添加新的对象到Info.dat时,将里面原有的对象读出放入ArrayList中,清空文件,再将对象集一次写入。
尝试解决办法:
那个“AC ED 00 05”是 ObjectOutputStream.writeSystemHeader()写进去的,你可以继承ObjectOutputStream类,覆盖这个方法。 在你自己的writeSystemHeader()里判断是不是第一次写入一个文件,如果是向一个文件大小不为零的文件追加的话,就调用 super.reset(),如果是第一次写这个文件,不是追加,就调用super.writeSystemHeader()
/** * 此类继承ObjectOutputStream,重写writeStreamHeader()方法,以实现追加写入时去掉头部信息 */ public static class MyObjectOutputStream extends ObjectOutputStream { private static File f; // writeStreamHeader()方法是在ObjectOutputStream的构造方法里调用的 // 由于覆盖后的writeStreamHeader()方法用到了f。如果直接用此构造方法创建 // 一个MyObjectOutputStream对象,那么writeStreamHeader()中的f是空指针 // 因为f还没有初始化。所以这里采用单态模式 private MyObjectOutputStream(OutputStream out, File f) throws IOException, SecurityException { super(out); } // 返回一个MyObjectOutputStream对象,这里保证了new MyObjectOutputStream(out, f) // 之前f已经指向一个File对象 public static MyObjectOutputStream newInstance(File file, OutputStream out) throws IOException { f = file;// 本方法最重要的地方:构建文件对象,两个引用指向同一个文件对象 return new MyObjectOutputStream(out, f); } @Override protected void writeStreamHeader() throws IOException { // 文件不存在或文件为空,此时是第一次写入文件,所以要把头部信息写入。 if (!f.exists() || (f.exists() && f.length() == 0)) { super.writeStreamHeader(); } else { // 不需要做任何事情 } } }