Java编写计算器--学习笔记,附源码
实验题目及内容
注:嵌套布局需要灵活使用中间容器JPanel
软件设计简易流程图
实验流程图
实验程序与结果
package counter; //引用包
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Stack;
import javax.swing.*;
//处理计算结果异常
class MyException extends Exception{ private static final long serialVersionUID = 1649159247013670200L;
public MyException() {
super(); }
public MyException(String message) {
super(message);
}
}
//将计算器的参数传递
class SwingConsole{
public static void run(final JFrame f,final int width,final int height){
//调用invokeLater来请求事件分发线程以运行某段代 码
//必须将这段代码放入一个Runnable对象的run方法中,并将该指定Runnable对象作为参数传递给invokeLater。
SwingUtilities.invokeLater(new Runnable(){
//invokeLater函数会立即返回,不会等到事件分发线程执行完这段代码。
public void run(){
f.setTitle(f.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(width,height);
f.setVisible(true);
}
});
}
}
public class calculator2 extends JFrame{
private static final long serialVersionUID = 1L; //定义变量
private JTextField textField; //输入文本框
private String input; //结果
private JButton button; //按钮
//在构造方法中重写,创建框架,创建组件,添加组件,以及给组件注册事件监听器
public calculator2() {
input = ""; //得到框架的内容窗格,才可以添加组件,使用默认的BorderLayout
Container container = this.getContentPane();
//创建中间容器JPanel
JPanel panel = new JPanel();
//创建文本框,指定单行长度
textField = new JTextField(30);
textField.setEditable(false); //文本框禁止编辑
textField.setHorizontalAlignment(JTextField.LEFT); //textField.setBounds(100, 100, 20, 20);
//在容器布局为空情况下生效
textField.setPreferredSize(new Dimension(200,30)); //使用add来添加组件,添加文本框到北部
container.add(textField, BorderLayout.NORTH);
String[] name=
{"1","2","3","+","4","5","6","-","7","8","9","*","0","C","=","/"}; //给中间容器设置布局管理器,GridLayout(4,4,1,1)设置行数,列数,组件水平距离,垂直距离
panel.setLayout(new GridLayout(4,4,1,1)); //将16个按钮放入JPanel
for(int i=0;i<name.length;i++) {
button = new JButton(name[i]); //给按钮添加事件监听器
button.addActionListener(new MyActionListener()); //添加按钮组件
panel.add(button); } //添加JPanel到中部
container.add(panel,BorderLayout.CENTER);
}
//继承事件适配器,重写事件监听器的方法
//内部类实现按钮响应,将输入值送入
class MyActionListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
int cnt=0;
String actionCommand = e.getActionCommand(); //获取按钮上的字符串 //用equals函数来判断是否为符号,若是括号中的则为true
if(actionCommand.equals("+") ||
actionCommand.equals("-") ||
actionCommand.equals("*") ||
actionCommand.equals("/")) {
input += " " + actionCommand + " ";
}
//按下C清除输入
else if(actionCommand.equals("C")) {
input = "";
}
//按下等号,若有异常,则调用MyException
else if(actionCommand.equals("=")) {
try {
input+= "="+calculate(input); }
catch (MyException e1) {
if(e1.getMessage().equals("Infinity"))
input+= "=" + e1.getMessage();
else
input = e1.getMessage(); }
//文本框输出input
textField.setText(input);
//储存结果清零
input="";
//cnt置1表示计算结束
cnt = 1;
}
else//按下数字
input += actionCommand;
//cnt为零时,输入继续,储存之前的结果
if(cnt == 0)
textField.setText(input);
}
}
//借助栈来完成表达式的计算
private String calculate(String input) throws MyException{
//创建数组
String[] comput = input.split(" ");
//创建栈
Stack<Double> stack = new Stack<>();
//创建变量
Double m = Double.parseDouble(comput[0]);
//第一个操作数入栈
stack.push(m);
//将字符串分割成字符串数组,依次运算
for(int i = 1; i < comput.length; i++) {
//数组奇数位为运算符(从第0位开始),偶数位为操作数,因此可将偶数为操作数进栈,
if(i%2==1) {
//遇见+(-)运算符,则将下一个数以正(负)的形式压人栈中
if(comput[i].equals("+"))
stack.push(Double.parseDouble(comput[i+1]));
if(comput[i].equals("-"))
stack.push(-Double.parseDouble(comput[i+1]));
//遇见*或/运算符,则将栈顶元素出栈与数组后一元素进行计算,并将其结果重新压入栈中,直至遍历至数组最后一个元素。最后将栈中的元素进行求和。
if(comput[i].equals("*")) { Double d = stack.peek(); //取栈顶元素
stack.pop(); //将栈顶元素出栈
stack.push(d*Double.parseDouble(comput[i+1]));//做乘法再入栈 }
if(comput[i].equals("/")) {
//将前一个数出栈做乘法再入栈
double help = Double.parseDouble(comput[i+1]);
if(help == 0)
throw new MyException("Infinity"); //若0是除数,不会继续执行该函数
double d = stack.peek();
stack.pop();
stack.push(d/help);
}
}
}
//将栈中元素求和
double d = 0d;
while(!stack.isEmpty()) {
d += stack.peek();
stack.pop();
}
//强制转换类型为字符,返回结果
String result = String.valueOf(d);
return result;
}
//main方法
public static void main(String[] args) {
//创建计算机,并且设置长宽
SwingConsole.run(new calculator2(), 250, 300);
}
}
之后不满意之前的设置,又进一步升级了计算器
package counter;
//引用包
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Stack;
import javax.swing.*;
//处理计算结果异常
class MyException extends Exception{
private static final long serialVersionUID = 1649159247013670200L;
public MyException() {
super();
}
public MyException(String message) {
super(message);
}
}
//将计算器的参数传递
class SwingConsole{
public static void run(final JFrame f,final int width,final int height){
//调用invokeLater来请求事件分发线程以运行某段代 码
//必须将这段代码放入一个Runnable对象的run方法中,并将该指定Runnable对象作为参数传递给invokeLater。
SwingUtilities.invokeLater(new Runnable(){
//invokeLater函数会立即返回,不会等到事件分发线程执行完这段代码。
public void run(){
f.setTitle(//f.getClass().getSimpleName() "计算器");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(width,height);
f.setVisible(true);
}
});
}
}
public class calculator2 extends JFrame{
private static final long serialVersionUID = 1L;
//定义变量
private JTextField textField; //输入文本框
private String input; //结果
private JButton button; //按钮
private JLabel jlabel;
//在构造方法中重写,创建框架,创建组件,添加组件,以及给组件注册事件监听器
public calculator2() {
//由于在线程设置里设置了title,所以这里不用再设计
super("计算器");
//在屏幕上显示计算器的位置为(500, 300)
this.setLocation(500, 300);
input = "";
//得到框架的内容窗格,才可以添加组件,使用默认的BorderLayout
Container container = this.getContentPane();
//创建中间容器JPanel
JPanel panel = new JPanel();
//设置颜色
this.setBackground(Color.blue);
//创建文本框,指定单行长度
textField = new JTextField(30);
textField.setEditable(false); //文本框禁止编辑
textField.setHorizontalAlignment(JTextField.LEFT);
//textField.setBounds(100, 100, 20, 20); //在容器布局为空情况下生效
textField.setPreferredSize(new Dimension(200,30));
//设置文本框颜色
textField.setBackground(Color.WHITE);
//使用add来添加组件,添加文本框到北部
container.add(textField, BorderLayout.NORTH);
//添加标题
jlabel = new JLabel("DESIGN BY **",JLabel.CENTER);
container.add(jlabel,BorderLayout.SOUTH);
String[] name=
{"1","2","3","+","4","5","6","-","7","8","9","*","0","C","=","/"};
//给中间容器设置布局管理器,GridLayout(4,4,1,1)设置行数,列数,组件水平距离,垂直距离
panel.setLayout(new GridLayout(4,4,1,1));
//将16个按钮放入JPanel
for(int i=0;i<name.length;i++) {
button = new JButton(name[i]);
//给按钮添加事件监听器
button.addActionListener(new MyActionListener());
//添加按钮组件
panel.add(button);
if((i==3)||(i==7)||(i==11)||(i==14)||(i==15))button.setBackground(Color.RED);
if(i==14)button.setBackground(Color.GREEN);
}
//添加JPanel到中部
container.add(panel,BorderLayout.CENTER);
String[] name2= {"%","1/X","sqrt"};
JPanel panel2 = new JPanel();
panel2.setLayout(new GridLayout(3,1,1,1));
//将16个按钮放入JPanel
for(int i=0;i<name2.length;i++) {
button = new JButton(name2[i]);
//给按钮添加事件监听器
button.addActionListener(new MyActionListener());
//添加按钮组件
button.setBackground(Color.RED); panel2.add(button); }
//添加JPanel到中部
container.add(panel2,BorderLayout.EAST);
}
//继承事件适配器,重写事件监听器的方法
//内部类实现按钮响应,将输入值送入
class MyActionListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
int cnt=0;
String actionCommand = e.getActionCommand();
//获取按钮上的字符串
//用equals函数来判断是否为符号,若是括号中的则为true
if(actionCommand.equals("+") || actionCommand.equals("-") ||
actionCommand.equals("*")
|| actionCommand.equals("/")||actionCommand.equals("%"))
{
input += " " + actionCommand + " ";
}
//按下C清除输入
else if(actionCommand.equals("C")) {
input = "";
}
//按下1/x求相反数
else if(actionCommand.equals("1/X")) {
input += " " + actionCommand + " ";
}
//按下sqrt求相反数
else if(actionCommand.equals("sqrt")) { input += " " + actionCommand + " "; }
//按下等号,若有异常,则调用MyException
else if(actionCommand.equals("=")) {
try {
input+= "="+calculate(input);
} catch (MyException e1) {
if(e1.getMessage().equals("Infinity"))
input+= "=" + e1.getMessage();
else
input = e1.getMessage();
}
//文本框输出input
textField.setText(input);
//储存结果清零
input="";
//cnt置1表示计算结束
cnt = 1;
}
else//按下数字
input += actionCommand;
//cnt为零时,输入继续,储存之前的结果
if(cnt == 0)
textField.setText(input);
}
}
//借助栈来完成表达式的计算
private String calculate(String input) throws MyException{
//创建数组
String[] comput = input.split(" ");
//创建栈
Stack<Double> stack = new Stack<>();
//创建变量
Double m = Double.parseDouble(comput[0]);
//第一个操作数入栈
stack.push(m);
//将字符串分割成字符串数组,依次运算
for(int i = 1; i < comput.length; i++) {
//数组奇数位为运算符(从第0位开始),偶数位为操作数,因此可将偶数为操作数进栈,
if(i%2==1) {
//遇见+(-)运算符,则将下一个数以正(负)的形式压人栈中
if(comput[i].equals("+"))
stack.push(Double.parseDouble(comput[i+1]));
if(comput[i].equals("-"))
stack.push(-Double.parseDouble(comput[i+1]));
//遇见*或/运算符,则将栈顶元素出栈与数组后一元素进行计算,并将其结果重新压入栈中,直至遍历至数组最后一个元素。最后将栈中的元素进行求和。
if(comput[i].equals("*")) {
Double d = stack.peek(); //取栈顶元素
stack.pop(); //将栈顶元素出栈
stack.push(d*Double.parseDouble(comput[i+1]));//做乘法再入栈
}
if(comput[i].equals("/")) {
//将前一个数出栈做乘法再入栈
double help = Double.parseDouble(comput[i+1]);
if(help == 0)
throw new MyException("Infinity"); //若0是除数,不会继续执行该函数
double d = stack.peek();
stack.pop();
stack.push(d/help);
}
if(comput[i].equals("%")) {
double help = Double.parseDouble(comput[i+1]);
if(help == 0)
throw new MyException("Infinity"); //若0是除数,不会继续执行该函数
double d = stack.peek(); //取栈顶元素
stack.pop(); //将栈顶元素出栈
stack.push(d%Double.parseDouble(comput[i+1]));//做求余再入栈
}
if(comput[i].equals("sqrt")) {
Double d = stack.peek(); //取栈顶元素
stack.pop(); //将栈顶元素出栈
stack.push(Math.sqrt(d));//做乘法再入栈
}
if(comput[i].equals("1/X")) {
double help = Double.parseDouble(comput[i-1]);
if(help == 0)
throw new MyException("Infinity"); //若0是除数,不会继续执行该函数
else{
double d = stack.peek(); //取栈顶元素
stack.pop(); //将栈顶元素出栈
stack.push(1/d);}//做求余再入栈
}
}
}
//将栈中元素求和
double d = 0;
while(!stack.isEmpty()) {
d += stack.peek();
stack.pop();
}
//强制转换类型为字符,返回结果
String result = String.valueOf(d);
return result;
}
//main方法
public static void main(String[] args) {
//创建计算机,并且设置长宽
SwingConsole.run(new calculator2(), 250, 300);
}
}
实验结果分析和问题讨论
1.当开发了一个应用程序后,这个应用程序包含了很多类,如果需要把这个应用程序提供给别人使用,通常会将这些类文件打包成一个JAR文件,把这个JAR文件提供给别人使用。只要别人在系统的CLASSPATH环境变量中添加这个JAR文件,则Java虚拟机就可以自动在内存中解压这个JAR包,把这个JAR文件当成一个路径,在这个路径中查找所于晓的类或宝层次对应的路径结构。
2.第一次用java写一个GUI,是挺累的。但是仔细看书后,知道了该编程GUI的几个环节和步骤:1引入包和类2设置顶层容器3设置布局管理器4将组件添加入容器5为相应的事件编写事件监听器的方法
3.麻烦的是,使用了嵌套布局,用BorderLayout布局面板,显示框放容器北部,整个按钮面板放容器中部。显示框是一个JTextFiled组件,按钮面板是一个JPanel组件存放16个JButton组件,且它的布局是4x4的网格布局(GridLayout)。
4之后在编程计算器的时候,也就是对事件监听器内部方法重写,由于继承的是适配器,所以只重写了一个方法,将输入的运算符和数字存入并且计算,当检测到“=”时输出结果并清零。
5之后运算的时候,运用到了栈这种数据结构,首先将字符串分割成字符串数组,数组奇数位为运算符(从第0位开始),偶数位为操作数,因此可将偶数为操作数进栈,遇见+(-)运算符,则将下一个数以正(负)的形式压人栈中,遇见*或/运算符,则将栈顶元素出栈与数组后一元素进行计算,并将其结果重新压入栈中,直至遍历至数组最后一个元素。最后将栈中的元素进行求和。
6注意到,在事件处理和计算的过程中,还使用了异常处理,这是很好的思想。
7通过在网上查阅资料,使用了线程的方法,调用invokerLater来请求事件分发线程来运行这段代码。但是必须将这段代码放入Runnable对象中的run方法中,并将该对象作为参数传递给invokerLater。这样的话,就直接可以在main方法里传递参数创建对象了。
8. 这次第一次写这样的代码,开始有点发虚,后来查阅书本以及在网上找了很多资料,明白了代码的思路,对GUI的编程,以及用栈来写计算器,异常处理,以及一些线程的思想,真的是受益匪浅。所谓抄袭是把别人的东西百分百拿过来,创新就是把百分之九十九抄袭,然后加上百分之一的自己的东西。所谓代码,就是别人的拿过来,看懂学会,然后自己去写,再加上自己的东西,这就算完成了。以后有这样的机会,还是要多练习,Java很有趣,我很喜欢Java。
9. 最后将java文件打包成jar文件,再用exe4j生成.exe文件,这样的话点击是可以运行的,但如果要在其他电脑运行,则每次都要把jre文件和exe一起复制到其他电脑上。所以下一步就是把exe文件和jre用innosetup编译成在windows下的可按照文件。
上一篇: 生成随机验证码 并且验证
下一篇: 获取图像任意4边形内的灰度平均值