A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© ssx0101 中级黑马   /  2012-11-15 16:31  /  1846 人查看  /  7 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 曹自祥 于 2012-11-15 17:37 编辑
  1. package pt;

  2. import java.io.FileInputStream;
  3. import java.io.FileNotFoundException;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;

  6. public class File {

  7.         /**
  8.          * @param args
  9.          */
  10.         public static void main(String[] args){
  11.                 FileInputStream fis=null;//字节输入流
  12.                 FileOutputStream fos=null;//字节输出流
  13.                 try {
  14.                         fis = new FileInputStream("90914011.jpg");
  15.                         fos=new FileOutputStream("copy_90914011.jpg");
  16.                         fis.mark(1024*100);//标记输入流的当前位置,此输入流在标记位置无效之前允许读取的字节数为1024*100
  17.                         int len;
  18.                         byte[] buf=new byte[1024*100];
  19.                         len=fis.read(buf);//从流中读取数据并存进字节数组buf
  20.                         if(len!=-1){
  21.                                 fos.write(buf, 0, len);//把字节数组的数据写进输出流。由于字节流对字节进行直接操作,不需要刷新即可直接写进文件里。
  22.                                 System.out.println("复制文件\"copy_90914011.jpg\"成功!");
  23.                         }
  24.                                 
  25.                         else
  26.                                 System.out.println("复制文件“copy_90914011.jpg”失败!");
  27.                         
  28.                         
  29.                         //把输入流重定位到上次标记的位置,再进行一次文件复制
  30.                         if(fis.markSupported())
  31.                                 fis.reset();
  32.                         else
  33.                                 System.out.println("reset失败!");
  34.                         
  35.                         fos=new FileOutputStream("copy_1_90914011.jpg");
  36.                         len=fis.read(buf);
  37.                         if(len!=-1){
  38.                                 fos.write(buf, 0, len);
  39.                                 System.out.println("复制文件\"copy_90914011.jpg\"成功!");
  40.                         }
  41.                         else
  42.                                 System.out.println("复制文件\"copy_1_90914011.jpg\"失败!");
  43.                 } catch (FileNotFoundException e) {
  44.                         System.out.println("打开文件失败!"+e.toString());
  45.                 } catch (IOException e) {
  46.                         System.out.println(e.toString());
  47.                 }
  48.                 //关闭数据流
  49.                 finally{
  50.                         if(fis!=null)
  51.                                 try {
  52.                                         fis.close();
  53.                                 } catch (IOException e) {
  54.                                         System.out.println(e.toString());
  55.                                 }
  56.                      if(fis!=null)
  57.                                 try {
  58.                                         fis.close();
  59.                                 } catch (IOException e) {
  60.                                         System.out.println(e.toString());        
  61.                                 }
  62.                 }
  63.         }
  64. }
复制代码
运行结果:
复制文件"copy_90914011.jpg"成功!
reset失败!
复制文件"copy_1_90914011.jpg"失败!

疑问:
FileInputStream继承了基类InputStream,应该就可以使用基类的mark和reset方法,但以上结果显然说明有问题。
我试过把FileInputStream换成BufferedInputStream就能正常使用mark和reset方法。
我猜想可能是基类InputStream定义的mark和reset操作只是空操作,其子类FileInputStream没有对这两个方法进行重写,而子类BufferedInputStream对这两个方法进行了重写,定义了标记和重置操作。


求真相!

评分

参与人数 1技术分 +1 收起 理由
古银平 + 1 赞一个!

查看全部评分

7 个回复

倒序浏览
在InputStream对象中reset方法总是抛出IOException,而mark方法不做任何事情。我们用InputStream中的方法markSupported()来判断是否支持mark()方法,在InputStream对象中的markSupported方法始终返回false。同样FileInputStream中的markSupported也是返回false,这样FileInputStream不支持mark方法。我现在已知的支持mark的IO流有:BufferedInputStream、BufferedReader,应该是与里面的缓冲区数组有关系。

根据JAVA官方文档的描述,mark(int readlimit)方法表示,标记当前位置,并保证在mark以后最多可以读取readlimit字节数据,mark标记仍有效。如果在mark后读取超过readlimit字节数据,mark标记就会失效,调用reset()方法会有异常。
但实际的运行情况却和JAVA文档中的描述并不完全相符。 有时候在BufferedInputStream类中调用mark(int readlimit)方法后,即使读取超过readlimit字节的数据,mark标记仍有效,仍然能正确调用reset方法重置。

事实上,mark在JAVA中的实现是和缓冲区相关的。只要缓冲区够大,mark后读取的数据没有超出缓冲区的大小,mark标记就不会失效。如果不够大,mark后又读取了大量的数据,导致缓冲区更新,原来标记的位置自然找不到了。

因此,mark后读取多少字节才失效,并不完全由readlimit参数确定,也和BufferedInputStream类的缓冲区大小有关。 如果BufferedInputStream类的缓冲区大小大于readlimit,在mark以后只有读取超过缓冲区大小的数据,mark标记才会失效。看下面的例子。

?<SPAN style="COLOR: #ff0000">ava代码  
package packet1;   
   
import java.io.BufferedInputStream;   
import java.io.ByteArrayInputStream;   
import java.io.IOException;   
   
/**   
* @author WuDian   
*   
*/  
public class MarkExample {   
    public static void main(String[] args) {   
   
        try {   
            // 初始化一个字节数组,内有5个字节的数据   
            byte[] bytes={1,2,3,4,5};   
            // 用一个ByteArrayInputStream来读取这个字节数组   
            ByteArrayInputStream in=new ByteArrayInputStream(bytes);   
            // 将ByteArrayInputStream包含在一个BufferedInputStream,并初始化缓冲区大小为2。   
            BufferedInputStream bis=new BufferedInputStream(in,2);     
            // 读取字节1   
            System.out.print(bis.read()+",");   
            // 在字节2处做标记,同时设置readlimit参数为1   
            // 根据JAVA文档mark以后最多只能读取1个字节,否则mark标记失效,但实际运行结果不是这样   
            System.out.println("mark");   
            bis.mark(1);   
                 
            /*   
             * 连续读取两个字节,超过了readlimit的大小,mark标记仍有效   
             */   
            // 连续读取两个字节   
            System.out.print(bis.read()+",");     
            System.out.print(bis.read()+",");     
            // 调用reset方法,未发生异常,说明mark标记仍有效。   
            // 因为,虽然readlimit参数为1,但是这个BufferedInputStream类的缓冲区大小为2,   
            // 所以允许读取2字节   
            System.out.println("reset");   
            bis.reset();   
                 
            /*   
             * 连续读取3个字节,超过了缓冲区大小,mark标记失效。   
             * 在这个例子中BufferedInputStream类的缓冲区大小大于readlimit,   
             * mark标记由缓冲区大小决定   
             */   
            // reset重置后连续读取3个字节,超过了BufferedInputStream类的缓冲区大小   
            System.out.print(bis.read()+",");   
            System.out.print(bis.read()+",");   
            System.out.print(bis.read()+",");   
            // 再次调用reset重置,抛出异常,说明mark后读取3个字节,mark标记失效   
            System.out.println("reset again");   
            bis.reset();   
        } catch (IOException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
    }   
}   
  
package packet1;
  
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
  
/**
* @author WuDian
*  
*/
public class MarkExample {
        public static void main(String[] args) {
  
                try {
                        // 初始化一个字节数组,内有5个字节的数据
                        byte[] bytes={1,2,3,4,5};
                        // 用一个ByteArrayInputStream来读取这个字节数组
                        ByteArrayInputStream in=new ByteArrayInputStream(bytes);
                        // 将ByteArrayInputStream包含在一个BufferedInputStream,并初始化缓冲区大小为2。
                        BufferedInputStream bis=new BufferedInputStream(in,2);  
                        // 读取字节1
                        System.out.print(bis.read()+",");
                        // 在字节2处做标记,同时设置readlimit参数为1
                        // 根据JAVA文档mark以后最多只能读取1个字节,否则mark标记失效,但实际运行结果不是这样
                        System.out.println("mark");
                        bis.mark(1);
                          
                        /*
                         * 连续读取两个字节,超过了readlimit的大小,mark标记仍有效
                         */
                        // 连续读取两个字节
                        System.out.print(bis.read()+",");  
                        System.out.print(bis.read()+",");  
                        // 调用reset方法,未发生异常,说明mark标记仍有效。
                        // 因为,虽然readlimit参数为1,但是这个BufferedInputStream类的缓冲区大小为2,
                        // 所以允许读取2字节
                        System.out.println("reset");
                        bis.reset();
                          
                        /*
                         * 连续读取3个字节,超过了缓冲区大小,mark标记失效。
                         * 在这个例子中BufferedInputStream类的缓冲区大小大于readlimit,
                         * mark标记由缓冲区大小决定
                         */
                        // reset重置后连续读取3个字节,超过了BufferedInputStream类的缓冲区大小
                        System.out.print(bis.read()+",");
                        System.out.print(bis.read()+",");
                        System.out.print(bis.read()+",");
                        // 再次调用reset重置,抛出异常,说明mark后读取3个字节,mark标记失效
                        System.out.println("reset again");
                        bis.reset();
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
        }
}
</SPAN>
  运行结果如下:

Java代码
1,mark   
2,3,reset   
2,3,4,reset again   
java.io.IOException: Resetting to invalid mark   
    at java.io.BufferedInputStream.reset(BufferedInputStream.java:416)   
    at packet1.MarkExample.main(MarkExample.java:51)  

1,mark
2,3,reset
2,3,4,reset again
java.io.IOException: Resetting to invalid mark
        at java.io.BufferedInputStream.reset(BufferedInputStream.java:416)
        at packet1.MarkExample.main(MarkExample.java:51)       同样的,在调用mark(int readlimit)方法时,如果readlimit大于BufferedInputStream类缓冲区的大小,缓冲区会被扩大,那mark后最多就可以读readlimit字节。
   
        简言之,BufferedInputStream类调用mark(int readlimit)方法后读取多少字节标记才失效,是取readlimit和BufferedInputStream类的缓冲区大小两者中的最大值,而并非完全由readlimit确定。这个在JAVA文档中是没有提到的。

JAVA中mark()和reset()用法的通俗理解

mark就像书签一样,在这个BufferedReader对应的buffer里作个标记,以后再调用reset时就可以再回到这个mark过的地方。mark方法有个参数,通过这个整型参数,你告诉系统,希望在读出这么多个字符之前,这个mark保持有效。读过这么多字符之后,系统可以使mark不再有效,而你不能觉得奇怪或怪罪它。这跟buffer有关,如果你需要很长的距离,那么系统就必须分配很大的buffer来保持你的mark。   
    //eg.   
    //reader      is      a      BufferedReader   
      
    reader.mark(50);//要求在50个字符之内,这个mark应该保持有效,系统会保证buffer至少可以存储50个字符   
    int      a      =      reader.read();//读了一个字符   
    int      b      =      reader.read();//又读了一个字符   
      
    //做了某些处理,发现需要再读一次   
    reader.reset();   
    reader.read();//读到的字符和a相同   
    reader.read();//读到的字符和b相同


评分

参与人数 1技术分 +1 收起 理由
奋斗的青春 + 1 以后请注明,转载地址 。

查看全部评分

回复 使用道具 举报
本帖最后由 曹自祥 于 2012-11-17 17:11 编辑

楼上的大篇幅答非所问,一看就是粘贴复制别人的。上百度一搜果然有原文,你丫一个字没改就搞过来了。
http://www.cnblogs.com/zhang-qiang/articles/2050885.html

点评

端正下学习态度 。  发表于 2012-11-17 19:09
回复 使用道具 举报
我自己如果不懂,会给你贴上,虽然是抄的,难道没道理
回复 使用道具 举报
我看过说的对的才给你张过来来的
回复 使用道具 举报
我学了才给你弄过来的!1
回复 使用道具 举报
奚华 中级黑马 2012-11-17 19:06:00
7#
第一段话,难道网上有?
回复 使用道具 举报
奚华 中级黑马 2012-11-17 19:11:15
8#
奚华 发表于 2012-11-17 02:04
在InputStream对象中reset方法总是抛出IOException,而mark方法不做任何事情。我们用InputStream中的方法ma ...

知道了,会的!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马