三个Java入门项目
Java实现简单计算器
参考链接🔗:https://www.bilibili.com/video/BV1d54y1s7uC?p=1&vd_source=cf21268954e139179e71f046bac01e56
- 设计思路
- 创建容器框架
- 创建组件和组件布局方式
- 组件的测试
- 数字按钮和功能按钮的实现
- 设置事件监视器
- 运算功能和清空功能的实现
-
代码
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class Calculator extends JFrame implements ActionListener { private JPanel jp_north = new JPanel(); //北面面板 private JTextField input_text = new JTextField(); //输入框 private JButton c_button = new JButton("C"); //清除按键 private JPanel jp_south = new JPanel(); //南面面板 public Calculator() throws HeadlessException{ this.init(); this.addNorthComponent(); this.addSouthButton(); } //初始化窗口 public void init() { this.setTitle("计算器"); this.setSize(300, 300); this.setLayout(new BorderLayout()); this.setResizable(false); //设置窗口不可拉伸 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLocationRelativeTo(null); //使窗口打开时默认在屏幕中央 } //添加北面的控件 public void addNorthComponent() { this.input_text.setPreferredSize(new Dimension(230, 30)); jp_north.add(input_text); this.c_button.setForeground(Color.RED); this.c_button.setBackground(Color.ORANGE); jp_north.add(c_button); c_button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { input_text.setText(""); firstInput = null; } }); this.add(jp_north, BorderLayout.NORTH); } //添加南面的按钮 public void addSouthButton() { String button_text = "123+456-789x.0=÷"; this.jp_south.setLayout(new GridLayout(4, 4)); for (int i = 0; i < 16; i++) { String temp = button_text.substring(i, i + 1); JButton button = new JButton(temp); if (temp.equals("=")) { button.setBackground(Color.CYAN); } else { button.setBackground(Color.white); } if (temp.matches("[\\+\\-x÷=]")) { button.setFont(new Font("粗体", Font.BOLD, 16)); } button.addActionListener(this::actionPerformed); jp_south.add(button); } this.add(jp_south, BorderLayout.CENTER); } public static void main(String[] args) { Calculator calculator = new Calculator(); calculator.setVisible(true); } private String firstInput = null; private String secondInput = null; private String operator = null; private boolean calculated = false; @Override public void actionPerformed(ActionEvent e) { String clickStr = e.getActionCommand(); if (calculated) { this.input_text.setText(""); calculated = false; } if (".0123456789".indexOf(clickStr) != -1) { if (firstInput != null) { if (secondInput == null) { secondInput = clickStr; } else { secondInput += clickStr; } } this.input_text.setText(input_text.getText() + clickStr); this.input_text.setHorizontalAlignment(JTextField.RIGHT); //JOptionPane.showMessageDialog(this, clickStr); } else if (clickStr.matches("[\\+\\-x÷]{1}")) { operator = clickStr; secondInput = null; firstInput = this.input_text.getText(); this.input_text.setText(input_text.getText() + clickStr); } else if (clickStr.equals("=")) { Double a = Double.valueOf(firstInput); Double b = Double.valueOf(secondInput); Double result = null; calculated = true; switch (operator) { case "+": result = a + b; break; case "-": result = a - b; break; case "x": result = a * b; break; case "÷": if (b != 0) { result = a / b; } break; } this.input_text.setText(result.toString()); } } }
-
运行结果
Eclipse 实现 Java 编辑器
参考链接🔗:https://blog.csdn.net/qq_52354698/article/details/127657550
https://blog.csdn.net/qq_52354698/article/details/127657836
- Eclipse
Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。
Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。
- 设计思路
- 新建项目
- 创建包和类
- GUI 界面的实现
- 测试类的实现
- actionPerformed 方法的实现
- run 方法的实现
- 简单测试
- 实现步骤
在Eclipse中创建一个Java project
创建包和类
包:com.java.myedit
类:FileWindow(主要方法类,用作GUI界面以及逻辑功能的实现)和Main(测试类)
代码
FileWindow.java
package com.java.myedit; import java.awt.CardLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; public class FileWindow extends JFrame implements ActionListener, Runnable { private static final long serialVersionUID = 1L; Thread compiler = null; Thread run_prom = null; boolean bn = true; CardLayout mycard = new CardLayout(); //声明布局 File file_saved = null; JButton button_input_txt=new JButton("程序输入区(白色)"), //按钮的定义 button_compiler_text=new JButton("编译结果区(粉红色)"), button_see_doswin=new JButton("程序运行结果(浅蓝色)"), button_compiler=new JButton("编译程序"), button_run_prom=new JButton("运行程序"); JPanel panel = new JPanel(); //下方显示区 JTextArea input_text = new JTextArea(); // 程序输入区 JTextArea compiler_text = new JTextArea();// 编译错误显示区 JTextArea dos_out_text = new JTextArea();// 程序的输出信息 JTextField input_file_name_text = new JTextField(); JTextField run_file_name_text = new JTextField(); public FileWindow() { super("Java语言编译器"); compiler=new Thread(this); run_prom=new Thread(this); panel.setLayout(mycard);//设置卡片布局 panel.add("input",input_text);//定义卡片名称 panel.add("compiler", compiler_text); panel.add("dos",dos_out_text); add(panel,"Center"); compiler_text.setBackground(Color.pink); //设置颜色 dos_out_text.setBackground(Color.cyan); JPanel panel_north=new JPanel(); panel_north.setLayout(new GridLayout(3, 3)); //设置表格布局 //添加组件 panel_north.add(new JLabel("输入编译文件名(.java):")); panel_north.add(input_file_name_text); panel_north.add(button_compiler); panel_north.add(new JLabel("输入应用程序主类名:")); panel_north.add(run_file_name_text); panel_north.add(button_run_prom); panel_north.add(button_input_txt); panel_north.add(button_compiler_text); panel_north.add(button_see_doswin); add(panel_north,"North"); //定义事件 button_input_txt.addActionListener(this); button_compiler.addActionListener(this); button_compiler_text.addActionListener(this); button_run_prom.addActionListener(this); button_see_doswin.addActionListener(this); } public void actionPerformed(ActionEvent e) { if(e.getSource()==button_input_txt) { //显示程序输入区 mycard.show(panel,"input"); } else if(e.getSource()==button_compiler_text) { //显示编译结果显示区 mycard.show(panel,"compiler"); } else if(e.getSource()==button_see_doswin) { //显示程序运行结果区 mycard.show(panel,"dos"); } else if(e.getSource()==button_compiler) { //如果是编译按钮,执行编译文件的方法 if(!(compiler.isAlive())) { compiler=new Thread(this); } try { compiler.start(); } catch (Exception e2) { e2.printStackTrace(); } mycard.show(panel,"compiler"); } else if(e.getSource()==button_run_prom) { //如果是运行按钮,执行运行文件的方法 if(!(run_prom.isAlive())) { run_prom=new Thread(this); } try { run_prom.start(); } catch (Exception e2) { e2.printStackTrace(); } mycard.show(panel,"dos"); } } @Override public void run() { if(Thread.currentThread()==compiler) { /*如果当前 Thread 是编译, * 那么会将程序输入区中的代码以.java 文件的形式 * 保存到项目的当前目录下, * 并通过javac命令执行刚才保存的.java 文件生成.class 文件, * 编译后的信息会输出到编译结果显示区。*/ compiler_text.setText(null); String temp=input_text.getText().trim(); // trim()方法用于删除字符串的头尾空白符 byte [] buffer=temp.getBytes(); int b=buffer.length; String file_name=input_file_name_text.getText().trim(); try { file_saved = new File(file_name); FileOutputStream writefile = new FileOutputStream(file_saved); writefile.write(buffer, 0, b); writefile.close(); } catch (Exception e) { System.out.println("ERROR"); } try { //获得该进程的错误流,才可以知道运行结果到底是失败了还是成功。 Runtime rt=Runtime.getRuntime(); InputStream in=rt.exec("javac "+file_name).getErrorStream(); //通过Runtime调用javac命令。注意:“javac ”这个字符串是有一个空格的!! BufferedInputStream bufIn=new BufferedInputStream(in); byte[] arr=new byte[100]; int n=0; boolean flag=true; //用于判断编译是否成功 //输入错误信息 while((n=bufIn.read(arr, 0,arr.length))!=-1) { String s=null; s=new String(arr,0,n); compiler_text.append(s); if(s!=null) { flag=false; } } //判断是否编译成功 if(flag) { compiler_text.append("Compile Succeed!"); } } catch (Exception e) { System.out.println("ERROR"); } }else if(Thread.currentThread()==run_prom){ /*如果当前 Thread 是运行, * 那么会通过java命令执行编译生成的.class 文件, * 并将程序结果显示到程序运行结果区中*/ dos_out_text.setText(null); try { Runtime rt=Runtime.getRuntime(); String path=run_file_name_text.getText().trim(); Process stream=rt.exec("java "+path);//调用java命令 InputStream in=stream.getInputStream(); BufferedInputStream bisErr=new BufferedInputStream(stream.getErrorStream()); BufferedInputStream bisIn=new BufferedInputStream(in); byte[] buf=new byte[150]; byte[] err_buf=new byte[150]; @SuppressWarnings("unused") int m=0; @SuppressWarnings("unused") int i=0; String s=null; String err=null; //打印编译信息及错误信息 while((m=bisIn.read(buf, 0, 150))!=-1) { s=new String(buf,0,150); dos_out_text.append(s); } while((i=bisErr.read(err_buf))!=-1) { err=new String(err_buf,0,150); dos_out_text.append(err); } }catch (Exception e) { System.out.println("ERROR"); } } } }
Main.java
package com.java.myedit; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class Main { public static void main(String[] args) { // TODO Auto-generated method stub FileWindow win=new FileWindow(); win.pack(); //调整框架的大小,使框架的内容等于或大于它们的首选大小 win.addWindowListener(new WindowAdapter() { //WindowAdapter是接收窗口事件的抽象适配器类 public void windowClosing(WindowEvent e) { System.exit(0); } }); //设置窗口大小 win.setBounds(200, 180,550,360); win.setVisible(true); } }
- 结果及测试
一本糊涂账
- 知识
- 面向对象,字符串数字,日期
- 异常,集合,JDBC,反射机制,I/O,Swing,利用TableModel更新数据,图形界面的皮肤
- 图表chart动态生成,数据库的备份与恢复,自定义圆形进度条
- 软件设计思想:单例模式,面板类与监听器类松耦合,Entity层设计,DAO层设计,Service层设计
- 业务常见处理手法:CRUD操作,配置信息,配置信息初始化,报表生成,一对多关系,多对一关系
- 实现目标
步骤1:消费一览
统计本月的消费总数,今日消费,日均消费,本月剩余,日均可用,距离月末有多少天。
同时使用一个环形进度条,这个环形进度条不是JDK自带的,需要自己设计,并且随着消费用度,颜色从绿色渐变为红色。
步骤2:记一笔
记录本日的消费额度, 分类下拉框从 消费分类数据中读取,并且把经常消费的分类放在前面。
日期默认选中今天,也可以手动指定日期。
步骤3:消费分类管理
对消费进行经典的CRUD 增删改查管理,同时显示一个分类下的消费次数。
这里涉及到多表关系:消费记录和消费分类是多对一关系
步骤4:月度消费报表
使用第三方chart类生成柱状报表,显示本月的消费趋势
步骤5:设置预算和数据库路径
在消费一览中需要显示本月可用多少金额,都是建立在预算的基础上的。
在设置页面,设置本月的预算金额。
后续的还原和备份,都需要用到数据库的命令mysql和mysqldump,需要在这里配置mysql的安装目录
步骤6:备份数据
把数据库中的所有数据,备份到.sql文件中
步骤7:恢复数据
根据.sql文件还原数据库
-
开发流程
- 表结构设计
-
数据库和表的创建
1)创建数据库
create database hutubill; use hutubill;
2)确定需要哪些表
根据业务上的需要,一共要3个表:
- 配置表信息 config
用于保存每月预算和Mysql的安装路径( 用于备份还原用) - 消费分类表 category
用于保存消费分类,比如餐饮,交通,住宿 - 消费记录表 record
用于存放每一笔的消费记录,并且会用到消费分类
3)配置信息表config
配置信息表 config有如下字段
id 主键,每个表都有一个主键 类型是 int
key_ 配置信息按照键值对的形式出现 ,类型是varchar(255)
value配置信息的值, 类型是 varchar(255)-
键值对
进一步解释一下键值对,比如要存放每个月的预算,则需要在config表中增加一条记录,key="budget" value="500",就表示预算是500. -
varchar(255) 表示变长字符,如果实际存放只有30个字符,那么在数据库中只占用30的空间,最多占用255
-
key 是关键字,不适合用于作为字段名,所以在key后面加了一个下划线 key_ 就不会有任何问题了,识别性一样很好,一眼就知道这个字段是干什么用的
-
ENGINE=InnoDB MySQL有多种存储引擎,MyISAM和InnoDB是其中常用的两种, 他们之间的区别很多。 这里使用ENGINE=InnoDB 是因为后续要使用的外键约束只有在InnoDB中才生效。
-
DEFAULT CHARSET=utf8; 表示该表按照UTF-8的编码存放中文
CREATE TABLE config ( id int , key_ varchar(255) , value varchar(255) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4)消费分类表 category
消费分类表 category 有如下字段:
id 主键,每个表都有一个主键 类型是 int
name分类的名称,类型是varchar(255)CREATE TABLE category ( id int, name varchar(255) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
5)消费记录表 record
消费记录表 record 有如下字段:
id 主键,每个表都有一个主键 类型是 int
spend 本次花费,类型是int
cid 对应的消费分类表的中记录的id, 类型是int
comment 备注,比如分类是娱乐,但是你希望记录更详细的内容,啪啪啪,那么就存放在这里。
date 日期,本次记录发生的时间。CREATE TABLE record ( id int, spend int, cid int, comment varchar(255) , date Date ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 配置表信息 config
-
表关系
分类表和记录表之间的关系是 一对多,
又叫ONE TO MANY 1:M确定好分类和记录之间的关系之后,就需要在数据库表结构中通过外键表现出来
外键是加在多表中的,也就是加在Record表中的,在这里就是cid
record (
id int,
spend int,
cid int,
comment varchar(255) ,
date Date
)
cid指向了category表的主键 id -
约束
1)主键
所有的表都有一个主键id,但是这只是语意上的(我们认为的),为了让数据库把id识别为主键,需要加上主键约束。
主键约束自带非空和唯一属性,即不能插入空,也不能重复。
对三张表都加上主键约束:
alter table category add constraint pk_category_id primary key (id); alter table record add constraint pk_record_id primary key (id); alter table config add constraint pk_config_id primary key (id);
alter table category 表示修改表category
add constraint 增加约束
pk_category_id 约束名称 pk 是primary key的缩写,category是表名, id表示约束加在id字段上。约束名称是可以自己定义的,你可以写成abc,但是尽量使用好的命名,使得一眼就能够看出来这个约束是什么意思。 能够降低维护成本。
primary key 约束类型是主键约束
(id) 表示约束加在id字段上2)设置id为自增长
设置id为自增长是常用的插入数据策略。 换句话说,插入消费分类数据的时候,只用提供分类名称即可,不需要自己提供id, id由数据库自己生成。
不同的数据库采用的自增长方式是不一样的,比如Oracle使用Sequence来实现,而MySQL就使用AUTO_INCREMENT来实现。alter table category change id id int auto_increment; alter table record change id id int auto_increment; alter table config change id id int auto_increment;
alter table category 表示修改表category
change id 表示修改字段 id
id int auto_increment; 修改后的id是 int类型,并且是auto_increment(修改之前仅仅是int类型,没有auto_increment)3)外键
外键约束的作用是保持数据的一致性(比如增加一条消费记录,金额是500,cid是5。但是cid=5在分类表category中找不到对应的数据,那么这就破坏了数据的一致性,会带来一系列的后续问题,比如根据分类进行统计,就会找不到这条数据。)
增加外键约束之前首先确定record表的外键是cid,指向了category表的id主键。
所以增加外键之前,必须把category的id字段设置为主键。从而保证cid=5的数据在category中只能找到一条,而不是找到多条。alter table record add constraint fk_record_category foreign key (cid) references category(id);
alter table record 修改表record
add constraint 增加约束
fk_record_category 约束名称fk_record_category,fk是foreign key的缩写,record_category表示是从record表指向category表的约束。 与主键一样,约束名称也是可以自己定义的,比如写成abc. 不过依然建议使用可读性好的命名方式。
foreign key 约束类型,外键
(cid) references category(id) 本表record的字段 cid 指向category表的字段id
- Java代码层面
我的GitHub链接🔗:https://github.com/pop-yasigi/BillManagementSystem
- 思考&感悟
在这个项目中主要学习到了以下内容:
- JDBC的使用,将Java代码与MySQL的连接
- 各功能代码分类打包的方式
- Entity、DAO、Service、Listener层的设计方式
- WorkingPanel抽象类(将所有panel的共同点打包成WorkingPanel,并实现该父类,强制重写几个共同的函数
- 表结构的设计方式:一对多关系,多对一关系
- 在点击打开某一板块的时候,数据随之刷新,不会出现UI界面数据和MySQL数据不统一的情况
热门相关:我有一座冒险屋 我是仙凡 悠哉兽世:种种田,生生崽 上将大叔,狼来了! 楚氏赘婿