字节(byte)流:InputStream、OutputStream 原生操作,不需要处理
字符(char)流:Reader、Writer通过转换来的,需要中间处理
所有流在使用后一定要关闭。(不然会占用白白内存)一般出现在finally代码块中关闭流,保证流无论是否产生异常一定会被关闭。
应用场景:
字符流用于文字处理,尤其是处理中文的时候处理较好,除此之外所有的流处理均使用字节流(磁盘数据存储、网络传输)
流操作模式:
- 取得终端对象
- 取得输入输出流
- 进行数据的输入与输出
- 关闭流
1.字节输出流
OutputStream 类的定义结构:
public abstract class OutputStream implements Closeable, Flushable
OutputStream类实现了Closeable,Flushable两个接口,这两个接口中的方法:
1.Closeable:
public void close() throws IOException;
2. Flushable:
public void flush() throws IOException;
OutputStream类中的其他方法:
3. 将给定的字节数组内容全部输出:
public void write(byte b[]) throws IOException
4. 将部分字节数组内容输出:
public void write(byte b[], int off, int len) throws IOException
5. 输出单个字节:
public abstract void write(int b) throws IOException;
由于OutputStream是一个抽象类,所以要想为父类实例化,就必须要使用子类。如果要进行文件的操作,可以使用FileOutputStream类来处理,这个类的构造方法如下:
- 接收File类(覆盖):
public FileOutputStream(File file) throws FileNotFoundException - 接收File类(追加):
public FileOutputStream(File file, boolean append)
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class OutputStreamTest {
public static void main(String[] args) throws IOException {
//1. 取得终端对象
File file = new File(File.separator + "F" + File.separator +
"Users" + File.separator+ "FWB" + File.separator
+ "Desktop" + File.separator + "OutputStreamTest.txt");
//有输出流以后不需要手动创建文件,只要路径正确会自动创建
//虽然文件可以自动创建,但是文件夹不行,需要手动创建
//2. 取得相应终端的输出流,
OutputStream out = new FileOutputStream(file);//覆盖模式
//当再次执行此方法会默认覆盖,只需将 FileOutputStream默认值设置为true就改成在后面增加的模式
//OutputStream out = new FileOutputStream(file,true);追加模式
//3. 将数据通过输出流输出
//getBytes(),将字符串以字节数组的形式返回
out.write("hello world".getBytes());
//4. 关闭流
out.close();
}
}
字节输出流优化:
由于write方法只支持字符数组,所以使用起来有些不方便,我们可以将OutputStream这个类做一个简单的二次封装。
思路:通过valueOf()方法将其他类型转换为String类,String类再通过getBytes()将字符串转换为字符数组。
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
class MyPrint{
private OutputStream out;
public MyPrint(OutputStream out){
this.out = out;
}
public void print(String str) throws IOException {
this.out.write(str.getBytes());
}
public void println(String str) throws IOException {
this.print(str + "\n");
}
public void print(double data) throws IOException {
this.print(String.valueOf(data));
}
public void println(double data) throws IOException {
this.print(data + "\n");
}
public void close() throws IOException {
this.out.close();
}
}
public class betterOut {
public static void main(String[] args) throws IOException {
//1. 取得终端对象
File file = new File(File.separator + "F" + File.separator + "Users" + File.separator
+ "fwb" + File.separator + "Desktop" + File.separator + "betterOut.txt");
MyPrint myPrint = new MyPrint(new FileOutputStream(file));
myPrint.print("乔奶奶好");
myPrint.println(10.2322);
myPrint.close();
}
}
注:在JDK1.7后追加了AutoCloseable接口,自动调用close方法
AutoCloseable接口必须处在try代码块才会自动调用close()
package fwb.inAndOut;
class Myclass implements AutoCloseable{
@Override
public void close() throws Exception {
System.out.println("自动");
}
public void print(){
System.out.println("显示调用");
}
}
public class AutoCloseableTest {
public static void main(String[] args) {
Myclass myclass = new Myclass();
//写在外面并不会自动追加
myclass.print();
System.out.println("---------------------");
//必须写在在try代码块中
try( Myclass myclass1 = new Myclass();) {
myclass1.print();
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:
2. 字节输入流
int read():读取一个字节
int read(byte b[]):一次读取多个字节并将数据存放到字节数组中,返回此次读取的长度。(可以理解为用勺子吃饭,勺子就是缓冲区,碗中的饭就是总大小)
- 返回b.length:此时要读取的数据大小大于缓冲区字节数组的大小(碗中剩余食物大于勺子容量)
- 返回读取个数:此时要读取的的数据小于缓冲区字节数组大小
(剩下的饭一勺子舀不完) - 返回 -1 :此时数据已经全部读取完毕
import java.io.*;
public class InputStreamTest {
public static void main(String[] args) {
//1. 取得终端对象
File file = new File(File.separator + "F" + File.separator + "Users" + File.separator
+ "FWB" + File.separator + "Desktop" + File.separator + "OutputStreamTest.txt");
//2. 取得输入流
InputStream in = null;
try {
in = new FileInputStream(file);
// 3.读取数据
int len = 0;
byte[] data = new byte[1024];//缓冲区
try {
len = in.read(data);//将读入的数据存放在data中
System.out.println("文件内容为:" + new String(data,0,len));//从0到有长度的地方即可
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
数据拷贝:
import java.io.*;
/**
* @program: Io
* @description: 使用字节输入输出流进行文件拷贝
* @author: fwb
* @create: 2019-07-31 19:27
**/
public class copyTest {
public static void main(String[] args) throws IOException {
//传进来的的文件必须是2个,一个输入一个输出
if (args.length != 2){
System.out.println("参数非法");
}
//源文件
File sourceFile = new File(args[0]);
//目标文件
File destFile = new File(args[1]);
InputStream in = new FileInputStream(sourceFile);
OutputStream out = new FileOutputStream(destFile);
//文件拷贝
copyFile(in,out);
}
//选择接受最顶底层的抽象类(InputStream in,OutputStream out),而不是具体的文件输入输出流这个类的原因是:如果以后有从网络、内存而来的都可以使用,而不是只能接收从文件来的
private static void copyFile(InputStream in,OutputStream out) throws IOException {
System.out.println("文件拷贝开始");
long start = System.currentTimeMillis();//开始拷贝时间
int len = 0;
// //边读边写,当被读取的文件长度不等于 -1就代表还有数据读,这种方法默认一次一个字节的文件,会非常慢
// while((len = in.read() )!= -1){
// out.write(len);
// }
//开一个缓冲区可以快一点,但是并不是说缓冲区越大越快
byte[] data = new byte[1024];
while((len = in.read(data))!= -1){
out.write(data,0,len);
}
long end = System.currentTimeMillis();//结束拷贝时间
System.out.println("文件拷贝结束,共耗时" + (end - start) + "ms");
}
}