零基础学习java------day16-----文件,递归,IO流(字节流读写数据)
1.File
1.1 构造方法(只是创建已经存在文件的对象,并不能创建没有的文件)
(1)public File(String pathname)
(2)public File(String parent, String child)
(3)public File(File parent, String child)
public class FileDemo1 { public static void main(String[] args) { File f = new File("e:/a/b"); System.out.println(f.exists()); //true File f1 = new File("e:/G09 result/","H2O"); //此处result后的“/”号有无都可以 System.out.println(f1.exists()); //true File f2 = new File(f1,"H2O.out"); System.out.println(f2.exists());//true } }
1.2 File类成员方法
1.2.1 创建删除重命名功能
(1)创建功能
public boolean createNewFile() 创建文件
public boolean mkdir() 创建单层文件夹
public boolean mkdirs() 创建多层文件夹
(2)删除功能
public boolean delete() 删除文件或文件夹(文件夹中有内容无法删除)
(3)重命名功能
public boolean renameTo(File dest) 重命名(移动)
public class FileDemo2 { public static void main(String[] args) { try { File f = new File("e:/G09 result/H2O/t.txt");//创建文件的时候,目录必须存在 boolean b = f.createNewFile(); new File("e:/a").mkdir(); //在盘创建了a文件夹 new File("e:/a/b/c/d").mkdirs();//创建了多层文件夹,此对象代表d这个文件夹 File f1 = new File("e:/123"); f1.createNewFile();//得到的是一个文件,文件可以没有后缀名 File f2 = new File("e:/789.txt"); f2.mkdir();//得到的是一个文件夹 f1.delete(); f2.delete(); // 如果文件夹中有内容是不能被删除的 f.renameTo(new File("d:/haha.txt")); //将e:/G09 result/H2O/t.txt文件移动到d盘,并重命名为haha.txt } catch (IOException e) { e.printStackTrace(); } } }
1.2.2 判断功能
(1)public boolean isDirectory():是否是目录
(2)public boolean isFile():是否是文件
(3)public boolean exists():是否存在
(4)public boolean canRead():是否可读
(5)public boolean canWrite():是否可写
(6)public boolean isHidden(): 是否隐藏
public class FileDemo3 { public static void main(String[] args) { File f = new File("e:/a"); System.out.println(f.exists()); //true System.out.println(f.isDirectory());//true System.out.println(f.isFile());//false System.out.println(f.canRead()); //true System.out.println(f.canWrite()); //true System.out.println(f.isHidden()); //false } }
1.2.3 获取功能
a . 基本获取功能
(1)public String getAbsolutePath():获取绝对路径
(2)public String getPath(): 获取相对路径
(3)public String getName(): 获取文件名
(4)public long length(): 获取字节数
(5)public long lastModified: 获取最后修改时间
public class FileDemo4 { public static void main(String[] args) throws IOException { File f1 = new File("忽然.txt"); f1.createNewFile(); System.out.println(f1.getAbsolutePath());//E:\development\workspace\java\javase\javase\忽然.txt System.out.println(f1.getPath());//忽然.txt System.out.println(f1.getName());//忽然.txt System.out.println(f1.length()); System.out.println(f1.lastModified());//1566540753201 //先将long类型的毫秒值转为date类型的时间,然后再讲此时间转为特定格式的时间 System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date(f1.lastModified()))); } }
b . 高级获取功能
(1)public String[ ] list(): 获取所有子文件名称(返回值类型为字符串数组)
(2)public File[ ] listFiles(): 获取所有子文件对象 (返回值类型为文件类型数组)
public class FileDemo5 { public static void main(String[] args) { File f = new File("E:\\exercise"); System.out.println(f.list()); //[Ljava.lang.String;@279f2327 System.out.println(Arrays.toString(f.list()));//这样转化后就能打印数组了 File[] listFiles = f.listFiles(); System.out.println(f.listFiles());//[Ljava.io.File;@2ff4acd0,需要将其遍历出来 for(File file:listFiles) { System.out.println(file); } } }
练习 查询单层文件夹下所有以后缀名(.jpg))结尾的文件,此处,我要查询的文件夹如下:
法一:
public class FindFile { public static void main(String[] args) { getFile("E:\\exercise",".jpg"); } public static void getFile(String path,String suffix) { File f = new File(path); File[] listFile = f.listFiles(); for (File file : listFile) {
//除了判断后缀,还要判断是不是文件,有大写文件的话,转为小写 if(file.isFile() && file.getName().toLowerCase().endsWith(suffix)) { System.out.println(file.getAbsolutePath()); } } } }
法二(使用过滤器)
public class FindFile { public static void main(String[] args) { getFile("E:\\exercise",".jpg"); } public static void getFile(String path,String suffix) { File f = new File(path); // 带有过滤器为参数的listFiles方法,这里使用了匿名函数来创建过滤器对象 File[] files = f.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { File f1 = new File(dir,name); if(f1.isFile()&&f1.getName().toLowerCase().endsWith(suffix)) { return true; } return false; } }); for (File file : files) { System.out.println(file.getAbsolutePath()); } } }
2. 递归
2.1 递归的思想概述
方法定义中调用本身的现象。
注意事项:
递归要有出口,否则就是死递归;次数不能太多,否则内存会溢出;构造方法不能递归使用
2.2 递归的思想
找到出口;找到规律
2.3 递归练习
1. 有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到三个月后每个月又生一对兔子,假如兔子都不死,问第十个月兔子对数为多少?
本题的本质是斐波那契数列,如下图:
第一个月时,有一对兔子(刚生下来为小兔子,过了一个月后就变成大兔子,再过一个月就能生小兔子),第二个月是总的兔子数还是为1,但此时小兔子变成了大兔子,第三个月大兔子生了一对小兔子,所以第三个月有2对兔子(一大一小),依次类推下去可得斐波那契数列(正如图上的sum行)
public class BornRabbit { public static void main(String[] args) { System.out.println("10个月后兔子对数:"+ rabbitNumber(10)); } public static int rabbitNumber(int month) { if(month==1 || month==2) { return 1; }else { return rabbitNumber(month-1)+rabbitNumber(month-2); } } }
2.小猴子第一天摘下若干桃子,当即吃掉一半,又多吃一个,第二天早上又将剩下的桃子吃掉一半,又多吃一个,以后每天早上吃前一天剩下的一半以及另一个。到第10天早上猴子想再吃时发现,只剩下一个桃子了,问第一天猴子共摘了多少个桃子?
规律如下图
public class Monkey { public static void main(String[] args) { System.out.println(eatPeach(1)); } public static int eatPeach(int day) { if(day==10) { return 1; }else { return 2*(eatPeach(day+1)+1); } } }
3. 递归查找文件(文件夹有多层)
public class RecursionFindFiles { public static void main(String[] args) { findAllFiles("E:/exercise",".jpg"); } public static void findAllFiles(String path, String suffix) { File f = new File(path); //如果传入的路径为文件 if(f.isFile()) { if(f.getName().toLowerCase().endsWith(suffix));{ System.out.println(f.getAbsolutePath()); } }else { //是文件 //获取所有子文件 File[] listFiles = f.listFiles(); if(listFiles!=null && listFiles.length>0) { for (File file : listFiles) { findAllFiles(file.getAbsolutePath(),suffix); } } } } }
变形:递归删除文件夹以及其中的所有文件
public class RecursionDelete { public static void main(String[] args) { findAllFiles("E:/a"); } public static void findAllFiles(String path) { File f = new File(path); //如果传入的路径为文件 if(f.isFile()) { f.delete(); }else { //是文件 //获取所有子文件 File[] listFiles = f.listFiles(); if(listFiles!=null && listFiles.length>0) { for (File file : listFiles) { findAllFiles(file.getAbsolutePath()); } } f.delete(); } } }
3. IO流
3.1 IO流的概述
IO流用来处理设备之间的数据传输(上传文件和下载文件),Java对数据的操作是通过流的方式,此外java用于流的操作对象都在IO包中
3.2 IO流的分类
(1)按照数据流向
输入流: 读入数据
输出流:写出数据
(2)按照数据类型
字节流,字符流
一个汉字若按字节流处理,需要处理3次(utf8编码中一个汉字3个字节),若用字符流来处理只需要一次,但字符流一般只能用来处理文本文件,如图片就不能处理,但字节流(任何文件都可以处理)可以,所以字节流较字符流使用范围更广
(3)什么情况下使用哪种流呢?
如果数据所在的文件通过windows自带的记事本打开并能读里面的内容,就用字符流。其他用字节流
3.3 IO流常用基类
(1)字节流的抽象基类:
InputStream,OutputStream
(2)字符流的抽象基类
Reader , Writer
注意:由这四个类派生出来的子类名称都是以其父类名作为类名的后缀
如:InputStream的子类FileInputStream
Reader的子类FileReader
3.4 字节流写数据(FileOutputStream)
3.41 FileOutputStream的构造方法:
(1)FileOutputStream(File, file)
(2)FileOutputStream(String name) //一般用第二种,方便点
3.4.2 FileOutputStream的成员方法
(1)public void write(int b)
(2)public void write(byte[ ] b)
(3)public void write(byte[ ] b,int off, int len)
public class FileOutputStreamDemo { public static void main(String[] args) { FileOutputStream fos = null; try { fos = new FileOutputStream("d:/haha.txt"); fos.write(97); fos.write(98); fos.write(99); } catch (Exception e) { e.printStackTrace(); }finally { // 关流,即关闭文件 if(fos != null) { //此处要判断下不为null try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果:d盘中的haha.txt写入了abc
但是怎样写入一字符串呢?=====>将字符串转成Byte,改用第二或第三个成员方法
fos.write("忽然就流出泪来".getBytes());//此代码紧接着fos.write(99)哪行写
fos.write("哈哈忽然间想要听到他的声音".getBytes(),6,33);//后面两个int值第一个为偏移量,第二个为从偏移量位置开始写入数据的长度,这里长度以Byte为单位
运行结果:abc忽然就流出泪来,忽然间想要听到她的声音
有上面代码可知,手动关流写起来比较麻烦,JDK1.7以后提供了自动关流的方法,格式如下:
try(流的定义语句){ }catch(){ }
注意:另外创建一个流,并将数据写入原先有数据的文件,会将原先的数据覆盖掉)(如上面例子中haha.txt原先若有内容则会覆盖掉),但一个流中依次写入的数据,后者写入不会覆盖前者写入的数据
让其不覆盖院线数据的方法是:在利用构造方法创建对象时传两个参数,除了路径再传个True(默认是false),这样就不会覆盖原先的数据了
当将上诉第5行代码换成如下代码时,运行多次程序,会得到多句同样的结果,不会覆盖(原先无论运行几次都是一句话)
fos = new FileOutputStream("d:/haha.txt",true);
3.5 字节流读取数据(FileInputStream)
3.5.1 FileInputStream的构造方法
(1)FileInputStream(File file)
(2)FileInputStream(String) //一般用第二种,方便点
3.5.1 FileInputStream的成员方法
(1)public int read() 每次读取一个字节并 返回读取一个字节所对应的编码
(2)public int read(byte[] b) :每次读取数组的长度个字节,返回读回来的长度(实际读取的内容)
第二个成员方法返回的是buffer(缓存区) b的长度
1 public class FileInputStreamDemo { 2 public static void main(String[] args) { 3 try( 4 FileInputStream fis = new FileInputStream("d:/a.txt"); //啊的内容为 ab 5 ){ 6 System.out.println(fis.read()); //97 7 System.out.println(fis.read());//98 8 System.out.println(fis.read());//-1 可见当没内容可读取的时候,返回的是-1 9 } catch (Exception e) { 10 e.printStackTrace(); 11 } 12 } 13 }
若将a.txt的内容改为abc大,那么这个中文字“大”怎么读取出来呢?,代码如下
在上面第8行代码后加如下代码
1 int d = fis.read(); 2 int e = fis.read(); 3 int f = fis.read(); 4 // 创建一个Bytes数组,将上面三个字节平成一个byte数组 5 byte[] bs = new byte[] {(byte)d,(byte)e,(byte)f}; 6 System.out.println(bs);//[B@279f2327 7 System.out.println(new String(bs));//大
这样一个字节一个字节读取很费经,可以直接1kb的读取,要想1字节的话可以用for循环读取,循环控制条件就是读取数据返回值为-1
若a文件内容还是为”abc大",这次换成一次性读取1kb,代码如下
public class FileInputStreamDemo { public static void main(String[] args) { try( FileInputStream fis = new FileInputStream("d:/a.txt"); //啊的内容为 ab ){ System.out.println(fis.read()); //97 System.out.println(fis.read());//98 System.out.println(fis.read());//99 byte[] bs1 = new byte[1024]; //数组在每个位置的默认值为0,转为字符串就是空格 fis.read(bs1); System.out.println(new String(bs1)); //将byte数组转为字符串打印出来 System.out.println(new String(bs1).length());//1022 字符串的长度,说明其将空格也读取出来了 } catch (Exception e) { e.printStackTrace(); } } }
字符串长度为1022的由来:空格长度为1024-3=1021,一个汉字(大)长度为1,所以字符串长度为1022+1=1023
怎样让其读取的内容为实际的长度呢?======>让第二个成员方法接收返回值,其返回的是真实的长度
如将上面代码fis.read(bs1)改为System.out.println(fis.read(bs1)),打印的结果为3,可见返回的是真实读取的字节数
下例中把文件的内容为:没有人在热河里谈恋爱
public class FileInputStreamDemo1 { public static void main(String[] args) { try( FileInputStream fis = new FileInputStream("d:/b.txt"); //啊的内容为 ab ){ byte[] bs1 = new byte[1024]; int len; while((len = fis.read(bs1))!=-1) { System.out.println(new String(bs1,0,len));//没有人在热河里谈恋爱 System.out.println(new String(bs1,0,len).length());//10 } } catch (Exception e) { e.printStackTrace(); } } }
由结果可知,读取的内容是实际的长度,并不会将空格也读出来
拷贝文件,比如将"E:/exercise/haha/天空之城.jpg"拷贝到"E:/exercise/haha/热河.jpg"
public class CopyFile { public static void main(String[] args) { try( FileInputStream fis = new FileInputStream("E:/exercise/haha/天空之城.jpg"); FileOutputStream fos = new FileOutputStream("E:/exercise/haha/热河.jpg"); ) { byte[] bs = new byte[1024]; int len; while((len = fis.read(bs)) != -1) { //读取数据,当返回值为-1时,表示读取完毕 fos.write(bs,0,len); //写入 } } catch (Exception e) { e.printStackTrace(); } } }