IO流
1.概念
* IO流用来处理设备之间的数据传输 * Java对数据的操作是通过流的方式 * Java用于操作流的类都在IO包中 * 流按流向分为两种:输入流,输出流。 * 流按操作类型分为两种: * 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的 * 字符流 : 字符流只能操作纯字符数据,比较方便。
2.IO流常用父类
* 字节流的抽象父类:* InputStream * OutputStream* 字符流的抽象父类:
* Reader * Writer
3.IO程序书写
* 使用前,导入IO包中的类 * 使用时,进行IO异常处理 * 使用后,释放资源
类 InputStream
java.lang.Object ——java.io.InputStream 所有已实现的接口: Closeable
直接已知子类:
AudioInputStream, ByteArrayInputStream, FileInputStream,
InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream,
StringBufferInputStream
public abstract class InputStreamextends Objectimplements Closeable
此抽象类是表示字节输入流的所有类的超类。 需要定义 InputStream 子类的应用程序必须总是提供返回下一个输入字节的方法。
类 OutputStream
java.lang.Object ——java.io.OutputStream 所有已实现的接口: Closeable, Flushable
直接已知子类: ByteArrayOutputStream, FilterOutputStream, ObjectOutputStream, OutputStream, PipedOutputStream
public abstract class OutputStreamextends Objectimplements Closeable, Flushable
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。 需要定义 OutputStream 子类的应用程序必须始终提供至少一种可写入一个输出字节的方法。
类 Reader
java.lang.Object
java.io.Reader
所有已实现的接口:
Closeable, Readable
直接已知子类: BufferedReader, CharArrayReader, FilterReader, InputStreamReader, PipedReader, StringReader
public abstract class Reader extends Objectimplements Readable, Closeable
用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。 但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
类 Writer
java.lang.Object
——java.io.Writer
所有已实现的接口:
Closeable, Flushable, Appendable
直接已知子类:
BufferedWriter, CharArrayWriter, FilterWriter, OutputStreamWriter,
PipedWriter, PrintWriter, StringWriter
public abstract class Writerextends Object implements Appendable, Closeable, Flushable
写入字符流的抽象类。 子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。 但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
类 FileInputStream
java.lang.Object
——java.io.InputStream
————java.io.FileInputStream
所有已实现的接口:
Closeable
public class FileInputStream extends InputStream
从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。 FileInputStream 用于读取诸如图像数据之类的原始字节流。
构造方法
public FileInputStream(File file) throws FileNotFoundException
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
如果指定文件不存在,或者它是一个目录,而不是一个常规文件,抑或因为其他某些原因而无法打开进行读取,则抛出 FileNotFoundException
public FileInputStream(String name) throws FileNotFoundException
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
如果指定文件不存在,或者它是一个目录,而不是一个常规文件,抑或因为其他某些原因而无法打开进行读取,则抛出 FileNotFoundException
。
public int available() throws IOException
一次读取所有字节,并返回字节数
public int read() throws IOException
从此输入流中读取一个数据字节。如果没有输入可用,则此方法将阻塞。 指定者: 类 InputStream 中的 read 返回: 从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。;如果已到达文件末尾,则返回 -1。 抛出: IOException - 如果发生 I/O 错误。
public int read(byte[] b) throws IOException
从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 参数: b - 存储读取数据的缓冲区。 返回: 读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。
eg
public class b {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileInputStream fis = new FileInputStream("ccc.txt"); //创建流对象
int x = fis.read(); //从硬盘上读取一个字节
System.out.println(x);//a,97
int y = fis.read();
System.out.println(y);//98
int z = fis.read();
System.out.println(z);//99
int a = fis.read();
System.out.println(a);//100
int b = fis.read();
System.out.println(b);//-1
fis.close(); //关流释放资源
FileInputStream fistream = new FileInputStream("ccc.txt"); //创建流对象
int m;
while((m = fistream.read()) != -1) {
System.out.println(m);
}
fistream.close();
}
}
IO流(read()方法返回值为什么是int?
因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的, 如果每次读取都返回byte,有可能在读到中间的时候遇到111111111 那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了, 所以在读取的时候用int类型接收,如果11111111会在其前面补上 24个0凑足4个字节,那么byte类型的-1就变成int类型的255了 这样可以保证整个数据读完,而结束标记的-1就是int类型
一个字节byte是8个二进制位bit, 文件底层都是以二进制形式的存储的 00010100 00100100 01000001 11111111 0000000 10000001 byte类型-1的原码 11111110 -1的反码(高位不变,其余取反) 11111111 -1的补码 (高位不变,取反加1) 运算时都是用补码进行运算 如上若用byte则读取不能完成 若用int四个字节32位 byte类型-1的补码11111111为 00000000 00000000 00000000 11111111 每读一个字节前面补24个0
类 FileOutputStream
java.lang.Object ——java.io.OutputStream ————java.io.FileOutputStream 所有已实现的接口: Closeable, Flushable
FileOutputStream在创建对象的时候是如果没有这个文件会帮我创建出来
如果有这个文件就会先将文件清空
public void write(int b) throws IOException
将指定字节写入此文件输出流。实现 OutputStream
的 write
方法。
public void write(byte[] b) throws IOException
将 b.length 个字节从指定 byte 数组写入此文件输出流中。public void write(byte[] b,int off,int len) throws IOException
将指定 byte 数组中从偏移量 off(从数组的索引off处)开始的 len 个字节写入此文件输出流。
public class c {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("cc.txt");
//创建字节输出流对象,如果没有就自动创建一个
fos.write(97);
//虽然写出的是一个int数,但是到文件上的是一个字节,会自动去除前三个8位
fos.write(98);
fos.write(99);
fos.write(100);
fos.close();
FileOutputStream fostream = new FileOutputStream("cc.txt",true);
//如果想续写就在第二个参数传true
fostream.write(97);
fostream.write(98);
fostream.close();
}
}
读取文件然后打印的一个小问题
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("cc.txt");
byte[] arr = new byte[2];
int a = fis.read(arr);//将文件上的字节读取到字节数组中
System.out.println(a);//读到的有效字节个数,2
for (byte b : arr) {
System.out.println(b);//第一次获取到的是文件上的a和b,97,98
}
System.out.println("-----------------------");
int c = fis.read(arr);
System.out.println(c);//1
for (byte b : arr) {
System.out.println(b);//99,98
/*在arr数组里,第一次读到97,98
* arr[]={97,98}
* 第二次int c=fis.read(arr)
* 只读到99,arr[0]被覆盖
* 则arr[]={99,98}
* */
}
fis.close();
在文件后追加demo
public static void main(String[] args) throws IOException {
FileInputStream fis1 = new FileInputStream("cc.txt");
FileOutputStream fos1 = new FileOutputStream("cc1.txt");
byte[] arr1 = new byte[2];
int len;
while((len = fis1.read(arr1)) != -1) {
fos1.write(arr1,0,len);
//把arr1字节数组的0-len个字节写入fos1的末尾
}
fis1.close();
fos1.close();
文件拷贝1
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("h.jpg"); //创建输入流对象,h.jpg
FileOutputStream fos = new FileOutputStream("copy.jpg");//创建输出流对象,关联copy.jpg
int b;
while((b = fis.read()) != -1) { //在不断的读取每一个字节
fos.write(b); //将每一个字节写出
}
fis.close();//关流释放资源
fos.close();//ok+
文件拷贝2
public static void main(String[] args) throws IOException {
FileInputStream fis1 = new FileInputStream("Eight.mp3");
//创建输入流对象,关联Eight.mp3
FileOutputStream fos1 = new FileOutputStream("copy.mp3");
//创建输出流对象,关联copy.mp3
int b1;
while((b1 = fis1.read()) != -1) {//在不断的读取每一个字节
fos1.write(b1); //将每一个字节写出
}
System.out.print("ok,mp3 finish");
//开发中不推荐使用,效率太低,每次一个字节,读一次,写一次,花费双倍时间
//我们需要一个容器,一次拿更多的字节
fis1.close();//关流释放资源
fos1.close();
文件拷贝3
public class copy {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//这种种拷贝,不推荐使用,因为有可能会导致内存溢出
FileInputStream fis3 = new FileInputStream("Eight.mp3");
FileOutputStream fos3 = new FileOutputStream("copy1.mp3");
/*int len = fis3.available();
System.out.println(len);//17577392*/
byte[] arr = new byte[fis3.available()];//创建与文件一样大小的字节数组
//若字节数过大,可能会导致内存溢出
fis3.read(arr); //将文件上的字节读取到内存中
fos3.write(arr);//将字节数组中的字节数据写到文件上
fis3.close();//很快
fos3.close();
}
}
文件拷贝3(标准拷贝)
public class d {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//标准格式
FileInputStream fis2 = new FileInputStream("Eight.mp3");
FileOutputStream fos2 = new FileOutputStream("copyi.mp3");
byte[] arr2 = new byte[1024 * 8];//1024的整数倍
int len2;
while((len2 = fis2.read(arr2)) != -1) {
//如果忘记加arr,返回的就不是读取的字节个数,而是字节的码表值
fos2.write(arr2,0,len2);
}
fis2.close();
fos2.close();
}
}
BufferedInputStream和BufferOutputStream拷贝
A:缓冲思想 * 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多, * 这是加入了数组这样的缓冲区效果,java本身在设计的时候, * 也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流 B.BufferedInputStream * BufferedInputStream内置了一个缓冲区(数组) * 从BufferedInputStream中读取一个字节时 * BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个 * 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取 * 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个 C.BufferedOutputStream * BufferedOutputStream也内置了一个缓冲区(数组) * 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中 * 直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里 D.小数组的读写和带Buffered的读取哪个更快? * 定义小数组如果是8192个字节大小和Buffered比较的话 * 定义小数组会略胜一筹,因为读和写操作的是同一个数组 * 而Buffered操作的是两个数组
IO流flush和close方法的区别
* flush()方法
* 用来刷新缓冲区的,将缓冲区的字节全都写到到文件上,刷新后可以再次写出
* close()方法
* 用来关闭流释放资源的的,
* 如果是带缓冲区的流对象的close()方法,
* 不但会关闭流,还会再关闭流之前刷新一次缓冲区,将缓冲区的字节全都刷新到文件上,再关闭,
* close方法刷完之后就不能写了
在聊天软件开发中,我们常用的是flush()
public class e {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("Eight.mp3");
FileOutputStream fos = new FileOutputStream("copybs.mp3");
BufferedInputStream bis = new BufferedInputStream(fis);
//创建缓冲区对象,对输入流进行包装让其变得更加强大
BufferedOutputStream bos = new BufferedOutputStream(fos);
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
BufferedInputStream bis1 = new BufferedInputStream(new FileInputStream("Eight.mp3"));
BufferedOutputStream bos1 = new BufferedOutputStream(new FileOutputStream("copy_bs.mp3"));
int b1;
while((b1 = bis1.read()) != -1) {
bos1.write(b1);
}
bis1.close();
bos1.close();
}
}
字节流读写中文
* 字节流读取中文的问题
* 一个中文3个字节(UTF-8)[GBK,一个中文2个字节],字节流在读中文的时候有可能会读到半个中文,造成乱码
* 字节流写出中文的问题
* 字节流直接操作的是字节,所以写出中文必须将字符串转换成字节数组
* 写出回车换行 write("\r\n".getBytes());
public class f {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("yyy.txt");
byte[] arr = new byte[3];
int len;
while((len = fis.read(arr)) != -1) {
System.out.println(new String(arr,0,len));//纯中英文可以但加入任何符号就会出错,包括换行符
}
fis.close();
FileOutputStream fos = new FileOutputStream("zzz.txt");
fos.write("我读书少,你不要骗我".getBytes());
fos.write("\r\n".getBytes());
fos.write(",骗子".getBytes());
fos.close();//写完全可以
}
}
关于中文到流的读写有好多问题,有关编码平台~~
有次开发,别人在前台用gbk码给后台发中文,后台又是用utf-8开发和显示~就是一堆乱码
流的标准异常处理
jdk1.6及之前标准异常处理public class g {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
FileInputStream fis = null;//作用域问题
FileOutputStream fos = null;
//null,io局部变量有可能赋值失败,局部变量使用之前要初始化
//finally不能单独使用
//IO严谨逻辑,
try {
fis = new FileInputStream("xxx.txt");
fos = new FileOutputStream("yyy.txt");
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}finally {
try{
if(fis != null)
fis.close();
}finally { //try fianlly的嵌套目的是能关一个尽量关一个
if(fos != null)
fos.close();
}
}
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author fish
*创建时间2017-5-13下午4:44:43
*/
public class h {
/**1.7版本
* 在try()中创建的流对象必须实现了AutoCloseable这个接口,
* 如果实现了,在try后面的{}(读写代码)执行后就会自动调用,流对象的close方法将流关掉
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
try(//流对象写在try()后{}运行完后,流对象自动关闭
FileInputStream fis = new FileInputStream("ccc.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
MyClose mc = new MyClose();
){
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}
}
}
//AutoCloseable1.7版本,自动关闭,所有的流都实现了这个接口
class MyClose implements AutoCloseable {
@Override
public void close() throws Exception {
// TODO Auto-generated method stub
System.out.print("我关了");
}
}
IO流的加密与解密
一个数异或另一个数两次就是它本身
public class i {
/**
* 一个数异或另一个数两次就是它本身
* 将写出的字节异或上一个数,这个数就是密钥,解密的时候再次异或就可以了
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
/**
* 一个数异或另一个数两次就是它本身
* int num = 8;
*System.out.println(num ^ 3);//11
*System.out.println((num ^ 3) ^ 3);//8
**/
/*
* 加密
* */
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("copy.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("addcopy.jpg"));
int b;
while((b = bis.read()) != -1) {
//bos.write(b);
bos.write(b ^ 123);
}
bis.close();
bos.close();
/*
* 解密
* */
BufferedInputStream bis1 = new BufferedInputStream(new FileInputStream("addcopy.jpg"));
BufferedOutputStream bos1 = new BufferedOutputStream(new FileOutputStream("se_addcopy.jpg"));
int b1;
while((b1 = bis1.read()) != -1) {
bos1.write(b1 ^ 123);
}
bis1.close();
bos1.close();
}
}