IO流知识点补全

Day17

 

计算机如何存储中文的?

  当前平台默认编码集 :GBK 一个中文两个字节

  第一个字节:一定是负数

  第二个字节:一般是负数,可能也会是正数,不会影响的结果

一次读取一个字节数组的方式要比一次读取一个字节方式高效.

  一次读取一个字节数组,相当于构造一个缓冲区,有比一次读取一个字节数组还要高效的流

  字节缓冲流 :

字节缓冲输入流

  public BufferedInputStream(InputStream in):默认缓冲区大小构造缓冲输入流对象

  public BufferedInputStream(InputStream in,int size):指定缓冲区大小构造缓冲输入流对象

  

  public int read()

  public int read(byte[] b,int off,int len)

  

 在使输入流的时候,

 两种方式读取(一次读取一个字节/一次读取一个字节数),只能用一种方式,否则,会出现错误!

 

 

字节缓冲输出流:

  构造方式:

  (第一种开发中)public BufferedOutputStream(OutputStream out):采用的默认的缓冲区大小(足够大了) ,来构造一个字节缓冲输出流对象

  public BufferedOutputStream(OutputStream out,int size):指定size缓冲区大小构造缓冲输出流对象

  IllegalArgumentException - 如果 size <= 0

  

  写数据的方式:

  一次写一个字节

  write(int by)

  一次写一个字节数组的一部分

  write(byte[] b, int off, int len)

  

  方法:

  void flush() ;刷新缓冲区的流

 

 

 面试题:

  字节缓冲输出流它的构造方法为什么不能直接传递路径/文件?

  缓冲输入流/缓冲输出流,它只是在底层内部提供一个缓冲区的数组,

  底层实现文件的复制/读取/写入这些操作都依赖于基本流对象来操作(InputStream/OutputStream/FileInputStream/FileOutputstream)

 

 

简单的字节缓冲输出流读写文件应用 

public static void main(String[] args) throws Exception {

//符合Java一种设计模式:装饰者设计模式(过滤器:Filter)

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("23.1.txt"));

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("23.2.txt"));

byte[] arrb = new byte[1024];

int len = 0 ;

while((len=bis.read(arrb))!=  -1 ) {

bos.write(arrb);

}

bis.close();

bos.close();

}

}

 

 

 

存储文件

 IO:永久存储(耗时)

  数据库:永久存储

  

  基本的字节流

  文件字节输入流/文件字节输出流

  高效的字节流(缓冲流)

 

InputStream字节流的两个读写方式和Buffered两种读写方式进行比较

将其读写速度进行排序

 

 

由于代码过于赘述,这里只展示大概思路:

应用System类中的currentTimeMillis() 方法获取当前的时间

运行上述各个方式的读写的方式后

再次读取当前时间,用于减去之前获取的时间,得到运行耗费的时间

 

排序后的结果如下

 

基本的文件字节流中一个一个字节读写是最慢的

然后是高效的字节缓冲流一个一个字节读写

接着是基本的字节流读写整个数组

最后是字节缓冲流读写整个数组是最快的.

 

 

使用字节流一次读取一个字节的方式,会造成中文乱码--->Java提供了一个字符流(专门用来解决中文乱码问题)

 

 

 

编码和解码:前后的编码格式要一致!

  编码:

  简单理解:将能看懂的东西--->看不懂的东西

  解码:

  看不懂的东西---能看懂的东西

  

  举例:谍战片

  

  今天老地方见...

  编码:

  ---->字节---->二进制数据

  

  解码:二进制数据-->十进制数据--->字节---->字符串

  

  

  编码:将字符串变成一个字节数组

  public byte[] getBytes() :平台默认编码集(默认的是Gbk)

     public byte[] getBytes(Charset charset) ;"指定编码格式

  

  解码:将字节数组--->字符串

  public String(byte[] bytes) :使用平台默认编码集(gbk)

  public String(byte[] bytes,Charset charset):用指定的编码格式来解码

  

 

使用平台默认编码集(gbk) 一个中文对应两个字节

使用utf-8编码  一个中文对应三个字节

 

 

 

字符流:

  字符输入流:Reader

  字符输出流:Writer

  字符输出流/字符输入流:都是抽象类

  使用一个子类:转换流

  

  字符输出流的构造方法

  public OutputStreamWriter(OutputStream out):使用默认的编码格式构造一个字符转换输出流对象

  public OutputStreamWriter(OutputStream out, Charset cs):使用指定编码格式构造一个字符转换输出流对象

  

  转换流的构成=字节流+编码格式(平台默认/指定)

  

  

  转换流的对象的创建,格式比较长,非常麻烦,Java--->转换流的便捷类

  Reader:抽象类:字符输入流

  inputStreamReader(字符转换输入流 :inputStream+编码格式)

  便捷类:FileReader,这个类可以直接对文件进行操作

  

  Writer:抽象类:字符输出流

  outputStreamWriter(字符转换输出流:outputStream+编码格式)

  便捷类:FileWriter,这个类可以直接对文件进行操作

  

 

 

 

 

字符转换输入流:InputStreamReader

 InputStreamReader(InputStream in) :构造一个字符转换输入流,默认编码

 public InputStreamReader(InputStream in,Charset cs) 构造一个字符转换输入流,指定编码

 字符转换输入流=字节流+编码格式

 

 

 

//方式一  通过转换流的方式

InputStreamReader ipsr = new InputStreamReader(new FileInputStream("23.1.txt"));

OutputStreamWriter opsr = new OutputStreamWriter(new FileOutputStream("23.3.txt"));

char[] arrc = new char[1024];

int len = 0 ;

while  ((len=ipsr.read(arrc)) != -1){

opsr.write(arrc);

}

ipsr.close();

opsr.close();

 

 

//方式2:使用便捷类进行操作

FileReader fr = new FileReader("a.txt") ;

FileWriter fw = new FileWriter("b.txt") ;

//一次读取一个字符数组

//一次读取一个字符数组

char[] chs2 = new char[1024] ;

int len2 = 0 ;

while((len2=fr.read(chs2))!=-1) {

//

fw.write(chs2, 0, len2);

}

fr.close();

fw.close();

}


 

 

 

 

字符输入流读数据的方法:

 int read(char[] chs):读取一个字符数组

 int read():读取单个字符

 

一次读取一个字符

int ch = 0 ;

while((ch=fr.read())!=-1) {

System.out.print((char)ch);

}

 

//一次读取一个字符数组

char[] chs = new char[1024] ;

int len = 0 ;

while((len=fr.read(chs))!=-1) {

System.out.println(new String(chs,0,len));

}

 

 

字符输出流写数据的功能:

 public void write(int c):写单个字符

 public void write(char[] cbuf):写字符数组

 public abstract void write(char[] cbuf, int off,  int len):写字符数组的一部分

 public void write(String str):写字符串

 public void write(String str,int off, int len):写字符串的某一部分

 

 

 

 

flushclose方法的区别?

 close:关闭该流,关闭该流对象以及它关联 的资源文件,关闭之后,不能再对流对象进行操作了,否则会有异常

 flush:刷新该流,为了防止一些文件(图片文件/音频文件),缺失,或者没有加载到流对象中,刷新了该流,还是可以流对象进行操作

 

 

 

 

 

 

字符缓冲流:

 

在字符流中提供了一个更高效的流-->字符缓冲流

  字符缓冲输入流

  字符缓冲输出流

 

BufferedWrier:文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入

  构造方法

  BufferedWriter(Writer out) :默认缓冲区大小构造字符缓冲输出流对象

  BufferedWriter(Writer out,int size):指定缓冲区大小

 

BufferedReader:字符缓冲输入流

 

  构造方法

  public BufferedReader(Reader in)创建一个使用默认大小输入缓冲区的缓冲字符输入流。

  public BufferedReader(Reader in, int sz)创建一个使用指定大小输入缓冲区的缓冲字符输入流。

  

 特有功能:public void newLine():写入一个行的分隔符号

 

 字符缓冲输入流:

 特有功能:public String readLine():一次读取一行

 

 

需求:先使用字符缓冲输出流写数据,在使用字符缓冲输入读数据,显示控制台上

 

具体应用

需求:我有一个文本文件中存储了几个名称,请大家写一个程序实现随机获取一个人的名字。

 

 

 1)封装一个文本文件:使用字符缓冲输入流读文件

 2)创建一个ArrayList<String>

 3)使用字符缓冲输入流readLine(),一次读取一行,就将该行数据添加到集合中

 4)创建Random类对象

 5)Random类对象.nextInt(集合对象.size()) ;

 6)通过角标get(int index ):获取内容

 

public class Test {

public static void main(String[] args) throws Exception {

//封装文件

BufferedReader br = new BufferedReader(new FileReader("b.txt")) ;

//创建一个集合

ArrayList<String> array = new ArrayList<String>() ;

//一次读取一行

String line = null ;

while((line=br.readLine())!=null) {

//将数据添加到集合

array.add(line) ;

}

//释放资源

br.close();

//创建Random类对象

Random r = new Random();

//获取随机数

int index = r.nextInt(array.size()) ;

//通过角标查找集合中元素

String name = array.get(index) ;

System.out.println("幸运的人是:"+name);

}

}

 

 

 

 

 

内存操作流:适用于临时存储文件.

 内存操作输入流:byteArrayInputStream

 ByteArrayInputStream(byte[] buf)

 

 内存操作输出流: byteArrayOutputStream

 构造方法:ByteArrayOutputStream()

内存操作流:一个程序结束后,那么这些程序的变量,就会从内存消失(马上消失的这些数据进行读取写入)因此不需要关闭该流

 

public class ByteArrayTest {

public static void main(String[] args) throws IOException {

//创建字节流输出对象,先进行写操作

ByteArrayOutputStream baos = new ByteArrayOutputStream();

baos.write("nihao".getBytes());

//创建用于存储的字节数组

byte[] arrb = baos.toByteArray();

//创建输入流对象,同时读入数组数据

ByteArrayInputStream bais = new ByteArrayInputStream(arrb);

byte[] arrby = new byte[1024];

int len = 0;

while ( (len = bais.read())!= -1) {

System.out.print((char)len );

}

}

} 

 

数据流:针对Java基本类型的数据进行读写操作

 数据输入流:DataInputStream

 数据输出流:DataOutputStream  

打印流:

字符打印流(针对文本进行操作:PrintWriter)

字节打印流(printStream 和标准输出流有关系 System.out;)  

 

PrintWriter:属于输出流

 1)只能写数据(只能针对目的地文件进行操作),不能读数据(不能针对源文件进行操作)

 2)可以针对文件直接进行操作

 如果一个类中的构造方法里面有File对象或者String类型数据,这个类可以对文本文件直接操作

 FileInputStream

 FileOutputStream

 FileWriter

 FileReader..

 PrintWriter

 3)自动刷新功能::PrintWriter(OutputStream out/Writer out,boolean autoflush);第二个参数如果是true 表示启动自动刷新功能

 4)打印的方法:print(XXX x)/println(XXX  xx)

 

//示例:

 public static void main(String[] args) throws IOException {

//创建字符流打印对象

PrintWriter pw = new PrintWriter(new FileWriter("24.1.txt"),true);

pw.println("可怕");

pw.println("恐惧");

pw.close();

}

 

 

 

SequenceInputStream 表示其他输入流的逻辑串联(合并流)

  

  

 构造方法

  public SequenceInputStream(InputStream s1, InputStream s2)

 复制文件

  a.txt----->b.txt

  c.txt----->d.txt

  

  现在的需求:

  a.txt+b.txt--->c.txt

 

 

 

 

 

public static void main(String[] args) throws IOException {

//封装InputStream对象

FileInputStream fis1 = new FileInputStream("23.4");

FileInputStream fis2 = new FileInputStream("23.5.txt");

//合并两个流

SequenceInputStream sis = new SequenceInputStream(fis1,fis2);

//创建字符节输出对象

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("24.copy.txt"));

//创建用于存储的字节流数组

byte[] arrb = new byte[1024];

int len = 0;

while ((len = sis.read(arrb))!=-1) {

//写入数据

bos.write(arrb);

//当读取文件的时候需要多次刷新避免读取不全,放在循环里面刷新

}

bos.flush();

bos.close();

sis.close();

}

}

 

 

 

 

 

sequenceInputStream另一种构造方式

 public SequenceInputStream(Enumeration e)

 

 将多个输入流对象进行合并

 a.txt+b.txt+c.txt--->d.txt

 

 

public class Hebingliu2 {

public static void main(String[] args) throws IOException {

//创建Vector对象

Vector<InputStream> vt = new Vector<InputStream>();

FileInputStream fis1 = new FileInputStream("23.5.txt");

FileInputStream fis2 = new FileInputStream("23.4");

FileInputStream fis3 = new FileInputStream("23.3.txt");

//添加

vt.add(fis1);

vt.add(fis2);

vt.add(fis3);

//特有功能

Enumeration<InputStream>  er = vt.elements();

//创建字节缓冲输入合并流对象

SequenceInputStream sis = new SequenceInputStream(er);

//创建字节缓冲输出流对象

FileOutputStream fos = new FileOutputStream("24.copy2.txt");

byte[] arrb =new byte[1024];

int len = 0;

while( (len=sis.read(arrb))!= -1) {

fos.write(arrb);

}

fos.flush();

fos.close();

sis.close();

}

}

 

 

 

标准的输入输出流

 InputStream in = System.in

 PrintStream out = Syste.out ;

 

jdk5以后,Java--->Scanner(InputStream in)

 

 键盘录入

 1)Scanner

 2)BufferedReader里面包装字符转换输入流,包装System.in

 

用户登录注册IO流版(自定义版本)

 

接口

package IO注册;

public interface Gongju {

//注册功能

public abstract void zhuce();

//登陆功能

public abstract boolean denglu();

}

接口实现类

package IO注册;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import java.util.Properties;

import java.util.Scanner;

import java.util.Set;

public class UserImpl implements Gongju {

// 要判断存储文件是否存在

private static File f = new File("user.txt");

static {

if (f == null) {

try {

f.createNewFile();

} catch (IOException e) {

// TODO Auto-generated catch block

System.out.println("创建存储用户信息的文件失败");

}

}

}

@Override

public void zhuce() {

Scanner sc = new Scanner(System.in);

System.out.println("请输入您注册的用户名");

String username = sc.nextLine();

System.out.println("请输入您注册的密码");

String password = sc.nextLine();

BufferedWriter bw = null;

Properties prop = new Properties();

prop.setProperty(username, password);

try {

bw = new BufferedWriter(new FileWriter(f));

prop.store(bw, "用户信息");

} catch (IOException e) {

System.out.println("用户注册失败了");

} finally {

if (bw != null) {

try {

bw.close();

} catch (IOException e) {

System.out.println("释放资源失败");

}

}

}

}

@Override

public  boolean denglu(){

Scanner sc = new Scanner(System.in);

System.out.println("请输入用户名");

String username = sc.nextLine();

System.out.println("请输入密码");

String password = sc.nextLine();

boolean flag = false ;

//创建字符流读取对象

BufferedReader br = null;

Properties prop = new Properties();

try {

br = new BufferedReader( new FileReader(f));

prop.load(br);

//存储它的键值

Set<String> keyset = prop.stringPropertyNames();

//遍历一一对比

for ( String key : keyset) {

if (username.equals(key) ) {

if (password.equals(prop.getProperty(key))) {

System.out.println("登录成功");

return true;

}

}

}

} catch (Exception e) {

System.out.println("用户登录失败");

}//释放资源

finally {

if ( br!= null) {

try {

br.close();

} catch (IOException e) {

System.out.println("释放资源失败");

}

}

}

return flag ;

}

}

测试类

package IO注册;

import java.util.Scanner;

public class UserTest {

public static void main(String[] args) {

UserImpl u = new UserImpl();

System.out.println("请输入您要选择的功能");

System.out.println("1.注册 2.登录 3.退出");

Scanner sc =new Scanner(System.in);

int num = sc.nextInt();

switch (num) {

case 1:

u.zhuce();

break;

case 2:

boolean flag =u.denglu();

if (flag = false) {

System.out.println("登录失败");

}

break;

case 3:

System.exit(0);

default:

System.out.println("您输入了非法字符");

break;

}

}

}

序列化以及反序列化

 

 序列化:将对象按照流的方式存储到文本文件中或者再网络中传输    对象---->流数据序列化流 (ObjectOutputStream)

 反序列化:将文本文件中的流对象或者网络传输中的流对象还原成对象   流数据--->对象  反序列化流(ObjectInputStream)

 

java.io.NotSerializableException :当前类未实现序列化功能的异常

  

   Serializable:接口没有构造方法,没有字段,也没有方法

   接口---->标记接口

   

   自定义类要实现序列化功能,必须实现接口Serializable接口

   

类实现了serializable也意味着他是标记类

   

   假设之前操作针对Peroson操作序列的时候,产生一个标记  Preson.class--->固定ID 100

   

   name-100

   age-100

   

   已经序列化完毕了,然后有修改了Person类里面的一些东西,加入了toString()

  Person.class---固定id -- 200

 

 

  因为手动修改了这些类的属性/成员变量,将序列化版本Id改变了

   InvalidClassException:一般情况:该类的序列版本号与从流中读取的类描述符的版本号不匹配

   实际开发中,不想多次对当前这些序列化,如果这样做,非常麻烦?

   如何解决呢?

   让当前实现类序列化功能的这个类产生一个固定ID,注意看程序有黄色警告线,直接就点它固定Id

  

   

 

transient int age  ;//这样这个类的属性就不会被序列化

transient修饰的变量不会被序列化

Properties:表示了一个持久的属性集(简称:属性集合类)  extends Hashtable<K,V> Map集合的

 

特点:

  可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

  public Properties():无参构造

 

 

public Object setProperty(String key, String value) :给属性列表中添加键和值,并且强制都使用String

 public Set<String> stringPropertyNames():遍历的功能

 public String getProperty(String key)用指定的键在此属性列表中搜索属性

 

 

 

 可保存在流中或从流中加载,只能使用属性集合类

 public void store(Writer writer,String comments):把集合中的数据保存文本文件中(属性集合)

 public void load(Reader reader):将文本文件中的数据加载到属性集合中

 public void store(Writer writer,String comments):把集合中的数据保存文本文件中(属性集合)

 

 

 

Properties具体应用

 

 我有一个文本文件(user.txt),我知道数据是键值对形式的,但是不知道内容是什么。

  请写一个程序判断是否有lisi”这样的键存在,如果有就改变其实为”100

  

  1)读取文件的内容,将文件内容加载属性集合类中

  2)遍历属性集合,获取所有的键的集合

  3)遍历的键的时候,可以判断是否有"lisi"这样一个键

  4)有的话,就更改

  5)需要将当前属性集合类中的保存文本文件中

 

 

package test11;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import java.util.Properties;

import java.util.Set;

public class ProperTest2 {

public static void main(String[] args) throws IOException {

// 先进行存文件操作

Properties prop = new Properties();

prop.setProperty("Java", "200");

prop.setProperty("lisi", "150");

prop.setProperty("map", "20");

FileWriter fw = new FileWriter("24.5.txt");

prop.store(fw, "标题");

fw.close();

// 对文件进行读操作

Properties prop2 = new Properties();

FileReader fr = new FileReader("24.5.txt");

prop2.load(fr);

// 将键值存放在Set集合中

Set<String> keyset = prop.stringPropertyNames();

// 遍历该集合

for (String key : keyset) {

if ("lisi".equals(key)) {

prop2.setProperty(key, "100");

}

}

FileWriter fw2 = new FileWriter("24.5.txt");

prop2.store(fw2, "标题");

fw2.close();

}

}

 

 

需求:

  我有一个猜数字小游戏的程序,请写一个程序实现在测试类中只能用5次,

  超过5次提示:游戏试玩已结束,请付费。

 

 

package test11;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import java.util.Properties;

import java.util.Scanner;

public class ProperTest4 {

public static void main(String[] args) throws IOException {

//先写入文件,开始的时候count判定为0

/*Properties prop = new  Properties();

prop.setProperty("count", "0");

FileWriter fw = new FileWriter("count.txt");

prop.store(fw, "game-count");

fw.close();*/

panduan();

}

//count值进行判断若是大于等5判定玩过五次游戏 ,不能再玩了

private static void panduan() throws IOException {

Properties prop2 = new Properties();

//读入文件的count,并且修改count

FileReader fr2 = new FileReader("count.txt");

prop2.load(fr2);

String count = prop2.getProperty("count");

fr2.close();

//将其转化为int类型

int num = Integer.parseInt(count);

if ( num >= 5) {

System.out.println("您已经玩了五次了,需要付费才能继续游戏");

//判定超过原定游戏次数,直接退出程序

System.exit(0);

}

else {

num ++ ;

 count = String.valueOf(num) ;

 //需要将数据保存到原有的文件中去

 FileWriter fw3 = new FileWriter("count.txt");

 //及时将数数据保存到文件中

 prop2.setProperty("count", count);

 prop2.store(fw3, "game-count");

 fw3.close();

 System.out.println("游戏开始了");

 guessGame();

 

 

}

}

private static void guessGame() {

Scanner sc = new Scanner(System.in);

int num  =(int)(Math.random()*10);

while (true) {

System.out.println("请输入一个一到十的数据");

int guessnum = sc.nextInt();

if( guessnum > num ) {

System.out.println("您输入的数大了");

}

else if( guessnum < num ) {

System.out.println("您输入的数小了");

}

else {

System.out.println("恭喜您,猜对了");

break;

}

}

}

}

 

 

猜你喜欢

转载自blog.csdn.net/qq_35501660/article/details/80456297