JAVA总结 DAY020 1108

DAY020

一、字符流

1.使用字符流的原因
1、使用字节流写字符
可以使用,但是需要先把字符串转成字节数组,再存储到文件中,比较麻烦
2、使用字节流读取字符
如果是纯英文,可以一次读取一个字节
如果是纯中文,可以一次读取两个字节(GBK)
如果是中英文混杂,每次不知道读取多少个字节,因此无论字节数组准备多大,都会出现乱码
解决:
1、出现乱码的原因:
每次不知道读取多少个字节,转换成字符。
而我们在代码中将每次读取的字节个数写死了。
2、解决:
动态判断每次应该读取多少个字节
在GBK编码表中,如果是一个英文,那么一定读取到的字节是正数,如果读取到的字节是正数,那么就可以断定是英文字符,就读取一个字节转成字符即可
在GBK编码表中,如果是一个中文,那么就一定读取到的第一个字节是负数,如果读取到一个负数,就说明读到的是一个中文,就需要再次读取一个字节,两个字节一起转成字符。
3、说明:
如果在工程中频繁做这么底层的操作,太复杂,直接使用jdk中已经提供好的解决方案
字符流:不仅能动态判断GBK的每个字符是中文还是英文,还可以判断其他编码表的所有语种

2.Reader

  1. Reader==> InputStreamReader==> FileReader

  2. 公有方法:
    abstract void close() 关闭资源
    public int read() 一个一个字符读取
    public int read(char[] cbuf) 读取多个字符保存到 char类型的数组里
    public abstract int read(char[] cbuf,int off,int len) 读取多个字符保存到 char类型的数组里 给定长度

  3. FileReader :
    构造方法: public FileReader(String fileName) fileName 文件的路径
    4.读取的方法 :
    一个一个字符读取 int read() ==>返回值是读取的具体的数据
    读取多个字符 read(char[] cbuf) ==> 返回值是读取的有效字符数, 读取的数据保存在数组里

3.Writer
1.Writer==>OutputStreamWriter==>FileWriter
2.共有方法:
abstract void flush() 刷新流。
void write(char[] cbuf) 写入一个字符数组。
abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
void write(String str) 写一个字符
void write(String str, int off, int len) 写一个字符串的一部分。
3.FileWriter:
用于写入字符流 要编写原始字节流,请考虑使用FileOutputStream.也就是内部提供了一个缓冲区的大小
构造方法:
public FileWriter(File file) 构造一个给定文件名的FileWriter对象
public FileWriter(File file,boolean append) 构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据
public FileWriter(String fileName) 给一个File对象构造一个FileWriter对象。
public FileWriter(String fileName,boolean append) 给一个File对象构造一个FileWriter对象。 如果第二个参数是true ,则字节将写入文件的末尾而不是开头。
代码示例

        FileWriter fw = new FileWriter("e.txt");
		char[] ch = {'哈','g','a'};
		char[] ch1 = {'虾','g','a','哈','g','a','哈','g','a'};
		fw.write(97);
		fw.write('喝'+ 'a');
		fw.write("我是谁");
		fw.write(ch);
		fw.write(ch1,0,ch1.length);
		fw.close();
		
		

4.字符流的拷贝
1、使用字符输入流读取信息,使用字符输出流写出信息,完成文件的使用字符流拷贝
2、字符流拷贝的必要性:
没有必要使用字符流来进行拷贝,因为字符流会先将字节信息转成字符,读取到内存中,最后还要再把字符信息转成字节信息,写出到另外一个文件中。中间做了两次没有意义的相反的操作,浪费了时间。
3、字符流的使用场景:
如果在读取到字符之后,需要人为的阅读和修改这个字符,那么就需要使用字符流
如果只是简单的将信息进行转移或者拷贝,就不需要转成字符。
代码示例

public static void main(String[] args) throws IOException {
		//字符流拷贝
		FileReader fr = new FileReader("a.txt");
		FileWriter fw = new FileWriter("f.txt");
		int len;
		while((len = fr.read())!=-1) {
			fw.write(len);
		}
		fr.close();
		fw.close();
		System.out.println("拷贝成功");
	}

5.字符流是否可以操作非纯文本文件
1、非纯文本文件:文件中可以储存非字符以外的内容,例如图片、视频、音频等不能使用字符表示的信息。
2、结论:不能
3、原因:当字符流读取到一个字节信息之后,需要查询编码表转成字符信息,如果是非纯文本文件,就可能读取到的字节信息无法转成对应的字符,因为这个字节信息在编码表中没有对应的字符,就只能使用英文的?来代替这个字符,这一步中,就对信息进行篡改。接下来将篡改之后的?字符信息,通过编码表转成字节信息(这一步虽然正确,但是已经没有意义),转成的字节信息,就和当初字符输入流读取的字节信息不一致了,写出到目标文件,就是错误信息

6.高效缓冲字符流
1、BufferedReader和BufferedWriter
2、使用:
创建了高效缓冲流对象之后,使用和加强之前一样的方式,BufferedWriter是Writer的子类,BufferedReader是Reader的子类,所以可以继续使用在抽象类中定义的各种方法。
3、高效的原因:
BufferedReader:每次调用read方法,只有第一次从磁盘中读取了8192个字符,存储到该类型对象的缓冲区数组中,将其中一个返回给调用者,再次调用read方法时,就不需要再访问磁盘,直接从缓冲区中拿一个出来即可,效率提升了很多。
BufferedWriter:每次调用write方法,不会直接将字符刷新到文件中,而是存储到字符数组中,等字符数组写满了,才一次性刷新到文件中,减少了和磁盘交互的次数,提升了效率

7.高效缓冲字符流的特有方法
1、BufferedReader:
readLine():可以从输入流中,一次读取一行数据,返回一个字符串,如果到达文件末尾,则返回null
2、BufferedWriter:
newLine():换行。在不同的操作系统中,换行符各不相同,newLine方法就是可以给我们根据操作系统的不同,提供不同的换行符
代码示例

BufferedReader br = new BufferedReader(fr);
		BufferedWriter bw = new BufferedWriter(fw);
		int i;
		while ((i = br.read()) != -1) {
			bw.write(i);
		}
		br.close();
		bw.close();


public static void main(String[] args) throws IOException {
//		readlinec();
		//BufferedWriter 中特有的方法 newline()
		BufferedWriter bw = new BufferedWriter(new FileWriter("h.txt"));
		bw.write("中国");
		bw.newLine();
		bw.write("武汉");
		bw.newLine();
		bw.write("北京");
		bw.newLine();
		bw.write("天津");
		bw.close();
二、转换流

1.InputStreamReader
1.作用:
①InputStreamReader 是从字节流到字符流的桥
②可以指定其编码格式(最重要的一个作用)
③为了最大的效率,请考虑在BufferedReader中包装一个InputStreamReader
2.常用的构造方法
public InputStreamReader(InputStream in) 构造传递了一个InputStream
public InputStreamReader(InputStream in,String charsetName) charsetName 设置其编码
3.常规的方法
public void close() 关闭资源
int read() 读一个字符
int read(char[] cbuf, int offset, int length) 将字符读入数组的一部分。
4.步骤:
1.实例化三个 流 BufferedReader==>InputStreamReader==>FileInputStream
2.调用其读的方法
3,关闭资源

2.OutputStreamWriter
1.作用:
字符流转换成字节流的一个桥梁
设置编码格式
为了最大的效率,请考虑在BufferedWriter中包装一个OutputStreamWriter,以避免频繁的转换
2.常用的构造方法
public OutputStreamWriter(OutputStream out) 构造中传递一个OutputStream
public OutputStreamWriter(OutputStream out,String charsetName) charsetName 设置编码格式
3.常规的方法:
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) 写一个字符串的一部分。
4.步骤:
1.实例化 BufferedWriter =>OutputStreamWriter==>FileOutputStream
2.调用其写的方法
3.刷新缓冲区
4.关闭资源
代码示例

public static void main(String[] args) {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {

            br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("2.txt")), "utf-8"));
            bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("6.txt")), "utf-8"));
            String str = null;
            while ((str = br.readLine()) != null) {
                bw.write(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
三、对象操作流

1.概念
1、用于将内存中的对象,直接写出到目标文件中,或者从文件中读取一个对象到内存中。
2、对象输出流、对象输入流

2.对象输出流
1、ObjectOutputStream:也是一个字节输出流,是OutputStream的子类,可以输出一个对象,用于将内存中的对象,写到其他设备中,例如文件、网络
2、也是一个包装类,可以将一个没有输出对象功能的字节输出流,包装成有输出对象功能的字节流,包装完成之后,就多了一些方法。
3、构造方法:
ObjectOutputStream(OutputStream os):将一个普通的字节输出流包装成对象输出流
4、最重要的成员方法:
writeObject(Object obj):将内存中的obj对象,输出到该对象流中
5、注意事项:
1、要存储到文件中的对象,所属类型必须实现java.io.Serializable接口
2、对象存储到文件时,没有经过编码也没有经过解码,直接将字节信息存储到文件中;使用文本编辑器打开这个文件,相当于是做了解码操作,所以一定是乱码。将来这个文件就不应该使用文本编辑器打开,也不应该给人阅读,而是用于将来使用输入流读取的内容

3.对象输入流
1、ObjectInputStream:也是一个字节输入流,也是一个包装类,拥有父类的所有方法
2、作用:是InputStream的子类,可以将文件中、网络中的一个对象,读取到内存中,进行使用
3、构造方法:
ObjectInputStream(InputStream is):将一个普通的字节输入流,包装成对象流
4、有一个特有方法:
readObject():可以将流中的一个对象读取到内存中

4.对象操作流程中的错误
错误1:java.io.NotSerializableException 对象没有进行序列化==> 序列化也就是让对象实现这个接口Serializable
错误2: public static final long serialVersionUID = 200;给对象序列化加一个标识,不加系统默认生成
java.io.InvalidClassException: 产生的原因就是写对象到文件里产生的serialVersionUID与读的时候本地的serialVersionUID 不一样
错误3: java.io.EOFException 在读取对象的时候产生这个原因: ObjectInputStream 读取文件对象,没有标识表示读取到了末尾
解决方法: 添加一个标识

5.注意事项
1、序列化和反序列化概念:
对象从内存到其他设备的过程:序列化
对象从其他设备到内存的过程:反序列化
2、序列化还是发序列化:都需要实现接口java.io.Serializable
3、版本号问题:
Exception in thread “main” java.io.InvalidClassException: com.ujiuye.demos.Student; local class incompatible: stream classdesc serialVersionUID = 384528848059540731, local class serialVersionUID = 2430787247088292168
意思:修改了本地的Person类型,新的类型和文件中对象的类型已经不一致,所以两个版本冲突了,标志就是两个版本的序列化版本id不同
解决:不要使用自动生成的序列化版本ID,手动给定一个序列化版本ID,将来这个类型是否发生了版本变化,主要取决于程序员是否手动修改了这个类型的版本ID,如果修改了,那么文件中的对象类型和本地类型就不兼容,如果没有修改这个版本ID,那么无论怎样修改了类型内容,都可以做到文件对象类型和本地类型兼容。
代码示例

public class Demo_1 {
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		
		
		//序列化
		objectlist();
		//反序列化
//		objectlistread();
	}

	private static void objectlistread() throws IOException, FileNotFoundException, ClassNotFoundException {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"));
		Object obj = ois.readObject();
		//向下转型
		ArrayList<Person> arrlist1 = (ArrayList<Person>) obj;
		for (Person p : arrlist1) {
				System.out.println(p.getName()+""+p.getAge()+"、、、");
		}
	}

	private static void objectlist() throws IOException, FileNotFoundException {
		// 创建对象输出流
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
		ArrayList<Person> arrlist = new ArrayList<Person>();
		arrlist.add(new Person("王祖贤", 23));
		arrlist.add(new Person("李嘉庆", 23));
		arrlist.add(new Person("林青霞", 23));
		oos.writeObject(arrlist);
		oos.close();
		System.out.println("写入成功");
	}

	private static void ObjectInput() throws IOException, FileNotFoundException, ClassNotFoundException {

		// 对象输入流
		// ObjectOutputStream的构造方法
//		ObjectWrites();
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"));
		System.out.println(ois.readObject());
		System.out.println(ois.readObject());
		System.out.println(ois.readObject());
		System.out.println(ois.readObject());
		System.out.println(ois.readObject());
		System.out.println(ois.readObject());
		ois.close();
	}

	private static void ObjectWrites() throws IOException, FileNotFoundException {
		// 对象输入流
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
		oos.writeObject("asd");
		oos.writeObject(new Person("张三", 23));
		oos.writeObject(new Person("李四", 24));
		oos.writeObject(new Person("王五", 25));
		oos.close();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值