一、I/O(Input and Output)介绍
java的IO主要分为
两种类型,面向字节和数字的(byte- and number-oriented, 由input和output stream处理)和面向字符和文本的,(character and text, 由reader和writer处理)。
1.1 what is a stream
stream就是一个不确定长度的有序的
字节序列。inputstream能把数据从外部数据源(控制台,文件,网络等)读到java程序中。outputstream能把数据从java程序内部输出到外部目标。stream也能把数据从一个java程序传送到另一个java程序中。
System类的final static变量,in,out,err就是常见的控制台输入/输出变量,可以直接使用。其中System.out和System.err都是PrintStream的实例。
大部分直接使用stream处理IO的类都在java.io包里。java.io.InputStream和java.io.OutputStream是其他有具体能力的IO类的
抽象基类。
其中LineNumberInputStream和StringBufferInputStream已经废弃了。
java.util.zip包里也有4个基于stream的类,他们用来处理压缩格式的数据。
java.util.jar包里有2个基于stream的类,JarInputStream和JarOutputStream.
java.security包里还有一对基于stream的类可以计算信息的摘要。DigestInputStream和DigestOutputStream。
1.2 Numberic Data
Input Stream读和写都是
基于字节(byte),Reader和Writer都是基于字符(character)。字节是计算机数据的基础,所以要理解io,必须清楚的理解java是如何处理bytes,integers,characters和其他原始类型数据,以及何时互相转换。很多时候这些行为并不是那么明显。
1.2.1 Integer
java的integer数据类型根本上就是int,4字节,大端存储,二进制的补码整数。int的范围是-2,147,483,648到2,147,483,647。
long是8字节,大端存储,二进制补码整数,范围是9,223,372,036,854,775,808 到 9,223,372,036,854,775,807,通常在数字的后面加上小写或大写的后缀L,代表是long类型。
byte在java中用的也很多,特别是IO中。
byte是1字节(8bit)整数,二进制补码整数。范围是-128到127. 注意,就像其他数字类型一样,
byte是有符号的。当
直接使用数字时java都是把数字当做int型,除非定义变量时显示指明byte或short类型。但是注意,int类型的变量不能赋值给short或byte类型,会编译错误。但是可以使用强制转换。
同时byte或short类型也不能直接进行算术运算。比如:
byte b1 = 22;
byte b2 = 23;
byte b3 = b1 + b2; // compiler error
也是错误的。
考虑到直接使用byte不方便。基于stream的很多类输入和输出的虽然是byte类型,但是实际使用的是int,范围是
无符号byte(0-255),这跟java的任何原始数据类型都对应不上,
这些int会在内部被转成byte.
比如,java.io.InputStream的read()方法返回“the next byte of data, or -1 if the end of the stream is reached.”乍一看有些奇怪,-1是byte类型的合法值,实际上这个方法正常返回的是0-255的 int。如果程序员显式的传入任意int值,就会只取低8位,抛弃高24位,采用的方法就是:
b = b % 256 >= 0 ? b % 256 : 256 + b % 256;
更简单有效的方法是使用位运算:
b = b & 0x000000FF;
1.3 character
数字只是java程序需要处理的一部分数据类型,大多数需要处理由
character组成的文本。但是因为计算机只认识数字(01组成嘛),
character都会被编码成一个唯一的数字。比如,ASCII编码中,大写字母’A’会被映射为数字65. 不同的编码方案,character会被映射为不同的数字。java能识别好几种编码方案,比如ASCII, SJIS, Unicode等。
java内部使用的是Unicode character set,比如java的class文件内部使用Unicode字符集编码存储(注意并不是utf-8,后面第5篇会单独介绍编码相关内容),DataInputStream和DataOutputStream也是使用utf8读写,不过这些通常是对程序员透明的。
stream不能很好的处理unicode,所以引入了Reader/Writer。
关于unicode和utf8的区别推荐阅读:
http://www.cnblogs.com/cy163/archive/2007/05/31/766886.html
character内部又是由java的原始类型char组成的,比如String就是char array。
char是一个2字节,无符号整数,java中的
唯一的无符号类型。范围是0到65,535。
每个char代表unicode编码的一个字符。char可以使用int值来赋值。
sun的javac编译器能把很多不同的编码类型转变成Unicode,使用-encoding参数。比如已知一个java文件时别的编码格式,就可以如下转换:
% javac –encoding 8859_1 CharTest.java
由于很多字符集的字符并不能跟Unicode完全对应上,使用其他字符集不包含的字符时可以使用unicode的转义符(任何字符都可以直接使用unicode编码(16位编码)来表示,可以写在代码的任意位置,只要转义后是合法的,只要在Unicode编码前面加上unicode转义符就可以了)’\u’或’\U’来指定。
1.4 Readers and Writers
java1.1及之后,提供了Reader和Writer来处理文本的读写。stream主要用来处理纯字节(byte, numberic数据,这些都被编码成一定顺序的二进制数字)。
Reader和Writer可以基于不同的编码集对字符进行编码。因为character归根到底还是由byte组成的,
所以Reader也是从inputStream里获得这些字节,然后根据编码格式来把字节转成字符;Writer正好是反过程。
java.io.Reader和java.io.Writer是基于字符的IO类的抽象父类。这些类都包含在java.io包里:
reader/writer的方法和stream里的方法基本上能对应,只是参数类型有所改变。另外,int都可以在两者的方法里作为参数,但是在OutputStream write()方法里传入int,会在int值上减去256再转成byte输出,如果是int传入Writer write()方法里,则减去65,536再输出。这也反应出了char和byte的范围不同。
二、 OutputStream
2.1 the OutputStream class
java.io.OutputStream类声明了3个通过stream来输出byte的基本方法,同时它还提供了关闭(close)和刷新(flush) stream的方法。
public abstract void write(int b) throws IOException
public void write(byte[] data) throws IOException
public void write(byte[] data, int offset, int length) throws IOException
public void flush() throws IOException
public void close() throws IOException
OutputStream是一个抽象类,子类实现抽象方法或重写非抽象方法。虽然OutputStream是抽象类,但我们经常只需要知道我们使用的是一个OutputStream类型,而不关系它的具体类型。比如java.net.URLConnection的getOutputStream()方法签名:
public OutputStream getOutputStream() throws IOException
根据URLConnection对象的具体类型,该方法的返回值可能是sun.net.TelnetOutputStream, sun.net.smtp.SmtpPrintStream, sun.net.www.http.KeepAliveStream或者其他的。
2.2 Writing Bytes to Output Streams
Outputstream类最根本的方法是write():
public abstract void write(int b) throws IOException
这个方法只写一个字节,其值是0-255的无符号整数。如果传入的值不在0-255范围,就会用256对其取模。
2.3 Writing Arrays of Bytes
一次性写一段数据的速度比一个字节一个字节的写数据要快,单字节写的方法write()有两个重载的方法。
public void write(byte[] data) throws IOException
public void write(byte[] data, int offset, int length) throws IOException
第一个方法一次写整个数组的字节。第二个方法只写data数组的一部分,从offset下标开始写length个字节。
相反,
如果一次写太多字节也会遇到性能问题。具体的转折点依输出的目标而定。输出到文件通常最好是一次写硬盘的块(block)大小的小倍数,比如,512字节,1024自己或2048字节。输出到网络通常需要比较小的缓存大小,128或256字节。
通常网络可以使用128字节,文件可以使用1024字节。
[b]2.4 Flushing and Closing OutputStreams[/b]
很多OutputStream 使用缓存来提高性能。他们不是每次往目的地(文件,网络等)写一个字节,而是在缓存里缓冲很多字节,当缓存填满时一次性的输出到目的地。flush()方法不管缓存是否填满,都会强制把缓存里的数据写到目的地。
public void flush() throws IOException
三、 Input Streams
3.1 The InputStream Class
java.io.InputStream类是所有input streams类的抽象基类。它声明了三个从stream读取byte的基本方法和close,flush类的方法,以及有多少字节是允许读取的,跳过输入字节,标识stream的位置,重置位置和判断是否支持making和resetting操作的方法。
public abstract int read() throws IOException
public int read(byte[] data) throws IOException
public int read(byte[] data, int offset, int length) throws IOException
public long skip(long n) throws IOException
public int available() throws IOException
public void close() throws IOException
public synchronized void mark(int readlimit)
public synchronized void reset() throws IOException
public boolean markSupported()
3.2 The read() Method
InputStream类最基本的方法就是read(), 每次读取一个无符号的字节,然后返回这个无符号字节的整数值,返回范围在0-255之内。
public abstract int read() throws IOException
关于int和byte的转换,请参考:
http://my.oschina.net/u/169390/blog/97495
http://freewind886.blog.163.com/blog/static/661924642011810236100/
调用read()方法时需要捕获IOException,比如硬盘失败,网络断了等。但是如果read()方法encounters the end of the input stream,则不会抛出IOException,而是返回-1.这是读到流结束的标识。
比如:
try
{
int[] data = new int[10];
for (int i = 0; i < data.length; i++)
{
int datum = System.in.read();
if (datum == -1) break;
data[i] = datum;
}
}
catch (IOException e)
{
System.err.println("Couldn't read from System.in!");
}
read()方法在字节可用之前会一直
等待或阻塞(
原文:The read() method waits or blocks until a byte of data is available and ready to be read).
3.3 Reading Chunks of Data from a Stream
输入输出通常是程序的性能瓶颈。从硬盘读写要比从内存读写慢成百上千倍;从网络读写更慢。所有的InputStream子类都有重载的read()方法,可以一次读取连续的一段数据存到byte数组里。
public int read(byte[] data) throws IOException
public int read(byte[] data, int offset, int length) throws IOException
java.io.InputStream类里默认的实现只是不断重复调用read()方法一次读取一个字节,这对于性能没有什么提高。大部分InputStream的子类用更有效的方式重写了上面两个方法,比如使用native方法一次读取一块数据。
3.4 Counting the Available Bytes
在读取数据之前如果能知道有多少字节数据是可以读取的就太好了。InputStream类的available()方法可以获取不阻塞时有多少字节数据可读。
public int available() throws IOException
java.io.InputStream类的available()方法总是返回0,这个方法需要子类来重写,但要注意,实际情况
并不是所有类都重写了这个方法。
3.5 Skipping Bytes
虽然我们可以从Stream里读取一定字节数据然后忽略掉他们,但是java提供了skip()方法跳过一定字节数据更有效率。
public long skip(long bytesToSkip) throws IOException
bytesToSkip是打算跳过的字节数,返回值是实际跳过的字节数,可能比bytesToSkip小。如果遇到了流的结束标识,则返回-1。
3.6 Closing Input Streams
使用完stream之后必须close它。这样操作系统才能释放跟stream相关的资源。
public void close() throws IOException
当然也不是所有的流都必须关闭,System.in就不用,因为它内部自己处理了。但几乎所有的都需要显示关闭,比如和文件,网络关联的输出输入流,比如:
try
{
URL u = new URL("http://www.javasoft.com/");
InputStream in = u.openStream();
// Read from the stream...
in.close();
}
catch (IOException e)
{
System.err.println(e);
}
3.7 Marking and Resetting
有时候读了一定字节之后需要再回退回去再读一遍。有些(不是所有)input Stream允许设置一个标志位置可以重新再来读取。
public synchronized void mark(int readLimit)
public synchronized void reset() throws IOException
public boolean markSupported()
markSupported()方法可以先用来判断具体的stream类是否支持标记。后标记的位置会冲掉之前标记的位置。BufferedInputStream和ByteArrayInputStream类总是支持标记。
参考:
《java I/O》 Elliotte Rusty Harold
- 大小: 71.4 KB
- 大小: 41.2 KB
- 大小: 116.1 KB
分享到:
相关推荐
IO流文档InputStream / OutputStream IO流是一种基本的数据交换方式,在Java中,IO流是通过输入流(InputStream)和输出流(OutputStream)来实现的。输入流用于读取数据,而输出流用于写入数据。 InputStream是...
对象成员变量在对象创建时会获得一个缺省的初始值.<br>5.Java数组随时可以改变大小.<br>6.InputStream和OutputStream读写的数据是8位的.<br>7.Java是强类型语言.<br>8.Java没有指针,引用不能等同于...
<struts> <!-- 系统常量定义,定义上传文件字符集编码 --> <constant name="struts.i18n.encoding" value="UTF-8"></constant> <!-- 系统常量定义,定义上传文件临时存放路径 --> <constant name=...
<artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> ``` 然后,编写`FileUploadAction`类的上传方法: ```java import org.apache.struts2.interceptor.FileUploadInterceptor; ...
<p>您上传的文件名为: <s:property value="myFileFileName"/></p> </body> </html> ``` #### 二、多文件上传 多文件上传是指用户可以选择多个文件进行同时上传的操作。其实现方式与单个文件上传类似,但需要做...
在Java编程语言中,`InputStream`和`OutputStream`是处理数据流的核心类,它们属于Java的I/O(Input/Output)框架。`File`类则用于处理文件操作,如读写、创建、删除等。理解如何在这些对象之间进行转换是Java开发中...
<br>A、Reader类 B、InputStream类 C、OutputStream类 D、File类<br>5、 缺省的存局管理器是( )。<br>A、BorderLayout B、FlowLayout C、GridLayout D、CardLayout<br>6、 用于存放创建后则不变的字符串常量是( ...
<artifactId>itext</artifactId> <version>2.1.7.js6</version> </dependency> <dependency> <groupId>com.github.jai-imageio</groupId> <artifactId>jai-imageio-core</artifactId> <version>1.4.0</version>...
public void exportExcel(List<DataModel> dataList, OutputStream os) { // 创建上下文对象,用于存储需要导入Excel的数据 Context context = new Context(); context.putVar("dataList", dataList); // 加载...
InputStream is = new FileInputStream(file1);// 打开文件 byte[] cont = new byte[(int) file1.length()]; is.read(cont);// 读取文件 for (int i = 0; i < cont.length; i++) { System.out....
<artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> </dependencies> ``` 2. **创建Servlet**:创建一个Servlet来接收HTTP请求。在`doPost`方法中解析请求,获取上传的文件。 3. ...
java打开原始文件L2加密 Java的开放源代码库,用于加密/解密世袭2文件。 支持的标头:111、120、121、211、212...<id>jitpack.io</id> <url>https://jitpack.io</url> </repository> <dependency> <groupId>com.github
<artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> </dependencies> ``` 2. 创建Servlet:创建一个Servlet类,重写`doPost`方法,因为文件上传通常通过POST请求进行。 3. 解析请求...
JavaIO类InputStream/OutputStream继承关系图,使您对IO类一目了然
<artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>commons-betwixt</groupId> <artifactId>commons-betwixt</artifactId> <version>0.8</version> ...
<s:file name="upload" label="ImageFile1" /> </td> </tr> <tr> <td> <s:file name="upload" label="ImageFile2" /> </td> </tr> <tr> <td> <s:file name="upload" label="ImageFile3" /> </td> </tr>...
<groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version...
在Java编程中,有时我们可能需要将一个已经写入数据的`OutputStream`转换为`InputStream`,以便重新读取这些数据。这种情况通常出现在临时存储或处理数据时,例如在网络传输或者存储到内存中的场景。本篇文章将深入...
import java.io.OutputStream; import java.util.List; import java.util.Map; public class ExcelUtil { public static List<Map<String, Object>> importExcel(MultipartFile file) throws IOException { // ...