本文讲述Java图形用户界面的编写,重点讲述一个简单计算器的实现。
文中程序的运行环境为Windows10 ,编译环境为MyEclipse 8.5。
一:输入,输出
①输入:允许输入带有括号的完整计算式(例 6*(3+5)-5÷2)
②输出:输出Double类型的结果
二:功能
①:基本的加,减,乘,除,四则运算
②:平方运算
③:开方运算
最终界面如下图:
除了常规的数字按钮和运算符,还有两个常数e,π
一:输入,以及运算的实现
要实现完整计算式的输入并运算,就得用到计算后缀表达式的算法,和将中缀表达式转换为后缀表达式的算法(这里不再赘述中缀表达式和后缀表达式,以及之后有关栈的知识也不再多说,有兴趣可以阅读数据结构中关于栈的相关章节),这里重点讲解以上两个算法。
算法1(中缀表达式转后缀表达式):
因为计算式要在计算器的文本编辑框中显示,所以计算式用字符串记录(定义为字符串)
规则:从左向右遍历中缀表达式
①:遇到数字字符,直接加入后缀表达式
②:遇到高优先级运算符,若栈为空直接入栈,若栈不为空,则将当前运算符与栈顶元素比较
比较1(栈顶元素也为高优先级运算符):栈顶元素出栈加入后缀表达式,当前运算符入栈。这样的操作使得栈 中不会出现连续的高优先级运算符
比较2(栈顶元素为'('左括号):将当前元素入栈
比较3(栈顶元素为低优先级运算符):将当前元素入栈
③:遇到'('左括号,将左括号入栈
④:遇到')'右括号,将栈顶元素顺序出栈,直到栈顶元素为左括号,此时删去栈顶的左括号
⑤:遇到低优先级运算符,若栈为空直接入栈,若栈不为空,则将当前运算符与栈顶元素比较
比较1(栈顶元素也为低优先级运算符):栈顶元素出栈加入后缀表达式,当前运算符入栈。这样的操作使得栈中 不会出现连续的低优先级运算符
比较2(栈顶元素为'('左括号):将当前运算符入栈
比较③(栈顶元素为高优先级运算符):栈顶元素出栈加入后缀表达式,当前运算符入栈
程序段如下(代码段中的注释根据上述讲解过程书写,可以根据步骤阅读)
代码段中用到的某些函数注释:
indexof():String.indexof()方法搜索在该字符串上是否出现了作为参数传递的字符串,若找到字符串,则返回字符 串首字母的位置(0代表第一个位置),在下面的代码段中,为了判断字符类型用到了该方法。
String.valueof():将double类型的浮点数转换为字符串
Double.parseDouble():将字符串转化为double类型的浮点数
private String[] yunsuan(String str)
{
String s="";//用于承接多位数的字符串
char a[]=new char[100];//静态的栈
String jieguo[]=new String[100];//后缀表达式字符串数组,为了将多位数存储为独立的字符串
int top=-1,j=0;//静态指针top,控制变量j
for (int i=0;i//遍历中缀表达式
{
if ("0123456789.".indexOf(str.charAt(i))>=0)//indexof函数见上方注释,遇到数字字符的情况
{
s="";//作为承接的字符串,每次开始都要清空
for (;i=0;i++)//将多位数存储在一个字符串中
{
s=s+str.charAt(i);
}
i--;
jieguo[j]=s;//数字字符直接加入后缀表达式
j++;
}
else if ("(".indexOf(str.charAt(i))>=0)//遇到左括号
{
top++;
a[top]=str.charAt(i);//左括号入栈
}
else if (")".indexOf(str.charAt(i))>=0)//遇到右括号
{
for (;;)//栈顶元素循环出栈,直到找到左括号为止
{
if (a[top]!='(')//栈顶元素不是左括号
{
jieguo[j]=a[top]+"";//栈顶元素出栈
j++;
top--;
}
else//找到栈顶元素是左括号
{
top--;//删除栈顶的左括号
break;//循环结束
}
}
}
else if ("*%÷".indexOf(str.charAt(i))>=0)//遇到高优先级运算符
{
if (top==-1)//若栈为空直接入栈
{
top++;
a[top]=str.charAt(i);
}
else//栈不为空
{
if ("*%÷".indexOf(a[top])>=0)//栈顶元素也为高优先级运算符
{
jieguo[j]=a[top]+"";//栈顶元素出栈进入后缀表达式
j++;
a[top]=str.charAt(i);//当前运算符入栈
}
else if ("(".indexOf(a[top])>=0)//栈顶元素为左括号,当前运算符入栈
{
top++;
a[top]=str.charAt(i);
}
else if ("+-".indexOf(a[top])>=0)//栈顶元素为低优先级运算符,当前运算符入栈
{
top++;
a[top]=str.charAt(i);
}
}
}
else if ("+-".indexOf(str.charAt(i))>=0)//遇到低优先级运算符
{
if (top==-1)//栈为空直接入栈
{
top++;
a[top]=str.charAt(i);
}
else//栈不为空
{
if ("%*÷".indexOf(a[top])>=0)//栈顶元素为高优先级运算符
{
jieguo[j]=a[top]+"";//栈顶元素出栈加入后缀表达式
j++;
a[top]=str.charAt(i);//当前运算符入栈
}
else if ("(".indexOf(a[top])>=0)//栈顶元素为左括号
{
top++;
a[top]=str.charAt(i);//当前运算符入栈
}
else if ("+-".indexOf(a[top])>=0)//栈顶元素也为低优先级运算符
{
jieguo[j]=a[top]+"";//栈顶元素出栈进入后缀表达式
j++;
a[top]=str.charAt(i);//当前元素入栈
}
}
}
}
for (;top!=-1;)//遍历结束后将栈中剩余元素依次出栈进入后缀表达式
{
jieguo[j]=a[top]+"";
j++;
top--;
}
return jieguo;
}
算法2:计算后缀表达式
计算后缀表达式依然借助栈来实现
运算规则:遍历后缀表达式
①:遇到数字字符,直接入栈
②:遇到运算符,顺序出栈两个元素(数字),进行运算,将运算结果入栈
循环以上步骤最终栈中剩下的那个数字就是最终答案
public String Result(String str[])
{
String Result[]=new String[100];//顺序存储的栈,数据类型为字符串
int Top=-1;//静态指针Top
for (int i=0;str[i]!=null;i++)//遍历后缀表达式
{
if ("+-*%÷".indexOf(str[i])<0)//遇到数字字符进栈
{
Top++;
Result[Top]=str[i];
}
if ("+-*%÷".indexOf(str[i])>=0)//遇到运算符
{
double x,y,n;
x=Double.parseDouble(Result[Top]);//顺序出栈两个数字字符串,并转换为double类型的数字
Top--;
y=Double.parseDouble(Result[Top]);//顺序出栈两个数字字符串,并转换为double类型的数字
Top--;
if ("-".indexOf(str[i])>=0)//一下步骤根据运算符来进行运算
{
n=y-x;
Top++;
Result[Top]=String.valueOf(n);//将运算结果重新入栈
}
if ("+".indexOf(str[i])>=0)
{
n=y+x;
Top++;
Result[Top]=String.valueOf(n);
}
if ("*".indexOf(str[i])>=0)
{
n=y*x;
Top++;
Result[Top]=String.valueOf(n);
}
if ("÷".indexOf(str[i])>=0)
{
if (x==0)//不允许被除数为0
{
String s="ERROR";
return s;
}
else
{
n=y/x;
Top++;
Result[Top]=String.valueOf(n);
}
}
if ("%".indexOf(str[i])>=0)
{
if (x==0)//不允许被除数为0
{
String s="ERROR";
return s;
}
else
{
n=y%x;
Top++;
Result[Top]=String.valueOf(n);
}
}
}
}
return Result[Top];//返回最终结果
}
算法3:开方运算
public String yunsuan2(String str)
{
String result="";
double a=Double.parseDouble(str),b=0;
b=Math.sqrt(a);
result=String.valueOf(b);
return result;
}
算法4:平方运算
public String yunsuan3(String str)
{
String result="";
double a=Double.parseDouble(str),b=0;
b=Math.pow(a, 2);
result=String.valueOf(b);
return result;
}
一:框架
本计算器是基于Swing组建的图形用户界面,采用JFrame框架作为主窗口
public class Calculator extends JFrame implements ActionListener
类名Calculator(计算器),继承JFrame框架,实现事件监听器接口
二:文本行,按钮
整个计算器界面,可以随意发挥,我编写的界面最终的样子在文章开头有图片
计算器类中,定义的成员有:
①private String[] KEYS={"7","8","9","*","4","5","6","-","1","2","3","+","0","e","π","÷","c","%",".","=","(",")","sqr","x*x"};
② private JButton keys[]=new JButton[KEYS.length];
③private JTextField resultText = new JTextField("0.0");
④private String b="";
①:按钮标签字符串
②:定义按钮数组
③:定义文本行
④:用于存放计算式
public Calculator()
{
super("计算器");
this.setLayout(null);//未采用任何类型的布局,后面的文本框和按钮显示位置都是自己计算好的位置
resultText.setBounds(20, 5, 255, 40);//设置文本框大小
resultText.setHorizontalAlignment(JTextField.RIGHT);//文本框内容右对齐
resultText.setEditable(false);//文本框不允许修改结果
this.add(resultText);//新建文本框
int x=20,y=55;
for (int i=0;i//放置按钮
{
keys[i] = new JButton();
keys[i].setText(KEYS[i]);
keys[i].setBounds(x, y, 60, 40);
if(x<215)
{
x+=65;
}
else
{
x = 20;
y+=45;
}
this.add(keys[i]);
}
for (int i = 0; i //每个按钮都注册事件监听器
{
keys[i].addActionListener(this);
}
this.setResizable(false);
this.setBounds(500, 200, 300, 400);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e)//处理事件
{
String label = e.getActionCommand(); //获得事件源的标签
if (label=="c"||label=="=")//按钮C清空文本框,消除前面所有的输入和结果
{
if(label=="=")//按钮=得出最终结果,调用计算方法
{
String s[]=yunsuan(this.b);
String result=Result(s);
this.b=result+"";
resultText.setText(this.b);//更新文本框,当前结果在字符串b中,并未删除,下一次输入是接着这个结果的,这样可以实现连续运算
}
else
{
this.b="";
resultText.setText("0");//更新文本域的显示,显示初始值
}
}
else if (label=="sqr")//sqr 按钮计算开方
{
String n=yunsuan2(this.b);
resultText.setText(n);
this.b=n;
}
else if(label=="x*x")//x*x按钮实现某个数的平方
{
String m=yunsuan3(this.b);
resultText.setText(m);
this.b=m;
}
else if(label=="e"||label=="π")
{
if (label=="e")//常数e
{
String m=String.valueOf(2.71828);
this.b=this.b+m;
resultText.setText(this.b);
}
if (label=="π")//常数π
{
String m=String.valueOf(3.14159265);
this.b=this.b+m;
resultText.setText(this.b);
}
}
else
{
this.b=this.b+label;
resultText.setText(this.b);
}
}
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Calculator extends JFrame implements ActionListener
{
private String[] KEYS={"7","8","9","*","4","5","6","-","1","2","3","+","0","e","π","÷","c","%",".","=","(",")","sqr","x*x"};
private JButton keys[]=new JButton[KEYS.length];
private JTextField resultText = new JTextField("0.0");
private String b="";
public Calculator()
{
super("计算器");
this.setLayout(null);
resultText.setBounds(20, 5, 255, 40);
resultText.setHorizontalAlignment(JTextField.RIGHT);
resultText.setEditable(false);
this.add(resultText);
int x=20,y=55;
for (int i=0;i=0)
{
s="";
for (;i=0;i++)
{
s=s+str.charAt(i);
}
i--;
jieguo[j]=s;
j++;
}
else if ("(".indexOf(str.charAt(i))>=0)
{
top++;
a[top]=str.charAt(i);
}
else if (")".indexOf(str.charAt(i))>=0)
{
for (;;)
{
if (a[top]!='(')
{
jieguo[j]=a[top]+"";
j++;
top--;
}
else
{
top--;
break;
}
}
}
else if ("*%÷".indexOf(str.charAt(i))>=0)
{
if (top==-1)
{
top++;
a[top]=str.charAt(i);
}
else
{
if ("*%÷".indexOf(a[top])>=0)
{
jieguo[j]=a[top]+"";
j++;
a[top]=str.charAt(i);
}
else if ("(".indexOf(a[top])>=0)
{
top++;
a[top]=str.charAt(i);
}
else if ("+-".indexOf(a[top])>=0)
{
top++;
a[top]=str.charAt(i);
}
}
}
else if ("+-".indexOf(str.charAt(i))>=0)
{
if (top==-1)
{
top++;
a[top]=str.charAt(i);
}
else
{
if ("%*÷".indexOf(a[top])>=0)
{
jieguo[j]=a[top]+"";
j++;
a[top]=str.charAt(i);
}
else if ("(".indexOf(a[top])>=0)
{
top++;
a[top]=str.charAt(i);
}
else if ("+-".indexOf(a[top])>=0)
{
jieguo[j]=a[top]+"";
j++;
a[top]=str.charAt(i);
}
}
}
}
for (;top!=-1;)
{
jieguo[j]=a[top]+"";
j++;
top--;
}
return jieguo;
}
public String yunsuan2(String str)
{
String result="";
double a=Double.parseDouble(str),b=0;
b=Math.sqrt(a);
result=String.valueOf(b);
return result;
}
public String yunsuan3(String str)
{
String result="";
double a=Double.parseDouble(str),b=0;
b=Math.pow(a, 2);
result=String.valueOf(b);
return result;
}
public String Result(String str[])
{
String Result[]=new String[100];
int Top=-1;
for (int i=0;str[i]!=null;i++)
{
if ("+-*%÷".indexOf(str[i])<0)
{
Top++;
Result[Top]=str[i];
}
if ("+-*%÷".indexOf(str[i])>=0)
{
double x,y,n;
x=Double.parseDouble(Result[Top]);
Top--;
y=Double.parseDouble(Result[Top]);
Top--;
if ("-".indexOf(str[i])>=0)
{
n=y-x;
Top++;
Result[Top]=String.valueOf(n);
}
if ("+".indexOf(str[i])>=0)
{
n=y+x;
Top++;
Result[Top]=String.valueOf(n);
}
if ("*".indexOf(str[i])>=0)
{
n=y*x;
Top++;
Result[Top]=String.valueOf(n);
}
if ("÷".indexOf(str[i])>=0)
{
if (x==0)
{
String s="ERROR";
return s;
}
else
{
n=y/x;
Top++;
Result[Top]=String.valueOf(n);
}
}
if ("%".indexOf(str[i])>=0)
{
if (x==0)
{
String s="ERROR";
return s;
}
else
{
n=y%x;
Top++;
Result[Top]=String.valueOf(n);
}
}
}
}
return Result[Top];
}
public static void main(String arg[])
{
Calculator a=new Calculator();
}
}
该计算器功能有限,只限于日常生活的使用,可以改进的方向很多
一:实现带符号数的运算,例如:-6-6
二:实现复数运算
三:实现多次方的运算
四:实现一元一次方程的运算
五:实现一元二次方程的运算
以上改进方向只是一小部分,对于有一些编程基础的人,可以尝试一下,对于大神这就是小儿科了。
欢迎读者评论,共同交流,共同进步。