流的分类:字节流、字符流
字节流两个基类:(×××InputStream ×××OutputStream)
FileInputStream BufferedInputStream
FileOutputStream BufferedOutputStream
字符流两个基类:(×××Reader ×××Writer)
FileWriter BufferedWriter
FileReader BufferedReader
(注:后缀名是父类名,前缀名是该流对象的功能。)
复制文件的原理和代码
原理:
首先用一个读取流对象和一个文件进行关联,然后用一个写入流对象作为目地的,
为了把读取流中的文件传输到目的地流对象中,我们就提供了一个字符数组,
为了关联这个数组,所以读取流对象有一个read()方法与这个字符数组进行关联,
同理,写入流对象也有一个write()方法与这个字符数组进行关联,
这样两个流对象就相连接了,而这个字符数组就相当于一个中转站。
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
对文本文件进行复制。将c盘的文件复制到d盘中。
原理:其实就是一个最简单的读写过程。
从c盘源,读取数据,并将读到的数据,写入到目的d盘中。
*/
public class CopyTextFileTest {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
//1,创建一个字符读取流读取与源数据相关联。
fr = new FileReader("demo.txt");
//2,创建一个存储数据的目的地。
fw = new FileWriter("copyDemo.txt");
//3,创建一个字符数组将读取流对象和写入流对象相连接。
char[] buf = new char[1024];
//4,每次读取的长度不一样,所以定义一个变量.
int len = 0;
//5,用循环读取文件中的数据
while((len= fr.read(buf)) != -1) //判断是否读取完没
fw.write(buf,0,len); //为了只读取有效的数据
} catch (Exception e) {
}finally{
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
读取字符流对象的两种方式
第一种读取方式 一次读一个字符
//1,创建一个文件读取流对象,和指定名称的文件相关联
//2,要保证该文件时已经存在的。如果不存在,会发生异常。FileNotFoundException
FileReader fr = new FileReader("Demo.txt");
//3,调用读取流的方法,read方法一次读一个字符,而且会自动往下读。
int line=0; //read返回时int型的数,即返回的是字符的ascII表对应的数字
while ((line=fr.read())!=-1)
{
sop((char)ch);
}
第二种读取方式
//1,创建一个文件读取流对象,和指定名称的文件相关联
//2,要保证该文件时已经存在的。如果不存在,会发生异常。FileNotFoundException
FileReader fr = new FileReader("Demo.txt"); //把Demo.txt中的数据读到控制台
//3,定义一个字符数组,用于存储独到的字符该read(char[]) //返回的是读到字符的个数
char[] buf = new char[1024];
int len=0;
while((len=fr.read(buf))!=-1) //把读到的字符暂时存到buf数组中
{
sop("num="+num+"...."+new String(buf,0,len));
}
缓冲区
(BufferedInputStream BufferedOutputStream BufferedWriter BufferedReader)
缓冲区内部将数组进行封装。变成对象后,方便于对缓冲区的操作,提高效率,并提供了对文本便捷操作的方法:readLine,newLine。
自定义缓冲区,MyBufferedReader
import java.io.IOException;
import java.io.Reader;
/*
模拟一个缓冲区
基于已有的缓冲区思想,我们可以从源读取用read方法。
我们的缓冲区,应该是一个更高效的read读取方法。
*/
public class MyBufferTest extends Reader{
private Reader r;
private char[] buf = new char[1024];
private int count = 0,pos = 0;
public MyBufferTest(Reader r){
this.r = r;
}
/*
一次从缓冲区中取一个
*/
public int myRead() throws IOException {
//1,首先判断缓冲区中是否有数据,如果没有就从源中去拿。
if(count == 0){
count = r.read(buf);
pos = 0;
}
//2,当缓冲区中没数据了且源中也没有数据时,count自减1小于0时就返回-1结束.
if(count < 0)
return -1;
//3,如果以上都不满足,那么从缓冲区中写入一个字符到新的文件中。
char ch = buf[pos];
pos++;
count--;
return ch;
}
/*
按照文本特点,提供一个特有的操作文本的方法。
一次读取一行文本,只要是到行结束符之前的文本即可。
原理:
就是从缓冲区中取出数据,并存储到一个临时容器中。
如果取到了回车符,就将临时容器中的数据转成字符串返回。
*/
public String myReadLine() throws IOException{
//1,定义一个临时容器,进行临时存储
StringBuilder sb = new StringBuilder();
//2,定义一个变量,接收读取到的字符,也就是转成ask码表后的一个int型数字
int ch = 0;
while((ch = myRead()) != -1){
//3,当读取到\r时,直接跳出本次循环
if(ch == '\r')
continue;
//4,当读取到\n时,直接跳出当前循环
if(ch == '\n')
return sb.toString();
//5,当都没有读取到时,就将这些数据存储到临时容器中。
sb.append((char)ch);
}
//6,当临时容器中的长度不等于0时,就输出字符。
if(sb.length() != 0)
return sb.toString();
return null;
}
public void close() throws IOException {
}
public int read(char[] arg0, int arg1, int arg2) throws IOException {
return 0;
}
}
装饰设计模式
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
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();
//p.chifan();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}
File类
作用:1、用来将文件或者文件夹封装成对象
2、方便对文件与文件夹的属性信息进行操作。
3、File对象可以作为参数传递给流的构造函数。
File对象基本使用
创建:
boolean createNewFile():创建一个新的空文件,不过该文件已存在,就不会创建
boolean mkdir():创建指定的目录
boolean mkdirs():创建指定的目录已经父目录
删除:
boolean delete():删除文件或目录
void deleteOnExit():虚拟机结束时,会自动删除指定的文件或目录
获取:
String getAbsolutePath():绝对路径
String getPath():相对路径
String getParent():返回此路径名父目录的路径,如果没有则返回null
String getName():返回此路径名表示的文件或目录的名称
long length():返回此路径名表示的文件的长度
long lastModified():返回此文件最后一次被修改的时间
判断:
boolean exists():判断此路径名表示的文件或目录是否存在
boolean isFile():判断此路径名表示的文件或目录是否是一个标准的文件
boolean isDirectory():判断此路径名表示的文件或目录是否是一个目录
IO中的其他功能流对象:
打印流:
PrintStream:
特点:
1、构造函数可接收File对象,字符串路径,字节输出流。意味着打印目的可以有很多。
2、该对象具备特有的方法 打印方法print println,可以任何类型的数据。
3、特有的print方法可以保持任意类型的数据表现形式的原样性,将数据输出到目的地。
而对于OutputStream父类中的write方法,是将数据的最低字节写出去。
PrintWriter:
特点:
1、当操作的数据是字符时,可以选择PrintWriter,比PrintStream要方便。
2、它的构造函数可以接收File对象,字符串路径,字节输出流,字符输出流。
3、构造函数中,如果参数是输出流,那么可以通过指定另一个参数true完成自动刷新,该true对println方法有效。
序列流:
SequenceInputStream:
特点:
1,将多个字节读取流合并成一个读取流,将多个源合并成一个源,操作起来更方便;
2,需要的枚举接口可以通过Collections.enumeration(collection)。
IO流的操作规律总结:
1,明确体系:
数据源:InputStream ,Reader
数据汇:OutputStream,Writer
2,明确数据:因为数据分两种:字节,字符。
数据源:是否是纯文本数据呢?
是:Reader
否:InputStream
数据汇:
是:Writer
否:OutputStream
到这里就可以明确具体要使用哪一个体系了。
剩下的就是要明确使用这个体系中的哪个对象。
3,明确设备:
数据源:
键盘:System.in
硬盘:FileXXX
内存:数组。
网络:socket
数据汇:
控制台:System.out
硬盘:FileXXX
内存:数组
网络:socket
4,明确额外功能:
1,需要转换?是,使用转换流。InputStreamReader OutputStreamWriter
2,需要高效?是,使用缓冲区。Buffered
3,需要其他?
1, 复制一个文本文件。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:硬盘上的一个文件。 FileReader
目的:硬盘上的一个文件。FileWriter
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要额外功能吗?
需要,高效,使用buffer
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
2, 读取键盘录入,将数据存储到一个文件中。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:键盘,System.in
目的:硬盘,FileWriter
InputStream in = System.in;
FileWriter fw = new FileWriter("a.txt");
4,需要额外功能吗?
需要,因为源明确的体系时Reader。可是源的设备是System.in。
所以为了方便于操作文本数据,将源转成字符流。需要转换流。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("a.txt");
需要高效不?需要。Buffer
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));
3, 读取一个文本文件,将数据展现在控制台上。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:硬盘文件,FileReader。
目的:控制台:System.out。
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;
4,需要额外功能?
因为源是文本数据,确定是Writer体系。所以为了方便操作字符数据,
需要使用字符流,但是目的又是一个字节输出流。
需要一个转换流,OutputStreamWriter
FileReader fr = new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要高效吗?需要。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
4, 读取键盘录入,将数据展现在控制台上。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:键盘:System.in
目的:控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,需要额外功能吗?
因为处理的数据是文本数据,同时确定是字符流体系。
为方便操作字符数据的可以将源和目的都转成字符流。使用转换流。为了提高效率,使用Buffer
BufferedReader bufr =new BufferedReader(new InputStreamReader(Systme.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));