image.png

    1. package com.itheima.ATM;
    2. import java.util.ArrayList;
    3. import java.util.Random;
    4. import java.util.Scanner;
    5. /**
    6. * ATM系统入口类
    7. */
    8. public class ATMSystem {
    9. public static void main(String[] args) {
    10. // 1.定义一个账户类(面向对象编程)
    11. // 2. 定义一个集合容器,负责以后存储全部的账户对象,进行相关的业务操作
    12. ArrayList<Account> accounts = new ArrayList<>(); // 里面存储的是Account类的对象
    13. // 展示系统的首页
    14. while (true) {
    15. System.out.println("===============黑马ATM系统====================");
    16. System.out.println("1.账户登录");
    17. System.out.println("2.账户开户");
    18. Scanner sc = new Scanner(System.in);
    19. System.out.println("请选择你的操作:");
    20. int command = sc.nextInt();
    21. switch (command){
    22. case 1:
    23. // 用户登录操作
    24. login(accounts,sc);// 自己定义一个方法要接所有的账户集合和让用户输入的扫描器Scanner对象sc
    25. break; // 这里的break跳出switch分支,跳到while循环,刚开始的地方
    26. case 2:
    27. // 用户账户开户
    28. // 目前还没有register方法,按alt + enter键自动创建方法
    29. register(accounts,sc); // register注册的意思,
    30. break;
    31. default:
    32. System.out.println("你输入的操作命令不正确"); // 这里应该定义一个死循环,让用户一直输入(选中要循环的部分,按ctrl + alt + t)
    33. }
    34. }
    35. }
    36. private static void login(ArrayList<Account> accounts, Scanner sc) {
    37. System.out.println("==================系统登录操作==================");
    38. // 1.判断账户集合中是否存在账户,如果不存在账户,登录功能不能进行
    39. if (accounts.size() == 0){ // 当输入里面的元素长度为0时代表里面没有账户对象,
    40. System.out.println("对不起,当前系统中,无任何账户,请先开户,再来登录");
    41. return; // return这里单独使用是:结束方法 (卫语言风格)
    42. }
    43. // 2.正式进入登录操作
    44. while (true) {
    45. System.out.println("请您输入登录卡号: ");
    46. String cardId = sc.next();
    47. // 3.判断卡号是否存在,根据卡号去账户集合中查询账户对象
    48. Account acc = getAccountByCardId(cardId, accounts);
    49. if (acc != null) {
    50. // 卡号存在的
    51. // 4.让用户输入密码,认证密码
    52. System.out.println("请您输入登录密码;");
    53. String passWord = sc.next();
    54. // 判断当前账户对象的密码是否与用户输入的密码一致
    55. if (acc.getPassword().equals(passWord)){
    56. System.out.println("恭喜你," + acc.getUserName() + "先生/女士进入系统,您的卡号是:" + acc.getCardId());
    57. // 代码走到上面,代表登录成功, // 现在要做的事查询 转账 取款 。。。
    58. // 展示登录后的操作页
    59. // 由于这个方法有了集合的形参,所以可以用形参作为showUserCommand的实参
    60. showUserCommand(sc,acc,accounts);// 由于定义的方法要用到扫描器Scanner,所以现在要在方法中定义形参,所以在调用方法时也要传入形参
    61. return; //这里加return干掉登录方法,结束当前login登录方法
    62. }else {
    63. System.out.println("你输入的密码有误");
    64. }
    65. }else {
    66. System.out.println("对不起,系统中不存在该账户卡号~~"); // 应该定义一个死循环,如果用户输入错误即让用户一直输入卡号
    67. }
    68. }
    69. }
    70. /**
    71. * 展示登录后的操作页
    72. */
    73. private static void showUserCommand(Scanner sc,Account acc,ArrayList<Account> accounts) {// 这样子就接到了全部账户的集合
    74. while (true) {
    75. System.out.println("=====================用户操作页=========================");
    76. System.out.println("1.查询账户");
    77. System.out.println("2.存款");
    78. System.out.println("3.取款");
    79. System.out.println("4.转账");
    80. System.out.println("5.修改密码");
    81. System.out.println("6.退出");
    82. System.out.println("7.注销账户");
    83. System.out.println("请选择");
    84. int command = sc.nextInt();
    85. switch (command) {
    86. case 1:
    87. // 查询账户(展示当前登录的账户信息) // 让用户传参,将用户对象传下来
    88. showAccount(acc); // 展示当前用户的方法, 将acc作为实参传进去
    89. break;
    90. case 2:
    91. // 存款
    92. depositMoney(acc,sc);// 调用存钱方法(depositMoney)首先ctrl + enter定义一个存钱方法里面传入账户对象
    93. break;
    94. case 3:
    95. // 取款
    96. drawMoney(acc,sc);
    97. break;
    98. case 4:
    99. // 转账
    100. // (方法的参数是送下来的)
    101. transferMoney(sc,acc,accounts); // 由于上面showUserCommand方法有accounts形参,所以这里可以当transferMoney实参
    102. break;
    103. case 5:
    104. // 修改密码
    105. updatePassWord(sc,acc);
    106. // break; // 这里就不要用break了,用break是退出switch,应该加return干掉当前方法
    107. return; // 让当前方法停止执行,跳出去
    108. case 6:
    109. // 退出
    110. System.out.println("退出成功,欢迎下次光临");
    111. return; // 用return方法,跳出当前方法
    112. case 7:
    113. // 注销账户
    114. // 这里的销户功能做完了就return结束当前方法,回到系统登录操作页面
    115. if (deleteAccount(acc,sc,accounts)){
    116. return; // 如果方法返回值为true,则退出方法,代表销户成功
    117. }else {
    118. break; // 如果方法返回值为false,代表销户失败,退出当前switch分支,回到当前操作页(有死循环)
    119. }
    120. // 定义一个默认情况,就是当上面所有的case都不满足时:就执行default语句
    121. default:
    122. System.out.println("你输入的命令有误,请您重新输入"); // 这个时候要定义一个死循环如果用户输入错误,让用户一直输入
    123. }
    124. }
    125. }
    126. /**
    127. * 销户功能
    128. * @param acc 当前账户
    129. * @param sc 扫描器
    130. * @param accounts // 全部账户的集合
    131. * 该方法定义为boolean类型的返回值
    132. */
    133. private static boolean deleteAccount(Account acc, Scanner sc, ArrayList<Account> accounts) {
    134. System.out.println("===============用户销户=====================");
    135. System.out.println("您真的要销户吗?y/n");
    136. String rs = sc.next(); // rs这里得到的是个值,值 匹配做分支的时候要用switch,这是写代码的规范
    137. switch (rs){
    138. case "y": // 由于rs的值是字符串类型,所以这里要写双引号
    139. // 真正的销户
    140. // 从当前账户集合中,删除当前账户对象,销毁就完成了
    141. if (acc.getMoney() > 0){
    142. System.out.println("您的账户中还有钱没有取完,不允许销户~");
    143. }else {
    144. accounts.remove(acc); // remove方法是集合的方法,用于去除集合中的元素对象(实际是对象的地址)
    145. System.out.println("你的账户销户完成~~");
    146. return true; // 销户成功返回true
    147. }
    148. break;// 销户完成,跳出switch分支
    149. default: // 如果不是case ”y“ 就走default
    150. System.out.println("好的,当前账户继续保留");
    151. }
    152. return false; // 销户失败就return false
    153. }
    154. /**
    155. * 修改密码
    156. * @param sc 扫描器
    157. * @param acc 当前登录成功的账户对象
    158. */
    159. private static void updatePassWord(Scanner sc, Account acc) {
    160. while (true) {
    161. System.out.println("===============用户密码修改====================");
    162. System.out.println("请您输入当前密码:");
    163. String passWord = sc.next();
    164. // 1. 判断这个密码是否正确
    165. if (acc.getPassword().equals(passWord)){
    166. while (true) {
    167. // 密码正确
    168. // 2.输入新密码
    169. System.out.println("请输入新密码");
    170. String newPassWord = sc.next();
    171. // 让用户确认密码
    172. System.out.println("请您确认新密码");
    173. String okPassWrod = sc.next();
    174. if (newPassWord.equals(okPassWrod)){
    175. // 两次密码一致,可以修改了
    176. acc.setPassword(newPassWord); // 将新密码传入进入,就修改了密码
    177. System.out.println("恭喜你,你的密码修改成功了");
    178. return; // 密码修改完成,干掉当前方法
    179. }else {
    180. System.out.println("您输入的两次密码不一致"); // 不一致又要定义死循环让用户重新输入
    181. }
    182. }
    183. }else {
    184. System.out.println("您输入的密码不正确"); // 像出现这种情况就是要定义死循环让用户一直输入
    185. }
    186. }
    187. }
    188. /**
    189. * 转账功能
    190. * @param sc 扫描器
    191. * @param acc 自己的账户对象
    192. * @param accounts 全部账户的集合
    193. */
    194. private static void transferMoney(Scanner sc, Account acc, ArrayList<Account> accounts) {
    195. System.out.println("================用户转账操作====================");
    196. // 1.判断是否足够2个账户
    197. if (accounts.size() < 2){ // accounts.size()是查看账户的长度从1开始 索引是0开始get的索引
    198. System.out.println("当前系统中,不足两个账户,不能进行转账,请去开户去吧~~");
    199. return; // 结束当前方法
    200. }
    201. while (true) {
    202. // 3. 真正开始转账
    203. System.out.println("请您输入对方账户的卡号:");
    204. String cardId = sc.next(); // 这里自动定义变量,可以直接按ctrl + alt + v 也可以按alt + enter
    205. // 判断这个卡号是否是自己的卡号,如果是自己的卡号,就跳过当前循环,死循环进入下一次
    206. if (cardId.equals(acc.getCardId())){
    207. System.out.println("对不起,您不可以给自己进行转账");
    208. continue; // 结束当次执行,死循环进入下一次, 如果是break,死循环就结束,是continue的话,死循环进入下一次
    209. }
    210. // 判断这个卡号是否存在,根据这个卡号去查询对方账户对象
    211. Account account = getAccountByCardId(cardId, accounts);//调用之前定义的查询账户方法,
    212. if (account == null){
    213. System.out.println("对不起,你输入的这个账号不存在"); // 一般不存在,还要查,就要定义死循环,让用户一直输入
    214. }else {
    215. // 这个账户存在了:继续认证他的姓氏
    216. String userName = account.getUserName(); // 黑马周芷若
    217. // 这里可以用截取函数(String类的),将索引为1的元素后面都截取下来,再加上一个" * "
    218. String tip = "*" + userName.substring(1);
    219. System.out.println("请您输入[" + tip + "]的姓氏");
    220. String preName = sc.next();
    221. // 认证姓氏是否输入正确
    222. if (userName.startsWith(preName)){ // startsWith是查看是否包含该字符串
    223. while (true) {
    224. // 如果包含,就认证通过,真正开始转账
    225. System.out.println("请您输入转账金额:");
    226. double money = sc.nextDouble();
    227. // 判断余额是否足够
    228. if (money > acc.getMoney()){ // 如果转账额度大于自己的余额,就提示余额不足
    229. System.out.println("对不起,您的余额不足,您最多可转账" + acc.getMoney()); // 像这种出现不足的情况,要多次输入,要定义死循环了
    230. }else {
    231. // 余额足够,可以转账
    232. // 更新自己对象的余额
    233. acc.setMoney(acc.getMoney() - money); // acc是自己账户的对象,account是对方账户的对象
    234. // 刷新对方账户的额度
    235. account.setMoney(account.getMoney() + money); //
    236. System.out.println("转账成功,您的账户还剩余:" + acc.getMoney());
    237. return; // 结束该方法
    238. }
    239. }
    240. }else {
    241. // 姓氏输入错误
    242. System.out.println("对不起,您输入的信息有误");
    243. }
    244. }
    245. }
    246. }
    247. /**
    248. * 取钱功能
    249. * @param acc 当前账户对象
    250. * @param sc 扫描器
    251. */
    252. private static void drawMoney(Account acc, Scanner sc) {
    253. System.out.println("===============用户取钱操作===================");
    254. // 1. 判断是否足够100元
    255. if (acc.getMoney() < 100){
    256. System.out.println("对不起,当前账户中不够100元,不能取钱");
    257. return; // 卫语言风格(像个卫士一样结束此方法)
    258. }
    259. while (true) {
    260. // 2.提示用户输入取钱金额
    261. System.out.println("请您输入取款金额:");
    262. double money = sc.nextDouble();
    263. // 判断这个金额是否满足要求
    264. if (money > acc.getQuotaMoney()){ // 如果取的钱大于限额quotaMoney则不能取钱
    265. System.out.println("对不起,您当前取款金额超过每次限额,每次最多可取:" + acc.getQuotaMoney());
    266. // 如果这里用户超过了限额,但是非要取走这个钱,可以定义死循环,一直执行这个取款操作
    267. }else {
    268. // 如果没有超过当前余额
    269. if (money > acc.getMoney()){ // acc.getMoney是当前对象的余额, money是取得钱
    270. System.out.println("余额不足,你的余额是:" + acc.getMoney());
    271. }else {
    272. // 没有超过当前余额
    273. System.out.println("恭喜你,取钱成功,你取款得金额是:" + money + "元,成功!");
    274. // 更新余额
    275. acc.setMoney(acc.getMoney() - money); // acc.setMoney是往对象得成员赋值 这里用来更新数据
    276. // 取钱结束后,将最新得余额展示给用户看(调用showAccount方法)
    277. showAccount(acc);
    278. return; // 取钱结束后,结束方法
    279. }
    280. }
    281. }
    282. }
    283. private static void depositMoney(Account acc, Scanner sc) {
    284. System.out.println("================用户存钱操作===============");
    285. System.out.println("请输入你的存款金额");
    286. double money = sc.nextDouble();
    287. // 最后总额度为: 账户本身的money(getMoney) + 存入的钱 (money)
    288. acc.setMoney(acc.getMoney() + money); // 这样已经修改了acc用户对象的值(集合中的对象也跟着修改了,因为集合存储的是对象的地址)
    289. // accounts.add(acc); //不要进行这样的操作,你操作对象,集合中存储该对象也跟着改变,因为存储的是地址
    290. showAccount(acc); //将更新后的对象,传入到展示用户信息的方法中
    291. }
    292. /**
    293. * 展示账户信息
    294. * @param acc
    295. */
    296. private static void showAccount(Account acc) {
    297. System.out.println("===================当前账户的信息如下=================");
    298. System.out.println("卡号:" + acc.getCardId());
    299. System.out.println("户主:" + acc.getUserName());
    300. System.out.println("余额:" + acc.getMoney());
    301. System.out.println("限额:" + acc.getQuotaMoney());
    302. }
    303. // 该方法是private方法,只能在本类中使用(在这里,使用private也没关系,就在这个ATMSystem类访问
    304. /**
    305. *
    306. * @param accounts
    307. * @param sc // 由于下面要用到ScannerAPI,但是在新方法中创对象占内存,所以定义方法时,设置Scanner类 的传参即可使用该类的方法
    308. */
    309. private static void register(ArrayList<Account> accounts,Scanner sc) {
    310. System.out.println("===================系统开户操作=======================");
    311. // 1.创建一个账户对象,用于后期封装账户信息
    312. Account account = new Account();
    313. // 2.录入当前这个账户的信息,注入到账户对象中去
    314. System.out.println("请输入账户用户名:");
    315. String userName = sc.next();
    316. account.setUserName(userName); // 使用自己定义的Account类的方法
    317. while (true) {
    318. System.out.println("请输入你的密码:");
    319. String passWord = sc.next();
    320. System.out.println("请输入确认密码:");
    321. String okPassword = sc.next();
    322. if (passWord.equals(okPassword)){
    323. // 密码认证通过,可以注入给账户对象
    324. account.setPassword(okPassword);
    325. break; // 每次定义死循环一定要结束它,要不然后面写不了语句会报错Unreachable statement 无法到达的语句
    326. }else {
    327. System.out.println("对不起你输入的2次密码不一致,请重新确认"); // 为了让用户输入正确,将要循环的部分加入while死循环
    328. }
    329. }
    330. System.out.println("请你输入账户当次的限额"); // 死循环后面一定要写break要不然,这里会报错Unreachable statement
    331. double quotaMoney = sc.nextDouble(); // .nextDouble输入的是浮点型
    332. account.setQuotaMoney(quotaMoney);
    333. // 为账户随机一个8为且与其他账户的卡号不重复的号码,(独立功能,独立成方法)
    334. String cardId = getRandomCardid(accounts);//getRandomCardid是自己创建的方法
    335. account.setCardId(cardId); // 将新生成的cardID(不重复的) 传入这个account类中
    336. // 3.把账户对象添加到账户集合中去
    337. accounts.add(account);
    338. System.out.println("恭喜你," + userName + "先生/女士,您开户成功,您的卡号是:" + cardId + ",请您妥善保管卡号");
    339. }
    340. private static String getRandomCardid(ArrayList<Account> accounts) { // 为了避免不重复,可以将集合容器传入进形参
    341. Random r = new Random();
    342. while (true) {
    343. // 1.先生成8位数字
    344. String cardId = ""; // 定义一个容器,将每次遍历后的随机数字存在这个变量中
    345. // Random r = new Random(); // 定义了死循环后,这个随机数的对象就可以放到外面去,避免占用堆内存
    346. for (int i = 0; i < 8; i++) {
    347. cardId += r.nextInt(10);// 定义随机数的范围是0-9
    348. }
    349. // 2.判断这个这个8位的卡号是否与其他账户的卡号重复
    350. // 根据整个卡号去查询账户的对象
    351. Account acc = getAccountByCardId(cardId, accounts); // 这里的cardId是传入随机生成的
    352. if (acc == null){ // 如果为null则代表这个卡号没有重复,这个卡号是一个新卡号,可以使用这个卡号作为新注册账户的卡号
    353. return cardId; // 如果没有acc != null代表,卡号重复了,要使用死循环,再次生成新的卡号,再搜索
    354. }
    355. }
    356. }
    357. /**
    358. * 根据卡号查询出一个账户对象出来
    359. * cardId 卡号
    360. * accounts 全部账户的集合
    361. * 账户对象 | null
    362. */
    363. private static Account getAccountByCardId(String cardId,ArrayList<Account> accounts){ // 这里的cardID是自己定义的形参
    364. for (int i = 0; i < accounts.size(); i++) {
    365. Account acc = accounts.get(i); // acc变量是用来存储集合中遍历出来的对象, 遍历出来的对象都可以使用account对象的方法get
    366. if (acc.getCardId().equals(cardId)){
    367. // 如果传入的卡号cardId与账户类的卡号一样,则返回该账户
    368. return acc;
    369. }
    370. }
    371. // 当遍历整个集合中的对象的卡号都不一样是:代表没有该卡号
    372. return null; // 查无此账户
    373. }
    374. }
    1. package com.itheima.ATM;
    2. /**
    3. * 账户类
    4. */
    5. public class Account {
    6. /**
    7. * 成员变量,私有
    8. */
    9. private String cardId;
    10. private String userName; // 用户名
    11. private String password; // 密码
    12. private double money; // 账户余额
    13. private double quotaMoney; // 每次提取的额度 // quota是定额的意思
    14. public String getCardId() {
    15. return cardId;
    16. }
    17. public void setCardId(String cardId) {
    18. this.cardId = cardId;
    19. }
    20. public String getUserName() {
    21. return userName;
    22. }
    23. public void setUserName(String userName) {
    24. this.userName = userName;
    25. }
    26. public String getPassword() {
    27. return password;
    28. }
    29. public void setPassword(String password) {
    30. this.password = password;
    31. }
    32. public double getMoney() {
    33. return money;
    34. }
    35. public void setMoney(double money) {
    36. this.money = money;
    37. }
    38. public double getQuotaMoney() {
    39. return quotaMoney;
    40. }
    41. public void setQuotaMoney(double quotaMoney) {
    42. this.quotaMoney = quotaMoney;
    43. }
    44. }