IO流的概念和分类
IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的类都在IO包中
流按流向分为两种:输入流,输出流。
流按操作类型分为两种:
字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
字符流 : 字符流只能操作纯字符数据,比较方便。
IO流常用父类
字节流的抽象父类:
InputStream
OutputStream
字符流的抽象父类:
Reader
Writer
IO程序书写
使用前,导入IO包中的类
使用时,进行IO异常处理
使用后,释放资源
字节流
读取和写出方法
public class Demo1 {
public static void main(String[] args) throws IOException {
//demo1();
//FileOutputStream fos = new FileOutputStream("eee.txt");
//如果没有eee.txt,会创建一个新eee.txt
//如果有eee.txt,会将eee.txt清空
//fos.write(97); //虽然写的是int数,但写出会是byte数因为写的时候会将前面的24个0去掉
FileOutputStream fos = new FileOutputStream("eee.txt",true);
fos.write(98);//不会将bbb.txt清空,如果再次写入会续写
}
private static void demo1() throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("bbb.txt");//这里在写的过程中需要向外抛异常
//创建一个文件输入流对象,并关联以个文件
int b; //定义变量,用于储存读取到的字节
//read()方法每次读取返回一个字节,为什么不用byte接受而用int?
/*因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制的的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111
* 那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上
* 24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型
*/
while((b=fis.read()) != -1){//将每次读到的字节赋给b,当读到没有时返回的是-1.
//read方法每读一次就向后移动一次
System.out.println(b); //打印每一个字节
}
fis.close(); //关闭流释放资源
}
}
拷贝的四种方法
public class DemoCopy {
public static void main(String[] args) throws IOException {
<strong>copy1();</strong>//逐个字节拷贝:效率太慢
<strong>copy2();</strong>//将整个文件转化为一个字节数组拷贝:文件太大的话可能造成内存溢出
<strong>copy3();</strong>//定义一个小数组拷贝文件:推荐使用
<strong>copy4();</strong>//使用缓冲区拷贝:推荐使用
//3和4相比差不多,相比之下3比4快一点点,因为在缓冲区内是一个字节一个字节传给输出流的
}
private static void <strong>copy4()</strong> throws FileNotFoundException, IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("logo.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.jpg"));
int b;
while((b = bis.read()) != -1){/*如果文件足够大,read()方法执行一次,就会将文件上字节数据一次读取8192个字节存储在BufferedInputStream的缓冲区中
从缓冲区中返给一个字节一个字节返给b
如果write一次,先将缓冲区装满,然后一次性的写到文件上去
这么做减少了到硬盘上读和写的操作*/
bos.write(b);
}
bis.close();
bos.close();
}
private static void<strong> copy3(</strong>) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("logo.jpg");
FileOutputStream fos = new FileOutputStream("copy.jpg");
int len; //声明变量用来记录读取字节个数
byte[] arr = new byte[1024*8];
while((len =fis.read(arr)) != -1){ //如果read方法忘记写字节数组了,就会返回的字节的码表值,写出就会比原来的文件大的多
fos.write(arr, 0, len);
}
fis.close();
fos.close();
}
private static void <strong>copy2()</strong> throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("logo.jpg");
FileOutputStream fos = new FileOutputStream("copy.jpg");
byte[] arr = new byte[fis.available()];//根据文件大小做一个字节数组
fis.read(arr); //将文件上的所有字节读取到数组中
fos.write(arr); //将数组中的所有字节一次写到了文件上
fis.close();
fos.close();
}
private static void <strong>copy1()</strong> throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream("E:\\logo.jpg");//创建输入流对象,并关联E:\\logo.jpg
FileOutputStream fos = new FileOutputStream("logo.jpg");//创建输出流对象,并关联logo.jpg
int b; //声明一个变量
while((b = fis.read()) != -1){ //变量接受写入的每个字节,并判断结果是否为-1
fos.write(b); //将变量接收 的每个字节写入
}
fis.close(); //关闭输入流
fos.close(); //关闭输出流
}
}
flush和close方法的区别
public class DemoFlush {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(""));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(""));
int b;
while((b = bis.read()) != -1){
bos.write(b);
// bos.flush(); //需要实时刷新缓冲区内容时使用flush
} //
bis.close();
bos.close();/* flush和close的区别
* flush刷新之后还可以继续写
* close在关闭之前会刷新一次,把缓冲区剩余的字节刷到文件上去,但是调用之后不能再写
*/
}
}
流的标准处理异常代码1.7版本和流的标准处理异常代码1.6版本及其以前
public class DemoIoExcepion {
public static void main(String[] args) throws IOException{
//exception1();//1.7版本处理异常代码
exception2();//1.7版本之前处理异常代码
}
private static void exception2() throws FileNotFoundException, IOException {
FileInputStream fis = null;//必须赋值 .因为如果没有读取到文件fis成了局部变量,必须赋值
FileOutputStream fos = null;
try{
fis = new FileInputStream("bbb.txt");
fos = new FileOutputStream("eee.txt"); //这四行放在检测区中
int b;
while((b = fis.read()) != -1){
fos.write(b);
}
}finally{
try{
if(fis!= null)
fis.close();
}finally{
if(fos != null)//保证至少能关闭一个
fos.close();
}
}
}
private static void exception1() throws IOException, FileNotFoundException {
try(
FileInputStream fis = new FileInputStream("bbb.txt");
FileOutputStream fos = new FileOutputStream("eee.txt");//放在小括号内
){
int b;
while((b = fis.read()) != -1){//放在大括号内
fos.write(b);
}
}
}
}
字符流
字符流是可以直接读写字符的IO流
字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
读取和写出方法
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("aaa.txt");//创建字符输入流对象
int b;//将char类型提升为int类型,因为文件结束标记都是-1,char取值范围0到65535,不能存储-1
while((b = fr.read()) != -1){ //read()方法是通过编码表按照字符的大小读取
System.out.print((char)b);//在读时是以int类型读到的输出时要强转
}
fr.close(); //关流
}
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("aaa.txt");//创建字符写出流对象,并关联文件aaa.txt
fw.write(98);// 写出字符
fw.write("好好学习");//写出字符串
fw.close();//关流
}
字符流拷贝方法
public static void main(String[] args) throws IOException {
<strong<strong>>copy1</strong>();
copy2();
copy3()</strong>;
<strong>demo4()</strong>;//BufferedReader的readline方法和newline
/*拷贝纯文本的文件用字符流好还是用字节流好?
* 拷贝纯文本字节流更好,因为字符流也是用字节的形式进行操作先将字节转化为字符然后再将字符转化为字节输出浪费时间。而字节流是直接将字节传入传出
*
* 字符流在什么时候用呢?
* 操作纯文本
* 只读,一次读取的是一个字符,不会出现乱码
* 只写,可以直接写出的是字符串,不用转换
*
* 当字符流拷贝非纯文本文件和拷贝纯文本的操作是一样的,需要先将字节转换为字符,转换字符如果没有转换成功就会变成?,写出去的时候
* 就会将?写出,也就是会乱码
*
* 字符流只能操作纯文本的文件
* 字节流可以操作的是任意文件*/
}
private static void <strong>demo4()</strong> throws FileNotFoundException, IOException {
BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));
String line;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
}
br.close();
bw.close();
}
private static void <strong>copy3()</strong> throws FileNotFoundException, IOException {
// BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率
//BufferedWriter的write()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率
BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));
int b;
while((b = br.read()) != -1){//read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量b
bw.write(b); //write一次,是将数据装到字符数组,装满后再一起写出去
}
br.close();
bw.close();
}
private static void <strong>copy2()</strong> throws FileNotFoundException, IOException {
FileReader fr = new FileReader("aaa.txt");
FileWriter fw = new FileWriter("bbb.txt");
char[] arr = new char[8192]; //自定义一个数组缓冲区
int b;
while((b = fr.read(arr)) != -1){ //按照缓冲区大小读取文件
fw.write(arr,0,b); //将读取的内容写入文件
}
fr.close();
fw.close();
}
private static void<strong> copy1()</strong> throws FileNotFoundException, IOException {
FileReader fr = new FileReader("aaa.txt");//创建字符写入流对象
FileWriter fw = new FileWriter("bbb.txt");//创建字符写出流对象
int ch;
while((ch = fr.read()) != -1){
fw.write(ch);
}
fr.close();
fw.close();
}
}
BufferedReader的readLine()方法和newLine()方法
/** BufferedReader的readLine()方法可以读取一行字符(不包含换行符号)
* BufferedWriter的newLine()可以输出一个跨平台的换行符号"\r\n"
* @param args
* 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
* @throws IOException
*/
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("ccc.txt"));//创建输入流并关联文件aaa.txt
ArrayList<String> list = new ArrayList<>(); //创建集合
String line;
while((line = br.readLine()) != null){ //按行读取文件
list.add(line); //将读取的每行文件放集合中
}
br.close(); //关闭流 流对象晚开早关
BufferedWriter bw = new BufferedWriter(new FileWriter("ccc.txt"));//创建输出流对象并关联aaa.txt,这里写同样的名字不会删除文件内容
//因为都在缓冲区中
for (int i = list.size()-1; i >= 0; i--) { //倒序遍历
bw.write(list.get(i)); //将遍历内容写入文件中
bw.newLine(); //换行 这种方式 跨平台,\r\n只用于windows
}
bw.close(); //关闭流
}
LineNumberReader
/*
* LineNumberReader是BufferedReader的子类, 具有相同的功能, 并且可以统计行号
* 调用getLineNumber()方法可以获取当前行号
* 调用setLineNumber()方法可以设置当前行号*/
public class DemoLineNumberReader {
public static void main(String[] args) throws IOException {
LineNumberReader lnr = new LineNumberReader(new FileReader("aaa.txt"));//创建LineNumberReader对象,并关联aaa.txt
String line;
lnr.setLineNumber(100);//设置行号
while((line = lnr.readLine()) != null){
System.out.println(lnr.getLineNumber() + ":" + line);//获取行号
}
lnr.close();
}
IO流的装饰设计模式 三步走
public static void main(String[] args) {
ItcastStudent is = new ItcastStudent(new Student());
is.coder();
Student s= new Student();
s.coder();
}
}
interface Coder{
public void coder();
}
class Student implements Coder{
@Override
public void coder() {
System.out.println("javaee");
System.out.println("adroid");
System.out.println("黑客");
}}
class ItcastStudent implements Coder{
private Student s;//获取到被包装的类的引用 第一步
public ItcastStudent(Student s){//创建构造函数对象时,传入被包装的对象 第二步
this.s = s;
}
@Override
public void coder() {
//进行功能的改造 第三步
System.out.println("大数据");
System.out.println("ios");
}
使用指定的码表读写字符
/*
* FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)
* FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)*/
private static void demo1() throws UnsupportedEncodingException,
FileNotFoundException, IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("UTF-8.txt"),"UTF-8");
//(字节流,编码表)通过指定编码表读写
OutputStreamWriter osr = new OutputStreamWriter(new FileOutputStream("GBK.txt"),"GBK");
//(字节流,编码表)通过指定编码表写
int b;
while((b = isr.read()) != -1){
osr.write(b);
}
isr.close();
osr.close();
}
private static void demo2() throws UnsupportedEncodingException,
FileNotFoundException, IOException {
BufferedReader br = //高效转换流
new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"),"UTF-8"));
BufferedWriter bw = //InputStreamReader是字节通向字符的桥梁
new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"),"GBK"));
//OutputStreamWriter是字符通向字节的桥梁
int b;
while((b = br.read()) != -1){
bw.write(b);
}
br.close();
bw.close();
序列流
/*.什么是序列流
* 序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推.*/
public static void main(String[] args) throws IOException {
//demo1();
//序列流整合多个文件
FileInputStream fis1 = new FileInputStream("aaa.txt");//创建输入流对象并关联文件aaa.txt
FileInputStream fis2 = new FileInputStream("bbb.txt");//创建第二个输入流对象并关联文件bbb.txt
FileInputStream fis3 = new FileInputStream("ccc.txt");//创建输入流对象并关联文件ccc.txt
FileInputStream fis4 = new FileInputStream("ddd.txt");//创建第二个输入流对象并关联文件ddd.txt
Vector<FileInputStream> v = new Vector<>();//创建vactor集合对象
v.add(fis1); //添加流对象
v.add(fis2);
v.add(fis3);
v.add(fis4);
Enumeration<FileInputStream> en = v.elements();//调用枚举
SequenceInputStream sis = new SequenceInputStream(en);//传给序列流
FileOutputStream fos = new FileOutputStream("fff.txt");//创建输出流,并关联fff.txt
int b;
while ((b = sis.read()) != -1){
fos.write(b);
}
sis.close();
fos.close();
}
private static void demo1() throws FileNotFoundException, IOException {
//序列流整合两个文件,
FileInputStream fis1 = new FileInputStream("aaa.txt");//创建输入流对象并关联文件aaa.txt
FileInputStream fis2 = new FileInputStream("bbb.txt");//创建第二个输入流对象并关联文件bbb.txt
SequenceInputStream sis = new SequenceInputStream(fis1,fis2);//创建序列流对象并关联两个文件
FileOutputStream fos = new FileOutputStream("ccc.txt");//创建输出流对象并关联ccc.txt
int b ;
while((b = sis.read()) != -1){
fos.write(b);
}
sis.close();
fos.close();
}
}
内存输出流
/* 1.什么是内存输出流
* 该输出流可以向内存中写数据, 把内存当作一个缓冲区, 写出之后可以一次性获取出所有数据
2.使用方式
* 创建对象: new ByteArrayOutputStream()
* 写出数据: write(int), write(byte[])
* 获取数据: toByteArray()*/
/*定义一个文件输入流,调用read(byte[] b)方法,将a.txt文件中的内容打印出来(byte数组大小限制为5)*/
public class Test1 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("ddd.txt");//创建字节输入流,并关联ddd.txt
ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建内存输出流
int len;
byte[] b = new byte[5];//创建大小为5 的数组
while((len = fis.read(b)) != -1){//将文件上内容读到字节数组中
bos.write(b,0,len); //将数组中的数据写出到内存缓冲区中
}
对象操作流
//demo1();前两种是未优化的
//demo2();
demo3();
//3和4对对象流的优化减少了读和写的次数
demo4();
}
private static void demo4() throws IOException, FileNotFoundException,
ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("t.txt"));
@SuppressWarnings("unchecked")//这里的黄线是因为泛型只对编译期有效,,去掉黄线的方法是加"unchecked"
ArrayList<Peason> list = (ArrayList<Peason>)ois.readObject();
for (Peason peason : list) {
System.out.println(peason);
}
ois.close();
}
private static void demo3() throws IOException, FileNotFoundException {
Peason p = new Peason("张三",34);
Peason p1 = new Peason("李四",36);
Peason p2 = new Peason("王五",35);
Peason p3 = new Peason("赵六",37);
ArrayList<Peason> list = new ArrayList<>();
list.add(p);
list.add(p1);
list.add(p2);
list.add(p3);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c.txt"));
oos.writeObject(list);
oos.close();
}
private static void demo2() throws IOException, FileNotFoundException,
ClassNotFoundException {
//将文件上的对象读出来叫反序列化(读档)
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("t.txt"));//不能与序列化文件名相同
Peason p = (Peason)ois.readObject();
Peason p1 = (Peason)ois.readObject();
Peason p2 = (Peason)ois.readObject();
//Peason p3 = (Peason)ois.readObject();//如果写三次读两次会报java.io.EOFException异常EOF代表end of file
System.out.println(p);
System.out.println(p1);
System.out.println(p2);
ois.close();
}
private static void demo1() throws IOException, FileNotFoundException {
Peason p = new Peason("张三",34);
Peason p1 = new Peason("李四",36);
Peason p2 = new Peason("王五",35);
//将实现serializable接口的对象写到文件上去,,这也叫序列化。(也叫存档)先写(存档)后读id不会报错
//在文件上我们读不懂没关系,他自己的read方法可以读懂。在写时他将对象转变为字节数组,编码表对其无法翻译
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c.txt"));
oos.writeObject(p);
oos.writeObject(p1);
oos.writeObject(p2);
oos.close();
}
}
打印流
/*打印流
* 该流可以很方便的将对象的toString()结果输出, 并且自动加上换行, 而且可以使用自动刷出的模式
* 只操作数据目的 对应是是FileOutputStream*/
private static void demo2() throws FileNotFoundException {
//字符打印流
PrintWriter pw = new PrintWriter(new FileOutputStream("ddd.txt"),true);
//是autoFlush方法 是boolean类型的给true就自动刷新
pw.print(97);
pw.write(45);
pw.println(97);//autoFlush自动刷新还针对println有效
pw.close();//关流也能刷新所以意义不大
}
private static void demo1() {
//打印字节流
PrintStream ps = System.out;
ps.println(97);//打印a//其实底层用的是Integer.toString(x),将x转换为数字字符串打印
ps.println(new Peason("小李",23));
Peason p = null;
ps.println(p);//如果是null,就返回null,如果不是null,就调用对象的toString(),这里是默认toString方法
}
}
标准输入输出流
System.in 标准输入流 返回值是InputStream 默认可以从键盘输入读取字节数据
* System.out 标准输出流 返回是PrintStream 默认可以向Console中输出字符和字节数据 */
public static void main(String[] args) throws IOException {
System.setIn(new FileInputStream("logo.jpg"));//修改标准输入流
System.setOut(new PrintStream("copy.jpg"));//修改标准输出流
InputStream is = System.in; //获取标准输入流对象
OutputStream os = System.out; //获取标准输出流对象
int b;
byte[] arr = new byte[8192];//定义小数组提高效率
while((b = is.read(arr)) != -1){
os.write(arr,0,b);
}
is.close();
os.close();
}
}
随机访问流
RandomAccessFile类不属于流,是Object类的子类。但它融合了InputStream和OutputStream的功能。
* 支持对随机访问文件的读取和写入。
*/
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("ddd.txt","rw");
System.out.println( raf.read()); //可以读也可以写
raf.seek(3);//设置指针在指定位置写入
raf.write(98);
raf.close();
}
数据输入输出流
/*DataInputStream, DataOutputStream可以按照基本数据类型大小读写数据.可以读很大的数 */
public static void main(String[] args) throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("d.txt"));
//在文件中是看不懂的,因为它写时不能将int数写过来而是将其转变为一个int数组写过来,
// 当码表对其进行翻译时没有翻译过来
dos.writeInt(789);
dos.writeInt(788);
dos.writeInt(777);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("d.txt"));
//但是可以用它对应的read方法读出来,打印到控制台
int x = dis.readInt();
int y = dis.readInt();
int z = dis.readInt();
System.out.println(x);
System.out.println(y);
System.out.println(z);
dis.close();
}
Properties
/*:Properties 是MAP集合Hashtable 的子类
* 可以没有泛型,可以说是一个配置工具
* Properties 类表示了一个持久的属性集。
* Properties 可保存在流中或从流中加载。
* 属性列表中每个键及其对应值都是一个字符串。 */
//demo1();
//demo2();
//list 和load 方法
Properties pr = new Properties();
pr.load(new FileInputStream("ddd.properties"));//获取文件上的数据
pr.setProperty("name", "zhangsan"); //进行修改和填充
pr.list(new PrintStream("ddd.properties")); //写回到文件中
System.out.println(pr);
}
private static void demo2() {
Properties pr = new Properties();
pr.setProperty("姓名","四" );
pr.setProperty("qq","12345676" );
pr.setProperty("工号","234" );
pr.setProperty("电话","18945679876" );
@SuppressWarnings("unchecked")
Enumeration<String> en = (Enumeration<String>)pr.propertyNames();//调用迭代器
while(en.hasMoreElements()){ //判断集合中是否有元素
String key =en.nextElement(); //获取键
String value = pr.getProperty(key);//根据键获取值
System.out.println(key+"-"+value);
}
}
private static void demo1() {
Properties pr = new Properties();//创建集合对象
pr.setProperty("姓名","四" ); //添加内容因为是hashtable的子类所以是双列集合
pr.setProperty("qq","12345676" );
pr.setProperty("工号","234" );
pr.setProperty("电话","18945679876" );
System.out.println(pr);
String value = pr.getProperty("姓名");//通过键获取值
System.out.println(value);
}
}
两种方式实现键盘录入
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入");
String line1 = br.readLine();
System.out.println(line1);
//这两种键盘录入方式基本相同scanner中方法更多些,而bufferedreader 中只有readLine()方法
Scanner sc = new Scanner(System.in);
System.out.println("请输入");
String line = sc.nextLine();
System.out.println(line);
}