1、系统结构图(xmind)
2、IO流继承体系简图
3、tips
——1.字符流Writer
需求:将一些文字存储到硬盘一个文件中。
如果要操作文字数据,建议优先考虑字符流。而且要将数据从内存写到硬盘上,要使用字符流中的输出流:Writer。硬盘的数据基本体现是文件,希望找到一个可以操作文件的Writer:FileWrite。
代码:
import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo { public static void main(String[] args) throws IOException { //创建一个可以往文件中写入字符数据的字符输出流对象 //既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地) //如果文件不存在,则会自动创建 //如果文件存在,则会被覆盖 FileWriter fw = new FileWriter("demo.txt" ); //调用Writer对象中的write(string)方法,写入数据 //其实数据写入到临时存储缓冲区中 fw.write( "abcde"); //进行刷新,将数据直接写入到目的地中 fw.flush(); //关闭流,关闭资源,在关闭前会先调用flush刷新缓冲中的数据到目的地。 fw.close(); } }
1.close方法只能用一次。
——2.字符流Reader
1.使用read()方法读取文本文件数据。
栗:
import java.io.FileReader; import java.io.IOException; public class FileReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("demo.txt" ); //用Reader中的read方法读取字符 int ch = 0; while((ch = fr.read()) != -1) { System.out.print(( char)ch+" "); } fr.close(); } }
运行结果:
public int read()
throw IOException
读取单个字符。在字符可用、发生I/O错误或者已达到流的末尾前,此方法一直阻塞。用于支持高效的单字符输入的子类应重写此方法。
返回:作为整数读取的字符,范围在0到65535之间(0x00-0xffff),如果已达到流的末尾,则返回-1
抛出:IOException-如果发生I/O错误。
2.使用read(char[])方法读取文本文件数据。
栗:
import java.io.FileReader; import java.io.IOException; public class FileReaderDemo2 { public static void main(String[] args)throws IOException { FileReader fr = new FileReader("demo.txt" ); //使用read(char[])读取文本文件数据 //先创建字符数组 char[] buf = new char[3]; int len = 0; while((len = fr.read(buf)) != -1) { System.out.println( new String(buf,0,len)); } fr.close(); } }
运行结果:
——3.字符流缓冲区
写入换行使用BufferedWriter类中的newLine()方法。读取一行数据使用BufferedReader类中的readLine()方法。bufr.read():这个read方法是从缓冲区中读取字符数据,所以覆盖了父类中的read方法。bufr.readLine():另外开辟了一个缓冲区,存储的是原缓冲区一行的数据,不包含换行符。原理:使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记,将标记前的缓冲数据变成字符串返回。
栗:
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; public class CopyTextBufTest { public static void main(String[] args) throws Exception { FileReader fr = new FileReader("buf.txt" ); BufferedReader bufr = new BufferedReader(fr); FileWriter fw = new FileWriter("buf_copy.txt" ); BufferedWriter bufw = new BufferedWriter(fw); String line = null; //方式一 while((line = bufr.readLine()) != null) { bufw.write(line); bufw.newLine(); bufw.flush(); } //方式二 /* int ch = 0; while((ch = bufr.read()) != -1) { bufw.write(ch); } */ bufr.close(); bufw.close(); } }
运行结果:
——4.装饰设计模式
1.当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。
2.特点:装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
3.装饰和继承的区别:
1)装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。
2)装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
3)从继承结构转为组合结构。
在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。
——5.字节流
栗1:
import java.io.FileOutputStream; import java.io.IOException; public class ByteStreamDemo { public static void main(String[] args) throws IOException { demo_write(); } public static void demo_write() throws IOException { //1、创建字节输出流对象,用于操作文件 FileOutputStream fos = new FileOutputStream( "bytedemo.txt"); //2、写数据,直接写入到了目的地中 fos.write( "abcdefg".getBytes()); //关闭资源动作要完成 fos.close(); } }
运行结果:
栗2:
/* 练习: 复制一个图片 思路: 1、用字节读取流对象和图片关联。 2、用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。 3、通过循环读写,完成数据的存储。 4、关闭资源。 */ import java.io.*; class CopyPic { public static void main(String[] args) { //常用方法复制 byteArrayCopy(); //利用输入流的available方法进行复制 availableCopy(); //利用available方法对复制文件大小有限制,慎用 } //使用available方法进行复制 public static void availableCopy() { FileInputStream fis=null; FileOutputStream fos=null; try { //关联要复制的文件 fis=new FileInputStream("D:/桌面/1.jpg"); //指定复制的路径 fos=new FileOutputStream("D:/桌面/2.jpg"); //利用available方法指定数组长度 byte[] b=new byte[fis.available()]; fis.read(b);//复制关联文件数据 fos.write(b);//粘贴到指定路径 } 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("写入字节流关闭失败"); } } } //使用读数组方式进行复制 public static void byteArrayCopy() { FileInputStream fis=null; FileOutputStream fos=null; try { //关联要复制的文件 fis=new FileInputStream("D:/桌面/1.jpg"); //指定复制的路径 fos=new FileOutputStream("D:/桌面/3.jpg"); //利用数组的读取方式 byte[] b=new byte[1024]; int len=0; while ((len=fis.read(b))!=-1)//复制文件的全部数据 { fos.write(b,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("写入字节流关闭失败"); } } } }
运行结果:
——6.字节流缓冲区
需求:根据字节流缓冲区的原理,自定义一个字节流缓冲区。
注意:
1、字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。
2、byte类型的-1提升为int类型时还是-1。原因:因为在bit8个1前面补的全是1导致的。如果在bit8个1前面补0,即可以保留原字节数据不变,又可以避免-1的出现。这时将byte型数据&0xff即255即可。
栗:
/* 自定义字节流读取缓冲区 思路: 1、定义一个固定长度的数组 2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为0 3、每次将字节数据存入元素要先将数组中的元素取完 */ import java.io.*; class MyBufferedInputStream { private InputStream in; private byte[] by=new byte[1024]; private int count=0,pos=0; MyBufferedInputStream(InputStream in) { this.in = in; } //自定义读方法,一次读一个字节 public int myRead()throws IOException { //通过in对象读取硬盘上数据,并存储by中。 //存储在数组中的数据被读取完,再通过in对象从硬盘上读取数据 if(count == 0) { count = in.read(by); if(count < 0)//文件数据全部被读取出来了 return -1; pos = 0;//初始化指针 byte b = by[pos]; count--;//每被读一个字节,表示数组中的字节数少一个 pos++;//指针加1 return b&255;//返回的byte类型提升为int类型,字节数增加,且高24位被补1,原字节数据改变。 //通过与上255,主动将byte类型提升为int类型,将高24位补0,原字节数据不变。 //而在输出字节流写入数据时,只写该int类型数据的最低8位。 } else if(count > 0)//如果数组中的数据没被读取完,则继续读取 { byte b = by[pos]; count--; pos++; return b&0xff; } return -1; } //自定义关闭资源方法 public void close()throws IOException { in.close(); } } //测试自定义输入字节流缓冲区 class MyBufferedCopyMp3 { public static void main(String[] args) { long start = System.currentTimeMillis(); //利用字节流的缓冲区进行复制 copy_2(); long end=System.currentTimeMillis(); System.out.println("复制共用时:"+(end-start)+"毫秒"); } //使用字节流的缓冲区进行复制 public static void copy_2() { BufferedOutputStream bout=null; MyBufferedInputStream bin=null; try { //关联复制文件输入流对象到缓冲区 bin = new MyBufferedInputStream(new FileInputStream("D:/桌面/Sugar.mp3")); //指定文件粘贴位置的输出流对象到缓冲区 bout = new BufferedOutputStream(new FileOutputStream("D:/桌面/Sugar2.mp3")); int by=0; while((by = bin.myRead())!=-1) { bout.write(by);//将缓冲区中的数据写入指定文件中 } } catch(IOException e) { throw new RuntimeException("MP3复制失败"); } finally { try { if(bin != null) bin.close();//关闭输入字节流 } catch(IOException e) { throw new RuntimeException("读取字节流关闭失败"); } try { if(bout != null) bout.close();//关闭输出字节流 } catch(IOException e) { throw new RuntimeException("写入字节流关闭失败"); } } } }
运行结果:
——7.转换流
1.InputStreamReader将字节流通向字符流
1、获取键盘录入对象。
InputStream in=System.in;
2、将字节流对象转成字符流对象,使用转换流。
InputStreamReaderisr=new InputStreamReader(in);
3、为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
BufferedReaderbr=new BufferedReader(isr);
//键盘录入最常见写法
BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));
2. OutputStreamWriter字符流通向字节流
字符通向字节:录入的是字符,存到硬盘上的是字节。步骤和InputStreamReader转换流一样。
/* 需求:将键盘录入的数据,显示在控制台,当输入over时,表示结束 源:键盘录入。 目的:控制台。 */ import java.io.*; class TransStreamDemo { public static void main(String[] args)throws IOException { //获取键盘录入对象。 //InputStream in=System.in; //将字节流对象转成字符流对象,使用转换流。 //InputStreamReader isr=new InputStreamReader(in); //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader //BufferedReader br=new BufferedReader(isr); //键盘录入最常见写法 BufferedReader in=new BufferedReader(new InputStreamReader(System.in)); //字符流通向字节流 BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out)); String s=null; while((s=in.readLine())!=null) { if("over".equals(s)) break; bw.write(s.toUpperCase());//写入数据 bw.newLine();//换行 bw.flush();//刷新 } bw.close();//关闭流资源 in.close(); } }
运行结果:
——8.Properties集合
Map
|--Hashtable
|--Properties
特点:
1. 该集合中的键和值都是字符串类型。
2. 集合中的数据可以保存到流中,或者从流中获取。
3. 通常该集合用于操作以键值对形式存在的配置文件。
栗:
import java.util.Properties; import java.util.Set; public class PropertiesDemo { public static void main(String[] args) { propertiesDemo(); } public static void propertiesDemo() { //创建一个Properties集合 Properties prop = new Properties(); //存储元素 prop.setProperty( "zhangsan","10" ); prop.setProperty( "lisi","20" ); prop.setProperty( "wangwu","30" ); prop.setProperty( "zhaoliu","40" ); //修改元素 prop.setProperty( "wangwu","26" ); //取出所有元素 Set<String> names = prop.stringPropertyNames(); for(String name : names) { String value = prop.getProperty(name); System.out.println(name + ":" + value); } } }
运行结果:
——9.实例
/* 需求:获取指定目录下,指定扩展名的文件(包含子目录中的),并且将这些文件的绝对路径写入到 一个文本文件中。 简单说:就是建立一个指定扩展名的文件的列表。 思路: 1. 必须进行深度遍历。 2. 要在遍历的过程中进行过滤,将符合条件的内容都存储到容器中。 3. 对容器中的内容进行遍历并将绝对路径写入到文件中。 */ import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) throws IOException { File dir = new File("E:/Java/day21" ); FilenameFilter filter = new FilenameFilter() { public boolean accept(File dir,String name) { return name.endsWith(".java" ); } }; List<File> list = new ArrayList<File>(); getFiles(dir,filter,list); File destFile = new File(dir,"javalist.txt" ); write2File(list,destFile); } /* 对指定目录中的内容进行深度遍历,并按照指定过滤器,进行过滤。 将过滤后的内容存储到指定容器List中。 */ public static void getFiles(File dir,FilenameFilter filter,List<File>list) { File[] files = dir.listFiles(); for(File file : files) { //递归 if(file.isDirectory()) { getFiles(file,filter,list); } else { //对便利到的文件进行过滤器的过滤。将符合条件File对象,存储到List集合中 if(filter.accept(dir,file.getName())) { list.add(file); } } } } public static void write2File(List<File> list,File destFile) throws IOException { BufferedWriter bufw = null; try { bufw = new BufferedWriter(new FileWriter(destFile)); for(File file : list) { bufw.write(file.getAbsolutePath()); bufw.newLine(); bufw.flush(); } } finally { if(bufw!=null) try { bufw.close(); } catch(IOException e) { throw new RuntimeException("关闭失败"); } } } }
运行结果:
——10.小结
流的基本应用小结:
•流是用来处理数据的。
•处理数据时,一定要先明确数据源,与数据目的地(数据汇)。
•数据源可以是文件,可以是键盘。
•数据目的地可以是文件、显示器或者其他设备。
•而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理.转换处理等。