从0开始学习Java到制作一个有界面显示的简易计算机
首先,写这篇博客的目的是:1.简单回顾这几天学到的知识;2.给那些想自学Java基础的小伙伴们一些小小的建议。(建议写在最后)
首先,我得声明在此之前我都没有学习Java的想法,也没有什么Java基础,有的只是一些c,c++和一丢丢数据结构基础,而为什么突然间开始自学Java,这一切的一切得从报名参加学校的物联网实验室说起。加入实验室需要面试和完成他们给出来的一堆task里面的一项,鉴于我面试结果比较差(也就是代码题没能做出来O-O。。。别问,问就是菜)学长推荐我做那个用Java写一个计算器小程序的题,因此走上了这条不归路(Java大法好!!!)。
OK,话不多说,我们直接进入正题。
这是计算器的界面
计算器的主要难点还是在将中缀表达式转换成后缀式表达式,在解决这一问题的时候,这篇文章给了我很大的帮助 图解中缀表达式转后缀表达式 这里面有很详细的解释并附有代码,很适合小白。
我在这里也简单写一下他的思想吧。
1.首先将中缀表达式按数字和字符进行分割,存入ls中(这里并没改变表达式原来的顺序,例如原来的表达式为2+3*(4-2)+6的话,分割存入ls中以后变为[2,3,*,(,4,-,2,),+,6]。为了更加适合我的程序,我对这部分代码做了较大的调整);
2.将中缀表达式转化为后缀表达式式,具体步骤为:
- 1.初始化两个栈:运算符栈s1和中间结果栈s2;
- 2.从左到右扫描中缀表达式;
- 3.遇到操作数时,将其压入s2;
- 4.遇到运算符时,比较其与s1栈顶运算符的优先级;
- 4.1.如果s1为空,或运算符栈顶为左括号“(”,则直接将此运算符入栈;
- 4.2.否则,若优先级比栈顶运算符的高,也将运算符压入s1(注意必须是高,等于或低于都不行)
- 4.3.否则,将s1栈顶的运算符弹出并压入s2中,再次转到4.1与s1中新的栈顶运算符进行比较;
- 5.遇到括号时:
- 5.1.如果是左括号“(”,则直接压入s1;
- 5.2.如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
- 6.重复步骤2-5,直到表达式的最右边;
- 7.将s1中剩余的运算符依次弹出并压入s2;
- 8.依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
3.计算后缀表达式(这个过程比较简单,就是当遇到操作数时,将数压栈,遇到操作符,则将栈中前两个元素出栈并计算,计算时需要注意的是,如果操作符为“-”或“/”时,应该用后出栈的减去或除以先出栈的,直到栈中只剩一个数)
接下来是具体的代码(我把他们分为了两个包,一个用于计算compute包,一个用于显示界面和运行calculator包)
这是calculator包
package calculator;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.regex.Pattern;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import Compute.compute;
public class Calculator {
String input = "";// 通过按钮输入的表达式
JFrame frame = new JFrame();
JTextField text = new JTextField("0.0");// 文本框
JPanel panel = new JPanel();// 中间面板
GridLayout lay = new GridLayout(4, 5, 1, 1);// 网格布局方式,4行5列,组件上下左右间距为1
public Calculator() {// 构造方法
frame.setTitle("Calculator"); // 设置窗口名
frame.setSize(400, 350); // 设置窗口大小
frame.add(text,BorderLayout.NORTH); // 将文本框放入框架内的北方
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置关闭窗口时,程序结束
text.setEditable(false); // 文本框不可被编辑
text.setPreferredSize(new Dimension(200, 50)); // 设置文本框大小
panel.setLayout(lay); // 将中间框架布局设为网格布局
String[] p = { "1", "2", "3", "(", ")", "4", "5", "6", "-", "+", "7", "8", "9", "*", "/", "0", "c", "=", "π",
"." };
for (int i = 0; i < p.length; i++) {
setbutton(frame, p[i]); // 利用setbutton方法,设置按钮
}
frame.add(panel); // 将中间面板加入窗口
frame.setLocationRelativeTo(null); // 设置窗口弹出时居中
frame.setVisible(true);
}
public static boolean isInteger(String str) {
Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
return pattern.matcher(str).matches();
}
public void setbutton(JFrame jf, String bt) { // 设置按钮的方法
JButton b = new JButton(bt);
if (!isInteger(bt)||bt.equals("+")||bt.equals("-"))
b.setBackground(Color.gray);
b.addActionListener(new mylistener()); // 为按钮添加监听器
panel.add(b); // 将按钮加入中间面板
}
public class mylistener implements ActionListener { // 内部类实现按钮响应
@Override
public void actionPerformed(ActionEvent e) {
int mark = 0;
String t = e.getActionCommand(); // 获取按钮上的字符串
if (t.equals("+") || t.equals("-") || t.equals("*") || t.equals("/")||t.equals("(")||t.equals(")")) {
if(t.equals("("))
input += t+" ";
else if(t.equals(")"))
input += " "+t;
else
input += " "+t+" ";
} else if (t.equals("c")) {
input = "";
} else if (t.equals("=")) {
input += "=" + Compute(input);
text.setText(input);
input="";
mark = 1;
} else{// 按下数字
if(t.equals("π"))
input+="3.1415926";
else
input += t;
}
if (mark == 0)
text.setText(input);// 将按下的字符显示到文本框内
}
}
public String Compute(String expression) {
compute opt = new compute();
return compute.calculator(compute.infix2SuffixExpressionList(compute.infixExpression2List(expression)));
}
public static void main(String[] args) {
Calculator a = new Calculator();
}
}
这是compute包
package Compute;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
class Operation {
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
public static int getValue(String operation) {
int result = 0;
switch (operation) {
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
if (!operation.equals("(") && operation.equals(")"))
System.out.println("不存在的运算符");
break;
}
return result;
}
}
public class compute {
public static List<String> infixExpression2List(String infixExpression) {
ArrayList<String> ls = new ArrayList<String>();
String[] item = infixExpression.split(" ");
int i = 0;
while (i < item.length) {
ls.add(item[i]);
i++;
}
System.out.println(ls);
return ls;
}
public static List<String> infix2SuffixExpressionList(List<String> infixList) {
Stack<String> s1 = new Stack<String>();// 操作符栈
// 由于在整个转换过程中s2栈都没有pop操作,而且后面需要逆序输出,为了简化,可以不用栈结构,改用List<String> s2代替
List<String> s2 = new ArrayList<String>();
// 遍历infixList
for (String item : infixList) {
// 如果是一个数,则入s2
if (isNumeric(item)) {
s2.add(item);
} else if (item.equals("(")) {// 如果是左括号,直接入s1栈
s1.push(item);
} else if (item.equals(")")) {// 如果是右括号,则依次弹出s1栈顶的运算符,并加入s2,直到遇到左括号为止,此时将这一对括号丢弃;
while (!s1.peek().equals("(")) {
s2.add(s1.pop());
}
s1.pop();// 去除s1中对应的左括号,不能忘记,很重要!!!
} else {
// 如果是运算符,比较其与s1栈顶运算符的优先级:
// 1.如果s1为空,或运算符栈顶为左括号“(”,则直接将此运算符入栈;
// 2.否则,若优先级比栈顶运算符的高,也将运算符压入s1(注意必须是高,等于或低于都不行)
// 3.否则,将s1栈顶的运算符弹出并压入s2中,再次转到4.1与s1中新的栈顶运算符进行比较;
// 当item的优先级小于等于s1栈顶的优先级时,将s1栈顶的运算符弹出并加入s2中,再次转到1与s1中新的栈顶运算符进行比较
while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
s2.add(s1.pop());
}
// 将item入栈s1
s1.push(item);
}
}
// 将s1中的剩余运算符弹出并加入s2
while (s1.size() != 0) {
s2.add(s1.pop());
}
System.out.println(s2);
return s2;// 由于加入的是一个List,本身有序,所以正常输出即为后缀表达式而不必逆序
}
public static String calculator(List<String> ls) {
Stack<String> stack = new Stack<String>();
// 遍历
for (String item : ls) {
if (isNumeric(item)) {// 如果是数直接入栈
stack.push(item);
} else {
double num2 = Double.parseDouble(stack.pop());
double num1 = Double.parseDouble(stack.pop());
double res = 0;
if (item.equals("+")) {
res = num1 + num2;
} else if (item.equals("-")) {// 后缀表达式用后取出的数-先取出的数
res = num1 - num2;
} else if (item.equals("*")) {
res = num1 * num2;
} else if (item.equals("/")) {
if (num2 == 0)
return "错误!";
res = num1 / num2;
} else {
throw new RuntimeException("运算符有误");
}
stack.push(res + "");
}
}
// 栈最后剩下的数即为运算结果
return stack.pop();
}
public static boolean isNumeric(String str) {
String bigStr;
try {
bigStr = new BigDecimal(str).toString();
} catch (Exception e) {
return false;// 异常 说明包含非数字。
}
return true;
}
}
写在最后:
在整个自学过程中找了很多资料(问学长要学长忘给了Q-Q),我把其中比较好的资源的连接附在了下面,大家可以去看看,都是比较基础的,因为有一点c和数据结构的基础,所以学的比较快,学完下面这些大概用了两天不到,但仅仅只是学完,后面着手写这个程序的时候还是遇到了很多麻烦,特别是不记得很多类的自带的方法,巩固还是需要时间的,所以学的时候不要太过着急,另外,一定要多动手写代码。
1.Java从零基础到入门这个是视频资源,讲的很细,老师也很有趣(讲话不像其他视频中的老师那样让人感觉无聊想睡)链接好像不可用,大家可以直接上腾讯视频搜,这是孙国安老师的视频,点击量挺多的。
2.Java swing教程 学完基础以后,为了做出有界面的计算器,就可以开始学这个了,这个是文字版的,里面有很多的例子并附有代码,很适合初学者。
3.另外就是前面提到的文章了 图解中缀表达式转后缀表达式;
这就是这次的全部内容了,能看到这里的小伙伴很感谢你们呀,最后,祝正在学Java的你永葆这份热情,奥利给!!!
end