当前位置:首页 > 百科

javaatm银行模拟系统

第一次跑通JavaATM模拟系统的行模时候,我盯着控制台里那行“余额:¥0.00”看了半天。拟系不是行模因为这个数字有多特别,而是拟系我突然意识到,一个能查余额、行模能取钱、拟系能转账的行模东西,核心逻辑居然这么薄——薄到像一张纸,拟系但纸背后站着的行模是整个银行业务的骨架。

先说说这个系统到底在模拟什么。拟系它不碰真实银行卡,行模不连银联接口,拟系甚至连数据库都可以省掉。行模所有账户信息塞在内存里,拟系用HashMap<String,行模 Account>存着,键是卡号,值是账户对象。Account类里就三个字段:卡号、密码、余额。简单到让人怀疑“这也算银行系统?”但恰恰是这种剥离了所有外围干扰的模型,把银行最本质的操作暴露出来了。

我写的第一版取款逻辑是这样的:输入卡号,输入密码,输入金额,判断余额够不够,够就减,不够就提示。跑起来没问题。然后我试着连续取两次,第二次取完余额变成负数了。查代码才发现,我忘了在减余额之前再查一次——因为第一次取款后余额已经变了,但我的判断用的是取款前的快照。这种bug在真实银行里叫“超取”,后果是银行直接亏钱。修法很简单,把判断和扣款放在同一个同步块里。synchronized关键字一加,问题消失。

转账更有意思。A转给B,要保证A扣钱和B加钱要么都成功,要么都失败。我一开始写的是先扣A再加B,结果测试时故意让B的账户不存在,A的钱扣了,B没收到。这就是典型的“钱丢了”。后来改成先检查两个账户都存在,再执行扣和加,并且把这两步包在同一个同步块里。虽然内存操作不会真的出现数据库那种事务回滚,但逻辑上必须模拟出原子性。不然这个系统就是残废的。

密码验证那块我偷了个懒。没做加密,明文比对。真实系统里密码得加盐哈希,但模拟系统里明文反而方便调试——你能直接看到用户输的是什么。当然,这只能存在于教学代码里。有一次我故意输错密码三次,系统没锁卡,只是反复提示“密码错误”。后来加了个计数器,错三次就冻结账户。这个改动很小,但让整个系统突然有了“风控”的味道。

界面?没有界面。所有交互都在控制台里用Scanner读输入,用System.out.println输出菜单。菜单长这样:

 1. 查询余额 2. 取款 3. 存款 4. 转账 5. 退出 

用户输入数字,switch跳转。没有图形化,没有动画,连清屏都做不到。但就是这种粗糙感,让我更清楚地看到每个操作背后的数据流动。比如存款,其实就是account.setBalance(account.getBalance() + amount)。一行代码,对应的是现实中柜员点钞、系统入账、短信通知一整套流程。模拟系统把中间那些全砍了,只留下最核心的算术。

有个细节值得说:余额我用的是double。后来发现0.1 + 0.2不等于0.3,取款时偶尔会出现0.30000000000000004这种鬼东西。换成BigDecimal之后干净了。这个坑在真实金融系统里是致命的,但在模拟系统里,它反而成了一个很好的教学案例——让你亲手踩一次,比看十篇浮点数精度文章都管用。

整个系统写完,代码不到三百行。但就是这三百行,把账户、认证、事务、并发、精度这些银行系统的核心概念全串了一遍。跑通之后我改了几个参数,比如把初始余额设成一百万,然后写个循环连续转账一千次,看余额会不会错。没错。又故意在转账中途抛异常,看会不会卡死。也没卡。那一刻我觉得,这个简陋的控制台程序,比很多花里胡哨的Demo都更接近“系统”两个字。

如果你也想写一个,别急着加界面加数据库。先把取款、存款、转账这三个操作的原子性做对,再考虑别的。内存版跑稳了,换JDBC就是分分钟的事。反过来,如果内存版都漏洞百出,接上MySQL也只是把bug藏得更深而已。

分享到: