字节流顾名思义就是操作字节的流。字节流是可以操作任意数据的,比如说媒体数据,音乐,电影,图片等,当然它也是可以操作字符的。字符流就是基于字节流来实现的,我们知道一个字节是8个二进制位,在UTF-8中一个字节对应一个英文字符,人们发现用单个字节操作文本比较麻烦,所以就对字节流进行封装,当对文本进行操作的时候就不直接对字节流进行操作,而直接对封装后的字节流也就是字符流进行操作,这样就比较方便。
通过上述概述我们可以知道,使用字节流操作字符是完全行得通的。那么字符流是专门针对字符进行操作的,但是字符流能不能对除了字符以外的数据进行操作呢?
如果你对除字符外的数据,比如说多媒体数据(音乐,电影,图片)进行操作,比如说复制一个音乐文件,程序编译时不会报错的,但是操作过程中可能会出现数据丢失的情况,当你复制结束打开该文件的时候,可能会因为数据的丢失而无法打开。所以强烈建议对字符文本的操作使用字符流,对字符以外的数据,使用字节流。
字节流的基类:
InputStream和OutputStream
二,字节流读取文本文件:
需求:使用字节输入流,读取一个文本文件中的数据,打印该文件的文本内容到控制台。
import java.io.*;
public class InputStreamDemo
{
public static void main(String[] args) throws IOException {
method_4();
}
//这种方法在读取字节的时候,可以读取刚好和文件大小相同的字节,用available获取字节的大小
//但是这种方法要慎用,如果文件比较大,当在内存中建立字节数组缓冲区时,内存可能会无法装这么大,损坏内存
public static void method_4() throws IOException {
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[fis.available()];//available获取文件中数据的大小,可以设置大小刚好点的字节数组缓冲区,该方法是字节流特有的。
fis.read(buf);
System.out.println(new String(buf));
}
//将多个字节存储到字节数组缓冲区,然后打印
public static void method_3() throws IOException{
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[1024];
int len=0;
while ((len = fis.read(buf)) != -1)
{
System.out.println(new String(buf,0,len));
}
fis.close();
}
//一个一个的去读取字节
public static void method_2() throws IOException {
FileInputStream fis = new FileInputStream("fos.txt");
int len=0;
while ((len=fis.read()) != -1)
{
System.out.println((char)len);
}
fis.close();
}
public static void method_1() throws IOException {
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("asghkj".getBytes());
fos.close();
}
}
注意:字节流不需要刷新缓冲区,即没有flush方法,close方法只关闭流,不刷新;
原因是字符流也是基于字节流的,字节流是可操作的最小单位,可以直接写入一个字节操作一个,字符流之所以要刷新,是因为字符不一定是一个字节,必须要先存入到缓冲区,才能完整的输出,比如中文是两个字节,如果读取一个操作一个,那么读取到的一个字节只有半个汉字,根本没法输出,所以必须要先将字节存储到缓冲区,然后字节到编码表查找对应的文字,刷新后才能输出。
三,字节流操作图片文件
需求:通过字节流复制一个图片文件。
思路:1,用字节读取流对象和图片文件关联;
2,用字节写入流对象创建一个文件用于存储原图片;
3,循环读取字节写入目的文件;
4,关闭流;
import java.io.*;
public class PictureCopy
{
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("hh.jpg");//原文件
FileOutputStream fos = new FileOutputStream("mu.jpg");//目的文件
int len = 0;
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fis.close();
fos.close();
}
}
四,字节流的缓冲区
字节流的缓冲区的原理和字符流的一样,都是将原始流传入到缓冲区的构造函数,增强原有流的功能。
public BufferedInputStream(InputStream in)
public BufferedOutputStream(OutputStream out)
通过复制图片文件的程序我们知道多媒体文件的复制方法,原理如下:
import java.io.*;
public class MyCopyMp3
{
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("1.mp3");
FileOutputStream fod = new FileOutputStream("2.mp3");
byte[] buf = new byte[1024*1024];
int len=0;
while ((len = fis.read(buf)) != -1)
{
//System.out.println(new String(buf,0,len));
fod.write(buf);
}
fis.close();
}
}
那么又该如何通过字节流缓冲区的方法复制一个MP3文件呢?
需求:通过字节流缓冲区复制MP3文件.
import java.io.*;
public class Mp3Copy
{
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
copy_1();
long end = System.currentTimeMillis();
System.out.println("用时:" + (end-start) + "毫秒");
}
public static void copy_1() throws IOException {
//建立字节流缓冲区
BufferedInputStream bi = new BufferedInputStream(new FileInputStream ("1.mp3"));
BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream("2.mp3"));
int len = 0;
while ((len=bi.read())!=-1)
{
bo.write(len);
}
bi.close();
bo.close();
}
}
总结:不使用缓冲区读取字节文件的方法是直接建立字节输入流文件对象和字节输出流文件对象,创建字节数组,循环将字节输入流中的字节值(int类型,范围是0到255)读取到字节数组,每循环一次就将字节数组写入字节输出流中,字节值为-1就到了末尾,循环结束条件。
五,读取键盘输入
System.in是标准的输入流,它的类型是InputStream;
System.out 标准输出设备,控制台
System.in 标准输入设备,键盘
1,需求:通过键盘输入一行数据,当一行输入结束后,打印出来,当输入over的时候退出;
import java.io.*;
public class SysInputStream
{
public static void main(String[] args) throws IOException {
InputStream in = System.in;//接受键盘输入的字节输入流;
//建立缓冲区,把一行的数据存储到缓冲区中,当一行结束后,要清空缓冲区存储下一行的数据;
StringBuilder sb = new StringBuilder();
//因为不知道用户什么时候能输入结束,用while(true)循环
while (true)
{
int ch = in.read();//将每次读取到的一个字节存储在整型ch中
if(ch == '\r')
continue;
if(ch == '\n')//windows平台的回车是\r\n,两个字节,遇到'\r'时跳出本次循环,不保存到缓冲区sb
//遇到'\n'时说明一行结束
{
String s = sb.toString();//将缓冲区的字符转换成字符串toString();
if("over".equals(s)) //结束后的判断,当over和缓冲区的字符串相等时退出,并打印字符串
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());//打印完一行后要清除缓冲区缓存,否则每次读取到的字符都会存储到之前的字符后面;
}
else
sb.append((char)ch);//没有遇到换行符时,将读取到的字符添加到缓冲区,直到遇到回车键后退出;
}
//int ch = in.read();读取单个字符,当输入的字符数小于读取次数,会读取到windows平台下的回车'\r','\n'
//System.out.println(ch);
//int ch2 = in.read();
//System.out.println(ch2);
}
}
六,键盘录入以及打印的综合应用
需求:一下程序实现了键盘一次录入一行数据,使用的是字节转换流。需要特别注意,在使用字符输出流的时候要注意刷新缓冲区,否则数据只是在内存缓冲区。
字节转换流:
InputStreamReader是字节流通向字符流的桥梁;
对应的有: OutputStreamWriter 是字符流通向字节流的桥梁;
import java.io.*;
public class OutputStreamWriterDemo
{
public static void main(String[] args) throws IOException {
/*InputStream in = System.in;
InputStreamReader inr = new InputStreamReader(in);
BufferedReader bufr = new BufferedReader(inr);*/
//最常用的键盘录入的方法
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
/*
//建立字节输出流对象
OutputStream out = System.out;
//将字符输出流转换成字节输出流
OutputStreamWriter osw = new OutputStreamWriter(out);
//为了提高输出效率,采用缓冲区
BufferedWriter bufw = new BufferedWriter(osw); */
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while ((line=bufr.readLine())!=null)
{
if(line.equals("over"))
break;
bufw.write(line,0,line.length());
bufw.newLine();
bufw.flush();//字符输出流必须要刷新缓冲区内存才能将流里面的字符显示到控制台
}
bufw.close();
}
}
七,使用流的规律总结:
到目前为止,所接触到的几个重点流有:
Writer,
FileWriter,
BufferedWriter,
OutputStreamWriter,
PrintWriter,
Reader,
FileReader,
BufferedReader,
InputStreamReader,
LineNumberReader,
InputStream,
FileInputStream,
BufferedInputStream,
System.in,
LineNumberInputStream,
OutputStream,
FileOutputStream,
BufferedOutputStream,
System.out,
这些列举的流是最常用的几个流对象,我们在使用时怎样确定应该使用哪一个呢?其实在使用这些流的时候都是有规律的。
流操作的基本规律:
三个明确:
1,明确操作的源和目的:
源:输入流 InputStream Reader
目的:输出流 OutputStream Writer
2,明确操作的数据是否为纯文本:
是:字符流
否:字节流
3,明确要使用具体的那个对象,通过硬件设备进行区分
源设备:内存,硬盘,键盘;
目的设备:内存,硬盘,控制台
实例分析一:
需求:将一个文本文件存储到另一个文件中,文件的复制;
源:因为是源,所以使用读取流:InputStream或Reader
源的数据时文本,所以选用字符流Reader
源设备是硬盘上的一个文件,而Reader中可以对文件进行操作的是FileReader对象;
是否需要在原有基础上提高对文件的操作效率?需要,加入文本读取流的缓冲区
FileReader fr = new FileReader("tt.txt");
BufferedReader bufR = new BufferedReader(fr);
目的:因为是目的,所以采用OutputStream或Write
操作的数据是纯文本,所以选用字符流Writer
目的设备是硬盘上的一个文件,所以选用FileWriter对象;
FileWriter fw = new FileWriter("hah.txt");
BufferedWriter bufW = new BufferedWriter(fw);
实例分析二:
需求:将一个图片文件存储到另一个文件中;图片的复制;
源:是输入流,选择InputStream或Reader
不是一个纯文本文件,所以采用自节流,所以习题应选择InputStream
它是硬盘上的一个文件,InputStream体系统可以操作文件的类是FileInputStream
是否需要提高文件的读取效率,不需要
FileInputStream fis = new FileInoutStream("mm.jpg");
目的:
是输出流,所以选择OutputStream或Writer
图片文件不是文本文件,所以选用字节流;OutputStream
对于硬盘上的一个文件,OutputStream体系中可以操作文件的只有FileOutputStream
FileOutputStream fos = new FileOutputStream("mu.jpg");
实例分析三:
需求:将键盘录入的数据保存到一个文件中。
这个需求中有源和目的都存在。那么分别分析:
源:InputStream Reader
是不是纯文本?是!Reader
设备:键盘。对应的对象是System.in.
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader,那么就将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
实例分析四:
实例分析四的扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer
是否是纯文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
但是FileWriter是使用的默认编码表。GBK.
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。
而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,
需要用到转换流。
实例分析五:
练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
源:输入流 --> InputStream或Reader
操作的数据是否为纯文本?是。Reader
设备:硬盘,一个文件,FileReader
FileReader fr = new FileReader("Demo.txt");
需要提高效率吗?需要;BufferedReader
BufferedReader bugR = new BufferedReader(fr);
目的:输出流-->OutputStream或Writer
操作数据是纯文本;所以选择Writer;
设备:控制台。对应的对象时System.out,不是Writer吗,System.out是字节流啊?怎么办?
将字节流转换成字符流输出到控制台;
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要提高效率吗?需要。BufferedWriter
BufferedWriter bufW = new BufferedWriter(osw);
八,其他知识点扩展
System类中还提供了两个设置输入和输出的目的,通过这两个方法,可以
static void setIn(InputStream in)
重新分配“标准”输入流。
static void setOut(PrintStream out)
重新分配“标准”输出流
这两个方法可以将键盘输入改成从某一个指定文件输入,将控制台输出改为输出到一个文件中。
采用键盘录入的方将文件复制到另外一个文件中;七,使用流的规律总结:
到目前为止,所接触到的几个重点流有:
Writer,
FileWriter,
BufferedWriter,
OutputStreamWriter,
PrintWriter,
Reader,
FileReader,
BufferedReader,
InputStreamReader,
LineNumberReader,
InputStream,
FileInputStream,
BufferedInputStream,
System.in,
LineNumberInputStream,
OutputStream,
FileOutputStream,
BufferedOutputStream,
System.out,
这些列举的流是最常用的几个流对象,我们在使用时怎样确定应该使用哪一个呢?其实在使用这些流的时候都是有规律的。
流操作的基本规律:
三个明确:
1,明确操作的源和目的:
源:输入流 InputStream Reader
目的:输出流 OutputStream Writer
2,明确操作的数据是否为纯文本:
是:字符流
否:字节流
3,明确要使用具体的那个对象,通过硬件设备进行区分
源设备:内存,硬盘,键盘;
目的设备:内存,硬盘,控制台
实例分析一:
需求:将一个文本文件存储到另一个文件中,文件的复制;
源:因为是源,所以使用读取流:InputStream或Reader
源的数据时文本,所以选用字符流Reader
源设备是硬盘上的一个文件,而Reader中可以对文件进行操作的是FileReader对象;
是否需要在原有基础上提高对文件的操作效率?需要,加入文本读取流的缓冲区
FileReader fr = new FileReader("tt.txt");
BufferedReader bufR = new BufferedReader(fr);
目的:因为是目的,所以采用OutputStream或Write
操作的数据是纯文本,所以选用字符流Writer
目的设备是硬盘上的一个文件,所以选用FileWriter对象;
FileWriter fw = new FileWriter("hah.txt");
BufferedWriter bufW = new BufferedWriter(fw);
实例分析二:
需求:将一个图片文件存储到另一个文件中;图片的复制;
源:是输入流,选择InputStream或Reader
不是一个纯文本文件,所以采用自节流,所以习题应选择InputStream
它是硬盘上的一个文件,InputStream体系统可以操作文件的类是FileInputStream
是否需要提高文件的读取效率,不需要
FileInputStream fis = new FileInoutStream("mm.jpg");
目的:
是输出流,所以选择OutputStream或Writer
图片文件不是文本文件,所以选用字节流;OutputStream
对于硬盘上的一个文件,OutputStream体系中可以操作文件的只有FileOutputStream
FileOutputStream fos = new FileOutputStream("mu.jpg");
实例分析三:
需求:将键盘录入的数据保存到一个文件中。
这个需求中有源和目的都存在。那么分别分析:
源:InputStream Reader
是不是纯文本?是!Reader
设备:键盘。对应的对象是System.in.
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader,那么就将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
实例分析四:
实例分析四的扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer
是否是纯文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
但是FileWriter是使用的默认编码表。GBK.
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。
而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,
需要用到转换流。
实例分析五:
练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
源:输入流 --> InputStream或Reader
操作的数据是否为纯文本?是。Reader
设备:硬盘,一个文件,FileReader
FileReader fr = new FileReader("Demo.txt");
需要提高效率吗?需要;BufferedReader
BufferedReader bugR = new BufferedReader(fr);
目的:输出流-->OutputStream或Writer
操作数据是纯文本;所以选择Writer;
设备:控制台。对应的对象时System.out,不是Writer吗,System.out是字节流啊?怎么办?
将字节流转换成字符流输出到控制台;
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要提高效率吗?需要。BufferedWriter
BufferedWriter bufW = new BufferedWriter(osw);
八,其他知识点扩展
System类中还提供了两个设置输入和输出的目的,通过这两个方法,可以
static void setIn(InputStream in)
重新分配“标准”输入流。
static void setOut(PrintStream out)
重新分配“标准”输出流
这两个方法可以将键盘输入改成从某一个指定文件输入,将控制台输出改为输出到一个文件中。
采用键盘录入的方将文件复制到另外一个文件中;
import java.io.*;
public class Demo
{
public static void main(String[] args) throws IOException {
System.setIn(new FileInputStream("Demo.txt"));//默认的是键盘,改为一个文件
System.setOut(new PrintStream("hehe.txt"));//默认的是控制台,改为文件;
//常用键盘输入格式,这里的System.in就是Demo.txt文件了,不是键盘了
BufferedReader bufR = new BufferedReader(new InputStreamReader(System.in));
//BufferedReader bufR = new BufferedReader(new InputStreamReader(new FileInputStream("Demo.txt")));
//这里的System.out也不是控制台了,而是hehe.txt文件
BufferedWriter bufW = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while ((line = bufR.readLine())!=null)
{
if("over".equals(line))
break;
bufW.write(line,0,line.length());
bufW.newLine();
bufW.flush();
}
bufW.close();
}
}
九,异常记录原理以及用户系信息获取
需求:自定义一个类,当类中的某个方法出现异常的时候,记录这个异常,将这个异常信息保存到一个文件中,并且要求记录异常出现的时间。
import java.util.*;
import java.io.*;
import java.text.*;
public class Example {
public static void main(String[] args) {
try
{
int[] arr = new int[3];
System.out.println(arr[4]);
}
catch (Exception e)
{
try
{
//e.printStackTrace(new PrintStream("exception.log"));
//创建时间格式
PrintStream ps = new PrintStream("exception.log");
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String time = sdf.format(d);
ps.println(time);
e.printStackTrace(ps);//异常默认是打印到控制台上,其实内部就是使用的是e.printStackTrace(System.out),
//接收的是一个PrintStream
}
catch (Exception ex)
{
throw new RuntimeException("创建日志失败");
}
}
}
}
import java.util.*;
import java.io.*;
public class PropertiesDemo {
public static void main(String[] args) throws Exception {
Properties pros =System.getProperties();
//System.out.println(pros);//输出的结果没有格式
//将系统属性信息与流相结合,利用流中封装的方法就可以读取到属性信息,并且格式化输出
pros.list(System.out);
//pros.list(new PrintWriter(System.out,true));
//将系统属性信息读取到一个文件中,保存
PrintStream ps = new PrintStream("pros.info");
pros.list(ps);
}
}