目录
BufferedInputStream和BufferOutputStream 带缓冲区的输入输出流(内置字节数组)拷贝
IO流概述及其分类
1、概念:
IO流用来处理设备之间的数据传输。
Java对数据的操作是通过流的方式。
Java用于操作流的类都在IO包中。
流按流向分为两种:输入流,输出流。
流按操作类型分为两种:
Ⅰ.字节流:字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的。(举例,照片就是字节数据)
Ⅱ.字符流:字符流只能操作纯字符数据,比较方便。(字符就是文字)
2、IO流常用父类:(四个最顶层的超类)
Ⅰ.字节流的抽象父类:
InputStream,OutputStream。不能用来创建对象。java.io包下,使用需要导包。
InputStream类概述:
public abstract class InputStream extends Object implements Closeable,此抽象类是表示字节输入流的所有类的超类。
需要定义InputStream子类的应用程序必须总是提供返回下一个输入字节的方法。
OutputStream类概述:
public abstract class OutputStream extends Object implements Closeable, Flushable,此类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。
需要定义OutputStream子类的应用程序必须始终提供至少一种可写入一个输出字节的方法。
Ⅱ.字符流的抽象父类:
Reader,Writer。java.io包下,使用需要导包。
Reader类概述:
public abstract class Ready extends Object implements Readable, Closeable,用于读取字符流的抽象类。子类必须实现的方法只有read(char[], int, int)和close()。但是,多数子类将重写此处(api中)定义的一些方法,以提供更高的效率和/或其他功能。
Writer类概述:
public abstract class Writer extends Object implements Appendable, Closeable, Flushable,写入字符流的抽象类。子类必须实现的方法仅有write(char[], int, int)、flush()和close()。但是,多数子类将重写此处(api中)定义的一些方法,以提供更高的效率和/或其他功能。
3、IO程序书写:
使用前,导入IO包中的类。
使用时,进行IO异常处理。io流操作的是硬盘和内存之间的关系,硬盘有可能不存在,就会出现异常,所以需要异常处理。
使用后,释放资源。将数据流通的“管道”关掉。
FileInputStream类
FileInputStream类概述:
public class FileInputStream extends InputStream。相当于一个“管子”,从内存怼到硬盘的文件上,再对文件进行操作(举例,读数据)。
FileInputStream从文件系统中的文件获取输入字节。什么文件可用取决于主机环境。
FileInputStream用于读取诸如图像数据的原始字节流。要读取字符串,请考虑使用FileReader。
java.io包下,使用需要导包。
FileInputStream类构造方法:
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定。其实就是将一个路径封装成file对象。
FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径名name指定。直接读路径也行。
FileInputStream类方法:
public int read() 从此输入流中读取一个数据字节。(一次读取一个字节)
演示:
import java.io.FileInputStream;
import java.io.IOException;
public class Demo1_FileInputStream {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("xxx.txt"); //创建流对象(开启了流),并且关联了xxx.txt 文件可能找不到,抛异常。
/* int x = fis.read(); //文件可能不可读,抛异常。 从硬盘上读取一个字节。
System.out.println(x); //97,'a'在硬盘上是以97的二进制形式去存储的。我们看到的是码表将97翻译成了'a'
int y = fis.read();
System.out.println(y); //98,每read一次,指针便向后移动一个字节。
int z = fis.read();
System.out.println(z); //99
int a = fis.read();
System.out.println(a); //-1 文件的结束标记是-1。文件一共有三个有效字节,-1不是有效字节。有了文件的结束标记,好处是 读的时候,只要不是-1,就可以不断地读。可以作为循环的结束条件。
*/
int b;
while((b = fis.read()) != -1) { //优化读的格式
System.out.println(b); //指针自动后移
}
fis.close(); //关流,释放资源。
}
}
问题:read()方法读取的是一个字节,为什么返回是int,而不是byte?
答:因为字节输入流可以操作任意类型的文件,比如图片、音频,视频等。这些文件底层都是以二进制形式的存储的。
如果每次读取都返回byte,有可能在读到中间的时候遇到1111 1111,则1111 1111是byte类型的-1。(举例:0001 0100 0010 0100 0100 0001 1111 1111 0000 0000 byte类型-1的原码1000 0001 反码1111 1110 补码1111 1111)我们的程序是遇到-1就会停止不读了,后面的数据就读不到了。
所以在读取的时候用int类型接收每个字节都补24个0,如果1111 1111在其前面补上24个0凑足4个字节(0000 0000 0000 0000 0000 0000 1111 1111),那么byte类型的-1就变成int类型的255了。这样可以保证整个数据读完,而结束标记的-1就是int类型,不用补24个0了。
write方法在写入的时候会将前面24个零自动砍去,保留最后一个字节,所以写入的时候一样会保证数据的原样性。
FileOutputStream类
FileOutputStream类概述:
public class FileOutputStream extends OutputStream。
文件输出流是用于将数据写入File或一个FileDescriptor的输出流。文件是否可用或能否可以被创建取决于底层平台。特别是某些平台一次只允许一个FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造函数(方法)将失败。
FileOutputStream用于写入诸如图像数据之类的原始字节的流。对于写入字符流,请考虑使用FileWriter 。
java.io包下,使用需要导包。
FileOutputStream类构造方法:
FileOutputStream(File file) 创建一个向指定File对象表示的文件中写入数据的文件输出流。
FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的文件输出流。
FileOutputStream(File file, boolean append) 创建一个向指定File对象表示的文件中写入数据的文件输出流。append追加,所以该方法不清空原文件,在原文件内容后追加新内容。
FileOutputStream(String name, boolean append) 创建一个向具有指定名称的文件中写入数据的文件输出流。
FileOutputStream类方法:
public void write(int b) 将指定的字节写入此文件输出流。(一次写出一个字节)虽然是int类型,但会自动去前三个八位,留最后一个八位,将该字节写出。(写入,写出相对而言,不要混淆:写入硬盘,从内存写出,一个意思)
演示:
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo2_FileOutputStream {
public static void main(String[] args) throws IOException {
//创建字节输出流对象。如果没有,就自动创建一个。
FileOutputStream fos = new FileOutputStream("yyy.txt"); //读的时候文件必须存在,写的时候不用。最主要的是数据承载在哪里,读的时候数据在目标文件里,写的时候数据在我们自己手里。所以,写的时候如果不存在会帮我们创建该文件,但前提是路径必须存在(相对路径不用担心,肯定存在,代表当前项目)。
fos.write(97); //虽然写出的是一个int数,但是写到文件上的是一个字节。会自动去除前三个八位。
fos.write(98);
fos.write(99);
fos.close(); //关流。
}
}
案例演示:FileOutputStream的构造方法写出数据如何实现数据的追加写入。
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo2_FileOutputStream {
public static void main(String[] args) throws IOException {
/*FileOutputStream fos = new FileOutputStream("yyy.txt"); //不但创建字节输出流,如果文件不存在,还帮助我们创建yyy.txt。 如果文件存在,会将原文件的内容清空,以便后续操作(举例:再次写入)。
fos.write(97);
fos.write(98);
fos.write(99);*/
FileOutputStream fos = new FileOutputStream("yyy.txt", true); //如果想续写,就在第二个参数传true。
fos.write(100);
fos.close(); //关流。
}
}
io流核心代码,之后接围绕此拓展
FileInputStream fis = new FileInputStream("demo.jpg"); //创建输入流对象,关联一个路径(必须存在)
FileOutputStream fos = new FileOutputStream("copy.jpg"); //创建输出流对象,关联一个路径(可以存在)
int b;
while((b = fis.read()) != -1) { //在不断地读取每一个字节
fos.write(b); //将每一个字节写出
}
fis.close(); //关输入流,释放资源
fos.close(); //关输出流,释放资源
拷贝图片、音频文件(逐个字节拷贝)
演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo3_Copy {
public static void main(String[] args) throws IOException {
// copy_picture();
FileInputStream fis = new FileInputStream("四级 List 1.mp3"); //创建输入流对象,关联四级 List 1.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3"); //创建输出流对象,关联copy.mp3
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
}
public static void copy_picture() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("demo.jpg"); //创建输入流对象,关联demo.jpg
FileOutputStream fos = new FileOutputStream("copy.jpg"); //创建输出流对象,关联copy.jpg
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
fis.close();
fos.close();
}
}
弊端:字节流一次读写一个字节。读一次,写一次(共操作两倍字节数的次数),复制音频,效率太低,所以开发中不推荐。
字节数组拷贝(大数组)
public int read(byte[] b) 从此输入流中将最多b.length个字节的数据读入一个byte数组中。(一次读取一个字节数组)
public void write(byte[] b) 将b.length个字节从指定byte数组写入此文件输出流中。(一次写出一个字节数组)
public int available() 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。(获取读取的文件所有的字节个数)
演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo3_Copy {
public static void main(String[] args) throws IOException {
//第二种拷贝
FileInputStream fis = new FileInputStream("四级 List 1.mp3"); //创建输入流对象,关联四级 List 1.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3"); //创建输出流对象,关联copy.mp3
// int len = fis.available(); //获取四级 List 1.mp3所有的字节个数
// System.out.println(len);
byte[] arr = new byte[fis.available()]; //创建与文件大小一致的字节数组
fis.read(arr); //将文件中的所有字节读到字节数组(内存)中
fos.write(arr); //将字节数组中的字节数据全部写到文件(硬盘)中
fis.close();
fos.close();
}
}
弊端:jvm虚拟机虚拟在内存上,有可能会导致内存溢出,所以开发中不推荐。
字节数组拷贝(小数组)
public void write(byte[] b, int off, int len) 将指定byte数组中从偏移量off开始的len个字节写入此文件输出流。(写出有效的字节个数)参数:b-数据 off-数据中起始的偏移量(数组中的索引) len-要写入的字节数
演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo4_ArrayCopy {
public static void main(String[] args) throws IOException {
//第三种拷贝,定义小数组
// demo_read();
FileInputStream fis = new FileInputStream("xxx.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
byte[] arr = new byte[2];
int len;
while((len = fis.read(arr)) != -1) { //有 有效的字节个数,返回字节个数;无 有效的字节个数,则返回-1。 读的方式
// fos.write(arr); //abcb 多了个b
fos.write(arr, 0, len); //abc 写的方式
}
fis.close();
fos.close();
}
public static void demo_read() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("xxx.txt");
byte[] arr = new byte[2];
int a = fis.read(arr); //将文件上的字节读取到字节数组中。
System.out.println(a); //2 读到的有效字节个数
for (byte b : arr) {
System.out.println(b); //97 98 第一次获取到文件上的a和b
}
System.out.println("------------------------------------");
int c = fis.read(arr);
System.out.println(c); //1 读到的有效字节个数
for (byte b : arr) {
System.out.println(b); //99 98 第二次获取到文件上的c和 99覆盖了97,但98没有被覆盖
}
fis.close();
}
}
定义小数组的标准格式
演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo4_ArrayCopy {
public static void main(String[] args) throws IOException {
//第三种拷贝,定义小数组,推荐。
FileInputStream fis = new FileInputStream("xxx.txt"); //xxx.txt文件内容abc
FileOutputStream fos = new FileOutputStream("yyy.txt");
byte[] arr = new byte[1024 * 8]; //小数组一般是1024的整数倍。
int len;
while((len = fis.read(arr)) != -1) { //如果()内不写arr,则会返回294(97+98+99)个空,实际看到一堆小方框。因为fis一次读一个字节,先读到a,将97赋给了len,fos从零开始将97个空字节写到文件里(arr创建后一堆0),相当于写了97个0到文件中。再然后fis读到b,重复之前步骤,文件再增加98个空字节。fis读到c,再增加99个空字节。第四次再读,程序返回-1,所以程序结束了,文件一共294个空字节。
fos.write(arr, 0, len); //定义了字节数组。拷贝的时候,一定要先读到字节数组里,写才能再从字节数组中写出去。 总结:如果忘记加arr,返回的就不是读取的字节个数,而是字节的码表值。
}
fis.close();
fos.close();
}
}
BufferedInputStream和BufferOutputStream 带缓冲区的输入输出流(内置字节数组)拷贝
缓冲思想:
字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果。java本身在设计的时候,也考虑到了这样的设计思想(装饰设计模式字符流中讲解),所以提供了字节缓冲区流。
在调用BufferedInputStream和BufferedOutputStream时,都会创建出字节数组 byre[] arr = new byte[8192];整个操作都在内存中完成,内存的运算效率远远高于硬盘,这样减少了到硬盘读写的次数,内存与硬盘之间读写的效率就大大提高。
BufferedInputStream概述:
public class BufferedInputStream extends FilterInputStream,BufferEdInputStream为另一个输入流添加一些功能,即缓冲输入以及支持mark和reset方法的能力。在创建BufferEdInputStream时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark操作记录输入流的某个点,reset操作使得从包含的输入流中获取新字节之前,再次读取自最后一次mark操作后读取的所有字节。(api)java.io包下,使用需要导包。
BufferedInputStream是怎么读的?
BufferedInputStream内置了一个缓冲区(数组)。
从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个字节,存到缓冲区中,返回给程序一个字节(缓冲区中的数据一个一个的给定义的局部变量b,即每次返回一个)。
程序再次读取时,就不用找文件了,直接从缓冲区中获取。直到缓冲区中所有的都被使用过,才重新从文件中读取8192个。
BufferedInputStream构造方法:
BufferedInputStream(InputStream in) 创建一个BufferedInputStream并保存其参数,即输入流in,以便将来使用。对InputStream进行了一次包装,提高拷贝效率。不能直接new InputStream对象,因为InputStream是抽象的,可以给个子类对象(举例,FileInputStream)。
BufferedInputStream(InputStream in, int size) 创具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流in,以便将来使用。
BufferedOutputStream概述:
public class BufferedOutputStream extends FilterOutputStream,该类实现了缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
BufferedInputStream是怎么写的?
BufferedOutputStream也内置了一个缓冲区(数组)。
程序向流中写出字节时,不会直接写到文件里,先写到缓冲区中(局部变量b一个一个字节的往缓冲区里装)。直到缓冲区写满8192个字节,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。
BufferedInputStream构造方法:
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
演示:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo5_BufferCopy {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("四级 List 1.mp3"); //创建输入流对象,关联四级 List 1.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3"); //创建输出流对象,关联copy.mp3
BufferedInputStream bis = new BufferedInputStream(fis); //创建缓冲区对象,对输入流践行包装让其变得更加强大。以后,参数直接传匿名对象即可。
BufferedOutputStream bos = new BufferedOutputStream(fos); //创建缓冲区对象,对输出流践行包装让其变得更加强大。以后,参数直接传匿名对象即可。
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
//只关包装后的流即可。包装后的流被关掉了,原来的流也被关掉了。
bis.close();
bos.close();
//BufferedInputStream底层源代码
/* private static int DEFAULT_BUFFER_SIZE = 8192; //默认缓冲区大小8192
protected volatile byte buf[]; //刚一创建BufferedInputStream对象时,就有了字节数组 byre[] arr = new byte[8192];
*/
//BufferedOutputStream底层源代码
/* protected byte buf[];
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
*/
}
}
小数组的读写和带Buffered的读取哪个更快?
定义小数组如果是8192个字节大小和Buffered比较的话,定义小数组会略胜一筹。因为读和写操作的是同一个数组(读进数组,再从数组中写出),而Buffered操作的是两个数组(字节读进数组,数组传给变量,变量将字节装进新数组)。
flush和close方法的区别
flush()方法:
用来刷新缓冲区的,刷新后可以再次写出。
close()方法:
用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后就不能再写出了。
因为变量b不是每次往写出的缓冲区搬都能正好够8192个字节,比如搬运内存文件到了剩余的字节数<8192个时,这些字节会存在缓冲区上。这时调用close()刷新缓冲区,将剩余的这些字节从缓冲区释放到硬盘文件上,然后再关闭流。
演示:flush和close方法的区别。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo5_BufferCopy {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("四级 List 1.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.mp3"));
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bos.flush(); //具备刷新的功能,刷新完可以继续写。 适用于需要实时刷新的程序。简单拷贝的程序是不需要用此方法的,缓冲区每装满8192个字节,自动写入硬盘。只有最后一次缓冲区字节数不足8192时,close()会自动刷新,所以用不上flush()。
bis.close();
bos.close(); //具备刷新的功能,在关闭流之前,就会先刷新一次缓冲区。将缓冲区的字节全都刷新到文件上,再关闭流。 刷新后就不能再写了。
}
}
字节流读写中文
String类构造方法:
String(byte[] bytes) 通过使用平台的默认字符集解码指定的byte数组,构造一个新的String。(将字节数组转换成字符(即中文))
String(byte[] bytes, int offset, int length) 通过使用平台的默认字符集解码指定的byte子数组,构造一个新的String。((将字节数组从offset开始,转length个字节转换成字符)
字节流读取中文的问题:
字节流在读中文的时候有可能会读到半个中文,造成乱码。
字节流写出中文的问题:
字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组(举例:fos.write("你大爷的快点!".getBytes());)。写出回车换行:fos.write("\r\n".getBytes());
演示:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo6_Chinese {
public static void main(String[] args) throws IOException {
// demo_readchinese();
FileOutputStream fos = new FileOutputStream("zzz.txt");
fos.write("你大爷的快点!".getBytes()); //将字符串转成字节数组。
fos.write("\r\n".getBytes());
fos.close();
}
public static void demo_readchinese() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("yyy.txt");
byte[] arr = new byte[3];
int len;
while((len = fis.read(arr)) != -1) {
System.out.println(new String(arr, 0, len)); //从零开始,转有效的字节个数。
}
fis.close();
}
}
总结:字节流读中文容易出问题,写中文有些麻烦,必须先.getBytes()。
流的标准处理异常代码1.6版本及其以前
演示:try finally嵌套。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* io流是用来操作底层的,所以不能用try catch。try catch相当于将问题给隐藏了,没有向上抛。我们需要将问题抛出去。
* 直接throws出去是不太负责任的,如果程序执行到创建fos对象的时候没有找到指定的路径,出现异常。那么程序终止,后续代码不执行,则打开的fis流就没有关掉。
* 所以不能光抛,还得加个finally,但finally不能单独使用,必须和try一起使用。那我们便把会出问题的代码都放在try里,把关流的操作都放在finally里。
*/
public class Demo7_TryFinally {
public static void main(String[] args) throws IOException {
FileInputStream fis = null; //局部变量在使用之前必须进行赋值。如果不初始化,且文件不存在,则没有被赋值的局部变量调用close()是不被允许的。下同。
FileOutputStream fos = null;
try {
fis = new FileInputStream("xxx.txt"); //文件可能不存在
fos = new FileOutputStream("yyy.txt"); //路径可能不存在
int b;
while((b = fis.read()) != -1) { //文件可能不可读
fos.write(b); //文件可能不可写
}
}finally {
//finally一定会执行,但如果程序运行到关输入流时出问题了(举例,关输入流的时候数据库崩溃,服务器宕机等),程序一样会终止,导致输入流没关成。建议,能关一个是一个,输入流没关成,建议把输出流关掉。
try {
if(fis != null) //做判断的原因是如果文件不存在,且不做判断,则会报空指针异常。如果没有文件,则流没有被开启,就不需要关闭流。所以值不为null时,再关流。下同。
fis.close(); //关流,可能关不掉
}finally { //这么做目的是能关一个尽量关一个,所以用try finally进行嵌套。
if(fos != null)
fos.close(); //关流,可能关不掉
}
}
}
}
流的标准处理异常代码1.7版本
演示:try close。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo8_TryClose {
public static void main(String[] args) throws IOException {
//流自动关闭
try( //FileInputStream和FileOutputStream可以创建对象,因为二者具备自动关闭的功能。
FileInputStream fis = new FileInputStream("xxx.txt");
FileOutputStream fos = new FileOutputStream("yyy.txt");
// MyClose mc = new MyClose(); //不允许创建对象,因为不具备自动关闭的功能。
MyClose mc = new MyClose(); //类实现AutoCloseable接口后,不报错了,可以创建对象。该类也具备了自动关闭的功能,自动调用close()。close()没有被调用也运行了,说明他被自动调用了。
){
int b;
while((b = fis.read()) != -1) {
fos.write(b);
}
}
//FileInputStream的曾祖父接口AutoCloseable的源代码
/* void close() throws Exception; 说明能自动关闭的流都实现了AutoCloseable接口*/
}
}
class MyClose implements AutoCloseable{
public void close() {
System.out.println("我关啦~");
}
}
原理:在try()中创建的流对象必须实现了AutoCloseable这个接口。如果实现了,在try后面的{}中的读写代码执行后,就会自动调用流对象的close方法将流关掉。
图片加密
演示:给图片加密。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test1 {
public static void main(String[] args) throws IOException {
//加密
// BufferedInputStream bis = new BufferedInputStream(new FileInputStream("demo.jpg"));
// BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.jpg"));
//解密
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("copy.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy2.jpg"));
int b;
while((b = bis.read()) != -1) {
bos.write(b ^ 123); //一个数被另一个数异或两次,还等于他本身。异或一次相当于加密,再异或一次相当于解密。异或的这个数相当于密钥。
}
bis.close();
bos.close();
}
}
总结:将写出的字节异或上一个数,这个数就是密钥。解密的时候再次异或这个数即可。
拷贝文件
演示:在控制台录入文件的路径,将文件拷贝到当前项目下。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
/**
* 在控制台录入文件的路径,将文件拷贝到当前项目下。
* 分析:
* 1.定义方法对键盘录入的路径进行判断,如果是文件就返回。
* 2.在主方法中接收该文件。
* 3.读和写该文件。
*
*/
public class Test2 {
public static void main(String[] args) throws IOException {
File file = getFile(); //获取文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName())); //写到当前项目下,且名字不改变
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
}
/**
* 定义一个方法获取键盘录入的文件路径,并封装成File对象返回。
* 1.返回值类型File。
* 2.参数列表 无。
*/
public static File getFile() {
Scanner sc = new Scanner(System.in); //创建键盘录入对象
System.out.println("请输入一个文件的路径:");
while(true) {
String line = sc.nextLine(); //接收键盘录入的路径
File file = new File(line); //封装成File对象,并对其进行判断
if(!file.exists()) {
System.out.println("您录入的文件路径不存在!请重新录入:");
}else if(file.isDirectory()) {
System.out.println("您录入的是文件夹路径,请重新录入:");
}else {
return file; //返回文件,终止方法,整个方法弹栈。循环失去意义。
}
}
}
}
录入数据拷贝到文件
演示:将键盘录入的数据拷贝到当前项目下的text.txt文件中,键盘录入数据当遇到quit时就退出。
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
/**
* 将键盘录入的数据拷贝到当前项目下的text.txt文件中,键盘录入数据当遇到quit时就退出。
* 分析:
* 1.创建键盘录入对象。
* 2.创建输出流对象,关联text.txt文件。
* 3.定义无限循环。
* 4.遇到quit退出循环。
* 5.如果不是quit,就将内容写出到text.txt中。
* 6.关闭流。
*/
public class Test3 {
public static void main(String[] args) throws IOException {
//1.创建键盘录入对象。
Scanner sc = new Scanner(System.in);
System.out.println("请输入数据:");
//2.创建输出流对象,关联text.txt文件。
FileOutputStream fos = new FileOutputStream("text.txt");
//3.定义无限循环。
while(true) {
String line = sc.nextLine(); //将键盘录入的数据存储在line中
//4.遇到quit退出循环。
if("quit".equals(line)) {
break;
}
//5.如果不是quit,就将内容写出到text.txt中。
fos.write(line.getBytes()); //字符串写出必须转换成字节数组
fos.write("\r\n".getBytes());
}
//6.关闭流。
fos.close();
}
}