欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-10 22:16:26
...

首先,写这篇博客的目的是:1.简单回顾这几天学到的知识;2.给那些想自学Java基础的小伙伴们一些小小的建议。(建议写在最后)

首先,我得声明在此之前我都没有学习Java的想法,也没有什么Java基础,有的只是一些c,c++和一丢丢数据结构基础,而为什么突然间开始自学Java,这一切的一切得从报名参加学校的物联网实验室说起。加入实验室需要面试和完成他们给出来的一堆task里面的一项,鉴于我面试结果比较差(也就是代码题没能做出来O-O。。。别问,问就是菜)学长推荐我做那个用Java写一个计算器小程序的题,因此走上了这条不归路(Java大法好!!!)。

OK,话不多说,我们直接进入正题。
这是计算器的界面
从0开始学习Java到制作一个有界面显示的简易计算机

计算器的主要难点还是在将中缀表达式转换成后缀式表达式,在解决这一问题的时候,这篇文章给了我很大的帮助 图解中缀表达式转后缀表达式 这里面有很详细的解释并附有代码,很适合小白。
我在这里也简单写一下他的思想吧。
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