A021_IO

本文详细介绍了Java IO流的基础概念、分类,重点关注字节流(FileInputStream、FileOutputStream)的读写操作,包括中文乱码处理,以及字符流(FileReader、FileWriter)的应用。讲解了转换流(InputStreamReader和OutputStreamWriter)的作用,并剖析了缓冲流提高性能的原理。此外,还涵盖了异常处理和随机文件访问的实践技巧。
摘要由CSDN通过智能技术生成

1.内容介绍

1. 什么是IO流及分类;(了解)
2. 字节流【重点掌握】
读写乱码问题-字符编码(了解)
3. 字符流【掌握】
4. 字节流和字符流的区别(掌握)
5. 转换流(了解)
6. 缓冲流【了解】
7. IO流中的异常处理(1.7)【了解】
8. 随机访问文件【了解】断点续传

2.什么是IO流及分类

2.1.什么是IO流
2.2.IO流如何分类

任何事物提到分类都必须有一个分类的标准,例如人,按照肤色可分为:黄的,白的,黑的;按照性别可分为:男,女,人妖。IO流的常见分类标准是按照流动方向和操作数据单位来分的。
1.流动方法:输入 输出
2.操作单位:字节 字符
在这里插入图片描述

Java中的IO流主要都派生至一下4大抽象流,[抽象流------->抽象类] : 都是java.io包中的,分为如下四种

操作单位流动方向输入流输出流
字节流InputStreamOutputStream
字符流ReaderWriter
2.3.小结

1.输入流中主要就是学习 读取的方法 read
2.输出流中主要就是学习 写出的方法 write

3.字节流

3.1.字节输入流FileInputStream

现在我们需要读取记事本文件中的数据,经过分析需要找到一个输入流来读取文件内容,所以找到了InputStream(字节输入流),但是此类是抽象类,而且读文件的方法(read)是实例方法,
所以我们向下找子类来创建对象并调用方法,InputStream有很多的子类,其中最常用的是文件流FileInputStream了,主要用来操作文件的。

3.1.1.如何读取文件内容

构造方法:
1.FileInputStream(File file) 表示从file指向的文件中读取
2.FileInputStream(String name) name表示文件的路径名(路径+文件名)
读取的方法:

  1. int read()
    调用一次读到一个数据字节,返回的int值就是读到的数据
    如果已到达文件末尾,则返回 -1
  2. int read(byte[] b)
    调用一次本方法表示可以读取多个数据
    读到的内容保存传入的byte数组b中
    返回的是本次调用方法读到的数据字节个数
    思考? b数组是有长度,干嘛还要单独返回读到的个数呢?
  3. int read(byte[] b, int off, int len)
    和上面的第二个类似,可以指定从b数组什么位置开始装

使用循环读取整个文件内容

int read = 0;//准备一个变量 : 将读取的每一个字节的数据,;零时存入到该变量
while(   (read = fis.read())   != -1     ){//用read保存的结果:跟-1比较
	System.out.println((char)read);//每次保存的结果,强转成字符
}

使用一次读取一个数组的方式

int len;//读取的字节的个数
byte[] arr = new byte[1024];//五个字节的数据,缓存在该数组的
while(   (len = fis.read(arr))  !=  -1  ){
	System.out.println(  new String(arr,0,len) );
}
3.1.2.读取中文产生乱码原因分析

总结(字节输入流读取的流程以及注意事项)
1.搞清楚是需要读文件还是写文件 —> 选择什么流
2.明确读哪个文件
3.创建一个输入流的对象(目的是为了调用其中的read方法)
4.调用读取方法获得数据
5.关闭流资源 通过流对象来调用close方法(流关闭之后是不能够在操作的-读取)

读取的文本内容:Ab中文
-------------------------------------------------------------------------------------------------------
FileInputStream fis = new FileInputStream("cc.txt");
		int len;
		byte[] arr = new byte[1];//一次只读取一个字节:但是中文字节数不止一个
		while(   (len = fis.read(arr))  !=  -1  ){
			System.out.print(  new String(arr,0,len) );
		}
		fis.close();
-------------------------------------------------------------------------------------------------------
ab????

注意事项:
中文乱码的问题 ,一个中文是两个数据字节, read() 一次读到的是一个数据字节,期待后面的字符流,定义临时读取的数组的长度问题,适当大小即可,最好不要想一次读完

3.2.字节流输出流(FileOutputStream)

现在我们需要将代码中的数据保存到记事本文件中,经过分析需要找到一个输出流来把数据写入到文件中,所以找到了OutputStream(字节输出流),但是此类是抽象类,而且写文件的方法(write)是实例方法,所以我们向下找子类来创建对象并调用方法,OutputStream有很多的子类,其中最常用的算是文件流FileOutputStream了,主要用来操作文件的。

3.2.1.如何把数据写入文件中
  • 构造方法:
    FileOutputStream(File file)
    FileOutputStream(String name)
    FileOutputStream(File file, boolean append)
    FileOutputStream(String name, boolean append)
  • 方法:
    void write(int b) 调用一次写入一个数据字节
    void write(byte[] b) 调用一次,可以把一个byte数组中的数据写入
    void write(byte[] b, int off, int len) 调用一次,把b数组中的一部分数据写入
注意: FileOutputStream 会自动创建一个文件(如果文件不存在,并且文件的路径存在的)

1.写单个数据字节

OutputStream os = new FileOutputStream("D:/123.avi");
		os.write(65);
os.close();

2.写一个字符串

OutputStream os = new FileOutputStream("D:/123.txt",true);
os.write("66666".getBytes());
os.close();

总结
1.确定输出到哪里去
2.创建输 出流的对象(调用输入write方法)
3.调用write方法写数据
4.关闭流资源

3.3.读写乱码问题-字符编码
  1. 乱码怎么产生的
    i.编码和解码规则不一致
    ii.本身这个字符编码不支持某种语言(中文)
  2. 代码提现-尽可能避免乱码的产生

编码 : String —> byte[] String中有对应的方法:

byte[] getBytes() 	使用平台的默认字符集将此 String 编码为 byte 序列 
byte[] getBytes(Charset charset) 	使用指定的字符编码来编码字符串
byte[] getBytes(String charsetName) 	使用指定的字符编码来编码字符串

解码 : byte[] —> String String中有对应的构造方法:

String(byte[] bytes) 	通过使用平台的默认字符集解码指定的 byte 数组
String(byte[] bytes, Charset charset) 	使用指定的字符集来解码指定的byte数组
String(byte[] bytes, String charsetName) 	使用指定的字符集来解码指定的byte数组
  1. 字符编码
    什么是字符编码
    例如有一个字 : 好 ----> 存储到磁盘
    常见的字符编码
    ASCII编码:用来表示英文,它使用1个字节表示,其中第一位规定为0,其他7位存储数据,一共可以表示128个字符。
    拓展ASCII编码:用于表示更多的欧洲文字,用8个位存储数据,一共可以表示256个字符
    GBK/GB2312/GB18030:简称国标,表示汉字。GB2312表示简体中文,GBK/GB18030表示繁体中文,其实就是几个不同的版本而已。
    Unicode编码:包含世界上所有的字符,是一个字符集。
    UTF-8:是Unicode字符的实现方式之一,它使用1-4个字符表示一个符号,根据不同的符号而变化字节长度
    ISO8859-1:是单字节编码,向下兼容ASCII,不支持中文!
友情提醒 
实际web开发  前台网页  后台Java程序  数据库   多个地方都会涉及到字符编码-> 一定要一致
3.4.小结

4.字符流

4.1.为什么需要学习字符流

使用字节流来操作中文的时候非常不方便,容易产生乱码,他不管你是哪种编码方式都按照一个字符一个字符的方式读取数据

4.2.字符流-文件输入流(FileReader)

其中的构造方法和方法和字节流输入流FileInputStream非常类似,差别就是单位是char

Reader r = new FileReader("D:/123.txt");
char[] cbuf = new char[3];
int len;
while((len = r.read(cbuf))!=-1){
	System.out.println(new String(cbuf,0,len));
}
r.close();
4.3.字符流-文件输出流(FileWriter)

特有的方法: 可以直接写字符串

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

5.字节流和字符流的区别

  1. 操作的单位不一样,一个是字节,一个是字符
  2. 操作中文的时候使用字符流更方便, 字节流更广泛:文本,视频,音频,图片…
  3. 字符流中有可以直接写字符串的方法
  4. 字节输出流 : 程序 —> 磁盘文件 如果不关闭流也会写入
    字符输出流 : 程序 —> 缓冲 —> 磁盘文件 如果不关闭流或者刷新缓冲区,不会写入文件

字符输出流,关闭的时候会先刷新,关闭之后不能够在操作,刷新之后可以继续操作
什么时候会用到刷新 : 写入的数据,比较多,可以在中途手动调用刷新的方法提交数据
自己写代码测试上面的效果

6.转换流

6.1.为什么需要转换流

例如实现一个方法内部的代码,但是方法的结构是写好了的,如果我们需要覆写方法内部需要处理文本,那么覆写的方法,方法参数改不了。所以可以在覆写的时候方法体将字节流参数,转换成字符流使用

	public static void print(InputStream is){
	}
6.2.字节流转字符流 InputStreamReader

1.InputStreamReader 拓展至 Reader;当成一个特殊的Reader使用就OK了
2.怎么转? 在创建此类的对象时候,构造方法中的数据来源是一个InputStream(字节输入流)
构造方法摘要:

InputStreamReader(InputStream in) 
InputStreamReader(InputStream in, Charset cs) 
InputStreamReader(InputStream in, String charsetName)

第一个使用的是平台默认的字符集,后面的两个我们可以自己指定字符集

InputStream is = new FileInputStream("D:/111.txt");
InputStreamReader r = new InputStreamReader (is);
char[] cbuf = new char[10];
int len;
while((len = r.read(cbuf))!=-1){
	System.out.println(new String(cbuf,0,len));
}
r.close();
6.3.字符流转字节流 OutputStreamWriter

1.OutputStreamWriter拓展至 Writer;当成一个特殊的Writer使用就OK了
2.怎么转? 在创建此类的对象时候,构造方法中的数据输出是一个OutputStream(字节输出流)
构造方法摘要:

OutputStreamWriter(OutputStream out) 
OutputStreamWriter(OutputStream out, Charset cs) 
OutputStreamWriter(OutputStream out, String charsetName) 
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:/333.txt"));
osw.write("爱上快乐到家");
osw.close();
6.4.小结

1.上面的字节转字符,字符转字节流,是按照数据流动方向来讲的
2.如果上面的a点不是很理解,建议当成两个读写的流使用就完了

7.缓冲流

7.1.什么是缓冲流

在这里插入图片描述
之前循环独写数据,操作磁盘的次数非常多,影响性能,通过缓存流,可以先缓存大量读写数据,等到比较多了之后,在连接磁盘一次从磁盘读取或者写。避免多次连接磁盘影响性能,缓冲流的使用[缓冲,达到高效读写操作]有如下四种
BufferedInputStream [ 1024 * 8]
BufferedOutputStream
BufferedReader
BufferedWriter

7.2.缓冲流示例

1.通过测试普通流读写速度与缓冲流独写速度理解缓冲流优势

long start = System.currentTimeMillis();//毫秒值
FileInputStream fis = new FileInputStream("F:/b/aa.jpg");
BufferedInputStream bis = new BufferedInputStream(fis);//先缓冲,再读取
int len;
byte[] b = new byte[1024];
while((len = bis.read(b))!=-1){
	System.out.println(new String(b,0,len));
}
bis.close();
fis.close();
long end = System.currentTimeMillis();//毫秒值
System.out.println(end-start);
7.3.小结

8.IO流中的异常处理

8.1.文件拷贝示例
public static void main(String[] args) throws IOException {
	/*每次读取到数组中,并且从数组中写入到文件,边读边写*/              
	FileInputStream fis = new FileInputStream("F:/bb.txt");
	FileOutputStream fos = new FileOutputStream("F:/cc.txt");
	byte[] b = new byte[1024];
	int len;
	while((len = fis.read(b)) != -1){
		fos.write(b,0,len);
	}
}

如上示例:IO中的一场我们自己不处理直接抛出在实际开发中是不合适的,所以我们需要进行处理

8.2.传统方式处理

try{
可能发生异常的代码块
}catch(异常类型 形式参数){
捕获异常之后要做的处理
}finally{
异常语句块一定会执行的代码块,释放锁,关闭流资源
}

FileInputStream fis = null;//局部变量,在使用之前应该初始化
FileOutputStream fos = null;
try {
	fis = new FileInputStream(src);
	fos = new FileOutputStream(dest);
	byte[] b = new byte[1024];
	int len;
	while((len = fis.read(b)) != -1){
		fos.write(b,0,len);
	}

} catch (Exception e) {
		e.printStackTrace();//获取异常信息
}finally{
	try {//先使用后关闭
		if(fos != null)
			fos.close();
		if(fis != null )
			fis.close();
	} catch (Exception e) {
				// TODO Auto-generated catch block
		e.printStackTrace();
	}
}
8.3.Java7新方式

自动关闭的流资源,必须是实现了AutoCloseable
FileInputStream extends InputStream implements Closeable extends AutoCloseable
try(需要自动关闭的流资源){
可能发生异常的代码块
}catch(){
捕获异常之后要做的处理
}

try(
	FileInputStream fis = new FileInputStream(src);
	FileOutputStream fos = new FileOutputStream(dest);
){
	byte[] b = new byte[1024];
	int len;
	while((len = fis.read(b)) != -1){
		fos.write(b,0,len);
	}
}catch(Exception e){
	e.printStackTrace();
}
8.4.小结

1.IO流操作一般都应该关闭;
2.就算读写过程中产生了异常,最后也应该关闭,所有关闭流资源的代码一般放在finally结构中
3.Java7新结构支持自动关闭,但是try的()中创建流的对象的流必须是支持自动关闭的(不过一般我们用到的IO流都是有实现此接口的,最好用的时候查看一下文档)

9.随机读写文件

9.1.随机读写文件介绍

1.java.io包中,拓展至Object: 没有拓展至四大抽象流 此类具有读取和写入的方法RandomAccessFile[ 可读可写 ]
2.可以实现随机读写[ 不是说在磁盘中随便找文件来读和写 ]
3.指定了一个具体的文件,进行读写:[文件当中的位置,可以随机的读写]
4.随机不是随意(随机)的;可以自己指定文件位置进行读写()
5.如何设置位置,默认的位置是什么
构造方法:
RandomAccessFile(File file, String mode)
RandomAccessFile(String name, String mode)
mode 指定是模式 : 读 或者 写 或者读写,具体有哪些模式参考文档
重要方法:
long getFilePointer() 获得偏移量
void seek(long pos) 设置文件指针
long length() 返回此文件的长度
void setLength(long newLength) 设置此文件的长度

9.2.随机独写文件示例
RandomAccessFile rm = new RandomAccessFile("F:/b/random.avi", "rw");
rm.write("Hello".getBytes());
//rm写过一次之后,自动的记录了下一个写入数据的位置
System.out.println(rm.getFilePointer());//获得文件指针偏移的位置[数据应该继续写入的下一个位置的字节值]
rm.seek(0);//设置从文件的哪个位置开始写操作【随机写操作】
rm.write("HA".getBytes());
System.out.println(rm.getFilePointer());//获得文件指针偏移的位置[数据应该继续写入的下一个位置的字节值]
rm.close();

//rm.setLength(1024*1024*1024);设置文件大小
9.3.小结

10.课程总结

10.1.重点

1、4个抽象类必须要会写,并且知道他们各自属于什么分类;
2、使用FileInputStream FileOutputStream熟练的读写文件(相应的代码);
3、IO流操作的流程必须情况

10.2.难点

1、使用循环读取一个文件中的所有数据的那一段代码 --》 多读多写!
2、做记事本的时候的思路 多想, 干!

11.常见面试题

12.课后练习[30分钟]

1、分别使用文件流(一共4个)来读取和写入一个文件[非常重要,必须非常熟练!!!]
2、说出你理解的字节流与字符流的区别
4、IO流操作(读写)的流程是什么?
5、写一个文件拷贝的方法(思考应该传入哪些参数,内部的代码流程)
完成上一天作业中拷贝一个文件件中的满足指定条件的文件到另外一个文件夹中的效果

13.每日一练[25]

1.请使用方法写出各种数据类型的转化
如:字符串-整数 整数–字符串 整数转–小数
请把Date,Calendar常用方法写出来,并说出他们什么意思
2.Jdk1.7版本以前Map存储实现原理hash表+链表结构读懂代码
在这里插入图片描述

public V put(K key, V value) {
        //如果table数组为空数组{},进行数组填充(为table分配实际内存空间),入参为threshold,
//threshold为initialCapacity 默认是1<<4(24=16)
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
       //如果key为null,存储位置为table[0]或table[0]的冲突链上
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀
        int i = indexFor(hash, table.length);//获取在table中的实际位置
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        //如果该对应数据已存在,执行覆盖操作。用新value替换旧value,并返回旧value            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败
        addEntry(hash, key, value, i);//新增一个entry
        return null;
    }
  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值