-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
概述
概述:
IO流是用来处理设备之中数据的传输;
Java对数据的操作是通过流的方式;
Java用于操作流的对象都在IO包中;
流按操作数据分为两种:字节流与字符流;
流按流向分为:输入流和输出流。
IO流的常用基类:
字节流的抽象基类:InputStream,OutputStream;
字符流的抽象基类:Reader,Writer
注:由这四个类派生出来的子类名称都是以其父类作为子类名的后缀。
如:InputStream的子类FileInputStream;
Reader的子类FileReader。
字符流
常用的操作字符型的节点流:
操作文件:FileWriter,FileReader;
字符流创建文件:
1:创建流对象,建立数据存放文件
FileWriter fw=new FileWriter("Test.txt");
2:调用流对象的写入方法,将数据写入流
fw.write("TEXT");
3:调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中
fw.flush();
4:关闭流资源,关闭之前会刷新一次内部的缓冲中的数据
fw.close();
注意事项:
1:使用FileWriter fw=new FileWriter("Test.txt")创建对象是,在创建对象是,传入参数true,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。如:FileWriter fw = new FileWriter("Test.txt",true);
2:由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中,需要对IO异常进行try处理;
3:java是通过调用系统资源完成数据的读写,所以数据读写完毕,一定要记得关闭资源。
通过下面一段代码演示以下用字符流创建文件:
<span style="font-size:12px;">/* IO异常的处理方式。 */ import java.io.*; class FileWriterDemo2 { public static void main(String[] args) { FileWriter fw = null; try { fw = new FileWriter("demo.txt"); fw.write("abcdefg"); } catch (IOException e) { throws new RuntimeException("写入文件失败"); } finally { try { if(fw!=null) fw.close(); } catch (IOException e) { throws new RuntimeException("关闭写入流失败"); } } } } </span>
字符流读取文件:
1:建立一个流对象,将已存在的一个文件加载进流;
FileReader fr = new FileReader(“Test.txt”);
2:创建一个临时存放数据的数组;
char[] ch = new char[1024];
3:调用流对象的读取方法将流中的数据读入到数组中;
fr.read(ch);、4:关闭流资源;
fr.close();
注意:读取文件时,必须保证文件存在,否则会出现异常;
通过下面一段代码演示以下用字符流读取文件:
<span style="font-size:12px;">import java.io.*; class FileReaderDemo2 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("demo.txt"); try { fr= new FileReader("demo.txt"); char[] buf = new char[1024]; int num = 0; while((num=fr.read(buf))!=-1) { System.out.println(new String(buf,0,num)); } } catch (IOException e) { throws new RuntimeException("读取文件失败"); } finally { try { if(fe!=null) fr.close(); } catch (IOException e) { throws new RuntimeException("关闭读取流失败"); } } } } </span>
字符流缓冲区:BufferedWriter、BufferedReader
缓冲区的出现提高了对数据的读写效率;
缓冲区要结合流才能使用;
在流的基础上对流的功能进行了增强。
缓冲区的原理:缓冲区中封装了数组,将数据写进数组,再一次性取出。
写入流缓冲区:该缓冲区提供了一个跨平台的换行符,newLine();
读取流缓冲区:该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。当返回null时,表示读到文件末尾。readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。
下面通过一段代码演示以下缓冲区的运用:
<span style="font-size:12px;">import java.io.*; class CopyTextByBuf { public static void main(String[] args) { BufferedReader bfr=null; BufferedWriter bfw=null; try { //为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。 bfr=new BufferedReader(new FileReader("BufferedReaderDemo.java")); //为了提高效率。加入缓冲技术。将字符写入流对象作为参数传递给缓冲对象的构造函数。 bfw=new BufferedWriter(new FileWriter("Buffer_Copy.txt")); String leng=null; while ((leng=bfr.readLine())!=null) { bfw.write(leng); bfw.newLine(); bfw.flush(); } } catch (IOException e) { throw new RuntimeException("读写失败"); } finally { try { if(bfr!=null) bfr.close(); } catch (IOException e) { } try { if(bfw!=null) bfw.close(); } catch (IOException e) { } } } } </span>
装饰设计模式当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。装饰类通常会通过构
造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
如以下代码:
<span style="font-size:12px;">class Person { public void chifan() { System.out.println("吃饭"); } } class SuperPerson { private Person p ; SuperPerson(Person p) { this.p = p; } public void superChifan() { System.out.println("开胃酒"); p.chifan(); System.out.println("甜点"); System.out.println("来一根"); } } class PersonDemo { public static void main(String[] args) { Person p = new Person(); SuperPerson sp = new SuperPerson(p); sp.superChifan(); } }</span>
装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。
字节流
概述:
基本操作与字符流类相同,但它不仅可以操作字符,还可以操作其他媒体文件。
常用的操作字节型的节点流:
操作文件:FileOutputStream,FileInputStream;
InputStream特有方法:
int available();返回文件中的字节个数
注:此方法可以用来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,会发出内存溢出异常,因为虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法时一定要小心,当文件不大时,可以使用。
通过下面一段代码演示一下available()方法的使用:
<span style="font-size:12px;">import java.io.*; class FileStream { public static void main(String[] args) throws IOException { readFile_3(); } public static void readFile_3()throws IOException { FileInputStream fis = null; try { fis = new FileInputStream("fos.txt"); int num = fis.available(); byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用在循环了。 fis.read(buf); System.out.println(new String(buf)); } catch (IOException e) { throw new RuntimeException("读取数据失败"); } finally { try { if(fis!=null) fis.close(); } catch (IOException e) { throw new RuntimeException("读取流关闭失败"); } } } public static void writeFile()throws IOException { FileOutputStream fos = null; try { fos = new FileOutputStream("fos.txt"); fos.write("abcde".getBytes()); } catch (IOException e) { throw new RuntimeException("写入数据失败"); } finally { try { if(fos!=null) fos.close(); } catch (IOException e) { throw new RuntimeException("写入流关闭失败"); } } } } </span>
字节流缓冲区:
同样是提高了字节流的读写效率。
read():会将字节byte型值提升为int型值,当返回-1是,就说明文件中的数据全部都被读出;因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。
write():会将int型强转为byte型,即保留二进制数的最后八位。byte类型的-1提升为int类型时还是-1。
下面我们模拟一个BufferedInputStream的实现过程:
<span style="font-size:12px;">import java.io.*; class MyBufferedInputStream { private InputStream in; private byte[] buf = new byte[1024*4]; private int pos = 0,count = 0; MyBufferedInputStream(InputStream in) { this.in = in; } //一次读一个字节,从缓冲区(字节数组)获取。 public int myRead()throws IOException { //通过in对象读取硬盘上数据,并存储buf中。 if(count==0) { count = in.read(buf); if(count<0) return -1; pos = 0; byte b = buf[pos]; count--; pos++; return b&255; } else if(count>0) { byte b = buf[pos]; count--; pos++; return b&0xff; } return -1; } public void myClose()throws IOException { in.close(); } } </span>
标准输入输出流与转换流
标准输入输出流:
System类中的字段:in,out。它们各代表了系统标准的输入和输出设备。默认输入设备是键盘,输出设备是显示器。
System.in的类型是InputStream;System.out的类型是PrintStream是OutputStream的子类FilterOutputStream 的子类。通过键盘录入一行数据并打印的过程,发现其实就是读一行数据的原理。也就是readLine方法。能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法。而键盘录入的read方法是字节流InputStream的方法。那么能不能将字节流转成字符流在使用字符流缓冲去的
readLine方法呢?这时就需要用到转换流了。
转化流:
InputStreamReader,OutputStreamWriter
转换流是字符流与字节流之间的桥梁,方便了字符流与字节流之间的操作
转换流的应用:字节流中的数据都是字符时,转成字符流操作更高效。
转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时, 需要用到转换流。
下面通过一段代码来演示一下标准输入输出流和转换流的使用:
<span style="font-size:12px;">import java.io.*; class TransStreamDemo { public static void main(String[] args) throws IOException { //获取键盘录入对象。 //InputStream in = System.in; //将字节流对象转成字符流对象,使用转换流。InputStreamReader //InputStreamReader isr = new InputStreamReader(in); //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader //BufferedReader bufr = new BufferedReader(isr); //键盘的最常见写法。 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //获取控制台对象。 //OutputStream out = System.out; //将字节流对象转成字符流对象,使用转换流。InputStreamWriter //OutputStreamWriter osw = new OutputStreamWriter(out); //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedWriter //BufferedWriter bufw = new BufferedWriter(osw); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(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(); } } </span>
流的基本应用小节
流是用来处理数据的。处理数据时,一定要先明确数据源,与数据目的地。数据源可以是文件,可以是键盘。数据目的地可以是文件、显示器或者其他设备。而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理,转换处理等。
File类
概述:
用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作 。File对象可以作为参数传递给流的构造函数。
File类常见方法:
1,创建。
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。
boolean mkdirs():创建多级文件夹。
2,删除。
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回falsel。
void deleteOnExit();在程序退出时删除指定文件。
3,判断。
boolean exists() :文件是否存在.
isFile():是否是文件;isDirectory();是否是文件夹;
isHidden();文件是否是隐藏;
isAbsolute();文件是否是绝对路径
4,获取信息。
getName():获取文件名
getPath():获取文件相对路径
getParent():该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。如果相对路径中有上一层目录那么该目录就是返回结果。
getAbsolutePath() 获取文件的绝对路径
long lastModified() 返回文件的最后一次修改时间
long length() 获取文件大小。
5、列出文件和文件过滤
static File[] listRoots();列出可用的文件系统根目录;
String[] list();出当前目录下所有文件,包括隐藏。调用list方法的file对象必须是封装了一个目录。该目录还必须存在。
String[]list(FilenameFilter filter);返回一个字符串数组,获取目录中满足指定过滤器的文件或目录,FilenameFilter:文件名过滤器,是一个接口,其中包含一个方法,accept(Filedir,String name),返回的是boolean型,对不符合条件的文件过滤掉。
File[] ListFiles(FilenameFilterfilter);返回抽象路径名数组,获取目录中满足指定过滤器的文件或目录。
下面通过一段小代码演示以下:
<span style="font-size:12px;">/* 需求:取出d:\\java\\day18目录下后缀名为java的文件。 */ import java.io.*; class FileDemo2 { public static void main(String[] args) { listDemo_2(); } public static void listDemo_2() { File dir = new File("d:\\java\\day18"); String[] arr = dir.list(new FilenameFilter() { public boolean accept(File dir,String name) { return name.endsWith(".java"); } }); for(String name : arr) { System.out.println(name); } } } </span>
IO包中的其他类
RandomAccessFile:
随机访问文件,自身具备读写的方法。通过skipBytes(int x),seek(int x)来达到随机访问。、
该类不是算是IO体系中子类。而是直接继承自Object。但是它是IO包中成员。因为它具备读和写功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。可
以通过getFilePointer获取指针位置,同时可以通过seek(int x)k改变指针的位置。其实完成读写的原理就是内部封装了字节输入流和输出流。通过构造函数可以看出,该
类只能操作文件。而且操作文件还有模式:只读r,,读写rw等。如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。如
果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。
通过一段代码演示一下:
<span style="font-size:12px;">import java.io.*; class RandomAccessFileDemo { public static void main(String[] args) throws IOException { writeFile(); readFile(); //System.out.println(Integer.toBinaryString(258)); } public static void readFile()throws IOException { RandomAccessFile raf = new RandomAccessFile("ran.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("ran.txt","rw"); raf.write("李四".getBytes()); raf.writeInt(97); raf.write("王五".getBytes()); raf.writeInt(99); raf.close(); } } </span>
Properties类:
Properties是hashtable的子类。也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。是集合中和IO技术相结合的集合容器。该对象的特点:可以用
于键值对形式的配置文件。那么在加载数据时,需要数据有固定格式:键=值。
Properties类常用方法:
1、设置
Object setProperty(String key,String value);设置键和值,调用Hashtable的方法put
2、获取
String getProperty(String key);指定key搜索value
Set<String> stringPropertyName();返回属性列表的键集,存入Set集合
3、加载流和存入流
void load(InputStream ism);从输入字节流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。
void load(Readerreader);从输入字符流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。
voidlist(PrintStream out);将属性列表输出到指定的输出流
void store(OutputStreamout,String comments);对应load(InputStream )将属性列表(键值对)写入输出流。comments属性列表的描述。
void store(Writerwriter, String comments);对应load(Reader)将属性列表(键值对)写入输出流。comments属性列表的描述。
练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。
<span style="font-size:12px;">import java.io.*; import java.util.*; class RunCount { public static void main(String[] args) throws IOException { Properties prop = new Properties(); File file = new File("count.ini"); if(!file.exists()) file.createNewFile(); FileInputStream fis = new FileInputStream(file); prop.load(fis); int count = 0; String value = prop.getProperty("time"); if(value!=null) { count = Integer.parseInt(value); if(count>=5) { System.out.println("您好,使用次数已到!请注册。"); return ; } } count++; prop.setProperty("time",count+""); FileOutputStream fos = new FileOutputStream(file); prop.store(fos,""); fos.close(); fis.close(); } }</span>
打印流:
PrintWriter与PrintStream;可以直接操作输入流和文件。
字节打印流:PrintStream
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
字符打印流:PrintWriter
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,字符输出流,Writer。
通过代码演示:
<span style="font-size:12px;">import java.io.*; class PrintStreamDemo { public static void main(String[] args) throws IOException { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true); String line = null; while((line=bufr.readLine())!=null) { if("over".equals(line)) break; out.println(line.toUpperCase()); //out.flush(); } out.close(); bufr.close(); } }</span>
序列流:SequenceInputStream
可以对多个流进行合并。合并多个流文件步骤:
1、创建集合,并将流对象添加进集合
2、创建Enumeration对象,将集合元素加入。
3、创建SequenceInputStream对象,合并流对象
4、创建写入流对象,FileOutputStream关联写入文件
5、利用SequenceInputStream对象和FileOutputStream对象读数据进行反复读写操作。
如下面代码:需求,将三个文本文件中的数据合并到一个文本文件中
<span style="font-size:12px;">import java.io.*; import java.util.*; class SequenceDemo { public static void main(String[] args) throws IOException { Vector<FileInputStream> v = new Vector<FileInputStream>(); v.add(new FileInputStream("c:\\1.txt")); v.add(new FileInputStream("c:\\2.txt")); v.add(new FileInputStream("c:\\3.txt")); Enumeration<FileInputStream> en = v.elements(); SequenceInputStream sis = new SequenceInputStream(en); FileOutputStream fos = new FileOutputStream("c:\\4.txt"); byte[] buf = new byte[1024]; int len =0; while((len=sis.read(buf))!=-1) { fos.write(buf,0,len); } fos.close(); sis.close(); } } </span>
操作对象:
ObjectInputStream与ObjectOutputStream;被操作的对象需要实现Serializable (标记接口);
ObjectOutputStream中的writeObject()方法可以写入对象,ObjectInputStream中的readObject()方法可以读取对象;
通过代码演示:
<span style="font-size:12px;">import java.io.*; class Person implements Serializable { public static final long serialVersionUID = 42L; private String name; transient int age; static String country = "cn"; Person(String name,int age,String country) { this.name = name; this.age = age; this.country = country; } public String toString() { return name+":"+age+":"+country; } } class ObjectStreamDemo { public static void main(String[] args) throws Exception { //writeObj(); readObj(); } public static void readObj()throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt")); Person p = (Person)ois.readObject(); System.out.println(p); ois.close(); } public static void writeObj()throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt")); oos.writeObject(new Person("lisi0",399,"kr")); oos.close(); } }</span>
操作基本上数据:
DataInputStream与DataOutputStream可以操作基本数据类型,通过下面代码演示:
<span style="font-size:12px;">import java.io.*; class DataStreamDemo { public static void main(String[] args) throws IOException { writeData(); readData(); } 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(9887.543); dos.close(); } } </span>
操作数组:
操作字节数组:
ByteArrayInputStream与ByteArrayOutputStream
ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行close关闭。
操作字符数组:
CharArrayReader与CharArrayWrite
CharArrayReader:在构造的时候,需要接收数据源,。而且数据源是一个字符数组。
CharArrayWrite: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字符数组,这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行close关闭。
通过代码演示:
<span style="font-size:12px;">import java.io.*; class ByteArrayStream { public static void main(String[] args) { ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int by = 0; while((by=bis.read())!=-1) { bos.write(by); } System.out.println(bos.size()); System.out.println(bos.toString()); } } </span>
操作字符数组的方式与字节数组雷同!
流的操作规律
到这里基本上把IO体系里的流对象学习完了,那么对于IO流有什么操作规律呢?
流操作的基本规律:最痛苦的就是流对象有很多,不知道该用哪一个。
通过三个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader;
目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘。键盘
目的设备:内存,硬盘,控制台。
例如以下需求:
将一个文本文件中数据存储到另一个文件中。复制文件。
源:因为是源,所以使用读取流。InputStream Reader
是不是操作文本文件。 是!这时就可以选择Reader
这样体系就明确了。
接下来明确要使用该体系中的哪个对象。 明确设备:硬盘。上一个文件。 Reader体系中可以操作文件的对象是 FileReader
是否需要提高效率:是!。加入Reader体系中缓冲区 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
目的:OutputStream Writer是否是纯文本。 是!Writer。
设备:硬盘,一个文件。 Writer体系中可以操作文件的对象FileWriter
是否需要提高效率:是!。加入Writer体系中缓冲区 BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);