Day13:IO流
IO流主要涉及到Java中的数据是怎样传送的,是一什么方式传送的,又是以什么方式阅读的
IO流:
IO:用于处理设备上的数据的技术。设备:内存,硬盘,光盘。
流:系统资源,windows系统本身就可以操作设备。各种语言只是使用系统平台上的这个资源。
并对外提供了各种语言自己的操作功能,这些功能最终调用的是系统资源。
使用完资源一定要记住:释放。
IO:java中所涉及的功能对象都存储到java.io包中。
设备上数据最常见的存储表现形式文件file.
小知识点 * 递归:函数自身调用自身。函数内部又使用到了该函数功能。 * 什么时候使用? * 功能被重复使用,但是每次该功能使用参与运算的数据不同时,可以考虑递归方式解决。 * 使用时,一定要定义条件。 * 注意递归次数过多,会出现栈内存溢出。 * String LINE_SEPARATOR = System.getProperty("line.separator");(任何系统都可以的换行符)
/* * 将数据写入到文件中。 * 使用字节输出流。 * FileOutputStream。 */ File dir = new File("tempfile"); if(!dir.exists()){ dir.mkdir(); } //1,创建字节输出流对象。用于操作文件,在对象初始化时,必须明确数据存储的目的地。 //输出流所关联的目的地,如果不存在,会自动创建。如果存在,则覆盖。 FileOutputStream fos = new FileOutputStream("tempfile\\fos.txt"); //2,调用输出流的写功能。 fos.write("abcde".getBytes()); //3,释放资源。 fos.close();
public static void main(String[] args) { // 需求获取一个想要的指定文件的集合。获取javase_code下(包含子目录)的所有的.java的文件对象。并存储到集合中。 /* * 思路: * 1,既然包含子目录,就需要递归。 * 2,在递归过程中需要过滤器。 * 3,满足条件,都添加到集合中。 */ File dir = new File("e:\\javase_code"); List<File> list = fileList(dir,".java"); for(File file : list){ System.out.println(file); } } /** * 对指定目录进行递归。 * * 多级目录下都要用到相同的集合和过滤器。那么不要在递归方法中定义,而是不断的进行传递。 * * @param dir 需要遍历的目录。 * @param list 用于存储符合条件的File对象。 * @param filter 接收指定的过滤器。 */ public static void getFileList(File dir,List<File> list,FileFilter filter){ //1,通过listFiles方法,获取dir当前下的所有的文件和文件夹对象。 File[] files = dir.listFiles(); //2,遍历该数组。 for(File file : files){ //3,判断是否是文件夹。如果是,递归。如果不是,那就是文件,就需要对文件进行过滤。 if(file.isDirectory()){ getFileList(file, list, filter); }else{ //4,通过过滤器对文件进行过滤 if(filter.accept(file)){ list.add(file); } } } }
/** * 定义一个获取指定过滤器条件的文件的集合。 */ public static List<File> fileList(File dir,String suffix){ //1,定义集合。 List<File> list = new ArrayList<File>(); //2,定义过滤器。 FileFilter filter = new FileFilterBySuffix(suffix);//自定义的文件拦截器 getFileList(dir, list, filter); return list; }
File:IO技术用于操作设备上数据的,而数据最常见的体现方式是文件。 先了解了文件的操作。 创建,删除,存在,隐藏,获取......; 需求:怎么操作文件的数据呢? 使用IO流对象。而且文件数据都是字节存在。 学习了可以操作文件的字节流。 InputStream |--FileInputStream OutputStream |--FileOutputStream 为了提高了操作效率。引入缓冲区。 InputStream |--FileInputStream |--FilterInputStream |--BufferedInputStream OutputStream |--FileOutputStream |--FilterOutputStream |--BufferedOutputStream 发现,文件数据,媒体文件字节流没问题。 但是对于文本文件,想要操作文件中的中文数据时,字节流只能操作字节,需要我们字节解码成字符。麻烦。 所以就到API找对象,就发现字符流中有字节和字符的桥梁,传说中的转换流。 Reader |--InputStreamReader:字节-->字符。 Writer |--OutputStreamWriter:字符--->字节。 它们出现了解决了中文的编码转换问题。 为了便捷操作字符文件。找到了转换流的子类,但是它有局限性,只能操作文件,而且是默认编码。 如果不操作文件,而且编码不是默认的,需要使用转换流。 Reader |--InputStreamReader:字节-->字符。 |--FileReader Writer |--OutputStreamWriter:字符--->字节。 |--FileWriter 为了提高了字符流的操作效率。引入字符串的缓冲区。
字符流=字节流+编码
Reader |--InputStreamReader:字节-->字符。 |--FileReader=FileInputStream+InputStreamReader |--BufferedReader:String readLine() Writer |--OutputStreamWriter:字符--->字节。 |--FileWriter=FileOutputStream+OutputStreamWritter |--BufferedWriter:String newLine();
/* * 需求:把目录中的1.jpg复制一次并把文件名改为2.jpg */ //1、获取文件 File file = new File("1.jpg"); //2、创建具体流对象,明确数据源 FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis); //3、创建对冲流,明确目的文件 FileOutputStream fos = new FileOutputStream("2.jpg"); BufferedOutputStream bos = new BufferedOutputStream(fos); //4、进行频繁读写 int len = 0; byte[] buf = new byte[1024]; while((len=bis.read(buf))!=-1){ //写入流 bos.write(buf,0,len); //刷新流 bos.flush(); } //5、释放资源 fis.close(); fos.close();
接着,来一个综合性的流的练习题
/* * 作业:键盘录入多名学生的信息:格式:姓名,数学成绩,语文成绩,英文成绩. * 按总分由高到低,将学生的信息进行排列到文件中。 * * 思路: * 1,使用键盘录入技术。 * 2,操作的学生信息,信息很多,需要将信息封装成学生对象。 * 3,总分由高到低,需要排序,需要对学生对象中的总分排序。需要将多个学生对象进行容器存储。 * 哪个容器呢?TreeSet集合。 * 4,将容器中学生对象的信息写入到文件中。 * * */ //创建一个逆序的比较器。 Comparator<Student> comp = Collections.reverseOrder(); //使用操作学生信息的工具类。 Set<Student> set = GetInfoTool.getStudents(comp); File destFile = new File("tempfile\\info.txt"); GetInfoTool.write2File(set, destFile);
//学生类
public class Student implements Comparable<Student> { private String name; private int ma,cn,en; private int sum; public Student() { super(); } public Student(String name, int ma, int cn, int en) { super(); this.name = name; this.ma = ma; this.cn = cn; this.en = en; this.sum = ma+cn+en; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + sum; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (sum != other.sum) return false; return true; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMa() { return ma; } public void setMa(int ma) { this.ma = ma; } public int getCn() { return cn; } public void setCn(int cn) { this.cn = cn; } public int getEn() { return en; } public void setEn(int en) { this.en = en; } public int getSum() { return sum; } public void setSum(int sum) { this.sum = sum; } @Override public int compareTo(Student o) { int temp = this.sum - o.sum; return temp==0?this.name.compareTo(o.name):temp; } }
//对学生信息进行操作
public class GetInfoTool { /** * 获取所有学生对象集合,按照学生对象的自然排序。 * @throws IOException */ public static Set<Student> getStudents() throws IOException{ return getStudents(null); } /** * 获取学生对象集合,按照指定的比较器排序。 * @param comp * @return * @throws IOException */ public static Set<Student> getStudents(Comparator<Student> comp) throws IOException{ //1,键盘输入。 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //创建一个容器存储学生对象。 Set<Student> set = null; //如果比较器存在,就创建带有比较器的对象。 if(comp!=null){ set = new TreeSet<Student>(comp); }else{ set = new TreeSet<Student>(); } //2,获取键盘录入的信息。 String line = null; while((line=bufr.readLine())!=null){ //键盘录入结束标记。 if("over".equals(line)){ break; } //因为录入的数据是有规律的。可以通过指定的规则进行分割。 String[] strs = line.split(","); //将数组中的元素封装成对象。 Student stu = new Student(strs[0],Integer.parseInt(strs[1]) ,Integer.parseInt(strs[2]) ,Integer.parseInt(strs[3])); //将学生对象存储到集合中。 set.add(stu); } //关闭键盘录入须知:如果后面不在使用键盘录入是可以关闭的,如果后面还要使用,就不要关闭,继续通过System.in就可以获取。 // bufr.close(); return set; } /** * 将集合中的学生信息写入到文件中。 * @throws IOException */ public static void write2File(Set<Student> set,File destFile) throws IOException{ BufferedWriter bufw = null; try{ bufw = new BufferedWriter(new FileWriter(destFile)); //遍历集合。 for(Student stu : set){ bufw.write(stu.getName()+"\t"+stu.getSum()); bufw.newLine(); bufw.flush(); } }finally{ if(bufw!=null){ try { bufw.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
缓冲区原理:
临时存储数据的方法。减少对设备操作的频率,提高了效率。其实就是将数据临时缓存到了内存(数组)中。
模拟一个BufferedReader。
Writer
|--TextWriter
|--MediaWriter
在对数据写入操作过程中,希望提升效率。
要对操作文本的对象提升效率,使用缓冲区技术。
Writer
|--TextWriter
|--BufferedTextWriter
|--MediaWriter
|--BufferedMediaWirter
|--AudioWriter
|--BufferedAudioWriter
这样的体系,为了增加一些功能,而通过产生子类来完成,会导致继承体系变得很臃肿。
重新思考体系的设计问题。都是在写的方法进行效率的提升。
为什么不将该功能进行单独的封装呢?要提升哪个具体对象,将哪个具体对象交给该功能不就可以了吗?
class BufferedWriter extends Writer
{
BufferedWriter(Writer w)
{
}
/*
BufferedWriter(TextWriter w)
{}
BufferedWriter(MediaWriter w)
{
}
*/
}
Writer
|--TextWriter
|--MediaWriter
|--AudioWriter
|--BufferedWriter
TextWriter tw = new TextWriter();
BufferedWriter bufw = new BufferedWriter(tw);
//tw.write();
bufw.write();
解决:可以给对象提供额外的功能(职责)。比继承这种方式更为灵活。
起个名字,装饰设计模式(Wrapper,Decorator)。
装饰类与被装饰类都所属于同一个体系。
同时装饰类中持有被装饰类的引用。