------- android培训、java培训、期待与您交流! ----------
IO 字节流
字节流两个超类:InputStream OutputStream(写)
一、
InputStream --->字节输入流 (读)
public abstract class InputStream
extends Object
implements Closeable
此抽象类是表示字节输入流的所有类的超类。
需要定义 InputStream 子类的应用程序必须总是提供返回下一个输入字节的方法。
OutputStream --->输出字节流(写)
public abstract class OutputStream
extends Object
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。
需要定义 OutputStream 子类的应用程序必须始终提供至少一种可写入一个输出字节的方法。
二、具体操作示例
1、补充:掌握类 FileInputStream 的available()方法
FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
int
available()
返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。
示例:import java.io.*;class FileStream{public static void main(String[] args) throws IOException{readFile_3();}public static void readFile_3()throws IOException{FileInputStream fis = new FileInputStream("fos.txt");byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用在循环了。数据量太大不好用
fis.read(buf);System.out.println(new String(buf));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_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 writeFile()throws IOException{FileOutputStream fos = new FileOutputStream("fos.txt");fos.write("abcde".getBytes());fos.close();}}
2、需求,想要操作图片数据,复制一个图片,这时就要用到字节流。
复制一个图片思路: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("写入关闭失败");
}}}}
3、演示mp3的复制。通过缓冲区。
BufferedOutputStreamBufferedInputStreamfileinputstream中read()存在数组然后用bufferedinputstream 得 read ()在缓冲区一个一个往外取
(自定义字节流的缓冲区-read和write的特点).
IO流自定义字节流的缓冲区:思路:BufferedInputStream类中read()方法的工作原理
1)先一个一个从字节流中读取字节,读取一定量(自定义)之后,存储在一个字节数组(缓冲区)(FileInputStream.read(byte[] b)),并获得存储数量(read方法的返回值)。
2)一个一个字节返回,返回一个,存储数量减1,然后指针往后移一位,准备取下一个。
3)如果存储数量为0 ,代表当前数组中所有数据已经全部取完,此时再来一次读取(read(byte[] b)),再获得此次存储数量。
4)如果存储数量(即read方法返回-1),代表读到文件末尾,返回-1。
因此,需要用到以下几个变量:读取的字节数量,指向数组中准备取哪一个的指针,将要返回的字节变量。自定义的read方法实现如下:
import java.io.*;class MyBufferedInputStream{private InputStream in;private byte[] buf = new byte[1024*4];private int pos = 0,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;byte b = buf[pos];count--;pos++;return b&255;}else if(count>0){byte b = buf[pos];count--;pos++;return b&0xff;}return -1;}public void myClose()throws IOException{in.close();}}/*11111111-111111110000000000101001001010100101010010101001010byte: -1 ---> int : -1;00000000 00000000 00000000 11111111 25511111111 11111111 11111111 1111111111111111 -->提升了一个int类型 那不还是-1吗?是-1的原因是因为在8个1前面补的是1导致的。
那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。
怎么补0呢?
11111111 11111111 11111111 11111111&00000000 00000000 00000000 11111111------------------------------------00000000 00000000 00000000 111111110000-00011111-11100000000011111-1111 -1结论:字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。
因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.
那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。【 如果文件开头第一个字节就是1111 1111,则返回的直接就是-1(方法返回类型为int),这样就被当做是读到文件末尾而导致读取结束。因此要与上255,即(00000000 00000000 00000000 11111111),这样的话,才保证原字节值没变,而且不被当做-1而导致读取结束。
】所以,为了避免这种情况将读到的字节进行int类型的提升。
并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。
而在写入数据时,只写该int类型数据的最低8位。
复制MP3示例:
import java.io.*;class CopyMp3{public static void main(String[] args) throws IOException{long start = System.currentTimeMillis();copy_2();long end = System.currentTimeMillis();System.out.println((end-start)+"毫秒");
}public static void copy_2()throws IOException{MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\9.mp3"));BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\3.mp3"));int by = 0;//System.out.println("第一个字节:"+bufis.myRead());
while((by=bufis.myRead())!=-1){bufos.write(by);}bufos.close();bufis.myClose();}//通过字节流的缓冲区完成复制。
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();}}
4、读取键盘录入。【必要掌握】
System.out:对应的是标准输出设备,控制台。
System.in:对应的标准输入设备:键盘。
需求:通过键盘录入数据。当录入一行数据后,就将该行数据进行打印。如果录入的数据是over,那么停止录入。
import java.io.*;class ReadIn{public static void main(String[] args) throws IOException{InputStream in = System.in;StringBuilder sb = new StringBuilder();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.delete(0,sb.length());}elsesb.append((char)ch);}}}
5、字节流转成字符流
通过键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。也就是readLine方法。
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法。
而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流在使用字符流缓冲去的readLine方法呢?
1)
public class InputStreamReader
extends Reader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:BufferedReader in= new BufferedReader(new InputStreamReader(System.in))
2)
public class OutputStreamWriter
extends Writer
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器
示例:import java.io.*;class TransStreamDemo{public static void main(String[] args) throws IOException{//获取键盘录入对象。
//InputStream in = System.in;//将字节流对象转成字符流对象,使用转换流。InputStreamReader
//InputStreamReader isr = new InputStreamReader(in);//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
//BufferedReader bufr = new BufferedReader(isr);//键盘的最常见写法。
BufferedReader bufr =new BufferedReader(new InputStreamReader(System.in));// OutputStream out = System.out;// OutputStreamWriter osw = new OutputStreamWriter(out);// BufferedWriter bufw = new BufferedWriter(osw);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();}