<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------</span>
概述
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
Reader Writer
1)创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。
2)调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。
第一种方式:读取单个字符。第二种方式:通过字符数组进行读取。
3)读取后要调用close方法将流资源关闭。
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args)
{
//创建一个文件读取流对象,和指定名称的文件相关联。
//要保证该文件是已经存在的,如果不存在,会发生FileNotFoundException异常
FileReader fr = null;
try
{
fr = new FileReader("demo.txt");
//调用读取流对象的read方法。
//read():一次读一个字符,而且会自动往下读。
/*
int ch1 = fr.read();
System.out.println("ch1="+(char)ch1);
int ch2 = fr.read();
System.out.println("ch2="+(char)ch2);
int ch3 = fr.read();
System.out.println("ch3="+(char)ch3);
int ch4 = fr.read();
System.out.println("ch4="+(char)ch4);
*/
/*
while (true)
{
int ch = fr.read();
if (ch==-1)
break;
System.out.println("ch="+(char)ch);
}
*/
//优化代码
int ch = 0;
while ((ch = fr.read()) != -1)
{
System.out.print((char)ch);
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if (fr!=null)
{
fr.close();
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
import java.io.*;
class FileReaderDemo2
{
public static void main(String[] args)
{
FileReader fr = null;
try
{
fr = new FileReader("demo.txt");
//定义一个字符数组,用于存储读到的字符。
//该read(char[])方法返回的是读到的字符的个数。
char[] arr = new char[1024];
//再优化
int num = 0;
while ((num = fr.read(arr)) != -1)
{
System.out.print(new String(arr,0,num));
}
//优化
/*
while (true)
{
int num = fr.read(arr);
if(num==-1)
break;
//System.out.println("num="+num);
System.out.print(new String(arr,0,num));
}
*/
//取出文本文件中的所有字符
/*
int num1 = fr.read(arr);
System.out.println("num1="+num1);
System.out.println(new String(arr));
int num2 = fr.read(arr);
System.out.println("num2="+num2);
System.out.println(new String(arr));
int num3 = fr.read(arr);
System.out.println("num3="+num3);
System.out.println(new String(arr));
int num4 = fr.read(arr);
System.out.println("num4="+num4);
System.out.println(new String(arr));
*/
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if (fr!=null)
{
fr.close();
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
字符流的读取操作:
a、创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。且该目录下如果已有同名文件,则同名文件将被覆盖。其实该步就是在明确数据要存放的目的地。
b、调用write(String s)方法,将字符串写入到流中。
c、调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中。
d、调用close()方法,关闭流资源。但是关闭前会刷新一次内部的缓冲数据,并将数据刷新到目的地中。
close()和flush()区别:
flush()刷新后,流可以继续使用;
而close()刷新后,将会关闭流,不可再写入字符流。
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个FileWriter对象,该对象一被初始化就必须要有明确被操作的文件。
//而且该文件会被创建到指定的目录下。如果该目录下已有重名文件,将被覆盖。
//其实该步骤就是在明确数据要存放的目的地。
FileWriter fw = new FileWriter("demo.txt");
//调用write方法,将字符串写入到流中。
fw.write("abcde");
//刷新流对象中的缓冲中的数据,将数据刷到目的地中。
//fw.flush();
//fw.write("\n\thaha");
//fw.flush();
//关闭流资源,在关闭之前会刷新一次内部缓冲中的数据。
//将数据刷到目的地中。
//和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.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();、
示例:通过缓冲区复制一个文本文件。
/*
练习:
通过缓冲区复制一个.java文件
*/
import java.io.*;
class CopyTestBuffered
{
public static void main(String[] args) throws RuntimeException
{
String oldName = "F:\\javazx\\19\\CopyTestBuffered.java";
String newName = "F:\\javazx\\19\\测试文件_生成.txt";
copyFileBuf(oldName,newName);
}
public static void copyFileBuf(String oldFileName,String newFileName)
{
/*四个引用
FileWriter fw = null;
BufferedWriter bufw = null;
FileReader fr = null;
BufferedReader bufr = null;
*/
//两个引用
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
/*
fr = new FileReader(oldFileName);
bufr = new BufferedReader(fr);
fw = new FileWriter(newFileName);
bufw = new BufferedWriter(fw);
*/
bufr = new BufferedReader(new FileReader(oldFileName));
bufw = new BufferedWriter(new FileWriter(newFileName));
String line = null;
while ((line = bufr.readLine()) != null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("读写错误");
}
finally
{
try
{
if (bufr != null)
{
bufr.close();
}
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
try
{
if (bufw != null)
{
bufw.close();
}
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
装饰设计模式
1、简述
当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。
2、特点
装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
3、装饰和继承的区别:
1)装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。
2)装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
3)从继承结构转为组合结构。
注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。
示例:上面讲到的MyBufferedReader的例子就是最好的装饰设计模式的例子。
<span style="font-size:14px;">/*
字符流:
FileReader
FileWriter
BufferedReader
BufferedWriter
字节流:
InputStream
OutputStream
需求:
想要操作图片数据,这时就要用到字节流。
*/
import java.io.*;
class FileStreamDemo
{
public static void main(String[] args) throws IOException
{
writeFile();
//readFile_1();
//readFile_2();
readFile_3();
}
public static void readFile_1() throws IOException
{
FileInputStream fis = new FileInputStream("字节流读取测试.txt");
int ch = 0;
while ((ch = fis.read())!=-1)
{
System.out.println((char)ch);
}
fis.close();
}
public static void readFile_2() throws IOException
{
FileInputStream fis = new FileInputStream("字节流读取测试.txt");
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf))!=-1)
{
System.out.print(new String(buf,0,len));
}
fis.close();
}
public static void readFile_3() throws IOException
{ //available()获取文件中的字节数,数据太大不建议使用,会发生内存溢出。
FileInputStream fis = new FileInputStream("字节流读取测试.txt");
//int num = fis.available();
byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区,不用再循环了
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}
public static void writeFile() throws IOException
{
FileOutputStream fos = new FileOutputStream("字节流输出测试.txt");
fos.write("abcdefg\r\nabcdefg".getBytes());
fos.close();
}
}</span>
字节流缓冲区
同样是提高了字节流的读写效率。
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.*;
class MyBuffered
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
class MyBufferedInputStream
{
private InputStream in = null;
private byte[] buf = new byte[1024];
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);
pos = 0;
if(count<0)
return -1;
byte b = buf[pos];
count--;
pos++;
return b&0xff;
}
else if (count>0)
{
byte b = buf[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
public void myClose() throws IOException
{
in.close();
}
}
/*
11111111 11111111010101011001101010101010110
byte:-1 ---> int:-1;
00000000 00000000 00000000 11111111 255
11111111 11111111 11111111 11111111
11111111 -->提升为了int类型 那不还是-1吗?是-1的原因是因为8个1前面补得是1导致的。
那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。
怎么补0呢?
11111111 11111111 11111111 11111111
&00000000 00000000 00000000 11111111
--------------------------------------
00000000 00000000 00000000 11111111
*/
/*
class MyBufferedOutputStream
{
private OutputStream os = null;
MyBufferedOutputStream(OutputStream os)
{
this.os = os;
}
}
*/
通过自定义的字节流缓冲区进行复制mp3操作
/*
演示mp3的复制,通过缓冲区。
BufferedOutputStream
BufferedInputStream
*/
import java.io.*;
class CopyMp3
{
public static void main(String[] args) throws IOException
{
String oldName = "F:\\javazx\\19\\Ylvis - The Fox.mp3";
String newName = "F:\\javazx\\19\\Ylvis - The Fox_new.mp3";
long start = System.currentTimeMillis();
copy_2(oldName,newName);
long end = System.currentTimeMillis();
System.out.println((end-start)+"ms");
}
//自制字节流的缓冲区
public static void copy_2(String oleFileName, String newFileName) throws IOException
{
MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream(oleFileName));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream(newFileName));
int by = 0;
while ((by = bufis.myRead()) != -1)
{
bufos.write(by);
}
bufis.myClose();
bufos.close();
}
//通过字节流的缓冲区完成复制。
public static void copy_1(String oleFileName, String newFileName) throws IOException
{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream(oleFileName));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream(newFileName));
int by = 0;
while ((by = bufis.read()) != -1)
{
bufos.write(by);
}
bufis.close();
bufos.close();
}
}
IO异常的处理方式。
全部步骤:
重点
在try外面创建FileWriter对象,并为空,在try内赋值。
close方法要在finally中执行,因为无论有没有异常抛出,都要关闭资源。
在close时也要try,catch,并且要进行对象是否为空的判断。