Java IO流及游戏数据持久化开发
线程游戏的实现 3
本节课为介绍“飞机大战”游戏的更多功能,主要实现排行榜、播放音频和读写文件操作的功能,进而介绍了数据库、IO字节流、位运算、Java异常处理等知识点。
数据库
作用:用来长期存放、批量管理大量数据的软件、文件系统
优势:支持快速增删改查、多程序同时访问、永久保存数据。
数据库的分类:
1.关系型数据库
数据以表格的形式存储,适合存储玩家分数、昵称等结构化信息
2.非关系型数据库
无固定表格结构,适合存储海量图片、缓存数据
完整数据库安装配置复杂,本篇使用文件字节流读写本地txt文件,实现分数持久化、排行榜功能。
IO流
要实现排行榜功能,需要“写入数据”和“读取数据”两个操作
写入数据
写入数据需要文件路径,而文件路径需要文件对象,写入数据时创建文件字节流(File)来创建文件对象,抛弃异常后刷新管道(强制写入,保证数据写入管道)。
读取数据
读取数据的操作和写入数据一样,利用文件对象创建输入流,同样要抛异常。
输入流 && 输出流
输入流(InputStream):数据从外面流向程序(读数据)
站在程序的角度区分输入:文件 -> 程序:输入(读)
用于读取硬盘上的图片、音乐、txt文件、键盘录入文字
输出流(OutputStream):数据从程序流向外面(写数据)
站在程序的角度区分输出:程序 -> 文件:输出(写)
用于把程序内容保存到文件、向控制台打印文字
而用于排行榜的文件字节流是万能流,支持数字、音频和图片
文件输出流(FileOutputStream):写入数据到本地文件
文件输入流(FileInputStream):从本地文件读取数据
特点:以1个字节(byte)为最小单位读写,单个字节取值范围(0~255)
文件字节流实现分数持久化
Q:明明写入的数据是1000但读取到的数据是232,怎么读写一个完整的整型数字?
原因:字节流能操作的数据大小:读写数据以字节为单位,一个字节最大值能存储2的八次方(256),所以直接写入一个字节能存储的最大值是256,超过该数字后就没办法正常读写了
那要怎么完整的写入:
先写入第一位,然后右移8位,即把高位字节的数据移到低位来再写入一次,但是移动完之后要再读两次
intscore=1000;fos.write(score);// 第1字节:低8位fos.write(score>>8);// 右移8位,取出第2段字节fos.write(score>>16);// 右移16位,取出第3段字节fos.write(score>>24);// 右移24位,取出最高位第4字节fos.flush();那要怎么合成一个整形呢:
不是简单把两个数字相加,可以通过右移写入数据,也可以通过左移将数据合在一起,即左移8位
要写入4字才能把完整的字节写入,将字节的移动写成一个方法,再调用这个方法
// 依次读取4个单字节inti1=fis.read();inti2=fis.read();inti3=fis.read();inti4=fis.read();// 左移拼接为完整int整数returni4<<24|i3<<16|i2<<8|i1;Q:4个字节才能合成一个int?
Java 中int整型固定占用4个字节(32位二进制),1字节 = 8位,4 x 8 = 32位,int 取值范围 -2147483648 ~ 2147483647,如果只写 1/2/3 个字节,高位数据丢失,读取时数字错乱;想要完整保存、读取任意 int 分数,必须拆分为 4 个字节存储,读取时 4 字节全部拼接还原。
Q:什么时候处理异常?
所有IO流操作(FileInputStream / FileOutputStream)、文件、音频必须处理异常,文件不存在、文件损坏、权限不足、读写中断都会抛出 FileNotFoundException、IOException
两种处理方式:
1.方法声明上 throws Exception 向上抛出(见后续代码)
2.try-catch 包裹捕获异常,自定义错误提示,凡是和硬盘文件、外部资源交互的代码块,都需要异常处理。
returni4<<24|i3<<16|i2<<8|i1;// 等价returni4<<24|i3<<16|i2<<8|i1<<0;注:单竖线 | :按位或(直接操作二进制每一位,拼接字节专用)
参考示例代码(继Java学习进程11)
Player类
packagecom.yw.oop0808;importjavax.sound.sampled.*;importjava.io.File;importjava.io.IOException;/** * @author YAN * @date 2026/8/9 14:31 * @description 加载音频文件 */publicclassPlayer{//加载音频文件publicvoidplay(Stringpath){//创建文件对象Filefile=newFile(path);Clipclip=null;try{AudioInputStreaminputStream=AudioSystem.getAudioInputStream(file);clip=AudioSystem.getClip();clip.open(inputStream);//开始clip.start();//设置音量大小//音量取值范围 -80 ~ 6FloatControlcontrol=(FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN);floatmax=control.getMaximum();floatmin=control.getMinimum();System.out.println("max = "+max+" min = "+min);floatvalue=control.getValue();Thread.sleep(3000);control.setValue(value-5);}catch(Exceptione){thrownewRuntimeException(e);}}//暂停//继续//设置音量publicstaticvoidmain(String[]args){}}TopFile类
packagecom.yw.oop0808;importjava.io.File;importjava.io.FileInputStream;importjava.io.FileOutputStream;/** * @author YAN * @date 2026/8/9 16:49 * @description 文件字节流 */publicclassTopFile{//写入数据publicstaticvoidwriteData(Filefile){//创建文字字节流:读写数据以字节为单位try{FileOutputStreamfos=newFileOutputStream(file);intscore=1000;//写入数据fos.write(score);fos.write(score>>8);fos.write(score>>16);fos.write(score>>24);//强制写入fos.flush();System.out.println("写入成功");}catch(Exceptione){thrownewRuntimeException(e);}}//读取数据publicstaticvoidreadData(Filefile){//输入流try{FileInputStreamfis=newFileInputStream(file);intscore=readInt(fis);System.out.println("score:"+score);}catch(Exceptione){thrownewRuntimeException(e);}}publicstaticintreadInt(FileInputStreamfis)throwsException{inti1=fis.read();inti2=fis.read();inti3=fis.read();inti4=fis.read();returni4<<24|i3<<16|i2<<8|i1<<0;}publicstaticvoidmain(String[]args){Stringpath="E:\\IdeaProjects\\GameThread\\src\\com\\yw\\oop0808\\top.txt";Filefile=newFile(path);TopFile.writeData(file);TopFile.readData(file);}}