Java-IO流

IO流
* 1.概念
* IO流用来处理设备之间的数据传输
* Java对数据的操作是通过流的方式
* Java用于操作流的类都在IO包中
* 流按流向分为两种:输入流,输出流。
* 流按操作类型分为两种:
* 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
* 字符流 : 字符流只能操作纯字符数据,比较方便。
* 2.IO流常用父类
* 字节流的抽象父类:
* InputStream
* OutputStream
* 字符流的抽象父类:
* Reader
* Writer
* 3.IO程序书写
* 使用前,导入IO包中的类
* 使用时,进行IO异常处理
* 使用后,释放资源
 
read()方法读取的是一个字节,为什么返回是int,而不是byte?
因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111
那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上
24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型
 
FileOutputStream fos = new FileOutputStream("yyy.txt"); //创建输出流对象,如果没有yyy.txt会自动创建。 fos.write(97); //虽然写出的是一个int数,但是在写出的时候会将前面的24个0去掉,所以写出的是一个byte
 
FileOutputStream在创建对象的时候,如果没有这个文件会帮我们创建出来,如果有这个文件就会先将这个文件清空。如果想续写,就在第二个参数传true,表示追加。
FileOutputStream fos = new FileOutputStream("yyy.txt",true);
 
 
io流读写文件(定义小数组的标准格式)
FileInputStream fis = new FileInputStream("2019年08月05日D-112.wmv"); FileOutputStream fos = new FileOutputStream("copy_D.wmv"); byte[] b = new byte[1024*16]; int len; //如果忘记加b,那么返回的就不是读取的字节个数,而是字节的码表值 while((len=fis.read(b))!=-1) { fos.write(b,0,len); } fis.close(); fos.close();
 
 
BufferedInputStream和BufferedOutputStream拷贝
* A:缓冲思想
* 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,
* 这是加入了数组这样的缓冲区效果,java本身在设计的时候,
* 也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流
 
* B.BufferedInputStream
* BufferedInputStream内置了一个缓冲区(数组)
* 从BufferedInputStream中读取一个字节时
* BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个
* 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取
* 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个
 
* C.BufferedOutputStream
* BufferedOutputStream也内置了一个缓冲区(数组)
* 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中
* 直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里
 
 
//创建缓冲区对文件输入流对象(关联019年08月05日D-112.wmv)装饰 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("2019年08月05日D-112.wmv")); //创建缓冲区对输出流对象(关联copy_D-112.wmv)装饰 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy_D-112.wmv")); int len; while((len=bis.read())!=-1) { bos.write(len); } //只关装饰后的对象即可 bis.close(); bos.close();
 
 
小数组的读写和带Buffered的读取哪个更快?
* 定义小数组如果是8192个字节大小和Buffered比较的话
* 定义小数组会略胜一筹,因为读和写操作的是同一个数组
* 而Buffered操作的是两个数组
 
flush和close方法的区别
flush()方法
用来刷新缓冲区的,刷新后可以再次写出
close()方法
用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出
 
字节流读写中文
* 字节流读取中文的问题
* 字节流在读中文的时候有可能会读到半个中文,造成乱码
* 字节流写出中文的问题
* 字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组
* 写出回车换行 write("\r\n".getBytes());
 
 
流的标准处理异常代码1.7版本
try( FileInputStream fis = new FileInputStream("aaa.txt"); FileOutputStream fos = new FileOutputStream("bbb.txt"); MyClose mc = new MyClose(); ){ int b; while((b = fis.read()) != -1) { fos.write(b); } }
 
原理:在try()中创建的流对象必须实现了AutoCloseable这个接口,如果实现了,在try后面的{}(读写代码)执行后就会自动调用,流对象的close方法将流关掉。
 
给图片加密解密:给写出的字符异或上一个数,这个数就是密钥,解密的时候再次异或就可以了。
//给图片加密 public static void demo1() throws FileNotFoundException, IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream("timg.jpg")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy_timg.jpg")); int b; while((b=bis.read())!=-1) { bos.write(b^123); } bis.close(); bos.close(); } //给图片解密 public static void demo2() throws FileNotFoundException, IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream("copy_timg.jpg")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy_timg2.jpg")); int b; while((b=bis.read())!=-1) { bos.write(b^123); } bis.close(); bos.close(); }
 
 
 
字符流FileReader
字符流是什么
* 字符流是可以直接读写字符的IO流
* 字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
 
字符流与字节流拷贝文件流程图:
 
什么情况下使用字符流?
  • 字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
  • 程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流
  • 读取的时候是按照字符的大小读取的,不会出现半个中文
  • 写出的时候可以直接将字符串写出,不用转换为字节数组
 
字符流是否可以拷贝非纯文本的文件?
  • 不可以拷贝非纯文本的文件
  • 因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去
  • 如果是?,直接写出,这样写出之后的文件就乱了,看不了了
 
带缓冲的字符流
  • BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率
  • BufferedWriter的write()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率
BufferedReader br = new BufferedReader(new FileReader("aaa.txt")); //创建字符输入流对象,关联aaa.txt BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt")); //创建字符输出流对象,关联bbb.txt int ch; while((ch = br.read()) != -1) { //read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量ch bw.write(ch); //write一次,是将数据装到字符数组,装满后再一起写出去 } br.close(); //关流 bw.close();
 
readLine()和newLine()方法
  • BufferedReader的readLine()方法可以读取一行字符(不包含换行符号)
  • BufferedWriter的newLine()可以输出一个跨平台的换行符号"\r\n"
newLine()与\r\n的区别:
newLine()是跨平台的方法
\r\n只支持windows系统
BufferedReader br = new BufferedReader(new FileReader("yyy.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("zzz.txt")); String len; while((len=br.readLine())!=null) { bw.write(len); bw.newLine(); } br.close(); bw.close();
 
 
将文本反转
public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("yyy.txt")); String line; ArrayList<String> list = new ArrayList<>(); BufferedWriter bw = new BufferedWriter(new FileWriter("zzz.txt")); while((line=br.readLine())!=null) { list.add(line); } br.close(); for(int i=list.size()-1;i>=0;i--) { bw.write(list.get(i)); bw.newLine(); } bw.close(); }
 
 
LineNumberReader
LineNumberReader是BufferedReader的子类, 具有相同的功能, 并且可以统计行号
* 调用getLineNumber()方法可以获取当前行号
* 调用setLineNumber()方法可以设置当前行号
public static void main(String[] args) throws IOException { LineNumberReader lnr = new LineNumberReader(new FileReader("yyy.txt")); String line; while((line=lnr.readLine())!=null) { System.out.println(lnr.getLineNumber()+":"+line); } lnr.close(); }
 
 
装饰设计模式
/* * 装饰设计模式的好处是耦合性不强,被装饰类的变化与装饰类的变化无关 */ public class Demo4_Wrap { public static void main(String[] args) { EpointStudent es = new EpointStudent(new Student()); es.code(); } } interface Inter{ public void code(); } class Student implements Inter{ @Override public void code() { System.out.println("javaee"); System.out.println("javase"); } } class EpointStudent implements Inter{ //获取被装饰类的引用 private Student s; public EpointStudent() {} //在构造方法中传入被装饰类的对象 public EpointStudent(Student s) { this.s=s; } //对原有的功能进行升级 @Override public void code() { s.code(); System.out.println("数据库"); System.out.println("ssh"); System.out.println("大数据"); System.out.println("..."); } }
 
 
使用指定的码表读写字符
  1. FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)
  2. FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)
public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("yyy.txt"),"utf-8")); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("zzz.txt"),"gbk")); int len; while((len=br.read())!=-1) { bw.write(len); } br.close(); bw.close(); }
 
 
序列流
可以把多个字节输入流整合成一个, 从序列流中读取数据时,将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推.
//整合多个字节输入流: SequenceInputStream(Enumeration) Vector<InputStream> v = new Vector<>(); v.add(new FileInputStream("a.txt")); v.add(new FileInputStream("b.txt")); v.add(new FileInputStream("c.txt")); Enumeration<InputStream> en = v.elements(); SequenceInputStream sis = new SequenceInputStream(en); int b; FileOutputStream fos = new FileOutputStream("d.txt"); while((b=sis.read())!=-1) { fos.write(b); } fos.close(); sis.close();
 
内存输出流
什么是内存输出流
该输出流可以向内存中写数据, 把内存当作一个缓冲区, 写出之后可以一次性获取出所有数据。
 
使用方式
* 创建对象: new ByteArrayOutputStream()
* 写出数据: write(int), write(byte[])
* 获取数据: toByteArray()
public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("c.txt"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] b = new byte[5]; int len; while((len=fis.read(b))!=-1) { baos.write(b,0,len); //System.out.println(new String(b)); //乱码 } fis.close(); System.out.println(baos); baos.close(); }
 
随机访问流概述和读写数据
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。
* 支持对随机访问文件的读取和写入。
read(),write(),seek()
 
 
数据输入输出流
DataInputStream, DataOutputStream可以按照基本数据类型大小读写数据
* 例如按Long大小写出一个数字, 写出时该数据占8字节. 读取的时候也可以按照Long类型读取, 一次读取8个字节.
* DataOutputStream(OutputStream), writeInt(), writeLong() DataOutputStream dos = new DataOutputStream(new FileOutputStream("b.txt")); dos.writeInt(997); dos.writeInt(998); dos.writeInt(999); dos.close(); * DataInputStream(InputStream), readInt(), readLong() DataInputStream dis = new DataInputStream(new FileInputStream("b.txt")); int x = dis.readInt(); int y = dis.readInt(); int z = dis.readInt(); System.out.println(x); System.out.println(y); System.out.println(z); dis.close();
 
 
打印流的概述和特点(掌握)
该流可以很方便的将对象的toString()结果输出, 并且自动加上换行, 而且可以使用自动刷出的模式
* System.out就是一个PrintStream, 其默认向控制台输出信息
 
使用方式
* 打印: print(), println()
* 自动刷出: PrintWriter(OutputStream out, boolean autoFlush, String encoding)
* 打印流只操作数据目的
 
 
 
标准输入输出流概述和输出语句
* 1.什么是标准输入输出流(掌握)
* System.in是InputStream, 标准输入流, 默认可以从键盘输入读取字节数据
* System.out是PrintStream, 标准输出流, 默认可以向Console中输出字符和字节数据
* 2.修改标准输入输出流(了解)
* 修改输入流: System.setIn(InputStream)
* 修改输出流: System.setOut(PrintStream)
System.setIn(new FileInputStream("a.txt")); //修改标准输入流 System.setOut(new PrintStream("b.txt")); //修改标准输出流 InputStream in = System.in; //获取标准输入流 PrintStream ps = System.out; //获取标准输出流 int b; while((b = in.read()) != -1) { //从a.txt上读取数据 ps.write(b); //将数据写到b.txt上 } in.close(); ps.close();
 
PrintStream和PrintWriter分别打印的是字节流和字符流,只操作数据目的的
PrintStream ps = System.out; ps.println(97); //其实底层用的是Integer.toString(x),将x转换为数字字符串打印 ps.println("xxx"); ps.println(new Person("张三", 23)); Person p = null; ps.println(p); //如果是null,就返回null,如果不是null,就调用对象的toString()
 
PrintWriter pw = new PrintWriter(new FileOutputStream("g.txt"), true); pw.write(97); pw.print("大家好"); pw.println("你好"); //自动刷出,只针对的是println方法
 
Properties的load()和store()功能
Properties p = new Properties(); System.out.println("读取前:"+p); p.load(new FileInputStream("xxx.properties")); System.out.println("读取后:"+p); p.setProperty("tel", "88888888888"); p.store(new FileOutputStream("xxx.properties"), null); System.out.println(p);
 

posted @ 2021-02-19 17:50  鲸落98  阅读(33)  评论(0)    收藏  举报