-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
概述
1、IO流:即InputOutput的缩写。
2、特点:
1)IO流用来处理设备间的数据传输。
2)Java对数据的操作是通过流的方式。
3)Java用于操作流的对象都在IO包中。
4)流按操作数据分为两种:字节流和字符流。
5)流按流向分为:输入流和输出流。
注意:流只能操作数据,而不能操作文件。
3、IO流的常用基类:
1)字节流的抽象基流:InputStream和OutputStream
2)字符流的抽象基流:Reader和Writer
注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;如InputStream子类FileInputStream;Reader子类FileReader
(一)字符流
一、简述
1、字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。
2、字符流只用于处理文字数据,而字节流可以处理媒体数据。
3、既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。查看API,找到一个专门用于操作文件的Writer子类对象:FileWriter。 后缀是父类名。前缀名是流对象的功能。该流对象一被初始化,就必须有被操作的文件存在。
二、字符流的读写
1、写入字符流步骤
a、创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。且该目录下如果已有同名文件,则同名文件将被覆盖。其实该步就是在明确数据要存放的目的地。
b、调用write(String s)方法,将字符串写入到流中。
c、调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中。
d、调用close()方法,关闭流资源。但是关闭前会刷新一次内部的缓冲数据,并将数据刷新到目的地中。
close()和flush()区别:
flush()刷新后,流可以继续使用;
而close()刷新后,将会关闭流,不可再写入字符流。
注意:
1、其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。
2、文件的数据的续写是通过构造函数 FileWriter(Strings,boolean append),在创建对象时,传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。(windows系统中的文件内换行用\r\n两个转义字符表示,在linux系统中只用\n表示换行)
3、由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中,需要对IO异常进行try处理。
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
// 创建字符输出流对象
FileWriter fw = new FileWriter("a.txt");
/*
* 创建字符输出流对象做了几件事情? A:调用系统功能创建了文件 B:创建字符输出流对象 C:把字符输出流对象指向创建的文件
*/
// 调用写数据的功能
// public void write(String str)
fw.write("hello,io,我来了。祝你元旦快乐。");
/*
* 字符流 1字符 = 2字节 文件数据底层单位是字节,而我们现在是字符,所以它不能直接把数据写入文件。 把字符输出存储到缓冲区里面。
*/
// 刷新缓冲区
// public void flush()
fw.flush();
// 释放资源
// public void close()
fw.close();
/*
* 为什么要close()呢?
* 1:让流对象变成垃圾。
* 2:通知系统去释放和文件相关的资源。
*/
//code
}
}
2、读取字符流步骤
1)创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。
2)调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。
第一种方式:读取单个字符。第二种方式:通过字符数组进行读取。
3)读取后要调用close方法将流资源关闭。
import java.io.FileReader;
import java.io.IOException;
/*
* 字符输入流读取数据步骤:
* A:创建字符输入流对象
* B:调用读取数据功能,并显示
* C:释放资源
* 一次读取一个字符。
*/
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
// 创建字符输入流对象
// FileReader fr = new FileReader("a.txt");
FileReader fr = new FileReader("FileWriterDemo.java");
// 调用读取数据功能,并显示
int ch = 0;
while((ch=fr.read())!=-1){
System.out.print((char) ch);
}
// 释放资源
fr.close();
}
}
综合练习:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
* 需求:我要把项目路径下的FileWriterDemo.java复制到d:\\Copy.java中
*
* 数据源:
* 读取数据:
* FileWriterDemo.java -- Reader -- FileReader
*
* 目的地:
* 写入数据:
* d:\\Copy.java -- Writer -- FileWriter
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
// 封装数据源
FileReader fr = new FileReader("FileWriterDemo.java");
// 封装目的地
FileWriter fw = new FileWriter("d:\\Copy.java");
// 读取数据
// int ch = 0;
// while ((ch = fr.read()) != -1) {
// fw.write(ch);
// // fw.flush();
// }
char[] chs = new char[1024];
int len = 0;
while ((len = fr.read(chs)) != -1) {
//写入数据.
fw.write(chs,0,len);
}
// 释放资源
fw.close();
fr.close();
}
}
三、字符流的缓冲区——BufferedReader和BufferedWriter
1、缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象。即先将流对象初始化到构造函数中。
2、缓冲技术原理:此对象中封装了数组,将数据存入,再一次性取出。
3、写入流缓冲区BufferedWriter的步骤:
1)创建一个字符写入流对象。
如:FileWriter fw=newFileWriter("buf.txt");
2)为了提高字符写入流效率。加入缓冲技术。只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
如: BufferedWriter bufw =new BufferedWriter(fw);
3)调用write方法写入数据到指定文件
如:bufw.write("adfg");
记住,只要用到缓冲区,就要记得刷新。(关闭流同样会刷新,但为了排除意外事故,保证数据存在,建议写入一次就刷新一次)
如:bufw.flush();
4)其实关闭缓冲区,就是在关闭缓冲区中的流对象。
如: bufw.close();
小知识:BufferedWriter缓冲区中提供了一个跨平台的换行符:newLine();可以在不同操作系统上调用,用作数据换行。
如:bufw.newLine();
4、读取流缓冲区BufferedReader
该缓冲区提供了一个一次读一行的方法readLine,方便于堆文本数据的获取,当返回null时表示读到文件末尾。readLine方法返回的时候,只返回回车符之前的数据内容。并不返回回车符。
readLine方法原理:
无论是读一行。或者读取多个字符。其实最终都是在在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。
步骤:
1)创建一个读取流对象和文件相关联
如: FileReader fr=newFileReader("buf.txt");
2)为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲区对象的构造函数。
如: BufferedReader bufr=new BufferedReader(fr);
3)调用该缓冲区提供的readLine方法一行一行读取,如果到达文件末尾,则返回null
如: String s=bufr.readLine();
4)关闭流资源
如: bufr.close();、
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
* 用字符高效流复制文本文件。
*
* 数据源:
* FileOutputStreamDemo.java
* 目的地:
* Copy.java
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
// 封装数据源和目的地
BufferedReader br = new BufferedReader(new FileReader(
"FileOutputStreamDemo.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));
// 一次一个字符数组
char[] chs = new char[1024];
int len = 0;
while ((len = br.read(chs)) != -1) {
bw.write(chs, 0, len);
}
// 释放资源
bw.close();
br.close();
}
}
5、自定义BufferedReader:
原理:
可根据BufferedReader类中特有一行一行读取方法readLine()的原理,自定义一个类中包含相同功能的方法
步骤:
a、初始化自定义的类,加入流对象。
b、定义一个临时容器,原BufferedReader封装的是字符数组,此类中可定义一个StringBuilder的容器,最终可实现字符串的提取。
四、装饰设计模式
1、简述
当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。
2、特点
装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
3、装饰和继承的区别:
1)装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。
2)装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
3)从继承结构转为组合结构。
注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。
示例:上面讲到的MyBufferedReader的例子就是最好的装饰设计模式的例子
(五)字节流
一、概述
1、字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。
2、由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。
3、读写字节流:InputStream 输入流(读)
OutputStream 输出流(写)
4、为何不用进行刷流动作:
因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。所以可直接将字节数据写入到指定文件中。
5、InputStream特有方法:
int available();//返回文件中的字节个数
注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* 用字节流复制文本文件。
* 把项目路径下的FileOutputStreamDemo.java复制到d:\\Copy.java中。
*
* 数据源:
* FileOutputStreamDemo.java -- 读取数据 -- FileInputStream
* 目的地:
* FileOutputStreamDemo.java -- 写入数据 -- FileOutputStream
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
// 封装数据源
FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java");
// 封装目的地
FileOutputStream fos = new FileOutputStream("d:\\Copy.java");
byte[] bys = new byte[1024];
int len = 0;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
}
// 释放资源
fos.close();
fis.close();
}
}
二、字节流缓冲区
同样是提高了字节流的读写效率。
1、读写特点:
read():会将字节byte型值提升为int型值
write():会将int型强转为byte型,即保留二进制数的最后八位。
2、原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区。
2)循环这个动作,直到最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素。
3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增。
4)取出的时候,数组中的元素在减少,取出一个,就减少一个,直到减到0即元素取完。
5)当文件中的全部数据都被读取出时,read()方法就返回-1。
3、自定义读取字节流缓冲区
需求:根据字节流缓冲区的原理,自定义一个字节流缓冲区。
注意:
1、字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。
因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。
2、byte类型的-1提升为int类型时还是-1。原因:因为在bit8个1前面补的全是1导致的。如果在bit8个1前面补0,即可以保留原字节数据不变,又可以避免-1的出现。这时将byte型数据&0xff即255即可。
代码:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
* 需求:把d:\\么么哒.mp4复制到项目路径下x.mp3(x=1,2,3,4)
*
* @author Administrator
*/
public class CopyMP3 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
method();
long end = System.currentTimeMillis();
System.out.println("共耗时:" + (end - start) + "毫秒");
}
//高效字节流一次读写一个字节数组
private static void method4() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"d:\\么么哒.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("4.mp4"));
byte[] bys = new byte[1024];
int len = 0;
while((len=bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bos.close();
bis.close();
}
//基本字节流一次读写一个字节数组
private static void method3() throws IOException {
FileInputStream fis = new FileInputStream("d:\\么么哒.mp4");
FileOutputStream fos = new FileOutputStream("3.mp4");
byte[] bys = new byte[1024];
int len = 0;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
}
fos.close();
fis.close();
}
// 高效字节流一次读写一个字节
private static void method2() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"d:\\么么哒.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("2.mp4"));
int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bos.close();
bis.close();
}
// 基本字节流一次读写一个字节
private static void method1() throws IOException {
FileInputStream fis = new FileInputStream("d:\\么么哒.mp4");
FileOutputStream fos = new FileOutputStream("1.mp4");
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
}
(六) File类
一、概述
1、File类:文件和目录路径名的抽象表现形式
2、特点:
1)用来将文件或文件夹封装成对象
2)方便于对文件与文件夹的属性信息进行操作
3)File类的实例是不可变的;也就是说,一旦创建,File 对象表示的抽象路径名将永不改变
4)File对象可以作为参数传递给流的构造函数
二、File对象创建
方式一:
File f =new File("a.txt");
将a.txt封装成File对象。可以将已有的和未出现的文件或者文件夹封装成对象。
方式二:
File f2=newFile("c:\\abc","b.txt");
将文件所在目录路径和文件一起传入,指定文件路径。
方式三:
File d=new File("c:\\abc");
File f3=new File(d,"c.txt");
将文件目录路径封装成对象。再创建文件对象。降低了文件于父目录的关联性。
小知识:
File.separator表示目录分隔符,可以跨平台使用。相当于路径中的“\”(双斜杠\\在windows中表示表示转义后的分隔符,但是在linux系统中就不是)。
三、File类的常见方法
1、创建
booleancreateNewFile();
//在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立就创建文件。而且文件已经存在,会覆盖。
boolean mkdir();//创建文件夹,只能创建一级文件夹
例:
File dir=new File("abc");
dir.mkdir();//创建abc这个文件夹
boolean mkdirs();//创建多级文件夹
2、删除
boolean delete();
//删除文件或目录。文件存在,返回true;文件不存在或者正在被执行,返回false。
void deleteOnExit();//在程序退出时删除指定文件
3、判断
boolean canExecute();//是否是可执行文件
boolean exists();//文件是否存在
boolean isFile();//是否是文件
boolean isDirectory();//是否是文件夹
boolean isHidden();//是否是隐藏文件
boolean isAbsolute();//文件是否是绝对路径
记住:在判断文件对象是否是文件或者目录时,必须要判断该文件对象封装的内容是否存在。通过exists判断。
4、获取信息
String getName();//获取文件名
String getPath();
//获取文件的相对路径(即创建的对象传入的参数是什么就获取到什么)
String getParent();
//获取文件父目录。返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。如果相对路径中有上一层目录,那么该目录就是返回结果。
String 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();//返回一个抽象路径名数组,获取当前文件夹下的所有文件和文件夹
File[] ListFiles(FilenameFilterfilter);//返回抽象路径名数组,获取目录中满足指定过滤器的文件或目录。
四、递归
1、定义
当函数内每一次循环还可以调用本功能来实现,也就是函数自身调用自身。这种表现形式,或者编程手法,称为递归。
2、递归注意事项
a、限定条件。是来结束循环调用,否则是死循环。
b、注意递归的次数,尽量避免内存溢出。因为每次调用自身的时候都会先执行下一次调用自己的方法,所以会不断在栈内存中开辟新空间,次数过多,会导致内存溢出。
实例:
/*
* 求9的阶乘
* */
public class Jie {
public static void main(String[] args) {
int in = show(9);
System.out.println(in);
}
//用递归的方法
public static int show(int n) {
if (n == 1) {
return 1;
} else {
return n * show(n - 1);
}
}
}
(七)打印流
一、概述
1、打印流包括:PrintStream和PrintWriter
2、该流提供了打印方法,可将各种类型的数据都原样打印。
二、字节打印流:PrintStream
构造方法中可接收的参数类型:
1、File对象。File
2、字符串路径:String
3、字符输出流:OutputStream
三、字符串打印流:PrintWriter
构造方法中可接受的参数类型
1、File对象:File
2、字符串路径:String
3、字节输出流:OutputStream
4、字符输出流:Writer
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class PrintDemo {
public static void main(String[] args) throws IOException{
BufferedReader bw=new BufferedReader(new FileReader("f:\\b.txt"));
//打印流关联文件,自动刷新
PrintWriter pw=new PrintWriter(new FileWriter("f:\\copy.txt"),true);
String s=null;
while((s=bw.readLine())!=null){
pw.println(s);
}
}
}