java学习的一些小实验


前言

开始学习java时做的一些关于java知识点的小实验。

在这里插入图片描述


一、 基础语法体验

1.输出汉字

问题描述:

在Java语言中,char类型是基本数据类型,采用Unicode编码,占2个字节的内存空间,用来存储一个字符,可以是一个英文字符如’a’,也可以是一个汉字如’好’。char本身具有特殊性,因为存储了Unicode字符,而字符都有对应的Unicode编码值,最小值为\u0000(即0),最大值为\uFFFF(即65535),因此char和一个整数值之间是可以进行互相转换表示的,只是不要超过范围。

现在已知有一个字符’中’和一个Unicode码22269,请完成代码的编写,分别输出这两个字符及其后面的4个字符的数据,要求输出的格式为:字符(字符的Unicode码值),括号为英文半角小括号,例如:妈(22920)妉(22921)妊(22922)妋(22923)妌(22924)妍(22925),每5个字符信息占一行。

问题分析:

本问题的核心就是在于字符和一个整数之间的转换表示,并通过循环进行输出。输出一个字符可以输出char类型的变量,如果是数值则可以强制类型转换为char;而输出字符的Unicode码值,则只需要将字符强制类型转换为int即可。
算法设计:

  • 第一步,建立循环,初始化一个字符型的循环变量,初始即为给定的字符值,循环条件为该字符小于等于给定的字符值+5;

  • 第二步,在循环中定义整型变量,赋值为当前循环变量字符的Unicode码值(强制类型转换得到),然后按照指定格式进行输出;

  • 第三步,建立循环,初始化一个整型变量,初始为给定的Unicode码值,循环条件为该数值小于等于给定的Unicode码值+5;

  • 第四步,在循环中定义字符变量,赋值为当前循环变量对应的char字符,然后按照指定的格式进行输出。

测试输入

‘中’ 22269

预期输出

中(20013)丮(20014)丯(20015)丰(20016)丱(20017)串(20018)

国(22269)图(22270)囿(22271)圀(22272)圁(22273)圂(22274)

public class OutputChinese {
    public static void getChineseString(char ch, int code){
         for(int i=0;i<5;i++){
             System.out.printf("%c(%d)",ch,(int)ch);
             ch++;
         }
         System.out.println("");
         for(int i=0;i<5;i++){
             System.out.printf("%c(%d)",(char)code,code);
             code++;
         }
    }

    public static void main(String[] args) {
        char ch = '中';
        int code = 22269;
        getChineseString(ch, code);
    }
}

2.超大整数的加法

问题描述:

某个开发小组在进行项目开发时遇到了一个问题,他们需要进行一个超大的整数的求和运算,例如9239293919+67267627367,而int的范围远远不够,此时他们想到,可以将这些数字分别存放到两个长度相同的数组中,然后再根据其长度创建一个等长的数组,接下来就可以按照下标对数组的每一位来进行求和,最后可以得到一个保存了结果数据的数组。

请完成代码的编写,实现两个存放了超大整数数字的数组的求和,并将数值的值输出出来,例如222222222+3333333333=3555555555,注意开头的0不可输出。

问题分析:

本问题实际要完成的是对数组的操作,由于数组中每个数字是一个个的数组元素,看似独立,实则是需要作为一个整体来看待,由于进行加法运算时需要从个位开始加起,意味着对数组元素的访问需要从数组的最后一个元素往前进行,另外每一位上的数字经过加法运算后可能产生进位也是需要注意的,如果产生了进位,则当前结果数组的元素只保留和值的个位,并将产生的进位1先累加到a或者b的下一位上。另外计算过程中可能会存在进位而改变了原数组的内容,所以需要提前输出加法的式子,然后进行求和运算,再输出结果,而输出时,考虑到前面可能存在0,因此输出时需要找到第一个不为0的数字开始输出。

算法设计:

  • 第一步,先输出加法式子,通过循环进行数组数字的输出,注意开头的0不可输出;
  • 第二步,需要根据当前已有的数组长度来创建一个存储目标结果的数组;
  • 第三步,建立循环,因为两个数组等长,所以只需要一个循环,从数组的最后一位往前遍历,对每一位元素进行计算,并判断结果是不是大于等于10,如果是则产生进位,结果数组中的元素只保留其个位,进位的1先累加到a或者b数组的下一个位上;
  • 第四步,计算完成后,再通过循环输出结果数组中的数字,同样开头的0不可输出。

测试输入

a = {0, 9, 9, 7, 9, 4, 5, 6, 7, 2, 3, 4, 5, 6, 4, 7, 8, 9, 8, 7, 6, 9}

b = {0, 0, 5, 9, 1, 6, 4, 5, 6, 2, 3, 4, 5, 7, 2, 1, 3, 4, 0, 3, 2, 9}

预期输出:

997945672345647898769+59164562345721340329=1057110234691369239098

public class HandleLargeNumber {
    public static void add(int[] a, int[] b) {
        int[] c = null;
        // 在此处完成代码的编写
        int max=Math.max(a.length,b.length);
        c=new int [max+1];
        int temp=0;
        for(int i=a.length-1;i>=0;i--){
            c[i]=(a[i]+b[i]+temp)%10;
            temp=(a[i]+b[i]+temp)/10;
        }
        for(int i=0;i<a.length;i++){
            if(a[i]!=0){
                for(;i<a.length;i++){
                    System.out.print(a[i]);
                }
                break;
            }
        }
        System.out.print("+");
        for(int i=0;i<b.length;i++){
            if(b[i]!=0){
                for(;i<b.length;i++){
                    System.out.print(b[i]);
                }
                break;
            }
        }
        System.out.print("=");
        for(int i=0;i<c.length;i++){
            if(c[i]!=0){
                for(;i<c.length;i++){
                    System.out.print(c[i]);
                }
                break;
            }
        }

    }

    public static void main(String[] args) {
        int[] a = {0, 9, 9, 7, 9, 4, 5, 6, 7, 2, 3, 4, 5, 6, 4, 7, 8, 9, 8, 7, 6, 9};
        int[] b = {0, 0, 5, 9, 1, 6, 4, 5, 6, 2, 3, 4, 5, 7, 2, 1, 3, 4, 0, 3, 2, 9};
        add(a, b);
    }

}

这个代码过于臃肿,可以改成以下写法

public class HandleLargeNumber {
    public static void add(int[] a, int[] b) {
        int[] c = null;
        // 在此处完成代码的编写
        //int max=Math.max(a.length,b.length);
        //c=new int [max+1];
        c=new int[a.length];
        int temp=0;
        for(int i=a.length-1;i>=0;i--){
            c[i]=(a[i]+b[i]+temp)%10;
            temp=(a[i]+b[i]+temp)/10;
        }
        P(a);
        System.out.print("+");
        P(b);
        System.out.print("=");
        P(c);

    }
    public static void P(int[] d){
        for(int i=0;i<d.length;i++){
            if(d[i]!=0){
                for(;i<d.length;i++){
                    System.out.print(d[i]);
                }
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] a = {0, 9, 9, 7, 9, 4, 5, 6, 7, 2, 3, 4, 5, 6, 4, 7, 8, 9, 8, 7, 6, 9};
        int[] b = {0, 0, 5, 9, 1, 6, 4, 5, 6, 2, 3, 4, 5, 7, 2, 1, 3, 4, 0, 3, 2, 9};
        add(a, b);
    }

}

二、初识面向对象

1. 游戏类设计

问题描述:

某软件公司接手开发一款游戏软件,其中有一个坦克类Tank,对该类做出了如下设计:

1)该类中包含一个int speed;成员表示其速度,一个int bulletAmount;成员表示坦克的炮弹数量;

2)速度初始化为0,可以通过speedUp和speedDown方法进行加速和减速(注意速度不能小于0),可以使用getSepped方法获取当前速度值;

3)炮弹数量通过setBulletAmount方法设置,可以通过getBulletAmount方法获得当前炮弹数量,并可以通过fire方法进行开火,在fire方法中,如果当前炮弹还有剩余,则输出“打出一发炮弹”并将剩余炮弹数量减1,如果没有剩余,则输出“没有炮弹了,无法开火”。
请完成坦克类Tank的定义,编写完成后可以通过Fight类进行调试。

问题分析:

本问题主要是进行面向对象中类的定义,类的定义包含属性和行为两部分,这里的属性有速度和炮弹数量,行为有加速、减速、开火,以及对属性的设置和获取。在Tank类中合适的位置按需求编写相关的代码即可。

算法设计:

  • 第一步,定义两个属性;
  • 第二步,实现指定的方法;
  • 第三步,通过调试Fight中的main方法进行验证。

测试输入:

tank1.setBulletAmount(10);

tank2.setBulletAmount(10);

预期输出:

tank1的炮弹数量:10

tank2的炮弹数量:10

tank1目前的速度:80

tank2目前的速度:90

tank1目前的速度:65

tank2目前的速度:60

tank1开火:

打出一发炮弹

tank2开火:

打出一发炮弹

打出一发炮弹

tank1的炮弹数量:9

tank2的炮弹数量:8

Fight.java

public class Fight {
    public static void main(String[] args) { 
        Tank tank1,tank2;     
        tank1 = new Tank();     
        tank2 = new Tank();     
        tank1.setBulletAmount(10);   
        tank2.setBulletAmount(10);      
        System.out.println("tank1的炮弹数量:"+tank1.getBulletAmount());  
        System.out.println("tank2的炮弹数量:"+tank2.getBulletAmount());   
        tank1.speedUp(80);      
        tank2.speedUp(90);    
        System.out.println("tank1目前的速度:"+tank1.getSpeed());  
        System.out.println("tank2目前的速度:"+tank2.getSpeed());    
        tank1.speedDown(15);      
        tank2.speedDown(30);       
        System.out.println("tank1目前的速度:"+tank1.getSpeed());   
        System.out.println("tank2目前的速度:"+tank2.getSpeed());   
        System.out.println("tank1开火:");      
        tank1.fire();   
        System.out.println("tank2开火:");   
        tank2.fire();     
        tank2.fire();   
        System.out.println("tank1的炮弹数量:"+tank1.getBulletAmount());   
        System.out.println("tank2的炮弹数量:"+tank2.getBulletAmount());  
    }
}

Tank.java

public class Tank {
    // 在此定义速度和炮弹数量属性
    private int speed;
    private int bulletAmount;

    // 加速方法
    public void speedUp(int s) {
        speed += s;
    }

    // 减速方法
    public void speedDown(int d) {
        speed -= d;
    }

    // 设置炮弹数量
    public void setBulletAmount(int m) {
        bulletAmount = m;
    }

    // 获取炮弹数量
    public int getBulletAmount() {
        return bulletAmount;
    }

    // 获取速度
    public int getSpeed() {
        return speed;
    }

    // 开火
    public void fire() {
        System.out.println("打出一发炮弹");
         bulletAmount--;
    }
}

2.计算机与CD

问题描述:

某公司设计了一款笔记本电脑,其上将会配置一款带刻录功能的光驱,可以读取CD光盘上的数据,也可以将电脑里的数据刻录到光盘中。为此需要你来设计相关的系统程序,该系统包含以下几个类:

1)光盘类CD:包含一个int[] content数组表示光盘的数据内容,另外还有相应的get方法和set方法;

2)电脑类Computer:包含一个int[]data数组表示电脑中的数据内容,一个CD
includeCD成员表示当前插入光驱的光盘,另外相应的放入光盘方法、数据处理方法、刻录数据到光盘方法;

3)用户类User:main方法进行操作的模拟实现。

请完成CD类和Computer类的定义,并调试User类中的main方法验证是否完成。

问题分析:

本问题主要是进行面向对象中类的定义,类的定义包含属性和行为两部分,这里有两个类,各自拥有不同的属性和行为,同时在Computer类中又包含了CD类的对象,所以还涉及到类的聚合以及引用传参的概念。

算法设计:

第一步,实现CD类的定义,对于其setContent方法,该set方法代表了刻录光盘内容,因此将根据方法传递的数据来重新创建CD中的内容数组并拷贝数组内容;

第二步,实现Computer类的定义,其中putCD方法表示光盘放入光驱,此处需要保存传入的CD对象,并根据该CD对象中的数据内容来创建data数组,然后拷贝CD中的content数据到data数组中;

第三步,Computer类的addData方法代表了数据处理,此处简单的将data数组的每一个数据加上参数传入的整型值即可;

第四步,Computer类的copyToCD方法没有参数,代表刻录操作,表示将本类的data数组写入到放入的光盘中,也就是调用本类中的CD对象的setContnet方法;

第五步,调试User类中的main方法,观察代码执行情况,验证类编写是否完整。

测试输入:

b = {-11, 2, 3, 4, 5, 6, 7, 8}

预期输出:

dataCD上的内容:
[-11, 2, 3, 4, 5, 6, 7, 8]

将dataCD的数据复制到计算机:computerIMB.

computerIMB上的内容:
[-11, 2, 3, 4, 5, 6, 7, 8]

computerIMB将每个数据增加12
computerIMB将增加值后的数据复制到CD:dataCD
dataCD上的内容:

[1, 14, 15, 16, 17, 18, 19, 20]

CD.java

public class CD {
    public int[] content; // 光盘数据内容数组

    // 获取内容数组
    public int[] getContent() {
        // 在此完成方法的定义   
        return this.content;
    }     // 设置光盘的数据内容,其中根据参数传入的数组来重新创建内容数组对象  
    public void setContent(int[] b) {
        // 在此完成方法的定义    
        this.content = new int[b.length];
        for (int i = 0; i < b.length; i++) {
            this.content[i] = b[i];
        }
    }
}

Computer.java

import java.util.Arrays;
public class Computer {
    private int[] data; // 电脑中的数据 
    private CD includeCD; // 电脑中放入的光盘  
      // 获取数据 
    public int[] getData(){
        // 在此完成方法的定义 
        return this.data;
    }    
     // 放入光盘方法,其中读取光盘内容,将根据光盘的内容来创建数据数组   
    public void putCD(CD cd) {
        // 在此完成方法的定义  
        this.includeCD = cd;
        this.data = this.includeCD.getContent();
    }    
     // 数据处理,此处对data数组中的每一个元素加上参数m的值 
    public void addData(int m) {
        // 在此完成方法的定义  
         for (int i = 0; i < this.data.length; i++) {
            this.data[i] += m;
        }
    }
       // 刻录数据到光盘中  
    public void copyToCD() {
        // 在此完成方法的定义  
        this.includeCD.setContent(this.data);
    }
}

User.java

import java.util.Arrays;
public class User {  
    public static void main(String[] args) {   
        CD dataCD = new CD(); 
        // 创建一个光盘对象       
        int[] b = {1, 2, 3, 4, 5, 6, 7, 8}; // 默认数据    
        dataCD.setContent(b); // 设置光盘的默认数据   
        int[] a = dataCD.getContent(); // 获取光盘中的数据进行输出   
        System.out.println("dataCD上的内容:");     
        System.out.println(Arrays.toString(a));    
        Computer computerIMB = new Computer(); // 创建一个电脑对象  
        computerIMB.putCD(dataCD); 
        // 放入光盘       
        System.out.println("\n将dataCD的数据复制到计算机:computerIMB.");
        System.out.println("computerIMB上的内容:");  
        System.out.println(Arrays.toString(computerIMB.getData())); 
        int m = 12;      
        System.out.println("\ncomputerIMB将每个数据增加" + m);  
        computerIMB.addData(m); // 处理数据    
        System.out.println("computerIMB将增加值后的数据复制到CD:dataCD");
        computerIMB.copyToCD();    // 刻录光盘     
        System.out.println("dataCD上的内容:");     
        a = dataCD.getContent(); // 获取光盘当前内容输出  
        System.out.println(Arrays.toString(a)); 
    }
}

三、面向对象进阶

1.面积之和

问题描述:

某工厂大量生产矩形和圆形的板件,成本和其面积有关。为了统计生产的所有板件的面积,开发了一款程序。在设计之初,考虑到目前生产的有矩形和原型板件,后期可能还有其他形状,因此设计了一个抽象的图形类Geometry,它包含一个用来获取面积的抽象方法,然后派生出两个子类:矩形类Rect和圆形类Circle,它们有各自的属性和构造方法。然后设计了一个TotalArea类,其中包含了一个图形数组,由外部传递一个图形对象数组进来,并定义了一个calculateTotalarea方法计算图形数组中所有图形的面积之和。

请完成各个类的定义,并调试MainClass中的main方法进行测试。

问题分析:

本问题中有一个抽象类Geometry,其中有抽象方法,其子类最终需要实例化对象,因此必须实现该抽象方法。另外在TotalArea类中传递了一个Geometry类型的数组,该数组的元素是其子类的对象,在计算所有图形面积之和时,只需要对数组元素调用获取面积的方法即可,因为虽然数组元素的类型是抽象的父类类型,然后由于子类中重写了该方法,所以具体在调用执行时,会按照对象的实际类型调用重写后的方法,这其实就是由继承引起的多态。

算法设计:

第一步,重写矩形类Rect和圆形类Circle中从父类继承来的抽象方法,按照各自特点计算其面积并返回;

第二步,完成TotalArea类中calculateTotalarea方法的定义,在该方法中对图形数组进行遍历,调用每个元素的获取面积的方法,并累加起来,最后返回;

第三步,测试main方法,观察结果。

public class MainClass {
    public static void main(String[] args) {
        // 模拟创建多个图形对象
        Geometry[] tuxing = new Geometry[29];//有29个Geometry对象
        for (int i = 0; i < tuxing.length; i++) {//29个Geometry对象分成两类
            if (i % 2 == 0) {
                tuxing[i] = new Rect(16 + i, 68);
            } else {
                tuxing[i] = new Circle(10 + i);
            }
        }

        // 计算图形面积之和
        TotalArea computer = new TotalArea();
        computer.setTuxing(tuxing);
        double area = computer.calculateTotalarea();
        System.out.println("各种图形的面积之和:" + area);
    }

}
public abstract class Geometry {
    public abstract double getArea();
}
public class Rect extends Geometry {
    private double length; // 长
    private double width; // 宽

    public Rect(double length, double width) {
        this.length = length;
        this.width = width;
    }

    // 在此重写getArea方法,计算矩形面积
    public double getArea(){
        return length*width;
        }
}
public class Circle extends Geometry {
    private double r; // 半径

    public Circle(double r) {
        this.r = r;
    }

    // 在此重写getArea方法,计算圆形面积
    public double getArea(){
        return r*r*3.14;
    }

    
}
public class TotalArea {
    private Geometry[] tuxing; // 图形数组

    // 设置图形数组
    public void setTuxing(Geometry[] t) {
        tuxing = t;
    }

    // 计算图形数组中所有图形的面积之和并返回
    public double calculateTotalarea() {
        // 在此完成方法的定义
        double sum=0;
        for(int i=0;i<tuxing.length;i++){
            sum+=tuxing[i].getArea();
        }
        return sum;
    }
}

2.教学管理系统的设计和实现

问题描述:

某高校为了对老师和学生进行管理,设计和开发了一套教学管理系统。在设计时充分考虑了面向对象的思想,整个系统中包含了如下几个类:

1)Person类:该类作为各种人员的父类,是一个抽象类,具有姓名name和年龄age属性,同时提供了无参和有参构造器,并提供了一个抽象方法showMsg,用于给子类输出特定的人员信息;

2)Student类:表示学生类,继承Person类,学生类中扩展了score分数属性,同时也提供了无参和有参构造器,提供了一个考试方法,另外需要实现从父类继承的showMsg方法,显式内容格式为“学生姓名为张三,年龄为18岁。”的样式,具体数据由构造器或者setXXX方法设定;

3)Teacher类:表示教师类,继承Person类,教师类中扩展了course课程属性,同时也提供了无参和有参构造器,提供了一个教学方法,另外需要实现从父类继承的showMsg方法,显式内容格式为“教师姓名为李四,年龄为35岁。”的样式,具体数据由构造器或者setXXX方法设定;

4)Professor类:表示教授类,继承Teacher类,提供无参和有参构造器,并提供了一个指导方法,需要重写从父类继承来的showMsg方法,显式内容格式为“教授姓名为王五,年龄为55岁。”的样式,具体数据由构造器或者setXXX方法设定。

请完成相关类的定义,并在TestMain类的main方法中进行测试。

问题分析:

本问题中Person类是一个抽象类,其派生了两个子类:Student和Teacher,而Teacher类又派生了Professor类。每个类显式输出的信息都不同,所以都需要进行重写继承来的showMsg方法。另外在继承中有参构造器的调用上需要注意,子类的有参构造器如何使用父类的有参构造器。最后就是关于getter和setter的实现。

算法设计:

第一步,完成Person类定义,实现带参构造方法以及getter和setter方法,并添加抽象方法showMsg的声明;

第二步,完成Student类定义,实现带参构造方法和扩展的score属性的getter和setter方法,并实现父类中的抽象方法,按固定格式进行输出;

第三步,完成Teacher类定义,实现带参构造方法和扩展的course属性的getter和setter方法,并实现父类中的抽象方法,按固定格式进行输出;

第四步,完成Professor类定义,实现带参构造方法,并重写继承来的showMsg方法按固定格式进行输出;

第五步,在main方法中进行测试。

public class TestMain {
    public static void main(String[] args) {
        // 测试教师类
        Person t = new Teacher("张三", 35, "数据库");
        t.showMsg(); // 输出教师信息
        ((Teacher)t).teach(); // 教师进行教学

        // 测试教授类
        Person p = new Professor("李四", 45, "人工智能");
        p.showMsg(); // 输出教授信息
        ((Professor)p).teach(); // 教授作为老师进行教学
        ((Professor)p).guide(); // 教授指导研究生

        // 测试学生类
        Person s = new Student("王五", 20, 95);
        s.showMsg(); // 输出学生信息
        ((Student)s).exam(); // 学生进行测验
    }
}
public abstract class Person {
    private String name; // 姓名
    private int age; // 年龄

    // 无参构造
    public Person() {
    }

    // 完成带参构造方法的定义
    public Person(String name, int age) {
        this.setAge(age);
        this.setName(name);
    }

    // 实现setXxx和getXxx方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name=name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age=age;
    }

    // 声明抽象方法
    public abstract void showMsg();
    
}
public class Student extends Person {
    private int score; // 分数

    // 无参构造
    public Student() {
    }

    // 完成带参构造方法定义
    public Student(String name, int age, int score) {
        
        super(name,age);
        this.setScore(score);

    }

    // 实现setXxx和getXxx方法
    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score=score;
    }

    // exam考试方法,不可修改
    public void exam() {
        System.out.println("学生" + getName() + "考了" + score + "分");
    }

    // 实现抽象方法
    public void showMsg(){
        System.out.println("学生姓名为"+getName()+",年龄为"+getAge()+"岁");
    }
}
public class Teacher extends Person {

    private String course; // 所教的课程

    // 无参构造
    public Teacher() {
    }

    // 完成带参构造方法定义
    public Teacher(String name, int age, String course) {
        
        super(name,age);
        this.setCourse(course);

    }

    // 实现setXxx和getXxx方法
    public String getCourse() {
        return course;
    }

    public void setCourse(String course) {
        this.course=course;
    }

    // teach教学方法,不可修改
    public void teach() {
        System.out.println("老师" + getName() + "讲授" + course + "课程");
    }

    // 实现抽象方法
    public void showMsg(){
        System.out.println("老师姓名为"+getName()+",年龄为"+getAge()+"岁");
    }
}
public class Professor extends Teacher{

    // 无参构造
    public Professor() {
    }

    // 完成带参构造方法定义
    public Professor(String name, int age, String course) {
        super(name,age,course);
    }

    // 指导方法,不可修改
    public void guide(){
        System.out.println("教授需要承担指导研究生的工作");
    }

    // 重写继承的showMsg方法
    public void showMsg(){
        System.out.println("教授姓名为"+getName()+",年龄为"+getAge()+"岁");
    }
}

四、异常处理实验

检查危险品

问题描述:

车站里存放了很多的货物,这些货物将会通过一个设备进行检测,如果是非危险品,则通过检查,而如果是危险品,则会报出警告并且禁止货物通行。

现有这样的一个系统可以完成货物的检查,它包含有如下几个类:

1)Goods:货物类,包含有货物的名称和是否是危险品两个属性;

2)DangerException:危险品异常类,当检测到危险品时需要抛出该异常类对象,该类继承Exception类,包含一个message成员,构造该异常对象时,message默认赋值为“危险品警告”,另外该类包含一个show方法,当捕获到该类异常对象时被调用输出message;

3)Machine:机器设备类,该类负责对某一个产品进行检测,如果是危险品,则抛出危险品异常对象,如果不是危险品则不做处理;

4)Station:车站类,该类中存储了多个货物对象,以及一台设备对象,可以启动机器设备对所有的货物进行检测,并对检测操作捕获异常,如果是非危险品,即未产生异常,则直接输出“商品名称检测通过”,若是危险品,则将输出异常信息,并输出“商品名称禁止通行”。

所有的输出均无标点,且使用println输出。

请编写代码实现该系统。

问题分析:

1)DangerException类是自定义的异常类,继承了Exception,构造方法中设置message的默认值即可,在show方法中可以直接输出

2)在Machine类中,检查货物时,要判断该货物的dangerous属性,如果为true,即为危险品,此时需要抛出一个危险品异常对象,并且要在方法上添加抛出声明;

3)在Station类的检查方法中,对于其货物列表中的每一个货物进行检查,需要使用try-catch包围每一个货物的检查操作而不是整体,可以直接在try语句的check调用之后输出通过的语句,如果发生异常,则将会直接进入catch语句,在catch中调用异常对象的show方法,然后输出禁止通行的结果。

算法设计:

第一步,实现DangerException类,添加构造方法,其中执行默认消息值,实现show方法,对消息进行输出;

第二步,实现Machine类的checkBag方法,其中根据货物属性判断来抛出异常对象;

第三步,实现Station类的checkAll方法,对列表进行遍历,遍历中使用try-catch语句对每个货物进行检查和捕获。

最后,在TestMain的main方法中进行调试测试。

测试输入:

“苹果”, “炸药”, “西服”, “硫酸”, “手表”, “硫磺”

预期输出:

苹果检测通过

危险品警告

炸药禁止通行

西服检测通过

危险品警告

硫酸禁止通行

手表检测通过

危险品警告

硫磺禁止通行

import java.util.ArrayList;
import java.util.List;
public class TestMain {
      public static void main(String[] args) {
            Station station = new Station();
// 模拟货物列表
             String[] names = {"苹果", "炸药", "西服", "硫酸", "手表"
             List<Goods> list = new ArrayList<>();
             for (int i = 0; i < names.length; i++){
                  Goods g = new Goods();
                  g.setName(names[i]);
           		  g.setDangerous(i % 2 != 0); // 下标为奇数的是危险品
           		  list.add(g);
          	 }
      		 station.setGoodsList(list);
      		  // 模拟检查
     		  station.checkAll();
/*
本例将会输出:
苹果检测通过
危险品警告
炸药禁止通行
西服检测通过
危险品警告
硫酸禁止通行
手表检测通过
危险品警告
硫磺禁止通行*/
     }
}
public class Goods {
	private boolean dangerous; // 是否是危险品
	private String name; // 商品名称
	public void setDangerous(boolean dangerous) {
		this.dangerous = dangerous;
	}
	public boolean isDangerous() {
		return dangerous;
	}
	public void setName(String s) {
		name = s;
	}
	public String getName() {
		return name;
	}
}
public class DangerException extends Exception {
	private String message; // 异常消息
	// 添加构造函数定义
	public DangerException() {
		this.message = "危险品警告";
	}
	// 添加show方法定义
	public void show() {
		System.out.println(message);
	}
}
public class Machine {
    // 检查货物,如果是危险品,则抛出DangerException异常
    public void checkBag(Goods goods) throws DangerException {
// 在此完成方法的定义
        if (goods.isDangerous()) {
            throw new DangerException();
        } else {
        }
    }
}
import java.util.List;
public class Station {
    private List<Goods> goodsList;
    private Machine machine = new Machine();
    public List<Goods> getGoodsList() {
        return goodsList;
    }
    public void setGoodsList(List<Goods> goodsList) {
        this.goodsList = goodsList;
    }
    // 检测所有货物
    public void checkAll() {
// 在此完成方法的定义
        for (Goods goods : goodsList) {
            try {
                machine.checkBag(goods);
                System.out.println(goods.getName() + "检测通过");
            } catch (DangerException e) {
                e.show();
                 System.out.println(goods.getName()+"禁止通行");
            }
        }
    }
}

五 常用工具类

1.大整数处理

问题描述:

某企业在进行一项工程计算时,发现需要处理的整数数据特别大,已经超过了基本类型中的最大整数long的表示范围,因此在设计时使用Java中的Big Integer类来进行处理,并设计了一个BigCalc类完成大整数的运算。

请完成该类的编写,实现两个大整数的加减乘除运算,并实现一个静态工具方法,获取一个大整数的因子的个数(因子是指可以整除的数,不包括该数本身)。

问题分析:

本问题涉及大整数类BigInteger的运算,作为一个对象,它的运算不像基本类型可以直接使用数学元素符完成,而是使用BigInteger类提供的多种方法进行,例如add、subtract、multiply和divide方法。另外在统计因子的个数时,其实就是编写一个循环,从1开始到该数前结束,判断大数能否被其中某个整除,如果是则进行计数,需要注意的是,这里的运算都是基于BigInteger的,因此编写循环时初始化1时要使用BigInteger对象的ONE,判断大小时要使用BigInteger的compareTo方法,循环变量增长时不能使用++而仍然需要使用i=i.add(xx)的方式,判断是否整除时需要通过remainder方法计算求余的结果并调用compareTo方法和BigInteger的ZERO比较判断结果是否为0。

需要注意:BigInteger中方法的调用不会改变对象本身的值,而是返回一个新的对象,所以某些情况下需要重新赋值才能达到修改的目的。

算法设计:

第一步,在加减乘除方法中直接调用相应的方法进行计算后返回结果;

第二步,在获取因子个数的静态方法中,定义循环,判断参数是否可以被循环中的变量整除,如果是则对计数变量进行加法运算,增加1。

测试输入:

“987654321987654321987654321”, “123456789123456789123456789”

预期输出:

和值:1111111111111111111111111110

差值:864197532864197532864197532

乘积:121932631356500531591068431581771069347203169112635269

商:8

17637一共有3个因子

import java.math.BigInteger;
public class Main {
    public static void main(String[] args) {
        BigCalc bc = new BigCalc("987654321987654321987654321", "123456789123456789123456789");
        BigInteger result = null;
        // 测试加法运算
        result = bc.doAdd();
        System.out.println("和值:" + result.toString());
        // 测试减法运算
        result = bc.doSub();
        System.out.println("差值:" + result.toString());
        // 测试乘法运算
        result = bc.doMult();
        System.out.println("乘积:" + result.toString());
        // 测试除法运算
        result = bc.doDiv();
        System.out.println("商:" + result.toString());
        // 测试计算因子
        BigInteger m = new BigInteger("17637");
        BigInteger count = BigCalc.getFactorCount(m);
        System.out.println(m.toString() + "一共有" + count.toString() + "个因子");
    }
}
import java.math.BigInteger;
public class BigCalc {
    private final BigInteger num1;
    private final BigInteger num2;
    public BigCalc(String str1, String str2) {
        num1 = new BigInteger(str1);
        num2 = new BigInteger(str2);
    }
    // 加法运算
    public BigInteger doAdd() {
        return num1.add(num2);
    }
    // 减法运算
    public BigInteger doSub() {
        return num1.subtract(num2);
    }
    // 乘法运算
     public BigInteger doMult() {
        return num1.multiply(num2);
     }
     // 除法运算
     public BigInteger doDiv() {
        return num1.divide(num2);
     }
     // 获取因子个数
     public static BigInteger getFactorCount(BigInteger num) {
        BigInteger count = new BigInteger("0");
        // 计数结果
         BigInteger zero = BigInteger.ZERO; // BigInteger内置字段,代表0
          BigInteger one = BigInteger.ONE; // BigInteger内置字段,代表1
         // 在此完成方法的定义,获取参数num的因子的个数

         for (BigInteger i = BigInteger.ONE; i.compareTo(num) <=0; i = i.add(BigInteger.ONE)) {
             if (num.remainder(i).compareTo(BigInteger.ZERO) == 0) {
                 count = count.add(BigInteger.ONE);
             }
         }
         return count;
    }
}

2.图书信息检索

问题描述:

在某图书管理系统中,经常会输入一条完整的图书信息,在处理时很不方便,因此需要开发一个工具用来截取图书信息中的关键数据。已知图书信息的格式如下:“书名:Java程序设计,出版时间:2011.10.01,

出版社:清华大学出版社,价格:29.8元,页数:389页”,其中冒号和逗号均是中文全角字符。

请完成工具类中方法的定义。

问题分析:

本问题需要从一个字符串中获取其子串的信息,可以使用String类的substring方法来进行,该方法可以通过传入两个参数来截取字符串中指定位置之间的内容,因此可以先通过Stirng类的indexOf方法来查找关键字的位置(注意这里查找到的位置是开始位置,实际截取的位置需要再调整),得到位置后再进行截取。另外,对于价格和页码,需要对截取到的字符串数值转换成相应的数值类型。

算法设计:

第一步,根据方法的描述完成方法的定义;

第二步,调试main方法进行测试,观察结果。

测试输入:

书名:Java程序设计,出版时间:2011.10.01,出版社:清华大学出版社,价格:29.8元,页数:389页

预期输出:

图书信息中包含关键字:程序

图书名称为:Java程序设计

图书价格为:29.8

图书共有389页

public class Main {
    public static void main(String[] args) {
        String bookInfo = "书名:Java程序设计,出版时间:2011.10.01," +
                "出版社:清华大学出版社,价格:29.8元,页数:389页";
        // 测试包含
        if (MyBookUtil.check(bookInfo, "程序")){
            System.out.println("图书信息中包含关键字:程序");
        }
        // 测试获取书名
        String bookName = MyBookUtil.getName(bookInfo);
        System.out.println("图书名称为:" + bookName);
        // 测试获取价格
        double price = MyBookUtil.getPrice(bookInfo);
        System.out.println("图书价格为:" + price);
        // 测试获取页码
        int page = MyBookUtil.getPage(bookInfo);
        System.out.println("图书共有" + page + "页");
    }
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyBookUtil {
    // 检查目标字符串中是否包含指定的关键字
    public static boolean check(String info, String key) {
        return info.contains(key);
    }

    // 截取图书信息中的书名
    public static String getName(String info) {
        int start = info.indexOf("书名:") + "书名:".length();
        int end = info.indexOf(",", start);
        return info.substring(start, end); 
    }

    // 截取图书信息中的价格
    public static double getPrice(String info) {
        int start = info.indexOf("价格:") + "价格:".length();
        int end = info.indexOf("元", start);
        return Double.parseDouble(info.substring(start, end));    }

    // 截取图书信息中的页码
    public static int getPage(String info) {
        int start = info.indexOf("页数:") + "页数:".length();
        int end = info.indexOf("页", start);
        return Integer.parseInt(info.substring(start,end));
    }
}

六 、简单记事本的设计与实现

设计并实现一个简单的记事本程序。能够实现一些基本功能,主要有:

(1) 具有编辑界面,能够实现文本的输入、删除等基本功能。

(2) 具有菜单条,包含三个主菜单:文件、编辑、帮助。

(3) 各个主菜单分别拥有其菜单选项。

(4) 文件主菜单含有新建、打开、保存、退出四个菜单选项;

(5) 编辑主菜单含有剪切、复制、粘贴、查找四个菜单选项。

(6) 帮助主菜单好友关于记事本信息的菜单选项。

(7) 各个菜单选项能够实现其对应的功能。

(8) 实现剪切、复制、粘贴、查找即编辑菜单的鼠标跳出菜单功能。

(9) 各功能具有快捷键。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;

public class SimpleNotePad extends JFrame {
    private JTextArea textArea;

    public SimpleNotePad() {
        setTitle("Simple Notepad");
        setSize(800, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        textArea = new JTextArea();
        JScrollPane scrollPane = new JScrollPane(textArea);
        add(scrollPane, BorderLayout.CENTER);

        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);

        JMenu fileMenu = new JMenu("文件");
        JMenu editMenu = new JMenu("编辑");
        JMenu helpMenu = new JMenu("帮助");

        JMenuItem newMenuItem = new JMenuItem("新建");
        JMenuItem openMenuItem = new JMenuItem("打开");
        JMenuItem saveMenuItem = new JMenuItem("保存");
        JMenuItem exitMenuItem = new JMenuItem("退出");

        JMenuItem cutMenuItem = new JMenuItem("剪切");
        JMenuItem copyMenuItem = new JMenuItem("复制");
        JMenuItem pasteMenuItem = new JMenuItem("粘贴");
        JMenuItem findMenuItem = new JMenuItem("查找");

        JMenuItem aboutMenuItem = new JMenuItem("关于");

        fileMenu.add(newMenuItem);
        fileMenu.add(openMenuItem);
        fileMenu.add(saveMenuItem);
        fileMenu.add(exitMenuItem);

        editMenu.add(cutMenuItem);
        editMenu.add(copyMenuItem);
        editMenu.add(pasteMenuItem);
        editMenu.add(findMenuItem);

        helpMenu.add(aboutMenuItem);

        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        menuBar.add(helpMenu);

        newMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                textArea.setText("");
            }
        });

        openMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                int returnValue = fileChooser.showOpenDialog(null);
                if (returnValue == JFileChooser.APPROVE_OPTION) {
                    try {
                        BufferedReader reader = new BufferedReader(new FileReader(fileChooser.getSelectedFile()));
                        textArea.read(reader, null);
                        reader.close();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });

        saveMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                int returnValue = fileChooser.showSaveDialog(null);
                if (returnValue == JFileChooser.APPROVE_OPTION) {
                    try {
                        FileWriter writer = new FileWriter(fileChooser.getSelectedFile());
                        textArea.write(writer);
                        writer.close();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });
        exitMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        cutMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                textArea.cut();
            }
        });
        copyMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                textArea.copy();
            }
        });
        pasteMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                textArea.paste();
            }
        });
        findMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String searchText = JOptionPane.showInputDialog("请输入要查找的文本:");
                if (searchText != null && !searchText.isEmpty()) {
                    String content = textArea.getText();
                    int index = content.indexOf(searchText);
                    if (index != -1) {
                        textArea.setCaretPosition(index);
                        textArea.moveCaretPosition(index + searchText.length());
                    } else {
                        JOptionPane.showMessageDialog(null, "未找到指定文本。");
                    }
                }
            }
        });
        aboutMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(null, "这是一个简单的记事本程序。");
            }
        });
        setVisible(true);
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new SimpleNotePad();
            }
        });
    }
}

七 、文件和流操作

成绩处理

问题描述:

在某举重比赛中,每个选手的成绩包括抓举成绩和挺举成绩,最后相加得到总成绩。现在有一个文本文件score1.data记录了该比赛中多位选手的分项成绩,该文件内容如下:

姓名:张三,抓举成绩106kg,挺举189kg.

姓名:李四,抓举成绩108kg,挺举186kg.

姓名:王五,抓举成绩112kg,挺举190kg.

现在需要对该成绩进行处理,计算出选手的总成绩后拼接到字符串之后成为新的字符串,格式为:

姓名:张三,抓举成绩106kg,挺举189kg. 总成绩: 295kg

然后将所有信息并写入到目标文件result.data中。

请编码实现该成绩统计系统。

问题分析:

本问题主要涉及到对文件的读写操作,首先需要按行来读取每一条记录,可以将读取到的一行行文本存储到List集合中,然后需要获取每一行字符串中的成绩信息(已经提供了工具类来获取一行文本中的成绩之和),并且拼接成新的字符串替换原List集合中的字符串内容,最后将整个List集合中的字符串依次写入到目标文件中。这里需要按行来读取和写入字符串,因此适合使用BufferedReaderBufferedWriter类。

算法设计:

第一步,使用给定的文件名常量创建BufferedReader对象,通过reader对象调用方法readLine读取每一行的内容,并存储到List集合中;

第二步,处理数据;

第三步,使用给定的目标文件名常量创建BufferedWriter对象,并将参数给定的List集合中的字符串依次写入到目标文件中。

注意:若控制台输出的结果中包含源文件内容则表示源文件中的数据读取成功,否则读取失败;包含目标文件内容语句,则表示数据写入到目标文件中写入成功,否则写入失败!

测试输入:

score1.data内容为:

姓名:张三,抓举成绩106kg,挺举189kg.

姓名:李四,抓举成绩108kg,挺举186kg.

姓名:王五,抓举成绩112kg,挺举190kg.

预期输出:

控制台输出结果:

源文件内容为:

姓名:张三,抓举成绩106kg,挺举189kg.总成绩:295kg 姓名:李四,抓举成绩108kg,挺举186kg.总成绩:294kg
姓名:王五,抓举成绩112kg,挺举190kg.总成绩:302kg

目标文件内容为:

姓名:张三,抓举成绩106kg,挺举189kg.总成绩:295kg 姓名:李四,抓举成绩108kg,挺举186kg.总成绩:294kg
姓名:王五,抓举成绩112kg,挺举190kg.总成绩:302kg

public class TestFile {
    public static final String SRC_FILE = TestFile.class.getClassLoader().getResource("")
                .getFile()+"source1.data"; // 源数据文件名
    public static final String RLT_FILE = TestFile.class.getClassLoader().getResource("")
                .getFile()+"result.data"; // 最终结果存放文件名

    public static void main(String[] args) {
        AnalysisResult ar = new AnalysisResult();
        ar.analysisFile(SRC_FILE,RLT_FILE);
    }
}
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.List;

public class Util {
    public static int getTotalScore(String s) {
        String regex = "[^0123456789]"; // 匹配非数字的正则表达式
        String digitMess = s.replaceAll(regex, "*"); // 使用*替换所有非数字
        StringTokenizer tokenizer = new StringTokenizer(digitMess, "*");
        int totalScore = 0;
        while (tokenizer.hasMoreTokens()) {
            int score = Integer.parseInt(tokenizer.nextToken());
            totalScore = totalScore + score;
        }
        return totalScore;
    }

    //String集合转换成字符串输出
    public static String listToString(List<String> stringList) {
        if (stringList == null) {
            return null;
        }
        StringBuilder result = new StringBuilder();
        boolean flag = false;
        for (String string : stringList) {
            if (flag) {
                result.append("   ");
            } else {
                flag = true;
            }
            result.append(string);
        }
        return result.toString();
    }

}
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class AnalysisResult {

   // 分析处理文件
    public void analysisFile(String srcFile, String rltFile) {
        List<String> srcFileList = readSrcFile(srcFile); // 读取源数据
        // 处理数据得到最终结果
        for (int i = 0; i < srcFileList.size(); i++){
            String line = srcFileList.get(i);
            int totalScore = Util.getTotalScore(line);
            line = line + "总成绩:" + totalScore + "kg";
            srcFileList.set(i, line);
        }
        saveRltFile(srcFileList, rltFile); // 将最终结果保存到目标文件中
        //写入完成后,读取目标文件中的内容
        List<String> rltFileList = readSrcFile(rltFile);
        System.out.println("源文件内容为:"+Util.listToString(srcFileList));
        System.out.println("目标文件内容为:"+Util.listToString(rltFileList));
    }

    // 读取文件数据,并将数据保存到List集合中
    private List<String> readSrcFile(String srcFile) {
        List<String> list = new ArrayList<>();
        BufferedReader reader = null;
        try {
            // 在此完成方法的定义, 从srcFile路径的文件中读取内容,并以行为单位存储到list集合中
             reader = new BufferedReader(new FileReader(srcFile));
            String line;
            while ((line = reader.readLine()) != null) {
                list.add(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }

    // 将List内容存储到目标文件中
    private void saveRltFile(List<String> list, String rltFile) {
        BufferedWriter writer = null;
        try {
            // 在此完成方法的定义,将list集合中的字符串写入到目标文件中,每个字符串占一行
             writer = new BufferedWriter(new FileWriter(rltFile));
            for (String line : list) {
                writer.write(line);
                writer.newLine();
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

八、多线程应用

多线程猜数字

问题描述:

猜数字游戏是这样一个游戏:事先给定一个数值,由游戏一方进行猜测,另一方则根据猜测的数字和给定的目标数字之间的关系,告知其结果是大了、小了还是猜对了,如果猜对了游戏结束,否则继续进行。

现在,我们可以使用多线程技术来实现这样的猜数字游戏,全智能、全自动,完全不需要人去操作了。

请完善给定的代码,并能够在执行main方法后实现完整的游戏流程。

问题分析:

本问题涉及到多线程的相关知识,问题中的NumberGame类实现了Runnable接口,在其中包含了Thread对象,Thread对象也是启动线程的主要对象,而它的目标对象就是一个实现了Runnable接口的类的对象,因此在构造时需要使用当前类对象也就是this作为参数来构造两个Thread类对象。

在启动线程之后,将会执行其中的run方法,因此在类中的方法需要区分当前正在进行方法调用的具体是哪一个线程,然后对不同的线程执行不同的功能。

整个系统的设计还涉及到线程同步问题,程序启动时默认状态是猜测状态,所以如果是检查线程来执行时,判断当前状态是猜测状态,则检查线程进入等待状态,等待猜测线程完成了猜测后改变状态,这样才能进入检查的过程,同理对于猜测线程也是如此,猜测结束后状态转换为检查状态,则检测线程执行线程方法时同样需要等待。而这两个线程其中之一完成操作后,为了唤醒另一个线程,需要通过调用线程方法通知其他所有等待的线程。

算法设计:

第一步,根据当前类对象创建两个线程对象,并在启动游戏的方法中启动这两个线程;

第二步,线程启动后执行run方法,进入游戏循环,每一次执行doGame方法时,都需要判断当前线程对象是谁;

第三步,根据不同的线程对象执行不同的操作,检查线程中根据当前的猜测的数字和目标数字之间的关系给出猜测结果,按照固定格式进行输出,而猜测线程则根据检查的结果修改边界后重新猜测;

第四步,完成操作后需要唤醒其他等待中的线程。

测试输入:

33

预期输出:

小了

我猜这个数是50

大了

我猜这个数是25

小了

我猜这个数是37

大了

我猜这个数是31

小了

我猜这个数是34

大了

我猜这个数是32

小了

我猜这个数是33

猜对了

public class TestGame {
    public static void main(String[] args) {
        NumberGame game = new NumberGame(33);
        game.startGame();
    }
}
public class NumberGame implements Runnable {
    private int targetNumber; // 目标数字
    private int guessNumber; // 猜测的数字
    private int min = 1, max = 100; // 猜测的边界
    private int result = -1; // 表示猜测结果,值为1表示猜测的大了,-1表示猜测的小了,0表示猜对了
    private boolean guessing; // 标记当前是否在猜测中,true表示轮到猜测线程,false表示轮到检查线程
    private Thread threadGuess; // 猜测线程
    private Thread threadCheck; // 检查线程

    public NumberGame(int target) {
        this.targetNumber = target;

        // 补充代码:在此创建猜测线程和检查线程,使用当前类对象作为线程的目标对象
        this.threadGuess = new Thread(this);
        this.threadCheck = new Thread(this);


    }

    public void startGame() {
        // 补充代码:启动猜测线程和检查线程
        this.threadGuess.start();
        this.threadCheck.start();        


    }

    @Override
    public void run() {
        while (true) { // 开始游戏循环
            doGame(); // 进行一次游戏过程

            if (result == 0) { // 如果才对了,游戏循环结束
                break;
            }
        }
    }

    // 一次游戏过程,该方法会由不同的线程执行
    private synchronized void doGame() {
        // 如果当前进入该方法的是检查线程
        if (  Thread.currentThread() == threadCheck  /*补充代码:判断当前线程是否是检查线程*/) {
            // 如果当前是正在猜测状态,则进入等待
            while (guessing) {
                try {
                    // 补充代码:进入等待
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            /* 补充代码:对猜测结果进行检查
            如果猜测的数字大于目标数字则输出“大了”
            如果猜测的数字小于目标数字则输出“小了”
            如果猜测的数字等于目标数字则输出“猜对了”
            */
             if (guessNumber > targetNumber) {
                System.out.println("大了");
                result = 1;
            } else if (guessNumber < targetNumber) {
                System.out.println("小了");
                result = -1;
            } else {
                System.out.println("猜对了");
                result = 0;
            }




            // 检查之后转换到猜测状态
            guessing = true;
        }

        // 如果当前进入该方法的是猜测线程
        if ( Thread.currentThread() == threadGuess   /*补充代码:判断当前线程是否是猜测线程*/) {
            // 如果当前是检查状态中,则进入等待
            while (!guessing) {
                try {
                    // 补充代码:进入等待
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            /* 补充代码:根据猜测结果进行边界修改并猜测新的数字
            当猜测结果不为0时,根据大于或者小于进行相应的修改并重新给出猜测的数字
            同时输出“我猜这个数是xx”
             */
             if (result != 0) {
                if (result == -1) {
                    min = guessNumber + 1;
                } else {
                    max = guessNumber - 1;
                }
                guessNumber = (min + max) / 2;
                System.out.println("我猜这个数是" + guessNumber);
            }

            // 猜测之后转换到检查状态
            guessing = false;
        }

        // 补充代码:一次过程后唤醒其他处于等待的线程
        notifyAll();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值