流的概念:
在之前的课程中我们学习类“File”,该类是对文件进行操作的,但是只能操作文件的的本身,而不能操作文件中的内容,比如要将程序中的字符串写入文件中这样的功能使用“File”是无法实现的
如果要实现这样的功能需要使用流的概念,先将程序中的字符串转换成二进制的形式之后按照流的方法进行读取或者输出
字节输出流:
字节输出流很显然就是以字节为单位从程序文件中输出内容,如果要实现字节输出流的操作需要使用两个类,第一个类是java.io.OutputStream,这个类是一个抽象类,我们说过抽象类要有自己的子类,在之前抽象类的子类是我们自己定义的,但是现在的所有的抽象类的子类都是定义好的,我们只需要使用即可。
要实现字节输出流就需要使用到“OutputStream”的一个子类,该子类是“java.io.FileOutputStream”
public void close() throws IOException
流需要一个连接通道,如果操作完成之后需要关闭这个通道,否则会浪费资源
public void flush() throws IOException
如果有缓冲区则将缓冲区中的数据强制输出到文件中,字节流是没有缓冲区的,而是直接对终端进行操作
public void write(byte[] b) throws IOException
将数据输出到指定的文件中,这里的参数是将原始的数据保存到数组中,之后再将数组中保存的数据转移到通道中输出到文件中
public void write(byte[] b, int off, int len)
将数组中的部分内容输出到指定的文件中
byte[] b:表示转移数据的数组
int off:表示需要转移的数组元素的开始下标
int len:需要转移的数据的长度
public void write(int b) throws IOException
这是子类覆写父类的方法,每次输出一个字节,但是参数的类型的int,可以这样操作的原因是int和byte之间是可以互相转换的
package com.sxt;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class CharFileDemo {
public static void main(String[] args) throws Exception {
File file = new File("E:" + File.separator + "demo" + File.separator + "text.txt");
//判断文件所在的文件夹是否存在
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream outputStream = new FileOutputStream(file);
byte bt[] = "我是一个好学生".getBytes();
//将数据输出
outputStream.write(bt);
//关闭流
outputStream.close();
}
}
以上代码输出的是一个数组,此时如果输出的内容比较多,比如输出5个G的内容,此时内存会吃不住,会造成程序的奔溃。此时我们可以每次输出指定的内容。
所以每次输出一个字节:
package com.sxt;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class CharFileDemo {
public static void main(String[] args) throws Exception {
File file = new File("E:" + File.separator + "demo" + File.separator + "text.txt");
//判断文件所在的文件夹是否存在
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream outputStream = new FileOutputStream(file);
byte bt[] = "我是一个好学生".getBytes();
//将数据输出
int index = 0;
while (index < bt.length) {
outputStream.write(bt[index]);
index ++;
}
//关闭流
outputStream.close();
}
}
==以上是每次输出一个字节,还可以输出数组中指定的元素。 ==
package com.sxt;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class CharFileDemo {
public static void main(String[] args) throws Exception {
File file = new File("E:" + File.separator + "demo" + File.separator + "text.txt");
//判断文件所在的文件夹是否存在
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
if (!file.exists()) {
file.createNewFile();
}
OutputStream out = new FileOutputStream(file);
byte [] bts = "今天是周五".getBytes();
out.write(bts, 0, 3);
out.close();
}
}
发现了以上输出的内容是有一个“?”,原因是每个汉字是两个字节,但是我们只取得三个字节输出,前面的两个字节表示“今”,第三个字节表示“天”的一半,于是出现了“?”,此时我们就可以看出使用字节流来操作文字是存在问题,本身来说字节流的主要目的是操作图片、音频、视频等等格式的文件,而是操作文字的,操作文字有字符流实现。
字节输入流:
字节输出流,是将程序中的内容输入到文件中,输入流就是将指定的文件中的内容读取到程序中
输出流需要使用到一个抽象类OutputStream及其子类FileOutputStream类
public FileInputStream(File file)
throws FileNotFoundException
这是构造方法,需要一个File对象作为构造方法的参数
public void close() throws IOException
关闭流
public flush()
强制清空缓冲区的内容
public int read(byte[] b) throws IOException
每次读取一个数组长度的内容的数据,返回值是读取的数据的长度,如果没有数据返回-1
public int read(byte[] b, int off, int len)
读取数组中指定的内容的数据
int off:指定开始读取的下标
int len:指定需要读取的数据的长度
public int read() throws IOException
每次读取一个字节,返回值是int类型,原因的byte和int是可以互相转换的,如果没有数据的返回-1
package com.sxt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class ByteFileDemo02 {
public static void main(String[] args) throws Exception {
File file = new File("E:" + File.separator + "demo" + File.separator + "text.txt");
//判断文件夹是否存在
if (!file.getParentFile().exists()) {
//如果不存在,则创建
file.getParentFile().mkdirs();
}
//创建字节输出流对象
OutputStream outputStream = new FileOutputStream(file);
//准备要输出的数据
byte [] outbt = "今天是周五".getBytes();
//输出数组中指定的元素
outputStream.write(outbt);
outputStream.close();
//开始读取数据
InputStream inputStream = new FileInputStream(file);
//声明一个字节数组
byte [] bts = new byte[1024];
int temp = 0;
int footer = 0;
while ((temp = inputStream.read())!=-1) {
bts[footer++] = (byte)temp;
}
//此时 bts 保存了读取的数据,之后将其转换成字符串输出
String str = new String(bts);
System.out.println("["+ str + "]");
}
}
以上的代码 中bts数组长度是1024,但是保存的数据的长度就是footer最终的值,那么footer最终的值在上面的代码中一定小于1024,但是在使用bts数组创建字符串的时候长度是按照1024计算,于是就出现了空字符串。此时最好解决方案是数组中有多少个数据就创建多少长度的字符串,所以需要使用String类的另外一个构造方法
完善代码:
每次读取一个数组长度的内容:
package com.sxt;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class ByteFileDemo03 {
public static void main(String[] args) throws Exception {
File file = new File("E:" + File.separator + "demo" + File.separator + "text.txt");
//创建一个输入流对象
InputStream input = new FileInputStream(file);
//将读取的数据保存到一个数组中
StringBuffer sb = new StringBuffer();
//声明一个数组
byte [] bts = new byte[2];
//开始读取
int temp = 0;
while ((temp = input.read(bts))!=-1) {
sb.append(new String(bts, 0, temp));
}
System.out.println(sb);
}
}
字节输出流和输入流是以后实现文件上传和下载的基本要求
面试题:
1、请说明String、StringBuffer、StringBuilder的区别?
1.String声明的字符串内容不可以改变,
所以如果字符串改变的频率很高则产生大量的垃圾,
所以此种情况下不选择String声明字符串
2.StringBuffer和StringBuilder声明的字符串内容可以
改变,所以如果字符串改变的频率很高的时候选择这两者。
3.StringBuffer 是线程安全的,但是性能相对低,
而StringBuilder是线程不安全的,但是性能相对较高
4.StringBuffer 是旧的类,在JDK1.0推出,
而StringBuilder是新的类,在JDK1.5版本推出。