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:
/*
缓冲区的出现是为了提高流的操作效率而出现的
所以在创建缓冲区之前,必须要有流对象
*/
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符流写入流对象
FileWriter fw = new FileWriter("buf.txt");
//为了提高字符写入流效率,加入了缓冲技术
//只需要将需要被操作的流对象作为参数传递给缓冲区的构造函数即可
BufferedWriter bufw = new BufferedWriter(fw);
for (int x=1; x < 5; x++)
{
bufw.write("abcde"+x);
bufw.newLine();
bufw.flush();
}
//换行符加入,跨平台不必考虑平台
//记住,只要用到缓冲区,就要刷新
//其实关闭缓冲区,就是在关闭缓冲区中的流对象 缓存区在内存里
//bufw.close();
fw.close();
}
}
字符写入流缓冲区
|--->OutputStreamWriter:
字符通向字节的转换流(涉及键盘录入时用)
|--->OutputStreamWriter:
打印流,可处理各种类型的数据
输入字符流:Reader: 字符读取流的抽象类
|--->FileReader:
字符读取流
|--->LineNumberReader:
跟踪行号的缓冲字符读取流
|--->BufferedReader:
字符读取流缓冲区
/*
字符流读取流缓冲区:
该缓冲区提供了一个一次读一行的方法readLine,方便于对文本数据的读取
*/
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("buf.txt"); //读取流对象
BufferedReader bufr = new BufferedReader(fr); //为提高效率,加入缓冲技术
String line = null;
while ((line = bufr.readLine()) != null)
{
System.out.println(line);
}
//String s1 = bufr.readLine();
//System.out.println("s1="+s1);
//bufr.close();
}
}
/*
明白了BufferedReader类中特有方法readLine方法原理后
可以自定义一个类中包含一个功能和readLine一致的方法
来模拟一下BufferedReader
*/
import java.io.*;
class MyBufferedReader//装饰设计模式,是对FileReader对象功能的增强
{
private FileReader r;
MyBufferedReader(FileReader r)
{
this.r = r;
}
//可以一次读一行的数据的方法
public String myReadLine() throws IOException
{
//定义一个临时容器,原BufferedReader中封装的是一个字符数组
//为了演示方便,定义一个StringBuilder容器,因为最终还要将数据变成字符串
StringBuilder sb = new StringBuilder();
int ch = 0;
while ((ch = r.read()) != -1)
{
if(ch=='\r')
continue;
if(ch == '\n')
return sb.toString();
else
sb.append((char)ch);
}
if((sb.length()) != 0)
return sb.toString();
return null;
}
public void MyClose() throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("123.txt");
MyBufferedReader bufr = new MyBufferedReader(fr);
String line = null;
while ((line = bufr.myReadLine()) != null)
{
System.out.println(line);
}
bufr.MyClose();
}
}
|--->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 方法的常规协定。
import java.io.*;
class FileStream
{
public static void main(String[] args) throws IOException
{
readFile3();
}
public static void writeFile() throws IOException
{
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("abcde".getBytes());
//字节流不需要刷新,因为是对字节操作的,不需要做任何转换
fos.close();
}
public static void readFile() throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
/*int ch = 0;
while ((ch = fis.read()) != -1)
{
System.out.println((char)ch);
}
fis.close();
*/
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 readFile3() throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
//int num = fis.available();
byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区,不用再循环了
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}
}
流操作的规律
IO流转换流的字符编码
(1)字符流的出现为了方便操作字符,更重要的是加入了编码转换
(2)通过子类转换流来完成
InputStreamReander
OutputStreamWriter
(3)在两个子类对象进行构造的时候可以加入编码表
(4)编码表:
将各个国家的文字用二进制数字表示并一一对应,形成一张表,这就是编码表
(5)常见的编码表:
**ASCII:美国标准信息交换码,用一个字节的七位表示
**ISO8859-1:拉丁码表,欧洲码表,用一个字节的八位表示
**GB2312:中文编码表,用两个字节表示
**GBK:中文编码表升级,融合录入更多的中文字符,用两个字节表示,为避免和老美重复
两字节的最高位都是1,即汉字都是用负数表示
**Unicode:国际标准码,融合了多种文字,所有文字都用两个字节表示
**UTF-8:用一个字节到三个字节表示。
注:Unicode能识别中文,UTF-8也能识别中文,但两种编码表示一个汉字所用的字节数不同
Unicode用两个字节,UTF-8用三个字节,故涉及到编码转换。