标签:
1.流的概念
一个对输入输出设备的抽象概念,JAVA中文件的操作都是以“流”的方式进行。可以把流理解成“管道”,数据通过管道得以从一端传输到另一端。
流具有方向性:一般而言这个方向是以程序作为参照,所以,输入流是指输向程序的流,输出流是指从程序输出的流;也可以理解成,程序从输入流获取数据,向输出流写数据。
流式输入输出的特点为:数据的获取和发送沿数据序列的顺序进行,即每一个数据都必须等待排在它前面的数据,等前面的数据读入或送出后才能被读写。因此,流和队列一样,只能以“先进先出”的方式对其中的数据进行读写,而不能随意选择读写位置。
2.JAVA IO库的设计原则
两个原则
1.输入输出对称性。如:InputStream和OutputStream各自占据字节流的输入和输出的两个平行等级结构的根部,Reader和Writer则对应字符流输入和输出的两个平行等级结构的根部。
2.byte和char的对称性。如:InputSteam和Reader,OutputSteam和Writer。
两种设计模式
1.装饰者模式(Decorator)。装饰者又称为包装器,其作用类似子类实现父类并自定义更多更丰富的方法,但是装饰者模式比子类更灵活。
上图以InputStream为例,描述了装饰者模式的实现案例。下面说一下个人对上图的简单理解:InputStream作为抽象component根节点,FileInputStream、StringBufferInputStream、ByteArrayInputStream三者作为被装饰对象的具体实现,而FilterInputStream及其子类则是装饰者。由于所有元素都是component根节点的子孙类,所以他们在方法调用上是一致的,装饰者通过在构建过程中调用被装饰对象,从而在实现具体方法的时候,可以在被装饰者具体方法前加入自己的逻辑,这样就实现了对原有方法基础上的自定义。(具体可检索"装饰者模式")
2.适配器模式(Adapter)。以FileInputStream为例。
public class FileInputStream extends InputStream { /* File Descriptor - handle to the open file */ private FileDescriptor fd; ...... public FileInputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkRead(fdObj); } fd = fdObj; } ......
先理解下Java磁盘IO实现机制:文件是操作系统与磁盘驱动器交互的最小单元,程序也只能通过文件来操作磁盘上的数据。在Java中,文件操作通过File类完成,但是一个File类其实并不代表一个真实存在的文件对象,当new一个File类的时候,返回的只是跟所传递的路径描述符相关联的虚拟对象,而真正要操作这个文件对象,则是通过File类中引用的FileSystem属性来进行底层系统交互来实现的(当然,这也与Java本身不能对操作系统底层进行访问,只能通过JNI接口调用其他语言来实现对底层访问有关)。
通过上面分析可知,同理,FileInputStream在调用系统文件方面,其实现适配器模式可以这样理解:通过引用FileDescriptor类,并将其适配成InputSteam类型的对象,实现文件操作输入流。
3.流的分类
按处理的数据单位分:字节流、字符流
按数据方向(相对于程序本身)分:输入流、输出流
按功能分:节点流、处理流
其中第三点的解释为:节点流是指从某个特定数据源读写数据的流;而处理流是基于已存在的流(节点流或处理流)之上,为数据处理提供更强大功能的流,又称为“过滤流”,它是对原有流的包装。
四种基本抽象类:输入流——InputStream、Reader;输出流——OutputStream、Writer
实现类如图,深色为节点流,浅色为处理流:
4.read和write方法
5.字节流和字符流的实现
①基于字节的输入流:
值得注意的地方:1、节点流(Level2)直接与数据源交互,所以其大多会指明数据源的形式:如ByteArray、File、Piped;2、过滤流(Level3)是基于其他流的包装(都集成自FilterInputStream),为其提供更丰富的功能,所以过滤流则多是指明功能:如Buffered、LineNumber。
Level2:
Level3:
②基于字节的输出流:
Level2:
Level3:
③基于字符的输入流:
需要注意的地方:实际上所有对文件的IO操作都是通过字节的形式实现的,所谓的字符流只不过逻辑上的一种说法。
上述类不做过多介绍,基本与字节输入流对应,其中StringReader是从一个字符串中读取内容,LineNumberReader是一个可以跟踪读入的“行数据”的字符输入流,它内置一个指示器用于跟踪读入数据的行数。
④基于字符的输出流:
⑤字节流和字符流之间的转换:
InputStreamReader和OutputStreamWriter分别提供了字节输入流到字符输入流和字符输出流到字节输出流的转换,转换可指定编码。
6.RandomAccessFile
RandomAccessFile是独立实现的JavaIO操作类,它并不在上面所说的输入输出流结构中。作为扩充了IO框架的随机文件流,它可以在文件的任意位置读取或写入数据。其在文件系统中建立一个文件指针,用于标记将要进行读写操作的下一字节的位置,seek方法可以将指针移动到文件内部的任意位置,从而实现文件的随机读取。
7.实例代码
代码1:将网络上的某个页面(资源)复制到本地(页面静态化)
public static void main(String[] args) throws Exception { InputStream in = new BufferedInputStream(new URL("http://www.jd.com").openStream()); File bdFile = new File("D:\\jd.html"); if (!bdFile.exists()) { bdFile.createNewFile(); } OutputStream ou = new BufferedOutputStream(new FileOutputStream(bdFile)); byte[] b = new byte[1024]; int len; while((len = in.read(b, 0, b.length)) != -1){ ou.write(b, 0, len); } }
代码2:将某个网络资源通过多线程下载到本地(使用RandomAccessFile类)
public class Main { /** * @param args */ public static void main(String[] args) throws Exception{ String path = "xxx";//网络资源URL String filePath = "D:\\";//本地磁盘位置 new Main().threadDownload(path, 5, filePath); } public void threadDownload(String path, int threadSize, String filePath) throws Exception{ URL url = new URL(path); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("GET"); if (httpURLConnection.getResponseCode() != 200) { throw new Exception("响应异常"); } int dataLength = httpURLConnection.getContentLength(); String fileName = Main.getFileName(httpURLConnection); httpURLConnection.disconnect(); threadSize = threadSize == 0 ? 5 : threadSize; int blockSize = dataLength/threadSize; int left = dataLength % threadSize; for (int i = 0; i < threadSize; i++) { if (i == threadSize) { blockSize += left; } new Thread(new DownloadThread(path, new RandomAccessFile(new File(filePath + fileName), "rw"), blockSize * i, blockSize, i + 1)).start(); } } private static String getFileName(HttpURLConnection httpURLConnection) throws Exception{ Map<String, List<String>> headerFields = httpURLConnection.getHeaderFields(); Set<Entry<String, List<String>>> set = headerFields.entrySet(); for (Entry<String, List<String>> entry : set) { if (entry.toString().contains("filename")) { List<String> list = entry.getValue(); for (String string : list) { if (string.contains("filename")) { String fileNameResult = new String(string.getBytes(), "UTF-8"); return fileNameResult.substring(fileNameResult.indexOf("\"") + 1, fileNameResult.lastIndexOf("\"")); } } } } return null; } } public class DownloadThread implements Runnable{ private String path; private RandomAccessFile randomAccessFile; private int startSize; private int blockSize; private int threadName; public DownloadThread(String path, RandomAccessFile randomAccessFile, int startSize, int blockSize, int threadName) { this.path = path; this.randomAccessFile = randomAccessFile; this.startSize = startSize; this.blockSize = blockSize; this.threadName = threadName; } @Override public void run() { HttpURLConnection httpURLConnection = null; InputStream inputStream = null; try { System.out.println("线程"+threadName+"开始下载"); URL url = new URL(path); httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setReadTimeout(10 * 1000); httpURLConnection.setRequestProperty("Range", "bytes=" + startSize + "-"); if (httpURLConnection.getResponseCode() != 206) {//206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它(HTTP 1.1新) System.out.println("线程" + threadName + "响应异常,代码为" + httpURLConnection.getResponseCode()); throw new Exception("响应异常"); } inputStream = httpURLConnection.getInputStream(); byte[] b = new byte[1024]; int len = -1; int length = 0; randomAccessFile.seek(startSize); while(((len = inputStream.read(b)) != -1) && length < blockSize){ randomAccessFile.write(b, 0, len); length += len; } randomAccessFile.close(); inputStream.close(); httpURLConnection.disconnect(); System.out.println("线程"+threadName+"下载完成"); } catch (Exception e) { e.printStackTrace(); } finally { randomAccessFile = null; inputStream = null; httpURLConnection = null; } } }
标签:
原文地址:http://www.cnblogs.com/loveufan/p/4491614.html