IO:输入就是input,输出是output,输入和输出就是某个方向流动的数据流。
Java对数据的操作是通过流的方式。输入和输出是相对于内存而言,将外设中的数据读取到内存中就是输入 ,将内存的数据写入到外设中就是输出。
流按操作数据分为两种:字节流和字符流。
流按流向分为:输入流和输出流。
字符流的由来:
其实就是:字节流读取指定文字字节数据后,不直接操作,而是先查指定的编码表。获取对应的文字。
再对文字进行操作。简单说:字节流+编码表。
字节流的抽象基类:
InputStream,OutPutStre+am。
字符流的抽象基类:
Reader,Writer。
由这四个类派生出来的子类名称都是以父类名作为后缀。
而且子类的前缀名就是该对象的功能。
例:InputStream的子类FileInputStream
Reder的子类FileReader
如果要操作文字数据,优先考虑字符流。
为了提高效率加入了缓冲区,缓冲区的设计使用了装饰设计模式。
字符流缓冲区:
BufferdeWriter:
newLine();
BufferddReader:
readLine();
缓冲区的实现原理:
自定义的读取缓冲区。其实就是模拟一个BufferedReader。
分析:缓冲区中就是封装了一个数组,
并对外提供了更多的方法对数组进行访问。
其实这些方法最终操作的都是数组的指针。
缓冲的原理:
其实就是从源中获取一起数据装进缓冲区中。
再从缓冲区中不断的取出一个一个数据。
在此次取完后,在从源中继续取一批数据放进缓冲区。
当源中的数据取光时,用-1作为结束标记。
-----------------------------------------------------------------
import java.io.FileReader;
import java.io.IOException;
/*
自定义的读取缓冲区。其实就是模拟一个BufferedReader。
分析:缓冲区中就是封装了一个数组,
并对外提供了更多的方法对数组进行访问。
其实这些方法最终操作的都是数组的指针。
缓冲的原理:
其实就是从源中获取一起数据装进缓冲区中。
再从缓冲区中不断的取出一个一个数据。
在此次取完后,在从源中继续取一批数据放进缓冲区。
当源中的数据取光时,用-1作为结束标记。
*/
public class MyBufferReader8 {
private FileReader r;
// 定义一个数组作为缓冲区。
private char[] buf = new char[1024];
// 定义一个指针用于操作这个数组中的元素,当操作完最后一个元素后,指针应该归零。
private int pos = 0;
// 定义一个计数器用于记录缓冲区中的数据个数,当数据减到0,就从源中继续获取数据到缓冲区中。
private int count = 0;
public MyBufferReader8(FileReader r) {
this.r = r;
}
public int myRead() throws IOException{
// 1.计数器为0的时候,从源中获取一批数据到缓冲区中.
if(count == 0){
count = r.read(buf);
// 每次获取数据到缓冲区后,角标归零。
pos = 0;
}
if(count<0)
return -1;
char ch = buf[pos];
pos++;
count--;
return ch;
}
public String myReadLine() throws IOException{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=myRead())!=-1){
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
if(sb.length()!=0)
return sb.toString();
// 将从缓冲区中读到的字符,存储到缓存行数据的缓冲区。
sb.append((char)ch);
}
return null;
}
public void myClose() throws IOException{
r.close();
}
}
-----------------------------------------------------------------
流操作的基本规律:
想要知道开发时用到哪些对象,通过四个明确即可确定。
明确一:明确目的和源
源:InputStream、Reader
目的:OutputStream、Writer
明确二:明确数据是否是纯文本
源:是纯文本:Reader。不是:InputStream
目的:是纯文本Writer。不是:OutputStream
明确三:明确具体的设备
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
键盘:System.out
内存:数组
网络:Socket流
明确四:是否需要其他功能。
是否需要高效:是,加上Buffer
是否需要对象序列化:ObjectInputStream
是否需要操作基本数据类型:DateInputStream,DateOutputStream
-----------------------------------------------------------------
LineNumberReader:
两个方法:可以读取行号和设置行号。
例:
FileReader fr = new FileReader("c:\\IO.txt");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(10);
while((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
-----------------------------------------------------------------
转换流:
InputStreamWriter:字节流通向字符流的桥梁。
OutputStreamWriter:字符转成字节的桥梁。
-----------------------------------------------------------------
File类:用来将文件或者文件夹封装成对象。
方便对文件或文件夹的属性进行操作。
构造函数:File.separator
File对象常见方法:
1.获取:
1.1获取文件名称。
String getName();
1.2获取文件路径.
StringgetPath();
StringgetAbsolutePath();
1.3获取文件大小.
long length();
1.4获取文件修改时间。(判断修改时间,来加载修改后的新文件。)
longlastModified();
2.创建和删除。
booleancreateNewFile();文件不存在就创建,文件存在不创建。
booleandelete();
deleteOnExit();在退出时删除。
File dir = new File("abc\\d\\e");//注意:File对象封装的是e文件夹。
boolean b = dir.mkdir();
boolean b = dir.mkdir();//创建多级目录
boolean b1 = dir.delete();//必须要由内向外删,目录里有内容删除不掉。
3.判断。
文件是否存在。
booleanexists();
booleanisDirectory();//应该先判断是否存在,不存在返回flase
boolean isFile();
4.重命名。
File f1 = new File("c:\\0.txt");
File f1 = new File("d:\\9.txt");
f1.renamenTo(f2);//剪切文件到d盘,并改名为9.txt。
5.获取系统根目录和容量
获取所有盘符:
File[] files = File.listRoots();
for(File file : files){
System.out.println(file);
}
getFreeSpace();
getTotalSpace();
6.获取目录内容。
File file = new File("c:\\");
String[] names = file.list();
for(String name : names){
System.out.println(name);
}
打印的是c盘当前目录下所有文件以及文件夹的名称,包括隐藏文件。
调用list方法的File对象中封装的必须是目录,否则会发生空指针异常。
如果目录存在但是没有内容会返回一个数组,但是长度为0.
7.过滤器。
File dir = new File("c:\\");
String[] names = dir.list(new FilterByJava());
for(String name: names){
System.out.println(name);
}
递归:
函数自身直接或者间接的调用到了自身。
一个功能在被重复使用个,并每次使用时参与运算的结果和上一次调用有关。
这时就可以用递归来解决问题。
注意:递归一定要明确条件,否则容易栈内存溢出。
注意递归的次数。次数太多会栈溢出。
小练习:
删除一个带内容的目录。
需要从里往外删,先进行深度遍历。
import java.io.File;
public class DeleteDirTest3 {
public static void main(String[] args){
File dir = new File("d:\\dir");
deleteDir(dir);
}
private static void deleteDir(File dir) {
File[] files = dir.listFiles();
for(File file : files){
if(file.isFile()){
file.delete();
}else{
deleteDir(file);
}
}
dir.delete();
}
-----------------------------------------------------------------
Map集合子类Properties和io流结合。
Map:
|--HashTable
|--Properties(没有泛型)
Properties集合特点:
1.集合中的键和值都是字符串对象。
2.集合中的数据可保存在流中,或从流中获取数据。
通常该集合用于操作以键值对形式存在的配置文件。
Properties集合和流结合使用:
store方法:(持久化)
prop.store(FileOutputStream fos, "name+age");//不能传中文信息。
Load方法:
prop.load(FileInputStream fis);
-----------------------------------------------------------------
序列流:对多个流进行合并。字节流。
构造函数接收枚举类型。
SequenceInputStream(Enumeration en)
使用方法:
将流添加进ArrayList集合,后使用Collections的静态方法。
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for (int i = 1; i < 4; i++) {
al.add(new FileInputStream(i+".txt"));
}
Enumeration<FileInputStream> e =Collections.enumeration(al);
-----------------------------------------------------------------
PrintStream:
1.提供了打印方法,可以对多中数据类型值进行打印。并保持数据的表现形式。
2.不抛出IOException
构造函数,接收三种类型的值:
1.字符串路径。
2.File对象。
3.字节输出流。
PrintWriter:字符打印流。
构造函数,接收四种类型的值:
1.字符串路径。
2.File对象。
3.字节输出流。
4.字符输出流。
注意:输出流能自动刷新。
PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);
-----------------------------------------------------------------
能操作对象的字节流:
ObjectInputStream:
ObjectOutputStream:
要序列化的对象要实现Serializable接口,启用序列化功能。
对象的默认序列化机制写入的内容是,对象的类,类签名,及非瞬态和非静态字段的值。
数据加transient(短暂的)修饰后不会写入,变为瞬态数据。
静态和瞬态都不会写入。
Serializable:用于给被序列化的类加入ID号。
用于判断类和对象是否是同一版本。
建议所有课序列化类都显示声明serialVersionUID值。
定义方式:
private static final long serialVersionUID = 12345L;
-----------------------------------------------------------------
RandomAccessFile:
不是io体系中的子类。
特点:
1.该对象既能读又能写。
2.内部维护了一个大型 的byte可变长度数组,并通过指针可以操作数组中的元素。
3.可以通过getFilePointer方法获取指针位置,通过seek方法设置指针位置。
4.其实该对象就是将字节输入流和输出流进行了封装。
5.该对象的源或者目的只能是文件,通过构造函数就可以看出。
通常在多线程写入的时候使用
-----------------------------------------------------------------
操作基本数据类型:
DateInputStream,DateOutputStream:
readUTF()和writeUTF()方法,使用指定的utf-8修改版编码表
操作字节数组:
ByteArrayInputStream:
包含一个内部缓冲区。
ByteArrayInputStream(byte[] buf)
ByteArrayOutputStream:
实现了一个输出流,其中的数据被写入一个byte数组,缓冲区会随着数据的不断写入而自动增长。
可使用toByteArray()和toString()获取数据。关闭此流无效(对象没有调用资源,在内存中读写)。
writeTo(OutputStream out):将此byte数组输出流的全部内容写入到指定的输出流参数中,
和使用out.write(buf,o,len)调用该输出流的write方法效果一样。
ByteArrayInputStream bis = new ByteArrayInputStream("abcd".getBytes());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int ch = 0;
while((ch=bis.read())!=-1){
bos.write(ch);
}
System.out.println(bos.toString());
操作字符数组:
CharArrayReader(源是字符数组)与CharAraryWrite
操作字符串:
SrringReader(源是字符串)与StringWriter