IO(Input Output)流
--->IO流用来处理设备之间的数据传输
--->java对数据的操作时通过流的方式
--->Java用于操作流的对象都在IO包中
--->流按操作数据分为2种:字节流和字符流
--->流按流向分:输入流,输出流
IO流常用基类
字节流的抽象基类
--->InputStream,OuputStream
字符流的抽象基类
--->Reader,Writer
字节流
主要操作byte类型数据,已byte数组为准,主要操作类为OutputStream类和InputStream类
字节输出流OuputStream 最基本的方法
write(int b):
将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。
write(byte[] b) 将 b.length 个字节从指定的 byte数组写入此输出流。write(b) 的常规协定是:应该与调用 write(b, 0, b.length) 的效果完全相同。
write(byte[] b,int off,int len) 将指定 byte数组中从偏移量 off 开始的 len 个字节写入此输出流。write(b,off, len) 的常规协定是:将数组 b 中的某些字节按顺序写入输出流;元素 b[off] 是此操作写入的第一个字节,b[off+len-1] 是此操作写入的最后一个字节。
数据最常见的体现形式是:文件。下面演示在硬盘上创建一个文件并且写入一些数据
向文件中写入字符串
将整个数组一次性写入文件
public class Demo{
public static void main(String args[]) throws Exception{ // 异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= newFile("d:" + File.separator + "test.txt"); // 声明File对象
// 第2步、通过子类实例化父类对象
OutputStream out = null; // 准备好一个输出的对象
out = newFileOutputStream(f) ; // 实例化
// 第3步、进行写操作
String str = "HelloWorld!!!" ; // 准备一个字符串
byte b[] =str.getBytes(); // 只能输出byte数组,所以将字符串变为byte数组
out.write(b); // 将内容输出,保存文件
// 第4步、关闭输出流
out.close(); // 关闭输出流
}
};
循环将每个字节写入
public class Demo{
public staticvoid main(String args[]) throws Exception{ //异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= new File("d:"+ File.separator + "test.txt") ; // 声明File对象
// 第2步、通过子类实例化父类对象
OutputStream out = null; // 准备好一个输出的对象
out = newFileOutputStream(f) ; // 通过对象多态性,进行实例化
// 第3步、进行写操作
String str = "HelloWorld!!!" ; // 准备一个字符串
byte b[] =str.getBytes(); // 只能输出byte数组,所以将字符串变为byte数组
for(inti=0;i<b.length;i++){ // 采用循环方式写入
out.write(b[i]) ; // 每次只写入一个内容
}
// 第4步、关闭输出流
out.close(); // 关闭输出流
}
};
字节输入流InputStream
字节流最基本的读取方法
int read():从输入流中读取数据的下一个字节。返回 0到 255 范围内的 int字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。
int read(byte[] b):
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中。以整数形式返回实际读取的字节数。在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。
如果 b的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b中。
read(byte[] b,int off,int len)
将输入流中最多 len个数据字节读入 byte数组。尝试读取 len个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。
从文件中读取内容
直接读取文件
public class Demo{
public static void main(String args[]) throwsException{ // 异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= newFile("d:" + File.separator + "test.txt"); // 声明File对象
// 第2步、通过子类实例化父类对象
InputStream input = null; // 准备好一个输入的对象
input = newFileInputStream(f) ; // 通过对象多态性,进行实例化
// 第3步、进行读操作
byte b[] = newbyte[1024] ; // 所有的内容都读到此数组之中
input.read(b); // 读取内容
// 第4步、关闭输出流
input.close(); // 关闭输出流
System.out.println("内容为:" + new String(b)); //把byte数组变为字符串输出,此时会有很多空格,因为文件大小没有1024个字节
}
};
按照文件大小读取
public class Demo{
public static void main(String args[]) throwsException{ // 异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= newFile("d:" + File.separator + "test.txt"); // 声明File对象
// 第2步、通过子类实例化父类对象
InputStream input = null; // 准备好一个输入的对象
input = newFileInputStream(f) ; // 通过对象多态性,进行实例化
// 第3步、进行读操作
byte b[] = newbyte[1024] ; // 所有的内容都读到此数组之中
int len = input.read(b); // 读取内容
// 第4步、关闭输出流
input.close(); // 关闭输出流\
System.out.println("读入数据的长度:" + len) ;
System.out.println("内容为:" + new String(b,0,len)); //把byte数组变为字符串输出,按照文件长度输出,不会有空格,但是依然开辟了1024个字节的空间,浪费了
}
};
根据文件的大小,来开辟对应的字节数组空间
public class Demo{
public static void main(String args[]) throwsException{ // 异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= newFile("d:" + File.separator + "test.txt"); // 声明File对象
// 第2步、通过子类实例化父类对象
InputStream input = null; // 准备好一个输入的对象
input = newFileInputStream(f) ; // 通过对象多态性,进行实例化
// 第3步、进行读操作
byte b[] = newbyte[(int)f.length()] ; // 数组大小由文件决定
int len = input.read(b); // 读取内容
// 第4步、关闭输出流
input.close(); // 关闭输出流\
System.out.println("读入数据的长度:" + len) ;
System.out.println("内容为:" + new String(b)); //把byte数组变为字符串输出
}
};
循环读取字节来输出
public class Demo{
public static void main(String args[]) throwsException{ // 异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= newFile("d:" + File.separator + "test.txt"); // 声明File对象
// 第2步、通过子类实例化父类对象
InputStream input = null; // 准备好一个输入的对象
input = newFileInputStream(f) ; // 通过对象多态性,进行实例化
// 第3步、进行读操作
byte b[] = newbyte[(int)f.length()] ; // 数组大小由文件决定
for(inti=0;i<b.length;i++){
b[i] = (byte)input.read(); // 读取内容
}
// 第4步、关闭输出流
input.close(); // 关闭输出流\
System.out.println("内容为:" + new String(b)); //把byte数组变为字符串输出
}
};
不知道文件大小,通过判断是否读取到文件末尾来读取文件
public class Demo{
public static void main(String args[]) throwsException{ // 异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= newFile("d:" + File.separator + "test.txt"); // 声明File对象
// 第2步、通过子类实例化父类对象
InputStream input = null; // 准备好一个输入的对象
input = newFileInputStream(f) ; // 通过对象多态性,进行实例化
// 第3步、进行读操作
byte b[] = newbyte[1024] ;
int len = 0 ;
int temp = 0; // 接收每一个读取进来的数据
while((temp=input.read())!=-1){
//返回 0到 255范围内的 int 字节值
b[len] = (byte)temp ;
len++ ;
}
// 第4步、关闭输出流
input.close(); // 关闭输出流\
System.out.println("内容为:" + new String(b,0,len)); //把byte数组变为字符串输出
}
};
PS:最标准的IO异常处理
public class Demo {
public static void main(String[] args) throws Exception {
//第1步、使用File类找到一个文件
Filef = new File("d:" + File.separator + "test.txt" ); //声明File对象
FileWriterfw = null ;//指定FW为全局引用
try {//IO操作都要处理异常
fw= new FileWriter(f);
fw.write( "abc" );
} catch (Exception e) {
e.printStackTrace();
} finally {//单独对 文件的关闭操作进行异常处理
try {
if (fw!=null ){//判断文件是否为空,如果进行判断,那么在下面执行关闭操作的时候,可能出现空指针异常
fw.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
小结最基本的读取方式
字节流
1 直接把字符串转成byte数组写入文件
String str = "hello world";
byte[] b = str.getBytes();
out.write(b);
2 通过循环把每个字节写入
String str = "hello world";
byte[] b = str.getBytes();
for(int i=0;i<b. length;i++){
out.write(b[i]);
}
3 读取文件,根据文件的数据量来选择开辟空间大小,这样最节省空间
byte[] b = new byte[(int)f.length()];
in.read(b);
4 循环一个一个字节读取文件(知道文件长度)
byte[] b = new byte[(int)f.length()];
for(int i=0;i<b.length;i++){
b[i] = (byte)in.read();
}
5 如果不知道具体要输入的内容有多大,则靠判断是否读到文件末尾的方式来读取文件
byte[] b = new byte[1024];
while((temp=in.read())!=-1){
//将每次读取的内容给 temp变量,如果不是-1,表示文件还没读完
//返回 0到 255范围内的 int 字节值
b[len] = (byte)temp;
len++;
}
字符流 最基本的操作方法
Writer
write(int c):写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16高位被忽略。
write(char[] cbuf):写入字符数组。
write(char[] cbuf,int off,int len) :写入字符数组的某一部分。
write(String str):写入字符串
字符输入流 Reader
用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int)和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
int read():读取单个字符,作为整数读取的字符,范围在0到 65535之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1
int read(char[]cbuf):将字符读入数组,读取的字符数个数,如果已到达流的末尾,则返回 -1
PS:如果读取字符个数的长度超过定义的char数组的长度,则读取时,指针会回到数组的开头,继续开始读取,将读取到的数组,从0下标开始,依次读入数组内
int read(char[]cbuf,int off,int len)将字符读入数组的某一部分。在某个输入可用、发生 I/O错误或者到达流的末尾前,此方法一直阻塞。
while((temp=reader.read())!=-1){
c[len] = (char)temp;
len++;
}
向文件中写入数据
public class Demo{
public static void main(String args[]) throwsException{ // 异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= newFile("d:" + File.separator + "test.txt"); // 声明File对象
// 第2步、通过子类实例化父类对象
Writer out = null; // 准备好一个输出的对象
out = newFileWriter(f) ; // 通过对象多态性,进行实例化
// 第3步、进行写操作
String str = "HelloWorld!!!" ; // 准备一个字符串
out.write(str); // 将内容输出,保存文件
// 第4步、关闭输出流
out.close(); // 关闭输出流
}
};
从文件中读取内容
public class Demo{
public static void main(String args[]) throwsException{ // 异常抛出,不处理
// 第1步、使用File类找到一个文件
File f= newFile("d:" + File.separator + "test.txt"); // 声明File对象
// 第2步、通过子类实例化父类对象
Reader input = null; // 准备好一个输入的对象
input = newFileReader(f) ; // 通过对象多态性,进行实例化
// 第3步、进行读操作
char c[] = newchar[1024] ; // 所有的内容都读到此数组之中
int len = input.read(c); // 读取内容
// 第4步、关闭输出流
input.close(); // 关闭输出流
System.out.println("内容为:" + new String(c,0,len)); //把字符数组变为字符串输出
}
};
练习实例:进行文件的拷贝
public class Demo {
public static void main(String[] args) throws Exception {
//创建一个要被拷复制的文件
FileReaderfr = new FileReader("d:" +File.separator +"test.txt" );
//创建一个复制的文件
FileWriterfw = new FileWriter("d:" +File.separator +"copytest.txt" );
//IO读取要处理异常
try {
char [] c = new char[1024];
int len = 0;
//没有读到文件末尾的时候
while ((len = fr.read(c))!=-1){
fw.write(c,0,len); //将读到的内容,,写入到 fw文件
}
} catch (Exception e){
e.printStackTrace();
} finally {// fw,fr流在最后都要进行关闭操作的异常处理
if (fr!=null ){
try {fr.close();}catch(Exception e){e.printStackTrace();};
}
if (fw!=null ){
try {fw.close();}catch(Exception e){e.printStackTrace();};
}
}
}
}
字节流与字符流的区别
字节流在操作时本身不会哟会给你到缓冲区,是文件本身直接操作的。而字符流在操作时使用了缓冲区,通过缓冲区再操作文件
转换流OutputStreamWriter和InputStreamReader
--->OutputStreamWriter:是Writer的子类,将输出的字符流变为字节流。
--->InputStreamReader:是Reader的子类,将输入的字节流变为字符流。
内存操作流
使用ByteArrayInputStream,ByteArryOutputStream来完成在内存的输入输出
public class Demo {
public static void main(String[] args) throws Exception {
Stringinfo = "helloworld";
InputStreaminput = new ByteArrayInputStream(info.getBytes());
OutputStreamoutput = new ByteArrayOutputStream();
int temp = 0;
while ((temp = input.read()) != -1) {
output.write(Character. toUpperCase((char) temp));
}
Stringstr = output.toString(); //取出内容
input.close();
output.close();
System. out.println(str);
}
}
结果:HELLOWORLD
打印流
在整个IO包中,打印流是输出信息最方便的类
字节打印流:PrintStream
字符打印流:PrintWriter
在PrintStream类中的构造方法中,可以直接接收OutputStream类的实例,这是因为与OutputStream类相比,PrintStream类可以更加方便的输出数据,就好像讲OutpuStream类重新包装了下,使之更加方便的输出
(装饰设计模式)
public class Demo {
public static void main(String[] args) throws Exception {
PrintStream out = new PrintStream( new FileOutputStream(new File("d:"
+File. separator + "test.txt")));
out.print(1+ " +" + 1 + " =" );
out.println(1+ 1);
out.println( "Hello World!!!") ;
}
}
System类对IO的支持
System.out 是PrintStream的对象,表示向显示器上输出,而PrintStream又是OutputStream的子类,所有可以直接利用此对象向屏幕上输出信息
System.err 表示的是错误信息的输出,如果程序出现错误,就可以直接使用System.err进行输出。
System.out和System.err都是PrintStream的实例化对象,一般来说,System.out是将信息显示给用户看,是正常的显示信息,而System.err是直接在后台打印,是专门显示错误的
System.in
System.in实际上是一个键盘的输入流,其本身是InputStream类型的对象。可以利用System.in完成从键盘读取数据的功能(不考虑可能出现的乱码情况,只是一个简单实例)
publicclass Demo{
public static void main(String args[]) throwsException { // 所有异常抛出
InputStream input =System.in ; // 从键盘接收数据
byte b[] = new byte[5]; // 开辟空间,接收数据
System.out.print("请输入内容:") ; //提示信息
int len = input.read(b); // 接收数据
System.out.println("输入的内容为:" + new String(b,0,len)) ;
input.close(); // 关闭输入流
}
};
输入/输出的重定向
通过System类可以改变System.in的输入流以及System.out和System.err2个输入路的位置
publicclass Demo{
public static void main(String args[]) throwsException {
System.setOut(
new PrintStream(
new FileOutputStream("d:" +
File.separator + "test.txt"))) ; //System.out输出重定向
System.out.print("hello world") ; // 输出时,不再向屏幕上输出
}
};
BufferedReader类
BufferedReader类用于从缓冲区读取内容,所有的读入字节数都放在缓冲区.
BufferedReader类中定义的构造方法只能接受字符输入流的实例,所以在使用时,必须使用字符输入流和字节输入流的转换类InputStreamReader将字节输入流System.in变为字符流。。
Ps:BufferedReader只能接受字符流的缓冲区,因为每一个中文要占两个字节,所以需要将System.in这个字节的输入流变为字符的输入流
键盘输入流的标准格式
将System.in变为字符流放入到BufferedReader后,可以通过readLine()方法等待用户输入信息
publicclass Demo {
public static void main(String[] args) throwsException{
BufferedReader buf = new BufferedReader(
newInputStreamReader(System.in));
String str = null;
System.out.println("请输入内容");
//处理可能出现的IO异常,写在一行,方便查看
try{str=buf.readLine();}catch(Exception e){e.printStackTrace();}
//打印信息
System.out.println(str);
}
}
Scanner类
此类不仅可以完成输入数据操作,也可以方便的对输入数据进行验证
PS:在Scanner类中提供了一个接受InputStream类型的构造方法,只要是字节输入流的子类都可以通过Scanner类进行方便的接收
publicclass Demo{
public static void main(String args[]){
Scanner scan = newScanner(System.in) ; // 从键盘接收数据
System.out.print("输入数据:") ;
String str = scan.next(); // 接收数据
System.out.println("输入的数据为:" + str) ;
}
};
数据操作流
在IO包中,有2个与平台无关的数据操作流,分别为数据输出流DataOutputStream和数据输入流DataInputStream。数据输出流会按照一定的格式将数据输出,再通过数据输入流按照一定的格式将数据输入,这样可以方便的对数据进行处理
publicclass DataOutputStreamDemo{
public static void main(String args[]) throwsException{ // 所有异常抛出
DataOutputStream dos =null; // 声明数据输出流对象
File f = newFile("d:" + File.separator + "test.txt") ; // 文件的保存路径
dos = newDataOutputStream(new FileOutputStream(f)) ; // 实例化数据输出流对象
String names[] = {"衣服","鞋子","围巾"} ; // 商品名称
float prices[] ={10.0f,10.0f,10.0f} ; // 商品价格
int nums[] = {1,1,1}; // 商品数量
for(inti=0;i<names.length;i++){ // 循环输出
dos.writeChars(names[i]) ; // 写入字符串
dos.writeChar('\t') ; // 写入分隔符
dos.writeFloat(prices[i]) ; // 写入价格
dos.writeChar('\t') ; // 写入分隔符
dos.writeInt(nums[i]) ; // 写入数量
dos.writeChar('\n') ; // 换行
}
dos.close(); // 关闭输出流
}
};
字符编码
在程序中处理不好字符的编码,就有可能出现乱码。要避免乱码,必须保证程序的编码与本地的默认编码一致。
要活的本地的默认编码,可以使用System类完成
publicclass Demo{
public static void main(String args[]){
System.out.println("系统默认编码:" +
System.getProperty("file.encoding")) ; // 获取当前系统编码
}
};
Ps:实现编码的转换,可以使用String类中的getBytes(String charset)方法设置编码
publicclass Demo{
public static void main(String args[]) throwsException {
File f = newFile("D:" + File.separator + "test.txt"); // 实例化File类
OutputStream out = newFileOutputStream(f) ; // 实例化输出流
byte b[] = "你好!".getBytes("ISO8859-1"); //转码操作
out.write(b); // 保存
out.close(); // 关闭
}
};
IO流小结:
1 在java中使用File类表示文件本身,可以直接使用此类完成文件的各种操作。
2RandomAccessFile类可以从指定的位置开始读取信息,但是要求文件中各个数据的保存长度必须固定
3 输入输出流主要分为字节流和字符流。在传输中以字节流操作比较多,字符流在操作时用到缓冲区,而字节流没有用到缓冲区U
4 IO包中是员工OutputStreamWriter和InputStreamReader完成字符流与字节流的转换
5 在IO中输出时最好使用打印流(PrintStream ,PrintWriter),这样可以更加方便的输入输出各种类型的数据
6 System类提供了三个支持IO操作的常量out,err,in
7BufferReader可以直接从缓冲区中读取数据
8 使用Scanner类可以方便的进入输入流操作
9 造成字符乱码的根本原因就在于程序编码与本地编码的不统一