java文件操作和流

1.如何获取文件的属性信息?
  boolean exists():判断文件或目录是否存在
  String getName():返回文件或目录的名称
  String getParent():返回父路径的路径名字符串
  String getAbsolutePath():返回绝对路径的路径名字符串
  String getPath():返回抽象路径的路径名字符串
  boolean isAbsolute():判断当前路径是否为绝对路径
  boolean isDirectory():判断当前文件是否为目录
  boolean isFile():判断当前文件是否为一个标准文件
  boolean isHidden():判断当前文件是否是一个隐藏文件
  long lastModified():返回当前文件最后一次被修改的时间
  long length():返回当前文件的长度
  boolean canRead():判断是否可读
  boolean canWrite():判断是否可写
有些属性是无法获取的,例如文件的创建时间。


2.如何判断文件是否为空?
  在File类中没有提供直接的方法判断文件是否为空,可以借助length()返回值为0来判断


3.如何实现文件的创建、删除、移动?
  创建:createNewFile()和createTempFile()
  删除:delete()
  移动:renameTo()
创建文件时应先检测文件是否存在,如果不存在就创建;如果目录错误,创建时也会发生异常。


4.如何创建和删除文件夹?
  创建:mkdir()和mkdirs()
  删除: delete(),如果不为空,需要遍历文件夹递归删除。


5.如何遍历目录中所有的文件?
  public File[] listFiles()
  因为目录中可能包含文件夹,所以需要借助listFiles()方法递归遍历目录中所有的文件。
  注意与public String[] list()区分开来。


6.如何获取文件夹的大小?
  long size=0;
  public void getDirSize(File file){
     if(file.isFile()){
       size+=file.length();
     }else if(file.isDirectory()){
       File[] f1=file.listFiles();
       for(int i=0;i<f1.length;i++){
          getDirSize(f1[i]);
       }
     }
  } 


7.什么是流?如何分类?具体包含哪些类?
  流代表经过管道流通的数据,为了进行数据的输入输出操作,Java把不同的输入输出源抽象表述为流。
  按照流向分类,可以分为输入流和输出流。
  按照处理单位不同,可以分为字节流和字符流。
  字节流:
  主要包含InputStream和OutputStream两个基础类。
  1》FileInputStream/FileOutputStream(读写文件)
  2》BufferedInputStream/BufferedOutputStream(读写缓冲流)
  3》ByteArrayInputStream/ByteArrayOutputStream(按字节数组读写内存中临时数据) 
  4》DataInputStream/DataOutputStream(读写基本类型和String)
  5》ObjectInputStream/ObjectOutputStream(读写对象)
  6》PipedInputStream/PipedOutputStream(主要在线程中使用)
  7》PrintStream(打印流,可以一次写一行)
  8》RandomAccessFile(随机访问文件),继承Object,不属于InputStream和OutputStream的子类

  字符流:
  主要包含Reader和Writer两个基础类。
  1》FileReader/FileWriter(只能采用系统默认编码方式读写)
  2》InputStreamReader/OutputStreamWriter(转换流,采用指定编码方式读写)
  3》BufferedReader/BufferedWriter(缓冲流,借助readLine()和newLine()可以一次读写一行)
  4》CharArrayReader/CharArrayWriter(按字符数组读写)
  5》PipedReader/PipedWriter(主要在线程中使用)
  6》PrintWriter(打印流,可以一次写一行)
所有流对象的应用,都有共同步骤:
        构建流对象;读写数据;释放资源,假如有多层流的嵌套,直接关闭外层流即可。



8.如何实现字节流和字符流之间的转化?
  字节流和字符流转化时需要注意两个问题:一个字符等于两个字节;编码不一致。
  InputStreamReader可完成字节流到字符流的转化。为了达到最高效率,可以使用BufferedReader包装。
  OutputStreamWriter可完成字符流到字节流的转化。为了达到最高效率,可以使用BufferedWriter包装。


9.如何判断要读的文件是否到达末尾?
  InputStream的read()方法返回-1表示到达末尾,Reader的read()方法返回-1表示到达末尾,BufferedReader的readLine()方法返回  null表示到达末尾。



10.如何读文件、写文件?
   如果是字节流文件,例如图片、流媒体等,可以用如下方式获取。
   public void testRead() throws Exception{
      int temp=-1;
      FileInputStream in=new FileInputStream("d:\\a.txt");
      BufferedInputStream bs=new BufferedInputStream(in);
      while((temp=bs.read())!=-1){
         System.out.println((char)temp);
      }
      bs.close();
      in.close();

   }


  如果是纯文本字符文件,例如txt,log,csv等格式,可以用如下方式获取。
  public void testRead() throws Exception{
      String temp=null;
      FileReader in=new FileReader("d:\\a.txt");
      BufferedReader br=new BufferedReader(in);
      while((temp=br.readLine())!=null){
         System.out.println(temp);
      }
      br.close();
      in.close();

  }


  如果是字节流文件,例如图片、流媒体等,可以用如下方式写入。
  public void testWrite() throws Exception{
       FileOutputStream fs=new FileOutputStream("d:\\a.txt");
       BufferedOutputStream bs=new BufferedOutputStream(fs); 
       fs.write('a'); 
       bs.close();
       fs.close();
  }

  如果是纯文本字符文件,例如txt,log,csv等格式,可以用如下方式写入。
  public void testWrite() throws Exception{
       FileWriter fw=new FileWriter("d:\\a.txt");
       BufferedWriter bw=new BufferedWriter(fw);
       bw.write('a');
       bw.close();
       fw.close();
  }


11.如何以追加方式写文件?
   FileOutputStream和FileWriter都提供了相应的构造方法用来指定是否以追加方式写文件。
   public FileOutputStream(File file,boolean append) throws FileNotFoundException;
   public FileOutputStream(String fileName,boolean append) throws FileNotFoundException;
   public FileWriter(File file,boolean append) throws IOException;
   public FileWriter(String fileName,boolean append) throws IOException;


12.如何实现文件和文件夹的复制?
   文件复制:
   public void copyFile(String oldPath,String newPath){
        InputStream in=null;
        FileOutputStream fs==null;
      try{
         int byteread=0;
         File oldFile=new File(oldPath);
         if(oldFile.exists()){
           in=new FileInputStream(oldPath);
           fs=new FileOutputStream(newPath);
           byte[] buffer=new byte[1024];
           while((byteread=in.read(buffer))!=-1){
              fs.write(buffer,0,byteread);
           }
         }
      }catch(Exception e){
         e.printStackTrace();
      }finally{
         if(in!=null)
         in.close();
         if(fs!=null)
         fs.close();
      }
   }


   文件夹复制:
   public void copyFolder(String oldPath,String newPath){
        FileInputStream in=null;
        FileOutputStream fs==null;
      try{
         (new File(newPath)).mkdirs();
         int byteread=0;
         File oldFile=new File(oldPath);
         String[] nameArray=oldFile.list();
         File temp=null;
         for(int i=0;i<nameArray.length;i++){
            if(oldPath.endsWith(File.separator)){
                temp=new File(oldPath+nameArray[i]);
            }else{
                temp=new File(oldPath+File.separator+nameArray[i]);
            }
         }
         if(temp.isFile()){
           in=new FileInputStream(temp);
           fs=new FileOutputStream(newPath+"/"+(temp.getName().toString()));
           byte[] buffer=new byte[1024*5];
           while((byteread=in.read(buffer))!=-1){
              fs.write(buffer,0,byteread);
           }
           fs.flush();
         }
         if(temp.isDirectory()){
            copyFolder(oldPath+"/"+nameArray[i],newPath+"/"+nameArray[i]);
         }
      }catch(Exception e){
         e.printStackTrace();
      }finally{
         if(in!=null)
         in.close();
         if(fs!=null)
         fs.close();
      }
   }
    


13.如何在文件的任意位置进行读写?
   利用RandomAccessFile可以实现任意位置读写,提供了两个构造方法:
   public RandomAccessFile(File file,String mode) throws FileNotFoundException;
   public RandomAccessFile(String fileName,String mode) throws FileNotFoundException;
   其中mode有四种,“r”“rw”“rws”“rwd”,“rwd”可用于减少IO操作次数。
   写操作:
    public static void testWrite() throws Exception{
       RandomAccessFile rf=new RandomAccessFile("c:\\a.txt","rw");
       rf.seek(rf.length());//指定位置写入,以字节为单位
       rf.writeByte('a');
       rf.writeUTF("我");
       rf.close();
    }
    读操作:
    public static void testRead() throws Exception{
      RandomAccessFile rf=new RandomAccessFile("c:\\a.txt","r");
       rf.seek(0);//指定位置读取,以字节为单位
       System.out.println((char)rf.read());
       System.out.println(rf.readUTF());
       rf.close();
    }



14.使用缓冲流写文件时,为什么内容没有写入?
   使用缓冲流写文件时,必须显示调用flush()和close()方法,否则无法将缓冲区中的信息输出到目标文件中。
  
注意:使用close()方法时,会默认执行flush()方法的功能,因此close()方法也可以将缓冲区中的信息输出到目标文件中。



15.如何实现文件的分割与合并?
   使用RandomAccessFile类可以实现文件的分割与合并。
   文件分割:
   //fileName源文件,filterFolder分割文件所在目录,size每一份大小,以KB为单位
   public void cut(String fileName,String filterFolder,int size) throws Exception{
        size*=1024;
        int maxx=0;
        File outFolder=new File(filterFolder);
        if(!outFolder.exists()){
           outFolder.mkdirs();
        }
        File inFile=new File(fileName);
        int fileLength=(int)inFile.length();
        int value=fileLength/size;//要分割的个数
        RandomAccessFile rf=new RandomAccessFile(inFile,"r");
        int i=0;
        int j=0;
        //根据要分割的数目输出文件
        for(;j<value;j++){
           File outFile=new File(filterFolder+File.separator+inFile.getName()+j+"tmp");
           RandomAccessFile outt=new RandomAccessFile(outFile,"rw");
           maxx+=size;
           for(;i<maxx;i++){
               outt.write(rf.read());
           }
           outt.close();
        }
        File outFile=new File(filterFolder+File.separator+inFile.getName()+j+"tmp");
           RandomAccessFile outt=new RandomAccessFile(outFile,"rw");
           for(;i<fileLength;i++){
               outt.write(rf.read());
           }
           outt.close();
           rf.close();
   }


   文件合并:
   //fileName合并之后的文件,filterFolder分割文件所在目录,filterName分割后的文件后缀
   public void unit(String fileName,String filterFolder,final String filterName) throws Exception{
        File[] tt;
        File inFile=new File(filterFolder);//在当前目录下的文件
        File outFile=new File(fileName);//输出的文件
        RandomAccessFile outt=new RandomAccessFile(outFile,"rw");
        //取得符合条件的文件名
        tt=inFile.listFiles(new FilenameFilter(){
              public boolean accept(File dir,String name){
                  String rr=new File(name).toString();
                  return rr.endsWith(filterName);
              }
         }
         );
         //将所有文件写入一个文件
         for(int i=0;i<tt.length;i++){
             RandomAccessFile inn=new RandomAccessFile(tt[i],"r");
             int c;
             while((c=inn.read())!=-1){
                outt.write(c);
             }
         }
         outt.close();
   }



16.什么是NIO?与IO有什么区别和联系?
   NIO与原有的IO有相同的作用和目的,是基于原有IO的扩展和改进。
   NIO新特性:
   1》更加灵活的可伸缩的IO接口,包括Channel的出现和新的多元的非阻塞的IO机制;
   2》快速缓存的二进制和字符IO接口;
   3》字符集的编码器和解码器;
   4》基于Perl风格的正则表达式的模式匹配机制;
   5》改良的文件系统接口,支持锁定和内存映射;
   6》新的违例类能使得用户更加有针对性的处理IO错误;
   7》增加了对并发的支持。
   NIO并不是对IO的替代,虽然NIO在IO操作时速度快,但是其实现借助了大量的本地代码,对操作系统和硬件平台有很强的依赖性。
   
   区别:
     一.IO是面向流的,NIO是面向缓冲区的。
     二.IO的各种流是阻塞的,NIO是非阻塞模式。
     三.Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:
          这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。


17.如何使用NIO读写文件?
   并不是所有的IO流类都支持NIO,支持NIO的有FileInputStream,FileOutputStream,RandomAccessFile.
   读操作:
   写操作:
   文件复制:


18.什么是字符编码和解码?
   在字符和字节信息相互转化时,需要涉及到编码和解码操作。
   在NIO中提供了与字符集、编码、解码相关的API,如:
   Charset:字符集,用于描述字符和字节之间的命名映射关系。
   CharsetDecoder:解码器,用于实现将字节解码为字符。
   CharsetEncoder:编码器,用于实现将字符编码为字节。



19.读写文件时为什么中文字符经常会产生乱码?
   存储文件时,需要将文件中的字符转换为二进制字节信息,属于编码。读取文件时,需要将字节信息转换为字符信息,称为解码。
   写文件和读文件使用不同的字符集时,就会产生乱码。另外API使用不当也会产生乱码,例如InputStream读取中文字符的文件,
   InputStream是以字节为单位读取的,所以会将中文字符拆成两个字节分两次读取,这就是中文乱码而英文正常的原因。


20.如何解决FileReader读文件乱码的问题?
   因为FileReader读文件只能采用系统默认编码,所以可能会存在字符集不一致问题。解决办法就是放弃FileReader采用   InputStreamReader,在读取时指定编码方式。


21.为什么DataInputStream和DataOutputStream读文件时乱码?
   在读写文件时,DataInputStream和DataOutputStream必须成对使用,而且读写时必须采取对应的顺序和类型,否则会产生乱码。


22.如何实现文件锁定功能?
   有两种方式:一种是使用RandomAccessFile,还有一种是使用FileChannel。
   1》new RandomAccessFile(file,"rws")中的"rws"代表以独占方式访问文件
   2》FileChannel是NIO提供的一个类,该类提供了锁定的方法。
     RandomAccessFile rf=new RandomAccessFile(new File("c:\\a.txt"),"rws");
     FileChannel fc=rf.getChannel();
     //尝试获取文件锁,如果被占用返回null
     FileLock fl=fc.tryLock();
     if(fl.isValid()){
         //省略写入操作代码
         fl.realease();//释放文件锁
     }else{
         //其他用户正在写入
     }



23.如何实现对文件和字符串加密、解密?
   常用的加密算法有DES、RSA、MD5、SHA-1。注意MD5和SHA-1算法只能加密不能解密,是不可逆的。



24.如何实现对文件和目录的压缩、解压缩?
    java支持的压缩格式有zip、gzip、jar等,下面以zip举例:zip压缩可以使用ZipEntry类和ZipOutputStream类,zip解压缩可以使用ZipEntry类和ZipInputStream类。
    


25.如何读写properties文件?
   可以使用Properties类读写properties文件,它使用的是ISO-8859-1编码,每个字节是一个Latin1字符,因此对于非Latin1字符在读写时就会发生乱码,需要先进行转换。为了避免读写中文字符乱码,需要先将字符进行转义。


26.如何读写xml文件?
   可以使用DOM和SAX读写xml文件。
   DOM可以实现对xml的读取、增加、修改、删除,但是在文件很大时比较耗费内存。
   SAX是一边读取一边处理,对内存要求较低,但是仅支持文件读取。



27.如何读写xml中的元素属性?
   对xml中元素属性的读写操作需要使用DOM技术实现。使用Element类的getAttributeNode()和getAttribute()方法可以获取属性信息,通过setAttributeNode()和setAttribute()方法可以设置属性信息。
   读取xml中元素属性:
   public void parseAttr(Element elt,String name){
        //String val=elt.getAttributeName(name);//获取属性值
        //System.out.println(val);
        Attr attr=elt.getAttributeNode(name);//获取属性对象
        if(attr!=null){
          System.out.println(attr.getName()+"/"+attr.getValue());
        }
        
   }
   设置xml中元素属性:
   public void setAttr(Document doc,Element elt){
       Attr attr=doc.createAttribute("年龄");
       Text attrText=doc.createTextNode(68);
       attr.appendChild(attrText);
       elt.setAttributeNode(attr);
       //elt.setAttribute(name,value);//设置属性值
   }



28.如何读写csv格式文件?
   csv文件与普通TXT文件不同的是,csv文件中的每项数据都是用逗号分隔,因此读写csv文件与读写txt文件相比只是多处理一些逗号和换行符而已。
   写csv文件:
   public static void main(String[] args){
         try{
            FileWriter fw=new FileWriter("c:\\a.csv");
            fw.write("aa,bb,cc,dd,ee\r\n");
            fw.write("aa1,bb1,cc1,dd1,ee1\r\n");
            fw.close();
         }catch(IOException e){
            e.printStackTrace();
         }
   }
  
   读csv文件:
   public static void main(String[] args){
         InputStreamReader fr=null;
         BufferedReader br=null;
         try{
            fr=new InputStreamReader(new FileInputStream("d:\\a.csv"));
            br=new BufferedReader(fr);
            String rec=null;
            String[] argsArr=null;
            while((rec=br.readLine())!=null){
                 argsArr=rec.split(",");
                 for(int i=0;i<argsArr.length;i++){
                     System.out.println("num"+(i+1)+":"+argsArr[i]);
                 }
            }
         }catch(IOException e){
            e.printStackTrace();
         }finally{
            try{
               if(fr!=null)fr.close();
               if(br!=null)br.close();
            }catch(IOException e){
               e.printStackTrace();
            }
         }
   }
注意:csv文件内容中的逗号为半角,不能为全角。



29.如何为图片文件生成缩略图?
  在实现缩略图的过程中,主要使用BufferedImage和ImageIO两个类。首先将图片信息读取到BufferedImage对象中,接着构造缩略图的BufferedImage对象,最后将缩略图输出。ImageIO提供了read()和write()方法,用于读写图片中的信息。
  示例代码:
   public class Test{
       //fromFileStr源图片路径,saveToFileStr缩略图路径,width:缩略图的宽,height:缩略图的高
       public static void saveImageAsJpg(String fromFileStr,String saveToFileStr,int width,int height){
             BufferedImage srcImage;
             String imgType="JPEG";
             if(fromFileStr.toLowerCase().endsWith(".png")){
                 imgType="PNG";
             }
             File saveFile=new File(saveToFileStr);
             File fromFile=new File(fromFileStr);
             srcImage=ImageIO.read(fromFile);
             if(width>0||height>0){
                srcImage=resize(srcImage,width,height);
             }
             ImageIO.write(srcImage,imgType,saveFile);
       }


       //将源图片的BufferedImage生成缩略图
       public static BufferedImage resize(BufferedImage source,int targetW,int targetH){
            int type=source.getType();
            BufferedImage target=null;
            double sx=(double)targetW/source.getWidth();
            double sy=(double)targetH/source.getHeight();
            //如果不需要等比缩放,将下面的if-else去掉即可
            if(sx>sy){
               sx=sy;
               targetW=(int)(sx*source.getWidth());
            }else{
               sy=sx;
               targetH=(int)(sy*source.getHeight());
            }
            if(type==BufferedImage.TYPE_CUSTOM){
               ColorModel cm=source.getColorModel();
               WritableRaster raster=cm.createCompatibleWritableRaster(targetW,targetH);
               boolean alphaPremultiplied=cm.isAlphaPremultiplied();
               target=new BufferedImage(cm,raster,alphaPremultiplied,null);
            }else{
               target=new BufferedImage(targetW,targetH,type);
            }
            Graphics2D g=target.createGraphics();
            g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
            g.drawRenderedImage(source,AffineTransform.getScaleInstance(sx,sy));
            g.dispose();
            return target;
       }

       public static void main(String[] args){
            try{
               Test.saveImageAsJpg("d:/a.gif","d:/me/b.gif",50,50);
            }catch(Exception e){
               e.printStackTrace();
            }
       }
   }



30.如何读写Excel文件?
   对于Excel文件操作需要借助第三方组件,如POI和JXL。


31.如何读写Word文件?
   同样需要借助第三方组件才能操作,例如jacob、iText、POI和java2word。其中jacob是最常用的一个。


32.如何读写PDF文件?
   同样需要借助第三方组件才能操作,例如pdfbox、xpdf、iText。


33.Java中的NIO,BIO,AIO分别是什么?

NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

 BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

猜你喜欢

转载自blog.csdn.net/DarlingRay/article/details/80858904