黑马程序员_【总结】_IO知识梳理3_(end)

时间:2023-02-18 22:08:02
IO知识梳理3


---------- android培训 java培训、期待与您交流! ---------- 

---------------------------------------------------------------------------------------------------------------------------------------------
1、在IO中设计到集合的是; SepenceInputStream
  在IO中涉及到多线程的是: 管道流
2、随机访问流 RandomAccessFile 
new RandomAccessFile("E:\\Demo\\test\\a.txt","rw");
该类只能操作文件。其模式: r 读--- rw  读写
1、模式 
2、直接写入数据基本类型 
3、通过seek 方法改变指针的位置来进行指定位置的数据读取和写入。【重点】
3、基本数据流   能直接的操作基本数据类型:
DataInputStream---DataOutputStream
指定编码表
new DataOutputStream(new FileOutputStream("E:\\Demo\\utfdate.txt"));
4、管道流
PipendInputStram --PipendOutputStream  
输入输出可以直接进行连接,通过结合线程使用。
管道流的特点::当多线程时进行读写功能时,
不论是先执行读,还是写 都不重要,最后终究会先执行写,然后再打印
5、字节数组流
用于操作字节数组的流对象:
ByteArrayInputStream 在构造时,需要接收数据源,而且数据源是一个字节数组
ByteArrayOutputStream 在构造时,不用定义数据目的,因为该对象中已经在内部封装
了可变长度的字节数组,这就是数据目的。
6、字节数组流  因为这两个流对象 都操作的是数组,并没有使用系统资源,
所以不用进行 close 关闭。
7、【操作【字节】数组】 、【操作【字符】数组】、【操作字符串】 都基本相同。
【字节】数组: ByteArrayInputStream ---   ByteArrayOutputStream
【字符】数组: CharArrayReader ------  CharArrayWriter
【字符串】 StringReader ----     StringWriter 
8、操作流 、序列化
ObjectInputStream--ObjectOutputStream
Serializable
1、被操作的对象必须实现 Serializable 接口
这接口太爽了,只需要实现,其他什么也不用写
2、自定义序列号
static final long serialVersionUID = 42L;  42L为自定义序列号
3、静态成员变量不能被序列号
(静态在方法区,能被序列的都在堆内存中)
4、 普通成员变量想要不被序列号必须加关键字:   
这样该成员不被序列号,但存在与堆内存中。
4让 普通成员变量也不被序列化呢?
java 提供了一个关键字:   transizent    
5、给类设定一个固定序列号,以保证唯一性
static final long serialVersionUID = 42L; 
9、
编码: 字符串 变 字节数组。
解码: 字节数组 变 字符串。
String --> byte[] : str.getBytes(charsetName);    
[charsetName  指定编码表(不指定用默认GBK编码表)]
byte   -->  String: newString(byte[],charsetName);
10、“联通”是 GBK 编码 但是 产生的二进制形式 居然和 UTF-8  一致
这就是为什么 会出现乱码了,因为识别的时候,为上列第2中
然后去查UTF-8码表。自然就错了。
---------------------------------------------------------------------
【6】
随机访问流
【重点】 
1、模式 
2、直接写入数据基本类型 
3、通过seek 方法改变指针的位置来进行指定位置的数据读取和写入。【重点】

RandomAccessFile
自身具备读写方法   达到随机访问。
skipBytes(int i)
seek(int i) 
skipBytes(int i) 跳过几个字节  只能往下跳不能往回   了解该方法即可。
1、模式
2、写入方法
3、seek 指定指针位置。

---------------------------------------------------------------------
该类不算是IO 体系中的子类  (后缀没有父类)
而直接继承自Object

但是它是 IO 包中成员,因为它具备读和写功能。
内部封装了一个 byte 数组 ,而通过指针对数组的元素进行操作。
可以通过geitFilePointer 获取指针位置。

其实,完成读写原理就是内部封装了 字节写入流 和输出流

通过构造函数可以看出,该类只能操作文件。
而操作文件的模式: r 读--- rw  读写

如果模式为只读: r 不会创建文件,会去读取一个已存在的文件,如果文件不存在
则会出现异常。
如果模式:rw 操作的文件不存在,会自动创建,如果存在则不会覆盖。
---------------------------------------------------------------------
1、通过指针的设定达到随意性的访问。
2、数据是有规律的。

1、能够进行数据的分段写入,比如下载,就是设计了多个线程,分别在写入数据
通过该方法给每个线程不同的段落,不会有任何冲突。
用一般流写数据因为是从头到尾,会导致数据都存完了,最后读出来是错误的,解码错误。

//问题:如何把名字搞为16的字节格式??
class $3RandomAccessFile {
	public static void main(String[] args) throws IOException{
		method_3();
		//method_2();
		//method_1();
	}
	public static void method_3()throws IOException{
		RandomAccessFile raf =new RandomAccessFile("E:\\Demo\\test\\a.txt","rw");
		raf.seek(8*4);//指针直接把位置指向8个字节的 第4个。
		raf.write("德吉".getBytes());
		raf.writeInt(99);//直接写 基本数据类型
		raf.close();
	}
	public static void method_2()throws IOException{
		RandomAccessFile raf =new RandomAccessFile("E:\\Demo\\test\\a.txt","rw");

		byte [] by = new byte [4];
		raf.seek(8*2);// 在数据规律的情况下*0表示第一个,*1第二个。。。
		raf.read(by);
		String name = new String(by);		
		int age = raf.readInt();
		System.out.println(name+">>"+age);
		raf.close();
	}
	public static void method_1() throws IOException{
		RandomAccessFile raf = new RandomAccessFile("E:\\Demo\\test\\a.txt","rw");
		raf.write("张三".getBytes());
		raf.writeInt(90);	
		raf.write("姜末".getBytes());
		raf.writeInt(99);
		raf.write("上官".getBytes());
		raf.writeInt(97);
		raf.close();
	}
}
【7】
基本数据流
操作基本数据类型:   (使用频率比 较高。)
DataInputStream---DataOutputStream

主要用于操作基本数据类型的对象。  [专业操作数据]
(ObjectInputStream、ObjectOutputStream 中 有很多相同方法 不过更专注于操作对象)

构造方法
DataInputStream(InputStream in) 
DataOutputStream(OutputStream out) 
import java.io.*;
class $4DataStream{
	public static void main(String[] args) throws IOException{
		dataRead();//
		dataWrite();
		dReadUTF();//
		dWriteUTF();
		yibanliu();
	}
	public static void yibanliu() throws IOException{
		OutputStreamWriter  osw =
			//new OutputStreamWriter(new FileOutputStream("E:\\Demo\\utf.txt"),"utf-8");
			new OutputStreamWriter(new FileOutputStream("E:\\Demo\\gbk.txt"),"gbk");
		osw.write("你好");
		osw.close();
		//创建 tuf-8 和 GBK 编码,需要转换流才可以办到
		//DataStream 可以修改版 utf-8  但是必须对应读取,
		//【a】 处读取 其他编码格式,报错
		//修改版 utf-8      8个字节  
		//	   utf-8    6字节
		//	   Gbk      4字节
	}
	public static void dWriteUTF() throws IOException{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("E:\\Demo\\utfdate.txt"));
		dos.writeUTF("你好");
		dos.close();
	}
	public static void dReadUTF() throws IOException{
		DataInputStream dos = 
			new DataInputStream(new FileInputStream("E:\\Demo\\utfdate.txt"));
			//new DataInputStream(new FileInputStream("E:\\Demo\\gbk.txt"));//【a】
		String s = dos.readUTF();
		System.out.println(s);
		dos.close();
	}
	public static void dataWrite() throws IOException{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("E:\\Demo\\987.txt"));
		dos.writeInt(223);
		dos.writeBoolean(true);
		dos.writeDouble(9293.234);//各种直接操作 基本数据类型
		System.out.println("Is ok!~");
		dos.close();
	}
	public static void dataRead() throws IOException{
		DataInputStream dos = new DataInputStream(new FileInputStream("E:\\Demo\\987.txt"));
		int i = dos.readInt();
		boolean b = dos.readBoolean();
		double d = dos.readDouble();//输出的时候, 也是直接操作即可。
		System.out.println("int:"+i);
		System.out.println("bool:"+b);
		System.out.println("dou:"+d);
		dos.close();
	}
}
能够直接对基本数据类型进行操作。很简便

【8】
管道流
PipendInputStram --PipendOutputStream  
输入输出可以直接进行连接,通过结合线程使用。

、、、、、、、、、、、、、
定义2个线程,我们知道,他们运行的时候随机性非常强
谁都有可能先执行。
通过在写入前设定写入sleep(6000)  秒 ,多次运行
我们发现,不论是先执行读,还是写 都不重要,最后终究会先执行写,然后再打印

这就是 管道流的特点。
----------
1、创建两个线程,初始化连接管道流
2、创建管道流,并进行连接。并传递给 线程
3、启动线程。
//读取线程
class read implements Runnable  // 多线程{
	private PipedInputStream in;
	read(PipedInputStream in ){//3、 初始化连接到 输出管道流
		this.in = in;
	}
	//1、实现 Runnable 成为多线程  2、覆盖 run 方法
	public void run(){
		//4、记住只能try 不能 throws
		try{
			//、读取方法就不用在多说了,因为示例,数据短,就没用while
			byte [] by = new byte[1024];
			int len = 0;
			System.out.println("读取前:");
			len = in.read(by);
			//System.out.println("读取后:");
			String s = new String(by,0,len);
			System.out.println(":"+s);
			in.close();
		}
		catch (IOException e){
			throw new RuntimeException("没有读取到信息");
		}
	}
}
// 写入线程  --基本步奏同上。
class write implements Runnable{
	private PipedOutputStream out;
	write(PipedOutputStream out){
		this.out = out;
	}
	public void run(){
		try{
			System.out.println("6秒后开始写入:");
			Thread.sleep(6000);//为了演示多态性让等待5秒后在写
			out.write("wo yao fei de geng gao".getBytes());
			out.close();
		}
		catch (Exception e){
			throw new RuntimeException("没有写入信息");
		}
	}
}
class $2PipedStream {
	public static void main(String[] args) throws Exception{
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		in.connect(out);//管道连接
		read r = new read(in);
		write w =  new write(out);
		//启动线程
		  //全写方法:
		//Thread t1 =new Thread(r);
		//t1.start();
		//Thread t2 = new Thread(w);
		//t2.start();  
		//	简写--内部类写法:
		new Thread(r).start();
		new Thread(w).start();
	}
}

在IO中设计到集合的是; SepenceInputStream
在IO中涉及到多线程的是: 管道流
【9】
字节数组流
用于操作字节数组的流对象:
ByteArrayInputStream 在构造时,需要接收数据源,而且数据源是一个字节数组

ByteArrayOutputStream 在构造时,不用定义数据目的,因为该对象中已经在内部封装
了可变长度的字节数组,这就是数据目的。
因为这两个流对象 都操作的是数组,并没有使用系统资源,
所以不用进行 close 关闭。
-------------------------
在流操作规律讲解时:
源设备:
键盘 System.in  
硬盘 FileStream
内存 ArrayStream
目的设备:
控制台 System.out
硬盘   FileStream
内存 ArrayStream
------------------------------------
发现:【操作【字节】数组】 、【操作【字符】数组】、【操作字符串】 都基本相同。
---------------------------------------------------------------------------------------------------
【字节】数组: ByteArrayInputStream---   ByteArrayOutputStream
---------------------------------------------------------------------------------------------------
【字符】数组:CharArrayReader  ------  CharArrayWriter
---------------------------------------------------------------------------------------------------
【字符串】 StringReader ----    StringWriter 
---------------------------------------------------------------------------------------------------

import java.io.*;
class $5ByteArrayStream {
	public static void main(String[] args) throws IOException{
		byteArray();
		charArray();
		string();
	}
	//【操作【字节】数组】
	public static void byteArray() throws IOException{
		ByteArrayInputStream bais = new ByteArrayInputStream("afdfgfdgdfgd".getBytes());
		ByteArrayOutputStreambaos = new ByteArrayOutputStream();
		int len = 0;
		while((len = bais.read())!=-1){
			baos.write(len);
		}
		System.out.println(baos.size());
		System.out.println(baos.toString());
	}
	//【操作【字符】数组】
	public static void charArray() throws IOException{
		CharArrayReader car = new CharArrayReader("fdafergr".toCharArray());
		CharArrayWriter caw = new CharArrayWriter();		
		int len = 0 ;
		while((len = car.read())!=-1){
			caw.write(len);
		}
		System.out.println(caw.size());
		System.out.println(caw.toString());
	}
	//【操作字符串】
	public static void string() throws IOException{
		StringReader sr = new StringReader("ccccccccccccccccMMMM");
		StringWriter sw = new StringWriter();
		int len = 0 ;
		while((len = sr.read())!=-1){
			sw.write(len);
		}
		System.out.println(sw.toString());
		//可以读取一行。
		//StringWriter sw2 = new StringWriter();
		//char [] ch = new char [1024];
		//while((len = sr.read(ch))!=-1){
		//	sw2.write(ch,0,len);
		//}
		//System.out.println("SW2:"+sw.toString());
		
	}
}
【10】
操作流 、序列化
ObjectInputStream--ObjectOutputStream
Serializable
操作流 、序列化
(对象被产生后存在于堆里面,而一旦使用完之后,就会消失,如何将对象存储到硬盘中呢)
ObjectInputStream--ObjectOutputStream  相互对应一个存,一个取
void writeObject(Object obj)--Object readObject()  方法也是对应的。
注意:
1、被操作的对象必须实现 Serializable 接口
这接口太爽了,只需要实现,其他什么也不用写
2、自定义序列号
static final long serialVersionUID = 42L;  42L为自定义序列号
3、静态成员变量不能被序列号
(静态在方法区,能被序列的都在堆内存中)
   普通成员变量想要不被序列号必须加关键字:   
这样该成员不被序列号,但存在与堆内存中。

结合 $1Person 完成 操作流 、序列化 的演示

比如,添加一个静态成员变量
(这个时候,在 $1Person 添加静态成员,并初始化到构造方法中 运行发现 是一个null )
为什么呢
因为 序列号是根据 成员变量生成的,是在堆内存中

而静态成员变量是在方法区中所以不可被序列化   
那么,可不可以 让 普通成员变量也不被序列化呢?
java 提供了一个关键字:   transizent    
 
(给年纪 用该关键字修饰  发现  打印的年纪 为0    )

发现,当 $1Person 设定完成后,进行操作流生成文件并能阅读
而一旦, $1Person 发生变化时,该文件不可被读取了,

因为 Serializable 根据 $1Person 的成员变量,生成了一个 序列号
当 成员变量发生变化时候,序列号会随着变化。 所以不能被阅读

为了避免这种情况,可以给类设定一个固定序列号,以保证唯一性:
在  $1Person 设定静态成员变量
static final long serialVersionUID = 42L;

最后,一般文件都存为  .Object  (因为文件无法阅读,都是乱码 )
import java.io.*;
class ObjectStream {
	public static void main(String[] args)  throws Exception{
		//writrObj();
		readerObj();
	}
	public static void writrObj() throws IOException{
		ObjectOutputStream oos = 
			new ObjectOutputStream(new FileOutputStream("E:\\Demo\\obj\\Person.Object"));
		
		oos.writeObject(new Person("liuh",19,"ch"));
		oos.close();
	}
	public static void readerObj() throws Exception{
		ObjectInputStream ois = 
			new ObjectInputStream(new FileInputStream("E:\\Demo\\obj\\Person.Object"));
		Person p =(Person) ois.readObject();
		System.out.println(p);
	}
}
class Person implements Serializable{
	String name="111";
	transient nt age=22;   //不序列化关键字
	static String guo="bbq";
	Person(String name, int age,String guo){
		this.name = name;
		this.age = age;
		this.guo = guo;
	}
	public String toString(){
		return name+">>"+age+":"+guo;
	}
}
【11】
字符编码表
1/
-----------------------------
字符流的出现为了方便操作字符
更重要的是加入了编码转换
通过子类转换流完成
InputStreamReader
OutoutStreamWriter
在两个对象进行构造时,可以加入字符集也就是编码表
----------------------------------------------------------
什么是编码表?
1、计算机只能识别二进制数据,早期由来是电信号
2、为了方便应用计算机,让它可以识别各个国家的文字
3、于是将各个国家的文字用数字来表示,并一一对应,形成一张表。
这就是编码表。
----------------------------------------------------------
常见编码表:
ASCII 美国标准信息交换码
用一个字节的7位表示
ISO8859-1 拉丁码表。欧洲码表
用一个字节的8位表示
GB2312 中国的中文编码表
GBK 中国的文字编码表升级,融合了更多的中文文字符号
Unicode 国际标准码,融合了多种文字
所有文字都是用两个字节表示
Java语言使用的就是该编码表
UTF-8 最多用三个字节来表示一个字符
。。。。
----------------------------------------------------------
2/
编码: 字符串 变 字节数组。
解码: 字节数组 变 字符串。
String --> byte[] : str.getBytes(charsetName);    
[charsetName  指定编码表(不指定用默认GBK编码表)]
byte   -->  String: newString(byte[],charsetName);
-----------------------------------------------
遇到不知道什么编码的情况下
可以用“你好” 编码解码根据结果:

返回:??     编码GBK  ----> 解码UTF-8 
返回:浣犲ソ     编码UTF-8----> 解码 GBK

------------
class $7EncodeStream {
	public static void main(String[] args)  throws Exception{
		toEncode_1();
	}
	public static void toEncode_1()throws Exception{
		String s = "我去";		
		byte [] b1 = s.getBytes("GBK");//默认GBK编码表
		System.out.println(Arrays.toString(b1));//记住数组变字符串的方法。
		String ss = new String(b1,"UTF-8");
		System.out.println("ss:"+ss);
		//出现解码错误的情况-进行UTF-8编码
		byte [] b2 =ss.getBytes("UTF-8");
		//进行GBK 解码
		String s3 = new String(b2,"GBK");
		System.out.println(Arrays.toString(b2));
		System.out.println("ss:"+s3);
	}
	public static void toEncode()throws Exception{
		String s = "你好";		
		byte [] b1 = s.getBytes("GBK");//默认GBK编码表
		//byte [] b1 = s.getBytes("ISO8859-1");//【1】 编码错误的时候,就不要解码了。
		System.out.println(Arrays.toString(b1));//记住数组变字符串的方法。
		String ss =// new String(b,"GBK");//默认GBK编码表
					new String(b1,"UTF-8");
		System.out.println("ss:"+ss);
//---------------
		//出现解码错误的情况
		String s2 = new String(b1,"ISO8859-1");//【2】解码错误的时候该如何处理:  再编码,在再解码一次:		
		//对s2 进行ISO8859-1 编码
		byte [] b2 =s2.getBytes("ISO8859-1");
		//进行GBK 编码
		String s3 = new String(b2,"GBK");
		System.out.println("ss:"+s3);
	}
}

对于 toEncode 中 出现ISO8859-1 解码错误
如果编码GBK 解码UTF-8  出现解码错误 根据前面的经验
进行 对UTF-8 进行编码 再用GBK 解码 会是正确结果吗?

-----发现错误,发现第一次编码的时候是4个字节,而再次编码的时候却是9个字节
他的把前面的三位?号有3个,所以对应是9个字节
为了验证,我们把“你好”更改为“哈哈”----发现?号变成了4个,对应变成了12个字节
----因为GBK和UTF-8 都识别中文,导致的错乱。
-----------------------------------------------
3/
联通
String --> byte[] : str.getBytes(charsetName);    
byte   -->  String: newString(byte[],charsetName);
----------------------------------
联通:
-63 -86 -51-88
转换二进制,&255  取八位
-63--> 11000001
-86--> 10101010
-51--> 11001101
-88--> 10101000
这个时候,对这个二进制,解码的时候,

UTF-8 --  最多3个字节,那么编译时如何知道是1个还是2 个3个字节?
1、   读到开头 0    把这个1字节 带去查表
2、   读到开头 110 会直接跳到下一行 读取10  然后把这2个字节 带去查表
3、   读到开头 1110 会跳过 读取 两行 10 10  然后把这3个字节 带去查表
----------------------------------
这个时候我们发现,
“联通”是 GBK 编码 但是 产生的二进制形式 居然和 UTF-8  一致

这就是为什么 会出现乱码了,因为识别的时候,为上列第2中
然后去查UTF-8码表。自然就错了。

class  $8EncodeStream{
	public static void main(String[] args) throws IOException{
		lianTong_1();
	}
	public static void lianTong_1() throws IOException{
		String s = "好号";
		byte [] by = s.getBytes("GBK");
		sop(Arrays.toString(by));
		sop("--------");
		for(byte b : by){
			sop(b);//
			//sop(Integer.toBinaryString(b));//【记住,转换二进制】。但是太长,只想要后8位怎么办?
			sop(Integer.toBinaryString(b & 255));//【记住,& 255】
		}
		sop("--------");
		String s1 = new String(by,"UTF-8");
		sop(s1);
		sop("--------");
		String s2 = new String(by,"GBK");
		sop(s2);
		sop("--------");
	}
	public static void lianTong() throws IOException
	{
		String s = "联通";
		byte [] by = s.getBytes("UTF-8");
		sop(Arrays.toString(by));	
		String s1 = new String(by,"UTF-8");
		sop(s1);
		sop("--------");
		String s2 = new String(by,"GBK");
		sop(s2);
	}
	public static void sop(Object obj){
			System.out.println(obj);
	}
}
据说,编码表是 程序员心中永远的通。。哈哈,不过还体会不到啊。






---------------------------------------------------------------------------------------------------------------------------------------------
---------- android培训、 java培训、期待与您交流!----------


----------------------------------------------