一、[实验内容]:设计一个包含登录界面的计算器软件,该软件可以实现第一次作业中的全部功能,同时可以保存用户的历史计算记录(保存数据最好使用数据库)。
二、[实验要求]:完成软件的UI设计、使用Visio设计软件中所涉及的所有流程图。
三、[实验目的]:1.掌握软件开发的基本流程 2.掌握常用的软件开发方式和工具。
四、[实验环境]:windows11系统,Eclipse,MySql
五、[实验步骤]:
1、流程图
(一)登录界面流程图

(二)计算器设计流程图

2、登录界面的实现


3、登录界面代码:
<!DOCTYPE html> <head> <title>登陆页面</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <div class="login-container"> <h2>欢迎登录</h2> <form> <label for="username">用户名:</label> <input type="text" id="username" name="username" placeholder="请输入用户名" required> <label for="password">密码:</label> <input type="password" id="password" name="password" placeholder="请输入密码" required> <button type="submit">登录</button> </form> </div> </body> </html>
4、计算器页面代码
import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
public class CalculatorFrame extends JFrame implements ActionListener{
//主方法
public static void main(String[] args) {
CalculatorFrame cf=new CalculatorFrame();
}
//创建显示器面板,采用默认的流布局
final JPanel viewPanel =new JPanel();
//创建显示器
final JTextField textField=new JTextField();
//创建按钮面板
final JPanel buttonPanel=new JPanel();
//创建网络布局管理器对象
final GridLayout gridLayout=new GridLayout(0,4);
//按钮里面的内容
String [][]names= {
{"**","ln","lg","clear"},
{"sin","cos","tan","X"},
{"PI","//","%","/"},
{"7","8","9","*"},
{"4","5","6","-"},
{"1","2","3","+"},
{"_/``","0",".","="}};
//程序里面的内容
String [][]target= {
{"A","N","G","R"},
{"S","C","T","X"},
{"P","B","%","/"},
{"7","8","9","*"},
{"4","5","6","-"},
{"1","2","3","+"},
{"D","0",".","="}};
//创建按钮对象
JButton[][] buttons=new JButton[names.length][4];
//创建左侧的占位标签
final JLabel leftLabel=new JLabel();
//创建右侧的占位标签
final JLabel rightLabel=new JLabel();
//创建下侧的占位标签
final JLabel bottomLabel=new JLabel();
//存储计算结果
double result=0;
//用来更新文本框输出的字符串
StringBuffer sb=new StringBuffer();
//用来对应文本框的输出,还是选择用HashMap来存储
Map<String ,String>map=new HashMap<String,String>();
//输出的字符串放这个里面,每次使用都需要清零哈
String output="";
//警告标签,放在弹窗里面
final JLabel warningLabel=new JLabel();
public CalculatorFrame(){
//初始化窗体
initFrame();
//初始化面板
initPanel();
//初始化哈希表里面的数据
Stringbind();
//绑定事件.键盘绑定也放在里面
buttonAction();
//窗体可见,放最后吧,不然里面的东西不会显示呢
this.setVisible(true);
}
//初始化窗体
public void initFrame() {
//设置窗体的标题
this.setTitle("计算器");
//设置窗体大小不可改变
this.setResizable(false);
//设置界面置顶(就是页面不会别其他页面覆盖,界面始终在最上面)
this.setAlwaysOnTop(true);
//设置窗体的位置和大小,位置应该失效了,因为设置了居中
//this.setBounds(300,150,400,500);
//那还是改成setSize吧,设置窗体的大小就行了
this.setSize(400,500);
//居中
this.setLocationRelativeTo(null);
//设置窗体关闭按钮的动作作为退出
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//将显示器面板添加到窗体顶部
this.getContentPane().add(viewPanel,BorderLayout.NORTH);
//添加屏幕焦点,没有这个用键盘输入没反应埃
this.setFocusable(true);
}
//初始化面板
public void initPanel() {
//初始化组件
initModule();
viewPanel.add(textField);
viewPanel.setPreferredSize(new Dimension(100,80));
this.getContentPane().add(viewPanel,BorderLayout.NORTH);
buttonPanel.setLayout(gridLayout);//按钮面板采用网络布局
this.getContentPane().add(buttonPanel,BorderLayout.CENTER);//将按钮面板添加到窗体中间
//把按钮添加到按钮面板中,虽然可以和初始化按钮写一起,但是便于理解还是把他们分开写了
for(int row=0;row<names.length;row++) {
for(int col=0;col<names[row].length;col++) {
buttonPanel.add(buttons[row][col]);
}
}
this.getContentPane().add(leftLabel,BorderLayout.WEST);
this.getContentPane().add(rightLabel,BorderLayout.EAST);
this.getContentPane().add(bottomLabel,BorderLayout.SOUTH);
}
//初始化组件
public void initModule() {
//初始化显示器相关数据
textField.setEditable(false);//设置显示器不可编辑
textField.setHorizontalAlignment(SwingConstants.RIGHT);
textField.setColumns(35);//调节文本框的宽度
textField.setPreferredSize(new Dimension(500,40));
//初始化面板按钮
gridLayout.setVgap(10);//设置组件的水平间距
gridLayout.setHgap(10);//设置组件的垂直间距
//初始化按钮对象
for(int row=0;row<names.length;row++) {
for(int col=0;col<names[row].length;col++) {
buttons[row][col]=new JButton(names[row][col]);//创建按钮
}
}
//静态的初始化设置一些,把多个单词的给标识起来
/*buttons[0][0].setActionCommand("A");//"**"
buttons[0][1].setActionCommand("N");//"ln"
buttons[0][2].setActionCommand("G");//"lg"
buttons[0][3].setActionCommand("R");//clear
buttons[1][0].setActionCommand("S");//sin
buttons[1][1].setActionCommand("C");//cos
buttons[1][2].setActionCommand("T");//tan
buttons[2][0].setActionCommand("P");//PT
buttons[2][1].setActionCommand("B");//"//"
buttons[6][0].setActionCommand("D");//"+/-"
*/
//还是用循环全部绑定吧,自己一个一个绑代码灵活性不高
for(int row=0;row<names.length;row++) {
for(int col=0;col<names[row].length;col++) {
buttons[row][col].setActionCommand(target[row][col]);
}
}
//设置左侧标签的宽度
leftLabel.setPreferredSize(new Dimension(10,0));
//设置右侧标签的宽度
rightLabel.setPreferredSize(new Dimension(10,0));
//设置底部标签的宽度,组件的有高度,可以没宽度,和两边相反
bottomLabel.setPreferredSize(new Dimension(0,10));
}
//通过哈希表实现输出字符串和里面的字符串的不同标识
public void Stringbind() {
//map.put(getWarningString(), getName())
for(int row=0;row<names.length;row++) {
for(int col=0;col<names[row].length;col++) {
map.put(target[row][col], names[row][col]);
//System.out.println("执行成功");
}
}
//用来判断值有没有填写进去
//System.out.println(map.size());
}
public void buttonAction() {
//按钮绑定动作事件,和自己绑定,自己实现的监听的方法
for(int row=0;row<names.length;row++) {
for(int col=0;col<names[row].length;col++) {
(buttons[row][col]).addActionListener(this);
}
}
//给整个界面添加键盘监听事件
this.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
//按着按键不松时调用
@Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
//按键松开后执行
@Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
//System.out.println("我被调用了啦");
int code = e.getKeyCode();
//输出每次按下的键盘按钮对应的code
//System.out.println(code);
//用键盘添加数字
//单纯输入数字,shift键没有被按下,不然就加减乘除无法被响应了
if(code>=48 && code<=57 && !e.isShiftDown()) {
sb.append((char)code);
//outPut();
}else if(code==56 && e.isShiftDown()) {
sb.append("*");
//outPut();
}else if(code==47 && !e.isShiftDown()) {
sb.append("/");
}else if(code==8) {//Backspace键
//删除最后的一个字符
sb.deleteCharAt(sb.length()-1);
}else if(code==53 && e.isShiftDown()) {
sb.append("%");
}else if(code==61 && e.isShiftDown()) {
sb.append("+");
}else if(code==61 && !e.isShiftDown()) {//"="
//计算结果
result();
}else if(code==45 && !e.isShiftDown()) {
sb.append("-");
}else if(code==46 && !e.isShiftDown()) {
sb.append(".");
}else if(code==10) {//Enter键
//计算结果
result();
}
//每次键盘输入之后都要更新,所以干脆就直接放判断最后
outPut();
//"="和"Enter"键
if(code==61 && !e.isShiftDown()||code==10) {
//就是把[0,length)的内容删除即可
sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个
}
}
});
}
//重写鼠标点击事件
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JButton button =(JButton)e.getSource();//获得触发此次动作事件的按钮对象
String buttonName =e.getActionCommand();//获得触发此次动作事件的按钮的标签文本
if(buttonName.equals("X")) {
//没有字符串之后就不能删除了
if(sb.length()!=0) {
sb.deleteCharAt(sb.length()-1);
//textField.setText(output);//删除之后还需要立即显示一次,不然没有反应
}
}else if(buttonName.equals("R")){
//就是把[0,length)的内容删除即可
sb.delete(0, sb.length());
//删除之后还需要立即显示一次,不然没有反应
//textField.setText(output);
}else if(buttonName.equals("=")) {
//计算结果
result();
}else {
sb.append(buttonName);
//textField.setText(output);
}
//反正每次响应事件之后都要更新,干脆直接放在最后
outPut();
//要重新使框架获得焦点,这要写呢,不写就按下按钮之后键盘就没反应了
if(buttonName.equals("=")) {
//就是把[0,length)的内容删除即可
sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个
}
this.requestFocus();
}
//记录每次将输出的字符串
public void outPut() {
output="";
for(int i=0;i<sb.length();i++) {
//这我可能知道原因了,字符串"A"和'A'不一样
output= output+map.get(String.valueOf(sb.charAt(i)));
}
//每次更新都要输出,我直接写到方法里面算了
textField.setText(output);
}
//计算每次的结果
public void result() {
//对应关系
//**--->A ln--->N lg--->G sin--->S cos--->C
//tan--->T PI--->P //--->B /``--->D
//计算是按照优先级来的
//PI不算运算,直接填进去吧
//也不行,本来就是用一个符号来表示,这突然变成一长串数字,那还是运算的时候展开吧
//展开PI
try {
pi();
//ln,lg,sin,cos,tan,开根号的运算
special();
//乘方运算
power();
//乘除,整除,求余运算
multiplyDivide();
//加减计算
addAndSubtract();
}catch(Exception e) {
//弹出警告的弹窗
warning();
}finally {
System.out.println("今天又是元气满满的一天");
}
}
//展开PI
public void pi() {
for(int i=0;i<sb.length();i++) {
if(sb.charAt(i)=='P') {
double res=Math.PI;
String resString=res+"";
//更新字符串
sb=sb.replace(i, i+1, resString);
i=resString.length()-1;
continue;
}
}
}
//ln,lg,sin,cos,tan,开根号的运算
public void special() {
//都是用右边一个操作数的运算
for(int i=0;i<sb.length();i++) {
if(sb.charAt(i)=='N'||sb.charAt(i)=='G'||
sb.charAt(i)=='S'||sb.charAt(i)=='C'||
sb.charAt(i)=='T'||sb.charAt(i)=='D') {
double num=0;
int len=0;//记录字符串长度,之后还要进行字符串的替换呢
//只需要应该一边的数字即可
for(int j=i+1;j<sb.length();j++) {
//是j不是i,咋又错在这里了
if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
sb.charAt(j)=='%'||sb.charAt(j)=='B'||
sb.charAt(j)=='+'||sb.charAt(j)=='-'||
sb.charAt(j)=='A'||
sb.charAt(j)=='N'||sb.charAt(j)=='G'||
sb.charAt(j)=='S'||sb.charAt(j)=='C'||
sb.charAt(j)=='T'||sb.charAt(j)=='D') {
String s1=sb.substring(i+1,j);
num=Double.parseDouble(s1);
len=s1.length();
break;
}
//找到最右边咯,中间没有运算符那也要停止了
if(j==sb.length()-1) {
//这边是到j+1哈,找错误找了半天
//如果右边是一位的数字,就会导致运算是数是空
//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)
String s1=sb.substring(i+1,j+1);
num=Double.parseDouble(s1);
len=s1.length();
break;
}
}
//进行运算
//ln运算
if(sb.charAt(i)=='N') {
double res=Math.log(num);
String resString=res+"";
//更新字符串
sb=sb.replace(i, i+len+1, resString);
i=resString.length()-1;
continue;
}
//lg运算
if(sb.charAt(i)=='G') {
//换底公式
double res=Math.log(num)/Math.log(10);
String resString=res+"";
//更新字符串
sb=sb.replace(i, i+len+1, resString);
i=resString.length()-1;
continue;
}
//sin运算
if(sb.charAt(i)=='S') {
//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
//偷懒一下就用角度值吧,不用加入括号
double res=Math.sin(Math.toRadians(num));
String resString=res+"";
//更新字符串
sb=sb.replace(i, i+len+1, resString);
i=resString.length()-1;
continue;
}
//cos运算
if(sb.charAt(i)=='C') {
//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
//偷懒一下就用角度值吧,不用加入括号
double res=Math.cos(Math.toRadians(num));
String resString=res+"";
//更新字符串
sb=sb.replace(i, i+len+1, resString);
i=resString.length()-1;
continue;
}
//tan运算
if(sb.charAt(i)=='T') {
//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
//偷懒一下就用角度值吧,不用加入括号
double res=Math.tan(Math.toRadians(num));
String resString=res+"";
//更新字符串
sb=sb.replace(i, i+len+1, resString);
i=resString.length()-1;
continue;
}
//开根号运算
if(sb.charAt(i)=='D') {
double res=Math.sqrt(num);
String resString=res+"";
//更新字符串
sb=sb.replace(i, i+len+1, resString);
i=resString.length()-1;
continue;
}
}
}
}
//乘方运算
public void power(){
for(int i=0;i<sb.length();i++) {
if(sb.charAt(i)=='A') {
double num1=0,num2=0;
int len1=0,len2=0;//记录字符串长度,之后还要进行字符串的替换呢
for(int j=i-1;j>=0;j--) {
//得到第一个操作数,遇到加减乘除就停止了,相当于分隔了操作数
if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
sb.charAt(j)=='%'||sb.charAt(j)=='B'||
sb.charAt(j)=='+'||sb.charAt(j)=='-') {
String s1=sb.substring(j+1,i);
num1=Double.parseDouble(s1);
len1=s1.length();
break;
}
//找到边界了,只能停止了咯
if(j==0) {
String s1=sb.substring(j,i);
num1=Double.parseDouble(s1);
len1=s1.length();
break;
}
}
//往右边找第二个操作数,第二个操作数的话遇到乘除也需要提前终止,第一个不需要
//因为如果第一个操作数有乘除这种符号,就已经提前终止了,只有第二个操作数才需要考虑
for(int j=i+1;j<sb.length();j++) {
if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
sb.charAt(j)=='%'||sb.charAt(j)=='B'||
sb.charAt(j)=='+'||sb.charAt(j)=='-'||sb.charAt(j)=='A') {
String s1=sb.substring(i+1,j);
num2=Double.parseDouble(s1);
len2=s1.length();
break;
}
//找到最右边咯,中间没有运算符那也要停止了
if(j==sb.length()-1) {
//这边是到j+1哈,找错误找了半天
//如果右边是一位的数字,就会导致运算是数是空
//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)
String s1=sb.substring(i+1,j+1);
num2=Double.parseDouble(s1);
len2=s1.length();
break;
}
}
//进行运算
double res=Math.pow(num1, num2);
String resString=res+"";
//更新字符串
sb=sb.replace(i-len1, i+len2+1, resString);
i=i-len1-1+resString.length();
continue;
}
}
}
//乘除,整除,求余运算
public void multiplyDivide() {
for(int i=0;i<sb.length();i++) {
if(sb.charAt(i)=='*'||sb.charAt(i)=='/'||
sb.charAt(i)=='%'||sb.charAt(i)=='B') {
double num1=0,num2=0;
int len1=0,len2=0;//记录字符串长度,之后还要进行字符串的替换呢
for(int j=i-1;j>=0;j--) {
//得到第一个操作数,遇到加减就停止了,相当于分隔了操作数
if(sb.charAt(j)=='+'||sb.charAt(j)=='-') {
String s1=sb.substring(j+1,i);
num1=Double.parseDouble(s1);
len1=s1.length();
break;
}
//找到边界了,只能停止了咯
if(j==0) {
String s1=sb.substring(j,i);
num1=Double.parseDouble(s1);
len1=s1.length();
break;
}
}
//往右边找第二个操作数,第二个操作数的话遇到乘除也需要提前终止,第一个不需要
//因为如果第一个操作数有乘除这种符号,就已经提前终止了,只有第二个操作数才需要考虑
for(int j=i+1;j<sb.length();j++) {
if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
sb.charAt(j)=='%'||sb.charAt(j)=='B'||
sb.charAt(j)=='+'||sb.charAt(j)=='-') {
String s1=sb.substring(i+1,j);
num2=Double.parseDouble(s1);
len2=s1.length();
break;
}
//找到最右边咯,中间没有运算符那也要停止了
if(j==sb.length()-1) {
//这边是到j+1哈,找错误找了半天
//如果右边是一位的数字,就会导致运算是数是空
//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)
String s1=sb.substring(i+1,j+1);
num2=Double.parseDouble(s1);
len2=s1.length();
break;
}
}
//进行运算
if(sb.charAt(i)=='*') {
double res=num1*num2;
String resString=res+"";
//更新字符串
sb=sb.replace(i-len1, i+len2+1, resString);
i=i-len1-1+resString.length();
continue;
}
if(sb.charAt(i)=='/') {
//除数不能为0,有异常抛出的弹窗了,就去掉这个了
/*if(Math.abs(num2)<=1e-6) {
System.out.println("输出警告");
continue;
}*/
double res=num1/num2;
String resString=res+"";
//更新字符串
sb=sb.replace(i-len1, i+len2+1, resString);
i=i-len1-1+resString.length();
continue;
}
if(sb.charAt(i)=='%') {
//整数才能求余,第二个操作数也不能为0
/*if(Math.abs(num2)<=1e-6) {
System.out.println("输出警告");
continue;
}*/
//是求余%
double res=(int)num1%(int)num2;
String resString=res+"";
//更新字符串
sb=sb.replace(i-len1, i+len2+1, resString);
i=i-len1-1+resString.length();
continue;
}
if(sb.charAt(i)=='B') {
//整数的运算
//除数不能为0
/*if(Math.abs(num2)<=1e-6) {
System.out.println("输出警告");
continue;
}*/
double res=(int)num1/(int)num2;
String resString=res+"";
//更新字符串
sb=sb.replace(i-len1, i+len2+1, resString);
i=i-len1-1+resString.length();
continue;
}
}
}
}
//加减运算
public void addAndSubtract() {
for(int i=0;i<sb.length();i++) {
if(sb.charAt(i)=='+'||sb.charAt(i)=='-') {
//运算符两边的操作数
double num1=0,num2=0;
//记录两个操作数的字符串长度
int len1=0,len2=0;
//寻找第一个操作数
for(int j=i-1;j>=0;j--) {
if(j==0) {
String s1=sb.substring(j,i);
//得到第一个操作数
num1=Double.parseDouble(s1);
len1=s1.length();
break;
}
}
for(int j=i+1;j<sb.length();j++) {
if(sb.charAt(j)=='+'||sb.charAt(j)=='-') {
String s1=sb.substring(i+1,j);
//得到第二个操作数
num2=Double.parseDouble(s1);
len2=s1.length();
break;
}
if(j==sb.length()-1) {
String s1=sb.substring(i+1,j+1);
num2=Double.parseDouble(s1);
len2=s1.length();
break;
}
}
//进行加运算
if(sb.charAt(i)=='+') {
double res=num1+num2;
//String s2=sb.substring(i-len1,i+len2+1);
//String s3=res+ "";
//sb=sb.replace(s2, s3);
String resString=res+"";
//更新字符串
sb=sb.replace(i-len1, i+len2+1, resString);
i=i-len1-1+resString.length();
continue;
}
//进行减运算
if(sb.charAt(i)=='-') {
double res=num1-num2;
String resString=res+"";
//更新字符串
sb=sb.replace(i-len1, i+len2+1, resString);
i=i-len1-1+resString.length();
continue;
}
}
}
}
//警告,输入不合法的时候弹出
public void warning() {
JDialog jDialog=new JDialog();//创建弹窗对象
jDialog.setTitle("警告");//设置弹窗标题,和Frame差不多,可能还要通过标签来提示
jDialog.setSize(500,600);//设置弹窗的大小
jDialog.setAlwaysOnTop(true);//让弹窗置顶
jDialog.setLocationRelativeTo(null);//让弹窗居中
jDialog.setModal(true);//弹窗不关闭则无法操作下面的界面
//设置字体的类型,加粗,和大小
warningLabel.setFont(new Font("Microsoft YaHei",Font.BOLD,30));
//输出警告提示符
warningLabel.setText("错误!!!");
//标签的位置和大小
warningLabel.setBounds(60,180,500,100);
//这个也要取消布局管理器才行
jDialog.getContentPane().setLayout(null);
//往弹窗中添加标签
jDialog.getContentPane().add(warningLabel);
jDialog.setVisible(true);//让弹窗显示出来
}
}
5、计算器运算界面


6、连接数据库代码
import java.sql.*;
public class CalculatorWithDatabase {
static final String JDBC_DRIVER = "org.sqlite.JDBC";
static final String DB_URL = "jdbc:sqlite:calculator.db"; // 数据库文件名为 calculator.db
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
// 注册 JDBC 驱动
Class.forName(JDBC_DRIVER);
// 打开一个连接
System.out.println("连接数据库...");
conn = DriverManager.getConnection(DB_URL);
// 创建表格(如果不存在)
System.out.println("创建表格...");
stmt = conn.createStatement();
String sql = "CREATE TABLE IF NOT EXISTS calculation_history " +
"(id INTEGER PRIMARY KEY AUTOINCREMENT, expression TEXT, result DOUBLE)";
stmt.executeUpdate(sql);
// 进行计算并保存历史记录
double result = performCalculation(5, "+", 3);
saveCalculation(conn, "5 + 3", result);
// 检索并显示历史记录
System.out.println("历史记录:");
displayHistory(conn);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
}
private static double performCalculation(double operand1, String operator, double operand2) {
switch (operator) {
case "+":
return operand1 + operand2;
case "-":
return operand1 - operand2;
case "*":
return operand1 * operand2;
case "/":
return operand1 / operand2;
default:
throw new IllegalArgumentException("Unsupported operator: " + operator);
}
}
private static void saveCalculation(Connection conn, String expression, double result) throws SQLException {
String sql = "INSERT INTO calculation_history (expression, result) VALUES (?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, expression);
pstmt.setDouble(2, result);
pstmt.executeUpdate();
}
}
private static void displayHistory(Connection conn) throws SQLException {
String sql = "SELECT * FROM calculation_history";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
int id = rs.getInt("id");
String expression = rs.getString("expression");
double result = rs.getDouble("result");
System.out.println(id + ": " + expression + " = " + result);
}
}
}
}