黑马程序员-面向对象:IO
------- android培训、java培训、期待与您交流! ----------
java.io 的描述
?
通过数据流、序列化和文件系统提供系统输入和输出。
? ? ? ? (1)用来处理设备(硬盘,控制台,内存)间的数据。
(2)java中对数据的操作都是通过流的方式。
(3)java用于操作流的类都在io包中。
(4)按照流操作的数据的类型不同:分为字节流和字符流。字符流是为了方便中文的操作而来的。
(5)按照流的流向不同分为:输入流,输出流
?
?
?
流
?
流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。
?
?
?
Java流输入输出原理
?
Java把这些不同来源和目标的数据都统一抽象为数据流。Java语言的输入输出功能是十分强大而灵活的,美中不足的是看上去输入输出的代码并不是很简洁,因为你往往需要包装许多不同的对象。
在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流。
?
?
Java流的分类
按流向分:
输入流: 程序可以从中读取数据的流。
输出流: 程序能向其中写入数据的流。
按数据传输单位分:
字节流: 以字节为单位传输数据的流
字符流: 以字符为单位传输数据的流
按功能分:
节点流: 用于直接操作目标设备的流
过滤流: 是对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能。
?
?
java.io常用类
JDK所提供的所有流类位于java.io包中,都分别继承自以下四种抽象流类。
InputStream:继承自InputStream的流都是用于向程序中输入数据的,且数据单位都是字节(8位)。
OutputSteam:继承自OutputStream的流都是程序用于向外输出数据的,且数据单位都是字节(8位)。
(1)字节流
输出字节流:OutputStream:字节写入流抽象类
|--->FileOutputStream:
字节写入流
|--->BufferedOutputStream:
字节写入流缓冲区
|--->PrintStream:
打印流
输入字节流:InputStream:字节读取流抽象类
|--->FileInputStream:
字节读取流
|--->BufferedInputStream:
字节读取流缓冲区
(2)字符流
输出字符流:Writer:字符写入流的抽象
|--->FileWriter:
字符写入流
|--->BufferedWriter:
字符写入流缓冲区
|--->OutputStreamWriter:
字符通向字节的转换流(涉及键盘录入时用)
|--->OutputStreamWriter:
打印流,可处理各种类型的数据
输入字符流:Reader: 字符读取流的抽象类
|--->FileReader:
字符读取流
|--->LineNumberReader:
跟踪行号的缓冲字符读取流
|--->BufferedReader:
字符读取流缓冲区
|--->InputStreamReader:
字节通向字符的转换流(涉及键盘录入时用)
?
?
Reader:继承自Reader的流都是用于向程序中输入数据的,且数据单位都是字符(16位)。
Writer:继承自Writer的流都是程序用于向外输出数据的,且数据单位都是字符(16位)。
?
字符写入流:Writer:
abstract ?void close() 关闭此流,但要先刷新它。
abstract ?void flush() 刷新该流的缓冲。
void write(int c) 写入单个字符。
void write(char[] cbuf) 写入字符数组。 ? ? ? ? ?
abstract ?void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。?
void write(String str) 写入字符串。?
void write(String str, int off, int len) 写入字符串的某一部分。?
字符读取流:Reader:
abstract ?void close() 关闭该流并释放与之关联的所有资源。
int read() 读取单个字符。
int read(char[] cbuf) ?将字符读入数组
abstract ?int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。
long skip(long n) ?跳过字符。?
?
?
?
IO流常用字节流基类的子类:
**写入流:
(1)FileOutputStream:
**构造方法:
FileOutputStream(String name)?
创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name, boolean append)?
创建一个向具有指定 name 的文件中写入数据的输出文件流。
FileOutputStream(File file)?
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。?
FileOutputStream(File file, boolean append)?
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
**方法摘要:
public void flush()
void close() 关闭此文件输出流并释放与此流有关的所有系统资源。
void write(int b) 将指定字节写入此文件输出流。
void write(byte[] b, int off, int len)?
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。?
void write(int b) 将指定字节写入此文件输出流。
(2)BufferedOutputStream:
**构造方法:
BufferedOutputStream(OutputStream out)?
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。?
BufferedOutputStream(OutputStream out, int size)?
创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。?
**方法摘要:
void flush() 刷新此缓冲的输出流。 ? ? ? ? ?
void write(byte[] b, int off, int len)?
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。?
void write(int b) 将指定的字节写入此缓冲的输出流。
(3)PrintStream:打印流,可将各种类型的数据原样打印,有自动刷新功能
**构造方法:
PrintStream(String fileName)?
创建具有指定文件名称且不带自动行刷新的新打印流。
PrintStream(File file)?
创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(OutputStream out)?
创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush) (当autoFlush为true时具有自动刷新功能)
创建新的打印流。
**方法摘要:
PrintStream append(char c)?
将指定字符添加到此输出流。
void close()?
关闭流。?
void flush()?
刷新该流的缓冲。
void print(各种类型的数据:)?
打印各种类型的数据?
void println(各种类型的数据:):自动换行
打印各种类型的数据?
void write(byte[] buf, int off, int len)?
将 len 字节从指定的初始偏移量为 off 的 byte 数组写入此流。?
void write(int b)?
将指定的字节写入此流。?
?
**读取流:
(1)FileInputStream:
**构造方法:
FileInputStream(String name)?
通过打开一个到实际文件的连接来创建一个 FileInputStream,
该文件通过文件系统中的路径名 name 指定。
FileInputStream(File file)?
通过打开一个到实际文件的连接来创建一个 FileInputStream,
该文件通过文件系统中的 File 对象 file 指定。
**方法摘要:
int available() (字节读取流特有方法!!!)
返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。
int read()?
从此输入流中读取一个数据字节。?
int read(byte[] b)?
从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。?
int read(byte[] b, int off, int len)?
从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。?
long skip(long n)?
从输入流中跳过并丢弃 n 个字节的数据。?
(2)BufferedInputStream:
**构造方法:
BufferedInputStream(InputStream in)?
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。?
BufferedInputStream(InputStream in, int size)?
创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。?
**方法摘要:
int available() (字节读取流特有方法!!!)
返回可以从此输入流读取(或跳过)、且不受此输入流接下来的方法调用阻塞的估计字节数。?
int read()?
参见 InputStream 的 read 方法的常规协定。?
int read(byte[] b, int off, int len)?
从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。?
long skip(long n)?
参见 InputStream 的 skip 方法的常规协定。
?
?
?
IO流转换流的字符编码
(1)字符流的出现为了方便操作字符,更重要的是加入了编码转换
(2)通过子类转换流来完成
InputStreamReander
OutputStreamWriter
(3)在两个子类对象进行构造的时候可以加入编码表
(4)编码表:
将各个国家的文字用二进制数字表示并一一对应,形成一张表,这就是编码表
(5)常见的编码表:
**ASCII:美国标准信息交换码,用一个字节的七位表示
**ISO8859-1:拉丁码表,欧洲码表,用一个字节的八位表示
**GB2312:中文编码表,用两个字节表示
**GBK:中文编码表升级,融合录入更多的中文字符,用两个字节表示,为避免和老美重复
? ? ? 两字节的最高位都是1,即汉字都是用负数表示
**Unicode:国际标准码,融合了多种文字,所有文字都用两个字节表示
**UTF-8:用一个字节到三个字节表示。
注:Unicode能识别中文,UTF-8也能识别中文,但两种编码表示一个汉字所用的字节数不同
Unicode用两个字节,UTF-8用三个字节,故涉及到编码转换。
(6)在流中涉及编码表的转换只有转换流:
InputStreamReander
OutputStreamWriter
(7)代码示例:
public static void write() throws IOException
{
OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");
osw1.write("你好");
osw1.close();
?
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"UTF-8");
osw2.write("你好");
osw2.close();
}
public static void read() throws IOException
{
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");
byte[] buf = new byte[1024];
int len = isr.read(buf);
sop(new String(buf,0,len));
}
(8)编码解码
编码:字符串变成字节数组:String-->getBytes()-->byte[]()
解码:字节数组变成字符串:byte[]-->new String(byte[],0,len)-->String
(9)代码示例:
public static void main(String[] args)
{
//编码解码1:默认编码
String str1 = "你好";
byte[] buf1 = str1.getBytes();//默认解码:Unicode,四个字节
?
//编码解码2:指定编码
String str2 = "你好";
byte[] buf2 = str2.getBytes("UTF-8");//指定解码:UTF-8,六个字节
?
?
//编码解码3:编码正确解码错误
String str3 = "你好";
byte[] buf3 = str3.getBytes("GBK");//指定编码:GBK,四个字节
String str3 = new String(buf3,"ISO8859-1");//错误解码
?
//编码解码4:错误编码正确解码
String str4 = "你好";
byte[] buf4 = str4.getBytes("ISO8859-1");//错误编码
String str4 = new String(buf4,"GBK");//正确解码,读不出来
?
//编码解码5:编码对了,但是解码错误了,怎么办呢?
//此时可以将错误的解码再错编回去,载用正确编码解码
String str5 = "你好";
byte[] buf5 = str5.getBytes("GBK");//正确编码
String str6 = new String(buf5,"ISO8859-1");//错误解码,读不出来
byte[] buf6 = str6.getBytes("ISO8859-1");//再错误编码
String str7 = new String(buf6,"GBK");//再正确解码,这样就可以读出来了