· IO流用来处理设备之间的数据传输
· java对数据的操作是通过流的方式
· 流按操作数据分为2种:字节流和字符流
· 流按流向分为:输入流和输出流
· 字节流的抽象基类:InputStream,OutputStream
· 字符流的抽象基类:Reader,Writer
· 注:由这4个类派生出来的子类名称都是以父类名作为子类后缀名。如InputStream的子类FileInputStream;Reader的子类FileReader
· 导入IO包中的类
· 进行IO异常处理
· 在finally中对流进行关闭
· 创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。而且该文件会被创建到指定目录下:
FileWriter writer = new FileWriter("demo.txt");//如果该目录下已有同名文件,将被覆盖
FileWriter writer = new FileWriter("demo.txt",true);//这种方法不覆盖已经存在文件的内容,并且从文件的末尾处续写内容
· 调用流对象的写入方法,将数据写入流:writer.write("hello world");
· 刷新该流的缓冲:writer.flush();
· 关闭流资源,并将流中的缓冲数据刷新到目的地中:writer.close();
· 完整代码:
FileWriter writer = null;//建立引用,全局使用 try { writer = new FileWriter("demo.txt"); writer.write("hello word"); } catch (IOException e) { System.out.println(e.toString()); } finally { try { if(writer!=null) writer.close(); } catch (Exception e2) { System.out.println(e2.toString()); } }
· 建立一个流对象,将已存在的一个文件加载进流。并保证此文件是存在的:FileReader reader = new FileReader("demo.txt");
· 读取方式1:int read():read()方法会读一个字符并自动往下读,当读到结束标识时会返回-1
int ch = 0; while((ch=reader.read())!=-1) { System.out.print((char)ch); }· 读取方式2:int read(char[] buf):将字符读进数组:
FileReader reader = new FileReader("demo.txt"); char[] chs = new char[1024];//一般定义为1024的整数倍 int num = 0;//每次reader.read(char[] chs)的返回值都是读到的字符数 while((num=reader.read(chs))!=-1) { System.out.print(new String(chs,0,num));//通过String的构造方法,创建这个字符串 }· 完整代码:
Reader reader = null; try { reader = new FileReader("demo.txt"); char[] chs = new char[1024]; int num = 0; while((num = reader.read(chs))!=-1) { System.out.print(new String(chs,0,num)); } } catch (IOException e) { System.out.println(e.toString()); } finally { try { if(reader!=null) reader.close(); } catch (Exception e2) { System.out.println(e2.toString()); } }
· 在定义文件路径时,可以用"\\"或者"/"
· 在创建一个文件时,如果目录下有同名文件将被覆盖
· 在读取一个文件时,必须保证该文件已经存在,否则会出异常
· 缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象
· 对应类:BufferedReader和BufferedReader。
· 缓冲区要结合流才可以使用
· 在流的基础上对流的功能进行了增强
· BufferedWriter提供了一个特有的方法,newLine(),里面提供了换行符
· BufferedReader提供了String readLine()的方法,当返回null时,代表读到行末,不包含换行符
· 对原有的类进行了功能的改变和增强
· 装饰设计模式的基本格式:装饰类一般都是通过构造函数接收被装饰的类,再提供比被装饰类更强的功能
· 它与继承有什么不同?
答:装饰模式比继承要更加灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有类是相同的,只不过提供了更强功能
。所以装饰类和被装饰类通常是属于一个体系的。
· 了解BufferedReader的原理:
答:BufferedReader的构造函数可以接收一个Reader的子类,内部定义了一个数组,调用Reader的read()方法,一个个的读取数据并存放到数组中。当读到行末时,把这个字符数组转换成字符串打印出来。
· 基本操作与字符流相同 InputStream和OutputStream;因为是对字节最小单位进行操作,所以不可以使用flush()方法。
· 不仅可以操作字符,还可以操作其它媒体文件
· 例:copy一个jpg文件:用FileInputStream读取jpg文件,再通过FileOutputStream写出
import java.io.*; class CopyPic { public static void main(String[] args) { FileOutputStream fos = null; FileInputStream fis = null; try { fos = new FileOutputStream("c:\\2.bmp"); fis = new FileInputStream("c:\\1.bmp"); byte[] buf = new byte[1024]; int len = 0; while((len=fis.read(buf))!=-1) { fos.write(buf,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("写入关闭失败"); } } } }
· 同样是提高了字节流的读写效率:BufferedInputStream和BufferedOutputStream
· 模拟一个BufferedIInputStream,BufferedInputStream是把数据一个个的存到缓冲区里面,再从缓冲区里取的
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(); } }
· InputStreamReader和OutputStreamWriter
· 转换流的由来
1. 字符流与字节流之间的桥梁
2. 方便了字符流与字节流之间的操作
· 转换流的应用:字节流中的数据都是字符时,转换成字符流操作更高效,而且构造方法中可以指定编码表
代码示例:标准输入输出
//键盘录入 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //控制台输出 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.flush(); bufw.newLine(); } bufr.close(); bufw.close();
· System类中的字段:in;out
· 它们各代表了系统标准的输入和输出设备
· 默认输出设备是键盘,输出设备是控制台
· System.in的类型是InputStream
· System.out的类型是PrintStream。是OutputStream的子类,FilterOutputStream的子类
· System中提供了setIn和setOut的方法,改变标准的输入输出流
· 流是用来处理数据的
· 处理数据时,一定要先明确数据来源,与数据目的地(数据汇)
· 数据源可以是文件,也可以是键盘
· 数据目的地可以是文件、显示器或者其他设备
· 而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等
· 用来对文件或者文件夹封装成对象
· 方便对文件与文件夹的属性信息进行操作
· File对象可以作为参数传递给流的构造函数
· File类中的常用方法
//1.创建: boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一初始化就会创建文件,如果文件存在则会覆盖 boolean mkdir():创建目录 boolean mkdirs():可以创建多级目录 //2.删除: boolean delete():删除,删除失败返回false void deleteOnExit():退出时删除 //3.判断: boolean exists():文件是否存在 boolean isFile():是否是文件 boolean isDirectory():是否是目录 boolean isHidden():是否隐藏 boolean isAbsolutely():是否是绝对路径 //4.获取信息 String getName():返回文件名字 String getPath(): String getAbsolutePath():返回绝对路径 File getAbsoluteFile():返回绝对路径并封装成对象 String getParent():如果获取是相对路径,则会返回null。如果相对路径中有上一层目录,那么该目录就是返回结果 long lastModified():返回最后一次修改时间 boolean renameTo():相当于剪切并可以重命名 String[] list():返回该目录下所有的文件和文件夹的名称 File[] listFiles():list里面还可以传递一个文件或者文件名过滤器
· 函数自己调用自己
· 注意:
1. 递归时一定要明确结束条件
2. 要注意递归次数,尽量避免内存溢出
· 应用场景:当某一个功能要重复使用时
· 例:列出一个文件夹下所有的子文件夹以及文件
public static void showDir(File dir) { if(!dir.exists() || !dir.isDirectory()) throw new RuntimeException("目录不存在或者传入路径不是目录"); System.out.println(dir); File[] dirs = dir.listFiles(); for(File file : dirs) { if(file.isDirectory()) showDir(file);//当file还是一个文件夹时,调用自己的方法继续递归 System.out.println(file); } }Properties:
· Properties是hashtable的子类。也就是说它具备集合的特点,而且它里面存储的键值对都是字符串
· 是集合和IO技术相结合的集合容器
· 该对象的特点,可以用于创建键值对形式的配置文件
//Properties中常见方法: String getProperty(String key):获取属性信息 void list(PrintStream out):将属性列表输出到指定的输出流 void list(PrintWriter out) void load(Reader reader):从输入流中读取属性列表(键值对) void load(InputStream inStream) setProperty(String key, String value):设置属性 store(OutputStream out, String comments):设置属性,并加载到输出流中
· RandomAccessFile
该类不算是IO体系中子类,而是直接继承自Object。
但是它是IO包中成员,因为它具备读和写的功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。可以通过getFilePointer获取指针位置。同时可以通过seek改变指针的位置。还可以通过skipBytes(int n)方法跳过n个字节。
其实完成读写的原理就是内部封装了字节输入流和输出流。
该类只能操作文件,并且操作文件还有固定的mode。
当mode为r只读时,new对象只会去查找已经存在的文件;当mode为rw时,如果文件不存在则会自动创建一个文件。
这个类通过设置指针,可以从文件的指定位置的开始读写和修改数据。
· 管道流:
PipedInputStream和PipedOutputStream:
输入输出可以直接进行连接,通过结合线程使用。不建议2个对象使用同一个线程,因为这样可能死锁。
通过PipedInputStream内的void connect(PipedOutputStreaem src)连接。
· 打印流:PrintWriter与PrintStream:
该流提供了打印方法,可以将各种数据类型的数据都原样打印。
构造函数可以接收的对象:
1,file对象 File
2,字符串路径 String
3,字节输出流 OutputStream
4,字符输出流 InputStream
参数中,也可以传入boolean autoFlush值,如果为true,println等方法自动刷新
· 序列流:SequenceInputStream:
将多个读取流合并成为一个读取流。反过来思考,切割一个文件就是把文件输出到不同的流中。
· 操作对象:ObjectInputStream与ObjectOutputStream:
直接操作对象的流,被操作的对象需要实现Serializable接口。该接口会根据对象的成员生成一个UID。也可以自己在对象中定义一个这个序列号ANY-ACCESS-MODIFIER static final long serialVessionUID = 42L。
并且,对象中的被static/transient修饰的成员,都不可以被序列化。
这个流就是对对象进行持久化本地存储。
· 操作基本数据类型:DataInputStream DataOutputStream
可以用于操作基本数据类型的流对象。