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):写字符串的某一部分
flush和close方法的区别?
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;
}
}
}
}