从0开始学习Java到制作一个有界面显示的简易计算机

标签: 数据结构  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

版权声明:本文为qq_46601338原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_46601338/article/details/107580515