您的位置:首页 > 职场人生

黑马程序员——Java基础—IO流(二)

2015-04-11 19:48 501 查看
———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———

一OutputStream类

二InputStream类

三字节流练习

四字节流缓冲区

五键盘录入

六转换流

IO流(二)

字节流



一、OutputStream类

1.概述

OutputStream
类可以在硬盘上创建一个文件,并写入或添加数据。该类的子类还能实现写入过程中的不同功能。

2.FileOutputStream类

FileOutputStream
类用于在硬盘上创建文件,并以字节的形式写入数据。其使用方式和
FileWriter
类相似,只是以字节的形式操作数据。下面的代码在指定目录创建一个文件并写入自定义数据。

示例代码:

package com.heisejiuhuche.io;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
/* 创建FileOutputStream对象并关联文件 */
fos = new FileOutputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt");

/* 写入数据,通过String类的getBytes方法将字符串转换为字节数组 */
fos.write("abcdefg".getBytes());
} catch(FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch(IOException e) {
throw new RuntimeException("操作失败");
} finally {
try {
if(fos != null)
fos.close();
} catch(IOException e) {
throw new RuntimeException("输出流关闭失败");
}
}
}
}


该程序在指定目录创建
fos.txt
并写入
abcdefg


注意:

字节输出流在直接使用的时候不需要调用flush()方法,与字符流不用。由于字符流底层操作的也是字节,同时用的是字节流的缓冲区;该缓冲区中有个数组,用于临时存储数据。要将数组中的数据写入目的地文件,字符流就需要调用flush()方法进行刷新。而字节输出流是对最小单位字节直接进行操作,没有使用具体缓冲区,所以不需要刷新,直接往目的地文件写入数据。

二、InputStream类

1.概述

InputStream
类以字节形式读取硬盘上的文件数据。该类的子类还能实现读取过程中的不同功能。

2.FileInputStream类

FileInputStream
类用于以字节形式读取文件数据。其特有的方法使创建字节数组有了明确的大小。该类的使用方式和
FileReader
相似。下面的代码读取文件中的数据。

示例代码:

package com.heisejiuhuche.io;

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
read_1();
System.out.println();
read_2();
read_3();

}

private static void read_3() throws IOException {
FileInputStream fis = new FileInputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt");

/* available方法返回文件的大小 */
byte[] buf = new byte[fis.available()];

fis.read(buf);

System.out.println("read_3: " + new String(buf));
}

private static void read_2() throws IOException {
FileInputStream fis = new FileInputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt");

byte[] buf = new byte[1024];

int len = 0;

while((len = fis.read(buf)) != -1) {
System.out.println("read_2: " + new String(buf, 0, len));
}
}

private static void read_1() throws IOException {
FileInputStream fis = new FileInputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt");

int ch = 0;
System.out.print("read_1: ");
while((ch = fis.read()) != -1) {
System.out.print((char)ch);
}
}
}


程序输出结果:

read_1: abcdefg
read_2: abcdefg
read_3: abcdefg


注意:

如果使用字节输入流读取的文件较大,建议使用1024字节整数倍方法读入数据,而不要使用创建available()方法返回值大小的数组;后者可能造成内存溢出

三、字节流练习

1.拷贝图片到指定目录

示例代码:

package com.heisejiuhuche.io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class PicCopy {
public static void main(String[] args) {
picCopy();
}

private static void picCopy() {
FileInputStream fis = null;
FileOutputStream fos = null;

try {
/* 创建输入输出流并关联文件 */
fos = new FileOutputStream("C:/Users/jeremy/Documents/me.jpg");
fis = new FileInputStream("C:/Users/jeremy/Documents/javaTmp/me.jpg");
/* 创建缓冲区 */
byte[] buf = new byte[1024];
int len = 0;
/* 复制文件 */
while((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
} catch(FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch(IOException e) {
throw new RuntimeException("复制失败");
} finally {
try {
if(fis != null)
fis.close();
} catch(IOException e) {
throw new RuntimeException("输入流关闭失败");
}
try {
if(fos != null)
fos.close();
} catch(IOException e) {
throw new RuntimeException("输出流关闭失败");
}
}
}
}


四、字节流缓冲区

1.缓冲区对应的类

字节流缓冲区对应
BufferedOuputStream
类和
BufferedInputStream
类。

2.缓冲区应用

1)用缓冲区拷贝一个Mp3文件

示例代码:

package com.heisejiuhuche.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyMp3Demo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
copy();
long end = System.currentTimeMillis();
System.out.println((end - start) + "毫秒");
}

private static void copy() throws IOException {
/* 创建Buffered缓冲区对象并关联文件 */
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream(
"C:/Users/jeremy/Documents/javaTmp/back.mp3"));
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream(
"C:\\Users\\jeremy\\Music\\BaiduMusic\\Songs\\Backseat Serenade - All Time Low,Cassadee Pope.mp3"));

int ch = 0;
/* 复制文件 */
while((ch = bufis.read()) != -1) {
bufos.write(ch);
}
}
}


程序输出结果:
136毫秒


2)自定义缓冲区



假设内存中字节数组的大小定义为
1024
字节;那么字节流缓冲区在工作时,首先由
FileInputStream
从硬盘抓取
1024
字节的数据存入字节数组,然后由
BufferedInputStream
read()
方法依次一个字节一个字节读取。缓冲区中有两个控制读取过程的变量,分别是数组的索引指针,和一个计数器。下标用于控制不断读取下一个字节,计数器用于控制下一次从硬盘抓数据存入缓冲区数组的时间。
read()
方法每读取一个字节,指针右移一位,计数器自减
1
;当计数器减至0的时候,意味着数组中已经没有字节可读,这时再由
FileInputStream
从硬盘抓取
1024
个字节存入数组,指针归零,计数器回到
1024
,再次进行以上步骤的循环,直至硬盘数据全部被抓取。

要自定义缓冲区,需要定义一个字节数组,两个变量(指针和计数器)。

示例代码:

package com.heisejiuhuche.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class MyBufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
copy();
long end = System.currentTimeMillis();
System.out.println((end - start) + "毫秒");
}

private static void copy() throws IOException {
/* 创建Buffered缓冲区对象并关联文件 */
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream(
"C:/Users/jeremy/Documents/javaTmp/back.mp3"));
MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream(
"C:\\Users\\jeremy\\Music\\BaiduMusic\\Songs\\Backseat Serenade - All Time Low,Cassadee Pope.mp3"));

int ch = 0;
/* 复制文件 */
while((ch = bufis.read()) != -1) {
bufos.write(ch);
}

bufos.close();
bufis.close();
}
}

class MyBufferedInputStream {
private FileInputStream fis;
private int pos;
private int count;
private byte ch;
private byte[] buf = new byte[1024];

MyBufferedInputStream(FileInputStream fis) {
this.fis = fis;
}

public int read() throws IOException {
/* 如果count=0了,继续抓取数据到缓冲区数组 */
if(count == 0) {
/* 抓取数据后,count回到1024 */
count = fis.read(buf);
/* 读到文件末尾,fis的read()方法返回-1,必须判断如果count < 0,说明读到了最后 */
if(count < 0) {
return -1;
}
/* 抓取数据后指针归0 */
pos = 0;
/* 取出第一个字节 */
ch = buf[pos];
/* 每取一个数据,指针+1,右移 */
pos++;
/* 每取一个数据,count-1 */
count--;
/* 返回第一个字节 */
return ch;
} else if(count > 0) {
ch = buf[pos];
pos++;
count--;
/* 返回第一个字节后的每一个字节 */
return ch;
}
/* 读完文件,返回-1 */
return -1;
}

public void close() throws IOException {
fis.close();
}
}


问题:

上面的代码运行结果只拷贝了
8K
到目的文件。

原因:

是因为媒体文件在硬盘上的数据以二进制形式存在;
read()
方法在读取第一个字节的时候,有可能会读到:
11111111
;这样8个1的情况;而8个1的的二进制就是十进制的
-1
;程序中
while
循环的跳进等于
-1
时,循环停止;因此只复制了8K大小。

理解BufferedInputStream的read()方法:

细看BufferedInputStream类的read()方法,其返回的是int类型。而方法中读到的字节都是byte类型;这样做的原因,就是为了解决读取字节读到8个1的情况。read()方法在返回byte类型字节数据的时候,将byte类型提升为int类型,存储位数由1个8位,变为4个8位。为了确保返回的数据与原数据相同而不产生-1的情况,read()方法在类型提升之后,补了3个8位的0在原byte数据前面。过程如下图:



在自定义缓冲区中,虽然方法返回了
int
类型,进行了数据类型提升,但是没有进行补
0
的操作,意味着当读到8个1组成的
byte
数据时,返回了一个由32个1组成的
int
类型数据,结果还是
-1
。那么,如果要完成相同功能,只需取32个1的最后
8位
即可。取最后8位,将原数据与上
255




将每个return ch;语句改为return ch & 255;即可。

五、键盘录入

1.System标准输出输入

System
类中对应的成员
out
in
分别是:

System.out-标准输出流

System.in-标准输入流

System.in
用于读取键盘录入。

2.接收键盘录入

从键盘接收输入,并打印在控制台。

示例代码:

package com.heisejiuhuche.io;

import java.io.IOException;
import java.io.InputStream;

public class SysteminDemo {
public static void main(String[] args) {
/* 创建标准输入对象 */
InputStream in = System.in;
try {
/* 录入一个字符 */
int ch = in.read();
/* 打印该字符的ASCII码 */
System.out.println(in.read());
} catch(IOException e) {
throw new RuntimeException("运行出错~");
}
}
}


程序输出结果:

a
97


3.练习

接收键盘录入,当回车时打印整行内容;当输入
over
回车时,结束输入。

示例代码:

package com.heisejiuhuche.io;

import java.io.IOException;
import java.io.InputStream;

public class SysteminDemo {
public static void main(String[] args) {
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
int ch = 0;
try {
/* 一直读入字符,每输入一个字符,就往StringBuilder添加一个;
* 如果读到回车符,继续读入;
* 如果读到换行符,将StringBuilder中的字符组成字符串
* 判断该字符串是否等于over,如果是,结束程序;
* 如果不是,就打印该字符串,并将StringBuilder容器清空
*/
while(true) {
ch = in.read();
if(ch == 13)
continue;
if(ch == 10) {
String str = sb.toString();
if(str.equals("over")) {
break;
}
System.out.println(str);
sb.delete(0, sb.length());
} else
sb.append((char)ch);
}
} catch(IOException e) {
throw new RuntimeException("运行出错~");
}
}
}


六、转换流

1.InputStreamReader类

InputStreamReader
类用于将字节流转换为字符流。该类可以将读取到的字节数据转换为字符数据。其使用的编码表可以由开发者指定,也可以使用系统默认。利用转换流将字节流转换为字符流,就意味着该字节流可以使用字符流的缓冲区技术,调用其
readLine()
方法,使键盘录入的读取过程更加高效便捷。

利用转换流修改键盘录入并打印的代码:

package com.heisejiuhuche.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class InputStreamReaderDemo {
public static void main(String[] args) {
/* 创建标准输入对象 */
InputStream in = System.in;

/* 利用转换流将字节流转换为字符流 */
InputStreamReader isr = new InputStreamReader(in);

/* 转换后的字节流,就可以像字符流一样使用字符流的缓冲技术 */
BufferedReader bufr = new BufferedReader(isr);

/* 调用BufferedReader的readLine()方法整行获取键盘录入 */
String line = null;
try {
while((line = bufr.readLine()) != null) {
/* 如果输入over 结束键盘录入 */
if(line.equals("over"))
break;
System.out.println(line.toUpperCase());
}
} catch(IOException e) {
throw new RuntimeException("Exception occured...");
}
}
}


将字节流转换为字符流,相当于在字节流上套了两根管子;一根使字节流变成字符流;另一根使字节流可以使用字符流的缓冲技术。

2.OutputStreamWriter类

OutputStreamWriter
类用于将字符流转换为字节流。该类的编码表同样可以指定或使用系统默认。

用OutputStreamWriter类修改上面的代码:

package com.heisejiuhuche.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

public class InputStreamReaderDemo {
public static void main(String[] args) {
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
BufferedReader bufr = new BufferedReader(isr);

/* 创建标准输出对象 */
OutputStream out = System.out;

/* 用转换流将字符流转换为字节流 */
OutputStreamWriter osw = new OutputStreamWriter(out);

/* 使用缓冲字符输出流增强字符输出流 */
BufferedWriter bufw = new BufferedWriter(osw);

String line = null;
try {
while((line = bufr.readLine()) != null) {
/* 如果输入over 结束键盘录入 */
if(line.equals("over"))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
} catch(IOException e) {
throw new RuntimeException("Exception occured...");
} finally {
try {
if(bufw != null)
bufw.close();
} catch(IOException e) {
throw new RuntimeException("资源关闭失败");
}
}
}
}


可以将字节流转字符流的三个步骤简化为一行代码:

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));


3.转换流的作用

转换流可以指定读写时使用的字符编码集;如不指定,将使用系统默认的编码集,本机默认使用
GBK
。下面的代码演示了用
UTF-8
写入,用
GBK
读取会发生乱码的情况。

示例代码:

package com.heisejiuhuche.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class CharSetDemo {
public static void main(String[] args) {
write();
}

private static void read() {
BufferedReader bufr = null;

try {
/* 使用默认GBK字符集读取数据,结果会出现乱码 */
bufr = new BufferedReader(new FileReader("C:/Users/jeremy/Documents/javaTmp/demo.txt"));
System.out.println(bufr.readLine());
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(bufr != null)
bufr.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}

private static void write() {
InputStreamReader isr = null;
OutputStreamWriter osw = null;
BufferedReader bufr = null;
BufferedWriter bufw = null;
String line = null;

try {
bufr = new BufferedReader(new InputStreamReader(System.in));
/* 指定使用UTF-8字符集写入数据 */
bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
"C:/Users/jeremy/Documents/javaTmp/demo.txt"),"UTF-8"));

while((line = bufr.readLine()) != null) {
if(line.equals("over")) {
break;
}
bufw.write(line);
bufw.newLine();
bufw.flush();
}
/* 将写入的数据读取打印在控制台 */
read();

} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(bufr != null)
bufr.close();
} catch(IOException e) {
e.printStackTrace();
}
try {
if(bufw != null)
bufw.close();
} catch(IOException e) {
e.printStackTrace();
}
}

}
}


程序输出结果:

你好,这里是虹桥镇~
over
浣犲ソ锛岃繖閲屾槸铏规ˉ闀噡


为了正常显示,用InputStreamReader指定读取时编码集为UTF-8即可

bufr = new BufferedReader(new InputStreamReader(new FileInputStream(
"C:/Users/jeremy/Documents/javaTmp/demo.txt"),"UTF-8"));


程序输出结果:

你好,这里是虹桥镇~
over
你好,这里是虹桥镇~


小扩展

使用System类的方法setIn()和setOut()改变标准输入输出。

示例代码:

package com.heisejiuhuche.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class SystemSetDemo {
public static void main(String[] args) throws IOException {
BufferedReader bufr = null;
BufferedWriter bufw = null;

/* 改变标准输入源,从键盘变为指定目录的文件 */
System.setIn(new FileInputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt"));

/* 改变标准输出目的地,从控制台变为指定目录的文件 */
System.setOut(new PrintStream("C:/Users/jeremy/Documents/javaTmp/fos1.txt"));

bufr = new BufferedReader(new InputStreamReader(System.in));
bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = bufr.readLine();
bufw.write(line);
bufw.flush();
bufr.close();
bufw.close();
}
}


———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: