0811(029天 输入输出流05 收尾)

0811(029天 输入输出流05 收尾)

每日一狗(田园犬西瓜瓜

29

输入输出流05 收尾

比较接口补充(不想自己写排序规则,想用别人的东西,就要遵守别人的规则)

  • Arrays.sort(arr); 要求arr必须可以强转成可比较类型(Comparable),必须重写compareTo方法
    • 要求类必须可扩展
  • Arrays.sort(arr, com); 中的com是一个比较器(Comparator)的局部内部类
    • 临时修改比较规则
    • 临时创建比较规则
    • 比较类无法扩展

1. 啥也不是

每日圣经

面向对象的学习方法

分散关注,每个人只关注自己所负责的一小部分

  1. 先学抽象中的东西
  2. 在学习特定实现中有什么功能
  3. 过滤

压缩文件写入

package com.yan2;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class Test1 {
	public static void main(String[] args) throws Exception {
//		PrintWriter pw = new PrintWriter(new BufferedWriter(
//				new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream("data/abc.data")))));
//		pw.println("1,zhangsan,23.45");
//		pw.println("2,lisi,55.55");
//		pw.close();

		BufferedReader br = new BufferedReader(
				new InputStreamReader(new GZIPInputStream(new FileInputStream("data/abc.data"))));
		String ss=null;
		while((ss=br.readLine())!=null)
			System.out.println(ss);
		br.close();
		
	}
}

image-20220811200759146

2. 特殊 RandomAccessFile

注释:本篇中所提到的指针并非真正的指针,只是相对于本文件开头的偏移字节数量,java中并没有指针和地址的概念。

RandomAccessFile不属于IO流,支持对文件的读取和写入随机访问

RandomAccessFile是Java输入/输出流体系中功能最丰富的文件内容访问类,既可以读取文件内容,也 可以向文件输出数据。

  • 可以读写文件已经操作过的部分
  • 支持随机读写
  • 覆盖写(字符级别的写一个覆盖一个字符,不会清空文件内容后再写)

构造器

RandomAccessFile类在创建对象时,除了指定文件本身,还需要指定一个mode参数指定 RandomAccessFile的访问模式,该参数有如下四个值:

  • r 只读
  • rw 可读可写,文件不存在时尝试创建文件
  • rws 可读可写,区别与上边rw的是本模式会同步数据到物理存储设备中
  • rwd与rws类似,只是仅对文件的内容同步更新到磁盘,而不修改文件的元数据

2.1 读写

  • 支持八种简单类型及其包装类和字符串类型的读写操作

  • 当然还有那三个write和read

  • 文件结尾会抛出EOFException文件结尾异常

注意:读写方法要一致,跟队列很像,先进后出

覆盖部分数据进行写入时,必须与原始数据的占用空间一致才行,(之前写入的是一个20汉字的字符,然后转到这个地址,只覆盖写入了10汉字的字符串,这时候文件内容就完了,后边的数据全错了)

考点:文件第一个写的是一个当前文件内有效的地址,要获取这个地址指向的数据是多少

package com.yang2;

import java.io.RandomAccessFile;
import java.util.Random;

public class Test01 {

	public static void main(String[] args) throws Exception {
		// 文件写入
		RandomAccessFile ref = new RandomAccessFile("data/3.data", "rw");
		Random r = new Random();
		ref.writeLong(104L);
		for (int i = 0; i < 100; i++) {
			int kk = r.nextInt();
			double dd = r.nextDouble();
			if (104 == ref.getFilePointer()) {
				System.out.println(kk + "--" + dd); // 1611856893--0.3018222595729477
			}
			ref.writeInt(kk);
			ref.writeDouble(dd);
		}
		ref.close();
	}

}


package com.yang2;

import java.io.RandomAccessFile;

public class Test02 {

	public static void main(String[] args) throws Exception {
		// 读出文件
		RandomAccessFile raf = new RandomAccessFile("data/3.data", "rw");
		long index = raf.readLong();
		raf.seek(index);
		System.out.println(raf.readInt());//1611856893
		System.out.println(raf.readDouble());//0.3018222595729477
		raf.close();
	}
}

2.2 记录指针(相对位置)的特殊方法

支持跳到文件任意位置读写数据,

  • 将指针定位到对应的地址:raf.seek(index);
  • 跳过多少字节(无法回退):raf.seek(byteLength);
  • 获取当前地址:ref.getFilePointer();

2.3 典型案例:

  • 断点续传
  • 多线程下载单个文件

2.4 常见方法

  • void close() 关闭操作
  • int read(byte[] b)将内容读取到一个byte数组之中
  • byte readByte()读取一个字节
  • int readInt()从文件中读取整型数据… readDouble()等8种简单类型
  • String readLine()读取一行数据
  • void writeBytes(String s)将一个字符串写入到文件之中,按字节的方式处理。
  • writeChars void writeInt(int v)将一个int型数据写入文件,长度为4位。

3. NIO开篇

从JDK1.4开始Java引入了一系列改进的输入/输出处理的新功能,统称为NIO,即新IO,新增了许多用于 处理输入输出的类,新IO采用内存映射文件的方式处理输入输出,新IO将文件或文件的一段区域映射到 内存中,这样就可以像访问内存一样来访问文件,这种方式进行输入输出比传统的输入输出快的多

先将文件映射到内存中,然后将在操作的时候是操作内存中的数据。

见见世面就行了🤣

3.1 NIO基础

Channel通道和Buffer缓冲是NIO中的两个核心对象

  • Chanel是对传统输入输出系统的模拟,通过map方法可以将一块数据映射到内存中
  • Buffer本质是一个数组,发送到Channel中的所有对象都必须先放到Buffer中,而从Channel中读 取的数据必须先放到Buffer中

3.2 Buffer的使用

Buffer是一个抽象类,主要作用是用于装入数据,然后输出数据

  • 最常见的子类ByteBuffer可以在底层字节数组上进行get/set操作
  • 其它基本数据类型都有对应的Buffer类:CharBuffer、 ShortBuffer、 IntBuffer、LongBuffer、 FloatBuffer、 DoubleBuffer
  • 静态方法static XxxBuffer allocate(int capacity)创建一个容量为capacity的XxxBuffer对象

Buffer中有3个重要概念:容量capacity、界限limit和位置position

  • 容量capacity表示该Buffer的最大数据容量,创建后则不能改变
  • 界限limit,位于limit后的数据既不可被读,也不可被写(默认和容量相等)
  • 位置position用于指明下一个可以被读出的或者写入缓冲区的位置索引(默认为0,使用put每放进去一个数据就会向后移动一个位置)
  • 标记mark位置:咱们自己打的标记,可以随时返回这个标记在干点啥
常用方法
  • capacity():int返回Buffer的容量大小
  • hasRemaining():boolean判断是否还有元素可以进行处理
  • remaining():int返回当前位置和界限之间的元素个数
  • position():int返回当前操作的位置
  • mark():Buffer设置Buffer的标记位置,只能在0和position之间做标记
  • reset():Buffer将位置position转到mark所在的位置
  • rewind():Buffer将位置position设置到0,取消设置的mark
  • put(obj)用于向Buffer中放入数据
  • get()用于从Buffer中取出数据
  • buffer.flip();void 将没有新数据的区域封印起来,把那个limit移动到指定位置
// 创建一个容积为10的buffer对象
CharBuffer bf = CharBuffer.allocate(10);
// capacity() 获取对象容积
System.out.println(bf.capacity()); // 10
// position() 获取当前操作位置
System.out.println(bf.position()); // 0
// limit() 获取当前封锁区开始位置
System.out.println(bf.limit()); // 10
// hasRemaining() //return position < limit; 看一下还有空间吗
System.out.println(bf.hasRemaining()); // true
bf.put((char) 250); // 为当前位置放上存储的数据
System.out.println(bf.get(0)); // ú 获取指定索引上的数据
// remaining(); // return limit - position; 看一下还有几个空间
System.out.println(bf.remaining()); // 9

bf.mark(); // 在当前位置上打个标记
for (int i = 0; i < 5; i++) {
    bf.put(i + "");
}
bf.reset();
for (int i = 0; i < 5; i++) {
    System.out.print(bf.get() + "  "); // 0 1 2 3 4
}
bf.rewind(); // 将位置拉到0,同时取消mark标记

3.3 Channel

Channel可以直接将文件的部分或者全部直接映射成Buffer

注意:不能直接访问Channel中的数据,包括读取、写入都不行。Channel只能与Buffer进行交互.

  • 所有Channel不应该通过构造器来直接创建,而是通过传统的节点InputStream、OutputStream的getChannel方法来返回对应的Channel

  • 常用的是FileInputStream、FileOutputStream的getChannel()返回的FileChannel

常用的方法

Channel中最常用的三个方法是map()、read()和write()

read():将数据读出到buffer中

File file = new File("data/Test1.java");
FileChannel inChannel = new FileInputStream(file).getChannel();
ByteBuffer buffer = ByteBuffer.allocate(256);
// 多次读取数据的方式从文件中获取内容
while (inChannel.read(buffer) != -1) {
    buffer.flip(); // 将没有数据的区域封印起来
    // 获取一个UTF-8的字符集对象
    Charset charset = Charset.forName("UTF-8");
    // 为这个字符集对象创建一个解码器
    CharsetDecoder decoder = charset.newDecoder();
    // 使用解码器将ByteBuffer转换为CharBuffer
    CharBuffer cb = decoder.decode(buffer);
    System.out.println(cb);
    buffer.clear();// 将position设置为0,为下一次读取数据做准备
}

map():方法将Channel对应的部分或全部数据映射成ByteBuffer

File f = new File("data/Test1.java");
FileChannel in = new FileInputStream(f).getChannel();
FileChannel out = new FileOutputStream("data/a.txt").getChannel();
MappedByteBuffer buffer = in.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
// 参数1为执行映射时的模式,有只读、读写模式;参数2和3用于设置哪些数据执行映射。可以将FileChannel中全部数据映射为ByteBuffer
out.write(buffer);

buffer.clear();
Charset charset = Charset.forName("UTF-8");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer cb = decoder.decode(buffer);
System.out.println(cb);

write():写入数据

File file = new File("data/a.txt");
int len = (int) file.length();
RandomAccessFile raf = new RandomAccessFile(file, "rw");
// 返回的channel是只读还是读写,取决于RandomAccessFile文件对象的打开模式
FileChannel channel = raf.getChannel();
// buffer支持可读,将文件中的所有内容映射到buffer中
ByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, len);

channel.position(len); // 移动指针到内容末尾
channel.write(buffer); // 重新写出buffer中的内容,实际上就是将文件内容拷贝

3.4 selector

是NIO的核心组件之一,用于检查数个NIO Channel通道的状态是否处于可读、可写。

好处在于: 使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上 下文切换带来的开销。

4. Chatset字符集

计算机中将二进制文件转化内字符的一张映射表,

Charset类的常用方法

  • availableCharsets():SortedMap 获取当前JDK所支持的所有字符集
  • newDecoder():CharsetDecoder获取该编码字符集对应的解码器
    • decode(ByteBuffer):CharBuffer方法可以将字节序列ByteBuffer转换为CharBuffer字符序列
  • newEncoder():CharsetEncoder获取该编码字符集对应的编码器
    • encode(CharBuffer):ByteBuffer方法可以将字符序列CharBuffer转换为ByteBuffer字节序列
Charset c1 = Charset.forName("GBK");
CharsetEncoder encoder = c1.newEncoder();
CharsetDecoder decoder = c1.newDecoder();
CharBuffer cb = CharBuffer.allocate(8);

cb.put('孙');
cb.put('误');
cb.put('空');
cb.flip();
//将CharBuffer转换为ByteBuffer
ByteBuffer bb=encoder.encode(cb);
for(int i=0;i<6;i++) {
	System.out.println(bb.get(i)+" ");
}
System.out.println("====================");
//将byteBuffer转换为charBuffer
System.out.println(decoder.decode(bb));

扩展小芝士

  • 字符串写出后在结尾部分会存在一个结尾标志字符
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值