用Java制作简单的计算器

本篇文章主要是提供思路,当然文章末尾也又提供了源代码。

代码也是写了几天,重要的理解,不是直接复制粘贴就交作业了。

转载请注明出处,尊重作者劳动成果。

目录

界面的设计:

事件的响应

计算

详细代码如下

 总结:


要制作一个简单的计算器,首先就是对于界面的设计,然后就是其功能的实现。

对于事件的实现大概就分下面几个步骤。

  1. 确定事件源和监听源
  2. 实现监听器的接口
  3. 将事件源注册到监听器

然后就一起进入代码的编写吧,我是分成了几个发放来编写功能,最后有最终的代码,可以直接运行,这要注意的是,直接粘贴的话,类名和文件名要一样哦。

界面的设计:

他最上面是要有一个文本框,中间要有很多的按钮,我这里大的框架是采用BorderLayout类布局管理器,其中他的NORTH(容器顶部)是添加了一个FlowLayout类布局管理器,FlowLayout里面是一个文本框,CENTER(容器的中间)是添加了一个GirdLayout类布局管理器,形成网格状的按钮,WEST(容器的左侧),EAST(容器的右侧),SOUTH(容器底部),这些是通过添加一个空白的标签来占位置的,让界面更加好看一点。

	//创建显示器面板,采用默认的流布局
	final JPanel viewPanel =new JPanel();
	//创建显示器
	final JTextField textField=new JTextField();
	//创建按钮面板
	final JPanel buttonPanel=new JPanel();
	//创建网络布局管理器对象
	final GridLayout gridLayout=new GridLayout(0,4);
	//按钮里面的内容
	String [][]names= {
			{"**","ln","lg","clear"},
			{"sin","cos","tan","X"},
			{"PI","//","%","/"},
			{"7","8","9","*"},
			{"4","5","6","-"},
			{"1","2","3","+"},
			{"_/``","0",".","="}};

	//创建按钮对象
	JButton[][] buttons=new JButton[names.length][4];
	//创建左侧的占位标签
	final JLabel leftLabel=new JLabel();
	//创建右侧的占位标签
	final JLabel rightLabel=new JLabel();
	//创建下侧的占位标签
	final JLabel bottomLabel=new JLabel();
//初始化组件
	public void initModule() {
		//初始化显示器相关数据
		textField.setEditable(false);//设置显示器不可编辑
		textField.setHorizontalAlignment(SwingConstants.RIGHT);
		textField.setColumns(35);//调节文本框的宽度
		textField.setPreferredSize(new Dimension(500,40));
	
		//初始化面板按钮
		gridLayout.setVgap(10);//设置组件的水平间距
		gridLayout.setHgap(10);//设置组件的垂直间距
		//初始化按钮对象
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				buttons[row][col]=new JButton(names[row][col]);//创建按钮
			}
		}
		
		//设置左侧标签的宽度
		leftLabel.setPreferredSize(new Dimension(10,0));
		//设置右侧标签的宽度
		rightLabel.setPreferredSize(new Dimension(10,0));
		//设置底部标签的宽度,组件的有高度,可以没宽度,和两边相反
		bottomLabel.setPreferredSize(new Dimension(0,10));
	}
//初始化面板
	public void initPanel() {
		//初始化组件
		initModule();
		
		viewPanel.add(textField);
		viewPanel.setPreferredSize(new Dimension(100,80));
		this.getContentPane().add(viewPanel,BorderLayout.NORTH);
		
		
		buttonPanel.setLayout(gridLayout);//按钮面板采用网络布局
		this.getContentPane().add(buttonPanel,BorderLayout.CENTER);//将按钮面板添加到窗体中间
		//把按钮添加到按钮面板中,虽然可以和初始化按钮写一起,但是便于理解还是把他们分开写了
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				buttonPanel.add(buttons[row][col]);
			}
		}
		this.getContentPane().add(leftLabel,BorderLayout.WEST);
		this.getContentPane().add(rightLabel,BorderLayout.EAST);
		this.getContentPane().add(bottomLabel,BorderLayout.SOUTH);
	}
	
    //初始化窗体
	public void initFrame() {
		//设置窗体的标题
		this.setTitle("计算器");
		//设置窗体大小不可改变
		this.setResizable(false);
		//设置界面置顶(就是页面不会别其他页面覆盖,界面始终在最上面)
        this.setAlwaysOnTop(true);
        //设置窗体的位置和大小,位置应该失效了,因为设置了居中
		//this.setBounds(300,150,400,500);
        //那还是改成setSize吧,设置窗体的大小就行了
        this.setSize(400,500);
		//居中
		this.setLocationRelativeTo(null);
		//设置窗体关闭按钮的动作作为退出
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//将显示器面板添加到窗体顶部
		this.getContentPane().add(viewPanel,BorderLayout.NORTH);
	}

遇到的问题:

问题1:上层的文本框的大小的宽度设置不了,然后一直没有变化。

解决:通过修改setColumns();调节成功了,这个我还不知道原理,有知道的希望大家可以告诉我呢,之前一直在修改setPreferredSize();然后没反应,修改流布局管理器也只是把空白的地方变多了。

问题2:底部宽度bottomLabel.setPreferredSize(new Dimension(10,0));用这个修改没反应。

解决:这个当然不会有反应,添加加底部的宽是10,高是0,结果当然不会显示,之前偷懒直接复制左右侧的导致没显示,所以应该是bottomLabel.setPreferredSize(new Dimension(0,10));才对。

事件的响应:

Java | swing 如何清空JTextField中的内容_如何清空jtextfield的数据_黄佳俊、的博客-CSDN博客情况描述:在一个JTextField中输入了数据,用一个按钮来清空里面的数据,要如何实现啊!首先要说的是:没有这个方法,clear,能设置JTextField内容为空但是可以这样巧妙地做到:使用jTextField.setText("");把内容替换为空字符串,来实现清空JTextField中的内容...https://blog.csdn.net/weixin_48419914/article/details/121470250解决java添加/点击JButton后键盘监听无效的问题_QASWINE的博客-CSDN博客问题:在点击JButton后,原来可以用的键盘操作用不了了原因:点击JButton,焦点在按钮上,原来的有键盘监听器的组件(JFrame、-JPanel)失去焦点解决方法:如果键盘监听器在frame上,添加 frame.requestFocus(),使他重获焦点。下面是一个例子: button.addActionListener(new ActionListener() { ...https://blog.csdn.net/qq_33831360/article/details/103280448Java 键盘事件无效的几种原因_mapcontrol.addkeylistener(_imonkeyi的博客-CSDN博客..https://blog.csdn.net/imonkeyi/article/details/86177348

 具体的方法就看方法内部的调用吧,有应该都有写方法的作用的,接下来就是确定事件源和监听源,我是打算通过键盘输入一些字符和通过鼠标点击按钮来实现。

1.确定事件源和监听源

事件源:JButton[][] buttons=new JButton[names.length][4];按钮,和自己本身框架

监听源:ActionListener,KeyListener

2.实现监听器的接口

//重写鼠标点击事件
	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		JButton button =(JButton)e.getSource();//获得触发此次动作事件的按钮对象
		String buttonName =e.getActionCommand();//获得触发此次动作事件的按钮的标签文本
		
	
		if(buttonName.equals("X")) {
			//没有字符串之后就不能删除了
			if(sb.length()!=0) {
				sb.deleteCharAt(sb.length()-1);
				//textField.setText(output);//删除之后还需要立即显示一次,不然没有反应
			}
		}else if(buttonName.equals("R")){
			//就是把[0,length)的内容删除即可
			sb.delete(0, sb.length());
			//删除之后还需要立即显示一次,不然没有反应
			//textField.setText(output);
		}else if(buttonName.equals("=")) {
			//计算结果
			result();
		}else {
			sb.append(buttonName);
			//textField.setText(output);
		}
		//反正每次响应事件之后都要更新,干脆直接放在最后
		outPut();
		//要重新使框架获得焦点,这要写呢,不写就按下按钮之后键盘就没反应了
		if(buttonName.equals("=")) {
			//就是把[0,length)的内容删除即可
			sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个
		}
		this.requestFocus();
	}
            @Override
        	public void keyTyped(KeyEvent e) {
        		// TODO Auto-generated method stub
        		
        	}
        	//按着按键不松时调用
        	@Override
        	public void keyPressed(KeyEvent e) {
        		// TODO Auto-generated method stub
        	}
        	//按键松开后执行
        	@Override
        	public void keyReleased(KeyEvent e) {
        		// TODO Auto-generated method stub
        		//System.out.println("我被调用了啦");
        		int code = e.getKeyCode();
        		//输出每次按下的键盘按钮对应的code
        		//System.out.println(code);
        		//用键盘添加数字
        		//单纯输入数字,shift键没有被按下,不然就加减乘除无法被响应了
        		if(code>=48 && code<=57 && !e.isShiftDown()) {
        			sb.append((char)code);
        			//outPut();
        		}else if(code==56 && e.isShiftDown()) {
        			sb.append("*");
        			//outPut();
        		}else if(code==47 && !e.isShiftDown()) {
        			sb.append("/");
        		}else if(code==8) {//Backspace键
        			//删除最后的一个字符
        			sb.deleteCharAt(sb.length()-1);
        		}else if(code==53 && e.isShiftDown()) {
        			sb.append("%");
        		}else if(code==61 && e.isShiftDown()) {
        			sb.append("+");
        		}else if(code==61 && !e.isShiftDown()) {//"="
        			//计算结果
        			result();
        		}else if(code==45 && !e.isShiftDown()) {
        			sb.append("-");
        		}else if(code==46 && !e.isShiftDown()) {
        			sb.append(".");
        		}else if(code==10) {//Enter键
        			//计算结果
        			result();
        		}
        		//每次键盘输入之后都要更新,所以干脆就直接放判断最后
        		outPut();
        		//"="和"Enter"键
        		if(code==61 && !e.isShiftDown()||code==10) {
        			//就是把[0,length)的内容删除即可
        			sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个
        		}
        	}
        }

3.将事件源注册到监听器

public void buttonAction() {
		//按钮绑定动作事件,和自己绑定,自己实现的监听的方法
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				(buttons[row][col]).addActionListener(this);
			}
		}	
		//给整个界面添加键盘监听事件
        this.addKeyListener(new KeyListener() {
        	
        	@Override
        	public void keyTyped(KeyEvent e) {
        		// TODO Auto-generated method stub
        		
        	}
        	//按着按键不松时调用
        	@Override
        	public void keyPressed(KeyEvent e) {
        		// TODO Auto-generated method stub
        	}
        	//按键松开后执行
        	@Override
        	public void keyReleased(KeyEvent e) {
        		// TODO Auto-generated method stub
        		//System.out.println("我被调用了啦");
        		int code = e.getKeyCode();
        		//输出每次按下的键盘按钮对应的code
        		//System.out.println(code);
        		//用键盘添加数字
        		//单纯输入数字,shift键没有被按下,不然就加减乘除无法被响应了
        		if(code>=48 && code<=57 && !e.isShiftDown()) {
        			sb.append((char)code);
        			//outPut();
        		}else if(code==56 && e.isShiftDown()) {
        			sb.append("*");
        			//outPut();
        		}else if(code==47 && !e.isShiftDown()) {
        			sb.append("/");
        		}else if(code==8) {//Backspace键
        			//删除最后的一个字符
        			sb.deleteCharAt(sb.length()-1);
        		}else if(code==53 && e.isShiftDown()) {
        			sb.append("%");
        		}else if(code==61 && e.isShiftDown()) {
        			sb.append("+");
        		}else if(code==61 && !e.isShiftDown()) {//"="
        			//计算结果
        			result();
        		}else if(code==45 && !e.isShiftDown()) {
        			sb.append("-");
        		}else if(code==46 && !e.isShiftDown()) {
        			sb.append(".");
        		}else if(code==10) {//Enter键
        			//计算结果
        			result();
        		}
        		//每次键盘输入之后都要更新,所以干脆就直接放判断最后
        		outPut();
        		//"="和"Enter"键
        		if(code==61 && !e.isShiftDown()||code==10) {
        			//就是把[0,length)的内容删除即可
        			sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个
        		}
        	}
        });
	}public void buttonAction() {
		//按钮绑定动作事件,和自己绑定,自己实现的监听的方法
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				(buttons[row][col]).addActionListener(this);
			}
		}	
		//给整个界面添加键盘监听事件
        this.addKeyListener(new KeyListener() {
        	
        	@Override
        	public void keyTyped(KeyEvent e) {
        		// TODO Auto-generated method stub
        		
        	}
        	//按着按键不松时调用
        	@Override
        	public void keyPressed(KeyEvent e) {
        		// TODO Auto-generated method stub
        	}
        	//按键松开后执行
        	@Override
        	public void keyReleased(KeyEvent e) {
        		// TODO Auto-generated method stub
        		//System.out.println("我被调用了啦");
        		int code = e.getKeyCode();
        		//输出每次按下的键盘按钮对应的code
        		//System.out.println(code);
        		//用键盘添加数字
        		//单纯输入数字,shift键没有被按下,不然就加减乘除无法被响应了
        		if(code>=48 && code<=57 && !e.isShiftDown()) {
        			sb.append((char)code);
        			//outPut();
        		}else if(code==56 && e.isShiftDown()) {
        			sb.append("*");
        			//outPut();
        		}else if(code==47 && !e.isShiftDown()) {
        			sb.append("/");
        		}else if(code==8) {//Backspace键
        			//删除最后的一个字符
        			sb.deleteCharAt(sb.length()-1);
        		}else if(code==53 && e.isShiftDown()) {
        			sb.append("%");
        		}else if(code==61 && e.isShiftDown()) {
        			sb.append("+");
        		}else if(code==61 && !e.isShiftDown()) {//"="
        			//计算结果
        			result();
        		}else if(code==45 && !e.isShiftDown()) {
        			sb.append("-");
        		}else if(code==46 && !e.isShiftDown()) {
        			sb.append(".");
        		}else if(code==10) {//Enter键
        			//计算结果
        			result();
        		}
        		//每次键盘输入之后都要更新,所以干脆就直接放判断最后
        		outPut();
        		//"="和"Enter"键
        		if(code==61 && !e.isShiftDown()||code==10) {
        			//就是把[0,length)的内容删除即可
        			sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个
        		}
        	}
        });
	}

问题3:因为没有响应之后都要有对应的应答,有时候要输出,但是输入的按钮有些是含义多个字符的字符串,在计算和删除的时候删除的是单个的字符不是整个的字符串,比如sin输入的是“sin”删除只能删除最后的'n’。

解决:我这里是用到了HashMap来解决,在程序里面是单个的字符,输出是输出字符表示的字符串,这样删除计算等操作都方便了。

问题4:键盘输入对于*,+等的字符无法得到。

解决:我是通过 e.getKeyCode();来得到键盘输入的code,但是对于*和8是同一个code,都是56,不同的是shift键是否按下,之前是想写在 keyPressed里面来响应,但是感觉也实现不来,后面就是看到了e.isShiftDown();来判断shift键是否按下,也确实抱着试一试的态度,结果发现还真可以,就加上了e.isShiftDown();的判断。就解决了'*'和'8'有相同code的情况。

	//按钮里面的内容
	String [][]names= {
			{"**","ln","lg","clear"},
			{"sin","cos","tan","X"},
			{"PI","//","%","/"},
			{"7","8","9","*"},
			{"4","5","6","-"},
			{"1","2","3","+"},
			{"_/``","0",".","="}};
	//程序里面的内容
	String [][]target= {
			{"A","N","G","R"},
			{"S","C","T","X"},
			{"P","B","%","/"},
			{"7","8","9","*"},
			{"4","5","6","-"},
			{"1","2","3","+"},
			{"D","0",".","="}};    
    //用来对应文本框的输出,还是选择用HashMap来存储
	Map<String ,String>map=new HashMap<String,String>();
    //通过哈希表实现输出字符串和里面的字符串的不同标识
	public void Stringbind() {
		
		//map.put(getWarningString(), getName())
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				map.put(target[row][col], names[row][col]);
				//System.out.println("执行成功");
			}
		}
		//用来判断值有没有填写进去
		//System.out.println(map.size());
	}

问题5:在查找的时候输出的是null,不是目标的字符串。

解决:这我找了挺久的,最后还看了HashMap里面的元素有多少,发现是存进去的,问题出现在字符串“A”和'A'不是一样的东西,所以哈希表没找到。

输出是通过一个函数来实现

    //用来更新文本框输出的字符串
	StringBuffer sb=new StringBuffer();
	//用来对应文本框的输出,还是选择用HashMap来存储
	Map<String ,String>map=new HashMap<String,String>();
	//输出的字符串放这个里面,每次使用都需要清零哈
	String output="";
    //记录每次将输出的字符串
	public void outPut() {
		output="";
		for(int i=0;i<sb.length();i++) {
		//这我可能知道原因了,字符串"A"和'A'不一样
			output= output+map.get(String.valueOf(sb.charAt(i)));
		}
		//每次更新都要输出,我直接写到方法里面算了
		textField.setText(output);
	}

问题6:键盘的输入失灵。

解决:要通过聚焦,才能实现,调用this.setFocusable(true);

问题7:开始输入可以通过键盘输出,但是在按下面板中的按钮之后键盘无法输入。

解决:在每次按下按钮之后重新聚焦this.requestFocus();要重新使框架获得焦点,这要写呢,不写就按下按钮之后键盘就没反应了。

最后就是计算器功能实现的部分了,比较好实现的就是除‘=’号的按钮,直接添加到字符串里面,在最后打印就行。我这里采用的是StringBuffer因为要添加和更新什么的。

删除按钮和清空按钮分别就是删除字符串最后一个字符和删除字符串所有的字符。

//删除最后一个字符
if(sb.length()!=0) {
	sb.deleteCharAt(sb.length()-1);
	//textField.setText(output);//删除之后还需要立即显示一次,不然没有反应
}
//删除所有的字符,确定是范围是[0,length)
sb.delete(0, sb.length());

问题8:按下按钮之后没有显示删除。

解决:这个是因为没有显示,在程序中已经删除了,但是呢,用户看到是还没删除的样子,所以还是要更新一下输出。

计算:

java 实现字符串算式的计算_段593405572的博客-CSDN博客输入一个字符串(包括数字、小数点,加减乘除百分比),模拟计算器进行运算。 思路:按运算符优先级依次运算百分比、乘除、加减。遍历字符串,将运算符前或前后的数字字符转为double进行计算,计算后的结果替换原字符串中的子算式。 import java.util.Scanner;public class Main{public static double f(String s){double p = 0;//百分比计算for(int i = 0; i &...https://blog.csdn.net/d593405572/article/details/106713937

Java中角度和弧度的转换,三角函数,反三角函数_java角度转弧度-CSDN博客Java中角度和弧度的转换,三角函数,反三角函数https://blog.csdn.net/senxu_/article/details/126025606

接下来就是最后的计算环节了。

首先确定计算的优先级,我这里没有引入括号了,所以把lg,ln,sin,cos,tan,开根号都是和右边的操作数看成一个整体的。

字符串的运算就是通过运算符来分割,通过寻找到他的运算数,进行运算,把运算结果的字符串和运算前的字符串进行替换即可。

    //计算每次的结果
	public void result() {
		
		//对应关系
		//**--->A  ln--->N  lg--->G  sin--->S   cos--->C
		//tan--->T  PI--->P  //--->B  /``--->D
		//计算是按照优先级来的
		//PI不算运算,直接填进去吧
		//也不行,本来就是用一个符号来表示,这突然变成一长串数字,那还是运算的时候展开吧
		//展开PI
		try {
			pi();
			//ln,lg,sin,cos,tan,开根号的运算
			special();
			//乘方运算
			power();
			//乘除,整除,求余运算
			multiplyDivide();
			//加减计算
			addAndSubtract();
		}catch(Exception e) {
			//弹出警告的弹窗
			warning();
		}finally {
			System.out.println("今天又是元气满满的一天");
		}
	}

 PI就是一个替换,把PI在计算的时候换成Math.PI就行了,因为我用哈希表的初衷就是希望在程序中字符串是用一个的字符表示,所以PI我是在计算的时候才进行展开。

    //展开PI
	public void pi() {
		for(int i=0;i<sb.length();i++) {
			if(sb.charAt(i)=='P') {
				double res=Math.PI;
				String resString=res+"";
				//更新字符串
				sb=sb.replace(i, i+1, resString);
				i=resString.length()-1;
				continue;
			}
		}
	}

 这些我不是用什么名字,就用这个表示了,这些的特点就是只需要一个操作数即可。

所以只需要往右边找操作数,统计操作数的长度,把找到的数进行相应的修改和替换。

问题9:sin和tan不好用,用PI直接报错了。

解决:这个弧度制好像是越界了,我又没加括号,那还是Math.toRadians(num)输入角度制转成弧度制来运算吧。

//ln,lg,sin,cos,tan,开根号的运算
	public void special() {
		//都是用右边一个操作数的运算
		for(int i=0;i<sb.length();i++) {
			if(sb.charAt(i)=='N'||sb.charAt(i)=='G'||
					sb.charAt(i)=='S'||sb.charAt(i)=='C'||
					sb.charAt(i)=='T'||sb.charAt(i)=='D') {
				double num=0;
				int len=0;//记录字符串长度,之后还要进行字符串的替换呢
				//只需要应该一边的数字即可
				for(int j=i+1;j<sb.length();j++) {
					//是j不是i,咋又错在这里了
					if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
							sb.charAt(j)=='%'||sb.charAt(j)=='B'||
							sb.charAt(j)=='+'||sb.charAt(j)=='-'||
							sb.charAt(j)=='A'||
							sb.charAt(j)=='N'||sb.charAt(j)=='G'||
							sb.charAt(j)=='S'||sb.charAt(j)=='C'||
							sb.charAt(j)=='T'||sb.charAt(j)=='D') {
						String s1=sb.substring(i+1,j);
						num=Double.parseDouble(s1);
						len=s1.length();
						break;
					}
					//找到最右边咯,中间没有运算符那也要停止了
					if(j==sb.length()-1) {
						//这边是到j+1哈,找错误找了半天
						//如果右边是一位的数字,就会导致运算是数是空
						//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)
						String s1=sb.substring(i+1,j+1);
						num=Double.parseDouble(s1);
						len=s1.length();
						break;
					}
				}
				//进行运算
				//ln运算
				if(sb.charAt(i)=='N') {
					double res=Math.log(num);
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//lg运算
				if(sb.charAt(i)=='G') {
					//换底公式
					double res=Math.log(num)/Math.log(10);
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//sin运算
				if(sb.charAt(i)=='S') {
					//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
					//偷懒一下就用角度值吧,不用加入括号
					double res=Math.sin(Math.toRadians(num));
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//cos运算
				if(sb.charAt(i)=='C') {
					//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
					//偷懒一下就用角度值吧,不用加入括号
					double res=Math.cos(Math.toRadians(num));
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//tan运算
				if(sb.charAt(i)=='T') {
					//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
					//偷懒一下就用角度值吧,不用加入括号
					double res=Math.tan(Math.toRadians(num));
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//开根号运算
				if(sb.charAt(i)=='D') {
					double res=Math.sqrt(num);
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
			}
		}
	}

接下来就是两个操作数的啦。以乘方为例。

往左边和右边寻找操作数,左边的话需要考虑优先级低的操作字符以及字符串越界的情况,但是右边不仅是需要考虑这个还需要考虑本身这个字符。

就是往左边判断是(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-'),而右边判断是(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-'||sb.charAt(j)=='A'),这也要理解吧,因为字符串吧遍历是从左往右,右边还有这个‘A’没有判断,但是左边已经判断结束了。

    //乘方运算
	public void power(){
		for(int i=0;i<sb.length();i++) {
			if(sb.charAt(i)=='A') {
				double num1=0,num2=0;
				int len1=0,len2=0;//记录字符串长度,之后还要进行字符串的替换呢
				for(int j=i-1;j>=0;j--) {
					//得到第一个操作数,遇到加减乘除就停止了,相当于分隔了操作数
					if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
							sb.charAt(j)=='%'||sb.charAt(j)=='B'||
							sb.charAt(j)=='+'||sb.charAt(j)=='-') {
						String s1=sb.substring(j+1,i);
						num1=Double.parseDouble(s1);
						len1=s1.length();
						break;
					}
					//找到边界了,只能停止了咯
					if(j==0) {
						String s1=sb.substring(j,i);
						num1=Double.parseDouble(s1);
						len1=s1.length();
						break;
					}
				}
				//往右边找第二个操作数,第二个操作数的话遇到乘除也需要提前终止,第一个不需要
				//因为如果第一个操作数有乘除这种符号,就已经提前终止了,只有第二个操作数才需要考虑
				for(int j=i+1;j<sb.length();j++) {
					if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
							sb.charAt(j)=='%'||sb.charAt(j)=='B'||
							sb.charAt(j)=='+'||sb.charAt(j)=='-'||sb.charAt(j)=='A') {
						String s1=sb.substring(i+1,j);
						num2=Double.parseDouble(s1);
						len2=s1.length();
						break;
					}
					//找到最右边咯,中间没有运算符那也要停止了
					if(j==sb.length()-1) {
						//这边是到j+1哈,找错误找了半天
						//如果右边是一位的数字,就会导致运算是数是空
						//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)
						String s1=sb.substring(i+1,j+1);
						num2=Double.parseDouble(s1);
						len2=s1.length();
						break;
					}
				}
				//进行运算
				double res=Math.pow(num1, num2);
				String resString=res+"";
				//更新字符串
				sb=sb.replace(i-len1, i+len2+1, resString);
				i=i-len1-1+resString.length();
				continue;
			}
		}
	}

加减乘除就和这个一样了。

问题10:会出现多个异常抛出的情况。

解答:循环里面操作数的判断,里面是sb.charAt(j)写成了sb.charAt(i);第二种就是在第二个操作数边界判断的时候String s1=sb.substring(i+1,j+1);写成了String s1=sb.substring(i+1,j);这也是直接复制前面的结果,因为之前停止是遇到运算符停止的,所以边界判断不需要考虑到j+1,但是这已经到底了,指针指向的就是数,如果右边是一位的数字,就会导致运算是数是空,如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)。

运算解决了,就是对异常的处理,之前的代码就有显示我加入了抛出异常的地方,如果错误是会出现一个弹窗。我还用finally来检验程序的健壮性。本来是对除数为0的情况加上了一个判断,因为是浮点数所以为0的判断条件设置成Math.abs(num2)<=1e-6,但是他没反应了,我干脆就直接不判断了这也算是异常,直接抛出弹出弹窗就行了。这还是方便一点。

注:这里有一个bug,因为操作数我最开始是设置成0,就导致如果只有一个数的时候也可以运算,比如“*1”是可以运行的,因为num1等于0,这个大家感兴趣可以自己完善一下哈

	//警告标签,放在弹窗里面
	final JLabel warningLabel=new JLabel();

    //警告,输入不合法的时候弹出
	public void warning() {
		JDialog jDialog=new JDialog();//创建弹窗对象
		jDialog.setTitle("警告");//设置弹窗标题,和Frame差不多,可能还要通过标签来提示
		jDialog.setSize(500,600);//设置弹窗的大小
		jDialog.setAlwaysOnTop(true);//让弹窗置顶
		jDialog.setLocationRelativeTo(null);//让弹窗居中
		jDialog.setModal(true);//弹窗不关闭则无法操作下面的界面
		
		//设置字体的类型,加粗,和大小
		warningLabel.setFont(new Font("Microsoft YaHei",Font.BOLD,30));
		//输出警告提示符
		warningLabel.setText("就是你小子在搞乱的是吧!!!");
		//标签的位置和大小
		warningLabel.setBounds(60,180,500,100);
		
		//这个也要取消布局管理器才行
		jDialog.getContentPane().setLayout(null);
		//往弹窗中添加标签
		jDialog.getContentPane().add(warningLabel);
		jDialog.setVisible(true);//让弹窗显示出来
		
	}

 警告的弹窗和内容都是自己设计的,弹窗就和框架一样,里面也是一个面板,我取消了布局,通过x,y坐标的方法进行移动。弹窗弹出如下所示。

问题11:在打印按钮的时候出现数组越界

因为整个数组不是矩阵,所以内存循环和外循环不是同一个边界值,内层循环的界限是用外层循环对应一维数组的长度作为边界值.

详细代码如下:



import java.awt.*;
import java.awt.event.*;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;


public class CalculatorFrame extends JFrame implements ActionListener{
	//主方法
	public static void main(String[] args) {
		CalculatorFrame cf=new CalculatorFrame();
	}
	//创建显示器面板,采用默认的流布局
	final JPanel viewPanel =new JPanel();
	//创建显示器
	final JTextField textField=new JTextField();
	//创建按钮面板
	final JPanel buttonPanel=new JPanel();
	//创建网络布局管理器对象
	final GridLayout gridLayout=new GridLayout(0,4);
	//按钮里面的内容
	String [][]names= {
			{"**","ln","lg","clear"},
			{"sin","cos","tan","X"},
			{"PI","//","%","/"},
			{"7","8","9","*"},
			{"4","5","6","-"},
			{"1","2","3","+"},
			{"_/``","0",".","="}};
	//程序里面的内容
	String [][]target= {
			{"A","N","G","R"},
			{"S","C","T","X"},
			{"P","B","%","/"},
			{"7","8","9","*"},
			{"4","5","6","-"},
			{"1","2","3","+"},
			{"D","0",".","="}};
	//创建按钮对象
	JButton[][] buttons=new JButton[names.length][4];
	//创建左侧的占位标签
	final JLabel leftLabel=new JLabel();
	//创建右侧的占位标签
	final JLabel rightLabel=new JLabel();
	//创建下侧的占位标签
	final JLabel bottomLabel=new JLabel();
	//存储计算结果
	double result=0;
	//用来更新文本框输出的字符串
	StringBuffer sb=new StringBuffer();
	//用来对应文本框的输出,还是选择用HashMap来存储
	Map<String ,String>map=new HashMap<String,String>();
	//输出的字符串放这个里面,每次使用都需要清零哈
	String output="";
	//警告标签,放在弹窗里面
	final JLabel warningLabel=new JLabel();
	
	public CalculatorFrame(){
		//初始化窗体
		initFrame();
		//初始化面板
		initPanel();
		//初始化哈希表里面的数据
		Stringbind();
		//绑定事件.键盘绑定也放在里面
		buttonAction();
		//窗体可见,放最后吧,不然里面的东西不会显示呢
		this.setVisible(true);
	}
	//初始化窗体
	public void initFrame() {
		//设置窗体的标题
		this.setTitle("计算器");
		//设置窗体大小不可改变
		this.setResizable(false);
		//设置界面置顶(就是页面不会别其他页面覆盖,界面始终在最上面)
        this.setAlwaysOnTop(true);
        //设置窗体的位置和大小,位置应该失效了,因为设置了居中
		//this.setBounds(300,150,400,500);
        //那还是改成setSize吧,设置窗体的大小就行了
        this.setSize(400,500);
		//居中
		this.setLocationRelativeTo(null);
		//设置窗体关闭按钮的动作作为退出
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//将显示器面板添加到窗体顶部
		this.getContentPane().add(viewPanel,BorderLayout.NORTH);
		//添加屏幕焦点,没有这个用键盘输入没反应埃
		this.setFocusable(true);
	}
	//初始化面板
	public void initPanel() {
		//初始化组件
		initModule();
		
		viewPanel.add(textField);
		viewPanel.setPreferredSize(new Dimension(100,80));
		this.getContentPane().add(viewPanel,BorderLayout.NORTH);
		
		
		buttonPanel.setLayout(gridLayout);//按钮面板采用网络布局
		this.getContentPane().add(buttonPanel,BorderLayout.CENTER);//将按钮面板添加到窗体中间
		//把按钮添加到按钮面板中,虽然可以和初始化按钮写一起,但是便于理解还是把他们分开写了
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				buttonPanel.add(buttons[row][col]);
			}
		}
		this.getContentPane().add(leftLabel,BorderLayout.WEST);
		this.getContentPane().add(rightLabel,BorderLayout.EAST);
		this.getContentPane().add(bottomLabel,BorderLayout.SOUTH);
	}
	
	//初始化组件
	public void initModule() {
		//初始化显示器相关数据
		textField.setEditable(false);//设置显示器不可编辑
		textField.setHorizontalAlignment(SwingConstants.RIGHT);
		textField.setColumns(35);//调节文本框的宽度
		textField.setPreferredSize(new Dimension(500,40));
	
		//初始化面板按钮
		gridLayout.setVgap(10);//设置组件的水平间距
		gridLayout.setHgap(10);//设置组件的垂直间距
		//初始化按钮对象
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				buttons[row][col]=new JButton(names[row][col]);//创建按钮
			}
		}
		//静态的初始化设置一些,把多个单词的给标识起来
		/*buttons[0][0].setActionCommand("A");//"**"
		buttons[0][1].setActionCommand("N");//"ln"
		buttons[0][2].setActionCommand("G");//"lg"
		buttons[0][3].setActionCommand("R");//clear
		buttons[1][0].setActionCommand("S");//sin
		buttons[1][1].setActionCommand("C");//cos
		buttons[1][2].setActionCommand("T");//tan
		buttons[2][0].setActionCommand("P");//PT
		buttons[2][1].setActionCommand("B");//"//"
		buttons[6][0].setActionCommand("D");//"+/-"
		*/
		//还是用循环全部绑定吧,自己一个一个绑代码灵活性不高
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				buttons[row][col].setActionCommand(target[row][col]);
			}
		}
		
		//设置左侧标签的宽度
		leftLabel.setPreferredSize(new Dimension(10,0));
		//设置右侧标签的宽度
		rightLabel.setPreferredSize(new Dimension(10,0));
		//设置底部标签的宽度,组件的有高度,可以没宽度,和两边相反
		bottomLabel.setPreferredSize(new Dimension(0,10));
	}
	//通过哈希表实现输出字符串和里面的字符串的不同标识
	public void Stringbind() {
		
		//map.put(getWarningString(), getName())
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				map.put(target[row][col], names[row][col]);
				//System.out.println("执行成功");
			}
		}
		//用来判断值有没有填写进去
		//System.out.println(map.size());
	}
	
	public void buttonAction() {
		//按钮绑定动作事件,和自己绑定,自己实现的监听的方法
		for(int row=0;row<names.length;row++) {
			for(int col=0;col<names[row].length;col++) {
				(buttons[row][col]).addActionListener(this);
			}
		}	
		//给整个界面添加键盘监听事件
        this.addKeyListener(new KeyListener() {
        	
        	@Override
        	public void keyTyped(KeyEvent e) {
        		// TODO Auto-generated method stub
        		
        	}
        	//按着按键不松时调用
        	@Override
        	public void keyPressed(KeyEvent e) {
        		// TODO Auto-generated method stub
        	}
        	//按键松开后执行
        	@Override
        	public void keyReleased(KeyEvent e) {
        		// TODO Auto-generated method stub
        		//System.out.println("我被调用了啦");
        		int code = e.getKeyCode();
        		//输出每次按下的键盘按钮对应的code
        		//System.out.println(code);
        		//用键盘添加数字
        		//单纯输入数字,shift键没有被按下,不然就加减乘除无法被响应了
        		if(code>=48 && code<=57 && !e.isShiftDown()) {
        			sb.append((char)code);
        			//outPut();
        		}else if(code==56 && e.isShiftDown()) {
        			sb.append("*");
        			//outPut();
        		}else if(code==47 && !e.isShiftDown()) {
        			sb.append("/");
        		}else if(code==8) {//Backspace键
        			//删除最后的一个字符
        			sb.deleteCharAt(sb.length()-1);
        		}else if(code==53 && e.isShiftDown()) {
        			sb.append("%");
        		}else if(code==61 && e.isShiftDown()) {
        			sb.append("+");
        		}else if(code==61 && !e.isShiftDown()) {//"="
        			//计算结果
        			result();
        		}else if(code==45 && !e.isShiftDown()) {
        			sb.append("-");
        		}else if(code==46 && !e.isShiftDown()) {
        			sb.append(".");
        		}else if(code==10) {//Enter键
        			//计算结果
        			result();
        		}
        		//每次键盘输入之后都要更新,所以干脆就直接放判断最后
        		outPut();
        		//"="和"Enter"键
        		if(code==61 && !e.isShiftDown()||code==10) {
        			//就是把[0,length)的内容删除即可
        			sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个
        		}
        	}
        });
	}
	//重写鼠标点击事件
	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		JButton button =(JButton)e.getSource();//获得触发此次动作事件的按钮对象
		String buttonName =e.getActionCommand();//获得触发此次动作事件的按钮的标签文本
		
	
		if(buttonName.equals("X")) {
			//没有字符串之后就不能删除了
			if(sb.length()!=0) {
				sb.deleteCharAt(sb.length()-1);
				//textField.setText(output);//删除之后还需要立即显示一次,不然没有反应
			}
		}else if(buttonName.equals("R")){
			//就是把[0,length)的内容删除即可
			sb.delete(0, sb.length());
			//删除之后还需要立即显示一次,不然没有反应
			//textField.setText(output);
		}else if(buttonName.equals("=")) {
			//计算结果
			result();
		}else {
			sb.append(buttonName);
			//textField.setText(output);
		}
		//反正每次响应事件之后都要更新,干脆直接放在最后
		outPut();
		//要重新使框架获得焦点,这要写呢,不写就按下按钮之后键盘就没反应了
		if(buttonName.equals("=")) {
			//就是把[0,length)的内容删除即可
			sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个
		}
		this.requestFocus();
	}
	//记录每次将输出的字符串
	public void outPut() {
		output="";
		for(int i=0;i<sb.length();i++) {
		//这我可能知道原因了,字符串"A"和'A'不一样
			output= output+map.get(String.valueOf(sb.charAt(i)));
		}
		//每次更新都要输出,我直接写到方法里面算了
		textField.setText(output);
	}
	//计算每次的结果
	public void result() {
		
		//对应关系
		//**--->A  ln--->N  lg--->G  sin--->S   cos--->C
		//tan--->T  PI--->P  //--->B  /``--->D
		//计算是按照优先级来的
		//PI不算运算,直接填进去吧
		//也不行,本来就是用一个符号来表示,这突然变成一长串数字,那还是运算的时候展开吧
		//展开PI
		try {
			pi();
			//ln,lg,sin,cos,tan,开根号的运算
			special();
			//乘方运算
			power();
			//乘除,整除,求余运算
			multiplyDivide();
			//加减计算
			addAndSubtract();
		}catch(Exception e) {
			//弹出警告的弹窗
			warning();
		}finally {
			System.out.println("今天又是元气满满的一天");
		}
	}
	//展开PI
	public void pi() {
		for(int i=0;i<sb.length();i++) {
			if(sb.charAt(i)=='P') {
				double res=Math.PI;
				String resString=res+"";
				//更新字符串
				sb=sb.replace(i, i+1, resString);
				i=resString.length()-1;
				continue;
			}
		}
	}
	//ln,lg,sin,cos,tan,开根号的运算
	public void special() {
		//都是用右边一个操作数的运算
		for(int i=0;i<sb.length();i++) {
			if(sb.charAt(i)=='N'||sb.charAt(i)=='G'||
					sb.charAt(i)=='S'||sb.charAt(i)=='C'||
					sb.charAt(i)=='T'||sb.charAt(i)=='D') {
				double num=0;
				int len=0;//记录字符串长度,之后还要进行字符串的替换呢
				//只需要应该一边的数字即可
				for(int j=i+1;j<sb.length();j++) {
					//是j不是i,咋又错在这里了
					if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
							sb.charAt(j)=='%'||sb.charAt(j)=='B'||
							sb.charAt(j)=='+'||sb.charAt(j)=='-'||
							sb.charAt(j)=='A'||
							sb.charAt(j)=='N'||sb.charAt(j)=='G'||
							sb.charAt(j)=='S'||sb.charAt(j)=='C'||
							sb.charAt(j)=='T'||sb.charAt(j)=='D') {
						String s1=sb.substring(i+1,j);
						num=Double.parseDouble(s1);
						len=s1.length();
						break;
					}
					//找到最右边咯,中间没有运算符那也要停止了
					if(j==sb.length()-1) {
						//这边是到j+1哈,找错误找了半天
						//如果右边是一位的数字,就会导致运算是数是空
						//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)
						String s1=sb.substring(i+1,j+1);
						num=Double.parseDouble(s1);
						len=s1.length();
						break;
					}
				}
				//进行运算
				//ln运算
				if(sb.charAt(i)=='N') {
					double res=Math.log(num);
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//lg运算
				if(sb.charAt(i)=='G') {
					//换底公式
					double res=Math.log(num)/Math.log(10);
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//sin运算
				if(sb.charAt(i)=='S') {
					//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
					//偷懒一下就用角度值吧,不用加入括号
					double res=Math.sin(Math.toRadians(num));
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//cos运算
				if(sb.charAt(i)=='C') {
					//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
					//偷懒一下就用角度值吧,不用加入括号
					double res=Math.cos(Math.toRadians(num));
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//tan运算
				if(sb.charAt(i)=='T') {
					//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制
					//偷懒一下就用角度值吧,不用加入括号
					double res=Math.tan(Math.toRadians(num));
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
				//开根号运算
				if(sb.charAt(i)=='D') {
					double res=Math.sqrt(num);
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i, i+len+1, resString);
					i=resString.length()-1;
					continue;
				}
			}
		}
	}
	//乘方运算
	public void power(){
		for(int i=0;i<sb.length();i++) {
			if(sb.charAt(i)=='A') {
				double num1=0,num2=0;
				int len1=0,len2=0;//记录字符串长度,之后还要进行字符串的替换呢
				for(int j=i-1;j>=0;j--) {
					//得到第一个操作数,遇到加减乘除就停止了,相当于分隔了操作数
					if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
							sb.charAt(j)=='%'||sb.charAt(j)=='B'||
							sb.charAt(j)=='+'||sb.charAt(j)=='-') {
						String s1=sb.substring(j+1,i);
						num1=Double.parseDouble(s1);
						len1=s1.length();
						break;
					}
					//找到边界了,只能停止了咯
					if(j==0) {
						String s1=sb.substring(j,i);
						num1=Double.parseDouble(s1);
						len1=s1.length();
						break;
					}
				}
				//往右边找第二个操作数,第二个操作数的话遇到乘除也需要提前终止,第一个不需要
				//因为如果第一个操作数有乘除这种符号,就已经提前终止了,只有第二个操作数才需要考虑
				for(int j=i+1;j<sb.length();j++) {
					if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
							sb.charAt(j)=='%'||sb.charAt(j)=='B'||
							sb.charAt(j)=='+'||sb.charAt(j)=='-'||sb.charAt(j)=='A') {
						String s1=sb.substring(i+1,j);
						num2=Double.parseDouble(s1);
						len2=s1.length();
						break;
					}
					//找到最右边咯,中间没有运算符那也要停止了
					if(j==sb.length()-1) {
						//这边是到j+1哈,找错误找了半天
						//如果右边是一位的数字,就会导致运算是数是空
						//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)
						String s1=sb.substring(i+1,j+1);
						num2=Double.parseDouble(s1);
						len2=s1.length();
						break;
					}
				}
				//进行运算
				double res=Math.pow(num1, num2);
				String resString=res+"";
				//更新字符串
				sb=sb.replace(i-len1, i+len2+1, resString);
				i=i-len1-1+resString.length();
				continue;
			}
		}
	}
	//乘除,整除,求余运算
	public void multiplyDivide() {
		for(int i=0;i<sb.length();i++) {
			if(sb.charAt(i)=='*'||sb.charAt(i)=='/'||
					sb.charAt(i)=='%'||sb.charAt(i)=='B') {
				double num1=0,num2=0;
				int len1=0,len2=0;//记录字符串长度,之后还要进行字符串的替换呢
				for(int j=i-1;j>=0;j--) {
					//得到第一个操作数,遇到加减就停止了,相当于分隔了操作数
					if(sb.charAt(j)=='+'||sb.charAt(j)=='-') {
						String s1=sb.substring(j+1,i);
						num1=Double.parseDouble(s1);
						len1=s1.length();
						break;
					}
					//找到边界了,只能停止了咯
					if(j==0) {
						String s1=sb.substring(j,i);
						num1=Double.parseDouble(s1);
						len1=s1.length();
						break;
					}
				}
				//往右边找第二个操作数,第二个操作数的话遇到乘除也需要提前终止,第一个不需要
				//因为如果第一个操作数有乘除这种符号,就已经提前终止了,只有第二个操作数才需要考虑
				for(int j=i+1;j<sb.length();j++) {
					if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||
							sb.charAt(j)=='%'||sb.charAt(j)=='B'||
							sb.charAt(j)=='+'||sb.charAt(j)=='-') {
						String s1=sb.substring(i+1,j);
						num2=Double.parseDouble(s1);
						len2=s1.length();
						break;
					}
					//找到最右边咯,中间没有运算符那也要停止了
					if(j==sb.length()-1) {
						//这边是到j+1哈,找错误找了半天
						//如果右边是一位的数字,就会导致运算是数是空
						//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)
						String s1=sb.substring(i+1,j+1);
						num2=Double.parseDouble(s1);
						len2=s1.length();
						break;
					}
				}
				//进行运算
				if(sb.charAt(i)=='*') {
					double res=num1*num2;
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i-len1, i+len2+1, resString);
					i=i-len1-1+resString.length();
					continue;
				}
				if(sb.charAt(i)=='/') {
					//除数不能为0,有异常抛出的弹窗了,就去掉这个了
					/*if(Math.abs(num2)<=1e-6) {
						System.out.println("输出警告");
						continue;
					}*/
					double res=num1/num2;
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i-len1, i+len2+1, resString);
					i=i-len1-1+resString.length();
					continue;
				}
				if(sb.charAt(i)=='%') {
					//整数才能求余,第二个操作数也不能为0
					/*if(Math.abs(num2)<=1e-6) {
						System.out.println("输出警告");
						continue;
					}*/
					//是求余%
					double res=(int)num1%(int)num2;
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i-len1, i+len2+1, resString);
					i=i-len1-1+resString.length();
					continue;
				}
				if(sb.charAt(i)=='B') {
					//整数的运算
					//除数不能为0
					/*if(Math.abs(num2)<=1e-6) {
						System.out.println("输出警告");
						continue;
					}*/
					double res=(int)num1/(int)num2;
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i-len1, i+len2+1, resString);
					i=i-len1-1+resString.length();
					continue;
				}
			}
		}
	}
	//加减运算
	public void addAndSubtract() {
		for(int i=0;i<sb.length();i++) {
			if(sb.charAt(i)=='+'||sb.charAt(i)=='-') {
				//运算符两边的操作数
				double num1=0,num2=0;
				//记录两个操作数的字符串长度
				int len1=0,len2=0;
				//寻找第一个操作数
				for(int j=i-1;j>=0;j--) {
					if(j==0) {
						String s1=sb.substring(j,i);
						//得到第一个操作数
						num1=Double.parseDouble(s1);
						len1=s1.length();
						break;
					}
				}
				for(int j=i+1;j<sb.length();j++) {
					if(sb.charAt(j)=='+'||sb.charAt(j)=='-') {
						String s1=sb.substring(i+1,j);
						//得到第二个操作数
						num2=Double.parseDouble(s1);
						len2=s1.length();
						break;
					}
					if(j==sb.length()-1) {
						String s1=sb.substring(i+1,j+1);
						num2=Double.parseDouble(s1);
						len2=s1.length();
						break;
					}
				}
				//进行加运算
				if(sb.charAt(i)=='+') {
					double res=num1+num2;
					//String s2=sb.substring(i-len1,i+len2+1);
					//String s3=res+ "";
					//sb=sb.replace(s2, s3);
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i-len1, i+len2+1, resString);
					
					i=i-len1-1+resString.length();
					continue;
				}
				//进行减运算
				if(sb.charAt(i)=='-') {
					double res=num1-num2;
					String resString=res+"";
					//更新字符串
					sb=sb.replace(i-len1, i+len2+1, resString);
					i=i-len1-1+resString.length();
					continue;
				}
			}
		}
	}
	//警告,输入不合法的时候弹出
	public void warning() {
		JDialog jDialog=new JDialog();//创建弹窗对象
		jDialog.setTitle("警告");//设置弹窗标题,和Frame差不多,可能还要通过标签来提示
		jDialog.setSize(500,600);//设置弹窗的大小
		jDialog.setAlwaysOnTop(true);//让弹窗置顶
		jDialog.setLocationRelativeTo(null);//让弹窗居中
		jDialog.setModal(true);//弹窗不关闭则无法操作下面的界面
		
		//设置字体的类型,加粗,和大小
		warningLabel.setFont(new Font("Microsoft YaHei",Font.BOLD,30));
		//输出警告提示符
		warningLabel.setText("就是你小子在搞乱的是吧!!!");
		//标签的位置和大小
		warningLabel.setBounds(60,180,500,100);
		
		//这个也要取消布局管理器才行
		jDialog.getContentPane().setLayout(null);
		//往弹窗中添加标签
		jDialog.getContentPane().add(warningLabel);
		jDialog.setVisible(true);//让弹窗显示出来
		
	}
}

 总结:

这写下来还是写了挺长的时间了,目前还是比较满意的,因为是刚学JAVA语言,所以有很多地方都没太弄懂,有错误的地方,希望大家能够指正。对于计算器的功能编写其实还是能够添加很多功能也可以把Math.E添加进去,进行其他运算,大家可以根据自己的需求进行编写。写这个我的感受就是就是把我学的东西都用到了哈希表,异常抛出,控件都用上了,我对于他有一个比较清晰的认识,他要实现什么功能,虽然看着简单,但是卡在了很多地方,还有到处找资料来分析他的原因,尽管是有点简陋,我还加入了一些自己的东西,按下Backspace键和Enter键也有对应的响应,这也是努力的结果,希望未来越来越好吧。

6月14日,给按钮添加颜色

这个的话看自己想怎么弄,如果有规律的话可以尝试用循环,不然就需要一个个的调整,建议这个添加到initModule方法里面,这个是我专门用来设计组件信息的方法。(下面是一个示范的案例)

buttons[0][0].setBackground(Color.red);//给第一个按钮添加红色背景的按钮

6月15日,调节按钮中字体的大小。

在创建按钮的时候,给字体统一设置。对原始程序中的修改如下。

 //初始化按钮对象
 for(int row=0;row<names.length;row++) {
     for(int col=0;col<names[row].length;col++) {
        buttons[row][col]=new JButton(names[row][col]);//创建按钮
        //设置字体的类型,是否加粗和字体的大小
        buttons[row][col].setFont(new Font("Microsoft YaHei",Font.BOLD,20));
      }
 }

 

  • 36
    点赞
  • 201
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
java计算器,能实现加减乘除。 //***该梦幻计算器与Windows附件自带计算器的标准版功能、界面相仿***// //******但还不支持键盘操作,如有需要,下一次等级将满足你**********// import java.awt.*; import java.lang.*; import javax.swing.*; import javax.swing.event.*; import java.awt.event.*; import java.awt.Component.*; import java.awt.color.*; import java.text.DecimalFormat; //*****************************************************************************// //*****************************************************************************// public class Calculator implements ActionListener //导入动作监听接口 { //******************************// //*****设计计算器界面的单位*****// JFrame frame; //定义框架 JTextField textAnswer; //定义输出显示框 JPanel panel, panel1, panel2, panel3;//定义面板作为子容器,从而达到要求的界面效果 JMenuBar mainMenu; JTextField textMemory; JLabel labelMemSpace; //labelMemSpace单纯做摆设,控制面板的形状 JButton buttonBk, buttonCe, buttonC; JButton button[]; JButton buttonMC, buttonMR, buttonMS, buttonMAdd; JButton buttonDot, buttonAddAndSub, buttonAdd, buttonSub, buttonMul,buttonDiv, buttonMod; JButton buttonSqrt, buttonDao, buttonEqual; JMenu editMenu, viewMenu, helpMenu; JMenuItem copyItem, pasteItem, tItem, sItem, numberGroup, topHelp, aboutCal,myCollege; DecimalFormat df; //设置数据输出精度 boolean clickable; //控制当前能否按键 double memoryd; //使用内存中存储的数字 int memoryi; double vard, answerd; //用来保存double型数据的中间值(vard)和最后结果(answerd) short key = -1, prekey = -1; //key用来保存当前进行何种运算,prekey用来保存前次进行何种运算 boolean clear = false; String copy; //做复制用 JTextArea help; //帮助 JScrollPane scrollHelp; static boolean One =true; //******************// //*****构造函数开始*****// public Calculator() { clickable = true; answerd = 0; frame = new JFrame("梦幻计算器--SWE08008"); frame.setResizable(false);//不允许改变计算器的大小 frame.setLocation(250,150);//设置计算器的起始位置 frame.setBackground(Color.YELLOW); df = new DecimalFormat("0.##############"); //设置数据输出精度(对于double型值)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

封奚泽优

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值