1.IO体系的框架图
我们先来介绍一下IO体系的整体框架图
接下来,我们首先开始流式部分的介绍。
2.流的基本概念
一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。
输入流:程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道。
输出流:程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。
采用数据流的目的就是使得输出输入独立于设备。
3.java中的字节流和字符流Java 流在处理上分为字符流和字节流。字符流处理的单元为2个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组()。
Java内用Unicode编码存储字符,字符流处理类负责将外部的其他编码的字符流和java内 Unicode字符流之间的转换。而类InputStreamReader和OutputStreamWriter处理字符流和字节流的转换。字符流(一次可以处理一个缓冲区)一次操作比字节流(一次一个字节)效率高。
字节流是最基本的,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的。但实际中很多的数据是文本,又提出了字符流(Reader和Writer)的概念(常用的),它是按虚拟机的encode来处理,也就是要进行字节到字符集的转化。实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件。
(1)字节流InputStream 和OutputStream是两个abstact 类,对于字节为导向的stream都扩展这两个。即二进制,他们的扩展类如下:
下面我们具体举例说明他们的使用,以文件字节流的读写为例。
文件的写操作(字节流的形式)
public static void main(String[] args) throws Exception {
//第一步:创建文件
File f=new File("d:"+File.pathSeparator+"test.txt");
//第二步:通过子类实例化父类对象
//通过FileOutputStream实例化OutputStream对象
//true表示可以追加
OutputStream out=null; //准备好一个输出对象
out=new FileOutputStream(f,true); //通过对象多态性进行实例化,
//第三步:进行读写操作
//1.准备一个字符串,其中\r\n 起到换行作用
String str="\r\n qzp 1991";
//2.将字符串变成字节数组(因为是字节流嘛)
byte b[]=str.getBytes();
//将内容输出,保存文件
out.write(b);
/**
* 也可以使用字节一个一个循环输出。
* for(int i=0;i<b.length;i++){
* out.write(b[i]);
* }
*/
//第四步:关闭输出流
out.close();
}
运行以后d盘中会创建test.txt这样一个文件,并且里面保存了字符串的内容。
文件的读操作(字节流的形式)
public static void main(String[] args) throws Exception {
//第一步:找到需要操作的文件名
File f=new File("d:"+File.pathSeparator+"test.txt");
//第二步:通过子类实例化父类对象
InputStream input=new FileInputStream(f);
//第三步:进行读操作,将需要读入的内容放入此数组中
byte b[]=new byte[1024];
//设置一个临时变量,存放输入流,
//当输入流不为空时,都读入,为空时则不读,节省资源
int temp=0;
int len=0;
while((temp=input.read())!=-1){
b[len]=(byte)temp;
len++;
}
//第四步:关闭输入流
input.close();
System.out.println("字节数组的长度"+len);
//输出文件中的内容。限制了len以后,不会有多余的空格输出
System.out.println("内容为"+new String(b,0,len));
}
运行后会打印如下语句
字节数组的长度34
内容为qzp 1991qzp 1991qzp 1991
qzp 1991
(2)字符流Reader/Writer 为 abstact 类,以Unicode字符为导向的 stream ,表示以Unicode字符为单位从stream中读取或往stream中写入信息。在java中一个字符等于二个字节。
下面还是以文件的写为例,此操作与字节流的读写操作差不多,只是返回的结果是一个字符,不是字节,可以直接输出,不比再转化。
public static void main(String[] args) throws IOException {
//第一步:创建文件
File f=new File("d:"+File.pathSeparator+"test.txt");
//第二步:通过子类实例化父类对象
Writer out=null;
out=new FileWriter(f,true);
//第三步:进行读写操作
//1.准备一个字符串
String str="\r\nqzp 1991";
//2.将字符串变成字节数组(因为是字节流嘛)
out.write(str);
//第四步:关闭输出流
out.close();
}
(3)字节流和字符流的比较
[1] 字节流返回的是字节,最好还是要转化成字符,便于操作输出;字符流返回的是字符
[2] 字节流操作不会用到缓冲区(内存),是文件的本身操作;而字符流使用时用到了缓冲区,通过缓冲区在操作文件。字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容
那到底实际开发中用字符流还是字节流呢?首先,文件在硬盘中的传输是以字节的方式进行的,包括图片也是按照字节方式(二进制)存储的,而字符只有在内存中才会形成,所以,实际开发中字节用的更多。
(4)字节流与字符流的转换InputStreamReader和OutputStreamReader :
把一个以字节为导向的stream转换成一个以字符为导向的 stream 。
InputStreamReader 类是Reader的子类,是从字节流到字符流的桥梁:它读入字节,并根据指定的编码方式,将之转换为字符流。
使用的编码方式可能由名称指定,或平台可接受的缺省编码方式。
InputStreamReader 的 read() 方法之一的每次调用,可能促使从基本字节输入流中读取一个或多个字节。
为了达到更高效率,考虑用 BufferedReader 封装 InputStreamReader ,
BufferedReader in = new BufferedReader(newInputStreamReader(System.in));
例子说明:
<p><strong><span style="color:#7F0055;">public </span><span style="color:#7F0055;">static</span><span style="color:#7F0055;"> void</span></strong><span style="color:black;"> main(String[] args) {</span></p><p><span style="color:black;"> </span>BufferedReaderbuf=<strong><span style="color:#7F0055;">null</span></strong><span style="color:black;">;</span></p><p><span style="color:black;"> </span>//<span style="color:black;">实例化</span><span style="color:black;">bufferedReader,</span><span style="color:black;">并接受其内容</span></p><p><span style="color:black;"> //此处将输入的字节流转化为字符流,然后存放到BufferedReader之中
</span></p><p><span style="color:black;"> </span>buf=<strong><span style="color:#7F0055;">new</span></strong><span style="color:black;"> BufferedReader(</span><strong><span style="color:#7F0055;">new</span></strong><span style="color:black;"> InputStreamReader(System.</span><em><span style="color:#0000C0;">in</span></em><span style="color:black;">));</span></p><p><span style="color:black;"> </span></p><p><span style="color:black;"> </span>String str=<strong><span style="color:#7F0055;">null</span></strong><span style="color:black;">;</span></p><p><span style="color:black;"> </span>System.<em><span style="color:#0000C0;">out</span></em><span style="color:black;">.println(</span><span style="color:#2A00FF;">"</span><span style="color:#2A00FF;">请输入字符串:</span><span style="color:#2A00FF;">"</span><span style="color:black;">);</span></p><p><span style="color:black;"> </span></p><p><span style="color:black;"> </span><strong><span style="color:#7F0055;">try</span></strong><span style="color:black;"> {</span></p><p><span style="color:black;"> </span>str=buf.readLine();</p><p><span style="color:black;"> </span>}<strong><span style="color:#7F0055;">catch</span></strong><span style="color:black;"> (IOException e) {</span></p><p><span style="color:black;"> </span>e.printStackTrace();</p><p><span style="color:black;"> </span>}</p><p><span style="color:black;"> </span>System.<em><span style="color:#0000C0;">out</span></em><span style="color:black;">.println(</span><span style="color:#2A00FF;">"</span><span style="color:#2A00FF;">输入的内容是</span><span style="color:#2A00FF;">"</span><span style="color:black;">+str);</span></p><p>}</p>
运行结果:
请输入字符串:
qzp
输入的内容是qzp
(5)url读写url读写:调用网页的api必将成为一个重要的研究方向,因而获取网站的url地址是从网站调用API的第一步,以下是将url地址从字节流转化为字符流的过程。从而方便程序中以字符串获取url的值。
public static String httpRequest(String requestUrl) {
StringBuffer buffer = new StringBuffer();
try {
URL url = new URL(requestUrl);
HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
httpUrlConn.setDoOutput(false);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
httpUrlConn.setRequestMethod("GET");
httpUrlConn.connect();
// 将返回的输入流转换成字符串 很重要将字节流转化为字符流
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine())!= null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
} catch (Exception e) {
}
return buffer.toString();
}
(6)装饰模式
以文件读写的例子为引子,我们进行具体的研究
public class Test
{
public static void main(String[] args)
{
try {
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
String line = br.readLine();
while (line != null) {
System.out.println(line);
line = br.readLine();
}
br.close();
}
catch(IOException e) {
System.out.println("IO Problem");
}
}
}
其中BufferedReader br = new BufferedReader(newFileReader("file.txt"));
BufferedReader()是一个装饰器(decorator), 它接收一个原始的对象,并返回一个经过装饰的、功能更复杂的对象。修饰器的好处是,它可以用于修饰不同的对象。我们这里被修饰的是从文件中读取的文本流。 其他的文本流,比如标准输入,网络传输的流等等,都可以被BufferedReader()修饰,从而实现缓存读取。
3.非流式的文件操作在整个IO包中,唯一一个与文件本身相关的就是File类,使用File类可以进行文件创建、删除等操作,构造方法:
File f=new File(“D:\\test.txt”);
File f=new File(“D:”+ File.separator +“test.txt”);
注:使用File.separator代替\\,这样有利于在多个不同的操作系统中进行扩展。因为在Linux中使用//作为文件分隔符,而windows中则是\\。
File类还有一些其他的常用方法,可以参考帮助文档,此处不再罗列。