关于Java第一次实验的对课后问题自己的理解--验证码实现及其四则运算
问题一、对于课上ppt中EnumTest所提出的的问题进行解答
将这段代码放到文件中进行运行后发现
1.对应的Size中不同元素的并不是同一个对象
2.以其中一个枚举类型s来说,不是原始数据,即他们都不是原始数据,都是属于引用类型
3.用枚举类型对字符串SMALL进行转换得到值的枚举类型s是同一数据,也就说明了转换得到的对象和直接引用Size的SMALL两个对象相等。
4.说明了枚举类型可以直接用java里面的标识符进行遍历,这应该是一种能在以后更加方便的写法,更加和之前在QT中写贪吃蛇的时候用到的foreach有点类似
总结:通过上述的四个问题的分析,基本上已经能够掌握java中的枚举类型的使用,感觉和C++的枚举差异并不是太大,这还是要通过具体的才能得知。
问题二、TestDouble中出现的小数相加的问题
代码如图:
这是testDouble的运行截图,
这是一个令人意外的结果,java中的浮点数和C++的输出不同,记得在C++中的双精度浮点数在相加成为整数的时候显示的还是相对较为精确的值,例如第一个结果在C++中是0.06,可是在这里为什么最后还是会出现感觉和结果不相符的值呢。
首先提出疑问,为什么以精确性著称的双精度浮点类型会出现精度方面的问题,首先,我数了数不精确的那一位所在小数点后的位数,
第一个式子相加后小数点的位数是19位,其中,出现0的位数18(我将其当做较为精确的位数);
第二个式子小数点后0的位数恰好为15位,1在16位的位置;也就是较为精确的位置为15位。
第三个式子较为精确的位数到13位。
第四类比之。
然后我发现了一个规律,显示的位数自左向右说,从第一个不为0的位数开始,依次显示17位,也就是显示到17位,而第17位是不精确的位数。同时貌似在java中,double类型精确的位数应该是16位,相对于c++似乎有些不同。
这是还未上网查询结果对于这个问题的分析,接下来查询网上对这个问题的分析。
首先在csdn中DZY_LUSTER的一篇名字为double类型的相加问题的一篇博客中看到了:
多个double类型的数直接相加的时候,可能存在精度误差.( 由于计算机算法以及硬件环境决定只能识别 0 1。计算机默认的计算结果在都在一个指定精度范围之内,想往深的了解,可以学习数值分析等) 在金融方面是绝对不允许的,好在java开发者有这个先见之明。 java.math.*里面提供了BigDecimal类(提供高精度计算的方法)
这里就可以知道对于这个问题java中已经提供了解决问题的方案。
接下来对博客园中xujishou的java中double和float精度丢失问题的这篇文章进行引用。
以下是作者的原文:
首先我们要搞清楚下面两个问题:
(1) 十进制整数如何转化为二进制数
算法很简单。举个例子,11表示成二进制数:
11/2=5 余 1
5/2=2 余 1
2/2=1 余 0
1/2=0 余 1
0结束 11二进制表示为(从下往上):1011
这里提一点:只要遇到除以后的结果为0了就结束了,大家想一想,所有的整数除以2是不是一定能够最终得到0。换句话说,所有的整数转变为二进制数的算法会不会无限循环下去呢?绝对不会,整数永远可以用二进制精确表示 ,但小数就不一定了。
(2) 十进制小数如何转化为二进制数
算法是乘以2直到没有了小数为止。举个例子,0.9表示成二进制数
0.9*2=1.8 取整数部分 1
0.8(1.8的小数部分)*2=1.6 取整数部分 1
0.6*2=1.2 取整数部分 1
0.2*2=0.4 取整数部分 0
0.4*2=0.8 取整数部分 0
0.8*2=1.6 取整数部分 1
0.6*2=1.2 取整数部分 0
......... 0.9二进制表示为(从上往下): 1100100100100......
注意:上面的计算过程循环了,也就是说*2永远不可能消灭小数部分,这样算法将无限下去。很显然,小数的二进制表示有时是不可能精确的 。其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了"减不尽"的精度丢失问题。
三、关于课堂上布置的两个实验报告的实现
一、 编写一个程序,写一个能自动生成30道小学四则运算题目的 “软件”。
实验要求:
1) 按照题目内容要求编写程序实现功能。
2) 实验报告中要求包括程序设计思想、程序流程图、源程序、实现结果截图、实验总结(包括调试过程中出现的错误等)。
首先是二三年级四则运算的实现思想:
根据查找,小学二年级的四则运算涉及的符号只有加减,运算的过程都是在正整数范围内,况且其中的加减的过程一般都是在1-100之间出现,再者就是一般运算的量在1次到2次之间,所以其设计的思想是生成随机数,判断1-2次运算的量。然后每次生成随机数,1代表加,2代表减号,如果生成的是减号,在判断两数相减是不是大于0,小于0的话循环到大于0为止。
二年级四则运算源程序的代码如下所示:
package sizeMath; public class SzMath { private static String szM[]=new String[]{"+","-"}; public static void main(String[] args) { // TODO 自动生成的方法存根 int numa=0,numb=0,numc=0; int fuhao_1=0,fuhao_2=0; for(int i=0;i<30;i++) { if(((int)(Math.random()*2+1))==2) { if((fuhao_1=(int)(Math.random()*2+1))==2) { numa=(int)(1+Math.random()*100); numb=(int)(1+Math.random()*100); while(numa<=numb) { numa=(int)(1+Math.random()*100); numb=(int)(1+Math.random()*100); } } else { numa=(int)(1+Math.random()*100); numb=(int)(1+Math.random()*100); } if((fuhao_2=(int)(Math.random()*2+1))==2) { numc=(int)(1+Math.random()*100); if(fuhao_1==2) while(numa-numb<numc) { numc=(int)(1+Math.random()*100); } else { while(numa+numb<numc) { numc=(int)(1+Math.random()*100); } } } else { numc=(int)(1+Math.random()*100); } System.out.println(numa+szM[fuhao_1-1]+numb+szM[fuhao_2-1]+numc+"="); } else { if((fuhao_1=(int)(Math.random()*2+1))==2) { numa=(int)(1+Math.random()*100); numb=(int)(1+Math.random()*100); while(numa<=numb) { numa=(int)(1+Math.random()*100); numb=(int)(1+Math.random()*100); } } else { numa=(int)(1+Math.random()*100); numb=(int)(1+Math.random()*100); } System.out.println(numa+szM[fuhao_1-1]+numb+"="); } } } }
三年级四则运算代码如下:
package sszMath; public class SszMath { //出现运算项的最高个数 public static final int SXSIZE=9; //出多少道题目 public static final int TIMES=30; public static void main(String[] args) { // TODO 自动生成的方法存根 int times=(int)(Math.random()*(SXSIZE-1)+1); String szM[]=new String[]{"+","-","X","÷"}; int numa=0,numb=0,numc=0; int type=0; for(int j=0;j<TIMES;j++) { boolean g_ok=false; boolean g_run=false; for(int i=0;i<times;i++) { if(g_ok&&isOk(numa)||g_ok&&isOk(numc)) { System.out.print("=\n"); numc=-2000; break; } if(!g_ok) { numa=(int)(Math.random()*600+1); numb=(int)(Math.random()*600+1); } type=(int)(Math.random()*4+1); //System.out.print("\t"+type+"\t"); if(type==4) { if(!g_ok) { while(numa%numb!=0||numb==1) { numa=(int)(Math.random()*600+1); numb=(int)(Math.random()*600+1); } numc=numa/numb; } else { while(numa%numb!=0) { numb=(int)(Math.random()*600+1); } numc/=numb; } g_run=!g_run; } else if(type==3) { if(!g_ok) { while(numa*numb>1000||numa==1||numb==1) { numa=(int)(Math.random()*600+1); numb=(int)(Math.random()*600+1); } numc=numa*numb; } else { while(numa*numb>1000||numb!=1) { numb=(int)(Math.random()*600+1); } numc*=numb; } } else if(type==2) { if(!g_ok) numc=numa-numb; else numc-=numb; } else if(type==1) { if(!g_ok) numc=numa+numb; else numc+=numb; } if(!g_ok) System.out.print(numa+szM[type-1]+numb); else System.out.print(szM[(type==4&&!g_run)?(int)(Math.random()*2):type-1]+numb); g_ok=true; numa=numb; numb=(int)(Math.random()*600+1); } if(numc!=-2000) { System.out.print("=\n"); } numc=0; times=(int)(Math.random()*(SXSIZE-1)+1); } } public static boolean isOk(int num) { if(num==1||num<=0||num==2) return false; for(int i=2;i<=Math.sqrt(num);i++) { if(num%i==0) return false; } return true; } }
二年级四则运算的所生成的30道题目测试截图:
三年级四则运算测试截图:
实验总结:
二年级:这次的实验中出现了一些死循环的情况,后来发现是忘记添加了+号的情况了,然后添加过后就成功实现了。
三年级:在进行运算的时候由于没有考虑到质数的问题导致一开始的时候总是陷入死循环,到了最后的时候,经过一些变量的调整,终于利用了3个变量实现了三年级题目的运算,在除法的运算中,判断整除的这部分卡了比较长的时候,还需要继续努力
二、编程实现用户登录界面需要输入验证码。
验证码实现思想:
由于之前尚未接触关于验证码生成的代码,这个功能没有实现的思路,上网搜寻狼来了关于一些此类的函数,然后整合并且仔细地看了一番,最终实现了类似的登录验证的功能
验证码实验的截图:
点击图片更换验证码
如果验证码正确的话,点击确定按钮会显示验证码正确的对话框
并且清空验证码框,同时更换验证码
验证码实现的代码如下:
package yanzhengtest; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.FileOutputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.Random; import javax.swing.*; import javax.imageio.ImageIO; public class CheckTest { private static int width = 110; // 定义图片的width private static int height = 20; // 定义图片的height private static int codeCount = 6; // 定义图片上显示验证码的个数 private static int codeX = 15; //字体x间距设置,间距坐标 private static int fontHeight = 18; //字体高度 private static int codeY = 16; //y坐标 private static int lineInfect = 20; private static float yawpRate = 0.05f; //噪点生成在图中的覆盖范围5% private static int fontColor = 200; //字体颜色范围,越小颜色越深(0-255) private static char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; public Map<String,Object> m1; /** * 生成一个map集合 * code为生成的验证码 * codePic为生成的验证码BufferedImage对象 * @return */ public static Map<String,Object> generateCodeAndPic() { // 定义图像buffer BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // Graphics2D gd = buffImg.createGraphics(); // Graphics2D gd = (Graphics2D) buffImg.getGraphics(); Graphics gd = buffImg.getGraphics(); // 创建一个随机数生成器类 Random random = new Random(); // 将图像填充为白色 gd.setColor(Color.WHITE); gd.fillRect(0, 0, width, height); // randomCode用于保存随机产生的验证码,以便用户登录后进行验证。 StringBuffer randomCode = new StringBuffer(); int red = 0, green = 0, blue = 0; // 创建字体,字体的大小应该根据图片的高度来定。 Font font = new Font("Fixedsys", Font.BOLD, fontHeight); // 设置字体。 gd.setFont(font); // 随机产生codeCount数字的验证码。 for (int i = 0; i < codeCount; i++) { // 得到随机产生的验证码数字。 String code = String.valueOf(codeSequence[random.nextInt(36)]); // 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。 red = random.nextInt(fontColor); green = random.nextInt(fontColor); blue = random.nextInt(fontColor); // 用随机产生的颜色将验证码绘制到图像中。 gd.setColor(new Color(red, green, blue)); gd.drawString(code, (i + 1) * codeX, codeY); // 将产生的四个随机数组合在一起。 randomCode.append(code); } //------------------------------------------------- //扭曲图片 //扭曲X int period = random.nextInt(2); int fc=200,bc=250; boolean borderGap = false; int frames = 1; int phase = random.nextInt(2); Color color; for (int i = 0; i < height; i++) { double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); gd.copyArea(0, i, width, 1, (int) d, 0); if (borderGap) { int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); color=new Color(r, g, b); gd.setColor(color); gd.drawLine((int) d, i, 0, i); gd.drawLine((int) d + width, i, width, i); } } //扭曲Y period = random.nextInt(10) + 5; // 50; borderGap = false; frames = 20; phase = 3; for (int i = 0; i < width; i++) { double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); gd.copyArea(i, 0, 1, height, 0, (int) d); if (borderGap) { int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); color=new Color(r, g, b); gd.setColor(color); gd.drawLine(i, (int) d, i, 0); gd.drawLine(i, (int) d + height, i, height); } } //------------------------------------------------- // 画边框。 gd.setColor(Color.BLACK); gd.drawRect(0, 0, width - 1, height - 1); //------------------------------------------------- // 随机产生30条干扰线,使图象中的认证码不易被其它程序探测到。 gd.setColor(Color.BLACK); for (int i = 0; i < lineInfect; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); gd.drawLine(x, y, x + xl, y + yl); } //------------------------------------------------- //生成噪点 int area = (int) (yawpRate * width * height); for (int i= 0; i < area; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int[] rgb = new int[3]; for (int j = 0; j < 3; j++) { rgb[j] = random.nextInt(255); } int color1 = 0; for (int c : rgb) { color1 = color1 << 8; color1 = color1 | c; } buffImg.setRGB(x, y, color1); } Map<String,Object> map =new HashMap<String,Object>(); //存放验证码 map.put("code", randomCode); //存放生成的验证码BufferedImage对象 map.put("codePic", buffImg); return map; } private Map<String,Object> reset() throws Exception { String imgPath = System.currentTimeMillis()+".jpg"; OutputStream out = new FileOutputStream(imgPath); Map<String,Object> map = CheckTest.generateCodeAndPic(); ImageIO.write((RenderedImage) map.get("codePic"), "jpeg", out); System.out.println("验证码的值为:"+map.get("code")); return map; } public static void main(String[] args) throws Exception { //创建文件输出流对象 CheckTest che_1=new CheckTest(); JFrame mainF=new JFrame("验证码测试(区分大小写)"); mainF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //mainF.setExtendedState(JFrame.MAXIMIZED_BOTH); //Container con=mainF.getContentPane(); JPanel a=new JPanel(); //a.setLayout(new BoxLayout(a,BoxLayout.Y_AXIS)); JPanel a1=new JPanel(); a1.setLayout(new BoxLayout(a1,BoxLayout.X_AXIS)); JLabel lab_1=new JLabel("姓名"); JTextField t1=new JTextField(20); a1.add(Box.createHorizontalGlue()); a1.add(lab_1); a1.add(t1); a1.add(Box.createHorizontalGlue()); JPanel a2=new JPanel(); a2.setLayout(new BoxLayout(a2,BoxLayout.X_AXIS)); JLabel lab_2=new JLabel("密码"); JPasswordField p1=new JPasswordField(20); //p1.setAlignmentY(Component.CENTER_ALIGNMENT); //p1.setEchoChar('*'); a2.add(Box.createHorizontalStrut(15)); a2.add(lab_2); a2.add(p1); a2.add(Box.createHorizontalStrut(15)); JPanel a3=new JPanel(); a3.setLayout(new BoxLayout(a3,BoxLayout.X_AXIS)); JLabel lab_3=new JLabel("验证码:"); JButton but_3=new JButton(); JTextField tex_3=new JTextField(5); che_1.m1=che_1.reset(); but_3.setIcon(new ImageIcon((Image)che_1.m1.get("codePic"))); a3.add(lab_3); a3.add(tex_3); a3.add(but_3); but_3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub //弹出对话框 // JOptionPane.showMessageDialog(null, "弹出对话框"); // String str= JOptionPane.showInputDialog(null, "输入一个整数","输入对话框", JOptionPane.PLAIN_MESSAGE); try { che_1.m1=che_1.reset(); but_3.setIcon(new ImageIcon((Image)che_1.m1.get("codePic"))); tex_3.setText(""); } catch (Exception e1) { // TODO 自动生成的 catch 块 e1.printStackTrace(); } } }); //a.add(Box.createHorizontalGlue()); GridBagConstraints c3 = new GridBagConstraints(); c3.gridx = 0; c3.gridy = 0; c3.weightx = 1.0; c3.weighty = 0; c3.fill = GridBagConstraints.HORIZONTAL ; // 加入 bottomPanel a.add(a1,c3); GridBagConstraints c2 = new GridBagConstraints(); c2.gridx = 0; c2.gridy = 1; c2.weightx = 1.0; c2.weighty = 0; c2.fill = GridBagConstraints.HORIZONTAL ; a.add(a2,c2); GridBagConstraints c1 = new GridBagConstraints(); c1.gridx = 0; c1.gridy = 2; c1.weightx = 1.0; c1.weighty = 0; c1.fill = GridBagConstraints.HORIZONTAL ; a.add(a3,c1); JButton but_4=new JButton("确定(区分大小写)"); but_4.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(tex_3.getText().equals(che_1.m1.get("code").toString())) { JOptionPane.showMessageDialog(null, "验证码正确"); try { che_1.m1=che_1.reset(); } catch (Exception e1) { // TODO 自动生成的 catch 块 e1.printStackTrace(); } but_3.setIcon(new ImageIcon((Image)che_1.m1.get("codePic"))); tex_3.setText(""); } else { JOptionPane.showMessageDialog(null, "验证码错误,(注意)区分大小写"); try { che_1.m1=che_1.reset(); } catch (Exception e1) { // TODO 自动生成的 catch 块 e1.printStackTrace(); } but_3.setIcon(new ImageIcon((Image)che_1.m1.get("codePic"))); tex_3.setText(""); } } }); a.add(but_4); mainF.setContentPane(a); mainF.setVisible(true); mainF.setSize(300, 170); mainF.setLocation(200, 200); } }
实验总结:
这是我第一次编写关于图形处理方面的java程序,写的过程中了解了很多关于图形处理方面的知识,受益匪浅,这次的实验是对自己的一次历练,但在某些方面仍然还有很多的不足,例如,很多函数的功能还得依照网上所给的资料才能够知道如何写,这说明了我知识储备的不足,同时,学习到java关于界面化程序的编写,还是有些满足的。