Java中的IO流

Java中的IO流基本概念及常用方法演示

一.文件类File

  IO流主要操作的对象是文件,表现为对文件的读和写,因此,在学习IO流之前,我们必须得了解文件类File的用法。

  文件类是对存储在磁盘上的文件和目录的抽取和封装的一个类,是文件和目录路径名的抽象表现形式

  构造方法:
  File(String pathname)
          通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。

  实例: //2.1当前File对象的创建方式是传入一个路径的字符串
File file1 = new File("D:\\SEWorkSpace\\Day15");
System.out.println(file1); //不是地址 当前的路径

  File(File parent,String child)
          根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。

  实例: //2.3根据当前传入的父目录路径来获取当前路径下的文件夹或文件
//当前的的父目录路径是 一个File对象
//第一个参数: File对象  第二个参数可以是文件或文件夹

File file3 = new File(file1,"bin");
System.out.println(file3);

  File(String parent,String child)
          根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

  实例: //2.2根据父目录的对应路径来创建当前路径下的文件或文件夹
//第一个参数 : 父路径(给定的路径)  第二个参数: 文件/文件夹

File file2 = new File("D:\\SEWorkSpace\\Day15","src");
System.out.println(file2);

  构造方法中的参数是用来放文件路径的,可以放绝对路径和相对路径

  绝对路径:从盘符开始的路径--> C:\Demo\本地文件.txt

  相对路径:不从盘符开始的路径 --> 当前工程中/文本文件.txt (写相对路径必须有参照路径-->一般是一个绝对路径)

  File中的常用方法:
 //1.判断当前文件对象是否封装的是文件夹(目录)
//返回值 true  是    false不是 
File file4 = new File("C:\\Users\\Admin\\Desktop\\file.txt");
System.out.println("是文件夹吗?"+file4.isDirectory());

//2.判断当前文件对象是不是封装的文件
System.out.println("是文件吗?"+file4.isFile());

//3.判断当前文件是不是可读文件(所有文件默认都是可读可写的)
System.out.println("是可读文件吗"+file4.canRead());

//4.判断当前文件是不是隐藏文件(默认所有的文件或文件夹都是不隐藏的)
System.out.println("是否隐藏:"+file4.isHidden());

//5.在当前路径下创建一个新的文件,若当前文件存在,返回一个false,否则返回true
try {
//当前异常是编译异常必须处理,若强制必然崩溃
System.out.println(new File("File/newFile.txt").createNewFile()?"新建成功":"文件已经存在!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//System.out.println(e.getMessage());
}

//6.删除一个文件或,一个空的文件夹(目录)
//若你删除一个不存在的文件或文件夹,返回false;
//删除的是当前文件夹(目录) 一级目录

//System.out.println(new File("File/newFile.txt").delete()?"删除成功":"删除失败");
System.out.println(new File("C:\\Users\\Admin\\Desktop\\1").delete()?"删除成功":"删除失败");

//7.判断文件或文件夹是否存在? true 存在  false  不存在
System.out.println(new File("file/newFile.txt").exists()?"存在":"不存在");

//8.获取当前文件所在磁盘目录剩余空间
System.out.println(new File("file").getFreeSpace());
 //147524104192是正常当前编译器和操作系统对磁盘计算空间的算法不一致

 
//9.获取当前文件实例所在的磁盘总空间
System.out.println(new File("file").getTotalSpace());
//10.获得当前文件实例所在磁盘的已用空间  getUsableSpace();

//11.将封装在目录文件实例中所有直系(一级)子目录和文件名名取出来包括隐藏目录
File file5 = new File("D:\\");
String[]  subFile = file5.list();
for(String str : subFile){
System.out.println(str);
}

  //12.使用参数指定文件名过滤当前目录所有的文件,并将过滤后的文件存储到一个字符串数组中
File file6 = new File("D:\\SEWorkSpace\\Day15\\src\\com\\qianfeng\\day15\\File"); 
String[] allJava =  file6.list(new FilenameFilter() {

@Override
//第一个参数是文件夹
//第二个参数是文件

public boolean accept(File dir, String name) {
// 当前这个方法时过滤器方法,用阿里过滤指定的要求
//过滤出当前目录下所有的.java文件

return name.endsWith(".java");
}
});

for(String java:allJava){
System.out.println(java);
}
  
//13.若当前实例封装的文件夹(目录)不存在,那么就创建一个新的目录,否则就不创建
System.out.println(new File("file/newdir").mkdir()?"创建成功":"不成功");

   //14.若当前文件实例中封装的文件夹(目录)不存在就创建一个新的目录,若当前父目录也不存在
//那么也会创建 (一直创建你所需要的目录)
//System.out.println(new File("file/newdir/a/b/c/d/f/e").mkdirs()?"创建成功":"创建失败");

//15.集 剪切 ,复制,重命名于一身的方法
//将当前file文件夹下的newFile.txt剪切到file/newdir/a下,并改名为KKK.txt
new File("file/newFile.txt").renameTo(new File("file/newdir/a/kkk.txt"));

二.IO流

 1.根据流的流向区分
 
 输入流:从磁盘等物理介质输入到当前内存中
 API -->InputStream(字节输入流) Reader(字符输入流)

 输出流:将程序处理好的数据从内存中输出到磁盘等物理介质
 API -->OuputStream(字节输出流)  Writer(字符输出流)

 2.根据当前流中的数据不同而分类

 字节流:流中的数据是以字节为单位流动
 InputStream和OutputStream

 字符流:流中的数据是以字符为单位流动
 Reader和Writer

跟具体介质打交道

 节点流:与具体的存储介质直接进行交互
 API-->FileInputStream  FileOutputStream 是字节流的子类(因为InputStream和OuoutStream是抽象类,因此一般获取子类的引用)

 API-->FileReader  FileWtiter  是字符流的子类(因为Reader和Writer是抽象类,因此一般获取子类的引用)

 3.字节输入输出流
所有字节流的根类并且都是抽象类
InputStream -->字节输入流 抽象类
-->子类 FileInputStream
FileInputStream(File file)
通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过系统中的Fiel对象file指定
FileInputStream(String name)
通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过系统中的路径名name
int read()
从此输入流中读取一个数据字节
int read(byte[] b)
从此输入流中将最多b.length 个字节的数据读入一个byte数组中

 字节输入流案例演示(1):
 public static void main(String[] args) {

//1.创建当前字节输入流对象 InputStream 抽象类
//第一种通过一个file对象来完成对象的创建
//InputStream ips = new FileInputStream(new File("路径"));
//第二种通过路径的方式直接创建对象
//InputStream ips = new FileInputStream("");
//FileNotFoundException   找不到当前文件异常

InputStream ips = null;
try {
ips = new FileInputStream(new File("C:\\Users\\Admin\\Desktop\\file.txt"));
//1.返回流中所有数据的长度
System.out.println(ips.available()); //当前这个方法只能在程序运行起来之后才能获取当前长度
//2.来读取当前文件中的内容
//2.1 read() 一次读取一个字节
//2.2 read(byte[] b) 去读取当前文件的长度,存储到当前数组中

byte[] b = new byte[26];
//读取的是后面()中数组的长度,而不是读取()中的数组,读取的是当前流中的文件,读取的内容会存储在当前字节数组中
//返回一个值:实际读取的长度

int len = ips.read(b);
System.out.println("读取的长度:"+len);
//读取的内容不能直接打印数组
//System.out.println(Arrays.toString(b));

System.out.println("文件中的内容是:"+new String(b));

//3.从当前输入的字节数 开始向后往后读取
ips.skip(10);//10个字节

byte[] b2 = new byte[16];
//若当前的返回值是-1  读取到了文件的末尾
int  len2 = ips.read(b2);
System.out.println(new String(b2) + len2);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//关闭当前流
if(ips != null){//当前流创建了
try {
ips.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
 
 字节流输入案例演示(2)-->循环读取文件内容:
public static void main(String[] args)throws IOException {

//1.创建当前输入流对象
InputStream ips = new FileInputStream("C:\\Users\\Admin\\Desktop\\file.txt");
//2读取文件的内容(清楚自己使用的是什么流)
//2.1创建一个字节数组用来存储和读取当前文件中的内容
//ps:一次读取的是1k = 1024 

byte[] b = new byte[1024];
//如何循环的读取内容?
//若读取到-1证明读取到了文件的末尾-->boolean
//read(字节数组)-->返回值
//循环读取
//1.定义变量来获取实际读取的长度

int len = 0;
while((len = ips.read(b))!=-1){
//打印当前结果:
//String str = new String(b);
//String 提供了一个构造方法
/*第一个参数: 当前字节数组
* 第二个参数: 偏移量开始的位置(默认都是0)
* 第三个参数: 实际读取的长度
*/

String str = new String(b,0,len);
System.out.println(str);

}

ips.close();
  OutputStream -->字节输出流 抽象类
-->子类FileOutputStream
FileOutputStream(File file) 
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 
FileOutputStream(File file, boolean append)  true追加  false 不追加
        创建一个向指定File对象表示的文件中写入数据,append要不要进行追加  
 FileOutputStream(String name) 
          创建一个向具有指定名称的文件中写入数据的输出文件流。 
FileOutputStream(String name, boolean append) 
          创建一个向具有指定 name 的文件中写入数据的输出文件流。 
  void close() 
          关闭此文件输出流并释放与此流有关的所有系统资源。
  void flush() 刷新         
 void write(byte[] b, int off, int len) 
          将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
 
  字节输出流案例演示:

 public static void main(String[] args) {
//1.创建当前字节输出流对象
OutputStream ops = null;
//2.创建一些数据写出去
String str1 = "自挂东南枝";
String str2 = "两只黄鹂鸣翠柳\n";
String str3 = "飞流直下三千尺!!!!";
//若通过字节输出流写出数据,当前文件不存在的情况下,那么会自动创建一个当前的文件
//若有就直接写入数据
//若要是追加数据,那么就在后天添加true,正常写入就什么都不要写

try {
ops = new FileOutputStream(new File("File/file.txt"),true);
//直接写入
//现在所使用的是字节输出流

ops.write(str1.getBytes());
ops.write(str2.getBytes());
ops.write(str3.getBytes());
//刷新-->在没有使用到网络的情况下,当前写刷新和不写刷新效果是一样
//加快流的流速

ops.flush();
System.out.println("写完了");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(ops != null){
try {
ops.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

  将指定文件中的内容读取到内存中再写入到当前工程中(读和写同时进行)
   public static void main(String[] args) {
//读取文件内容写入到当前工程中
//1.创建字节输入流

InputStream ips = null;
//2.创建字节输出流
OutputStream ops = null;
try {
ips = new FileInputStream("C:\\Users\\Admin\\Desktop\\file.txt");
ops = new FileOutputStream("File/inAndout.txt");
//1.将文件中内容循环的读取出来
//读取时字节--> 1个字节--> 乱码

byte[] b = new byte[1024];//不能保证一定不出乱码
//若实际读取的长度返回-1,读取到了文件的末尾

int len;
while((len = ips.read(b))!=-1){//循环读取
//将当前读取的内如写入到文件中
/*
* 第一个参数: 当前字节数组
* 第二个参数: 偏移量(默认都是 0)
* 第三个参数: 实际读取的长度
*/

ops.write(b, 0, len);
//System.out.println(new String(b,0,len));

}
ops.flush();
System.out.println("写玩了");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//最后创建的对象先释放
if(ops != null){
try {
ops.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ips != null){
try {
ips.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
  
  字节流的应用场景:
  拷贝一些特定的源文件(声音,图像,视频,压缩包,doc,exe)等等文件
  但是字节流最不好的地方在于,拷贝文本文件的时候,出现乱码
   

   4.字符输入输出流

  Reader -->字符输入流 抽象类
 -->FileReader
 FileReader(File file) 
          在给定从中读取数据的 File 的情况下创建一个新 FileReader。 
 FileReader(String fileName) 
          在给定从中读取数据的文件名的情况下创建一个新 FileReader。 
 void close() 
          关闭该流并释放与之关联的所有资源。 
 int read(char[] cbuf, int off, int len) 
          将字符读入数组的某一部分。 

 字符输入流案例演示:
  public static void main(String[] args) {
//创建当前Reader对象-->字符输入流对象
Reader reader = null;
try {
reader = new FileReader(new File("File/file.txt"));
//明确当前使用的是什么流
/*
*当前数组中存储的数据是填充数据,不是覆盖,当前数组中会遗留下原有的数据
* 1 1 1 1 1 1 当前放6个数据 将数组填满了
* 2 2 2 1 1 1 再次放入 3个数据 将之前三个填充了  后面三个保留了
*/

//字节流拷贝文本文件时-->乱码
//字符流-->专门用来拷贝文本文件使用
//数组的大小无论如何填写,都能读取字符
//1个汉字 用一个 字符也能存在
char[] cbuf = new char[1024];
//获取当前read方法的返回值
/*
* 若返回值的是-1,读取到了文件的末尾
* 若返回值不是-1,读取的是实际的长度
*/

int len;
while((len = reader.read(cbuf))!=-1){
System.out.println(new String(cbuf,0,len));
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(reader != null){
try {
reader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
      
 Writer --> 字符输出流  抽象类
 append()-->字符 /字符串 
          将指定字符添加到此 writer。  在没有特殊使用的情况下,当前方法等效于Write
 void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分。 
 void write() 
          写入单个字符或字符串。 
 void close() 
          关闭此流,但要先刷新它。 
 void flush() 
          刷新该流的缓冲。 
 --> FileWriter
  构造方法摘要 
 FileWriter(File file) 
          根据给定的 File 对象构造一个 FileWriter 对象。 
 FileWriter(File file, boolean append) 
          根据给定的 File 对象构造一个 FileWriter 对象,并且可以进行追加 true 追加 false
 FileWriter(String fileName) 
          根据给定的文件名构造一个 FileWriter 对象。 
 FileWriter(String fileName, boolean append) 
          根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象,并且可以进行追加 true 追加 false

 字符输入输出流的应用场景:
 字符流的主要作用,就是操作文本文件时使用的,可以满足基本的没有乱码,
 但是当前流不能特殊格式(声音,图片,视频)等等这样的文件进行拷贝

 5.标准输入输出

 System类中

 System.in -->InputStream "标准"输入流 字节输入流
 从控制台上获取数据

 System.out -->PrintStream "标准"输出流 字节打印流 是字节输出流的子类
 打印在控制台上

 输入输出重定向

 调用当前System类中的两个方法

 static void setIn(InputSteam in)
 重新分配"标准"输入流

 static void setOut(PrintStream out)
 重新分配"标准"输出流

 标准输入输出流演示案例:

 public class SystemInAndOutDemo {
/**
* 输入输出重定向
* 改变当前标准输入流和输出流的流向
* 标准输入流的流向是--> 控制台 修改 -->从文件读取
* 标准输出流的流向是--> 打印在控制台  修饰-->文件中
*/

public static void main(String[] args) {
//当前标准输入流和输出流的数据类型
/*
* 标准输入流的数据类型是 InputStream
* 标准输出流的数据类型是 PrintStream
*/

try {
//1.定制当前的流向
//1.1输入的流向

InputStream in = new FileInputStream("src/com/qianfeng/day16/ReaderAndWriter/ReaderAndWriter.java");
//1.2输出的流向 
//参数 是File对象  /路径 -->ps:是另外的流对象

//PrintSteam-->OutputStream的子类
PrintStream  out = new  PrintStream("File/ReaderAndWriter.java");
//将当前标准输入输出进行重新定向
System.setIn(in);
System.setOut(out);
//明确当前使用的流
byte[] bs = new byte[1024];
int len;
//循环读取
/*
* 若使用 in 对象 就相当于 直接使用InputStrem 读取方式 是完全可行,但违背我们要使用彼标准输入流
*/

while((len = System.in.read(bs))!=-1){
     //当前如果用in.read 相当于调用了PrintStream流来完成写入,完全可行,,但违背我们要使用彼标准输出流
 // out.write(buf, off, len);
//将流中的数据写入到当前文件中

System.out.write(bs, 0, len);

}

//System.out.flush();
//暂时不写-->如何改变回来
//System.out.println("写入完成");
//系统的标准输入和标准输出流是不需要关闭(释放资源)

} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

 [案例]从控制上获取信息,写入到当前文件中-->若输入 886,就结束,否则继续执行

 public class SystemInAndOutDemo2 {
      public static void main(String[] args) throws IOException {
  //1.在控制台上获取当前数据
     Scanner input = new Scanner(System.in);
     //2.改变当前System.out的流向
     //2.1流的流向

     PrintStream out = new PrintStream("File/输入的内容.txt");
     //2.2重新改变
     System.setOut(out);
     //3.循环的判断当前输入的是否是 886 结束输入
     while(true){
    String  str =  input.next();
     //1.判断
    if("886".equals(str)){
    break;
    }
     System.out.println(str);
     }
}

 6.转换流 -->可以定制编码集

 学习流时:装饰着模式
 
 字节字符转换输入流

 InputStreamReader :将字节流转换为字符流  -->桥梁

 InputStreamReader(InputStream in) //传进来一个字节流对象
 创建一个使用默认字符集的InputStreamReader

当前后两个方法可以指定当前文件的编码格式

 InputStreamReader(InputStream in,Charset cs) -->Charset.forName("编码集")
 创建一个使用给定字符集的InputStreamReader

 InputStreamReader(InputStream in,String charsetName)
 创建一个使用给定字符集的InputStreamReader

 void close()
 关闭该流并释放与之关联的所有资源

 String getEncoding()
 返回此流使用的字符编码名称

 int read()
 读取单个字符

 int read(char[] cbuf,int offset,int length)
 将字符读入数组的某一部分
 
 ps:若你读取文件时设置的编码集就不对,那么你输出时的编码集也不可能对

 字节流转换为字符流演示案例:

 public class InputStreamReaderDemo {
public static void main(String[] args) {
 //try....catch...finally
//直接释放资源
//1.创建当前字节转字符流对象
  //当前参数是一个InputStream对象  字节输入流对象
//直接释放资源JDK1.7以上语法

try(InputStreamReader isr = new InputStreamReader(
new FileInputStream(
"src/com/qianfeng/day16/ReaderAndWriter/ReaderAndWriter.java"))){
System.out.println("当前文件的编码是:"+isr.getEncoding());
//当前使用的是什么流
char[] cbuf = new char[1024];
int len;
while((len = isr.read(cbuf))!=-1){
System.out.println(new String(cbuf,0,len).toUpperCase());
}
}catch(IOException e){
System.out.println("当前异常信息是:"+e.getMessage());
}
}

 转换流之指定编码集案例:

 public class InputStreamReaderDemo2 {
public static void main(String[] args) {
 //try....catch...finally
//直接释放资源
//1.创建当前字节转字符流对象
  //第一个参数是一个InputStream对象  字节输入流对象 是抽象类不能new 所只能用子类来完成
//第二个参数是一个编码集
/*
*1. Charset.forName("UTF-8");
*2. "UTF-8" 
*/
//直接释放资源JDK1.7以上语法

try(InputStreamReader isr = new InputStreamReader(
new FileInputStream(
"src/com/qianfeng/day16/ReaderAndWriter/ReaderAndWriter.java"),Charset.forName("UTF-8"))){
System.out.println("当前文件的编码是:"+isr.getEncoding());
//当前使用的是什么流
char[] cbuf = new char[1024];
int len;
while((len = isr.read(cbuf))!=-1){
System.out.println(new String(cbuf,0,len).toUpperCase());
}
}catch(IOException e){
System.out.println("当前异常信息是:"+e.getMessage());
}

 目的:
 
 1.操作文本文件时,将字节流转换成字节流更加便于操作
 2.可以完全解决乱码的问题
 2.1若字节输入流,读取文件时,文件若存在中文,字符数据长度很小 -->不会产生乱码
 2.2若源文件的编码集与默认读取的文件的编码集不一致,也会出现乱码 -->读取的时候就错了,写出的时候一定是错的

 若想完全解决乱码需要将当前编码写成一致

 字节字符转换输出流

 OutputStreamWriter:将字符流转换为字节流 -->桥梁

 构造方法摘要

 OutputStreamWriter(OutputStream out)
 创建使用默认字符编码的OutputStreamWriter

 OutputStreamWriter(OutputStream out,Charset cs) -->Charset.forNmae("编码集")
 创建使用给定字符集编码的OutputStreamWriter

 OutputStreamWriter(OutputStream out,String charsetName) -->"编码集"
 创建使用给定字符集编码的OutputStreamWriter

 void close() 
 关闭此流,但要先刷新它。 

 void flush() 
 刷新该流的缓冲。 

 String getEncoding() 
 返回此流使用的字符编码的名称。 

 void write(char[] cbuf, int off, int len) 
 写入字符数组的某一部分。 

 void write(int c) 
 写入单个字符。 

 void write(String str, int off, int len) 
 写入字符串的某一部分。

 字节字符输出流案例演示:

 工具类:
 public class InAndOutUtil {
//1.不能有main方法
//2.当前工具类中所有的属性都是静态的
//3.当前工具类中的方法必须是静态方法
/**
* 转换流之间文件的拷贝(指定编码集)
* @param src  源文件
* @param des  目标文件
* @param srcCharSet  源文件的编码集
* @param desCharSet  目标文件的编码集
*/

public static void copy(File src,File des,String srcCharSet,String desCharSet){
//字符流
Reader reader = null;
Writer writer = null; 
try {
reader = new InputStreamReader(new FileInputStream(src),srcCharSet);
writer = new OutputStreamWriter(new FileOutputStream(des),desCharSet);
//1.循环的读写
char[] cbuf = new char[1024];
int len;
while((len = reader.read(cbuf))!=-1){
//写
writer.write(cbuf, 0, len);
}
writer.flush();
System.out.println("老子写写了,不写了!!!");

}
 
 测试类:
 public class Demo {
public static void main(String[] args) {
//1.创建源文件路径
File src = new File("File/UTF-8");
//2.目标文件路径
File des = new File("File/GBK");
//若指定编码集市若源文件的编码与读取时的编码集一致,那么我们在书写的时候可以进行编码集的修改
//若指定编码集与源文件的编码集读取时不一致,无论如何你都不会写对

InAndOutUtil.copy(src, des, "UTF-8", "GBK");
}

 ps:当前这两个转换流的父类 字符流Reader Writer

 **字节字符转换流总结:InputStreamReader是以读的方式将字节转换为字符,因此在传入一个字节流对象
     InputStream的时候已经转换了;但是OutputStreamWtriter是以写的方式将字符转换为字节,因此将
     OutputStreamWtriter对象转换为OutputStream对象写到指定文件中去**

 7.缓冲流 -->实际开发中是比较推荐的

 字节缓冲流
 字节缓冲流:在操作数据的时候,先将数据读入到内部的缓冲区中(字节数组),程序直接操作缓冲区中的数据
 最大的特点:可以提高数据在流中的传输速度,在项目中推荐使用

 字节输入缓冲流

 BufferedInputStream

 BufferedInputStream(InputStream in) 
          创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

 int available() 
          返回可以从此输入流读取(或跳过)、且不受此输入流接下来的方法调用阻塞的估计字节数。
 
 void close() 
          关闭此输入流并释放与该流关联的所有系统资源。 

 void mark(int readlimit) 
          参见 InputStream 的 mark 方法的常规协定。
 
 boolean markSupported() 
          测试此输入流是否支持 mark 和 reset 方法。 

 int read() 
          参见 InputStream 的 read 方法的常规协定。 

 int read(byte[] b, int off, int len) 
          从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。 

 void reset() 
          参见 InputStream 的 reset 方法的常规协定。
 
 long skip(long n) 
          参见 InputStream 的 skip 方法的常规协定。
 
 字节输入缓冲流案例演示:

 public class BufferedInputStreamDemo {
public static void main(String[] args) {
//创建当前字符缓冲输入流对象
//JDK1.7以后直接释放资源
//参数是一个字节输入流的对象

try(BufferedInputStream   bis = 
new BufferedInputStream(
new FileInputStream(
"src/com/qianfeng/day17/BufferedInputStream/BufferedInputStreamDemo.jav a"))){
//1.当前流是否支持mark标记
//true 支持标记     false 不支持

System.out.println("当前流是否支持标记?"+bis.markSupported());
//2.属于哪个类型的流 InputStream 字节输入流
/*
* 数组是填充 不是 覆盖实际读取长度来获取真正的数据     
*/
//添加标记 -->印戳 在此输入流中添加当前标记添加一个位置(当前在哪个位置重新开始读取)

bis.mark(100000000); //-->
byte[] b = new byte[1024];
int  len;
while((len = bis.read(b))!=-1){
System.out.println(new String(b,0,len));
}
System.out.println("-------------------再次读取的内容---------------------");
bis.reset();
byte[] b2 = new byte[1024];
int  len2;
while((len2 = bis.read(b2))!=-1){
System.out.println(new String(b2,0,len2));
}

 /*
  * hello
  * bis.read()--> h
  * bis.read()--> e
  * bis.mark(3);-->3最多可以读取3个字节
  * bis.read()--> l
  * bis.read()--> l
  * bis.read()--> o
  * bis.reset();
  * bisread()-->l,l,o
  */
//mark 一定要和 reset 标记一起出现

}catch(IOException e){
System.out.println("当前异常信息是:"+e.getMessage());

}
 
  字节缓冲输出流

 BufferedOutputStream

 BufferedOutputStream(OutputStream out) 
          创建一个新的缓冲输出流,以将数据写入指定的底层输出流。

 void flush() 
          刷新此缓冲的输出流。 

 void write(byte[] b, int off, int len) 
          将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。 

 void write(int b) 
          将指定的字节写入此缓冲的输出流。 

 void close()  关闭  

 字节缓冲输出流案例演示: --> [案例:]用字节缓冲输入流来从控制台上获取数据,
 写入到磁盘文件中 -->886程序结束
 不用Scanner实现控制台上获取 System.in -->Scanner是有缓冲区的 缓冲流

 public class BufferedOutputStreamDemo {
public static void main(String[] args) {
//1.构建输入输出流对象(带缓冲的)
//1.1构建一个字节缓冲输入流对象来获取控制台输入
/*
* 当前流是自带缓冲区的   Scanner扫描器自带缓冲区的
* System.in 标准输入流-->重控制台上获取数据
* 第二种在控制台上完全通过流来完成用户的输入
* 弊端:若使用这种方式来获取用户数据-->吸收 "\r\n" 换行
*/

BufferedInputStream bis = new BufferedInputStream(System.in);
PrintStream ps = null;
try {
//指定当前输出的方向
ps  = new PrintStream(new  FileOutputStream(new  File("dir/缓冲流演示.txt")));
 //重新定向
System.setOut(ps);
//3.创建字节缓冲输出流对象,进行输出
//参数: 字节输出流对象
/*
*System.out--> PrintStream-->OutputStream 
*当前这个使用方式只是为了满足当前需求
*若不是当前需求,那么当前字节缓冲输出流的使用方式和普通字节流是一样
*/

   BufferedOutputStream  bos = new BufferedOutputStream(System.out);
   //循环的读写
   //1.创建一个字节数组存储当前得到数据

   byte[] bs = new byte[1024];
   //死循环-->用户不用我不停
   while(true){
    //1.获取当前用户输入的内容
    int len = bis.read(bs);
    //2.将当前获取的byte类型的数据转换成一个字符串的形式
    String content = new String(bs,0,len);
    //当用书输入886要停止-->流直接读取数据-->换行"\r\n"
    if("886\r\n".equals(content)){
    break;
    }
    bos.write(content.getBytes());
    bos.flush();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

  字符缓冲流

 字符缓冲流: 流中的数据是在其内部的缓冲区中存储的(字符数组)

 字符缓冲输入流:BufferedReader

 BufferedReader(Reader in) 
          创建一个使用默认大小输入缓冲区的缓冲字符输入流。

 String readLine() 
          读取一个文本行。 弊端:没有换行


 void close() 
          关闭该流并释放与之关联的所有资源。 

 void mark(int readAheadLimit) 
          标记流中的当前位置。 

 boolean markSupported() 
          判断此流是否支持 mark() 操作(它一定支持)。 

 int read() 
          读取单个字符。 

 int read(char[] cbuf, int off, int len) 
          将字符读入数组的某一部分。 
 
 boolean ready() 
          判断此流是否已准备好被读取。 

 void reset() 
          将流重置到最新的标记。 

 long skip(long n) 
          跳过字符。


 字符缓冲输出流:BufferedWriter
 
 BufferedWriter(Writer out) 
          创建一个使用默认大小输出缓冲区的缓冲字符输出流。

 void newLine() 
          写入一个行分隔符。(新方法)**


 void close() 
          关闭此流,但要先刷新它。 

 void flush() 
          刷新该流的缓冲。  

 void write(char[] cbuf, int off, int len) 
          写入字符数组的某一部分。 

 void write(int c) 
          写入单个字符。 

 void write(String s, int off, int len) 
          写入字符串的某一部分。

 案例:使用字符缓冲流来实现,文件的拷贝 只能使用 readLine()方法来完成 写的时候怎么办?
          如何循环读取?

  工具类:

 public class BufferedUtil {
/*
* 1.绝对不能出现main
* 2.当前工具类中所有的属性必须是静态的
* 3.当前工具类中所有的方法都是静态方法
* ps:为什么都是静态的,因为工具类是不会创建对象的,所以若想调用当前类中属性和方法,那么就 必须是静
* 工具类的作用就是所有的类都可以通用当前类中方法
*/

public static void copyFile(File srcFile,File desFile){
   //1.创建当前字符缓冲流的对象
  //直接释放资源
  /*
   * 创建字符缓冲输入流对象 --> 参数 字符输入流的对象   
   * 创建字符缓冲输出流对象  --> 参数  字符输出流的对象
   * 若当前直接释放资源时有两个对象,那么对象与对象之间使用[;]分隔开
   */

try(BufferedReader br = new BufferedReader(new FileReader(srcFile));
BufferedWriter bw = new  BufferedWriter(new FileWriter(desFile))){
//1.循环读写之基本版
//明确使用的是什么流--> 字符流

// char[] cbuf = new char[1024];
// int len;
// while((len = br.read(cbuf))!=-1){
// bw.write(cbuf, 0, len);
// }
// bw.flush();
// System.out.println("老子写完了!!!!!!!");
 
//2.循环读写之进阶版
//ps:当前这种写法只能写下字符缓冲流中使用
/*
 * readLine 读取一行 --> 弊端 : 不能读取换行 返回值的类型是String
 * newLine 写入一个分割符(换行)
 */

//2.1创建一个空的字符串对象
/*
 * 三空
 * void-->使用在方法中的 -->代表没有返回值类型-->可以不写return
 * return -->带回一个返回值值  结束一个方法
 * 
 * null-->使用在引用类型 -->代表没有在堆栈空间中开辟(没有开辟空间-->没有地址)
 * 若在这个时候强行使用当前引用类--> 空指针异常 NullPointException
 * 无Bug不代码 --> 2小时写代码-->6 在修改bug
 * 
 * ""-->使用在字符串上-->代表的是空串(没有内容),但在字符串池中开辟空间  
 */

String str = null;
//循环读写
/*
 * 若当前我们这样判断读取的内容会有一定风险, 若当前文件中有多个换行,这样的判断就会少读取内      容
 * 若当前没有读取到内容,就相当于当前字符串没有在池中开辟空
 */

while((str = br.readLine())!=null){
bw.write(str);
  //ps:若使用readLine来读取数据,就必须添加newLine()
//就是为了保证格式的完成

bw.newLine();

}
bw.flush(); 
}catch (IOException e) {
  e.printStackTrace();
}  
}

   //扩展案例: 文件的拷贝(指定编码集)
   //改成字符缓冲来来完成
   //你读取正确,写的时候才能正确并修改编码
   /**
* 文件的拷贝(指定编码集)
* @param src  源文件
* @param des  目标文件
* @param srcCharSet  源文件的编码集
* @param desCharSet  目标文件的编码集  
* 所有的学习的流中只有一种流可以改变编码集     转换流

*/

public static void copy(File src,File des,String srcCharSet,String desCharSet){
       //1.创建当前字符缓冲输入输出流对象
  BufferedReader br = null;
  BufferedWriter bw = null;
  //InputStreamReader 和 OutputStreamWritrer --> 父类是 字符流
  //典型装饰者模式
  //1.会选择使用什么流 2,流与流之间的套用

  try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(src), srcCharSet));
   bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(des),desCharSet));
 //1.明确现在使用时什么流?  字符缓冲输入输出流
   //1.基本版本 -->直接用过char类型的数组循环的读写
   //2.进阶版本-->  readLine()读取一行,但没有换行   newLine() 加入分隔符号("\n")
   //2.1创建String类型的对象

   String content = null;
   while((content = br.readLine())!=null){
     bw.write(content);
     //这个地方必须添加newLine 这样可以保证格式的完整
     bw.newLine();
   }
  bw.flush();
  System.out.println("写完了!!!!!");
} catch (IOException e) {
System.out.println("当前异常信息:"+e.getMessage());
} finally {
//若出现了流的包含类型,那么只要释放当前创建出来的流对象,就可以所有流释放掉
if(bw != null){
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(br != null){
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}

  测试类:

 public class BufferedReaderAndWriter {
     public static void main(String[] args) {
      File srcFile = new File("src/com/qianfeng/day17/BufferedOutputStream/BufferedOutputStreamDemo.java");
      File desFile = new File("dir/BufferedOutputStreamDemo.java");
          // BufferedUtil.copyFile(srcFile, desFile);
      
           BufferedUtil.copy(srcFile, desFile, "GBK", "GBK");

 8.打印字节流打印字符流 

 1.PrintStream 打印字节流 -->字节系

  apperd()-->直接写文件  写在文件的末尾

  print()-->直接打印数据

  writer()-->直接写入数据 和输出流的用法完全相同

 2.PrintWriter 打印字符流 -->字符系

  apperd()-->直接写文件  写在文件的末尾

  print()-->直接打印数据

  writer()-->直接写入数据 和输出流的用法完全相同

  format("格式控制符","数据")

 打印字节流案例演示:

 public class PrintStreamDemo {
public static void main(String[] args) {
//1.创建字节打印流对象
try(PrintStream ps = new PrintStream("dir/字节打印流演示.txt")){
 //1. 追加
ps.append('男');
ps.append("你好");
//2.直接打印
ps.print("ok");
//print若是不带有ln--> 当前是不换行
//println带ln --> 会换行

ps.println(true);
//写入数据
ps.write("Ok".getBytes());
}catch (IOException e) {
e.printStackTrace();
}

 打印字符流案例演示:

 public class PrintWriterDemo {
public static void main(String[] args) {
//1.创建一个字符打印流对象
//字符系

try(PrintWriter pw = new PrintWriter(new FileWriter("dir/字符打印流演示.txt"))){
 pw.append('女');
 pw.print("你好");
 pw.write("哈哈哦啊好");
         //若使用format那么当前的格式控制符必须和后面出入的值一一对应
 pw.format("%d%s%.2f",100,"元都不给我!",123.1235);
}catch(IOException e){
e.printStackTrace();
}


  输入输出重定向

 System.in  标准输入<-->从控制台获取 <-- System.setIn() <-->从文件中读取

 System.out 标准输出<-->打印到控制台上 <--System.setOut() <-->写入到当前文件中

 类 FileDescritor -->文件句柄

 当前字段

 static FileDescriptor in
标准输入流的句柄

 static FileDescriptor out
标准输出流的句柄

 相当于系统为当前System.in和System.out留下的系统接口 -->帮助我们将改变了的标准输入和标准输出改回成系统 

 输入输出重定向演示案例:

 public class FileDescriptorDemo {
public static void main(String[] args) {
//1.进行源文件的读取
try(InputStream is = 
new FileInputStream("src/com/qianfeng/day17/BufferedInputStream/BufferedInputStreamDemo.java")){
//1.更改当前标准输入流的流向
System.setIn(is);
//字节打印流
PrintStream ps = new PrintStream("dir/BufferedInputStreamDemo.java");
//2.更改当前标准输出流的流向
System.setOut(ps);
byte[] b = new byte[1024];
int len;
while((len = System.in.read(b))!=-1){
System.out.write(b, 0, len);
}
System.out.flush();
//首先我们需要将流向进行重新的改变
//1.定义一个回到系统给的方向
//当前参数是一个字节输输出流

ps =  new PrintStream(new FileOutputStream(FileDescriptor.out));
System.setOut(ps);//ps 的流向 -->当前系统控制台 FileDescription.out
//当前这句会被写入到文件当中
System.out.println("写完了!!!!!!!!!");
}catch(IOException e){
e.printStackTrace();
}

  9.**对象流**

 对象流:流中的数据库是对象

当前流分两种:

对象(字节)输出流
ObjectOutputStream
FileOutputStream

对象(字节)输入流
ObjectInputStream
FileInputStream

什么是序列化
将对象固化到磁盘文件的过程称之为序列化-->ObjectOutputStream

什么是反序列化
将文件中的内容读取到当前内存中(对象)--> ObjectInputStream

ObjectOutputStream-->对象输出流-->序列化
  
ObjectOutputStream(OutputStream out) -->当前构造方法
         当前参数是一个字节输出流对象
void writerObject(Object obj) 将指定的对象写入到文件中-->序列化

ObjectInputStream-->对象输入流-->反序列化

ObjectInputStream(InputStream in) -->当前构造方法
当前参数是一个字节输入流对象

Object  readObject(); 从当前文件中将对象读取出来

 学生实体类:

 public class Student implements  Serializable{
private static final long serialVersionUID = 1L;
//属性
 //名字

private String name;
 //性别
private String gender;
 //年龄
private int age;
//构造方法 --> 有参 ,无参
//当前构造方法有两种访问权限修饰符
//公有 创建对象时使用     私有-->单利

public Student(){
//super关键字最大的作用,调用父类的有参构造方法来给来给当前属性赋值
//若发生重写,在当前勒种调用父类原有的方法 super.方法名()
// super(); //-->通过当前super关键字来调用父类的无参构造方法

}
//有参构造方法的作用-->就是为了当前属性的赋值
public Student(String name,String gender,int age){
//当前形参名与当前属性名一致
//this关键字最大的作用就是用来区分当前属性与形参的名字

this.name = name; //就近原则
this.gender = gender;
this.age = age;  
}
 //getter 访问器 获取当前属性值 所以一定有返回值,但没有参数
public String getName(){
return name;
}
 
//setter 访问器 给当前属性赋值用的 所以一定没有返回值,但是需要有参数类型
public void setName(String name){
this.name = name;
}
/**
* @return the gender
*/
public String getGender() {
return gender;
}
/**
* @param gender the gender to set
*/
public void setGender(String gender) {
this.gender = gender;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}

//若在不许要比较当前两个对象是否相同的时候,那么默认不需要提供hashcode和equals重写
//toString-->在不重写之前,打印当前对象 打印的是当前对象换算过后拼接而成的一个类似于地址的字符串
//我们需要查看当前对象中的属性-->直接打印对象,我们就必须重写当期方法

/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
//当期字符串是自定义的,只要合法即可
return "Student [name=" + name + ", gender=" + gender + ", age=" + age + "]";
}

  序列化案例演示:

 public class ObjectOutputStreamDemo {
/**
* 序列化-->将当前对象写入到磁盘文件中的过程-->就必须调用ObjecOuputStream类来完成
*/

public static void main(String[] args) {
 //1.提供一个Student类的对象
Student  student  = new Student("拓也哥", "男", 40);
//2.将当前对象写入到当前磁盘文件中-->序列化
//3.创建一个ObjectOutputStream对象
//传入的参数是一个字节输出流对象

try(ObjectOutputStream  oos = new ObjectOutputStream(new FileOutputStream("dir/序列化对象文件.txt"))) {
  //2.若需要给自定义对象进行序列化就必须实现一个接口Serializable
  oos.writeObject(student);
  oos.flush();
  System.out.println("完成!!!!!");
}catch(IOException e){
e.printStackTrace();
}

  反序列化案例演示:

 public class ObjectInputStreamDemo {
/**
*反序列化-->将文件中的内容读取到当前内存中(对象)-->ObjectInputStream 反序列化
*/

public static void main(String[] args) {
//1.创建当前对象输入流的ObjectInputStream对象
//当前输入的参数是一个字节输入流对象

try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dir/序列化对象文件.txt"))){
//将文件中的对象读取到内存中-->反序列化
//当前方法是有返回值--> Object类型

Object obj =  ois.readObject();
//当前读取的对象类型是Object类型 父类类型,但实际类型是Studnet是其子类类型
//需要进行强制类型转换,有风险,为了避免风险的发生 
//   instanceof --> 可以检查当前对象是否属于后面的类型

if(obj instanceof Student){
Student stu = (Student)obj;
System.out.println(stu);
}else{
System.out.println("当前对象不属于后面的类型");
}
System.out.println("当前obj内容是:"+obj);
}catch(IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

 总结:
 对象的序列化和反序列化
 对象的序列化就是将当前对象写入到当前的磁盘文件中,若需要序列化就必须使用一个流
 对象输出流 ObjectOutputStream
 并且调用其 writerObject(当前对象)方法
 若要给自定类进行序列化,那么当前类必须实现 Serializable接口
 并且当期接口没有任何需要实现的方法
 若给自定义类实现序列化但是没有实现Serializable接口,那么就会抛出NotSerializableException的异常

 对象的反序列化将当前文件中的内容读取到当前内中,需要使用反序列化那么必须使用一个流
 对象输入流 ObjectInputStream
 并调用其方法  readObject()
 当前方法是有返回值的其返回值类型是Object类型 父类类型
 若需要使用其实际类型,那么就需要对其进行强制类型转换,为了防止有错误发生
 可以使用一个关键字进行检测  instanceof 

 ===>扩展:多个对象的序列化和反序列化

 实体类(上面的那个Student ):

 多个对象的序列化和反序列化案例演示:

 public class MoreObjectStreamDemo {
/**
* 序列化 --> 将对象固化到磁盘文件中 -->ObjectOutputStream
* 反序列化--> 磁盘中的文件读取到内存中 --> ObjectInputStream
*/

public static void main(String[] args) {
//存储多个对象用集合
//若当前集合不使用泛型的情况下默认是Object,若规定泛型那么默认就是当前泛型所指定的数据类型

List<Student> list = new ArrayList<>();
//第一是集合
//第二是 是要初始化的数据(集合中存储出的对象)
//在多个对象序列化的时候,在做原始数据存储时需要在最后一位添加 null

Collections.addAll(list, new Student("战三","中性",19),
new Student("乐乐","男",30),new Student("旺旺","女",1),null);
  //writeToFile(list);
   List<Student> stuList = readFormFile();
   for(Student stu : stuList){
    System.out.println(stu);
   }
   List<Student> stuList2 = new ArrayList<>();
   readFormFile(stuList2);
   for(Student stu : stuList2){
    System.out.println(stu);
   }

}
/**
*  将多个对象进行序列化
* @param list 集合存储着多个对象
*/

public  static void  writeToFile(List<Student> list){
  //1.创建当前ObjectOutputStream对象
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dir/多个文件的序列化"))){
//2.向文件中对象(序列化)
for(Student stu : list){
oos.writeObject(stu);
}
  oos.flush();
  System.out.println("序列化完成!!!!");
  
// for(int  i = 0;i<5;i++){
// oos.writeObject(new  Student(name, gender, age));
// }   
// oos.writeObject(null);
//


}catch(IOException e){
e.printStackTrace();
}

}
/**
*  对多个对象的反序列化
* @return  存储着对象集合
*/

public static List<Student> readFormFile(){
List<Student> list = new  ArrayList<>();
//1.创建ObjectInputStream对象
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dir/多个文件的序列化"))){
//2.读取文件中的对象(反序列化)
//当前readObject->返回值Object

Object obj = null;
//若当前表达成证明obj中得到了对象
//若当前表达式不成立 obj 得到的是一个null
//EOFException 正常当前读取到最后一个对象的时候是没有值得,而我们在次强行读取了一次
//当前文件中只存储了3个对象,但是我们判断的时候,判断到位null停止

while((obj = ois.readObject())!=null){ 
if(obj instanceof Student){
Student stu = (Student)obj;
//加入到一个集合中
list.add(stu);
}

}

}catch(IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return list;

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值