JAVA钱包项目
能够在首页面展示
当输入1的时候,展示收支记录
如果有收支记录,则展示交易记录,回到首页面
如果没有收支记录,提示,无交易记录。直接回到首页面
当输入2的时候,展示收入记录,如下:
收入:
收入说明:
收入金额:
当输入3的时候,展示支出记录
当输入4的时候退出系统
首先看到这个题,我们要先了解它的需求
第一,它的首页面,我们能看到它有重复的元素,所以说可以先声明一个常量BANNER用来输出星号
第二,它的中间的内容,是居中显示的,用\t空格符添加到输出的内容中
观察四种情况可以发现每一种情况都会触发回到首页面,也就意味着首页面会重复出现,
也就是说我们要把打印主页面写到一个循环里,这里我们使用while循环(此处使用的是do-while),
但是有的情况也不会出现,所以说要声明个变量,用来判断是否进行输出主页面。
在四种情况下,可以改变flag的值,如果一种情况的最后需要出现这个界面,就让flag=true,
当执行到while(true)的时候,就会输出这个界面并且会让你在选择,即又执行了一次
package study;
public class Tstudy {
static final String BANNER="*******************************************";
public static void main(String[] args){
boolean flag=true;
do{
System.out.println(BANNER);
System.out.println("\t\t1.收支记录");
System.out.println("\t\t2.收入");
System.out.println("\t\t3.支出");
System.out.println("\t\t4.退出系统");
System.out.println(BANNER);
}while(flag);
}
}
第三,可以看到要输入字符进行选择,并且有四种输入情况,这种时候使用switch-case语句是最好的
这里需要注意!因为不确定会输入什么类型数据,但是作为开发者要考虑到多种情况,不能使其报错。
所以当我们定义用户输入的数字的时候我们定义成String类型,对应的就是next()方法
import java.util.Scanner;
static Scanner sc=new Scanner(System.in);//定义成static变量后面的每个方法都可以调用
System.out.print("请选择:");
String input=sc.next();
switch(input){
case "1":
break;
case "2":
break;
case "3":
break;
case "4":
break;
}
第四,接下来我们考虑每一种情况
首先情况一,我们要展示收支情况,此时需要定义一个布尔型的标志flag2,用来判断是否有交易记录,当有交易记录时我们输出我们的信息,先打印列名栏,然后输出我们交易记录,如果没有交易记录就返回主页面,此时就可以让用户输入是否回到主页面y/n,然后如果是y那就是说这个收支情况画面可以停止展示,即flag2=false
关于如何输出我们的交易记录信息:
一 .首先我们把每一条记录看作一个对象,就要定义一个类BankMsge,类的变量有余额,类型,金额和说明,以及get和set方法,还有构造方法。
class BankMsge{
double sal;
String ty;
double money;
String note;
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public String getTy() {
return ty;
}
public void setTy(String ty) {
this.ty = ty;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public BankMsgee(double sal,String ty,double money,String note){
this.sal = sal;
this.ty = ty;
this.money = money;
this.note = note;
}
二 .然后我们要创建两个BankMsge对象,就是两条记录,将两个对象写入集合,这里使用泛型,BankMsge类型的集合List,由于后面我们要把新建的对象数据添加到集合中,所以需要使用添加方法,此时使用ArrayList<>()构造方法;
static List<BankMsge> bmList=new ArrayList<>();
static {
bmList.add(new BankMsg(1300,"收入",300,"卖茄子"));
bmList.add(new BankMsg(1100,"支出",200,"买茄子"));
}
知识补充
1.asList()方法:
在Java中,Arrays
类提供了一个 asList()
方法,这个方法可以将一个数组转换成一个固定大小的列表。这个方法非常有用,因为它可以让你轻松地将数组中的元素转换为列表,而不需要创建一个新的数组或者手动填充列表。
asList()
方法的签名如下:
static <T> List<T> asList(T... a)
这里,T...
表示一个可变参数,可以是任何类型的数组。方法会返回一个 List
对象,这个列表包含了数组中的所有元素,而且列表的大小不会随着数组内容的改变而改变。这意味着如果你修改了原数组,转换得到的列表不会自动更新。
下面是一个 asList()
方法的例子:
import java.util.Arrays;
import java.util.List;
public class AsListExample {
public static void main(String[] args) {
// 创建一个整数数组
Integer[] numbers = {1, 2, 3, 4, 5};
// 使用 asList() 方法将数组转换为列表
List<Integer> numberList = Arrays.asList(numbers);
// 输出列表内容
System.out.println(numberList); // 输出:[1, 2, 3, 4, 5]
// 修改原数组的一个元素
numbers[0] = 10;
// 输出修改后的列表内容
System.out.println(numberList); // 输出:[10, 2, 3, 4, 5]
// 尝试修改列表的一个元素(不会改变原数组)
numberList.set(1, 20);
// 输出修改后的列表内容
System.out.println(Arrays.toString(numbers)); // 输出:[10, 2, 3, 4, 5]
System.out.println(numberList); // 输出:[10, 20, 3, 4, 5]
}
}
在这个例子中,我们首先创建了一个整数数组 numbers
。然后,我们使用 Arrays.asList()
方法将这个数组转换为一个列表 numberList
。即使我们后来修改了原数组 numbers
的元素,列表 numberList
的内容也并不会改变,因为它只是一个原数组的视图。但是,如果我们通过列表 numberList
修改某个元素,这个改变会反映到原数组 numbers
中,因为它们是相互关联的。
请注意,由于 asList()
方法返回的是一个固定大小的列表,所以如果你尝试添加或删除列表中的元素,将会导致 UnsupportedOperationException
。如果你需要一个可以动态增长的列表,你应该使用 Arrays.stream(T[] array).collect(Collectors.toList())
或者创建一个新的 ArrayList
实例。
2.ArrayList和List的区别
ArrayList
和 List
之间的关系可能会有些混淆,因为 ArrayList
是一个实现了 List
接口的类。下面是它们之间的区别:
- ArrayList:
ArrayList
是List
接口的一个具体实现。- 它基于动态数组的数据结构,这意味着它可以适应元素数量的增减而自动调整其容量。
ArrayList
提供了List
接口中定义的所有方法,包括添加、删除、获取和排序等操作。ArrayList
允许包含重复的元素和null
值。- 它不是线程安全的,如果需要在多线程环境中使用,需要外部同步或使用
CopyOnWriteArrayList
。
- List:
List
是一个接口,定义了顺序集合的一系列操作,如add
,remove
,get
,size
等。add(E e): 向列表中添加指定的元素。remove(Object o): 从列表中删除首次出现的指定元素。get(int index): 返回列表中指定位置的元素。size(): 返回列表中的元素数量。
请注意,虽然 ArrayList 实现了 List 接口,但它本身是一个类,可以被其他类继承或者作为父类来使用List
接口是一个泛型接口,意味着任何实现List
接口的类都可以使用类型参数来指定集合中元素的类型。List
接口由多个类实现,ArrayList
是其中最常见的一个。其他实现包括LinkedList
(双向链表),Vector
(线程安全的ArrayList
),Stack
(继承自Vector
的栈实现)等。- 任何实现了
List
接口的类都必须实现接口中定义的所有方法。
简而言之,ArrayList
是一个具体的List
实现,而List
是一个接口,定义了ArrayList
以及其他可能的实现类应该遵循的规范。在Java中,当你需要一个列表时,你通常会直接使用ArrayList
,因为它是List
接口的一个非常高效和常用的实现。
3.泛型
泛型(Generics)是Java编程语言的一个特性,它允许在代码中使用类型参数,这些类型参数可以在程序运行时替换为具体的类或接口类型。泛型的主要目的是提供编译时类型安全检查,同时允许代码在不同类型上重用。
在使用泛型之前,如果你想在程序中使用一个可以处理多种类型的数据结构的算法,你通常需要为每种类型编写相似的代码。例如,如果你想写一个可以处理任何对象列表的排序函数,你可能会遇到类型擦除的问题,因为Java在运行时无法识别泛型信息。这将导致在运行时类型转换错误,这些错误往往很难调试。
泛型的引入解决了这个问题。现在,你可以使用泛型来编写可重用的代码,这些代码可以在编译时检查类型,而不是在运行时。这样,你可以创建一个通用的类或方法,它能够使用具体的类型参数,而不是固定的数据类型。
下面是一个简单的泛型类的例子:
public class Box<T> {
private T t; // T 是一个类型参数
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
在这个例子中,Box
类是一个泛型类,它使用 <T>
作为类型参数。这个类型参数会在创建 Box
类的实例时被替换为具体的类型。例如,你可以创建一个 Box<Integer>
实例,也可以创建一个 Box<String>
实例。
使用泛型的好处包括:
- 类型安全:编译器会检查泛型代码的类型,确保类型匹配,减少了运行时错误。
- 代码重用:泛型代码可以用于多种数据类型,减少了重复代码。
- 更好的抽象:泛型允许你编写更抽象和通用的代码,更容易理解和维护。
泛型广泛应用于Java集合框架(Collection Framework)中,如List
,Set
,Map
等接口及其实现类,这些集合类可以使用泛型来存储和操作各种类型的数据。
三 .然后就是如何输出我们的对象。有四种方法:
(1)for i(此方法麻烦)
boolean flag2 =true;
while(flag2){
System.out.println("余额\t类型\t金额\t说明");
for(int i=0;i<bmList.size();i++){
System.out.println(bmList.get(i).getSal()+"\t"+
bmList.get(i).getTy()+"\t"+
bmList.get(i).getMoney()+"\t"+
bmList.get(i).getNote());
}
}
(2)foreach(所有利用迭代器迭代的元素都可以用for-each形式。)
@Override
public String toString() {
return sal+"\t"+ty+"\t"+money+"\t"+note;//使用句号连接
}
for(BankMsge msg:bmList){
System.out.println(msg);
}
如果只有后面这段代码打印出来是:包名 对象的唯一哈希码
关于toString()
在Java中,所有的类都默认继承自 java.lang.Object
类。Object
类中有一个 toString()
方法,它提供了一个默认的实现,即返回对象的字符串表示,这个字符串包括类名、@符号和对象的哈希码。
在Java中,使用 System.out.println() 方法打印一个对象时,默认情况下会输出该对象的 toString() 方法的返回值。toString() 方法是一个特殊的方法,它定义了对象的文本表示形式。
下面是 Object
类中 toString()
方法的具体代码:
public class Object {
// ... 其他方法 ...
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// ... 其他方法 ...
}
当你没有覆盖 toString()
方法时,你的类会使用这个默认实现。例如,如果你有一个 MyClass
类,它没有覆盖 toString()
方法,那么当你打印 MyClass
对象时,你会得到一个类似于这样的字符串:
MyClass@7f2c1547
这里 @7f2c1547
是对象的唯一哈希码,这个哈希码在对象创建时由JVM分配。
如果你想要自定义 toString()
方法的实现,你可以像这样覆盖它:
public class MyClass {
// ... 其他字段和方法 ...
@Override
public String toString() {
return "MyClass{" +
"value=" + value +
'}';
}
}
这样,当你打印 MyClass
对象时,你会得到一个自定义的文本表示,它提供了有关对象状态的有用信息。
(3)iterator(迭代器遍历(所有实现了Iterable接口的集合类))
Iterator msg=bmList.iterator();
while(msg.hasNext()){
System.out.println(msg.next());
}
(4)Stream(此方法只针对于jdk1.8及以上)
bmList.forEach(msg -> {
System.out.println(msg);
});
四 .最后询问用户是否回到主页面
System.out.print("是否回到主页面(y/n)");
String i=sc.next();
if(i.equals("y")||i.equals("Y")){
flag2=false;
}
五 .运行:
第二种情况收入,题干的意思是增加一条收入记录
System.out.println("收入:");
System.out.println("收入说明:");
String innote=sc.next();
System.out.println("收入金额");
double inmoney=sc.nextDouble();
double newsal=bmList.get(bmList.size()-1).getSal();
BankMsge b3=new BankMsge(inmoney+newsal,"收入",inmoney,innote);
bmList.add(b3);
System.out.println("添加收入记录成功");
第三种情况收入,题干的意思是增加一条支出记录
首先做减法就要想到如果出现负数就是支出金额过大是不可以支出的,所以是需要判断的。
判读语句之后是需要添加break;用来终止整个程序运行,不然还是会进行后面的计算。
System.out.println("支出说明:");
String outnote=sc.next();
System.out.println("支出金额");
double outmoney=sc.nextDouble();
double nnewsal=bmList.get(bmList.size()-1).getSal();
if (outmoney>nnewsal){
System.out.println("支出金额过大,无法支出");
break;
}
//因为double类型的值在做减法运算的时候会发生精度丢失问题,此处解决该问题
double v=BigDecimal.valueOf(nnewsal).subtract(BigDecimal.valueOf(outmoney)).doubleValue();
BankMsge b4=new BankMsge(v,"支出",outmoney,outnote);
bmList.add(b4);
System.out.println("添加支出记录成功");
这里需要注意!double类型的数据做减法会出现精度丢失,如下:
这里我们要使用BigDecimal的valueOf的方法;
减法可以使用subtract( BigDecimal.valueOf(5.66))方法;
再使用doublevalue()方法。
补充
doubleValue()
方法通常存在于Java语言中的Double
类中。这个方法用于获取一个Double
对象的数值。在Java中,Double
类是double
原始数据类型的包装类,用于提供诸如数值转换、大小比较等额外的方法。
下面是doubleValue()
方法的基本使用方法:
Double myDouble = 10.5; // 创建一个Double对象,包含数值10.5
double value = myDouble.doubleValue(); // 获取Double对象的数值
在上面的代码中,doubleValue()
方法被调用来获取myDouble
对象的内部double
值。
此外,如果你直接操作double
原始数据类型,就不需要使用doubleValue()
方法,可以直接使用数值:
double value = 10.5; // 直接使用double原始类型
请注意,在某些情况下,如果你已经有了一个String
对象,你可能需要使用Double.parseDouble(String s)
方法来将字符串转换为double
值,而不是使用doubleValue()
方法,因为doubleValue()
是针对Double
对象设计的。
String doubleAsString = "10.5";
double value = Double.parseDouble(doubleAsString); // 将字符串转换为double值
在使用这些方法时,确保输入合法,以避免NumberFormatException
。
情况四就是退出系统,此时不再显示主页面,即flag标志为false
flag=false;
System.out.println("已退出");
完整代码
package study;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
public class Tstudy {
static final String BANNER="*******************************************";
static Scanner sc=new Scanner(System.in);
static List<BankMsge> bmList=new ArrayList<>();
static {
bmList.add(new BankMsge(1300,"收入",300,"卖茄子"));
bmList.add(new BankMsge(1100,"支出",200,"买茄子"));
}
public static void main(String[] args){
boolean flag=true;
do{
System.out.println(BANNER);
System.out.println("\t\t1.收支记录");
System.out.println("\t\t2.收入");
System.out.println("\t\t3.支出");
System.out.println("\t\t4.退出系统");
System.out.println(BANNER);
System.out.print("请选择:");
String input=sc.next();
switch(input){
case "1":
boolean flag2 =true;
while(flag2){
System.out.println("余额\t类型\t金额\t说明");
//stream
bmList.forEach(msg -> {
System.out.println(msg);
});
//iterator
// Iterator msg=bmList.iterator();
// while(msg.hasNext()){
// System.out.println(msg.next());
// }
//foreach
// for(BankMsge msg:bmList){
// System.out.println(msg);
// }
//for i
// for(int i=0;i<bmList.size();i++){
// System.out.println(bmList.get(i).getSal()+"\t"+
// bmList.get(i).getTy()+"\t"+
// bmList.get(i).getMoney()+"\t"+
// bmList.get(i).getNote());
// }
System.out.print("是否回到主页面(y/n)");
String i=sc.next();
if(i.equals("y")||i.equals("Y")){
flag2=false;
}
}
break;
case "2":
System.out.println("收入:");
System.out.println("收入说明:");
String innote=sc.next();
System.out.println("收入金额");
double inmoney=sc.nextDouble();
double newsal=bmList.get(bmList.size()-1).getSal();
BankMsge b3=new BankMsge(inmoney+newsal,"收入",inmoney,innote);
bmList.add(b3);
System.out.println("添加收入记录成功");
break;
case "3":
System.out.println("支出:");
System.out.println("支出说明:");
String outnote=sc.next();
System.out.println("支出金额");
double outmoney=sc.nextDouble();
double nnewsal=bmList.get(bmList.size()-1).getSal();
if (outmoney>nnewsal){
System.out.println("支出金额过大,无法支出");
break;
}
//因为double类型的值在做减法运算的时候会发生精度丢失问题,此处解决该问题
double v=BigDecimal.valueOf(nnewsal).subtract(BigDecimal.valueOf(outmoney)).doubleValue();
BankMsge b4=new BankMsge(v,"支出",outmoney,outnote);
bmList.add(b4);
System.out.println("添加支出记录成功");
break;
case "4":
flag=false;
System.out.println("已退出");
break;
}
}while(flag);
}
}
class BankMsge{
@Override
public String toString() {
return sal+"\t"+ty+"\t"+money+"\t"+note;//使用句号连接
}
double sal;
String ty;
double money;
String note;
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public String getTy() {
return ty;
}
public void setTy(String ty) {
this.ty = ty;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public BankMsge(double sal,String ty,double money,String note){
this.sal = sal;
this.ty = ty;
this.money = money;
this.note = note;
}
}
写在最后:
现在已经是第二天的凌晨一点半了,刚整理完今天的试听培训题目,感觉真的之前复习的java’基础因为这个题目而变得生动起来,好像突然get到了,这就是实战的魅力吗?最神奇的是,我觉得真的一切都妙不可言,从最开始震惊的1v1上课,到之后聊到篮球,知识最开始一个不听白不听的想法,让我遇见了一个真心想教学生技术的老师,虽然可能没有机会继续一起工作学习,但是只要爱篮球就是朋友,感谢所有吧!