概念:
- IO流是一个有序的有开始结尾的字节数组,它是数据的传输过程。
- 我们知道机器存储数据的格式是二进制,而二进制以一个8bit为一个数据单元,也就是一个字节,所以字节才是计算机识别的最小单位
- 数据的传输过程中是以字节来进行传输的,包括所有种类的数据(视频,图片,文本等);而文本这种数据是我们人也需要看的,为了传输我们的文字就需要一种翻译了,通过字符集编码来翻译:
A:我们已经知道一个字节1byte=8bit,而这个bit是0或者1的二进制数,8bit就是8个0或者1构成,我们人识别的是十进制,这就引出了计算机到我们人的一种翻译基本了:可以通过二进制转换成十进制数来进行翻译
B:我们的字符集是把我们的字母符号文字等编在一张表里面,每个字符对应一个十进制数,这样存储的时候把这个十进制数翻译成二进制数就可以了,相印的这些二进制转换成十进制然后对照编码表就可以得到我们的字符
C:而我们上面说过1byte=8bit,那么一个字节最大能够存多大的十进制数呢?就是2的8-1次幂然后减1也就是0~127个,这里我么有算负数,负数值为-128;码表一般用正数,世上需要人类记录的字符何止千万,这么少的正数最开始也就只能记录下字母和符号了,也就是最初的ASCII编码表,它一直编到了数值为127,已经到达了1个字节的最大限度;而需要加入其它字符的话,这个编码表不够用,那么各个国家就另外编写编码表,这里我们举个例子:比如中国要把一个鸍字编到编码表中,它对应的十进制为65577,而这个65577很明显已经超过了1个字节,也超过了2个字节,它是3个字节里面的值,所以这个字符占了3个字节,而很不幸的是另外一张编码表中把这个字对应的十进制改为了65511,它只占2个字节;这里就引出了一个字符所占字节数量的问题:需要根据编码表来确定,有可能1个有可能2~4个
D:这里补充下bit和byte计算十进制数的算法:比如int占2个字节:2bytex8bit=16,那么就是2的16-1次幂-1,就是整数部分了,最大值为65535;java把基本类型所占字节都固定了,除了char字符,因为它要根据编码表确认该字符所占字节数
总结一下:字符集编码就相当于一本字典,编译的中转物是十进制数
IO流:
上面介绍了数据的传输在电脑中其实是以字节为单位进行的,java中将IO流根据操作对象不同分为了字节流和字符流
字节流:InputStream,OutputStream
字符流:Reader,Writer
java单独提一个字符流就是为了操作我上面说的人类文字,是给人看的,所以如果要把字节的东西转换成字符东西,就需要用到编码表:比如下面的string字符串和byte字节数组间的相互转换
public class StringByteTrans {
public static void main(String[] args) {
String str="中";
try {
//字符转成字节,采用编码表,不同的编码表如果有这个字符,那么字节数可能不一样,如下:
//这里会有UnsupportedEncodingException,是编码表不支持
byte[] bytes1 = str.getBytes("UTF-8");
System.out.println(bytes1.length);//3
byte[] bytes2 = str.getBytes("GBK");
System.out.println(bytes2.length);//2
//字节数组转字符,编码不一致会乱码
String str2=new String(bytes2,0,2,"UTF-8");
System.out.println(str2);//乱码
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
----------------------result-------------------
3
2
��
字符流:平常我们操作最多的应该就是处理文件的传输了吧!
提到字符流首先要想到的是,肯定是有编码的一个转换过程,从而引出了转换流:OutputStreamWriter和InputStreamReader
我们来看看他们是怎么转换的:
可以看出OutputStreamWriter需要一个OutputStream类,并且需要指定字符编码
而最终获取到的是一个StreamEncode对象,这个对象才是操作数据的关键,它根据传入的字符编码来对字节数组进行操作或者每次读取一个字符(不是字节返回的是字符对应编码的十进制数值),从而达到操作文本的目的,如果不传入编码,它会是使用默认的编码,看例子:
public class IOTrans {
public static void main(String[] args) {
BufferedOutputStream bufferedOutputStream = null;
OutputStreamWriter outputStreamWriter = null;
try {
//字节流的装饰类bufferedOutputStream
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("tt.txt"));
String str = "nihao\n";
byte[] bytes = str.getBytes(Charset.forName("UTF-8"));
bufferedOutputStream.write(bytes, 0, bytes.length);//只能操作字节数组
//只要是字节流的子类都可以
outputStreamWriter = new OutputStreamWriter(bufferedOutputStream,"UTF-8");
outputStreamWriter.write("bagayalu");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (outputStreamWriter != null) {
outputStreamWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/***********************InputStreamReader操作的是字符***这里我没有处理异常了***************************/
InputStreamReader inputStreamReader=new InputStreamReader(new FileInputStream("tt.txt"));
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(new FileOutputStream("kk.txt"));
long num;
while((num=inputStreamReader.read())!=-1){
outputStreamWriter.write((char)num);//字符和十进制数可以相互转换
if(num=='\r'){//字符和十进制数是可以直接比较的
continue;
}
}
outputStreamWriter.close();
inputStreamReader.close();
}
}
我们日常操作文本数据的时候用得最多的是FileWriter和BufferedWriter以及他们对应的reader。
FileWriter只是OutputStreamWriter的子类,它多了一个功能就是可以在写数据的时候才用追加,而OutputStreamWriter没有这个选项,只能每次覆盖:
构造方法:
public FileWriter(String fileName, boolean append) throws IOException {
super(new FileOutputStream(fileName, append));
}
会调用父类的构造:
public OutputStreamWriter(OutputStream out) {
super(out);
try {
se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
我们可以看到本质是用了字节流FileOutputStream,然后将这个字节流对象给了StreamEncode对象,让steamencode可以看到来做翻译工作,但FileWriter不能指定编码只能使用默认的编码
而BufferedWriter是Writer的子类,是其它Writer子类的装饰类,创建它的时候,只需要传入其它类的对象即可:
/**
* Created by Administrator on 2018/8/25.
* 装饰设计模式:
* 当想要对已有对象的功能进行加强时,
* 可以定义类一个类,引入该对象,基于原有的功能进行丰富
* 那么这个定义的类就是装饰类
* 装饰模式不要给同系列下不同的子类都创建一个装饰器,而是也作为顶层父类的子类,然后引入另外一个子类对象来操作即可
*/
public class MyBufferedReader extends Reader {
private Reader fileReader;
public MyBufferedReader(Reader fileReader) {
this.fileReader = fileReader;
}
//添加新方法
public String myRead() throws IOException {
return null;
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
System.out.println("我在这里来加强一个这个功能哈,我要换上新衣服来工作");
return fileReader.read(cbuf, off, len);//原有的功能
}
@Override
public void close() throws IOException {
fileReader.close();
}
}
上面说了字符流,而字节流是操作字节数组的,下面我们仅仅通过一个复制视频的例子来举例说明一下即可:
public class ControlBytesIO {
public static void main(String[] args) {
//首先创建copy的输入流
InputStream inputStream = null;
//创建输出流
OutputStream outputStream=null;
try {
inputStream = new FileInputStream("G:\\Friends\\Friends Season\\Friends Season 5\\六人行第5季01.rmvb");
outputStream=new FileOutputStream("G:\\copy_friend.rmvb");
byte[] bytes=new byte[1024];//创建字节数组,1024为1kb就是1024个字节
int len;//创建copy的长度,每read一次,就会把新的字节放到bytes中然后返回获取的新的字节数量
while((len=inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//先关输出流
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭输入流
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
下面介绍一些偏门:使用系统的System.out和System.in,System.out和System.in默认是针对控制台的,但是我们可以是用System.setOut或者System.setIn方法改变它们的输入输出对象
public static void main(String[] args) throws IOException {
/**
* 首先需要说明的是System.in和System.out是字节流,并且是针对控制台的
* 需求:将控制台输入的字符,打印到文件a.txt中
* 1.分析需求首先用到了System.in和System.out
* 2.为了方便我们将System.in封装到字符缓冲流中,这里就需要用到转换流
* 3.然后需要改变System.out的输出对象为a.txt文件
*/
//首先改变输出对象为a.txt
System.setOut(new PrintStream("a.txt"));
//操作输入:
//1首先创建字符转换流InputStreamReader
InputStreamReader inputStreamReader=new InputStreamReader(System.in);
//2使用字符缓冲流进行操纵,它是一个装饰类,需要传入被装饰对象inputStreamReader
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
//操作输出:为了方便我们将System.out也放到字符缓冲流里面
//1.创建转换流OutputStreamWriter
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(System.out);
//2.创建缓冲输出流
BufferedWriter bufferedWriter=new BufferedWriter(outputStreamWriter);
String str=null;
while((str=bufferedReader.readLine())!=null){
if(str.equals("over")){
break;
}
bufferedWriter.write(str);
bufferedWriter.newLine();
}
bufferedReader.close();
inputStreamReader.close();
/********************************小例子:copy properties的内容***************************************/
System.setOut(new PrintStream("mm.txt"));
FileInputStream fileInputStream=new FileInputStream("a.txt");
Properties properties=new Properties();
properties.load(fileInputStream);
properties.list(System.out);
}
总结一下:
字节流:操作的是字节,BufferedInputStream和BufferedOutputStream是装饰类,需要字节流的子对象
InputStream-FileInputSteam-BufferedInputStream
OutputStream-FileOutputSteam-BufferedOutputStream
/**
* 1.BufferedInputStream是InputStream的装饰类,需要传入一个InputStream的子类对象,如下
* 2.FileInputStream和BufferedInputStream操作的都是字节码数组或者单个的字节byte
*/
BufferedInputStream bufferedInputStream1=new BufferedInputStream(System.in);
FileInputStream fileInputStream=new FileInputStream("KK.TXT");
BufferedInputStream bufferedInputStream2=new BufferedInputStream(fileInputStream);
byte[] bytes=new byte[1024];
fileInputStream.read(bytes);
bufferedInputStream1.read(bytes);
/**
* 1.BufferedOutputStream是InputStream的装饰类,需要传入一个InputStream的子类对象,如下
* 2.FileOutputStream和BufferedOutputStream操作的都是字节码数组或者单个的字节byte
*/
BufferedOutputStream bufferedOutputStream1=new BufferedOutputStream(System.out);
FileOutputStream fileOutputStream=new FileOutputStream("a.txt");
BufferedOutputStream bufferedOutputStream2=new BufferedOutputStream(fileOutputStream);
fileOutputStream.write(bytes);
bufferedOutputStream1.write(bytes);
字符流:操作的是字符数组或者字符串
Reader-FileReader-BufferedReader ------转换流:InputStreamReader
Writer-FileWriter-BufferedWriter -------转换流:OutputStreamWriter
/**
* 1.FileReader操作的是字符
* 2.BufferedReader是装饰类,需要Reader的子类,并且可以操作字符串
* 3.转换流InputStreamReader
*/
FileReader fileReader=new FileReader("a.txt");
char[] chars=new char[1024];
fileReader.read(chars);
BufferedReader bufferedReader=new BufferedReader(fileReader);
bufferedReader.readLine();
bufferedReader.read(chars);
BufferedReader bufferedReader1=new BufferedReader(new InputStreamReader(System.in));
/**
* 1.FileWriter可以操作字符串和字符
* 2.BufferedWriter是装饰类,需要Writer的子类,只是比FileWriter多了一个nerLine方法
* 3.转换流OutputStreamReader
*/
FileWriter fileWriter=new FileWriter("b.txt");
fileWriter.write(chars);
fileWriter.write("string");
BufferedWriter bufferedWriter=new BufferedWriter(fileWriter);
bufferedWriter.newLine();
bufferedWriter.write(chars);
bufferedWriter.write("string");
BufferedWriter bufferedWriter1=new BufferedWriter(new OutputStreamWriter(System.out));