黑马程序员——Java语言基础——07.IO流(2)File类和其他流

来源:互联网 发布:汉诺塔非递归算法java 编辑:程序博客网 时间:2024/05/15 22:39

------- android培训、java培训、期待与您交流! ----------

2-1 File类

将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据

2-1-1 File类常见方法

1:创建。

boolean createNewFile():在指定目录下创建文件,如果该文件已存在,则不创建。而对操作文件的输出流而言,输出流对象已建立,就会创建文件,如果文件已存在,会覆盖。除非续写。

boolean mkdir():创建此抽象路径名指定的目录。

boolean mkdirs():创建多级目录。 

2:删除。

boolean delete():删除此抽象路径名表示的文件或目录。

void deleteOnExit():在虚拟机退出时删除。

注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。

window的删除动作,是从里往外删。注意:java删除文件不走回收站。要慎用。

3:获取.

long length():获取文件大小。

String getName():返回由此抽象路径名表示的文件或目录的名称。

String getPath():将此抽象路径名转换为一个路径名字符串。

String getAbsolutePath():返回此抽象路径名的绝对路径名字符串。

String getParent():返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目录,则返回 null。

long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。

File.pathSeparator:返回当前系统默认的路径分隔符,windows默认为 “;”。

File.Separator:返回当前系统默认的目录分隔符,windows默认为 “\”。

4:判断:

boolean exists():判断文件或者文件夹是否存在。

boolean isDirectory():测试此抽象路径名表示的文件是否是一个目录。

boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。

boolean isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件。

boolean isAbsolute():测试此抽象路径名是否为绝对路径名。

5:重命名。

 boolean renameTo(File dest):可以实现移动的效果。剪切+重命名。

String[] list():列出指定目录下的当前的文件和文件夹的名称。包含隐藏文件。

如果调用list方法的File 对象中封装的是一个文件,那么list方法返回数组为null。如果封装的对象不存在也会返回null。只有封装的对象存在并且是文件夹时,这个方法才有效。

示例:

class FileDemo{public static void main(String[] args)throws Exception{method4();}public static void method1(){try{File f = new File("c:\\shabi.txt");f.createNewFile();File file2 = new File("D:\\temp");// D;/temp 为一个目录File tempFile1= file2.createTempFile("msg", ".tmp",file2);//指定目录创建File tempFile2 = file2.createTempFile("msg", ".tmp");//系统默认目录创建System.out.println(tempFile2.getAbsolutePath());f.delete();}catch (IOException e){throw new RuntimeException("nishishabi");}}public static void method2()throws Exception{File dir = new File("nish\\b");System.out.println(dir.mkdirs());//多级目录System.out.println(dir.mkdir());//单个目录}public static void method3()throws Exception{File file = new File("d:\\workspace\\lianxi\\day20");//记住在判断文件对象是否是文件或者目的时,必须要先判断该文件对象封装的内容是否存在。//通过exists判断。sop(file.exists());sop(file.isDirectory());}public static void method4()throws Exception{File f = new File("d:\\workspace\\lianxi\\day20\\FileDemo.java");sop(f.getPath());sop(f.getAbsoluteFile());sop(f.getAbsolutePath());sop(f.getParent());sop(f.getParentFile());}public static void sop(Object obj){System.out.println(obj);}}
示例二:使用list和listFiles分别获取文件夹中的文件名称和文件对象

class FileDemo2 {public static void main(String[] args) throws Exception{File dir = new File("d:\\workspace\\lianxi\\day20");File[] files = dir.listFiles();//这个方法是获取到了对象,list仅仅获取了名称for(File f : files){System.out.println(f.getName()+"::"+f.length());}}public static void method1()throws Exception{/*File[] f = File.listRoots();for (File f1 : f){sop(f1);}*/File file = new File("d:\\workspace\\lianxi\\day20"); String[] str = file.list();for (String s : str){sop(s);}}public static void method2()throws Exception{File file = new File("d:\\workspace\\lianxi\\day20"); //使用匿名内部类的方式构造过滤器String[] str = file.list(new FilenameFilter()//很重要,要熟记。{public boolean accept(File dir,String name){return name.endsWith(".java");}});for (String s : str){sop(s);}}public static void sop(Object obj){System.out.println(obj);}}

2-1-2 递归

递归:就是函数自身调用自身。

什么时候用递归呢?

当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。

简单说:功能内部又用到该功能,但是传递的参数值不确定。(每次功能参与运算的未知内容不确定)。

递归的注意事项:

1:一定要定义递归的条件。

2:递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出错误。

其实递归就是在栈内存中不断的加载同一个函数。

示例一:使用递归获取一个目录中的所有文件名

class FileDemo3 {public static void main(String[] args) {File dir = new File("d:\\workspace\\lianxi");fileRecursion(dir,0);}public static String getLevel(int level){StringBuilder sb = new StringBuilder();for (int i=0; i<=level; i++){sb.append("|--");}return level+" "+sb.toString();}public static void fileRecursion(File dir,int level){sop(getLevel(level)+dir);level++;File[] f = dir.listFiles();for (File f1 : f){if(f1.isDirectory())fileRecursion(f1,level);if(f1.isFile())sop(dir);}}public static void sop(Object obj){System.out.println(obj);}}
示例二:删除一个目录中的所有文件

class FileListDemo{public static void main(String[] args)throws IOException{File dir = new File("d:\\workspace\\lianxi");List<File> li = new ArrayList<File>();getFile(dir,li);toText(li);}public static void getFile(File dir,List<File> li){File[] f = dir.listFiles();li.add(dir);for (File f1 : f){if(f1.isDirectory())getFile(f1,li);elseli.add(f1);}//删除时这里需要加一句delete,打印和添加到集合不需要}public static void toText(List<File> li)throws IOException{BufferedWriter bufw = new BufferedWriter(new FileWriter("sb.txt"));for (File f : li){String s = f.getAbsolutePath();bufw.write(s);bufw.newLine();bufw.flush();}bufw.close();}}

2-2 Properties类

特点:1:可以持久化存储数据。2:键值都是字符串。3:一般用于配置文件。

|-- load():将流中的数据加载进集合。

原理:其实就是将读取流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过 = 对该行数据进行切割,左边就是键,右边就是值,将键、值存储到properties集合中。

|-- store():写入各个项后,刷新输出流。

|-- list():将集合的键值数据列出到指定的目的地

Properties是hashtable的子类,也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串,是集合中和IO技术相结合的集合容器。
该对象的特点:可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键=值。

练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。

思路:

如果使用次数已到,那么给出注册提示,很容易想到的是:计数器。
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着该应用程序的退出,该计数器也在内存中消失了,下一次在启动该程序,又重新开始从0计数,这样不是我们想要的。
程序即使结束,该计数器的值也存在,下次程序启动在会先加载该计数器的值并加1后在重新存储起来。
所以要建立一个配置文件。用于记录该软件的使用次数,该配置文件使用键值对的形式。这样便于阅读数据,并操作数据。
键值对数据是map集合。
数据是以文件形式存储,使用io技术。那么map+io -->properties.

配置文件可以实现应用程序数据的共享。

class PropertiesTest {public static void main(String[] args) throws Exception{Properties p = new Properties();File f = new File("count.ini");if (! f.exists())f.createNewFile();//Properties p = new Properties();////File f = new File("count.ini");//if(!f.exists())//f.createNewFile();FileInputStream fis = new FileInputStream(f);p.load(fis);int num = 0;String times = p.getProperty("time");if (times != null){num = Integer.parseInt(times);}if (num >=5){System.out.println("使用次数已满,请充值");return ;//返回值为void的函数中return,用来终止函数运行}num++;p.setProperty("time",num+"");FileOutputStream fos = new FileOutputStream(f);p.store(fos,"ruanjianshiyongcishu");fos.close();fis.close();}}

2-3 IO流其他常用流

2-3-1 打印流

PrintStream可以操作目的:1:File对象。2:字符串路径。3:字节输出流。

前两个都JDK1.5版本才出现。而且在操作文本文件时,可指定字符编码了。

当目的是一个字节输出流时,如果使用的println方法,可以在printStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新,而不是等待缓冲区满了再刷新。最终print方法都将具体的数据转成字符串,而且都对IO异常进行了内部处理。

既然操作的数据都转成了字符串,那么使用PrintWriter更好一些。因为PrintWrite是字符流的子类,可以直接操作字符数据,同时也可以指定具体的编码。

PrintWriter:具备了PrintStream的特点同时,还有自身特点:

该对象的目的地有四个:1:File对象。2:字符串路径。3:字节输出流。4:字符输出流。

开发时尽量使用PrintWriter。

方法中直接操作文件的第二参数是编码表。

直接操作输出流的,第二参数是自动刷新。

示例:

class PrintStreamDemo {public static void main(String[] args) throws IOException{BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("abc.txt")),true);String line = null;while ((line = bufr.readLine()) != null){if("over".equals(line))break;pw.println(line.toUpperCase());}bufr.close();pw.close();}}

2-3-2 序列流

作用就是将多个读取流合并成一个读取流。实现数据合并。

表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

这样做,可以更方便的操作多个读取流,其实这个序列流内部会有一个有序的集合容器,用于存储多个读取流对象。

该对象的构造函数参数是枚举,想要获取枚举,需要有Vector集合,但不高效。需用ArrayList,但ArrayList中没有枚举,只有自己去创建枚举对象。

但是方法怎么实现呢?因为枚举操作的是具体集合中的元素,所以无法具体实现,但是枚举和迭代器是功能一样的,所以,可以用迭代替代枚举。

合并原理:多个读取流对应一个输出流。

示例:

class SequenceDemo {public static void main(String[] args) throws IOException{Vector<FileInputStream> v = new Vector<FileInputStream>();v.add(new FileInputStream("c:\\1.txt"));v.add(new FileInputStream("c:\\2.txt"));v.add(new FileInputStream("c:\\3.txt"));SequenceInputStream sis = new SequenceInputStream(v.elements());BufferedReader bufr = new BufferedReader(new InputStreamReader(sis));BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("c:\\4.txt")));String line = null;while ((line = bufr.readLine()) != null){bufw.write(line);bufw.flush();}bufw.close();bufr.close();}}

切割原理:一个读取流对应多个输出流

示例:

class SplitDemo{public static void main(String[] args)throws IOException{merge();}    public static void merge()throws IOException{File f = new File("d:\\1.jpg");List<FileInputStream> list = new ArrayList<FileInputStream>();for (int i=1; i<=3; i++){list.add(new FileInputStream(f+".part"+i));}final Iterator<FileInputStream> it = list.iterator();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("d:\\4.jpg");byte[] buf = new byte[1024];int len = 0;while ((len = sis.read(buf)) != -1){fos.write(buf,0,len);}sis.close();fos.close();}public static void split()throws IOException{File f = new File("d:\\1.jpg");BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));byte[] buf = new byte[1024*100];int len = 0;int count = 0;while ((len = bis.read(buf)) != -1){count++;FileOutputStream fos = new FileOutputStream(f+".part"+count);fos.write(buf,0,len);fos.close();}}}

2-3-3 RandomAccessFile

特点:

1:该对象即可读取,又可写入。

2:该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组。

3:可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。

4:该对象操作的源和目的必须是文件。 

5:其实该对象内部封装了字节读取流和字节写入流。

注意:实现随机访问,最好是数据有规律。

class RandomAccessFileDemo {public static void main(String[] args) throws IOException{writeFile();readFile();}public static void writeFile()throws IOException{RandomAccessFile raf = new RandomAccessFile("shabi.txt","rw");raf.seek(8);raf.write("sb".getBytes());raf.writeInt(27);raf.close();}public static void readFile()throws IOException{RandomAccessFile raf = new RandomAccessFile("shabi.txt","rw");//byte[] buf = new byte[4];//raf.read(buf);////String s = new String(buf);//调整对象中指针。//raf.seek(8*1);//跳过指定的字节数//raf.skipBytes(8);raf.seek(4);int age = raf.readInt();byte[] buf = new byte[4];raf.read(buf);String s = new String(buf);System.out.println(age+s);raf.close();}}

2-3-4 管道流

道读取流和管道写入流可以像管道一样对接上,管道读取流就可以读取管道写入流写入的数据。

注意:需要加入多线程技术,因为单线程,先执行read,会发生死锁,因为read方法是阻塞式的,没有数据的read方法会让线程等待。

2-3-5 DataInputStream与DataOutputStream

可以用于操作基本数据类型的数据的流对象。

class DataStreamDemo {public static void main(String[] args) throws IOException{//writeData();//readData();//writeUTFDemo();//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");////osw.write("你好");//osw.close();//readUTFDemo();}public static void readUTFDemo()throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));String s = dis.readUTF();System.out.println(s);dis.close();}public static void writeUTFDemo()throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));dos.writeUTF("你好");dos.close();}public static void readData()throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));int num = dis.readInt();boolean b = dis.readBoolean();double d = dis.readDouble();System.out.println("num="+num);System.out.println("b="+b);System.out.println("d="+d);dis.close();}public static void writeData()throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));dos.writeInt(234);dos.writeBoolean(true);dos.writeDouble(9887.543);dos.close();ObjectOutputStream oos = null;oos.writeObject(new O());}}

2-3-6 ByteArrayInputStream与ByteArrayOutputStream

ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。

示例:

class ByteArrayStream {public static void main(String[] args) {read();}public static void read(){ByteArrayInputStream bais = new ByteArrayInputStream("shabi".getBytes());ByteArrayOutputStream baos = new ByteArrayOutputStream();int buf = 0;while((buf = bais.read()) != -1){baos.write(buf);}System.out.println(baos.toString());}}

------- android培训、java培训、期待与您交流! ----------

0 0