黑马程序员——java基础——IO流(2)File类,IO常用流对象及IO包中其他类

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


一、File类:

1File类概述

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

2File类常见方法:

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。只有封装的对象存在并且是文件夹时,这个方法才有效。

代码示例:

import java.io.*;
class  FileDemo{
       public static voidmain(String[] args)throws IOException{
              //method_1();
              //method_2();
              //method_3();
              method_4();
       }
       //重命名
       public static void method_4(){
              File f1=newFile("C:\\Users\\Sun\\Desktop\\Test\\zz.txt");
              File f2=newFile("C:\\Users\\Sun\\Desktop\\Test\\11111.txt");
              sop("rename:"+f1.renameTo(f2));//将f1文件重命名为f2文件
       }
       //获取
       public static void method_3(){
              File ff=newFile("file.txt");
              //File ff=newFile("anv\\file.txt");
              sop("path:"+ff.getPath());//所获取的文件是可存在可不存在的
              sop("Abspath:"+ff.getAbsolutePath());
              sop("parent:"+ff.getParent());
              //该方法返回的是绝对路径中的父目录,如果获取的是相对路径,返回null
              //若相对路径中有上层目录,那么该目录就是返回结果。
       }
       //判断
       public static voidmethod_2()throws IOException{
              File dir = newFile("file.txt");
              dir.mkdirs();//创建文件夹,注意:file.txt可以是文件夹名称,不要主观臆断
              dir.createNewFile();//创建文件
              //在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在。
              //通过exists判断。
              if(dir.exists()){
                     sop("判断是否是目录 dir:"+dir.isDirectory());
                     sop("判断是否是文件 File:"+dir.isFile());
              }
              sop("判断是否为绝对路径 isAbsolute:"+dir.isAbsolute());
       }
       //创建、删除、判断
       public static voidmethod_1()throws IOException{
              //创建对象,设置在当前目录下的目录,但此时并未成功生成文件夹
              File dir=newFile("aaa\\kkk\\ccc");
              //dir.mkdir();//创建生成一级文件夹目录
              dir.mkdirs();//创建生成多级文件夹目录
              //创建文件对象,指定文件名,此时文件并未创建生成成功
              File ff=newFile("file.txt");
              ff.createNewFile();//创建文件
              //ff.deleteOnExit();//退出时,删除创建的文件
              sop("判断文件是否可执行 canExecute:"+ff.canExecute());
              sop("判断文件是否存在 exists:"+ff.exists());
       }
       //创建File对象,可以将已有的或未出现的文件或者文件夹封装成对象。
       public static voidconsMethod(){
              //四种方法作用相同。重点掌握第四种。
              //1、将a.txt封装成对象
              File f1=newFile("F:\\abc\\a.txt");
              //2、左边指父目录,右边指子文件.与f1作用一样。它们的区别是,右边的子文件是可以变化的,可以传参进去一个变量。
              File f2=newFile("F:\\abc","a.txt");
              //3、与上面2的作用一样。将文件的父目录封装成一个对象,然后作为参数传递
              File d=newFile("F:\\abc");
              File f3=newFile(d,"a.txt");
              //4、实现跨平台:由于系统分隔符是分平台的,为了体现java的跨平台性,使用File.separator作为系统的目录分隔符
              File f4=newFile("F:"+File.separator+"abc"+File.separator+"a.txt");
       }
       //输出语句方法
       public static void sop(Objecto){
              System.out.println(o);
       }
}

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

什么时候用递归呢?

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

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

递归的注意事项:

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

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

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

递归示例:

import java.io.*;
//recursive 递归
class  Recursive1{
       public static voidmain(String[] args) {
              File dir=newFile("F:\\other");
              showDir(dir);//利用递归输出指定文件夹下的所有文件
              //toBin(6);//调用二进制运算,演示递归
              //System.out.println(getSum(100));//求和方法,演示递归
       }
       //求和运算
       public static int getSum(intn){
              if(n==1){
                     return n;
              }else{
                     returnn+getSum(n-1);
              }
       }
       //递归运算,以二进制方法为例
       public static void toBin(intnum){
              if(num>0){
                     toBin(num/2);
                     System.out.print(num%2);
              }
       }
       //输出指定路径下的所有文件和文件夹
       public static voidshowDir(File dir){
              System.out.println(dir);//打印目录
              File[]files=dir.listFiles();
              for(inti=0;i<files.length;i++){
                     if(files[i].isDirectory()){//判断是否是目录(文件夹)
                            showDir(files[i]);//递归
                     }else{
                            System.out.println(files[i]);//打印指定路径下的所有文件名称
                     }
              }
       }
}

二、Properties

1、概述

1Properties一个可以将键值进行持久化存储的对象。是Hashtable的子类,它具备Map集合的特点。而且它里面还有存储的键值对,都是字符串,无泛型定义。是集合中和IO技术想结合的集合容器。

2)特点:

a)可用于键值对形式的配置文件

b)在加载时,需要数据有固定的格式,常用的是:键= 

c)键和值都是字符串

2、特有方法

1)设置

        Object setProperty(Stringkey,String value); //设置键和值,调用Hashtable的方法put

2)获取

        String getProperty(String key);  //指定key搜索value

        Set<String>stringPropertyName();//返回属性列表的键集,存入Set集合

3)加载流和存入流

        void load(InputStream ism); //从输入字节流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。

        void load(Readerreader); //从输入字符流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。

        voidlist(PrintStreamout);//将属性列表输出到指定的输出流

        voidstore(OutputStreamout,String comments); //对应load(InputStream)将属性列表(键值对)写入输出流。comments属性列表的描述。

        void store(Writerwriter,String comments); //对应load(Reader)将属性列表(键值对)写入输出流。comments属性列表的描述。 

Properties应用

/*
用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。
思路:1、用读取流关联文本信息文件。如果存在则读取,如果不存在,则创建
         2、每次运行,将文件数据存入集合中,读取值,判断次数,如果小于等于5次,则次数增加1次,如果大于则输出提示信息。
         3、将值小于等于5次的信息数据存入文件中
*/
import java.util.*;
import java.io.*;
 
class  RunCount{
       public static voidmain(String[] args)throws IOException {
              int count=runCount();
              if(count>5) {//如果程序被使用了超过5次,则终止使用,并提示
                     System.out.println("次数到了,交钱!!!!!");
                     return ;
              }else
                     System.out.println("程序第"+count+"次Run!");
       }
       //获取程序运行的次数
       public static intrunCount()throws IOException{
              Properties ps=newProperties();//创建集合对象
              File file=newFile("info.ini");//将文件进行封装
              if(!file.exists())//判断是否存在
                     file.createNewFile();
              FileReader fr=newFileReader(file);//将文件于读取流进行关联
              ps.load(fr);//加载流中的文件数据到集合中
              int count=0;//定义计数器
              Stringvalue=ps.getProperty("time");//获取次数值
              if(value!=null) {//如过值不等于null,则将其赋值给count
                     count=Integer.parseInt(value);
              }
              count++;//每启动一次自增
              ps.setProperty("time",count+"");//将次数记录住集合
              FileWriter fw=newFileWriter(file);
              ps.store(fw,"");//将集合中的数据存入硬盘文件中
              fr.close();//关流
              fw.close();
              return count;//返回程序启动的次数
       }
}

三、打印流

       打印流其实是装饰设计模式的一个体现,包含PrintStream和PrintWriter,提供了打印方法,可将各种类型的数据都原样打印。

1、打印流特点

1)提供了更多的功能,比如打印方法。可以直接打印任意类型的数据。

2)它有一个自动刷新机制,创建该对象,指定参数,对于指定方法可以自动刷新。

3)它使用的本机默认的字符编码

4)该流的print方法不抛出IOException

2、字节打印流PrintStream

1PrintStream的构造函数

PrintStream(File file)  :创建具有指定文件且不带自动行刷新的新打印流。 

PrintStream(File file, String csn) :创建具有指定文件名称和字符集且不带自动行刷新的新打印流。 

PrintStream(OutputStream out) :创建新的打印流。 

PrintStream(OutputStream out, boolean autoFlush) :创建新的打印流。 

PrintStream(OutputStream out, boolean autoFlush, String encoding) :创建新的打印流。 

PrintStream(String fileName) :创建具有指定文件名称且不带自动行刷新的新打印流。 

PrintStream(String fileName, String csn) 

2PrintStream可以操作目的:a)File对象。b)字符串路径。c)字节输出流。

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

3字符打印流 PrintWriter

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

1)该对象的目的地有四个:1File对象。2:字符串路径。3:字节输出流。4:字符输出流。开发时尽量使用PrintWriter

2)方法中直接操作文件的第二参数是编码表。直接操作输出流的,第二参数是自动刷新。

打印流示例:

import java.io.*;
class  PrintStreamDemo{
       public static voidmain(String[] args) throws IOException{
              //键盘录入
              BufferedReader bufr =
                     newBufferedReader(new InputStreamReader(System.in));
              //打印流关联文件,自动刷新
              PrintWriter out = newPrintWriter(new FileWriter("a.txt"),true);
              String line = null;
              while((line=bufr.readLine())!=null){
                     if("over".equals(line))//结束字符
                            break;
                     out.println(line.toUpperCase());
                     //out.flush();
              }
              //关流
              out.close();
              bufr.close();
       }     
}

四、SequenceInputStream:合并流

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

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

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

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

 

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

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

 代码示例:

1、合并流

import java.util.*;
import java.io.*;
class SequenceDemo {
       //一定要注意处理异常问题,此处演示只做抛异常处理
       public static voidmain(String[] args) throws IOException{
              Vector<FileInputStream>v = new Vector<FileInputStream>();//利用枚举,创建一个集合
              //添加(流)元素:文件
              v.add(newFileInputStream("C:\\Users\\Sun\\Desktop\\Test\\1.txt"));
              v.add(newFileInputStream("C:\\Users\\Sun\\Desktop\\Test\\2.txt"));
              v.add(newFileInputStream("C:\\Users\\Sun\\Desktop\\Test\\3.txt"));
 
              Enumeration<FileInputStream>en=v.elements();//可以理解为列举元素
 
              SequenceInputStreamsis=new SequenceInputStream(en);//创建序列流,对多个流进行合并,读取
              //创建输出流,关联写入文件(若不存在则创建)
              FileOutputStreamfos=new FileOutputStream("C:\\Users\\Sun\\Desktop\\Test\\4.txt");
              byte[] buf=newbyte[1024*5];
              int len=0;
              while((len=sis.read(buf))!=-1){
                     fos.write(buf,0,len);
                     //注意此处没有做流与流之间的换行问题处理。如果文件内容结尾都有换行就不存在此问题了。
                     //或者用fos.write("\r\n".getBytes());做换行处理。这里是指文件与文件之间内容的换行
              }
              fos.close();
              sis.close();//关闭所有流
       }
}

2、切割文件

import java.io.*;
class  SpiltDemo{
       public static voidmain(String[] args)throws IOException{
              partPic();
       }
       public static voidpartPic()throws IOException{
              //创建输入流,关联一个图片文件
FileInputStreamfis=new FileInputStream("C:\\Users\\Sun\\Desktop\\Test\\2.jpg");
              FileOutputStream fos=null;       //创建输出流,设为空,以便在循环中创建多个文件使用
              byte[] b=newbyte[1024*500];
              int len;
              int count=1;
              while((len=fis.read(b))!=-1){
                     fos=newFileOutputStream("C:\\Users\\Sun\\Desktop\\Test\\"+(count++)+"part.part");
                     //创建存入的文件,此文件随意设置,因为是没有实际作用的。
                     //切割成多个文件,文件名是变化的,因此用count记录变化。
                     fos.write(b,0,len);
                     fos.close();//需在循环中关闭输出流
              }
              fis.close();
       }
}

五、RandomAccessFile

1、概述

RandomAccessFile是直接继承Object类的一个类,此类的实例支持对随机访问文件的读取和写入。

构造函数:RandomAccessFile(Filefile,String mode),可已从它的构造函数中看出,该类只能操作文件(可以传入文件名字符串),而且操作文件还有模式:只读“r”、读写“rw”等四种模式。

注:如果模式为只读r,则不会创建文件,会去读一个已存在的文件,若文件不存在,则会出现异常。如果模式为读写rw,且该对象的构造函数要操作的文件不存在,会自动创建;如果存在,则不会覆盖。

2、特点:

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

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

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

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

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

代码示例:

//使用RandomAccessFileDemo进行读写操作
import java.io.*;
class RandomAccessFileDemo  {
       public static voidmain(String[] args)throws IOException {
              //指定文件
              File file =newFile("ran.txt");
              //写数据
              writeFile(file);
              //读数据
              readFile(file);
       }
       //读取指定文件中的数据
       public static voidreadFile(File file)throws IOException {
              //创建对象
              RandomAccessFileraf=new RandomAccessFile(file,"r");
              //设置指针位置
              raf.seek(8);
              //设置跳过的字节数
              //raf.skipBytes(8);
              //读取四个字节存入
              byte[] by=new byte[4];
              //读数据
              raf.read(by);
              //将存入数据的字节数组转换为字符串
              String name=newString(by);
              //根据写入规律,读取年龄,这里用到了读一个int方法
              int age=raf.readInt();
              System.out.println("name="+name+"age="+age);
              raf.close();//关流
       }
       //将数据写入指定文件中
       public static voidwriteFile(File file)throws IOException {
              //创建对象
              RandomAccessFileraf=new RandomAccessFile(file,"rw");
              //写入姓名
              raf.write("张三".getBytes());
              //写入年龄,这里调用了写一个int方法
              raf.writeInt(23);
              raf.write("李四".getBytes());
              raf.writeInt(100);
              raf.seek(8*0);//改变指针
              raf.write("小三".getBytes());
              raf.writeInt(3);
              raf.skipBytes(8*2);//改变指针
              raf.write("王五".getBytes());
              raf.writeInt(5);
              raf.close();//关流 
       }
}

六、管道流

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

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

代码示例

public static void main(String[] args) throws IOException{
<span style="white-space:pre">	</span>PipedInputStream pipin = new PipedInputStream();
<span style="white-space:pre">	</span>PipedOutputStream pipout = new PipedOutputStream();
<span style="white-space:pre">	</span>pipin.connect(pipout);
<span style="white-space:pre">	</span>new Thread(new Input(pipin)).start();
<span style="white-space:pre">	</span>new Thread(new Output(pipout)).start();
}

七、对象的序列化

1、概述

将堆内存中的对象存入硬盘,保留对象中的数据,称之为对象的持久化(或序列化)。使用到的两个类:ObjectInputStream和ObjectOutputStream

目的:将一个具体的对象进行持久化,写入到硬盘上。

注意:静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。

如何将非静态的数据不进行序列化?用transient 关键字修饰此变量即可。

 2、如何序列化

Serializable:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。

这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。

 代码示例:

//对象的序列化存储和读取
import java.io.*;
class ObjectStreamDemo {
       public static voidmain(String[] args)throws Exception{
              writeObj();//调用存储对象的方法
              readObj();//读取文件,返回对象。为避免文件不存在对象,需抛异常。
       }
       //读取对象的方法
       public static voidreadObj()throws Exception{
              //创建输入流,关联读取文件
              ObjectInputStreamois=new ObjectInputStream(new FileInputStream("obj.txt"));
              Personp=(Person)ois.readObject();//读取文件,并返回一个对象,对象需要向下转型
              System.out.println(p);
              ois.close();
       }
       //对象的序列化存储
       public static voidwriteObj()throws Exception{
              //创建输出流,关联创建写入文件.
              //注意存储的文件名一般不存成.txt文件,因为我们不需要看懂该文件。文件可自己定义:存储对象一般就用对象作后缀.object
              ObjectOutputStream oos=
                     newObjectOutputStream(new FileOutputStream("obj.txt"));
              oos.writeObject(newPerson("lisi",39,"kr"));//将对象存入流中
              oos.close();
       }
}
//对象的序列化持久存储;序列化必须实现接口Serializable
class Person implements Serializable{
       //保证对象的ID号是唯一的,避免成员改变时报错,42L是可以自定义的数值。
       //标识对象,序列化方便
       public static final longserialVersionUID=42L;
       private String name;
       //若非静态成员也不想被序列化,在其前面加transient即可。
       transient int age;
       //注意静态是不能被序列化的。因此country的值是始终不变的。
       static Stringcountry="cn";
       Person(String name,intage,String country){
              this.name=name;
              this.age=age;
              this.country=country;
       }
       public String toString(){
              returnname+":"+age+":"+country;
       }
       //此代码只为演示实例化存储效果,以下省略了set、get方法。
}


 

八、操作字节数组的流

ByteArrayInputStream:源:内存 

ByteArrayOutputStream:目的:内存。 

这两个流对象不涉及底层资源调用,操作的都是内存中数组,所以不需要关闭。

直接操作字节数组就可以了,为什么还要把数组封装到流对象中呢?因为数组本身没有方法,只有一个length属性。为了便于数组的操作,将数组进行封装,对外提供方法操作数组中的元素。

       对于数组元素操作无非两种操作:设置(写)和获取(读),而这两操作正好对应流的读写操作。这两个对象就是使用了流的读写思想来操作数组。

代码示例:

import java.io.*;
class ByteArrayStream {
       public static voidmain(String[] args) {
              //数据源。
              ByteArrayInputStreambis = new ByteArrayInputStream("ABCDEFD".getBytes());
              //数据目的
              ByteArrayOutputStreambos = new ByteArrayOutputStream();
              int by = 0;
              while((by=bis.read())!=-1){
                     bos.write(by);
              }
              System.out.println(bos.size());
              System.out.println(bos.toString());
              //     bos.writeTo(newFileOutputStream("a.txt"));需要抛异常或者处理异常,否则此句编译不通过。
       }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值