黑马程序员——Java IO流 2——字节流

来源:互联网 发布:微星网络协议栈开吗 编辑:程序博客网 时间:2024/04/29 22:50

------- android培训java培训、期待与您交流! ----------

字节流是独立于字符流的另一种输入输出方式,类层次结构如下:

InputStream(System.in)

|--FIleInputStream

|--BufferedInputStream

OuputStream

|--FileOutputStream

|--BufferedOuputStream

|--PrintStream(System.out)

字节流是一个字节一个字节传输数据的方式

FileStream

文件字节流对象是专门用于读写文件的字节流对象,它主要包括FileInputStream、FileOutputStream。

FileInputStream
FileInputStream(File file)

构造函数,创建一个FileInputStream对象。

file:为了读取而打开的文件。

FileNotFoundException:文件不存在,或不是文件,则抛此异常

FileInputStream(String name)

构造函数,创建一个FileInputStream对象。

name:文件名

FileNotFoundException:文件不存在,或不是文件,则抛此异常

int read()

从流中读取一个字节

返回值:下一个数据字节;如果已到达文件末尾,则返回-1。

int read(byte[] b)

从流中读取b.length个字节

b:存储读取数据的缓冲区

返回:读入缓冲区的字节总数,如果到达末尾则返回-1

int available()

获取可以从输入流读取的剩余字节数。

FileOutputStreamFileOutputStream(File file)

构造函数,创建一个FileOutputStream对象。

file:为了写入而打开的文件。如果文件不存在则创建

FileNotFoundException:如果file对象指向的是目录,则抛此异常


FileOutputStream(String name)

构造函数,创建一个FileOutputStream对象。

name:文件名。如果文件不存在则创建

FileNotFoundException:如果name指向的是目录,则抛此异常

void write(byte[] b)

将b.length个字节从b中写入到文件输出流

b:数据


下面举例说明用法:

import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class FileStream {public static void main(String[] args) {writeFile();readFile1();readFile2();readFile3();}//用write方法向程序外写出数据private static void writeFile(){FileOutputStream fos=null;try {fos=new FileOutputStream("test.txt");fos.write("write string by byteStream\n".getBytes());} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (fos!= null)fos.close();} catch (IOException e) {throw new RuntimeException(e);}}}//用read()方法向程序内写入数据private static void readFile1() {FileInputStream fis=null;try {fis=new FileInputStream("test.txt");int ch=0;while((ch=fis.read())!=-1){System.out.print((char)ch);}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (fis != null)fis.close();} catch (IOException e) {throw new RuntimeException(e);}}}//用read(byte[])方法向程序内写入数据private static void readFile2() {FileInputStream fis=null;try {fis=new FileInputStream("test.txt");byte[] buf=new byte[1024];int len=0;while((len=fis.read(buf))!=-1){System.out.print(new String(buf,0,len));}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (fis != null)fis.close();} catch (IOException e) {throw new RuntimeException(e);}}}//用read(byte[])方法配合available()方法向程序内写入数据//这方法不好,因为如果文件过大,会造成内存溢出private static void readFile3() {FileInputStream fis=null;try {fis=new FileInputStream("test.txt");byte[] buf=new byte[fis.available()];fis.read(buf);System.out.println(new String(buf));} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (fis != null)fis.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
上面的三种读取方式中,第二种方式最好,因为性能最好,且不容易出bug

复制图片

由于图片文件不是由字符组成,所以不能用字符流来复制,只能用字节流,例子如下:
import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class CopyImage {public static void main(String[] args) {FileOutputStream fos=null;FileInputStream fis=null;try {fis=new FileInputStream("Image1.png");fos=new FileOutputStream("Image2.png");byte[] buf=new byte[1024];int len=0;while((len=fis.read(buf))!=-1){fos.write(buf,0,len);}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (fis != null)fis.close();if (fos != null)fos.close();} catch (IOException e) {throw new RuntimeException(e);}}}}

字节流缓冲区

字节流对象和字符流一样也有对应的buffered对象,就是BufferedInputStream和BufferedOuputStream,这两个类的用法基本和字符流一样,此处以复制mp3举例:
import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class CopyMp3 {public static void main(String[] args) {BufferedInputStream bis=null;BufferedOutputStream bos=null;try {bis=new BufferedInputStream(new FileInputStream("喜欢你.mp3"));bos=new BufferedOutputStream(new FileOutputStream("喜欢.mp3"));int len=0;while((len=bis.read())!=-1){bos.write(len);}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (bis != null)bis.close();if (bos != null)bos.close();} catch (IOException e) {throw new RuntimeException(e);}}}}

自定义字节流缓冲区

下面我们看看能不能通过自定义的流缓冲区对象,提高缓冲区性能,代码如下:
自定义BufferedInputStream,此处用了8192的字节数组,会发现此数越大性能越高,但是到某一个阈值又会变小:
import java.io.IOException;import java.io.InputStream;public class MyBufferedInputStream {InputStream in;byte[] buf=new byte[8192];int pos=0;//指示当前读到哪个位置int count=0;//指示当前还剩多少字节没读public boolean isEnd=false;//指示文件是否读完MyBufferedInputStream(InputStream in){this.in=in;}public Byte read() throws IOException {if (pos<count) {return buf[pos++];}else {count=in.read(buf);if (count==-1) {isEnd=true;//此处添加结束标志是为了防止把数据-1和结束标志-1搞混淆return -1;}pos=0;return buf[pos++];}}public void close()throws IOException{in.close();}}
写一个测试方法测下性能:
import java.io.BufferedOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class CopyMp3InMyBuffer {public static void main(String[] args) {long start=System.currentTimeMillis();MyBufferedInputStream bis=null;BufferedOutputStream bos=null;try {bis=new MyBufferedInputStream(new FileInputStream("喜欢你.mp3"));bos=new BufferedOutputStream(new FileOutputStream("喜欢2.mp3"));while(!bis.isEnd){bos.write(bis.read());}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (bis != null)bis.close();if (bos != null)bos.close();} catch (IOException e) {throw new RuntimeException(e);}}long end=System.currentTimeMillis();System.out.println((end-start)+"毫秒");}}
这段代码在本机跑出了113毫秒的成绩,这比用jdk的BufferedInputStream足足快了一倍多,是不是很神奇!视频中讲了read方法返回int(之所以要转成int仅仅是为了多一个-1来判断文件结束,但是把1个字节转换为4个字节太浪费性能,所以此处没有照搬视频),但我觉得把byte转换成int很浪费性能,所以就自己加了个结束标志。

读取键盘录入

下面的从键盘读取一个字符串,转换成大写,并打印出来。
import java.io.IOException;import java.io.InputStream;public class keyboardInput {public static void main(String[] args) {InputStream in=System.in;//StringBuffer用于多线程,StringBuilder用于单线程StringBuilder sb=new StringBuilder();int ch=0;try {while(true){ch=in.read();if (ch=='\r') {continue;}if (ch=='\n') {String s=sb.toString();if ("over".equals(s)) {break;}System.out.println(s.toUpperCase());sb.delete(0, sb.length());}else {sb.append((char)ch);}}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (in != null)in.close();} catch (IOException e) {throw new RuntimeException(e);}}}}

读取转换流

通过把字节流转换为字符流,从而可以使用更方便的字符读取方法。主要通过InputStreamReader类,例子如下:
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;public class TransStreamDemo {public static void main(String[] args) {InputStream iStream=System.in;InputStreamReader isr=new InputStreamReader(iStream);BufferedReader bReader=null;try {bReader=new BufferedReader(isr);String line=null;while((line=bReader.readLine())!=null){if ("over".equals(line)) {break;}System.out.println(line.toUpperCase());}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (bReader != null)bReader.close();} catch (IOException e) {throw new RuntimeException(e);}}}}

写入转换流

如果想向标准输出设备(System.out)(控制台)输出数据,必须把字节流转换为字符流,要实现此功能,用OutputStreamWriter类,思路是System.out→OutputStreamWriter→BufferedWriter,例子代码如下:
import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;public class TransStreamDemo {public static void main(String[] args) {BufferedReader br=null;BufferedWriter bw=null;try {br=new BufferedReader(new InputStreamReader(System.in));bw=new BufferedWriter(new OutputStreamWriter(System.out));String line=null;while((line=br.readLine())!=null){if ("over".equals(line)) {break;}bw.write(line.toUpperCase());bw.newLine();bw.flush();}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (br != null)br.close();if (bw!=null) {bw.close();}} catch (IOException e) {throw new RuntimeException(e);}}}}
注意,上面的代码使用了创建对象的简写形式。上面的代码是键盘输入输出的最常见写法。

流操作规律

IO流对象很多,如何确定用哪一个流对象。主要是三个'明确',
1.明确是源还是目的
源:输入流就是从当前程序的外部向当前程序输入数据,此时就用InputStream Reader体系
目的:输出流就是从当前程序向外部输出数据,此时就用OutputStream Writer体系
2.明确操作的数据是否是纯文本
是:字符流 Reader Writer
不是:用字节流 InputStream OutputStream
3.明确设备
键盘:System.in
控制台:System.out
硬盘(文件):FileXXXXXX
4.明确是否需要提高效率
是:用BufferedXXXXXX

例子1:将一个文本文件中的数据存储到另一个文件中,复制文本文件。
明确源还是目的:既有源也有目的,所以输入流和输出流都用
明确是否操作纯文本:文本文件,所以用Reader和Writer系列
明确设备:硬盘上的文件,所以用FileReader和FileWriter 
明确是否提高效率:是所以使用BufferedReader和BufferedWriter

所以声明IO对象的代码如下:
FileReader fr=new FileReader("a.txt");
BufferedReader bufr=new BufferedReader(fr);
FileWriter fw=new FileWriter("b.txt");
BufferedWriter bufw=new BufferedWriter(fw);

例子2:将一个图片文件中的数据存储到另一个文件中,复制图片文件。
明确源还是目的:既有源也有目的,所以输入流和输出流都用
明确是否操作纯文本:图片文件,不是纯文本,所以用InputStream和OutputStream系列
明确设备:硬盘上的文件,所以用FileInputStream和FileOuputStream 
明确是否提高效率:是所以使用BufferedInputStream和BufferedOutputStream

所以声明IO对象的代码如下:
FileInputStream fis=new FileInputStream("a.png");
BufferedInputStream bis=new BufferedInputStream(fis);
FileOutpuStream fos=new FileOutputStream("b.png");
BufferedOutputStream bos=new BufferedOutputStream(fos);

例子3:将键盘流入的数据保存到一个文件中
明确源还是目的:既有源也有目的,所以输入流和输出流都用。
下面分别分析:
源:明确是否操作纯文本 是 用Reader;明确设备 键盘 用System.in;由于System.in是字节流而Reader是字符流,所以需要用InputSteamReader转换一下。是否需要提高效率:需要,用BufferedReader。所以代码如下:
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);

目的:明确是否操作文本 是 用Writer;明确设备 硬盘文件 用FileWriter;是否提高效率:是 所以用BufferedWriter.
BufferedWriter bw=new BufferedWriter(new FileWriter("b.txt"));

字符流与编码

字符流对象默认是用GBK编码来处理中文字符数据的。字符输入流(比如FileReader)默认用GBK来解码从文件中读到的二进制数据,而字符输出流(比如FileWriter)默认用GBK来编码要存到文件中的二进制数据。如果想要改变默认的GBK编码(比如要使用UTF-8编码),就需要用到转换流,转换流的构造方法中第二个参数可以指定编码。
例如:想把程序中的数据以UTF-8编码的形式存储到文件中
明确源还是目的:目的,用输出流
明确是否操作的是文本:是所以用Writer
明确设备文件:所以使用FileWriter
明确字符编码:UTF-8,由于FileWriter使用GBK编码,所以改用FileOutputStream结合OutputStreamWriter
明确是否提高效率:是用BufferedWriter
代码如下:
OutputStreamWriter osw=new OutputStreamWriter(new FIleOutputSteam("d.txt"),"UTF-8");
BufferedWriter bufw=new BufferedWriter(osw);

改变标准输入输出设备*

主要使用System.setIn()方法和System.setOut()方法
例子代码如下:
import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.io.PrintStream;public class TransStreamDemo {public static void main(String[] args) {BufferedReader br=null;BufferedWriter bw=null;try {System.setIn(new FileInputStream("AutoBoxing.java"));System.setOut(new PrintStream("test1.txt"));br=new BufferedReader(new InputStreamReader(System.in));bw=new BufferedWriter(new OutputStreamWriter(System.out));String line=null;while((line=br.readLine())!=null){if ("over".equals(line)) {break;}bw.write(line.toUpperCase());bw.newLine();bw.flush();}} catch (IOException e) {throw new RuntimeException(e);} finally {try {if (br != null)br.close();if (bw!=null) {bw.close();}} catch (IOException e) {throw new RuntimeException(e);}}}}

异常日志信息

用输入输出流将程序异常写到日志文件中:
import java.io.IOException;import java.io.PrintStream;import java.util.*;import java.text.*;public class ExceptionInfo{public static void main(String[] args) {try{//制造一个数组角标越界int[] arr=new int[2];System.out.println(arr[3]);}catch(Exception e){try {Date date=new Date();SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String s=sdf.format(date);PrintStream ps=new PrintStream("excetion.log");ps.println(s.toString());System.setOut(ps);} catch (IOException ex) {throw new RuntimeException("创建异常日志失败");}e.printStackTrace(System.out);}}}
这里使用了Date类和SimpleDateFormat类。Date类是java.util包中的Date类不是java.sql包中的Date类,java.sql.Date类没有无参构造函数,java.util.Date类有无参构造函数。SimpleDateFormat类的构造函数参数的格式如下:
字母日期或时间元素表示示例GEra 标志符TextADy年Year1996; 96M年中的月份MonthJuly; Jul; 07w年中的周数Number27W月份中的周数Number2D年中的天数Number189d月份中的天数Number10F月份中的星期Number2E星期中的天数TextTuesday; TueaAm/pm 标记TextPMH一天中的小时数(0-23)Number0k一天中的小时数(1-24)Number24Kam/pm 中的小时数(0-11)Number0ham/pm 中的小时数(1-12)Number12m小时中的分钟数Number30s分钟中的秒数Number55S毫秒数Number978z时区General time zonePacific Standard Time; PST; GMT-08:00Z时区RFC 822 time zone-0800

系统信息

将jdk的系统信息带换行输出到文件中,代码如下:
import java.io.IOException;import java.io.PrintStream;import java.util.Properties;public class SystemInfo {public static void main(String[] args) {Properties pro=System.getProperties();PrintStream ps=null;try {ps=new PrintStream("systemInfo.txt");pro.list(ps);} catch (IOException e) {throw new RuntimeException(e);} finally {if (ps != null)ps.close();}}}
注意PrintStream对象的close方法并不抛出异常

下面是程序输出的系统信息:
-- listing properties --java.runtime.name=Java(TM) SE Runtime Environmentsun.boot.library.path=C:\Users\boss big\AppData\Local\MyEcl...java.vm.version=24.45-b08java.vm.vendor=Oracle Corporationjava.vendor.url=http://java.oracle.com/path.separator=;java.vm.name=Java HotSpot(TM) 64-Bit Server VMfile.encoding.pkg=sun.iouser.script=user.country=CNsun.java.launcher=SUN_STANDARDsun.os.patch.level=java.vm.specification.name=Java Virtual Machine Specificationuser.dir=D:\Code\Java\javaenhancejava.runtime.version=1.7.0_45-b18java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironmentjava.endorsed.dirs=C:\Users\boss big\AppData\Local\MyEcl...os.arch=amd64java.io.tmpdir=C:\Users\BOSSBI~1\AppData\Local\Temp\line.separator=java.vm.specification.vendor=Oracle Corporationuser.variant=os.name=Windows 8sun.jnu.encoding=GBKjava.library.path=C:\Users\boss big\AppData\Local\MyEcl...java.specification.name=Java Platform API Specificationjava.class.version=51.0sun.management.compiler=HotSpot 64-Bit Tiered Compilersos.version=6.2user.home=C:\Users\boss biguser.timezone=java.awt.printerjob=sun.awt.windows.WPrinterJobfile.encoding=GBKjava.specification.version=1.7user.name=boss bigjava.class.path=D:\Code\Java\javaenhance\bin;D:\Code\...java.vm.specification.version=1.7sun.arch.data.model=64java.home=C:\Users\boss big\AppData\Local\MyEcl...sun.java.command=IOByteStream.SystemInfojava.specification.vendor=Oracle Corporationuser.language=zhawt.toolkit=sun.awt.windows.WToolkitjava.vm.info=mixed modejava.version=1.7.0_45java.ext.dirs=C:\Users\boss big\AppData\Local\MyEcl...sun.boot.class.path=C:\Users\boss big\AppData\Local\MyEcl...java.vendor=Oracle Corporationfile.separator=\java.vendor.url.bug=http://bugreport.sun.com/bugreport/sun.cpu.endian=littlesun.io.unicode.encoding=UnicodeLittlesun.desktop=windowssun.cpu.isalist=amd64

注意上面的file.encoding=GBK就是系统默认的字符编码






 






0 0
原创粉丝点击