黑马程序员_06Java_IO流知识总结
来源:互联网 发布:3d碰撞检测算法 编辑:程序博客网 时间:2024/06/06 00:12
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
七、IO(Input Output)流
1,IO概述
1) IO流用来处理的设备之间的数据传输
2) Java对数据的操作是通过流的方式
3) Java用于操作流的对象都在IO包中
4) 流按照操作数据分为两种:字节流和字符流。
5) 流按流向分为:输入流和输出流
字符流:是专门用来处理文字信息的,由于文字会有多种编码形式存在:ASCII、GBK、UTF-8
IO流常用基类
1) 字节流的抽象基类:InputStream,OutputStream
2) 字符流的抽象基类:Reader,Writer
注意:有这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。如InputStream的子类FileInputStream,Reader的子类FileReader等。
2,字节流
字节流:处理字节数据的流对象。设备上的数据无论是图片、视频或者文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
那为什么要有字符流呢?因为每个国家的的字符都不一样,所以设计到了字符编码问题,那么GBK编码的中文用unicode编码解析式有问题的,所以需要获取中文字节数据的同时+制定的编码表才可以解析正确数据。为了方便于文字的解析,所以讲字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。
1) 读取文件
创建FileInputStream对象,指定一个文件。文件必须存在,不存在则会抛出FileNotFoundException;使用read()方法可以从文件中读取一个字节,如果读取到文件末尾会读到-1;读取结束后需要释放资源,调用close()方法关闭输入流
2) 写出文件
创建FileOutputStream对象,指定一个文件,文件不存在会自动创建新文件,存在则清空原内容。如果需要追加,在构造函数中传入true;使用write()方法可以像文件写出一个字节;写出结束后同样需要调用close()
3) 拷贝文件
可以从文件中逐个字节读取,逐个字节写出,但这样做效率非常低,我们可以定义一个数组作为缓冲区,一次读取多个字节装入数组,然后再一次性吧数组中的字节写出。
4) 常用方法
InputStream:
read() 读取一个字节
read(byte[]) 读取若干(数组长度)字节
available() 获取可读的字节数
skip() 跳过若干字节
close 关闭流,释放资源
OutputStream:
write(int) 写出一个字节
write(byte[]) 写出数组中的所有字节
close() 关闭流,释放资源
5) BufferedInpustream
BufferedInputStream内置了一个缓冲区(数组),从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个,存在缓冲区中,返回给程序一个。程序再次读取时,就不用找文件了,直接从缓冲区中获取,知道缓冲区中所有的都被使用过,才重新从文件中读取8192个。
6) BufferedOutputStream
BufferedOutputStream也内置了一个缓冲区(数组),程序向六中写出字节时,不会直接写到文件,先写到缓冲区中,直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里
注意:流的操作只有两种:读和写。
代码演示:
1. 创建一个字符输出流对象,用于操作文件。该对象一建立,就必须明确数据存储位置,是一个文件。
2. 对象产生后,会在堆内存中有一个实体,同时也调用了系统底层资源,在指定的位置创建了一个存储数据的文件。
3. 如果指定位置,出现了同名文件,文件会被覆盖。
public static void main(String[] args) throws IOException{
FileWriter fw = new FileWriter(“demo.txt”);//FileNotFoundException
fw.write(“abcde”);
fw.flush();//刷新缓冲区,将缓冲区中的数据刷到目的地文件中
fw.close();//关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流
}
细节:调用Writer类中的write方法写入字符串。字符串并未直接写到目的地中,而是写入到了流中(其实是写到内存缓冲区中)。
3,close()和flush()的区别:
flush():将缓冲区的数据刷到目的地中后,流可以使用。
close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源,且这个动作一定要做。
IO异常的处理方式:IO一定要写finally
4,FileWriter写入数据的细节:
1)window中的换行符:\r\n两个符号组成;linux中的换行符:\n; Mac中的换行符:\r
2)续写数据,只要在构造函数中传入新的参数true
3)目录分割符:window:\\
代码演示:
public static voidmain(String[] args) { FileWriter fw = null; try{ fw = new FileWriter("demo.txt",true); fw.write("abc"); }catch(IOException e){ System.out.println(e.toString()); }finally{ if(fw!= null){ try{ fw.close(); }catch(IOException ex){ System.out.println("close:"+ ex.toString()); } } } }
5,FileReader:使用Reader体系,读取一个文本文件中的数据。返回-1,标志读到结尾。
代码演示:
import java.io.*;class FileReaderDemo{ public static void main(String[] args)throws IOException{ //创建可以读取文件文件的流对象,FileReader让创建好的流对象和指定的文件相关联。 FileReader fr = newFileReader("demo.txt"); int ch=0; while((ch=fr.read()) != -1){//条件是没有读到结尾 System.out.println((char)ch);//调用读取流的read方法,读取一个字符。 } fr.close(); }}
读取数据的第二种方式:第二种方式较为高效,自定义缓冲区
import java.io.*;class FileReaderDemo2{ public static void main(String[] args)throws IOException{ FileReader fr = newFileReader("demo.txt");//创建读取流对象和指定文件关联 //因为要使用read(char[])方法,将读取到的字符存入数组,所以要创建一个字符数组,一般数组的长度都是1024的整数倍 char[] buf = new char[1024]; int len=0; while((len=fr.read(buf)) != -1){ System.out.println(newString(buf,0,len)); } fr.close(); }}
6,IO中使用到了一个设计模式:装饰设计模式
装饰设计模式解决:对一组类进行功能的增强。
包装:写一个类(包装类)对包装对象进行包装;
1) 包装类和被包装对象要实现同样的接口;
2) 包装类要持有一个被包装对象;
3) 包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现;
代码演示:
class Test{ public static void main(String[] args) { //创建一个学生对象 Student s = new Student(); //s.study(); //创建学生对象的装饰类 ItheimaStudent is = new ItheimaStudent(s); is.study(); }}//定义一个接口,用于实现抽象方法interface Study{ void study();}//定义一个学生类,继承Study这个接口class Studentimplements Study{ //复写接口中的study方法 public void study(){ System.out.println("JavaSe"); System.out.println("JavaWeb"); }}//定义一个学生类的装饰类class ItheimaStudentextends Student{ //创建学生对象的引用 private Student s; //创建本类对象的构造函数 public ItheimaStudent(Student s){ this.s = s; } //复写接口中的方法 public void study(){ s.study(); System.out.println("JavaEE +Android"); System.out.println("JavaEE +Hadoop"); System.out.println(".Net +IOS + Android"); }}
7,字符流
Reader:用于读取字符流的抽象类。子类必须实现的方法有read(char[], int ,int)和close().
|--BufferedReader:从字符输入流中读取文本,缓冲各个字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。
|--LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int)和getLineNumber(),它们可分别设置和获取当前行号
|--InputStreamReader:是字节流通向字符流的桥梁,它使用指定charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显示给定,或者可以接受平台默认的字符集。
|--FileReader:用来读取字符文件的便捷类。此类的构造方法嘉定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以现在FileInputStream上构造一个InputStreamReader。
8,字节流
InputStream:是表示字节输入流的所有类的超类。
|--FileInputStream:从文件系统中的某个文件中获取输入字节。哪些文件可用取决于主机环境。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用FileReader。
|--FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
|--BufferedInputStream:该类实现缓冲的输入流
|--Stream:
|--ObjectInputStream:
|--PipedInputStream
9,OutputStream:此抽象类是表示输出字节流的所有类的超类
|--FileOutputStream:文件输出流是用于将数据写入File或FileDescriptor的输出流。
|--FilterOutputStream:此类是过滤输出流的所有类的超类。
|--BufferedOutputStream:该类实现缓冲的输出流
|--PrintStream:
|--DataOutputStream:
|--ObjectOutputStream:
|--PipedOutputStream:
10,BufferedWriter:给字符输出流提高效率用的,那就意味着,缓冲区对象建立是,必须要先有流对象。明确要提高具体的流对象效率。
代码演示:
FileWriter fw = new FileWriter(“demo.txt”); BufferedWriter bufw = new BufferedWriter(fw);//让缓冲区和指定流相关联 for(int x=0; x<4; x++){ bufw.write(x+ “abc”); bufw.newLine();//写入一个可跨平台的换行符 bufw.flush();//对缓冲区进行刷新,可以让数据刷到目的地中。}bufw.close();//关闭缓冲区,其实就是在关闭具体的流
11,BufferedReader
代码演示:
FileReader fr = newFileReader(“demo.txt”);BufferedReader bufr =new BuffredReader(fr);String line = null;while((line=bufr.readLine())!= null){//readLine方法返回的时候是不带换行符的。 System.out.println(line);}bufr.close();//键盘录入和输出到控制台BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));BufferedWriter bufw = new BufferedWriter(newOutputStreamWriter(System.out)); String line= null; while((line=bufr.readLine()) != null){ if(“over”.equals(line)){ break;}bufw.write(line.toUpperCase());//将输入的字符转成大写字符输出bufw.newLine();bufw.flush();}bufw.close();bufr.close();
流对象:其实很简单,即使读取和写入;但是因为功能的不同,流的体系中提供了N多的对象,那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律。
13,IO流操作规律
通过三个明确来完成:
1. 明确源和目的
源:输入流:InputStream Reader
目的:输出流:OutputStream Writer
2. 操作的数据是否是纯文本
是:字符流
不是:字节流
3. 当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存、硬盘、键盘
目的设备:内存、硬盘、控制台
练习1:将一个文本文件中的数据存储到另一个文件中,复制文件
1) 源:因为是源,所以使用读取流:InputStreamReader
是不是操作纯文本?是!
这时就可以选择字符流:Reader,这样体系就明确了。
2) 接下来明确要使用该体系中的哪个对象
明确设备:硬盘上的一个文件
Reader体系中可以操作文件的对象时FileReader
3) 是否需要提高效率:是!加入Reader体系中的缓冲区BufferedReader
FileReader fr = new FileReader();
BufferedReader bufr = new BufferedReader(fr);
4) 目的:OutputStream Writer
是否是纯文本?是,则选择Writer
设备:硬盘上的一个文件
Writer体系中可以操作文件的对象时FileWriter
5) 是否需要提高效率:是!加入Writer体系中的缓冲区BufferedWriter
FileWriter fw = new FileWriter();
BufferedWriter bufw = new BufferedWriter(fw);
练习2:将键盘录入的数据保存到一个文件中。
1) 这个需求中源和目的都存在,那么分别分析
源:InputStream Reader
是否是纯文本?是! Reader
2) 设备:键盘,对应的对象是System.in
不是选择Reader吗?System.in对应的不是字节流吗?为了操作键盘的文本数据方便,转成字符流按照字符串操作时最方便的。所以既然明确了Reader,那么就将System.in转成Reader。用了Reader体系中的转换流:InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
3) 需要提高效率吗?需要! BufferedReader(isr);
4) 目的:OutputStream Writer
是否是纯文本?是! Writer
设备:硬盘,一个文件,使用FileWriter
FileWriter fw = new FileWriter(“c.txt”);
5) 需要提高效率吗?需要!
BufferedWriter bufw = new BufferedWriter(fw);
练习3:把录入的数据按照指定的编码表(UTF-8),将数据存到文件中
1) 目的:OutputStream Writer
是否是纯文本?是!Writer
2) 设备:硬盘,一个文件,使用FileWriter
但是FileWriter使用的是默认编码表GBK
但是存储时,需要加入指定编码表UTF-8,而指定的编码表只有转换流可以指定,所以要使用的对象时OutputStreamWriter。而该转换流对象要接受一个字节输出流,而且还可以操作文件的字节输出流:FileOutputStream
OutputStreamWriter osw =
new OutputStreamWriter(new FileOutputStream(“d.txt”));
3) 需要高效吗?需要
BufferedWriter bufw = new BufferedWriter(osw);
问题:什么时候使用转换流呢?
字符和字节之间的桥梁,通常,涉及到字符编码转换时,就需要用到转换流。
代码演示:
import java.io.*;class TransStreamDemo2{ public static voidmain(String[] args) throws IOException { //设置输入文件 //System.setIn(newFileInputStream("TransStreamTest00.java")); System.setOut(newPrintStream("z.txt")); //录入键盘常用写法 BufferedReaderbufr = newBufferedReader(new InputStreamReader(System.in)); BufferedWriterbufw = new BufferedWriter(newOutputStreamWriter(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(); }}
小结:
由于考虑到readLine方法是字符流BufferedReader类中的方法,而键盘录入的read方法是字节流InputStreamReader类中的方法,于是这里就用到了转换流
转换流特有的功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。转换流最强的功能就是基于 字节流 + 编码表。没有转换,没有字符流
14,File类
File类:将文件系统中的文件和文件夹封装成了对象,提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。
File类常见方法:
1. 创建
booleancreateNewFile():在指定目录下创建文件,如果该文件已存在,则不创建。而对操作文件的输出流而言,输出流对象一建立,就会创建文件,如果文件已存在,则会先将其清空后覆盖,除非续写。
booleanmkdir():创建此抽象路径名指定的目录
booleanmkdirs():创建多级目录
2. 删除
boolean delete():删除此抽象路径名表示的文件或目录
void deleteOnExit():在虚拟机退出时删除。
注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。Windows的删除动作,是从里往外删,且Java删除文件不走回收站,要慎用。
3. 获取
long length():获取文件大小
String getName():返回由此抽象路径名表示的文件或目录的名称
StringgetAbsolutePath():返回此抽象路径名的绝对路径名字符串
String getParent():返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目录,则返回null
long lastModified():返回此抽象路径名表示的文件最后一次呗修改的时间。
File.pathSeparator:返回单签系统默认的路径分隔符,windows默认为”;”
File.Separator:返回当前系统默认的目录分隔符,windows默认为“\”
4. 判断:
boolean exists():判断文件或者文件夹是否存在
boolean isDirectory():测试此抽象路径名表示的文件是否是一个目录
boolean isFile():测试此抽象路径名表示的文件是否一个标准文件。
boolean isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件
boolean isAbsolute():测试此抽象路径名是否为绝对路径名
5. 重命名
boolean nameTo(File dest):可以实现移动的效果,剪切 + 重命名
String[] list():列出指定目录下的当前的文件盒问佳佳的名称,包含隐藏文件。如果调用list方法的File对象中封装的是一个文件,那么list方法返回数组为null;如果封装的对象不存在也会返回null。只有封装的对象存在并且是文件夹时,这个方法才有效。
15,递归:就是函数自身调用自身
1. 什么时候使用递归呢?
当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。简单的说:功能内部有用到该功能,但是传递的参数值不确定。(每次功能参与运算的位置内容不确定)
2. 递归的注意事项:
1) 一定要定义递归的条件;
2) 递归的次数不要过多,容易出现StackOverflowError栈内存溢出错误,其实递归就是在栈内存中不断的加载同一个函数。
代码演示:
import java.io.*;class FileDemo3 { public static void main(String[] args) { File dir = new File("D:\\java0414"); showDir_1(dir); //showDir(dir); //toBin(6); //int n = getSum(10); //System.out.println("n="+n); } public static int getSum(int n){ if(n==1) return 1; return n+getSum(n-1); } public static void toBin(int num){ while(num>0){ System.out.println(num%2); num= num/2; } } public static void showDir(File dir){ System.out.println(dir);//显示根目录 File[] files = dir.listFiles(); for(int x=0; x<files.length; x++){ if(files[x].isDirectory()) showDir(files[x]); else System.out.println(files[x]); } }}
16,Java.util.Properties:一个可以将键值进行持久化存储的对象。Map-Hashtable的子类。
Map
|--Hashtable
|--Properties:用于属性配置文件,键和值都是字符串类型。
特点:
1) 可以持久化存储数据;
2) 键值都是字符串;
3) 一般用于配置文件
常用方法
load():将流中的数据加载进集合,原理:其实就是讲读取流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过=对该行数据进行切割,左边就是键,右边就是值,将键、值存储到Properties集合中。
store():写入各项后,刷新输出流
list():将集合的键值数据列出到指定的目的地
17,Java.io.outputStream.PrintStream:打印流
1、PrintStream属于io包中扩展功能的流对象,基本都是装饰设计模式
1. 提供了更多的功能,比如打印方法,可以直接打印任意类型的数据
2. 它有一个自动刷新机制,创建该对象,指定参数,对于指定方法可以自动刷新
3. 它使用的本机默认的字符编码
4. 该流的print方法不抛出IOException
2、该对象的构造函数
PrintStream(File file): 创建具有指定文件且不带自动行刷新的新打印流
PrintStream(File file,String str): 创建具有指定文件名称和字符集且不带自动行刷新的新打印流
PrintStream(OutputStream out): 创建新的打印流
PrintStream(OutputStream out,boolean autoFlush):创建新的打印流
PrintStream(OutputStream out,boolean autoFlush,Stringencoding): 创建新的打印流
PrintStream(String fileName):创建具有指定文件名称且不带自动行刷新的新打印流
PrintStream(String fileName,String str):
3、PrintStream可以操作目的:
1) File对象;
2) 字符串路径;
3) 字节输出流
前两个都在JDK1.5才出现,而且在操作文本文件时,可指定字符编码了。
当目的是一个字节输出流时,如果使用println方法,可以在PrintStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新,而不是等待缓冲区满了再刷新。最终print方法都将具体的数据转成字符串,而且都对IO异常进行了内部处理。
既然操作的数据都转成了字符串,那么使用PrintWriter更好一些。因为PrintWriter是字符流的子类,可以直接操作字符数据,同时也可以指定具体的编码。
4,PrintWriter:具备了PrintStream特点的同时,还有自身特点:
该对象目的地有四个:
1) File对象;
2) 字符串路径;
3) 字节输出流;
4) 字符输出流
开发时尽量使用PrintWriter
方法中直接操作文件的第二参数是编码表;
直接操作输出流的,第二参数是自动刷新
代码演示:
//读取键盘录入将数据转成大写显示在控制台BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));//源:键盘输入//目的:把数据写到文件中,还需要自动刷新PrintWriter out = new PrintWriter(new FileWriter(“out.txt”),true);//设置true后自动刷新String line = null;while((line =bufr.readLine())!= null){ if("over".equals(line)){ break; out.println(line.toUpperCase());//转成大写并输出}}out.close();bufr.close();
注意:System.in,System.out这两个标准的输入输出流,在jvm启动时就已经存在了,随时可以使用。当jvm结束了,这两个流也就结束了。但是当使用了显示的close方法关闭时,这两个流就提前结束了。
18,SequenceInputStream:序列流
序列流:作用就是讲多个读取流合并成一个读取流,实现数据合并。
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,知道到达文件末尾,接着从第二个输入流读取,一次类推,知道到达包含的最后一个输入流的文件末尾为止。这样做,可以更方便的操作多个读取流,其实这个序列内部会有一个有序的集合容器,用于存储多个读取流对象。
该对象的构造函数参数式枚举,想要获取枚举,需要有Vector集合,但不高效。需用ArrayList,但ArrayList中没有枚举,只有自己去创建枚举对象。但是方法该怎样实现呢?因为枚举是操作的是具体集合中的元素,所以无法具体实现,但是枚举和迭代器是功能一样的,可以用迭代代替枚举。
合并原理:多个读取流对应一个输出流。
切割原理:一个读取流对应多个输出流。
代码演示:
import java.io.*;
import java.util.*;
class SplitFileDemo{
private static final String CFG = ".properties";
private static final String SP = ".part";
public static void main(String[] args) throws IOException{
File file = new File("c:\\0.bmp");
File dir = new File("c:\\partfiles");
meger(dir);
}
//数据的合并。
public static void meger(File dir)throws IOException{
if(!(dir.exists()&& dir.isDirectory()))
throw new RuntimeException("指定的目录不存在,或者不是正确的目录");
File[] files = dir.listFiles(new SuffixFilter(CFG));
if(files.length==0)
throw new RuntimeException("扩展名.proerpties的文件不存在");
//获取到配置文件
File config = files[0];
//获取配置文件的信息。
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(config);
prop.load(fis);
String fileName = prop.getProperty("filename");
int partcount = Integer.parseInt(prop.getProperty("partcount"));
//--------------------------
File[] partFiles = dir.listFiles(new SuffixFilter(SP));
if(partFiles.length!=partcount)
throw new RuntimeException("缺少碎片文件");
//---------------------
ArrayList<FileInputStream>al = new ArrayList<FileInputStream>();
for(int x=0; x<partcount; x++){
al.add(new FileInputStream(new File(dir,x+SP)));
}
Enumeration<FileInputStream> en = Collections.enumeration(al);
SequenceInputStream sis = new SequenceInputStream(en);
File file = new File(dir,fileName);
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
//带有配置信息的数据切割。
public static void splitFile(File file)throws IOException{
//用一个读取流和文件关联。
FileInputStream fis = new FileInputStream(file);
//创建目的地。因为有多个。所以先创建引用。
FileOutputStream fos = null;
//指定碎片的位置。
File dir = new File("c:\\partfiles");
if(!dir.exists())
dir.mkdir();
//碎片文件大小引用。
File f = null;
byte[] buf = new byte[1024*1024];
//因为切割完的文件通常都有规律的。为了简单标记规律使用计数器。
int count = 0;
int len = 0;
while((len=fis.read(buf))!=-1){
f= new File(dir,(count++)+".part");
fos= new FileOutputStream(f);
fos.write(buf,0,len);
fos.close();
}
//碎片文件生成后,还需要定义配置文件记录生成的碎片文件个数。以及被切割文件的名称。
//定义简单的键值信息,可是用Properties。
String filename = file.getName();
Properties prop = new Properties();
prop.setProperty("filename",filename);
prop.setProperty("partcount",count+"");
File config = new File(dir,count+".properties");
fos= new FileOutputStream(config);
prop.store(fos,"");
fos.close();
fis.close();
}
}
class SuffixFilter implements FileFilter{
private String suffix;
SuffixFilter(Stringsuffix){
this.suffix = suffix;
}
public boolean accept(File file){
return file.getName().endsWith(suffix);
}
}
19,RandomAccessFile:随机访问流
1. 什么是随机读取文件?
RandomAccessFile可以对文件执行读和写的操作,可以将指针移动到任意位置,在任意位置读写。
2. 使用方式
创建对象:RandomAccessFile(String,String)第一个参数是文件路径,第二个参数式操作模式,可以是只读或者控制是否使用缓冲区
读取:read(),readInt(),readDouble()
写出:write(),writeInt(),writeDouble()
移动指针:seek
3. 特点:
1) 该对象既可读取,又可写入;
2) 该对象中定义了一个大型的byte数组,通过定义指针来操作这个数组
3) 可以通过该对象的getFilePoint()获取指针的位置,通过seek()方法设置指针的位置;
4) 该对象操作的源和目的必须是文件;
5) 其实该对象内部封装了字节读取流和字节写入流
注意:实现随机访问,最好是数据有规律
代码演示:
import java.io.*;class RandomAccessFileDemo{ public static void main(String[] args) throws IOException{ //writeFile(); //readFile(); writeFile_2(); } public static void writeFile_2()throws IOException{ RandomAccessFile raf = new RandomAccessFile("fan.txt","rw"); raf.seek(8*1); raf.write("周七".getBytes()); raf.writeInt(107); raf.close(); } public static void readFile()throws IOException{ RandomAccessFile raf = new RandomAccessFile("fan.txt","r"); //调整对象中的指针 //raf.seek(8*1); //跳过制定的字节数,缺点是不能往回跳 raf.skipBytes(8); byte[] buf = new byte[4]; raf.read(buf); String name = new String(buf); int age = raf.readInt(); System.out.println("name="+name); System.out.println("age="+age); raf.close(); } public static void writeFile()throws IOException{ RandomAccessFile raf = new RandomAccessFile("fan.txt","rw"); raf.write("李四".getBytes()); raf.writeInt(97);//之所用writeInt是为了防止精度丢失 raf.write("王五".getBytes()); raf.writeInt(99); raf.close(); }}
小结:
1.由于write底层封装了只写出数据的最低八位,因此这样容易造成精度丢失,而为了防止出现这样的错误,于是就得将数据的最低八位都写出去,这样才能保证数据的原样性。
2.seek可以随机调整指针位置,而skipBytes只能向后调整指针位置。
20,IO-管道流
管道流分PipedInputStream和PipedOutputStream,输入输出可以直接进行连接,通过结合线程使用
管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从PipedInputStream对象读取,并由其他线程将其写入到相应的PipedOutputStream。不建议对这两个对象尝试使用耽搁线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果想连接管道流输出流提供数据字节的线程不再存在,则认为该管道已损坏。集合当中涉及到IO流的是Properties,IO当中涉及到多线程的就是管道流。
代码演示:
import java.io.*;class Read implements Runnable{ private PipedInputStream in; Read(PipedInputStream in){ this.in= in; } public void run(){ //读数据 try{ byte[] buf = new byte[1024]; System.out.println("读取前。。没有数据,阻塞"); int len = in.read(buf); System.out.println("读到数据。。阻塞结束"); String s = new String(buf,0,len); System.out.println(s); in.close(); } catch(IOException e){ throw new RuntimeException("管道读取流失败"); } }}class Write implements Runnable{ private PipedOutputStream out; Write(PipedOutputStream out){ this.out= out; } public void run(){ try{ System.out.println("开始写入数据,等待6秒后..."); Thread.sleep(6000); out.write("pipedlai le".getBytes());//转换成字节流 out.close(); } catch(Exception e){ throw new RuntimeException("管道输出流失败"); } }}class PipedStreamDemo{ public static void main(String[] args) throws IOException{ PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(); //将管道流连接起来 in.connect(out); Read r = new Read(in); Write w = new Write(out); newThread(r).start(); newThread(w).start(); }}
小结:
管道流之所以不会造成线程死锁的原因是管道读取流中有一个read方法,只要没有读取到数据,该线程则会出现阻塞情况
21,IO-对象的序列化
目的:将一个具体的对象进行持久化写入到硬盘上。
注意:静态数据不能被序列化,因为静态数据不再堆内存中,是存储在静态方法区中。
如何将非静态的数据不进行序列化?
用transient关键字修饰此变量即可。
Serializable:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口给序列化类提供UID。这个UID是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个UID,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。
代码演示:
import java.io.*;class Person implements Serializable{ //自定义UID值,保证UID值的唯一性 public static finallong serialVersionUID = 42L; String name; transient intage;//transient可以让age无法序列化,保证其值在堆内存中存在而不再文本文件中存在 static String country="cn"; Person(String name,intage,String country){ this.name =name; this.age =age; this.country= country; } public StringtoString(){ returnname+":"+age+"::"+country; }}
import java.io.*;class ObjectStreamDemo { public static voidmain(String[] args) throws Exception{ //writeObj(); readObj(); } public static void readObj()throwsException{ ObjectInputStreamois = new ObjectInputStream(new FileInputStream("obj.txt")); Person p =(Person)ois.readObject(); System.out.println(p); ois.close(); } public static voidwriteObj()throws IOException{ ObjectOutputStreamoos = newObjectOutputStream(new FileOutputStream("obj.txt")); oos.writeObject(newPerson("lisi",30,"cn")); oos.close(); }}
注意:在使用对象序列化的时候,必须是两者搭配使用,即用了ObjectInputStream就得用ObjectOutputStream;而其中,被静态所修饰的成员变量是无法被序列化的,另外transient也能让成员变量无法序列化
特别注意一点:静态时无法被序列化的
22,IO-数据输入与输出流
1. 什么是数据输入输出流
DataInputStream,DataOutputStream可以按照基本数据类型的大小读取数据,例如按Long大小写出一个数字,写出时该数据占8个字节,读取的时候也可以按照Long类读取,一次读8个字节。
2. 使用方式
DataInputStream(InputStream),readInt(),readLong()
DataOutputStream(OutputStream),writeInt(),writeLong()
3. 特点:
DataInputStream和DataOutputStream是专门用于操作基本数据类型数据的对象。
扩展:
操作字节数组:ByteArrayInputStream与ByteArrayOutputStream
操作字符数组:CharArrayReader与CharArrayWriter
操作字符串数组:StringReader与StringWriter
代码演示:
import java.io.*;class DataStreamDemo { public static void main(String[] args) throws IOException{ //writeData(); //readData(); //writeUTFDemo(); //writeGBKDemo(); readUTFDemo(); } public static void readUTFDemo()throws IOException{ DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt")); String s =dis.readUTF(); System.out.println(s); dis.close(); } public static void writeGBKDemo()throws IOException{ OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8"); osw.write("你好"); osw.close(); } public static void writeUTFDemo()throws IOException{ DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt")); dos.writeUTF("你好"); dos.close(); } public static void readData()throws IOException{ DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); int num =dis.readInt(); boolean b =dis.readBoolean(); double d =dis.readDouble(); System.out.println("num="+num); System.out.println("b="+b); System.out.println("d="+d); dis.close(); } public static void writeData()throws IOException{ DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt")); dos.writeInt(234); dos.writeBoolean(true); dos.writeDouble(9879.0); dos.close(); }}
小结:
1.切记一点,在读取数据的过程中,读取的顺序不能错,否则就会读取错误
2.凡是操作基本数据类型的就用DataStream
23,IO-内存输入与输出流
1. 什么是内存输出流?
该输出流可以像内存中写数据,吧内存当做一个缓冲区,写出之后可以一次性获取出所有数据。
2. 使用方式
创建对象:new ByteArrayOutputStream()
写出数据: write(int),write(byte[])
获取数据:toByteArray()
3. 特点
ByteArrayInputStream:源:内存
ByteArrayOutputStream:目的:内存
这两个流对象不涉及底层资源调用,操作的都是内存中的数组,所以不需要关闭。直接操作字节数组就可以了,为什么还要把数组封装到流对象中呢?因为数组本身没有方法,只有一个length属性。为了便于数组的操作,将数组进行封装,对外提供方法操作数组中的元素。
对于数组元素操作无非两种操作:设置(写)和获取(读),而这两操作正好对应流的读写操作。这两个对象就是使用了流的读写思想来操作数组。
在流操作规律时可以发现:
源设备:
键盘:System.in; 硬盘:FileStream 内存:ArrayStream
目的设备:
控制台:System.out硬盘:FileStream 内存:ArrayStream
代码演示:
//创建源:ByteArrayInputStream bis = new ByteArrayInputStream(“abcdef”.getBytes());//创建目的:ByteArrayOutputStream bos = new ByteArrayOutputStream();int ch=0;while((ch=bis.read())!= -1){ bos.write(ch);}System.out.println(bos.toString());
注意:由于内存输入输出流的特殊性,这里不需要将流其关闭
24,转换流的字符编码
1、字符编码
1) 字符流了的出现为了方便操作字符;
2) 更重要的是加入了编码符集;
3) 通过子类转换来完成:InputStreamReader和OutputStreamWriter
4) 在两个对象进行构造的时候可以加入字符集
2、编码表的由来
1) 计算机智能识别二进制数据,早期由来是电信号;
2) 为了方便应用计算机,让它可以识别各个国家的文字;
3) 就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。
3、常见编码表
ASCII:美国标准信息交换码,用一个字节的7位可以表示。
IOS8859-1:拉丁码表,欧洲码表,用一个字节的8位表示。
GBK2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字以及符号。
Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是Unicode码表。
UTF-8:最多用三个字节来表示一个字符。
代码演示:
import java.io.*;class EncodeStream { public static void main(String[] args) throws IOException{ //writeText(); readText(); } public static void readText()throws IOException{ InputStreamReader isr = new InputStreamReader(newFileInputStream("gbk.txt"),"GBK"); char[] buf =new char[10]; int len =isr.read(buf); String str =new String(buf,0,len); System.out.println(str); isr.close(); } public static void writeText()throws IOException{ OutputStreamWriter osw = new OutputStreamWriter(newFileOutputStream("gbk.txt"),"GBK"); osw.write("你好"); osw.close(); }}
4、字符编码的练习
编码:字符串变字节数组, String --> byte[]:str.getBytes(charsetName);
解码:字节数组变字符串, byte[] -->String : new String(byte[],charsetName);
编码解码的时候要是遇到解错码,且都是中文,一定得注意不能使用UTF-8去解码,因为都识别中文。
代码演示:
import java.util.*;class EncodeDemo{ public static void main(String[] args) throws Exception{ String s ="你好"; byte[] b1 =s.getBytes("GBK");//编码 System.out.println(Arrays.toString(b1));//字符数组变字符串 String s1 =new String(b1,"iso8859-1");//解码成拉丁编码表 System.out.println("s1="+s1); //解码 byte[] b2 =s1.getBytes("iso8859-1"); System.out.println(Arrays.toString(b2)); String s2 =new String(b2,"GBK"); System.out.println("s2="+s2); }}
注意:由于UTF-8和GBK均识别中文,因此在对字符串进行编码解码的过程中,千万要注意这点,否则就会得到乱码;Tomcat服务器使用的就是ISO8859-1的编码表,当数据传递给服务器时,需要通过编码解码才能得到最终数据。
练习:有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩),输入格式,如zhangsan,30,40,60,计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在磁盘文件”stu.txt”
1. 描述学生对象;
2. 定义一个可操作学生对象的工具类。
思路:
1) 通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2) 因为学生很多,那么就需要存储,使用到集合,因为要对学生的总分进行排序,所以可以使用TreeSet集合。
3) 将集合的信息写入到一个文件中。
代码演示:
import java.io.*;import java.util.*;//定义一个学生类,并实现Comparable接口class Student implements Comparable<Student>{ private String name; private int ma,cn,en; private int sum; Student(String name,int ma,int cn,int en){ this.name =name; this.ma =ma; this.cn =cn; this.en =en;//由于sum并不是传递进来的,因此这里是前者的累加和 sum = ma +cn + en; } public int compareTo(Student s){ int num =new Integer(this.sum).compareTo(new Integer(s.sum)); if(num==0) returnthis.name.compareTo(s.name); return num; } public String getName(){ return name; } public int getSum(){ return sum; }//定义一个hashCode方法,防止获取的值存到其他集合中 public int hashCode(){ returnname.hashCode()+sum*78; } public boolean equals(Object obj){ if(!(obj instanceof Student)) thrownew ClassCastException("类型不匹配"); //强制类型转换 Student s =(Student)obj; return this.name.equals(s.name) && this.sum==s.sum; } public String toString(){ return "Student["+name+", "+ma+", "+cn+","+en+"]"; } }
//定义一个学生工具类class StudentInfoTool{//定义一个不含比较器的方法 public staticSet<Student> getStudents()throws IOException{ return getStudents(null); }//定义一个含Comparator接口的方法 public static Set<Student> getStudents(Comparator<Student> cmp)throws IOException{ BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in)); String line= null; Set<Student> stus = null; if(cmp==null) stus= new TreeSet<Student>(); else stus= new TreeSet<Student>(cmp); while((line=bufr.readLine())!=null){ if("over".equals(line)) break; String[] info = line.split(","); Student stu = new Student(info[0],Integer.parseInt(info[1]), Integer.parseInt(info[2]), Integer.parseInt(info[3])); stus.add(stu); } bufr.close(); return stus; } public static void write2File(Set<Student> stus)throws IOException{//将流与文件相关联 BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));//使用高级for循环遍历集合 for(Student stu:stus){ bufw.write(stu.toString()+"\t"); bufw.write(stu.getSum()+""); bufw.newLine(); bufw.flush(); } bufw.close(); }}class StudentInfoTest{ public static void main(String[] args)throws IOException { Comparator<Student> cmp = Collections.reverseOrder();//反转比较器 Set<Student> stus = StudentInfoTool.getStudents(cmp); StudentInfoTool.writeTOFile(stus); }}
注意:reverse()只是反转的,而reverseOrder()则是反转比较器的,不能记错!
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
详细请查看:http://edu.csdn.net
- 黑马程序员_06Java_IO流知识总结
- 黑马程序员:IO流相关知识总结
- 黑马程序员_IO流知识总结
- 【黑马程序员】IO流知识总结
- 黑马程序员-反射知识总结
- 黑马程序员_WinForm知识总结
- 黑马程序员_MSSQLServer知识总结
- 黑马程序员_04Java_API知识总结
- 【 黑马程序员】多线程知识总结
- 【黑马程序员】API知识总结
- 【黑马程序员】反射知识总结
- 异常知识总结------------黑马程序员
- 黑马程序员—IO流缓冲区知识总结
- 黑马程序员-线程知识总结-No.02
- 黑马程序员_ADO.NET学习知识总结
- 黑马程序员_HTML学习知识简单总结
- 黑马程序员_Dom学习知识简单总结
- 黑马程序员_JQuery学习知识简单总结
- 文件存储-输入文件
- Android JNI编程提高篇之二
- 快速排序(1)
- 字符串公共串
- 从sdcard读取文件
- 黑马程序员_06Java_IO流知识总结
- 2012百度校园招聘、移动终端、笔试题目 2011-9-24
- Ubuntu下如何快速配置JDK环境
- 如何安全卸载MySql
- 读取资源文件(文本资源)
- 利用匿名namespace解决C++中重复定义的问题
- git使用
- 子类从基类中继承下来了什么?
- KMP