目录
(3)用BufferedReader和BufferedWriter做登录
(1)ObjectInputStream / ObjectOutputStream
二十五、IO之File类及常用方法
25.1 什么是I/O
I/O相关(输入/输出)流(数据流动)
数据流动的方向,读数据(输入Input) 写数据(输出output)
25.2 什么是文件
文件:一种电脑的存储形式,文件有不同的格式:.txt .doc .ppt .mp4 .jpg .zip等...
文件夹:是目录或路径
(1)为什么用文件存储
1、变量 只能存一份
2、数组 存储好多个 数据类型统一
3、集合 存储好多个 存储后个数还能改变 泛型----数据类型统一
如上三个都是Java中的类型(对象)-----------都存储在内存里,那么程序执行完毕,虚拟机jvm停止的时候,内存空间就回收了,数据都是临时性存储的。
4、文件 存储好多信息 文件是存储在硬盘上的-------都是永久性保存,数据虽然安全,但是文件毕竟不在内存中,需要通过I/O操作文件
25.3 File类
File:与电脑上的文件或文件夹产生一一对应的映射关系
File:是个类,在java.io中,可以表示文件或目录路径名(文件夹)的抽象形式,File与真实硬盘中的文件或文件夹,不是一个东西
File是在内存中的一个对象<---映射--->硬盘上的文件或文件夹,产生一个映射关系,互相映射
canRead() canWrite() isHidden() isFile()
isDirectory() 判断当前的file是否是一个目录(文件)
length() 获取文件中字节的个数
lastMondified() 获取文件最后的修改时间----毫秒值
String path = getAbsolutePath() 获取文件的绝对路径,绝对路径可以通过完整的字符串,定位盘符、文件夹、文件;
相对路径没有盘符的写法,当前工程(项目)所在的位置找寻
String name = getName() 获取文件的名字
boolean = createNeswFIle() 创建新的文件
boolean = mkdir() 创建新的文件夹,外层没有,不能创建
boolean = mkdirs() 创建新的文件夹,外层没有,可以自动创建
String name = getParent() 获取当前file的父亲file名字
File file = getParentFile() 获取当前file的父亲file对象
String[] names = list() 获取当前file的所有儿子的名字(只有文件夹可以调用,文件调用就是空的,只有文件夹才会有儿子)
File[] files = listFiles() 获取当前file的所有儿子的对象
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author: Insight
* @Description: TODO 操作File类
* @Date: 2023/11/3 20:25
* @Version: 1.0
*/
public class TestFile {
public static void main(String[] args) {
//File类没有无参构造函数,也就是创建对象时参数不能为空
File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test.txt");
//file对象 是真实的文件吗? 不是
//file对象在堆内存中,创建出来的一个对象空间
//路径是看创建的对象,是否能与硬盘中的一个真实文件产生对应映射关系
//通过文件流去读取文件的内容
//系统内硬盘上的文件的名字,是不区分大小写的,内存中File对象,变量名字区分大小写
//文件本身的一些属性 --->
//如果test.txt是否能打开,
System.out.println(file.canExecute());//true
//如果test.txt可写就true,否则false
System.out.println(file.canRead());//true
System.out.println(file.canWrite());//true
//文件是否隐藏
System.out.println(file.isHidden());//false
//判断当前的file是否是一个文件
System.out.println(file.isFile());//true
//判断当前的file是否是一个目录(文件)
System.out.println(file.isDirectory());//false
System.out.println("==============================================================");
//文件的大小是多少
//Array.length List.size() String.length() File.length()
long l = file.length();
System.out.println(l);//0
System.out.println("==============================================================");
//获取文件最后修改时间
long time = file.lastModified();//毫秒的 看不懂可以格式化一下
Date date = new Date(time);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd KK:mm:ss");
System.out.println(sdf.format(date));//2023-11-04 09:57:39
System.out.println("==============================================================");
//修改文件的时间
file.setLastModified(time);
System.out.println("==============================================================");
//获取文件的名字
String name = file.getName();//Test.txt
System.out.println("==============================================================");
//获取文件的绝对路径
String path = file.getAbsolutePath();
System.out.println(path);//D:\IDEA_JavaCode\Duyi\JavaBasics05\TestFile\Test.txt
System.out.println("==============================================================");
//获取文件的相对路径,通常是用相对路径,自动找到跟目录
File file2 = new File("TestFile\\Test.txt");
String path2 = file2.getAbsolutePath();
System.out.println(path2);//D:\IDEA_JavaCode\Duyi\JavaBasics05\TestFile\TestFil\Test.txt
System.out.println("==============================================================");
//创建一个file对象
File file1 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test02.txt");
//文件夹是不占空间的,占内存的是文件,是文件夹中的文件,所以文件夹是没有异常的
//通过这个对象,回头在硬盘上,创建文件,如果文件盘符写错了,他就真写不了了
try{
boolean value = file1.createNewFile();//编译时异常
System.out.println(value);//true
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("==============================================================");
//创建file对象
File file3 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test03");
//在硬盘上,创建一个新的文件夹,文件夹的创建是没有什么异常的
//文件夹是不占空间的,占内存的是文件,是文件夹中的文件,所以文件夹是没有异常的
boolean value = file3.mkdir();
System.out.println(value);//true
File file4 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test04//inter.txt");
boolean b2 = file4.mkdir();//外层(父元素)需要真实存在才能创建(Test04)
System.out.println(b2);//false
boolean b = file4.mkdirs();//可以创建文件夹,如果外层没有,也会同时创建文件夹
System.out.println(b);//true
System.out.println("==============================================================");
File file5 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//inter");
String pname = file5.getParent();//当前file的父亲名字 只是名字 TestFile
//这个用的多一点开发
File pfile = file5.getParentFile();//当前file的父亲对象 file----表示D盘IDEA_JavaCode//Duyi//JavaBasics05//TestFile//文件夹下的对象
System.out.println(pname);
System.out.println(pfile.getAbsolutePath());
System.out.println("==============================================================");
//遍历当前file的所有父目录
File files = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test.txt");
//想通过循环的方式看到这个文件往上的每一层关系
File pfiles = files.getParentFile();//获取它的父亲
while (pfiles != null) {//等于null说明是盘符
System.out.println(pfiles.getAbsolutePath());//先输出自己的父亲是谁
pfiles = pfiles.getParentFile();//再找一遍,找父亲的父亲
}
System.out.println("==============================================================");
File filess = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile");
//数组对象为空,证明当前的file是一个文件
//数组对象不文空,证明当前的file是一个文件夹
//如果数组对象的长度不为0,证明当前的file是一个不为空的文件夹,文件夹内有元素
File[] file0 = filess.listFiles();
System.out.println(file0);
System.out.println(file0.length);
System.out.println("==============================================================");
//遍历当前file的子元素需要通过while循环,那么如果当前file的子元素中还有子元素又需要while循环,所以遍历子元素不能和父元素一样
File file00 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test02.txt");
boolean bb = file.delete();//不能瞎玩啊
System.out.println(bb);
System.out.println("==============================================================");
}
}
如果把下面的属性改了,上面的代码就会跟随你文件属性进行改变
25.4 什么叫流,做什么?
流:就是操作文件中的内容
输入流:读取文件中的内容
输出流:向文件中写内容
比如:文件的复制和文件的加密
二十六、IO之文件夹遍历删除(递归)
26.1递归的理解
1、文件夹的遍历----需要一个递归来完成
public class TestMethod {
public void testOne() {
this.testTwo();
System.out.println("我是testOne方法");
}
public void testTwo() {
this.testThree();
System.out.println("我是testOne方法");
}
public void testThree() {
this.testOne();
System.out.println("我是testOne方法");
}
public static void main(String[] args) {
TestMethod tm = new TestMethod();
//调用One,One等Two做完在做事,Two等Three做完在做,所以Three最先做,然后在Two、One
/*
临时执行testOne(1.调用TestTwo 2.自己的执行;未销毁因为还未执行完)
临时执行testTwo(1.调用TestThree 2.自己的执行;未销毁因为还未执行完)
临时执行testThree(1.自己的执行;执行完销毁)
本质上就是递归
*/
tm.testOne();
}
}
理解递归:
public class TestRecursion {
//建房子
//递归思想:如果你想让我做事(让我盖第五层),只盖第5层,所以我要求别人先把前四层盖好了我才盖(5等4,4等3)
//调用顺序是从上到下的,执行顺序是从下到上的;1、让别人先做事;2、我自己做事
//5 等4做完 ---执行
//4 等3做完 ---执行
//3 等2做完 ---执行
//2 等1做完 ---执行
//1 不等 ---直接执行
public void buildHouse(int floor) {//5层
//判断当前floor是否为1,若不是找一个别人先盖之前的层
if (floor > 1) {
this.buildHouse(floor-1);
}
//2、我自己做事
System.out.println("盖到第" + floor + "层了");
}
public static void main(String[] args) {
TestRecursion testRecursion = new TestRecursion();
testRecursion.buildHouse(5);
}
}
26.2 文件夹遍历删除
递归不是一层一层找的,而是一个分支一直找到底
public class NewTestFile {
//方法:用来展示(遍历)文件夹;参数-file(代表文件或文件夹)
//递归不是一层一层找的,而是一个分支一直找到底
public void showFile(File file) {
//1.判断file是否一个文件夹,文件夹内如果有元素,找一个人先做
//获取file的子元素,files==nul是个文件,files!=null是个文件夹,files.length!=是一个带元素的文件夹
File[] files = file.listFiles();//src文件夹所有子元素
if (files != null && files.length != 0) {
for(File f:files) {//将每一个子元素都找人遍历一遍
this.showFile(f);//循环第一次 src文件夹中的test 第二次 test文件夹中的test.html
}
}
//2.做自己的显示(file是文件或file是一个空文件夹),放前面运行和放后面运行不一样
System.out.println(file.getAbsolutePath());
}
public static void main(String[] args) {
NewTestFile newTestFile = new NewTestFile();
File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02//Test");
newTestFile.showFile(file);//找到src中的所有子
}
}
递归删除文件夹中的子元素,别瞎玩
public static void main(String[] args) {
NewTestFile newTestFile = new NewTestFile();
File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02//Test");
newTestFile.deleteFile(file);//删除之后回收站也没有。别瞎玩
}
public void deleteFile(File file) {
//判断file不是空文件夹,找人先做事
File[] files = file.listFiles();
if (file != null && files.length != 0) {
for (File f:files) {
this.deleteFile(f);
}
}
//删除file(file是个文件或file是一个空文件夹)
file.delete();
}
1、文件夹的遍历、文件夹的删除----需要一个递归
2、文件夹的路径---循环
二十七、字节型文件流
27.1 流
file对象不能操作文件中的内容--------需要通过I/O的方式来完成
流 按照方向(功能)来区分:in(读取) out(写入)
操作的目标来区分:文件流、数组流、字符串流、数据流、对象流、网络流....
学习文件流:读取文件中的信息in,将信息写入文件中out;
文件流按照读取或写入的单位(字节数)大小来区分:
(1)字节型文件流(1字节) FileInputStream / FileOutputStream
(2)字符型文件流(2字节~1字符) FileReader / FileWriter
27.2 字节型文件流(1字节)
(1)字节型文件输入流FileInputStream
FileInputStream
(1)java.io
(2)了解继承:InputStream类,字节型输入流的父类
(3)创建对象:调用一个带file类型的构造方法、调用一个带String类型的构造方法
(4)常用方法:
int code = read(); 每次从流管道中读取一个字节,返回的是字节的code码
int count = read( byte() ); 每次从n流管道中读取若干个字节,存入数组内,返回有效元素个数
public static void main(String[] args){
//理解为:文件是一个仓库,fis对象搬运工,推一个平板车
try{
//创建一个字节型的文件输入流,读取一个文件中内容
File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02"
+ "//Test//aaa//02.txt");
FileInputStream fis = new FileInputStream(file);//真实去读文件,如果没有此文件就会报错,所以我们可以try
int i = fis.read(); //读取一个字节 -1,i表示这一个字节的code码,每次只能读一个字节太慢了
System.out.println(i);//97
//有可能读不到第一个字节
while (i != -1) {//知道最后读完
System.out.print((char)i);//读取的字节的UniCode码 0~65535
i = fis.read(); //读取下一行的一个字节 -1
}
} catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("D://IDEA_JavaCode//Duyi//JavaBasics05"
+ "//TestFile02//Test//aaa//02.txt");
//创建一个空的数组(不是去数组里读东西,目的是去文件里读东西,然后把东西装入数组内)
//利用一个数组从文件中读东西,装数组里,再拿这个数组给他显示出来
byte[] b = new byte[5];//每次存储5个字节
int count = fis.read(b);//去文件里读东西(读5个),装入数组b内,返回int型count表示读取到的有效字节个数
System.out.println("有效字节个数count = " + count);//5
while (count != -1) {
//把数组构建成一个String在展示出来
//String value = new String(b);
//第一次 a b c d e
//第二次 f g \r \n h count=5将第一次的元素覆盖
//3 i j k l m .....
//4 n \r \n o p .....
//5 q \r \n o p count=1,此时的数组内还有第4次的元素,但是第5次就一个元素只能覆盖第4次的第一个元素
//就是构建String出来问题,每次都构建5给元素的位置,最后一行没有5个元素,我们应该有几个元素就构建几个空间
//把数组构建成一个String在展示出来
String value02 = new String(b,0,count);//从索引0构建count个空间
System.out.print(value02);
//在读一遍,读下一行
count = fis.read(b);
}
}catch (IOException e) {
e.printStackTrace();
}
}
int count = available(); 返回流管道中还有多少缓冲的字节数(就是fis对象与硬盘之间有一个流管道,这个管道进行一一映射,其中这个管道中有流动的数据(元素),available()用于获取流动的元素)
long l = skip( long n ); 跳过几个字节在读取,多线程----利用几个线程同时读取文件(1000字节,5个人同时读取;第一个人读1~200;第二个人2001~400....)
close(); 将流管道关闭,必须要做的事,最好放在finally里,注意代码的健壮性,判断严谨
public static void main(String[] args) {
//先定义为空,因为new File有可能是空文件夹或没有此文件,会报异常(new的过程才会产生异常)
//,所以真正的文件或文件夹还是要在try中
FileInputStream fis = null;
try {
fis = new FileInputStream(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02"
+ "//Test//aaa//02.txt"));
long l = fis.skip(5);
System.out.println(l);
int code = fis.read();
System.out.println(code);
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭的是流通道,不是file对象,关闭这件事情必须要做(如果不关闭就像你优盘不能退出一样
//,当你点击退出时,显示您的设备正在打开)
//所以关闭不能在try中如果前面的代码出现问题,这个close就不能执行了,try体系中finally
//是必须执行的,所以放在finally
try {
//fis在执行try后就不存在了,所以finally中变量fis就失效了(相当于未定义fis),所以要在try体系外定义fis,
//但是你在外面给fis=null,这里的null空无法调用close方法,所以还要在这里定义一个try体系
if (fis != null) {
fis.close();//如果不为空,我最后就要关闭文件
}
//如果是空,就没必要关闭了
} catch (IOException e) {
e.printStackTrace();
}
}
}
(2)字节型文件输出流FileOutputStream
FileOutputStream 将数据写入文件中
(1)java.io
(2)继承OutputStream,所有字节型输出流的父类
(3)创建对象:1、调用一个带FIle参数,还有FIle,Boolean重载
2、调用一个带String参数,还有String,boolean重载
(4)常用方法
write(int code); 将给定code对应的字符写入文件
write( byte[] ); 将数组中的全部字节写入文件,可以用此方法getByte()将写好的字符串转化为byte数组
String str = "1+1=2"; byte[] b = str.getBytes();
close(); 注意在finally中关闭
flush(); 将管道内的字节推入文件中(刷新)
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//创建一个字节型文件输出流
File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02"
+ "//Test//test00//test.txt");
//如果创建的是文件输入流,若文件路径有问题,则抛出异常 FileNotFoundException(输入读)
//若创建是输出流,他是输出写入,若文件路径有问题,则直接帮我们创建一个新的文件(文件夹路径写错就不行)
fos = new FileOutputStream(file);
//我们写的a是写在了流管道里面了(第一次run执行a写入了txt文件中)
//fos.write(97);
//这时我们在写一个b,但是打开text文件中发现上次写入的a被这次的b覆盖了(第二次run),其实是构建的事FileOutputStream(file)
//它每次都创建一个新的file对象,然后覆盖了你之前的txt文件,如果想追加内容需要FileOutputStream(file,true)利用构造方法
fos.write(98);
//刷新,将管道中的字节,推入文件中
fos.flush();
System.out.println("写入完毕");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//创建一个字节型文件输出流
fos = new FileOutputStream("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02"
+ "//Test//test00//test.txt",true);
//创建一个数组(带信息的)
//byte[] b = new byte[]{97,98,99};
String str = "1+1=2";
byte[] b = str.getBytes();
fos.write(b);//将信息写入
fos.flush();//刷新
System.out.println("写入完毕");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
(3)小总
字节型文件流:FileInputStream、FileOutputStream
(1)所在的包java.io
(2)各自继承关系InputStream / OutStream
(3)构造方法
利用file对象构造 new FileInputStream( file ); new OutputStream( file, true );
利用String对象构造 new FileInputStream( " " ); new OutputStream( " " ,true);
27.3 小任务文件夹复制
(1)文件的复制
public class OperateFile {
public void copyFile(File file, String path) {
FileInputStream fis = null;
FileOutputStream fos = null;
try{
//创建输入流
fis = new FileInputStream(file);
//创建新的file对象,用于操作复制
File newFile = new File(path + "//" + file.getName());
//给新file创建输出流
fos = new FileOutputStream(newFile);
//读取文件,不可能一次读完,所以循环
byte[] b = new byte[1024];//每次读取 1kb~8kb之间
int count = fis.read(b);//每次1024个字节,最后一行没有1024,那么默认值是0,所以
//循环读取
while (count != -1) {
//可以在这里做点手脚,比如加密
//要将读取到的有效字节 写入,利用构造方法
fos.write(b,0,count);
fos.flush();
count = fis.read(b);//在读
}
System.out.println("复制完毕");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭输入流
try{
if (fis != null) {
//输入流和输出流 各关各个,如果出现一次就会导致下一个未关闭
fis.close();
}
}catch (IOException e) {
e.printStackTrace();
}
//关闭输出流
try{
if (fos != null) {
//输入流和输出流 各关各个,如果出现一次就会导致下一个未关闭
fos.close();
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
OperateFile of = new OperateFile();
of.copyFile(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02" +
"//Test//test.txt"), "D://IDEA_JavaCode//Duyi//JavaBasics05//" +
"TestFile02//Test02");
}
可以做点加密
如果想把加密完成的文件解密回去,首先将加密后的文件剪切到原路径下(把原路径下最开始的文件删除),然后在加密一次,也即是将元素在互换回去。
//循环读取
while (count != -1) {
//可以在这里做点手脚,比如加密,每一次数组的前两个元素位置互换 1024
byte temp = b[10];
b[0] = b[1];
b[1] = temp;
//要将读取到的有效字节 写入,利用构造方法
fos.write(b,0,count);
fos.flush();
count = fis.read(b);//在读
}
(2)文件夹的复制
//文件夹的复制 ,file可以表示文件 或者 文件夹
public void superCopyFile(File file, String newPath) {
//获取file(原文件)的绝对路径,拼串的方式获取新文件的名字
String oldFilePath = file.getAbsolutePath();
//按照冒号进行拆分(冒号没有了),只剩C和Test//aaa,[1]表示Test//aaa这一堆
//将目标路径放在想复制的路径前面D://Test//bb复制到
String newFilePath = newPath + oldFilePath.split(":")[1];
//创建新的file对象
File newFile = new File(newFilePath);
//判断当前传递进来的file是个文件还是文件夹 isFile isDirectory listFiles直接找寻当前文件夹的子元素
//获取当前传递进来的file对象的所以子元素
File[] files = file.listFiles();
//如果是文件夹,先把外层的文件夹复制过去,才能往里面写
if (files != null) {//file不空说明是一个文件夹,才有数组对象
//通过新的file对象操作,在硬盘上创建一个文件夹
newFile.mkdir();//不用mkdirs因为这个file就一个,先把一个文件夹创建过去,下一次递归还是一个文件
System.out.println(newFile.getName() + "文件夹复制完毕");
//如果里面还有元素怎么办?递归
//发现这个if什么时候会执行?是不是只有是文件夹的时候才会执行,文件是走的if 文件夹走下面else
if (files != null && files.length != 0) {//证明里面还有东西
//里面有子文件或文件夹,就在调用
for (File f:files) {
this.superCopyFile(f,newPath);
}
}
} else {//file是一个文件,没有子元素,不需要数组对象
//创建两个文件流,分别读取旧的file和写入新的newFile
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(file);
fos = new FileOutputStream(newFile);
byte[] b = new byte[1024];
int count = fis.read(b);
//循环读取
while (count != -1) {
//将有效字节读到就写入
fos.write(b,0,count);
fos.flush();
//别忘记在读一边,将读来的信息进行覆盖
count = fis.read(b);
}
System.out.println(newFile.getName() + "文件复制完毕");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭输入流
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//关闭输出流
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//剪切思路也是差不多
public void 剪切(){
this.superCopyFile();
this.deleteFile()
}
二十八、字符型文件流
27.2 字符型文件流
(1)和上面的字节型文件流使用方法一样,就是byte数组变成char数字,字符型是为了识别汉字的
(2)只能操作纯文本的文件.txt
(3)创建字符流对象的时候,会产生编译时异常 FileNotFoundException
(1)FileReader输入流
(1)java.io包
(2)继承 InputStreamReader
(3)常用:int code = read(); int count = read(char[])
(2)FileWriter输出流
(1)java.io包
(2)继承 OutputStreamWriter
(3)常用:write(int); write(char[]); write(String); flush close
(4)构造方法:
带file参数; 带file,boolean参数; 带String参数; 带String,boolean参数
(3)字符集
字符集
字符:文字和符号总成(Character)
不同国家的数字和符号是一样的,字母
不同国家的文字(中文、日文、韩文)
计算机最早产生是按照英文单词,单个字符设计的;字母、数字、符号--------1字节、8bit、256
如果计算机想要处理上述字母符号以外的其他字符--比如中文2字节,需要将中文进行字符编码----拆分和组合
拆分组合的规则------所谓的字符编码
平台(操作系统)默认字符集GBK,Linux(MacOs)默认字符集UTF-8
编译使用的开发环境 Idea---UTF-8 Eclipse---GBK
所以我们怎么才能让中文不出现乱码,我们改平台的字符集是比较麻烦的,所以我们改编译器的字符集
设置默认字符集为UTF-8
try{
//创建一个字符型文件输入流----一个字符
FileReader fr = new FileReader(new File("D://IDEA_JavaCode//Duyi//JavaBasics05" +
"//TestFile02//Test//test.txt"));
int code = fr.read();
System.out.println(code);//25105读出来的时候是以GBK的形式读取的
System.out.println((char)code);//我
} catch (IOException e) {
e.printStackTrace();
}
二十九、缓冲流
29.1 缓存流
文件流:FileInputStream/FileOutputStream、FileReader/FileWriter
缓冲流:为了在流管道内增加缓存的数据,让我们使用流读取的文字更加的流程
为了保证数据是源源不断的流出,而不是断断续续的,可以在管道的硬盘端变粗(增加缓存数据),让主干的数据远远大于流出的数据(这里就是将高级流去包装低级流,缓冲流也叫高级流,它的创建通过低级流来完成的),让我们使用流读取的文字更加的流程
缓冲流也叫高级流,它的创建通过低级流来完成的,缓冲流的创建它里面放的是FileInputStream、FileOutputStream、FileReader、FileWriter是这四个低级流,就是高级流BufferedInputStream/BufferedOutputStream、BufferedReader/BufferedWriter将前面四个包装起来了,但是后面四个的性能和效率更高,但是使用方法差不多
缓冲流:BufferedInputStream/BufferedOutputStream、BufferedReader/BufferedWriter
29.2 字节型缓存流
public static void main(String[] args) {
try {
/*TODO 高级流的构建方式需要使用低级流构建*/
FileInputStream fis = new FileInputStream(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
"TestFile02//Test//test.txt"));
BufferedInputStream bis = new BufferedInputStream(fis);//装入低级流
FileOutputStream fos = new FileOutputStream(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
"TestFile02//Test//test.txt"));
BufferedOutputStream bos = new BufferedOutputStream(fos);//装入低级流
} catch (IOException e) {
e.printStackTrace();
}
}
(1)BufferedInputStream
(1) 构建方式,使用低级流构建,需要将低级流装入高级流
(2)基本使用与低级流的方式完全一致
read(); skip(); available(); close();
bis.read(); bis.read(byte[]); bis.available(); bis.skip(long); bis.close();
(2)BufferedOutputStream
(1) 构建方式,使用低级流构建,需要将低级流装入高级流(注意:缓存流构建的时候没有boolean类型的参数)
(2)基本使用与低级流方法完全一致
write(); flush(); close();
bos.write(int); bos.write(byte[]); bos.flush(); bos.close();
29.3 字符型缓冲流
(1)BufferedReader
try{
FileReader fr = new FileReader(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
"TestFile02//Test//test.txt"));
BufferedReader br = new BufferedReader(fr);
//读取文件中一行的信息
String value = br.readLine();
while (value != null) {
System.out.println(value);
value = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
(3)用BufferedReader和BufferedWriter做登录
既然BufferedReader可以通过String value = br.readLine();方法读取一行信息,那么我们可不可以用它做登录?
以前数组、box、集合(List Set Map)用这些来做真实数据的存储,但是他们都是临时性的存储,因为他们都在内存中
所以用文件更好,永久性的存储,存储在硬盘上
public class TestBufferedReaderAndWriter {
/**
* @Description: TODO 用来做登录用户认证
* @Author: 曹宇希
* @Date: 2023/11/7 12:24
* @Param: [username, password]
* @Return: java.lang.String
*/
public String login(String username, String password) {
//真实的名字和密码,永久的存在数据库中-----数据持久化
//文件我们采用.txt形式的纯文本(方便),文本中的内容,以行为单位的,每一行记录一个人的信息
try {
BufferedReader br = new BufferedReader(new FileReader("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
"TestFile02//Test//login.txt"));
//user表示一行的记录信息,记录着账号和密码
String user = br.readLine();
while (user != null) {
//如果读到以后,将user信息,按照-拆分,分别于方法的参数进行比较 v[0]账号 v[1]密码
String[] value = user.split("-");
if (value[0].equals(username)) {
if (value[1].equals(password)) {
return "登录成功!";
}
}
//在读一边,进行下一行信息
user = br.readLine();
}
} catch (Exception e) {
}
return "用户名或密码错误!";
}
public static void main(String[] args) {
TestBufferedReaderAndWriter test = new TestBufferedReaderAndWriter();
String result = test.login("xxx","123");
System.out.println(result);
try{
//也可以向txt中写入信息,append追加信息
BufferedWriter bw = new BufferedWriter(new FileWriter("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
"TestFile02//Test//login.txt",true));
//写入
bw.newLine();
bw.write("sada-12434");
bw.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
三十、小总文件流
文件流:
低级: 字节型 FileInputStream / FileOutputStream
字符型 FileReader / FileWriter
缓存(高级): 字节型 BufferedInputStream / BufferedOutputStream(操作文件/文件夹本身的加密复制)
字符型 BufferedReader / BufferedWriter(开发的时候经常用,通常操作纯文本拿它来做数据库来用)
三十一、数组流(了解)
数组流: byte数组 ByteArrayInputStream / ByteArrayOutputStream
char数组 CharArrayReader / CharArrayWriter
三十二、对象流
(1)为什么有要用文件:文件永久性保存信息,将很多的数据直接存入为念----数据持久化
(2)如果按照以行为单位写信息:好处在于每一行记录的信息都是相关的,信息的我们可以读取出来,可以直接看懂文件;
第一:不安全,你能看懂别人也行
第二:如果对象中有一些方法(动作)它是无法记录的,只能记录一些String信息,属性也行
所以才会出现对象流。
对象流:ObjectInputStream / ObjectOutputStream
(1)将对象直接存入文件中,将对象拆分成字节码,然后直接写入文件
32.1 对象的序列化和反序列化
(1)ObjectInputStream / ObjectOutputStream
对象的序列化:将一个完整的对象,拆分成字节碎片,记录在文件中(将对象写入文件)
对象的反序列化:将文件记录的对象碎片,反过来组合成一个完整的对象(从文件中读取对象)
如果想要将对象序列化到文件中:
(1)需要让对象Person实现Serializable接口下 ,是一个示意性的接口(里面没有实质性的动作)
public static void main(String[] args) {
try {
//将对象直接记录在文件中,永久保存,对象的序列化(持久化)
Person p1 = new Person("cyx",18);
//对象输出流
FileOutputStream fos = new FileOutputStream("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
"TestFile02//Test//test.text");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//将对象拆分成碎片,序列化到文件里,不能随便的序列化,必须要有一定规则的才行---去对象Person中实现Serializable接口
oos.writeObject(p1);
oos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
(2)同时为了让对象可以反序列化,我们需要让对象多存在一个属性private long serialVersionUID = 任意L,然后就可以将对象写进文件里了
如果想要将对象反序列化(就是从文件中读出来),需要给对象提供一个序列化的版本号,jdk有很多版本,如果我用1.7版本写一个String,然后读String是在一个1.8版本的运行环境中读的,写的版本和读的版本不一致,可能就导致方法里面的东西不一样(比如String在1.8版本进行了更新,更新了一些其他功能)
就是拿出来的对象需要和你现在的运行版本进行比对一下,如果版本没错我就给你反序列化读出来,否则无法反序列化)
然后输入移入User对象
package testbuffered;
import java.io.Serializable;
/**
* @Description: TODO 人类
* @Author: 曹宇希
* @Date: 2023/11/7 13:40
* @Version: 1.0
* @Company: 版权所有
*/
public class Person implements Serializable {
/**
* @Description: TODO 反序列化需要添加版本号,添加一个serialVersionUID然后给初始值,这个值可以随意
* @Author: 曹宇希
* @Date: 2023/11/7 19:20
*/
private long serialVersionUID = 5291531515806950297L;
private String name;
private int age;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void eat() {
System.out.println("Person的eat方法");
}
/**
* @Description: TODO 重写toString方法,就可以直接打印对象里面的东西了
* @Author: 曹宇希
* @Date: 2023/11/7 19:13
* @Param: []
* @Return: java.lang.String
*/
@Override
public String toString() {
return this.name + "," + this.age;
}
}
public static void main(String[] args) {
try{
//需要一个对象输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://IDEA_JavaCode//Duyi" +
"//JavaBasics05//" +
"TestFile02//Test//test.text"));
//readObject返回值是Object
Person p = (Person) ois.readObject();
System.out.println(p);
p.eat();
} catch (Exception e) {
e.printStackTrace();
}
}
三十三、银行系统(网上银行)练习
33.1 业务说明
1、实现一个银行业务系统
2、要求有如下的业务功能
(1)登录
(2)查询余额
(3)存款
(4)取款
(5)转账
(6)开户
(7)销户
3、记录银行的用户信息
账户-密码-余额
33.2 登录模块
我能拿到项目的时候第一件事先做什么?
我们所有的功能业务是不是需要底层数据的支持啊?
1、底层数据如何存储:先采用文件形式.txt,然后每一行记录一个人的所有信息(账户-密码-余额),以后是需要数据库的引入
那这个.txt数据存储在哪里合适?
(1)文件存储在固定的位置,这样可能会造成客户不存在这个数据,比如你存储在E盘,但是客户没有E盘,还有就是你写的项目和你的数据不在一个地方,也不方便查看(不建议)
(2)文件存储在当前项目的内部,这样项目和代码都在一起,你把项目部署在客户上,这个数据就跟着项目一起走了
(1)基本登录思想
(1)先在项目中创建一个数据User.txt(暂时当作数据库)
(2)创建Test类(因为一开始不知道后面还有什么类出现)
(3)设计login方法,参数String用户给的名字和密码,返回String一串登录是否成功的信息
1.要比对数据信息是否正确,所以采用字符型输入流进行读取信息(BufferedReader),采用相对路径File file = new File("src\atmsystem\User.txt");,这个高级流要装入低级流
2.创建输入流后采用读取一行的形式readLine()
3.如果数据为空读取的第一行也为空,所以加循环判断 value != null,数据库不为空就一直读取到空为止;然后将读取到信息进行拆分(user[0]用户名,user[1]密码),拆分后进行用户名和密码判断,如果对就直接返回String信息就结束了;如果不对在while里添加读取下一行readLine()进行读取下一行;不对之后还有return错误信息(这个不能写try中,因为如果输入流有误,就无法执行return了)
4.login方法写完进行测试,创建TestMain类进行测试各个方法是否有误Test t = new Test(); t.login("曹宇希","123456");
package atmsystem;
import java.io.*;
/**
* @Description: TODO 模拟网络银行的业务
* @Author: 曹宇希
* @Date: 2023/11/7 20:07
* @Version: 1.0
* @Company: 版权所有
*/
public class Test {
/**
* @Description: TODO 登录
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
* @Param: [username, password] 用户给的名字, 用户给的密码
* @Return: java.lang.String 返回登录成功与否
*/
public String login(String username, String password) {
try{
//创建一个字符型输入流,读取真实文件的记录
//file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
File file = new File("src\\atmsystem\\User.txt");
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
//读取一行数据信息,value表示一行人的信息
String value = bufferedReader.readLine();
//如何数据库不为空,就一致读到为空为止
while (value != null) {
String[] user = value.split("-");
if (user[0].equals(username)) {
if (user[1].equals(password)) {
return "登录成功!";
}
}
//总要读取下一行
value = bufferedReader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
}
return "密码或用户名错误!";
}
}
(4)对登录的疑问?我们做完之后会有很多客户去用,每一次只有登录都要让对象读一边,那么读文件就需要一个管道,频繁读文件特别慢,那能不能一次性都读到计算机里,留着它,以后登录就不需要读文件了,而是读取你留好的那个地方,这就是缓存机制性能高
发现登录的方法,每一次调用需要创建流管道,读取文件的信息,内存中操作登录的业务,文件不在一个地方,认为读取过程很慢,
(5)解决登录性能的优化。创建一个Map集合充当缓存,将文件中的数据全部都读出来,就读一次;创建Map集合充当缓存,将文件中的数据全部读取出来
一行记录,创建一个对象存起来,一个人,一个对象,三个属性,HashMap<String, User>第二个参数无论是什么,它的目的都是为了存储人的所有信息的,所以拿对象当第二个参数来提高阅读性,因为对象的名字可以随便写(自己创建),人的信息是可变的数组不太好,Map阅读性差,对象中的属性可以自己定义
private HashMap<String, User> userBox = new HashMap<String, User>();
(2)整体优化后:
-
创建UserBox缓存,用Map集合充当缓存,存储用户数据信息,key为用户账户,value为User对象用于存储用户数据信息,提供阅读性
-
创建User对象,实现Serializable接口,对象中定义属性(版本号、String账户、String密码、Float余额),设定构造方法用于创建对象,创建get和set方法用于获取和设置用户数据信息
-
缓存创建完成后,要给Map集合存入用户数据信息,创建程序块{}用于将数据存入map集合
-
程序块中采用try{ } catch( ){ } finally{ }结构,在此结构中创建字符型输入流FileReader和字符型缓存输入流BufferedReader,用于读取,采用读取一行信息的形式String value = bufferedReader.readLine();,由于读取信息不是一次读完,所以while (value != null) {}循环遍历
-
在while中将读取到的信息value进行拆分成数组String[] userValue = value.split("-");,将这些信息存入对象中 new User(String,String,Float);,然后再将对象存入Map集合中hashMap<String账户,User对象>,然后再去读取一行信息value = bufferedReader.readLine();
-
最后将字符型输入流FielReader和字符型缓存输入流BufferedReader关闭
-
设计login方法,登录 参数:用户给的名字, 用户给的密码,返回值:返回登录成功与否
-
在login中获取缓存中的用户名User user = userBox.get(username),也就是获取key然后找value,value是User对象;然后if判断对象中存数据信息与输入的是否一致
package atmsystem;
import java.io.*;
import java.util.HashMap;
//模拟网络银行的业务
public class Test {
// Map集合充当缓存(方便查找),存储人当中的所有数据,参数:String每个人的用户名不同(key) User对象存用户数据(value)
// 登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来放入缓存中
//一行记录,创建一个对象存起来,一个人,一个对象,三个属性,HashMap<String, User>第二个参数无论是什么,它的目的都是
//为了存储人的所有信息的,所以拿对象当第二个参数来提高阅读性,因为对象的名字可以随便写(自己创建),人的信息是可变的数组
//不太好,Map阅读性差,对象中的属性可以自己定义
private HashMap<String, User> userBox = new HashMap<String, User>();
// 程序块给Map存用户数据程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
{
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try{
//创建一个字符型输入流,读取真实文件的记录
//file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
File file = new File("src\\atmsystem\\User.txt");
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
//读取一行数据信息,value表示一行人的信息
String value = bufferedReader.readLine();
//如何数据库不为空,就一致读到为空为止
while (value != null) {
//将value的信息拆分成三段
String[] userValue = value.split("-");
//构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
User user = new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
//将对象存入Map集合<账户,User对象>
userBox.put(userValue[0],user);
//再次读取
value = bufferedReader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//fileReader关闭字符型输入流
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//bufferedReader关闭字符型缓存流
try{
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//登录 参数:用户给的名字, 用户给的密码,返回值:返回登录成功与否
public String login(String username, String password) {
User user = userBox.get(username);
if (user != null && user.getPassword().equals(password)) {
return "登录成功!";
}
return "密码或用户名错误!";
}
}
package atmsystem;
import java.io.Serializable;
//User 记录数据库中的一行信息,把账户-密码-余额以对象的形式包起来,继承Serializable接口
public class User implements Serializable {
//版本号
private static final long serialVersionUID = -322964954562611312L;
private String username;
private String password;
private Float userBalance;
//构造器
public User(){}
public User(String username, String password, Float userBalance) {
this.username = username;
this.password = password;
this.userBalance = userBalance;
}
//get,set方法
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Float getUserBalance() {
return userBalance;
}
public void setUserBalance(Float userBalance) {
this.userBalance = userBalance;
}
}
33.2 查询余额模块
package atmsystem;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
/**
* @Description: TODO 模拟网络银行的业务
* @Author: 曹宇希
* @Date: 2023/11/7 20:07
* @Version: 1.0
* @Company: 版权所有
*/
public class Test {
/**
* @Description: TODO Map集合充当缓存,存储人当中的所有数据
* String每个人的用户名不同(key) User对象存用户数据(value)
*登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
*/
private HashMap<String, User> userBox = new HashMap<String, User>();
/**
* @Description: TODO 程序块给Map存用户数据
* 程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
* @Author: 曹宇希
* @Date: 2023/11/8 14:59
*/
{
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try{
//创建一个字符型输入流,读取真实文件的记录
//file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
File file = new File("src\\atmsystem\\User.txt");
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
//读取一行数据信息,value表示一行人的信息
String value = bufferedReader.readLine();
//如何数据库不为空,就一致读到为空为止
while (value != null) {
//将value的信息拆分成三段
String[] userValue = value.split("-");
//构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
User user = new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
//将对象存入Map集合<账户,User对象>
userBox.put(userValue[0],user);
//再次读取
value = bufferedReader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//fileReader关闭字符型输入流
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//bufferedReader关闭字符型缓存流
try{
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*网络银行的业务,所有的业务方法,按照我们之前的优化结构设计,以下只有业务逻辑,判断、比较、
计算等等,看不到任何数据的操作(从哪查,从哪存都看不见),横线以上都是对数据的真实操作,所以
这两个应该是放在不同的类中*/
/**
* @Description: TODO 登录
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
* @Param: [username, password] 用户给的名字, 用户给的密码
* @Return: java.lang.String 返回登录成功与否
*/
public String login(String username, String password) {
User user = this.selectOne(username);
if (user != null && user.getPassword().equals(password)) {
return "登录成功!";
}
return "密码或用户名错误!";
}
/**
* @Description: TODO 查询余额
* @Author: 曹宇希
* @Date: 2023/11/8 16:17
* @Param: [username] 账户
* @Return: java.lang.Float
*/
public Float queryBalance(String username) {
User user = this.selectOne(username);
return user.getUserBalance();
}
/**
* @Description: TODO 存款
* 我们只做业务,至于怎么查出来的,怎么存入的不用管
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [username, depositMoney] 账户 存入的金额
* @Return: void
*/
public void deposit(String username, Float depositMoney) {
//找到User对象,修改对象中的balance属性
User user = this.selectOne(username);
//将原有的金额 加上 存入的金额
user.setUserBalance(user.getUserBalance() + depositMoney);
//将修改后的对象数据,在存入Map集合中
this.update(user);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
this.commit();
}
/**
* @Description: TODO 查询缓存中的对象信息
* @Author: 曹宇希
* @Date: 2023/11/8 16:41
* @Param: [username] 账户
* @Return: atmsystem.User 对象
*/
public User selectOne(String username) {
//这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
return userBox.get(username);
}
/**
* @Description: TODO 将一个修改完毕的对象在存入集合
* @Author: 曹宇希
* @Date: 2023/11/8 16:51
* @Param: [user]
* @Return: void
*/
public void update(User user) {
//集合做了修改
userBox.put(user.getUsername(), user);
}
/**
* @Description: TODO 将修改后的对象数据全部写入文件中
* HashMap<String, User>
* @Author: 曹宇希
* @Date: 2023/11/8 16:57
* @Param: []
* @Return: void
*/
public void commit() {
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
try {
//创建一个字符型文件输出流
File file = new File("");
fileWriter = new FileWriter(file);
bufferedWriter = new BufferedWriter(fileWriter);
//遍历集合,因为Map的集合key是不一样的,userBox.keySet()返回值是Set集合所以用迭代器
Iterator<String> names = userBox.keySet().iterator();
while (names.hasNext()) {
//Set集合内获取的某一个人名,用人名确定对象
String name = names.next();
//获取对象,记录一个人的真实数据
User user = userBox.get(name);
//将user的真实数据拼凑成一行字符串
StringBuilder builder = new StringBuilder(user.getUsername());
builder.append("-");
builder.append(user.getPassword());
builder.append("-");
builder.append(user.getUserBalance());
//将字符串在重写写入文件里,字符型文件输出流将拼接好的builder写入文件
//write参数是String的,builder转为字符串
bufferedWriter.write(builder.toString());
//换行
bufferedWriter.newLine();
bufferedWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭字符型输出流
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//关闭字符型输出缓存流
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @Description: TODO 取款
* 与存款一样就是加减的问题
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [username, withdrawalMoney] 账户 取出的金额
* @Return: void
*/
public void withdrawal(String username, Float withdrawalMoney) {
//找到User对象,修改对象中的balance属性
User user = this.selectOne(username);
//如果账户的金额大于取出的金额就可以取
if (user.getUserBalance() > withdrawalMoney) {
//将原有的金额 减去 取出的金额
user.setUserBalance(user.getUserBalance() - withdrawalMoney);
//将修改后的对象数据,在存入Map集合中
this.update(user);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
this.commit();
} else {
System.out.println("对不起," + username + "您的账户余额不足!");
}
}
/**
* @Description: TODO 转账
* 转账就是先存后取
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [outName, inName, transferMoney] 转出账户,转入账户,转账金额
* @Return: void
*/
public void transfer(String outName, String inName, Float transferMoney) {
//找到User对象,修改对象中的balance属性
User outUser = this.selectOne(outName);
//如果转出用户的金额大于转账金额就可以转
if (outUser.getUserBalance() > transferMoney) {
//查询收账用户,查询到底转账的给哪个用户
User inUser = this.selectOne(inName);
//收账的用户不为空,说明收账用户存在
if (inUser != null) {
//转出用户的余额 - 传出的钱
outUser.setUserBalance(outUser.getUserBalance() - transferMoney);
//收账用户的余额 + 上面转出的钱
inUser.setUserBalance(inUser.getUserBalance() + transferMoney);
//将修改后的对象数据,在存入Map集合中
this.update(outUser);
this.update(inUser);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
this.commit();
} else {
System.out.println("对不起,您输入的" + inName + "账户不存在!");
}
} else {
System.out.println("对不起," + outName + "您的账户余额不足!");
}
}
}
你会发现登录需要先查询集合中的对象key,通过key去找数据value信息;然后余额也需要这一步也是先查。那么以后在添加方法也会执行此代码过程,太繁琐了,所以我们设计一个查询方法selectOne(String username),目的为了我的所有业务服务,参数账户名,返回值User对象
1、设计selectOne方法用于查询缓存(Map集合)中的对象,怎么进行查Map集合,通过Key查询相应Value(userBox.get(账户名)),key表示账户,value表示一个人的数据信息,参数:String账户,返回值一个User对象
33.3 存款模块
存款就是在数据库中做一个对金额的修改,数据库中的用户名不变,密码也不变,数据库中的行数也没变;他只是某一行当中的某个信息发生改变。
在没有集合前,我们修改数据库.txt,是修改某一行的记录,我们读取一行可以,写一行新的也可以,但是在.txt中修改金额根本做不到
所以,我们想要修改数据,需要将原数据存到一个临时的地方,然后在临时的地方进行修改,然后再把修改后的临时数据在存入到原数据的文件下。
在有了集合后,集合除了可以充当缓存,增强执行性能以外,还能用来做数据的修改。(先将集合内的数据做修改,找到某一个user对象,将对象中的balance属性修改,然后在将集合内所有最终的数据全部写入文件中,文件内的所有内容替换掉)
33.4 整体代码
package atmsystem;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
/**
* @Description: TODO 模拟网络银行的业务
* @Author: 曹宇希
* @Date: 2023/11/7 20:07
* @Version: 1.0
* @Company: 版权所有
*/
public class Test {
/**
* @Description: TODO Map集合充当缓存,存储人当中的所有数据
* String每个人的用户名不同(key) User对象存用户数据(value)
*登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
*/
private HashMap<String, User> userBox = new HashMap<String, User>();
/**
* @Description: TODO 程序块给Map存用户数据
* 程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
* @Author: 曹宇希
* @Date: 2023/11/8 14:59
*/
{
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try{
//创建一个字符型输入流,读取真实文件的记录
//file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
File file = new File("src\\atmsystem\\User.txt");
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
//读取一行数据信息,value表示一行人的信息
String value = bufferedReader.readLine();
//如何数据库不为空,就一致读到为空为止
while (value != null) {
//将value的信息拆分成三段
String[] userValue = value.split("-");
//构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
User user = new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
//将对象存入Map集合<账户,User对象>
userBox.put(userValue[0],user);
//再次读取
value = bufferedReader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//fileReader关闭字符型输入流
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//bufferedReader关闭字符型缓存流
try{
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*网络银行的业务,所有的业务方法,按照我们之前的优化结构设计,以下只有业务逻辑,判断、比较、
计算等等,看不到任何数据的操作(从哪查,从哪存都看不见),横线以上都是对数据的真实操作,所以
这两个应该是放在不同的类中*/
/**
* @Description: TODO 登录
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
* @Param: [username, password] 用户给的名字, 用户给的密码
* @Return: java.lang.String 返回登录成功与否
*/
public String login(String username, String password) {
User user = this.selectOne(username);
if (user != null && user.getPassword().equals(password)) {
return "登录成功!";
}
return "密码或用户名错误!";
}
/**
* @Description: TODO 查询余额
* @Author: 曹宇希
* @Date: 2023/11/8 16:17
* @Param: [username] 账户
* @Return: java.lang.Float
*/
public Float queryBalance(String username) {
User user = this.selectOne(username);
return user.getUserBalance();
}
/**
* @Description: TODO 存款
* 我们只做业务,至于怎么查出来的,怎么存入的不用管
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [username, depositMoney] 账户 存入的金额
* @Return: void
*/
public void deposit(String username, Float depositMoney) {
//找到User对象,修改对象中的balance属性
User user = this.selectOne(username);
//将原有的金额 加上 存入的金额
user.setUserBalance(user.getUserBalance() + depositMoney);
//将修改后的对象数据,在存入Map集合中
this.update(user);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
this.commit();
}
/**
* @Description: TODO 查询缓存中的对象信息
* @Author: 曹宇希
* @Date: 2023/11/8 16:41
* @Param: [username] 账户
* @Return: atmsystem.User 对象
*/
public User selectOne(String username) {
//这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
return userBox.get(username);
}
/**
* @Description: TODO 将一个修改完毕的对象在存入集合
* @Author: 曹宇希
* @Date: 2023/11/8 16:51
* @Param: [user]
* @Return: void
*/
public void update(User user) {
//集合做了修改
userBox.put(user.getUsername(), user);
}
/**
* @Description: TODO 将修改后的对象数据全部写入文件中
* HashMap<String, User>
* @Author: 曹宇希
* @Date: 2023/11/8 16:57
* @Param: []
* @Return: void
*/
public void commit() {
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
try {
//创建一个字符型文件输出流
File file = new File("");
fileWriter = new FileWriter(file);
bufferedWriter = new BufferedWriter(fileWriter);
//遍历集合,因为Map的集合key是不一样的,userBox.keySet()返回值是Set集合所以用迭代器
Iterator<String> names = userBox.keySet().iterator();
while (names.hasNext()) {
//Set集合内获取的某一个人名,用人名确定对象
String name = names.next();
//获取对象,记录一个人的真实数据
User user = userBox.get(name);
//将user的真实数据拼凑成一行字符串
StringBuilder builder = new StringBuilder(user.getUsername());
builder.append("-");
builder.append(user.getPassword());
builder.append("-");
builder.append(user.getUserBalance());
//将字符串在重写写入文件里,字符型文件输出流将拼接好的builder写入文件
//write参数是String的,builder转为字符串
bufferedWriter.write(builder.toString());
//换行
bufferedWriter.newLine();
bufferedWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭字符型输出流
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//关闭字符型输出缓存流
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @Description: TODO 取款
* 与存款一样就是加减的问题
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [username, withdrawalMoney] 账户 取出的金额
* @Return: void
*/
public void withdrawal(String username, Float withdrawalMoney) {
//找到User对象,修改对象中的balance属性
User user = this.selectOne(username);
//如果账户的金额大于取出的金额就可以取
if (user.getUserBalance() > withdrawalMoney) {
//将原有的金额 减去 取出的金额
user.setUserBalance(user.getUserBalance() - withdrawalMoney);
//将修改后的对象数据,在存入Map集合中
this.update(user);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
this.commit();
} else {
System.out.println("对不起," + username + "您的账户余额不足!");
}
}
/**
* @Description: TODO 转账
* 转账就是先存后取
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [outName, inName, transferMoney] 转出账户,转入账户,转账金额
* @Return: void
*/
public void transfer(String outName, String inName, Float transferMoney) {
//找到User对象,修改对象中的balance属性
User outUser = this.selectOne(outName);
//如果转出用户的金额大于转账金额就可以转
if (outUser.getUserBalance() > transferMoney) {
//查询收账用户,查询到底转账的给哪个用户
User inUser = this.selectOne(inName);
//收账的用户不为空,说明收账用户存在
if (inUser != null) {
//转出用户的余额 - 传出的钱
outUser.setUserBalance(outUser.getUserBalance() - transferMoney);
//收账用户的余额 + 上面转出的钱
inUser.setUserBalance(inUser.getUserBalance() + transferMoney);
//将修改后的对象数据,在存入Map集合中
this.update(outUser);
this.update(inUser);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
this.commit();
} else {
System.out.println("对不起,您输入的" + inName + "账户不存在!");
}
} else {
System.out.println("对不起," + outName + "您的账户余额不足!");
}
}
}
package atmsystem;
import java.io.Serializable;
/**
* @Description: TODO User 记录数据库中的一行信息
* 把账户-密码-余额以对象的形式包起来
* 继承Serializable接口
* @Author: 曹宇希
* @Date: 2023/11/8 13:51
* @Version: 1.0
* @Company: 版权所有
*/
public class User implements Serializable {
/**
* @Description: TODO 版本号
* @Author: 曹宇希
* @Date: 2023/11/8 14:35
*/
private static final long serialVersionUID = -322964954562611312L;
/**
* @Descript
* @Author: 曹宇希
* @Date: 2023/11/8 14:03
*/
private String username;
/**
* @Description: TODO 用户密码
* @Author: 曹宇希
* @Date: 2023/11/8 14:03
*/
private String password;
/**
* @Description: TODO 用户余额
* @Author: 曹宇希
* @Date: 2023/11/8 14:04
*/
private Float userBalance;
/**
* @Description: TODO 构造方法
* @Author: 曹宇希
* @Date: 2023/11/8 14:10
* @Param: [username, password, userBalance]
* @Return:
*/
public User(){}
public User(String username, String password, Float userBalance) {
this.username = username;
this.password = password;
this.userBalance = userBalance;
}
/**
* @Description: TODO 以下是get,set方法
* @Author: 曹宇希
* @Date: 2023/11/8 15:24
* @Param: []
* @Return: java.lang.String
*/
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Float getUserBalance() {
return userBalance;
}
public void setUserBalance(Float userBalance) {
this.userBalance = userBalance;
}
}
33.4 如何分类
1、我们写Test类,这个类中是银行的整体操作,包含数据的操作,业务逻辑的判断
2、我们发现这个数据的操作 和 业务逻辑是两个不同的模块,我们要实现每一个类只干一件事,这样才能体现出面向对象的思想
3、创建AtmDao类,用于存放数据的所有操作,首先Test类中:程序块缓存、selectOne方法查询缓存中的对象、update将一个修改完毕的对象在存入集合、commit方法将修改后的对象数据全部写入文件中;都是操作数据的,所以将这些都移入AtmDao类
这个DAO层属于---持久层,专门操作数据的层
4、这样Test类只负责业务上的逻辑,而DAO层AtmDao类只负责操作数据
网络银行的业务,所有的业务方法,按照我们之前的优化结构设计,以下只有业务逻辑,判断、比较、 计算等等,看不到任何数据的操作(从哪查,从哪存都看不见),横线以上都是对数据的真实操作,所以 这两个应该是放在不同的类中
package atmsystem;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
/**
* @Description: TODO 操作数据
* DAO作为层次--持久层,为了操作数据,只负责读写数据
* @Author: 曹宇希
* @Date: 2023/11/9 10:44
* @Version: 1.0
* @Company: 版权所有
*/
public class AtmDao {
/**
* @Description: TODO Map集合充当缓存,存储人当中的所有数据
* String每个人的用户名不同(key) User对象存用户数据(value)
*登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
*/
private HashMap<String, User> userBox = new HashMap<String, User>();
/**
* @Description: TODO 程序块给Map存用户数据
* 程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
* @Author: 曹宇希
* @Date: 2023/11/8 14:59
*/
{
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try{
//创建一个字符型输入流,读取真实文件的记录
//file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
File file = new File("src\\atmsystem\\User.txt");
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
//读取一行数据信息,value表示一行人的信息
String value = bufferedReader.readLine();
//如何数据库不为空,就一致读到为空为止
while (value != null) {
//将value的信息拆分成三段
String[] userValue = value.split("-");
//构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
User user = new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
//将对象存入Map集合<账户,User对象>
userBox.put(userValue[0],user);
//再次读取
value = bufferedReader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//fileReader关闭字符型输入流
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//bufferedReader关闭字符型缓存流
try{
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @Description: TODO 查询缓存中的对象信息
* @Author: 曹宇希
* @Date: 2023/11/8 16:41
* @Param: [username] 账户
* @Return: atmsystem.User 对象
*/
public User selectOne(String username) {
//这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
return userBox.get(username);
}
/**
* @Description: TODO 将一个修改完毕的对象在存入集合
* @Author: 曹宇希
* @Date: 2023/11/8 16:51
* @Param: [user]
* @Return: void
*/
public void update(User user) {
//集合做了修改
userBox.put(user.getUsername(), user);
}
/**
* @Description: TODO 将修改后的对象数据全部写入文件中
* HashMap<String, User>
* @Author: 曹宇希
* @Date: 2023/11/8 16:57
* @Param: []
* @Return: void
*/
public void commit() {
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
try {
//创建一个字符型文件输出流
File file = new File("");
fileWriter = new FileWriter(file);
bufferedWriter = new BufferedWriter(fileWriter);
//遍历集合,因为Map的集合key是不一样的,userBox.keySet()返回值是Set集合所以用迭代器
Iterator<String> names = userBox.keySet().iterator();
while (names.hasNext()) {
//Set集合内获取的某一个人名,用人名确定对象
String name = names.next();
//获取对象,记录一个人的真实数据
User user = userBox.get(name);
//将user的真实数据拼凑成一行字符串
StringBuilder builder = new StringBuilder(user.getUsername());
builder.append("-");
builder.append(user.getPassword());
builder.append("-");
builder.append(user.getUserBalance());
//将字符串在重写写入文件里,字符型文件输出流将拼接好的builder写入文件
//write参数是String的,builder转为字符串
bufferedWriter.write(builder.toString());
//换行
bufferedWriter.newLine();
bufferedWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭字符型输出流
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//关闭字符型输出缓存流
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
1、我们发现这个DAO层中,虽然都是操作数据的,但是它也分操作集合,和操作File流
2、所以我们还可以将其分类,创建FileLoaderAndCommit类用于操作File流,将跟流相关的东西全部移入此类中
3、将程序块输入流将信息写入Map集合,commit方法将修改后的数据对象在次装入文件中,将这两个存入此类
package atmsystem;
import java.util.HashMap;
/**
* @Description: TODO 操作数据
* DAO作为层次--持久层,为了操作数据,只负责读写数据
* @Author: 曹宇希
* @Date: 2023/11/9 10:44
* @Version: 1.0
* @Company: 版权所有
*/
public class AtmDao {
/**
* @Description: TODO Map集合充当缓存,存储人当中的所有数据
* String每个人的用户名不同(key) User对象存用户数据(value)
*登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
*/
private HashMap<String, User> userBox = new HashMap<String, User>();
/**
* @Description: TODO 查询缓存中的对象信息
* @Author: 曹宇希
* @Date: 2023/11/8 16:41
* @Param: [username] 账户
* @Return: atmsystem.User 对象
*/
public User selectOne(String username) {
//这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
return userBox.get(username);
}
/**
* @Description: TODO 将一个修改完毕的对象在存入集合
* @Author: 曹宇希
* @Date: 2023/11/8 16:51
* @Param: [user]
* @Return: void
*/
public void update(User user) {
//集合做了修改
userBox.put(user.getUsername(), user);
}
}
1、此时FileLoaderAndCommit类中有程序块,将程序块写出方法,程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中,所以设计loadFile方法,参数:void 返回值HashMap<String, User>集合
2、commit提交修改后的对象数据全部写入文件中,也设计成含参的,参数HashMap<String ,User> userBox
package atmsystem;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
/**
* @Description: TODO 操作File流
* @Author: 曹宇希
* @Date: 2023/11/9 10:49
* @Version: 1.0
* @Company: 版权所有
*/
public class FileLoaderAndCommit {
/**
* @Description: TODO 利用输入流程序块给Map存用户数据
* 程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
* @Author: 曹宇希
* @Date: 2023/11/8 14:59
*/
public HashMap<String, User> loadFile(){
HashMap<String, User> userBox = new HashMap<String, User>();
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try{
//创建一个字符型输入流,读取真实文件的记录
//file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
File file = new File("src\\atmsystem\\User.txt");
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
//读取一行数据信息,value表示一行人的信息
String value = bufferedReader.readLine();
//如何数据库不为空,就一致读到为空为止
while (value != null) {
//将value的信息拆分成三段
String[] userValue = value.split("-");
//构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
User user = new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
//将对象存入Map集合<账户,User对象>
userBox.put(userValue[0],user);
//再次读取
value = bufferedReader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//fileReader关闭字符型输入流
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//bufferedReader关闭字符型缓存流
try{
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return userBox;
}
/**
* @Description: TODO 将修改后的对象数据全部写入文件中
* HashMap<String, User>
* @Author: 曹宇希
* @Date: 2023/11/8 16:57
* @Param: []
* @Return: void
*/
public void commit(HashMap<String, User> userBox) {
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
try {
//创建一个字符型文件输出流
File file = new File("");
fileWriter = new FileWriter(file);
bufferedWriter = new BufferedWriter(fileWriter);
//遍历集合,因为Map的集合key是不一样的,userBox.keySet()返回值是Set集合所以用迭代器
Iterator<String> names = userBox.keySet().iterator();
while (names.hasNext()) {
//Set集合内获取的某一个人名,用人名确定对象
String name = names.next();
//获取对象,记录一个人的真实数据
User user = userBox.get(name);
//将user的真实数据拼凑成一行字符串
StringBuilder builder = new StringBuilder(user.getUsername());
builder.append("-");
builder.append(user.getPassword());
builder.append("-");
builder.append(user.getUserBalance());
//将字符串在重写写入文件里,字符型文件输出流将拼接好的builder写入文件
//write参数是String的,builder转为字符串
bufferedWriter.write(builder.toString());
//换行
bufferedWriter.newLine();
bufferedWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭字符型输出流
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
//关闭字符型输出缓存流
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
1、此时Test类中只有,负责处理业务逻辑的方法,它需要数据结构的支持,所以可以添加一个属性private AtmDao dao = new AtmDao();,这个dao就是数据,然后Test类中的this就可以替换为dao.用它来调用关于数据操作的事情,
2、你会发现取款,转账,存款这些方法最后都会调用commit来进行提交修改后的数据写入到文件中,其实我们可以在dao中的Update中实现,就是在更新完数据就立马提交
3、回到dao层AtmDao类中,要添加一个负责加载文件,更新文件数据的属性,因为你File操作和数据操作分开了;private FileLoaderAndCommit fileLoaderAndCommit = new FileLoaderAndCommit("src\atmsystem\User.txt"); 这个输入流要传递路径,因为路径不是写死的,所以在FileLoaderAndCommit中添加路径属性,还有设置构造方法fileName;
4、回到AtmDao类中,属性流创建完成后,还要有一个userBox存读取出来的信息private HashMap<String, User> userBox = fileLoaderAndCommit.loadFile();
5、最后都整理完发现Test类不能叫这个名字了,改为AtmService
package atmsystem;
/**
* @Description: TODO 模拟网络银行的业务
* 这个类都是负责处理业务逻辑
* @Author: 曹宇希
* @Date: 2023/11/7 20:07
* @Version: 1.0
* @Company: 版权所有
*/
public class AtmService {
/*网络银行的业务,所有的业务方法,按照我们之前的优化结构设计,以下只有业务逻辑,判断、比较、
计算等等,看不到任何数据的操作(从哪查,从哪存都看不见),横线以上都是对数据的真实操作,所以
这两个应该是放在不同的类中*/
/**
* @Description: TODO 数据结构
* @Author: 曹宇希
* @Date: 2023/11/9 18:06
*/
private AtmDao dao = new AtmDao();
/**
* @Description: TODO 登录
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
* @Param: [username, password] 用户给的名字, 用户给的密码
* @Return: java.lang.String 返回登录成功与否
*/
public String login(String username, String password) {
User user = dao.selectOne(username);
if (user != null && user.getPassword().equals(password)) {
return "登录成功!";
}
return "密码或用户名错误!";
}
/**
* @Description: TODO 查询余额
* @Author: 曹宇希
* @Date: 2023/11/8 16:17
* @Param: [username] 账户
* @Return: java.lang.Float
*/
public Float queryBalance(String username) {
User user = dao.selectOne(username);
return user.getUserBalance();
}
/**
* @Description: TODO 存款
* 我们只做业务,至于怎么查出来的,怎么存入的不用管
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [username, depositMoney] 账户 存入的金额
* @Return: void
*/
public void deposit(String username, Float depositMoney) {
//找到User对象,修改对象中的balance属性
User user = dao.selectOne(username);
//将原有的金额 加上 存入的金额
user.setUserBalance(user.getUserBalance() + depositMoney);
//将修改后的对象数据,在存入Map集合中
dao.update(user);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
//dao.commit();
//commit在流类,所以我们给提交设置为自动提交,也就是说在AtmDao类中的update中提交
}
/**
* @Description: TODO 取款
* 与存款一样就是加减的问题
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [username, withdrawalMoney] 账户 取出的金额
* @Return: void
*/
public void withdrawal(String username, Float withdrawalMoney) {
//找到User对象,修改对象中的balance属性
User user = dao.selectOne(username);
//如果账户的金额大于取出的金额就可以取
if (user.getUserBalance() > withdrawalMoney) {
//将原有的金额 减去 取出的金额
user.setUserBalance(user.getUserBalance() - withdrawalMoney);
//将修改后的对象数据,在存入Map集合中
dao.update(user);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
} else {
System.out.println("对不起," + username + "您的账户余额不足!");
}
}
/**
* @Description: TODO 转账
* 转账就是先存后取
* @Author: 曹宇希
* @Date: 2023/11/8 16:20
* @Param: [outName, inName, transferMoney] 转出账户,转入账户,转账金额
* @Return: void
*/
public void transfer(String outName, String inName, Float transferMoney) {
//找到User对象,修改对象中的balance属性
User outUser = dao.selectOne(outName);
//如果转出用户的金额大于转账金额就可以转
if (outUser.getUserBalance() > transferMoney) {
//查询收账用户,查询到底转账的给哪个用户
User inUser = dao.selectOne(inName);
//收账的用户不为空,说明收账用户存在
if (inUser != null) {
//转出用户的余额 - 传出的钱
outUser.setUserBalance(outUser.getUserBalance() - transferMoney);
//收账用户的余额 + 上面转出的钱
inUser.setUserBalance(inUser.getUserBalance() + transferMoney);
//将修改后的对象数据,在存入Map集合中
dao.update(outUser);
dao.update(inUser);
//当修改以后就可以将修改后的对象数据全部写入文件中
//将临时的数据永久的写入文件
} else {
System.out.println("对不起,您输入的" + inName + "账户不存在!");
}
} else {
System.out.println("对不起," + outName + "您的账户余额不足!");
}
}
}
package atmsystem;
import java.util.HashMap;
/**
* @Description: TODO 操作数据
* DAO作为层次--持久层,为了操作数据,只负责读写数据
* @Author: 曹宇希
* @Date: 2023/11/9 10:44
* @Version: 1.0
* @Company: 版权所有
*/
public class AtmDao {
/**
* @Description: TODO 负责加载文件,更新文件数据
* @Author: 曹宇希
* @Date: 2023/11/9 18:11
*/
private FileLoaderAndCommit fileLoaderAndCommit = new FileLoaderAndCommit("src\\atmsystem\\User.txt");
/**
* @Description: TODO Map集合充当缓存,存储人当中的所有数据
* String每个人的用户名不同(key) User对象存用户数据(value)
*登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
* @Author: 曹宇希
* @Date: 2023/11/7 20:34
*/
private HashMap<String, User> userBox = fileLoaderAndCommit.loadFile();
/**
* @Description: TODO 查询缓存中的对象信息
* @Author: 曹宇希
* @Date: 2023/11/8 16:41
* @Param: [username] 账户
* @Return: atmsystem.User 对象
*/
public User selectOne(String username) {
//这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
return userBox.get(username);
}
/**
* @Description: TODO 将一个修改完毕的对象在存入集合
* @Author: 曹宇希
* @Date: 2023/11/8 16:51
* @Param: [user]
* @Return: void
*/
public void update(User user) {
//集合做了修改
userBox.put(user.getUsername(), user);
//自动提交修改后的数据
fileLoaderAndCommit.commit(userBox);
}
}
package atmsystem;
import java.util.Scanner;
/**
* @Description: TODO 测试写的方法是否有问题
* @Author: 曹宇希
* @Date: 2023/11/7 21:15
* @Version: 1.0
* @Company: 版权所有
*/
public class TestMain {
public static void main(String[] args) {
AtmService service = new AtmService();
Scanner input = new Scanner(System.in);
System.out.println("欢迎您使用银行自助服务程序,请输入账户:\n");
String username = input.nextLine();
System.out.println("请输入密码:");
String password = input.nextLine();
String loginResult = service.login(username, password);
System.out.println(loginResult);
if ("登录成功!".equals(loginResult)) {
System.out.println("恭喜您登录成功,请输入操作项");
System.out.println("查询输入1\n存款输入2\n取款输入3\n转账输入4\n退出输入5\n");
String option = input.nextLine();
switch (option) {
case "1":
Float userBalance = service.queryBalance(username);
System.out.println("尊敬的" + username + "用户,您的余额为:" + userBalance);
break;
default:
System.out.println("您是输入的数字不匹配!");
}
} else {
System.out.println("对不起," + loginResult);
}
}
}
package atmsystem;
import java.util.Scanner;
/**
* @Description: TODO 测试写的方法是否有问题
* @Author: 曹宇希
* @Date: 2023/11/7 21:15
* @Version: 1.0
* @Company: 版权所有
*/
public class TestMain {
public static void main(String[] args) {
AtmService service = new AtmService();
Scanner input = new Scanner(System.in);
System.out.println("欢迎您使用银行自助服务程序,请输入账户:\n");
String username = input.nextLine();
System.out.println("请输入密码:");
String password = input.nextLine();
String loginResult = service.login(username, password);
System.out.println(loginResult);
if ("登录成功!".equals(loginResult)) {
System.out.println("恭喜您登录成功,请输入操作项");
System.out.println("查询输入1\n存款输入2\n取款输入3\n转账输入4\n退出输入5\n");
String option = input.nextLine();
switch (option) {
case "1":
Float userBalance = service.queryBalance(username);
System.out.println("尊敬的" + username + "用户,您的余额为:" + userBalance);
break;
default:
System.out.println("您是输入的数字不匹配!");
}
} else {
System.out.println("对不起," + loginResult);
}
}
}