赤赤赤赤辰

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

IO

  • 重点:节点流 缓冲流 转换流 对象流

1615633033628

  • 1615629839882

  • 1615629883782

    了解即可的流:标准输入输出流,打印流 ,数据流 。对象流是要求掌握的

Java IO原理及流的分类

  • I/O是Input/Output的缩写。I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等
  • Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行
  • Java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据
  • 文件本身没有读写数据的能力 得用流才行

流的原理

  • 输入input:读取外部的数据(磁盘,光盘等存储设备的数据)到程序(内存)中
  • 输出output:将程序(内存)数据输出到磁盘,光盘等存储设备中

流的分类

  • 操作数据单位不同分为:字节流(8bit),字符流(16bit)(一个字节8位 一个字符是两个字节)
  • 按数据流的流向不同分为:输入流,输出流
  • 流的角色不同分为:节点流,处理流

(节点流:直接作用于文件)(处理流:作用于已有的流)

  • 节点流可以从一个特定的数据源读写数据
  • 处理流是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能
(抽象基类) 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
  • java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生而来
  • 由这4个类派生出来的子类名称都是以其父类名作为名字后缀
/*
为什么要有抽象类

因为我们实际去读写的时候太复杂了 我们在这里没有办法知名如何去读去写 这些交给子类去做 我们在这里只从设计的角度讲要想读和写该提供哪些功能 我们把他们呢定义在父类中 具体如何实施子类去做 父类的方法就都抽象化
*/

img

  • IO流体系【蓝框】重要

  • 输入输出标准 只要读入都是read 只要输出都是write

  • 以四个基本的节点流为例 把基本输入输出以及套路弄清楚 后续的流关心他们的功能即可 具体实施方面跟这四个特别像 关心什么时候用 功能,怎么用

  • img

  • 只有FIleInputStream FileReader,FileOutputStream,FileWriter 是节点流,下面的都是处理流

  • 程序中打开的文件IO资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显示关闭文件IO资源

节点流(文件流)

抽象基类 节点流(文件流) 缓冲流(处理流的一种)
InputStream(字节流) FileInputStream ( read(byte[] buffer) ) BufferedInputStream ( read(byte[] buffer) )
OutputStream(字节流) FileOutputStream ( write(byte[] buffer,o,len) ) BufferedOutputStream ( write(byte[] buffer,o,len) ) / flush()
Reader(字符流) FileReader ( read(char[] cbuf) ) BufferedReader ( read(char[] cbuf) ) / readLine()
Writer(字符流) FileWriter ( write(char[] cbuf,0,len) ) BufferedWriter ( write(char[] cbuf,0,len) ) / flush()

FileReader

  • 套路:
      1. File类的实例化 指明要操作的文件 File file = new File("....");
      2. 提供具体的流 FileReader fr = new FileReader(file);
      3. 数据的读入
      4. 资源的关闭
    • 还有try-catch-finally{}
//在main中
File f1 = new File("hello.txt");//相较于当前工程
System.out.println(f1.getAbsolutePath());
File f2 = new File("shang0\\hello.txt");
System.out.println(f2.getAbsolutePath());
//C:\Users\de'l'l\eclipse-workspace\shang0\hello.txt
//C:\Users\de'l'l\eclipse-workspace\shang0\shang0\hello.txt

@Test
public void TestFileReadr(){
    File file = new File("hello.txt");//相较于当前Module
}
package shang0;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

/**
 * 一、流的分类
 * 1.操作数据单位:字节流,字符流
 * 2.数据的流向:输入流 输出流
 * 3.流的角色:节点流,处理流
 * 
 * 二。流的体系结构
 * 抽象基类				节点流(文件流)			缓冲流(处理流的一种)
 * InputStream			FileInputStream			BufferedInputStream
 * OutputStream			FileOutputStream		BufferedOutputStream
 * Reader				FileReader				BufferedReader
 * Writer				FileWriter				BufferedWriter
 * 
 * 
 * @author de'l'l
 */

public class FileReaderWriterTest {

	/**把硬盘中文件里的内容读入到内存里**/
	/**说明点:
	 * 1.read()的理解:返回读入的一个字符 如果到达文件末尾,返回-1
	 * 2.异常的处理:为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally处理
	 * 3.读入文件一定要存在,否则就会报FileNotFoundException
	 * @param args
	 */
	public static void main(String[] args) /*throws IOException*/ {
		// TODO Auto-generated method stub
		FileReader fr=null;
		try {
			//1.实例化File类对象,指明要操作的文件
			File file = new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang0\\src\\shang0\\hello.txt");
			
			//2.提供具体的流
			fr = new FileReader(file);
			
			//3.数据的读入
			//read() :返回读入的一个字符 如果达到文件末尾 返回-1
			int data;
			while((data=fr.read())!=-1) {
				System.out.print((char)data);
			}
		}catch(IOException e){
			e.printStackTrace();
		}finally {
			//4.流的关闭
			try {
//fr = new FileReader(file); 防止在这一步没有找到文件出现异常 FielNotFoundException 我们不能使用空指针关闭
// try里包if或者if里包catch均可
//				if(fr!=null) {
//					try {
//						fr.close();
//					}catch(IOException e) {
//						e.printStackTrace();
//					}
//				}
                
			//判断操作 是属于赋值之外的其他操作
			//局部变量没有默认赋值 
			//在第一次赋值之前不能进行赋值以外的其他操作
                //所以最开始要把fr初始化为null
				if(fr!=null) {
					fr.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
hello world
  • FileReader 的 read()升级操作
/*
 * read()方法
 * 1.空参 read() 每次只读取一个 并返回读取的字符
 * 2.read(char[] cbuff) 每次尽量读取数组长度个字符 存入char型数组
 * 如果都不满就尽量读 能读几个读几个(能覆盖几个就覆盖几个位置)
 * 3.read(char cbuf[],int off,int len) (这是一个更一般的方法) 往cbuf里读数据 不放满 每次只固定放几个
 *  read(char []cbuff,0,cbuff.length)就是2.
 *  public int read(char cbuf[]) throws IOException{
 *  	return read(cbuf,0,cbuf.length);
 *  }
 */


public class FileReaderWriterTest {
	
	//对read()操作升级
	//这一章都逃不出这四步 (2,3可能会有不同)
	@Test
	public void TestFileReader()  {
		//2.FileReader流的实例化
		FileReader fr = null;
		try {
			//1.File类的实例化
			File file = new File("C:\\\\Users\\\\de'l'l\\\\eclipse-workspace\\\\shang0\\\\src\\\\shang0\\\\hello.txt");
			
			fr = new FileReader(file);
			
			//3.读入的具体操作细节
			//read(carbuffer); 把从硬盘文件读取的数据读到char型数组carbuffer里中 carbuffer--cbuf 缓冲小车
			//read(carbuffer)返回每次读入carbuffer数组中的字符个数 如果达到文件末尾 返回-1
            //字符型!!char数组做FileReader的read()临时存放地!!!
            //字节型则要用byte数组做InputStream的read的临时存放地
			char[] carBuffer = new char[5];
			int len=0;
			while((len=fr.read(carBuffer))!=-1) {
				//错误写法:
				//for(int i=0;i<carBuffer.length;i++){System.out.println(carBUffer[i]);}
				//每回read向carBuffer中读数据时 是覆盖上一轮读的 所以如果最后一轮读的不满5个 会输出倒数第二轮读入carBuffer的数据
				
				//正确写法
//				for(int i=0;i<len;i++) {
//					System.out.print(carBuffer[i]);
//				}
				
				
				//方式二:(错误写法)
				//char型数组到String转换 调用构造器
				//最后一轮str也是存储了上一轮的部分数据
//				String str = new String(carBuffer);
//				System.out.print(str);
				
				//正确写法
				//从carBuffer中取数据 从0开始取 每次只取len个						
				String str = new String(carBuffer,0,len);
				System.out.print(str);
                //等价于上面的循环写法			
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			//4.资源关闭
			try {
				if(fr!=null){
					fr.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
    }
//char[]数到String类型的转换写法
//调用String的构造器
char []ch="hello";
String str;
str = new String(ch); //----->hello
str = new String(ch,0,1);//--->h 
	//(字符型型数组名,起始位置,转换几个)

FileWriter

读入:硬盘->内存

写出:内存->硬盘

/*说明:
	1. 输出操作:对应File可以不存在的,并不会报异常
	2. File对应的硬盘中的文件如果不存在,并不会报异常
		File对应的硬盘中的文件如果存在:
			如果流使用的构造器是:FileWriter(file,false)/FileWriter(file) :对原有文件进行覆盖
			如果流使用的构造器是:FileWriter(file,true):则不会对原有文件进行覆盖 而是在原内容之上追加
	套路:先throw 写完之后再改成try catch finally
	 */
	
	
	@Test
	public void testFileWriter(){
		//2.提供具体的流
		FileWriter fw = null;
		try {
			//1.提供File类的对象,指明写出到的文件
			File file = new File("hello1.txt");
			
			fw = new FileWriter(file,true);
            //IOException 可能会创建文件失败把应该 
			//异常之后会没有给fw赋值 那么后面的关闭就不能在一个变量未初始化之前做动作(不然编译error) 所以要给其初始化
			
			//3.写出的操作
			fw.write("I am the winner\n");
			fw.write("I hate you");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(fw !=null) {
				//4.流资源的关闭
				try {
					fw.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
	}
hello2.txt
I am the winner
I hate you
  • FileReader 与 FileWriter 结合应用
  • 从硬盘中读取一个文件 复制到另一个文件中
	/**从硬盘中读取一个文件 复制到另一个文件中**/	@Test	public void testFileReaderFileWriter()  {		//2.提供读入写出流		FileReader fr=null;		FileWriter fw=null;		try {			//1.File类的实例化 一个是要从硬盘中那个文件读入  一个是要输出的文件			File f1 = new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang0\\src\\shang0\\hello.txt");			File f2 = new File("hello2.txt");						fr = new FileReader(f1);			fw = new FileWriter(f2);						//3.读入写出的具体操作			char []cbuf = new char[5];			int len=0;			while((len=fr.read(cbuf))!=-1){				fw.write(cbuf,0,len);//			for(int i=0;i<len;i++) {//				System.out.print(cbuf[i]);//			}			}		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} finally {			//先关闭哪个都行			//两个if fw有异常时也可以实现fr关闭			if(fw!=null) {				try {					fw.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}						if(fr!=null) {				try {					fr.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}		}
hello.txt    hello world12hello2.txt    hello world12
  • FileReader 和FileWriter 只能用来处理字符型数据 不能用来处理图片(字节)(二进制)

InputStream

  • 为什么用字节流去处理英文字符型文本转换成String不乱码?

    ​ - 英文中的abcd用一个字节就足够存下

    ​ A~65 a~97 z~122 都小于127 所以可以用一个byte存下

    ​ 有中文就不行了 一个中午在UTF-8里占三个字节

    ​ 如helloworld123中国人 组成一个中文的字节有可能被拆开 (我们下面的程序是吧她读入到byte []cbuf = new byte【5】;里

    ​ 则 h e l l o w o r l d 1 2 3 中1 中2 中3 国1 国2 国3 人1人2 人3 国2 国3 人1

  • 结论

  • 对于文本文件(.txt .java .c .cpp) 使用字符流处理

  • 对于非文本文件,(.jpg,.mps,.mp4,avi,doc,ppt.....)使用字节流处理

package shang0review;/**- - 结论:- 对于文本文件(.txt   .java   .c  .cpp) 使用字符流处理- 对于非文本文件,(.jpg,.mps,.mp4,avi,doc,ppt.....)使用字节流处理**/import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;//使用字节流处理文本文件 在控制台输出时 可能出现乱码public class TestFileInoutStream {	public static void main(String[] args){		//2.提供具体的流		FileInputStream fis=null;				try {			// TODO Auto-generated method stub			//1.实例化File类			File file = new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang0review\\src\\shang0review\\cheer up.txt");			fis = new FileInputStream(file);			//3,具体操作			byte[] cbuffer = new byte[5];			int len;			while ((len = fis.read(cbuffer)) != -1) {				String str = new String(cbuffer, 0, len);				System.out.print(str);			} 		} catch (Exception e) {			// TODO: handle exception		} finally {			if(fis!=null) {				try {					//4.关闭资源					fis.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}	}}
cheer up.txthey~girl!我爱你啊控制台输出:hey~girl!?野?啊

OutputStream

  • 读取图片文件
package shang0review;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class TestFileInputFileOutputStream {	public static void main(String[] args)  {		//2.提供具体的流 造流时指明是流向哪个文件或者从哪个文件流出的		FileInputStream fis=null;		FileOutputStream fos=null;				try {			// TODO Auto-generated method stub			//1.实例化File类(要读取和写入的对象)			File file1 = new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang0review\\src\\shang0review\\猪啊猪.jpg");			File file2 = new File("小飞猪.jpg");			fis = new FileInputStream(file1);			fos = new FileOutputStream(file2);			//3.具体操作			int len = 0;//记录读取长度 便于判断是否结束 以及每次输出多少			byte[] cbuff = new byte[5];//缓冲小车 每轮从硬盘中读取数据存到这里			while ((len = fis.read(cbuff)) != -1) {				fos.write(cbuff, 0, len);/**写jpg图片文件**/                //不能String str = new String(cbuff,0,len)                  //  fos.write(str)                //FileOutputStream的write只能是int(byte也算是int)			} 		} catch (Exception e) {			// TODO: handle exception		} finally {			//4.关闭资源			if(fis!=null) {				try {					fis.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}			if(fos!=null) {				try {					fos.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}			}	}}
成功制作了一个 “小飞猪.jpg” 文件 里面是一张图片
  • 补充结论
  • 如果只是把文件从硬盘中读出再复制到另一个文件里 中间不再内存层面(程序)读取 那么用字节流也行
  • 对于文本文件来说
  • 字节流:
  • 硬盘源文件--------->内存层面(程序控制台)转化成String显示-------->要写入的文件
  • 字节流中间无法转化(会有乱码 原因是因为会有”截断“ 详情见前面) 但如果不转化 只是把他复制到另一个文件 是ok的 只做搬运工
  • 字符流:可以转化
  • 硬盘源文件------->内存层面(程序控制台)转化成String显示-------->要写入的文件
  • 文本文件最好用用字符流 显示到控制台 再复制到目标文件中更好一些,当然,如果只做文件复制的话 字节流也ok
  • 但是对于非文本文件来说(图片) 只能用字节流来读取,不能用字符流,即便用字符流但是不显示到控制台也不行!!
public static void main(String []args){   套下面的程序}z//这是一个下载图片文件的函数!!自己写的!!牛!!public void CopyFile(String srcPath,String desPath) {		//		System.out.println(file1.getAbsolutePath());		//2.提供具体的流		FileInputStream fis=null;		FileOutputStream fos=null;		try {			//1.实例化file类			File file1 = new File(srcPath);//			File file1 = new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang01erview\\src\\shang01erview\\猪啊猪.jpg");			File file2 = new File(desPath);			fis = new FileInputStream(file1);			fos = new FileOutputStream(file2);			//3具体操作			int len = 0;			byte[] carbuf = new byte[1024];//1024较快			while ((len = fis.read(carbuf)) != -1) {				fos.write(carbuf, 0, len);			} 		} catch (Exception e) {			e.printStackTrace();			// TODO: handle exception		}finally {            //4.关闭资源			if(fis!=null) {				try {					fis.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}			if(fos!=null) {				try {					fos.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}		}//参数:源文件名,目标文件名	@Test	public void TestCopyFile() {		//获取当前系统时间		long start = System.currentTimeMillis();//获取系统当前时间				String srcPath = "C:\\Users\\de'l'l\\Desktop\\01.mov";		String desPath = "C:\\Users\\de'l'l\\Desktop\\02.mov";		CopyFile(srcPath,desPath);				long end = System.currentTimeMillis();				System.out.println("复制操作花费的时间为:"+(end-start));	}
结果:成功将一个桌面的视频文件复制到桌面//复制操作花费的时间为:109

处理流

一、缓冲流

  • **1.缓冲流 ** 处理流的一种

  • BufferedInputStream

  • BufferedOutputStream

  • BufferedReader

  • BufferedWriter

  • 2.作用:提高流的读取,写入的速度

  • 能够提高读写效率原因:内部提供了一个缓冲区

  • 为什么更快?

  • BufferedIputStreeam/OutputStream/Reader/Writer 在内部提供了一个缓冲区 byte[1024*8]
    // 先读到buffer里缓存着 达到指定大小之后再依次写出 一次多读一些 这样运的次数就少一些 就会快一些
    //例如 100块砖 现要从硬盘读取到内存再写入文件 硬盘--->内存--->文件
    //假设从硬盘到内存 路费10s 从内存到文件 路费10s
    //有两种搬运方式
    //1.一次搬一块 搬一块就把一块送到要写入的文件里去 一共要花100 (10+10)=2000s
    //2.用一个小车 小车能装10块 装满小车的花费为10s 那么要花 100/10
    (10+10)=300s
    //明显提高了速率

    ​ 两种方式装砖的时间是一样的,不一样的是运输的时间。显然方案2提高了效率

  • 3.处理流:就是"套接"已有的流的基础之上(不一定是节点流)

BufferedInputStream BufferedOutputStream

  • public class BufferedInputStream extends FilterInputStream {    private static int DEFAULT_BUFFER_SIZE = 8192;     //  8192/1024=8; 8个1024    ..............    public BufferedInputStream(InputStream in) {        this(in, DEFAULT_BUFFER_SIZE); //该构造器调用另一个参数表符合(InputStream ..,int ...)的构造器    }        public BufferedInputStream(InputStream in, int size) {        super(in);        if (size <= 0) {            throw new 		IllegalArgumentException("Buffer size <= 0");        }        buf = new byte[size];        //即在内部提供了一个缓冲区 byte[1024*8]       // 先读到buffer里缓存着 达到指定大小之后再依次写出		一次多读一些 这样运的次数就少一些 就会快一些        //例如 100块砖 现要从硬盘读取到内存再写入文件 硬盘--->内存--->文件        //假设从硬盘到内存 路费10s 从内存到文件 路费10s        //有两种搬运方式         //1.一次搬一块 搬一块就把一块送到要写入的文件里去 一共要花100*(10+10)=2000s        //2.用一个小车 小车能装10块 装满小车的花费为10s 那么要花 100/10*(10+10+10)=300s        //明显提高了速率    }            }
    
  • 案例:非文本文件的复制

      1. 造文件
    1. 造流
    • 2·1 造字节流
    FileInputStream fis = new FileInputStream(file1);  FileOutputStream fos = new FileOutputStream(file2);
    
    • 2.2 造缓冲流
     BufferedInputStream bis = new BufferedInputStream (fis);	BufferedOutputStream  bos = new BufferedOutputStream(fos);
    
    3. 复制细节:读取 写出 int len=0;    byte []cbufer = new byte[5];    bis.read(cbufer);
    
    4. 关闭资源 bis.close()   bos.close()
    
  • 案例

package shangbuffer;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 org.junit.Test;public class BufferedTest {	/*	 * 实现非文本文件的复制	 */	@Test	public void BufferedStreamTest() {		//2.造流		//2.1造字节流		FileInputStream fis = null;		FileOutputStream fos = null;		//2.2造缓冲流		BufferedInputStream bis = null;		BufferedOutputStream bos = null;				try {			//1.File类的实例化			File srcFile = new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang0review\\小飞猪.jpg");			File desFile = new File("C:\\\\Users\\\\de'l'l\\\\eclipse-workspace\\\\shang0review\\\\赤赤.jpg");			fis = new FileInputStream(srcFile);			fos = new FileOutputStream(desFile);			bis = new BufferedInputStream(fis);			bos = new BufferedOutputStream(fos);			//3,复制细节操作 :读取 写入			byte[] cbufer = new byte[10];			int len = 0;			while ((len = bis.read(cbufer)) != -1) {				bos.write(cbufer, 0, len);                                bos.flush();//刷新缓冲区                //把缓冲区的数据清空 输出到文件				//缓冲区读到8924就会自动flush()				                			} 		} catch (Exception e) {			// TODO: handle exception		} finally {			if(bis!=null)			{				try {					bos.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}						if(bos!=null)			{				try {					bis.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}		//4.关闭资源		//要求:先关闭外部的流,再关闭内层的流//		bos.close();//		bis.close();		//说明:关闭外层流的同时,内层流也会自动进行关闭,所以我们可以省略对内层流的关闭//		fis.close();//		fos.close();	}				public static void main(String[] args) {		// TODO Auto-generated method stub			}}
与上面那个没有BufferedInputStream BufferedOutputStream 相比 同一文件更快 那个109ms//复制花费的时间为:25(ms)

自我小结

#读入文本用字符流FileReader /FileWriter (也可以用字节 如果不转化成String的话),读入非文本用字节流(FileInputStream,FileOutputStream) 是害怕乱码#读入文本/非文本用Buffered缓冲流 BufferedInputStream , BufferedOutputStream,BufferedReader, BufferWriter是为了提高速度

BufferedReader BufferedWriter

  • BufferedReader/BufferedWriter 结果不知道为啥有乱码 求指教???????(shang0 review)
  • 有乱码原因:charset不对 要读取的文件的charset是UTF-8,但是我的eclipise的默认charset不是UTF-8
  • 大概是应该要用InputStreamReader把输入字节流InputStream转化成输出字节流时顺便改变charset?
  • InputStreamReader isr = new InputStreamReader (new FileInputStream(new File(".....")) , "UTF-8" ) ;
	public void CopyFFilewithBuffered(String srcPath,String desPath) {		//2.2处理流 缓冲流		BufferedReader br=null;		BufferedWriter bw=null;				try {			//1.实例化File类			File srcFile = new File(srcPath);			File desFile = new File(desPath);			//2.提供具体流			//2.1节点流 字节流			FileReader fr = new FileReader(srcFile);			FileWriter fw = new FileWriter(desFile);			br = new BufferedReader(fr);			bw = new BufferedWriter(fw);            乱码原因:bw从文件中读取内容时 并不是按照编码UTF-8来读取的 使得读取到cbufer中就存在乱码 所以再从cbufer中写到要保存的文件中自然会有乱码            			//3.复制具体操作//			int len = 0;//			char[] cbufer = new char[1024];//			while ((len = br.read(cbufer)) != -1) {//				String str = new String(cbufer, 0, len);//				System.out.println(str);//转化成字符串,在内存层面上显示							//方式一:使用char数组//				bw.write(cbufer,0,len);////			fw.flush();缓冲区满了之后自动flush//			}					//方式二:使用String  一次读一行			//readLine()返回字符串 读完之后返回null				String data;				while((data=br.readLine())!=null) {					//方法一://					bw.write(data+"\n");//data中不包含换行符					//方法二:					bw.write(data);					bw.newLine();                    		} catch (Exception e) {			// TODO: handle exception		} finally {			if(br!=null) {				try {					br.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}						if(bw!=null) {				try {					bw.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}//		//4.关闭资源//		br.close();//		bw.close();//			}		@Test	public void testCopyFFilewithBuffered() {		long start = System.currentTimeMillis();		String srcPath = "C:\\Users\\de'l'l\\Desktop\\猪猪.txt";		String desPath = "C:\\Users\\\\de'l'l\\\\Desktop\\\\鹿鹿.txt";		CopyFFilewithBuffered(srcPath,desPath);		long end = System.currentTimeMillis();		System.out.println("复制花费时间为"+(end-start));	

乱码

#include<iostream>//鏂规硶涔嬪悗浼樺寲鏋佸ぇ//[1]涓轰簡鑳介亶鍘嗘墍鏈夋儏鍐碉紝鎴戜滑棣栧厛鑰冭檻鎼滅储椤...-------------------------------------------------------------    改进见下
//1.1 节点流//FileInputStream 字节输入流 InputStreamReader 将其转化为字符输入流输入 FileInputStream fis=new FileInputStream (newFile("C:\\Users\\de'l'l\\Desktop\\pp.txt"));FileWriter fw = new FileWriter(new File("C:\\Users\\de'l'l\\Desktop\\hh.txt"));				//1.2 处理流:转换流 	//new InputStreamReader(转换流要作用到的字节型的节点流,字符集(charset))		//参数2指明了字符集,具体使用哪个字符,取决于文件pp.txt保存时使用的字符集		InputStreamReader isr = new InputStreamReader(fis,"UTF-8");		//据说使用的默认是UTF-8 但我的不是				//1.3 处理流:缓冲流		BufferedWriter bw = new BufferedWriter(fw);				//2.具体细节		char []cbufer = new char[20];		int len;		//InputStreamReader是字符流 所以操作读到char型数组		while((len=isr.read(cbufer))!=-1) {			String str = new String(cbufer,0,len);//String构造器里没有可以指明字符集的地方			System.out.print(str);//读入cbufer时就会读入文本中的换行 所以不需要自己加			bw.write(str);		}				//关闭资源		isr.close();		bw.close();//一定要记得关闭资源!!不然输入到文件中的内容会丢失!!!

运行结果无乱码

输出到控制台上的无乱码 并且保存到文件中的也没有乱码

缓冲流复习复习缓冲流复习 加密

  • 1615536912904

    1. 这几个都是字节流 但也能实现文本文件的复制

      但是不能在控制台看 只能复制 所以我们推荐用字符流处理

    2. int b=0;while((b=fis.read())!=-1){    fos.write(b^s);//异或运算 打乱字节 加密}
      
    3. mnn=m

      m连续异或上同一个数 还是m

      加密/解密 都是如下

      int len = 0;byte []bbufer = new byte[1024];//千万别忘了在方法里写数组!!!while((len=bis.read(bbufer))!=-1){for(int i=0;i<len;i++) bbufer[i] = (byte) bbufer[i]^5;	​	bis.read(bbufer,0,len);}
      
  package shang1;  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 org.junit.Test;    public class PicTest {  	public static void main(String []args) {  		  	}  	  	//图片的加密  	@Test  	public void TestSecret() {  //		//1.造文件  //		File f1 = new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang1\\src\\小飞猪.jpg");  //		File f2 = new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang1\\src\\肥肥.jpg");  //		  //		//2.造流  //		FileInputStream fis = new FileInputStream(f1);  //		FileOutputStream fos = new FileOutputStream(f2);  //		BufferedInputStream bis = new BufferedInputStream(fis);  //		BufferedOutputStream bos = new BufferedOutputStream(fos);  //		  		//1.造文件以及流  		//[缓冲流 [ 节点流 [ 文件.......]  ]  ]  		//直接用文件名也可 FileInputStream的构造器会把它包装成一个File类  		BufferedInputStream bis=null;  		BufferedOutputStream bos=null;  		  		try {  			bis = new BufferedInputStream(  					new FileInputStream(new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang1\\src\\小飞猪.jpg")));  			bos = new BufferedOutputStream(  					new FileOutputStream(new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang1\\src\\hhhh.jpg")));  			  			//2.具体操作  			int len = 0;  			byte[] bbufer = new byte[1024];//[bbufer-->byte buffer]              //别忘了在方法里写byte数组!!!!!  			while ((len = bis.read(bbufer)) != -1) {  				//字节数据进行修改 来加密  				for (int i = 0; i < len; i++)  					bbufer[i] = (byte) (bbufer[i] ^ 5); //int 和byte运算完之后转化为byte  				bos.write(bbufer, 0, len);  			}   		} catch (Exception e) {  			// TODO: handle exception              e.printSteckTrace();  			if(bos!=null) {  				try {  					bos.close();  				} catch (IOException e1) {  					// TODO Auto-generated catch block  					e1.printStackTrace();  				}  			}  			if(bis!=null) {  				try {  					bos.close();  				} catch (IOException e1) {  					// TODO Auto-generated catch block  					e1.printStackTrace();  				}  			}  		}  	}  	  	  	//图片的解密  	//连续异或同以一个数两次 不变---m^n^n=m  		@Test  		public void FightSecret() {  			  			//1.造文件以及流  			//[缓冲流 [ 节点流 [ 文件.......]  ]  ]  			//直接用文件名也可 FileInputStream的构造器会把它包装成一个File类  			BufferedInputStream bis=null;  			BufferedOutputStream bos=null;  			  			try {  				bis = new BufferedInputStream(  						new FileInputStream(new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang1\\src\\小飞猪secret.jpg")));  				bos = new BufferedOutputStream(  						new FileOutputStream(new File("C:\\Users\\de'l'l\\eclipse-workspace\\shang1\\src\\小飞猪4.jpg")));  				//2.具体操作  				int len = 0;  				byte[] bbufer = new byte[1024];  				while ((len = bis.read(bbufer)) != -1) {  					//字节数据进行修改 来加密  					for (int i = 0; i < len; i++)  						bbufer[i] = (byte) (bbufer[i] ^ 5); //int 和byte运算完之后转化为byte  					bos.write(bbufer, 0, len);  				}   			} catch (Exception e) {  				e.printStackTrace();  				// TODO: handle exception  				if(bos!=null) {  					try {  						bos.close();  					} catch (IOException e1) {  						// TODO Auto-generated catch block  						e1.printStackTrace();  					}  				}  				if(bis!=null) {  					try {  						bos.close();  					} catch (IOException e1) {  						// TODO Auto-generated catch block  						e1.printStackTrace();  					}  				}  			}  		}  		  }  

运行结果

1615540462961

未完成任务

记录每个字符出现的次数 map实现


1615650839047

1615650858710

二、转换流

InputStreamReader / OutputStreamWriter

  • 转换流提供了在字符流字节流之间的转换

  • Java API提供了两个转换流

    • InputStreamReader:将InputStream转换为Reader
    • OutputStreamWriter:将Writer转换成OutputStream
  • 字节流中的数据都是字符时,转换成字符流的操作更高效

  • 很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能

  • 解码:utf8.txt---字节流---->InputStreamReader(utf-8)----字符流-->程序(内存)

    编码:程序---字符流-------->OutputStreamWriter(gbk)-----字节流--->gbk.txt

1615541801233

1615542044151

    1. (看后缀)转换流:属于字符流
    • 涉及到的类:

    • InputStreamReader:将一个字节的输入流转换为字符的输入流 ----------解码:字节,字节数组---->字符数组,字符串

    • OutputStreamWriter:将一个字符的输出转换为字节的输出流 ----------编码:字符数粗,字符串---->字节,字节数组

    1. 作用:提供字节流和字符流之间的转换
    1. 字符集:文件编码的方式(比如:GBK) ,决定了解析时使用的字符集(也只能是GBK)
  • InputStreamReadr和OutputStream案例

package zhuanhuanliu;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;/* * InputStreamReader 实现字节输入流到字符输出流的转化 */public class InputStreamReaderAndOutputStreamReaderTest {	public static void main (String args[]) throws IOException {		//此处应当使用try-catch-finally 但是我偷懒了 				//1.造文件以及流		//1.1		FileInputStream fis = new FileInputStream(new File("C:\\Users\\de'l'l\\Desktop\\pp.txt"));		//1.2转换流 	//new InputStreamReader(转换流要作用到的字节型的节点流,字符集(charset))		//参数2指明了字符集,具体使用哪个字符,取决于文件pp.txt保存时使用的字符集		InputStreamReader isr = new InputStreamReader(fis,"UTF-8");		//据说使用的默认是UTF-8 但我的不是				//2.具体细节		char []cbufer = new char[20];		int len;		//InputStreamReader是字符流 所以操作到char型数组		while((len=isr.read(cbufer))!=-1) {			String str = new String(cbufer,0,len);//String构造器里没有可以指明字符集的地方			System.out.print(str);//读入cbufer时就会读入文本中的换行 所以不需要自己加		}				//关闭资源		isr.close();			}}

指明正确charset,运行结果没有乱码

#include<iostream>/*** *  *  * (DFS,迭代加深,剪枝,贪心) O(n2n)O(n2n) (n^2)*(2^n) n^2是找在哪里插入数(可以二分来优化乘n*logn) 2^n是枚举每个数属于那个序列 2^55 剪枝+每个数的贪心方法之后优化极大[1]为了能遍历所有情况,我们首先考虑搜索顺序是什么。搜索顺序分为两个阶段:1.从前往后枚举每颗导弹属于某个上升子序列,还是下降子序列;2.如果属于上升子序列,则枚举属于哪个上升子序列(包括新开一个上升子序列);如果属于下降子序列,可以类似处理。[1].a因此可以仿照AcWing 896. 最长上升子序列 II,分别记录当前每个上升子序列的末尾数up[],和下降子序列的末尾数down[]。这样在枚举时可以快速判断当前数是否可以接在某个序列的后面。注意这里的记录方式和上一题稍有不同:这里是记录每个子序列末尾的数;上一题是记录每种长度的子序列的末尾最小值。此时搜索空间仍然很大,因此该如何剪枝呢?[1].b对于第二阶段的枚举,我们可以仿照上一题的贪心方式,对于上升子序列而言,我们将当前数接在最大的数后面,一定不会比接在其他数列后面更差。这是因为处理完当前数后,一定出现一个以当前数结尾的子序列,这是固定不变的,那么此时其他子序列的末尾数越小越好。!!!!注意到按照这种贪心思路,up[]数组和down[]数组一定是单调的,因此在遍历时找到第一个满足的序列后就可以直接break了。!!!!!最后还需要考虑如何求最小值。因为DFS和BFS不同,第一次搜索到的节点,不一定是步数最短的节点,所以需要进行额外处理。一般有两种处理方式:1.记录全局最小值,不断更新; 这种搜索顺序可以参考@一瞬流年丶涅槃同学的题解;2.迭代加深。一般平均答案深度较低时可以采用这种方式。后面的代码中采用的是这种方式。时间复杂度每个数在第一搜索阶段有两种选择,在第二搜索阶段只有一种选择,但遍历up[]和down[]数组需要 O(n)O(n) 的计算量,因此总时间复杂度是 O(n2n)O(n2n)。    /**二分的话则要找到最后一个大于等于a[u]的数 为了在数组中没有符合要求的数时通过+1延长数组(+1来找到真正要用的严格小于a[u]的数)**/    /**int l=0;    int r=nu;    while(l<r)    {        int mid=(l+r+1)/2;        if(up[mid]>=a[u]) l=mid;        else r=mid-1;    }    int t=up[l+1];    up[l+1]=a[u];    dfs(u+1,max(nu,l+1),nd);    up[l+1]=t;**/int main(){    while(cin>>n,n)    {        ans=n;        for(int i=1;i<=n;i++) cin>>a[i];        dfs(1,0,0);        cout<<ans<<endl;    }}
  • InputStreamReader和OutputStreamWriter 案例

  • package zhuanhuanliu;import java.io.BufferedWriter;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.FileWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.UnsupportedEncodingException;import org.junit.Test;/* * InputStreamReader 实现字节输入流到字符输出流的转化 */public class InputStreamReaderAndOutputStreamReaderTest {	public static void main (String args[]) throws IOException {		//此处应当使用try-catch-finally 但是我偷懒了 				//1.造文件以及流		//1.1 节点流		FileInputStream fis = new FileInputStream(new File("C:\\Users\\de'l'l\\Desktop\\pp.txt"));		FileWriter fw = new FileWriter(new File("C:\\Users\\de'l'l\\Desktop\\hh.txt"));				//1.2 处理流:转换流 	//new InputStreamReader(转换流要作用到的字节型的节点流,字符集(charset))		//参数2指明了字符集,具体使用哪个字符,取决于文件pp.txt保存时使用的字符集		InputStreamReader isr = new InputStreamReader(fis,"UTF-8");		//据说使用的默认是UTF-8 但我的不是				//1.3 处理流:缓冲流		BufferedWriter bw = new BufferedWriter(fw);				//2.具体细节		char []cbufer = new char[20];		int len;		//InputStreamReader是字符流 所以操作到char型数组		while((len=isr.read(cbufer))!=-1) {			String str = new String(cbufer,0,len);//String构造器里没有可以指明字符集的地方			System.out.print(str);//读入cbufer时就会读入文本中的换行 所以不需要自己加			bw.write(str);		}				//关闭资源		isr.close();		bw.close();	}		@Test	public void test()  {		OutputStreamWriter osr=null;		InputStreamReader isr = null;		try {			//1.造文件 造流			File file1 = new File("C:\\Users\\de'l'l\\Desktop\\二分.txt");			File file2 = new File("C:\\\\Users\\\\de'l'l\\\\Desktop\\\\二分01.txt");						FileInputStream fis = new FileInputStream(file1);		    isr = new InputStreamReader(fis,"UTF-8");//读入 解码					    FileOutputStream fos = new FileOutputStream(file2);			osr = new OutputStreamWriter(fos,"gbk");//写出 编码			//写出的文件放在桌面上时会自动给识别用哪种格式写入文件的 所以在桌面上可以正常显示 ANSI--就是国内的gbk			//如果直接放在eclipse里则会按照gbk来 则乱码			//2.具体操作			int len = 0;			char []cbufer = new char[1024];			while((len=isr.read(cbufer))!=-1) {//			String str = new String(cbufer,0,len);//			System.out.print(str);				osr.write(cbufer,0,len);				}		} catch (FileNotFoundException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (UnsupportedEncodingException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} finally {			if(isr!=null) {				try {					isr.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}						if(osr!=null) {				try {					osr.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}			}	}
    
  • //运行结果//写出的文件放在桌面上时会自动给识别用哪种格式写入文件的 所以在桌面上可以正常显示 ANSI--就是国内的gbk//如果直接放在eclipse里则会按照gbk来 则乱码
    

字符集

  • 字符编码

  • 编码表的由来:计算机只能识别二进制数据,早期由来是电信号,为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表

  • ASCII:美国标准信息交换码 用一个字节的7位表示

  • ISO8859-1:拉丁码表,欧洲码表

  • GB2312:中国的中文编码表,最多两个字节编码

  • GBK:中国的中文编码表升级:融合了更多的中文文字符号,最多两个字节编码

    • GBK GB2312 判断字符是由一个字节还是两个字节表示由首位的0/1决定
  • Unicode: 国际标准码,融合了人类目前所使用的所有字符,为每个字符分配唯一的字符码,所用的文字都用两个字节来表示

  • UTF-8:变长的编码方式,可用1-4来表示一个字节

1615551913986

Unicode 全世界符号加在一起也不超过2^16种 所以两个字节即可

  • Unicode不完美 三个问题

      1. 英文字母只用一个字节表示就够了
      1. 如何才能区别Unicode和ASCII?

      2. 计算机怎么知道两个字节表示一个符号?而不是分别表示两个符号呢?如果就和GBK等双字节变啊,1方式一样,用最高位1或0表示两个字节和一个字节,就减少了很多可以表示的字符。

    • Unicode在很长时间没有落地 知道互联网出现

  • 面向传输的众多UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次传输8个位传输数据,而UTF-16就是每次传输16个位。这就是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了

  • Unicode只是定义了一个庞大的,全球通用的字符集,并为每个字符规定了唯一的编号,具体存储成什么样的字节流,取决于字符编码方法。推荐的Unicode编码是UTF-8和UTF-16

  • 中文在Unciode中用3个字节存储

  • 注(了解):在标准UTF-8编码中,超出基本多语言范围的字符被编码为4字节模式,但是再修正的UTF-8编码中,他们由代理代码对表示,然后这些代理编码对在序列中分别重新编码,结果标准UTF-8编码中需要四个字节的字符,在修正后的UTF-8编码中需要6个字节

  • 0和1-----------------> ASCII --------------->ANSI(GB2312->GBK,JIS...)------------>Unicode (UTF-8,UTF-16,UTF-32)(常用-8)

  • ANSI编码,通常指的是平台默认编码,例如英文操作系统是ISO-8559-1,中文系统是GBK

  • Unicode字符集只是定义了字符的集合和唯一编号,Unicode编码,则是对UTF-8,UTF-16,UTF-32等具体方案的统称,并不是具体的编码方案

1615552431004

1615552455092

1615552864769

  • 对后面学习的启示:

  • 客户端/浏览器端 <-------> 后台(Java,Go,Pyhton,Node.js)<-----> 数据库

    要求前端后端使用的字符集要统一:UTF-8

三 、标准输入,输出流(了解即可)

System.in / System.out

  • System.in:标准输入流
  • System.out:标准输出流

1615556369126

package zhuanhuanliu;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;/** * 1.标准的输入、输出流 * 1.1 * System.in:标准的输入流,默认从键盘输入 * System.out:标准的输出流,默认从控制台输出 *  * 1.2 * System类的setIn(InputStream is)/setOut(PrintStream ps)方式重新指定输入和输出的流 *  * 1.3练习 * 从键盘中输入字符串,要求将读取到的整行字符串转换成大写输出。然后继续进行输出操作 * 直至当输入"e"活着"exit"时 退出程序 *  * 方法一:使用Scanner实现 调用next()返回一个字符串 * 方法二:使用System.in实现 System.in---->转换流---->BufferedReader的readLine() * @author de'l'l * */public class OtherStream {//System.in一样的套路	public static void main(String[] args) {		// TODO Auto-generated method stub		BufferedReader br=null;		try {			//提供流 只不过终点不是文件了 而是从键盘输入			InputStreamReader isr = new InputStreamReader(System.in);			br = new BufferedReader(isr);						while(true) {				System.out.println("请输入字符串:");				String data = br.readLine();				if("e".equalsIgnoreCase(data)||"exits".equalsIgnoreCase(data)) {					System.out.println("程序结束");					break;				}			}		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} finally {			if(br!=null) {				try {					br.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}		}}
  • 问题作业 如何实现一个能读任何数据的类?
  • 1615558533799

1615558550956

四、 打印流(了解即可)

PrintStream / PrintWriter

1615558367460

  • 实现将基本数据类型的数据格式转化为字符串输出

  • 打印流:PrintStreamPrintWriter

    • 提供了一系列重载的print()和println()方法,用于多种数据类型的输出

    • PrintStream和PrintWriter的输出不会抛出IOException异常

    • PrintStream和PrintWriter都有自动flush功能

    • PrintStream打印的所有字符都是用平台的默认字符编码转换为字节

      在学要写入字符而不是写入字节的情况下,应该使用PrintWriter类

    • System.out返回的时PrintStream的实例

1615558400217

	/*	 * 2.打印流:PrintStream 和PrintWriter	 * 2.1 提供了一系列重载的print方法	 * ....... 有时间再写吧。。。。	 * 截图了。。。	 */	

五、数据流(了解即可)

DataputStream / DataOutputStream

1615561124182

/*	 *3.数据流	 *如果要保存变量 放在内存中又不太靠谱 就可以用这两个流 把他们读入文件 用的时候在读取出来	 *3.1 DataInputStream 和DataOutputStream	 *3.2 作用:用于读取或写出基本数据类型的变量或字符串	 *练习:将内存中的字符串,基本数据类型变量写出到文件中	 *	 * 注意:处理异常的话,仍然使用try-catch-finally	 */	@Test	public void test3() throws IOException {		//1.造流造文件		DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("data.txt")));				//2.操作 注意读取顺序 要按照写入文件的顺序来读取		dos.writeUTF("孙河陈");		dos.flush();//刷新操作 将内存中的数据写入文件		dos.writeInt(23);		dos.flush();		dos.writeBoolean(true);		dos.flush();		//关闭资源		dos.close();	}	//这个写出来的文件不是双击去读 要用DataInputStream来读			/*将文件中存储的基本数据类型变量和字符春读取到内存中,保存在变量里	 * 	 */	@Test	public void test4() throws IOException {		//1.		DataInputStream dis = new DataInputStream(new FileInputStream(new File("data.txt")));		//2.注意读取顺序		String name = dis.readUTF();		int age = dis.readInt();		boolean isMale = dis.readBoolean();				System.out.println("name = "+name);		System.out.println("age = "+age);		System.out.println("isMale = "+isMale);				//3.		dis.close();	}

复习问题

IO流概述

    1. 说明流的三种分类方式

      • 按角色(作用于谁)划分:节点流(作用于文件),处理流(作用于已有的流)
      • 按操作的数据单位划分:字符流,字节流
      • 按(相对于内存的)流向分:输入流,输出流
    2. 写出4个IO流中的抽象基类,四个文件流,四个缓冲流

      抽象基类 文件流 缓冲流
      InputStream(字节流) FileInputStream BufferedInputStream
      OutputStream(字节流) FileOutputStream BufferedOutputStream
      Reader(字符流) FileReader BufferedReader
      Writer(字符流) FileWriter BufferedWriter

      拿到一个流之后 如何判断属于什么类型的流?

      看后缀!!!

      InputStreamReader:父类Reader

      如上一堂课讲的异常:XxxException XxxError

    3. 字符流于字节流的区别及使用情景

      • 字符流读出来的是char 字节读出来的的byte
      • 字节流:read(byte【】bbuffer) 返回读取的长度 / read() 空参的返回一个字节
      • **字符流:read(char【】cbufer)返回读取的长度/ read() 返回一个字符 **
      • 对于文本文件:在读取文本文件并复制到另一个文件中时 最好用字符流 这样可以在控制台输出查看,也可以用字节流,但是无法在控制台输出查看
      • 对于非文本文件:在读取非文本文件时 只能用字节流byte
    4. 使用缓冲流实现a.jpg文件赋值为b.jpg文件的操作

      BufferedInputStream bis = new BufferdeInputStream(new FileInputStream(new File("a.jpg")));//【缓冲流 【文件流【文件】】】//BufferedInput/OutputStream 是处理流 只能作用于已有的流 没有构造器使他能直接作用于File文件BufferedOutputStream bos = new  BufferedOutputStream(new FileOutputStream(new File("b.jpg")));//【缓冲流 【文件流 【文件】】】byte [] bbufer = new byte[1024];int len;while((len=bis.read(bbufer))!=-1){    bos.write(bbufer,0,len);}bis.close();bos.close();//此时的异常应该用用try-catch-fnally处理 不用throws
      
    5. 转换流是哪两个类?分别的作用是什么?请分别创建两个类的对象

      • InputStreamReader : 将输入的字节流转换为输入的字符流(解码)
      • OutputStreamWriter : 将输出的字符流转换输出的为字节流(编码)
    InputStreamReader isr = new InputStreamReader(new FileInputStream(new File("a.txt","...(charset)")));//因为要转化成可读的字符 所以只能文本文件,不能是非文本文件//所用charset取决于要读取的文本在最初存储的时候是用的什么字符集存储的//(英文字符不管在gbk gb2312 utf-8.... 还是什么什么 都是和ASCII一样的  所以英文不会出现乱码)OutputStreamWrter osw = new OutputStream(new FileOutputStream(new File("sss.txt","charset")));//这里的charset不需要参照别的编码自己想怎么编就怎么编
    
  1. 输入,输出的标准化过程

    • 4.1 输入过程
      • 创建FIle类对象,指明读取的数据的来源。(要求此文件一定要存在)
      • 创建相应的输入流,将File类的对象作为参数,传入流的构造器中
      • 具体的读入过程:
        • 创建相应的 byte[] 或 char[] 数组
      • 关闭流资源
      • 说明:程序中出现的异常需要使用try-catch-finally处理
    • 4.2 输出过程
      • 创建File类对象,只能够输出的数据流向(不要求此文件一定存在)
      • 创建相应的输入流,将File类的对象作为参数,传入流的构造器中
      • 具体的输出过程:
        • 利用之前创建的byte[] 或者char[] 写入指定长度
        • write(char []/byte[] cbuffer,0,len)
      • 关闭流子源
      • 说明:程序中出现的异常需要使用try-catch-finally处理
  2. 相对路径在IDEA和Eclipse中使用的区别?

    IDEA:如果使用单元测试方法,相对路径基于当前的Module的

    如果使用main()测试,相对路径基于当前Project的

    Eclipse:无论是单元测试还是main() 都是基于当前Project

  • 缓冲流
  1. 缓冲流涉及到的类:

    • BufferedInputStream 非文本
    • BufferedOutputStream 非文本
    • BufferedReader 文本
    • BufferedWriter 文本
  2. 作用:提高流的读取,写入速度。提高读写速度原因:内部提供了一个缓冲区 默认是8kb

  3. public class BufferedInputStream extends FileterInputStream{    private static int DEFAULT_BUFFER_SIZE = 8192;}
    

1615633033628

  • 转换流(重点)

    .........

OtherStream

  1. 标准输入输出流

    System.in:标准的输入流 默认从键盘输入

    System.out:标准的输出流,默认从控制台输出

    修改默认的输入和输出行为:

    System类的setIn(InputStream is)/setOut(PrintStream)方式重新制定输入和输出的流

  2. 打印流

    PrintStream 和PrintWriter

    • 说明:提供了一系列重载print()和println方法,用于多种数据类型的输出
    • System.out返回的是PrintStream的实例
    • 看P601~603
  3. 数据流

    DataInputStream 和DataOutputStream

    作用:用于读取或写出基本数据类型的变量或字符串

    • 实现将基本类型输出成字符串

    1615633845928

六、 对象流

ObjectInputStream / ObjectOutputStream

  • 用于存储和读取基本数据类型数据或对象的处理流。他的强大之处就是可以把Java的对象写入到数据源中,也能把对象从数据源中还原回来

  • 序列化:用ObjetOutputStream类保存基本数据类型或对象的机制

  • 反序列化:用ObjectInputStrem类读取基本类型数据或对象的机制

  • ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量

  • 对象的序列化

  • 对象序列化机制允许内存中的Java对象转换成平台无关的二进制,从而允许把这种二进制流持久的保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取了这种二进制流,就可以恢复成原来的Java对象

  • 序列化的好处是在于可将任何时间了Serializable接口的对象转化为字节数据,使其在保存和传输时可以被还原

  • 序列化是RMI(Rmote Mehtod Invoke--远程方法调用)过程的参数和返回值都必须实现的机制,而RMI是JavaEE的基础。因此序列化机制是JavaEE平台的基础

  • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出NotSeriaizableException异常

    • Serializable 可串行化的 (可序列化的)
    • Externalizable
  • 凡是实现Serializable接口的类都有一个标识序列化版本标识符的静态变量:

    • private static final long serialVersionUID
    • serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容
    • 如果类没有显式定义这个静态变量,他的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID可能发生变化。故建议,显式声明。
  • 简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地行营实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

  • 实例

    package shangduixiang;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import org.junit.Test;/** * 本节重点:序列化机制 而不非得是对象流 * 对象流的使用: * 1. ObjectInputStream 和 ObjectOutputStream *  * ObjectInputStream :把程序中的Java对象(转化成二进制)保存到硬盘或者传输到网络  * ObjectOutputStream :把内存中的被转化成二进制的对象再转化过来 输入到内存  * Person对象 在一端---时光门---> (二进制)[硬盘/网络] ----时光门---->在另一端出来 * 对象--ObjectOutputStream--硬盘/网络---ObjectInputStream--->输入到内存/传输到网络那端  * 对象要求: * a.可序列化 serializable  * b.serialVersionUID		身上要给标识 是为了在转化成二进制和还原的时候不和其他对象的二进制串 所以加上标识 为了识别是哪一个类 *  * 2. 作用:用于存储和读取基本类型数据或对象的处理流,他的强大之处就是可以把Java中的 * 3. 要想一个Java对象是可序列化的 需要满足相应的需求,见Person.java * 4. 序列化机制: *  * @author de'l'l * */public class TestObjectOutputStream {	//序列化过程:将内存中的Java对象保存到磁盘中或通过网络传输出去	//使用ObjectOutputStream实现	@Test	public void test(){		// TODO Auto-generated method stub		ObjectOutputStream oos=null;		try {			oos = new ObjectOutputStream(new FileOutputStream(new File("data.dat")));//这个写出来的文件不能看 不是让我们看的 如果要看 就要写反序列化过程						//2.写出过程			oos.writeObject(new String("我爱北京天安门"));			//数据一开始在内存层面可能会被回收 于是我们把他写入文件(可以持久保存)或者通网络直接传输出去也是OK的			oos.flush();//刷新操作 把缓冲区的内容全部写入文件						oos.writeObject(new Person("小明",15));			oos.flush();//					} catch (FileNotFoundException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} finally {			if(oos!=null) {				try {					oos.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}			}		//反序列化:将磁盘文件中的对象还原为内存中的一个Java对象	//使用ObjectInputStream	@Test	public void testObjectInputStream()  {		//1.造流造文件		ObjectInputStream ois = null;		try {			ois = new ObjectInputStream(new FileInputStream(new File("data.dat")));						//2.读取操作 readObject() 写的时候先写的String 读的时候也得先读String			Object obj = ois.readObject();			String str = (String) obj;//我们读的obj本身就是一个字符串 所以可以强转						Person p = (Person) ois.readObject();			//readObjet()一次读一个对象好像 						System.out.println(str);			System.out.println(p);							} catch (FileNotFoundException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (ClassNotFoundException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} finally {			if(ois!=null) {				try {					ois.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}	}}
    
    package shangduixiang;import java.io.Serializable;//如果Person不实现序列化则不能被ObjectOutputStream流写到文件中//String类为什么就不报错? 因为String已经自己实现了一个Serializable接口//Serializable接口中无任何方法 这样的接口称为标识接口/** * Person需要满足如下要求,方可序列化 * 1. 需要实现接口:Serializable * 2.当前类提供一个全局常量:serialVersionUID * 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部的所有属性也必须是可序列化的 *  默认情况下 基本数据类型也是可序列化的 *  补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员 * @author de'l'l * */public class Person implements Serializable{	//实现序列化必须要给(随便写一个值即可)//	public static final long serialVersionUID = 4246465L;//序列反转号		//属性 自己不初始化 也会自动初始化	private int age;	private String name;	public Person(String name,int age){		this.age = age;		this.name = name;	}	@Override	public String toString() {		return "Person [age=" + age + ", name=" + name + "]";	}	public void setAge(int age) {		this.age = age;	}	public void setName(String name) {		this.name = name;	}	public int getAge() {		return age;	}	public String getName() {		return name;	}}
    

随机存取文件流(没有转换流和缓冲流重要)

RandomAccseeFile类(独立于四大抽象类之外)

  • (没法根据后缀判断类型hh)

  • RandomAccessFile声明在java.io包下,但直接继承于java.lang.Object类。并且它实现了DatainputDataOutput这两个接口,也就意味着这个类可以读也可以写

  • RandomAccseeFile类支持 ” 随机访问 “的方式,程序可以直接跳到文件的任意地方来读,写文件

    • 支持只访问文件的部分内容
    • 可以向已存在的文件后追加内容
  • RandomAccseeFile 的对象包含一个记录指针,用以标识当前读写处的位置。RandomAccseeFile类对象可以自由移动记录指针

    • long getFilePointer() 获取文件记录指针的当前位置
    • void seek(long pos) 将文件记录指针定位到pos位置
    • 指针位置从0开始 文件中的第一个字符就是位置0
  • RandomAccessFile的写 就是覆盖原先文件的内容

  • 构造器

    • public RandomAccessFile(File file,String mode)
    • public RandomAccessFile(String name,String mode)
  • 创建RandomAccessFile类实例需要指定一个mode参数,该参数只当RandomAccessFile的访问模式:

    • r 以只读的方式打开
    • rw 打开以便读取和写入
    • rwd 打开以便读取和写入,同步文件内容的更新
    • rws 打开以便读取和写入,同步文件内容和元数据的更新
  • 如果模式为只读r,则不会创建文件,而是会读取一个已经存在的文件,如果读取的文件不存在则会出现异常。如果模式为rw读写,如果文件不存在则会去创建文件。如果存在就不会创建

  • 案例:复制 覆盖 插入

package shangduixiang;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;import org.junit.Test;//import sun.security.action.GetBooleanAction;/** * RandomAccessFile的使用 * 1.RandomAccessFile直接继承于java.langObject类 实现了DataInput和DataOutput接口 * 2.RandomAccessFile既可以作为一个输入流,又可以作为一个输出流 * 3.如果RandomAccessFile作为输出流存在 写出到的文件如果不存在,则在执行过程中自动创建 * 如果写出到的文件存在,则会对原有文件内容进行覆盖(默认情况下,从头覆盖)(能覆盖多少是多少)(不是覆盖文件 是覆盖内容)  * (所以我们一般都是在文件末位追加 ,而不做插入)(利用RandomAccessFile的seek((int)要写入文件.length()); 来定位指针 *  * 优点:RandomAccessFile 的seek() * 浏览器下载:不能续传 只能从头开始 迅雷下载:可以续传 * @author de'l'l * *///1.文件复制public class RandomAccessFileTest {		public static void main(String[] args) {		// TODO Auto-generated method stub		RandomAccessFile raf1=null;		RandomAccessFile raf2=null;		File f2 = new File("2021.3.14.txt");		try {			raf1 = new RandomAccessFile(new File("review.txt"),"r");			raf2 = new RandomAccessFile(f2,"rw");						//2.具体读写操作			int len = 0;			byte []bbufer = new byte[1024];//			raf2.seek(3);			/**1.将指针移动到角标为3的位置 要写的文件的第一个位置指针为0**///			 2.如何追加? 移动到文件无内容的第一位即可 所以用文件的长度//			 如文件原先有4个字 a b c d//							   0 1 2 3  文件.length()即返回4//			 所以要写的文件.seek(要读文件.length)即将指针定位到下一位要书写的地方			raf2.seek(f2.length());			while((len=raf1.read(bbufer))!=-1) {				raf2.write(bbufer,0,len);			}		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		} finally {			if(raf1!=null) {				try {					raf1.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}							}			if(raf2!=null) {				try {					raf2.close();				} catch (IOException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}			}		}	}			//2.对文件覆盖	@Test	public void test2() throws IOException {		RandomAccessFile raf1 = new RandomAccessFile("C:\\Users\\de'l'l\\eclipse-workspace\\shang1\\src\\zhuanhuanliu\\yy.txt","rw");				raf1.write("ccccccccccccc".getBytes());		//将字符串转化为字节		//RandomAccessFile的write只能写字节 所以要把字符串转化成字节				raf1.close();	}	//3.插入	@Test	//此处应当用try-catch-finally	//如何插入文件 将要插入的位置之后的内容复制到一个地方 插入(覆盖)要插入的内容之后再接着将这些保存的原内容覆盖进去	public void test3() throws IOException {		//1.		RandomAccessFile raf1 = new RandomAccessFile(new File("review.txt"),"rw");				//2.具体操作		//2.1 先将插入位置之后的内容读取下来保存在字符串中		raf1.seek(3);//将指针定位到要插入的位置 以便复制之后的内容		//避免总是扩容(StringBuilder底层默认实现大小为16的数组)(我们指定大小为要读取文件的长度 就一i的那个不会扩容了 节省时间)		StringBuilder sb = new StringBuilder((int) new File("review.txt").length()); 		//File的length()方法返回long型 StringBuilder要int型 所以要强转		byte []bbufer = new byte[20];  		int len=0;		//读取的时候是 :文件指针移动一格,raf1读取一个。在内容都读取完时,文件指针有向下移动一个位置,但此时这个位置已无内容 raf1什么也没读到 所以返回-1		while((len=raf1.read(bbufer))!=-1) {			String str = new String(bbufer,0,len);//一定别忘了0,len指定长度 不然最后一轮str可能会保留上一轮的部分数据			System.out.print(str);			sb.append(str);		}						//2.2 再将要插入的内容插入进去		//此时读完之后 文件指针已指向文件的末位置的下一个位置		//所以需要把重新定位文件指针 到要插入的位置		raf1.seek(3);		raf1.write("888".getBytes());				//2.3 将StringBuilder中的数据写入到文件中		raf1.write(sb.toString().getBytes());//StringBuilder-->String-->Byte				raf1.close();			}	//思考:将StringBuilder替换为ByteArrayOutputStream	//			paramater   n.决定因素; 规范; 范围;//			denote      v.标志 预示 象征 意指//			specify     v.具体说明;明确规定;详述;详列    }
  • 注意: 与IDEA不同 eclispe 无论是在test还是在main方法 都是以工程为单位去找文件
  • eclispe以工程为单位找相对路径 如果想写文件的相对路径,要注意他只会在工程的一级目录下找文件。所以直接写写文件里的子文件的名字是找不到的。无论怎样 写绝对路径都能找到。绝对路径:从最高一级开始写。
//1.成功在project的一级目录(与src同级)下复制了一个小飞猪.jpg到hhh.jpg//2.覆盖了自己//3.成功插入了自己abcdefghijkelmnopqrst       abc888defghijmnopqrst

1615683890833

  • 我们可以用RandomAccessFile这个类,来实现一个多线程断点下载的功能,。用过下载工具的朋友都知道,下载前都会建立两个临时文件,一个是与被下载文件大小相同的空文件,另一个是记录文件指针的位置文件,每次被暂停时,都会保存上一次的指针,然后断点下载的时候,会继续从上一次的地方下载,从而实现断点下载或上传的功能。

NIO(目前了解即可)

1615685208453

1615685313232

1615685442192

1615685479200

1615685459743

1615685535428

jar

  • jar包:第三方提供的额外提供不属于JDK的API
  • 这些jar包也都是我们讲的基本API实现的
  • 如何导入 看P617 jar
posted on 2021-08-21 15:06  咕噜辰  阅读(87)  评论(0)    收藏  举报