-------android培训、java培训、java学习型技术博客、期待与您交流! ----------
知识点二 字节流
一、概述:
1、字节流的操作原理和字符流类是相似的,只不过字节流可以对图片、音频和视频媒体进行操作。
2、由于媒体数据中都是以字节存储的,所以,字节流对象可直接对媒体进行操作,可以不用再进行刷新流的动作。
3、读写字节流:InputStream ---> 输入流(读)
OutputStream ---> 输出流(写)
4、不用进行刷新流的动作的原因:
因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。可直接将字节写入到指定文件中,但是需要在写代码的时候,如果有字符串,要将字符串转为字节数组再进行操作。
5、字节流所特有方法:
int available() ---> 放回数据字节的长度,包含终止符
在定义字节数组长度的时候,可以用到这个方法:byte[] = new byte[fos.available()] (fos为字节流对象)
但是,对于这个方法要慎用,如果字节过大,比如一部电影(几个G),那么如此大的数组就会损坏内存,超过jvm所承受的大小(指定内存为64M)。
举例:
/*
字符流:
FileReader
FileWriter
BufferedReader
BufferedWriter
字节流:
两个基类:
InputStream(读) OutputStream(写)
需求:想要操作图片数据,这时候就要用到字节流
*/
import java.io.*;
class FileStream{
public static void main(String []args) throws IOException
{
// writeFile();
// readFile_1();
// readFile_2();
readFile_3();
}
public static void readFile_1() throws IOException
{
FileInputStream fis=new FileInputStream("fos.txt");
int ch=0;
while((ch=fis.read())!=-1)
{
System.out.println((char)ch);
}
fis.close();
}
public static void readFile_2() 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 readFile_3() throws IOException
{
FileInputStream fis=new FileInputStream("fos.txt");
// int num=fis.available();
//这种方式要慎用,数据不是太大的话,可以这样使用
byte[] buf=new byte[fis.available()];//定义一个刚刚好的缓冲区,不用再循环了
fis.read(buf);
// System.out.println("num="+num);
System.out.println(new String(buf));
fis.close();
}
public static void writeFile() throws IOException
{
FileOutputStream fos=new FileOutputStream("fos.txt");
//将字符串变为字节数组
fos.write("abcde".getBytes());
fos.close();
}
}
二、复制媒体文件(一张图片):
1、思路:
1)用字节流读取流对象和媒体文件相关联
2)用字节写入流对象,创建一个媒体文件,用于存储获取到的媒体文件数据
3)通过循环读写,完成数据的存储
4)关闭资源
注意:不要用字符流操作媒体文件,因为图片数据读取一段后去查编码表,如果找到对应的数字编码未变话,如果没找到去找编码表的未知区域,所以编码会发生变化,这样生成新图片的编码和老图片的编码就不一样了,字节码发生变化,因此字符流只用于处理文字数据。
示例:
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
FileOutputStream fos = null;
FileInputStream fis = null;
try
{
fos = new FileOutputStream("c:\\2.bmp");
fis = new FileInputStream("c:\\1.bmp");
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("复制文件失败");
}
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、读写特点:
read():会将字节byte型值提升为int型值
write():会将int型强转为byte型,即保留二进制数的最后八位。
2、原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区
2)循环这个动作,知道最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素
3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增
4)取出的时候,数组中的元素再减少,取出一个,就减少一个,直到减到0即数组取完
5)到了文件的结尾处,存入最后一组数据,当取完数组中的元素,就会减少到0,这是全部数据就取完了
示例:
/*
通过缓冲区,演示Mp3的复制
BufferedOutputStream
BufferedInputStream
*/
import java.io.*;
class CopyMp3{
public static void main(String []args) throws IOException
{
long start=System.currentTimeMillis();
//copy_1();
copy_2();
long end=System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
//通过字节流的缓冲区,完成复制
public static void copy_1() throws IOException
{
//缓冲区里已经定义了数组
BufferedInputStream bufis=new BufferedInputStream(new FileInputStream("C:\\0.mp3"));
BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("C:\\1.mp3"));
int by=0;
while((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
public static void copy_2() throws IOException
{
MyBufferedInputStream bufis=new MyBufferedInputStream(new FileInputStream("C:\\0.mp3"));
BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("C:\\2.mp3"));
int by=0;
//读了一次没有写出去
// System.out.println("第一个字节:"+bufis.myRead());
while((by=bufis.myRead())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.myClose();
}
}
4、自定义字节流缓冲区:
思路:
1、定义一个固定长度的数组
2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为0
3、每次将字节数据存入元素要先将数组中的元素取完
注:取出的是byte型,返回的是int型,这里存在提升的动作,
当byte中的八位全为1的时候是byte的-1,提升为int类型,就变为int型的-1,,read循环条件就结束了
变为-1的原因是由于在提升时,将byte的八位前都补的是1,即32位的数都是1,即为int型的-1了。
如何保证提升后的最后八位仍为1呢?就需要将前24位补0,就可以保留原字节数据不变,又可以避免转为int型出现-1的情况;
那么要如何做呢?
示例:
/*
11111111 -->提升了一个int类型,那不还是-1,吗?是-1的原因是因为在8个1的前面补得的是1导致的
那么我只要在前面补0,既可以保留原字节数据不变,又可以避免-1的出现,那么怎,补0呢
11111111 11111111 11111111 11111111
&00000000 00000000 00000000 11111111
-------------------------------------
00000000 00000000 00000000 11111111
这样就可以避免-1的发生
byte:-1 ---->int:-1
read方法在提升,write方法将指定的字节写入此缓冲的输出流(强制转换,取最低的8位)
*/
import java.io.*;
class MyBufferedInputStream{
private InputStream in;
private byte[] buf=new byte[1024*4];
private int pos=0;
private int count=0;
MyBufferedInputStream(InputStream in)
{
this.in=in;
}
//一次读一个字节,从缓冲区(字节数组)获取
public int myRead() throws IOException
{
//通过in对象,读取硬盘上的数据,并存储到buf中
if(count==0)
{
count= in.read(buf);
if(count<0)
return -1;
pos = 0;
//取数组第一个元素pos=0;
byte b=buf[pos];
count--;
pos++;
//b与255进行与操作,用十六进制表示就是0xff
return b&255;
}else if(count>0)
{
byte b=buf[pos];
count--;
pos++;
//b与255进行与操作,用十六进制表示就是0xff
return b&255;
}
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}
5.读取键盘录入
System.out:对应的是标准的输出设备,控制台。
System.in:对应的标准输入设备,键盘
/*
需求:
通过键盘录入数据,当录入一行数据后就进行打印。
如果当录入的数据时over,那么停止录入
Dos下键盘录入操作终止可以使用Ctrl+C操作
*/
import java.io.*;
class ReadIn
{
public static void main(String[] args) throws IOException
{
InputStream in=System.in;
StringBuilder sb=new StringBuilder();
/*
int ch=0;
while((ch=in.read())!=-1)
{
System.out.println(ch);
}
*/
while(true)
{
int 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=new StringBuilder();
sb.delete(0,sb.length());
}
else
sb.append((char)ch);
}
in.close();
//数据类型转换
System.out.println('\r'+0);
System.out.println('\n'+0);
/*
int by=in.read();
int by1=in.read();
int by2=in.read();
System.out.println(by);
System.out.println(by1);
System.out.println(by2);
*/
}
}
知识点三 转换流
一、概述:
转换流的由来:内部可以指定编码表
1.通过上面的示例,通过键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法
而键盘录入的read方法字节流InputStream的方法
那么能不能将字节流转成字符流在使用字符流缓冲区的readLine方法呢?
2.两个转换流:读取转换流InputStreamReader和写入转换流OutputStreamWriter,这两个类分别属于字符流Reade和Writer的子类
二、读取转换流:
InputStreamReader是字节流通向字符流的桥梁,它使用指定的charset读取字节并将其解码为字符。
InputStreamReader:读取转换流
a.获取键盘录入对象: ---> InputStream in = System.in;
b.将字节流对象转换成字符流对象,使用转换流InputStreamReader: ---> InputStreamReader isr = new InputStreamReader(in);
c.为提高效率,将字符串进行缓冲区技术操作,使用BufferedReader: ---> BufferedReader bufw = new BufferedReader(isr);
d.之后就可以使用readLine()方法读取录入的一行数据了。
示例:
class TransStreamDemo{
public static void main(String []args) throws IOException
{
//获取键盘录入对象
// InputStream in=System.in;
//将字节流对象装换成字符流对象,InputStreamReader
//InputStreamReader isr=new InputStreamReader(in);
//为了提高效率,讲将字符流进行缓冲区技术的高效操作
// BufferedReader bufr=new BufferedReader(isr);
BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("CopyMp3.java")));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
System.out.println(line.toUpperCase());
}
bufr.close();
}
}
OutputStreamWriter是字符流通向字节流的桥梁,可使用指定的charset将要写入流中的字符编码成字节。
OutputStreamWriter:写入转换流 ---> 和读取转换流同理,即使用对应的Writer的子类
示例:
class TransStreamDemo{
public static void main(String []args) throws IOException
{
//*************毕老师说这句换一定要记住,键盘录入,非常的重要********************
//键盘录入最常见写法
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
知识点四 流操作规律
一、分析清楚源和目的以及需求:
1.源:键盘录入
目的:控制台
2.需求:想把键盘录入的数据存储到一个文件中
源:键盘
目的:文件
3.需求:想要将一个文件的数据打印在控制台上。
源:文件
目的:控制台
二:流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个
通过三个来明确
1.明确源和目的
源:输入流 InputStream Reader
目的:输出流 OutputStream Writer
2.明确操作的数据是否是纯文本
是:字符流
不是:字节流
3.当体系明确后,在明确要使用哪个具体的对象
通过设备来进行区分;
源设备:内存(ArrayStream),硬盘(FileStream),键盘(System.in)
目的设备:内存(ArrayStream),硬盘(FileStream),控制台(System.out)。
4、需求体现:
4.1需求:将一个文本文件中的数据存储到另一个文件中,复制文件。
源:因为是源,所以使用读取流 InputStream Reader
是不是操作文本文件?
是!这时就可以选择Reader
这样体系就明确了
接下来明确对象该体系中的哪个对象。
明确设备:硬盘上的一个文件。
Reader体系中可以操作文件的对象是FileReader
是否需要提高效率:是!加入Reader体系中的缓冲区,BufferedReader
FileReader fr=new FileReader("a.txt");
BufferReader bufr=new BufferedReader(fr);
目的:OutputStream Writer
是否是纯文本文件?
是!Writer。
设备:硬盘。一个文件。
Writer体系中可以操作文件的对象是FileWriter
是否需要提高效率:是!加入Writer体系中的缓冲区,BufferedWriter
FileWriter fw=new FileWriter("b.txt");
BufferedWriter bufw=new BufferedWriter(fw);
练习:将一个图片文件中数据存储到另一个文件中,复制文件。要按照以上格式完成三个明确。
分析:
1)源:InputStream和Reader
图片:字节流 ---> InputStream
设备:硬盘上的文件 ---> FileInputStream
2)目的:OutoutStream和Writer
图片:字节流 ---> OutputStream
设备:硬盘上的文件 ---> FileOutputStream
4.2需求:将键盘录入的数据保存到一个文件中
这个需求中有源和目的都存在。
那么分别分析
源: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);
目的:OutputputStream Writer
是否是纯文本?是!Writer
设备:硬盘。一个文件。使用FileWriter
FileWriter fw=new FileWriter("c.txt");
需要提高效率吗?需要
BufferedWriter bufw=new BufferedWriter(fw);
5.扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中
class TransStreamDemo2{
public static void main(String []args) throws IOException
{
//获取键盘录入对象
// InputStream in=System.in;
//键盘录入最常见写法
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d2.txt"),"UTF-8"));
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
6.改变标准输入输出设备
System.setIn(new FileInputStream("PersonDemo.java"));
System.setOut(new PrintStream("zz.txt"));
一、异常的日志信息:
当程序在执行的时候,出现的问题是不希望直接打印给用户看的,是需要作为文件存储起来,方便程序员查看,并及时调整的。
示例:/*
说明:Log4j的jar包可以完成日志信息建立
*/
import java.io.*;
import java.util.*;
//格式化要用的包
import java.text.*;
class ExceptionInfo
{
public static void main(String[] args) throws IOException
{
try{
int[] arr=new int [2];
System.out.println(arr[3]);
}catch(Exception e)
{
try{
//打印日期
Date d=new Date();
//SimpleDateFormat将时间格式化
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//h是12小时制,H是24小时制
String s=sdf.format(d);
PrintStream ps=new PrintStream("exception.log");
//ps是字节流,要将字符串变成字节数组写出去
//ps.write(d.toString().getBytes);
//ps.println(d.toString());
ps.println(s);
//改变标准输出
System.setOut(ps);
//e.printStackTrace(new PrintStream("a.txt"));
}catch(IOException ex)
{
throw new RuntimeException("日志文件创建失败");
}
e.printStackTrace(System.out);
}
}
}
二、系统信息:
获取系统信息:Properties getProperties()
将信息输出到指定输出流中
void list(PrintStream out)
将输出流中数据存入指定文件中
new PrintStream("systeminfo.txt")
import java.io.*;
import java.util.*;
class SystemInfo
{
public static void main(String[] args) throws IOException
{
//打印系统信息
//源就是集合中的数据也就是内存
Properties prop=System.getProperties();
//System.out.println(prop);
//将系统信息打印在控制台上
//prop.list(System.out);
prop.list(new PrintStream("sysinfo.txt"));
}
}
最新最全的的java学习视频教程:http://pro.net.itcast.cn/View-22-1458.aspx