Skip to content

Latest commit

 

History

History
2937 lines (2353 loc) · 87.4 KB

File metadata and controls

2937 lines (2353 loc) · 87.4 KB

十、附录

关于

本节旨在帮助学生完成书中的活动。

它包括学生为实现活动目标而执行的详细步骤。

第 1 课:Java 简介

活动 1:打印简单算术运算的结果

解决方案:

  1. 创建一个名为Operations的类,如下所示:

    public class Operations
    {
  2. Within main(), print a sentence describing the operation on the values you will be performing along with the result:

        public static void main(String[] args) {
            System.out.println("The sum of 3 + 4 is " + (3 + 4));
            System.out.println("The product of 3 + 4 is " + (3 * 4));
        }
    }

    输出结果如下:

    The sum of 3 + 4 is 7
    The product of 3 + 4 is 12

活动 2:从用户读取值并使用 Scanner 类执行操作。

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 输入ReadScanner作为类名,然后点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 在**main()**中输入以下内容:

    public class ReadScanner
    {
        static Scanner sc = new Scanner(System.in);
      public static void main(String[] args) {
        System.out.print("Enter a number: ");
        int a = sc.nextInt();
        System.out.print("Enter 2nd number: ");
        int b = sc.nextInt();
        System.out.println("The sum is " + (a + b) + ".");
        }
    }
  5. Run the main program.

    输出结果如下:

    Enter a number: 12                                                                                                             
    Enter 2nd number: 23
    The sum is 35\.  

活动 3:计算金融工具的增减百分比

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 输入StockChangeCalculator作为类名,然后点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 在**main()**中输入以下内容:

    public class StockChangeCalculator{
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        System.out.print("Enter the stock symbol: ");
        String symbol = sc.nextLine();
        System.out.printf("Enter %s's day 1 value: ", symbol);
        double day1 = sc.nextDouble();
        System.out.printf("Enter %s's day 2 value: ", symbol);
        double day2 = sc.nextDouble();
        double percentChange = 100 * (day2 - day1) / day1;
        System.out.printf("%s has changed %.2f%% in one day.", symbol, percentChange);
    }
    }
  5. Run the main program.

    输出应类似于:

    Enter the stock symbol: AAPL                                                                                                             
    Enter AAPL's day 1 value: 100                                                                                                           
    Enter AAPL's day 2 value: 91.5                                                                                                           
    AAPL has changed -8.50% in one day.

第 2 课:变量、数据类型和运算符

活动 4:输入学生信息并输出 ID

解决方案:

  1. 导入扫描仪包,创建新类

    import java.util.Scanner;
    {
    public class Input{
    static Scanner sc = new Scanner(System.in);
        public static void main(String[] args) 
    {
  2. 将学生姓名作为字符串。

    System.out.print("Enter student name: ");
    String name = sc.nextLine();
  3. 将大学名称作为字符串。

    System.out.print("Enter Name of the University: ");
    String uni = sc.nextLine();
  4. 把学生的年龄作为一个整数。

    System.out.print("Enter Age: ");
    int age = sc.nextInt();
  5. 打印出学生的详细信息。

    System.out.println("Here is your ID");
    System.out.println("*********************************");
    System.out.println("Name: " + name);
    System.out.println("University: " + uni);
    System.out.println("Age: " + age);
    System.out.println("*********************************");
        }
    } 
    }

活动 5:计算装满水果盒的数量

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 输入PeachCalculator作为类名,然后点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 在**main()**中输入以下内容:

    public class PeachCalculator{
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        System.out.print("Enter the number of peaches picked: ");
        int numberOfPeaches = sc.nextInt();
        int numberOfFullBoxes = numberOfPeaches / 20;
        int numberOfPeachesLeft = numberOfPeaches - numberOfFullBoxes * 20;
        System.out.printf("We have %d full boxes and %d peaches left.", numberOfFullBoxes, numberOfPeachesLeft);
    }
    }
  5. Run the main program.

    输出应类似于:

    Enter the number of peaches picked: 55
    We have 2 full boxes and 15 peaches left.

第三课:控制流

活动 6:使用条件控制执行流

解决方案:

  1. 创建一个名为Salary的类,添加**main()**方法:

    public class Salary {
       public static void main(String args[]) { 
  2. 初始化两个变量工时工资

    int workerhours = 10; 
    double salary = 0;
  3. 如果条件下,检查工人的工作时间是否低于要求的时间。如果条件为,则工资应为(工作时间*10)。

    if (workerhours <= 8 ) 
    salary = workerhours*10;
  4. 使用else if语句检查工作时间是否在 8 小时到 12 小时之间。如果这是真的,那么前八个小时的工资应按每小时 10 美元计算,其余的小时应按每小时 12 美元计算。

    else if((workerhours > 8) && (workerhours < 12)) 
    salary = 8*10 + (workerhours - 8) * 12;
  5. 使用else块,默认为每天 160 美元(额外一天的工资)。

    else
        salary = 160;
    System.out.println("The worker's salary is " + salary);
    }
    }

活动 7:开发温度系统

解决方案:

  1. 声明两个字符串,温度天气警告,然后用潮湿初始化温度

    public class TempSystem
    {
        public static void main(String[] args) {
            String temp = "Low";
            String weatherWarning;
  2. 创建一个开关语句,检查温度的不同情况,然后初始化变量weatherWarning为每种情况的温度(潮湿的相应消息。

    switch (temp) { 
            case "High": 
                weatherWarning = "It's hot outside, do not forget sunblock."; 
                break; 
            case "Low": 
                weatherWarning = "It's cold outside, do not forget your coat."; 
                break; 
            case "Humid": 
                weatherWarning = "The weather is humid, open your windows."; 
                break;
  3. 在默认情况下,将weatherWarning初始化为“天气看起来不错,出去走走”。

    default: 
      weatherWarning = "The weather looks good. Take a walk outside"; 
      break;
  4. 完成开关构造后,打印weatherWarning的值。

    } 
            System.out.println(weatherWarning); 
        }
    }
  5. Run the program to see the output, it should be similar to:

    It's cold outside, do not forget your coat.

    完整代码如下:

    public class TempSystem
    {
        public static void main(String[] args) {
            String temp = "Low";
            String weatherWarning;
                switch (temp) { 
            case "High": 
                weatherWarning = "It's hot outside, do not forget sunblock."; 
                break; 
            case "Low": 
                weatherWarning = "It's cold outside, do not forget your coat."; 
                break; 
            case "Humid": 
                weatherWarning = "The weather is humid, open your windows."; 
                break; 
    
            default: 
                weatherWarning = "The weather looks good. Take a walk outside"; 
                break; 
            } 
            System.out.println(weatherWarning); 
        }
    }

活动 8:实现 for 循环

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 输入PeachBoxCounter作为类名,然后点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 在**main()**中输入以下内容:

    public class PeachBoxCounter
    {
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
    System.out.print("Enter the number of peaches picked: ");
    int numberOfPeaches = sc.nextInt();
    for (int numShipped = 0; numShipped < numberOfPeaches; numShipped += 20)      {
    System.out.printf("shipped %d peaches so far\n", numShipped);
    }
    }
    }

活动 9:实现 while 循环

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 输入PeachBoxCounters作为类名,然后点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 在**main()**中输入以下内容:

    public class PeachBoxCounters{
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        System.out.print("Enter the number of peaches picked: ");
        int numberOfPeaches = sc.nextInt();
        int numberOfBoxesShipped = 0;
        while (numberOfPeaches >= 20) {
            numberOfPeaches -= 20;
            numberOfBoxesShipped += 1;
            System.out.printf("%d boxes shipped, %d peaches remaining\n", 
                    numberOfBoxesShipped, numberOfPeaches);
        }
    }
    }

活动 10:实施循环构造

解决方案:

  1. 导入从用户读取数据所需的包。

    import java.util.Scanner;
    public class Theater {
    public static void main(String[] args)
  2. 声明变量以存储可用座位总数、剩余座位数和请求的票数。

    {
    int total = 10, request = 0, remaining = 10;
  3. while循环中,执行检查请求是否有效的if else循环,这意味着请求的票数小于剩余的座位数。

    while (remaining>=0)
    {
    System.out.println("Enter the number of tickets");
    Scanner in = new Scanner(System.in);
    request = in.nextInt();
  4. 如果上一步中的逻辑为真,则打印一条消息以表示已处理票证,将剩余座位设置为适当的值,然后请求下一组票证。

    if(request <= remaining)
    {
    System.out.println("Your " + request +" tickets have been procced. Please pay and enjoy the show.");
    remaining = remaining - request;
    request = 0;
    }
  5. 如果步骤 3 中的逻辑为假,则打印适当的消息并跳出循环:

    else
    {
    System.out.println("Sorry your request could not be processed");
    break;
    }
    }
    }
    }

活动 11:嵌套循环的连续桃子装运

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 输入PeachBoxCounter作为类名,然后点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 在**main()**中输入以下内容:

    public class PeachBoxCount{    
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        int numberOfBoxesShipped = 0;
        int numberOfPeaches = 0;
        while (true) {
            System.out.print("Enter the number of peaches picked: ");
            int incomingNumberOfPeaches = sc.nextInt();
            if (incomingNumberOfPeaches == 0) {
                break;
            }
            numberOfPeaches += incomingNumberOfPeaches;
            while (numberOfPeaches >= 20) {
                numberOfPeaches -= 20;
                numberOfBoxesShipped += 1;
                System.out.printf("%d boxes shipped, %d peaches remaining\n",
                        numberOfBoxesShipped, numberOfPeaches);
            }
        }
    }
    }

第 4 课:面向对象编程

活动 12:用 Java 创建简单类

解决方案:

  1. 在 IDE 中创建一个名为动物的新项目。

  2. 在项目中,在src/文件夹下创建一个名为Animal.java的新文件。

  3. 打开Animal.java并粘贴以下代码:

    public class Animal {
    
    }
  4. 在花括号内,创建以下实例变量来保存数据,如下所示:

    public class Animal {
            int legs;
            int ears;
            int eyes;
            String family;
            String name;
    
        }
  5. 在实例变量下面,定义两个构造函数。其中一个将不接受任何参数,并将 legs 初始化为 4,ears 初始化为 2,eyes 初始化为 2。第二个构造函数将 legs、ear 和 eyes 的值作为参数,并设置这些值:

    public class Animal {
            int legs;
            int ears;
            int eyes;
            String family;
            String name;
            public Animal(){
                this(4, 2,2);
            }
            public Animal(int legs, int ears, int eyes){
                this.legs = legs;
                this.ears = ears;
                this.eyes = ears;
            }
    }
  6. Define four methods, two to set and get the family and two to set and get the name:

    笔记

    在对象中设置值的方法称为 setter,而获取这些值的方法称为 getter。

    public class Animal {
        int legs;
        int ears;
        int eyes;
        String family;
        String name;
        public Animal(){
            this(4, 2,2);
        }
        public Animal(int legs, int ears, int eyes){
            this.legs = legs;
            this.ears = ears;
            this.eyes = ears;
        }
        public String getFamily() {
            return family;
        }
        public void setFamily(String family) {
            this.family = family;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }

    我们已经完成了动物课的建设。让我们继续并创建几个此类实例。

  7. 创建一个名为Animals.java的新文件,并将以下代码复制到其中,如下所示:

    public class Animals {
    
           public static void main(String[] args){
    
           }
    }
  8. 创建动物类的两个对象:

    public class Animals {
            public static void main(String[] args){
                Animal cow = new Animal();
                Animal goat = new Animal();
            }
    }
  9. 让我们创建另一个有 2 条腿、2 耳朵和 2 眼睛的动物:

    Animal duck = new Animal(2, 2, 2);
  10. To set the animals' names and family, we will use the getters and setters we created in the class. Copy/write the following lines into the Animals class:

```java
public class Animals {
    public static void main(String[] args){
        Animal cow = new Animal();
        Animal goat = new Animal();
        Animal duck = new Animal(2, 2, 2);
        cow.setName("Cow");
        cow.setFamily("Bovidae");
        goat.setName("Goat");
        goat.setFamily("Bovidae");
        duck.setName("Duck");
        duck.setFamily("Anatidae");

        System.out.println(cow.getName());
        System.out.println(goat.getName());
        System.out.println(duck.getFamily());
    }
}
```

上述代码的输出如下所示:

```java
Cow
Goat
Anatide
```

图 4.9:动物类的产出

活动 13:编写计算器课程

解决方案:

  1. 创建一个计算器类,如下所示:

    public class Calculator {
  2. 创建三个字段双操作数 1双操作数 2字符串运算符。添加设置所有三个字段的构造函数。

    private final double operand1;
    private final double operand2;
    private final String operator;
    public Calculator(double operand1, double operand2, String operator){
    this.operand1 = operand1;
    this.operand2 = operand2;
    this.operator = operator;
    }
  3. 在此类中,添加一个操作方法,该方法将检查什么运算符是(“+”、“-”、“x”或“/”),并执行正确的操作,返回结果:

    public double operate() {
    if (this.operator.equals("-")) {
    return operand1 - operand2;
    } else if (this.operator.equals("x")) {
    return operand1 * operand2;
    } else if (this.operator.equals("/")) {
    return operand1 / operand2;
    } else {
    // If operator is sum or unknown, return sum
    return operand1 + operand2;
    }
    }
  4. 编写一个**main()**方法如下:

    public static void main (String [] args) {
            System.out.println("1 + 1 = " + new Calculator(1, 1, "+").operate());
            System.out.println("4 - 2 = " + new Calculator(4, 2, "-").operate());
            System.out.println("1 x 2 = " + new Calculator(1, 2, "x").operate());
            System.out.println("10 / 2 = " + new Calculator(10, 2, "/").operate());
        }
    }

活动 14:使用 Java 创建计算器

解决方案:

  1. 创建一个类运算符,该类在表示运算符的构造函数中初始化了一个字符串字段。这个类应该有一个表示默认运算符的默认构造函数,即 sum。操作符类还应该有一个名为operate的方法,该方法接收两个 double 并将操作符的结果作为 double 返回。默认操作为

    public class Operator {
        public final String operator;
        public Operator() {
            this("+");
        }
        public Operator(String operator) {
            this.operator = operator;
        }
        public boolean matches(String toCheckFor) {
            return this.operator.equals(toCheckFor);
        }
        public double operate(double operand1, double operand2) {
            return operand1 + operand2;
        }
    }
  2. 创建另一个名为减法的类。它从操作符扩展而来,并用它表示的每个操作覆盖 操作方法。它还需要一个无参数构造函数来调用超级传递它所表示的运算符:

  3. 创建另一个名为乘法的类。它从运算符扩展而来,并用它表示的每个操作覆盖操作方法。它还需要一个无参数构造函数来调用 super,并传递它所表示的运算符:

    public class Multiplication extends Operator {
        public Multiplication() {
            super("x");
        }
        @Override
        public double operate(double operand1, double operand2) {
            return operand1 * operand2;
        }
    }
  4. 创建另一个名为部门的类。它从运算符扩展而来,并用它表示的每个操作覆盖操作方法。它还需要一个无参数构造函数来调用 super,并传递它所表示的运算符:

    public class Division extends Operator {
        public Division() {
            super("/");
        }
        @Override
        public double operate(double operand1, double operand2) {
            return operand1 / operand2;
        }
    }
  5. 与前面的计算器类一样,这个类也会有一个操作方法,但它只会委托给操作符实例。最后,编写一个main方法,多次调用新计算器,每次打印运算结果:

    public class CalculatorWithFixedOperators {
        public static void main (String [] args) {
            System.out.println("1 + 1 = " + new CalculatorWithFixedOperators(1, 1, "+").operate());
            System.out.println("4 - 2 = " + new CalculatorWithFixedOperators(4, 2, "-").operate());
            System.out.println("1 x 2 = " + new CalculatorWithFixedOperators(1, 2, "x").operate());
            System.out.println("10 / 2 = " + new CalculatorWithFixedOperators(10, 2, "/").operate());
        }
        private final double operand1;
        private final double operand2;
        // The current operator
        private final Operator operator;
        // All possible operations
        private final Division division = new Division();
        private final Multiplication multiplication = new Multiplication();
        private final Operator sum = new Operator();
        private final Subtraction subtraction = new Subtraction();
        public CalculatorWithFixedOperators(double operand1, double operand2, String operator) {
            this.operand1 = operand1;
            this.operand2 = operand2;
            if (subtraction.matches(operator)) {
                this.operator = subtraction;
            } else if (multiplication.matches(operator)) {
                this.operator = multiplication;
            } else if (division.matches(operator)) {
                this.operator = division;
            } else {
                this.operator = sum;
            }
        }
        public double operate() {
            return operator.operate(operand1, operand2);
        }
    }

活动 15:了解 Java 中的继承和多态性

解决方案:

  1. 创建一个继承自动物

     public class Cat extends Animal {

  2. 创建实例变量所有者NumberOfDeaths年龄如下:

    //Fields specific to the Cat family
    String owner;
    int numberOfTeeth;
    int age;
  3. Create main() method as follows:

    public static void main(String[] args){
    Cat myCat = new Cat();
    //Since Cat inherits from Animal, we have access to it's methods and fields
    //We don't need to redefine these methods and fields
    myCat.setFamily("Cat");
    myCat.setName("Puppy");
    myCat.ears = 2;
    myCat.legs = 4;
    myCat.eyes = 2;
    System.out.println(myCat.getFamily());
    System.out.println(myCat.getName());
    System.out.println(myCat.ears);
    System.out.println(myCat.legs);
    System.out.println(myCat.eyes);
    }
    }

    结果如下

    Cat
    Puppy
    2
    4
    2

第五课:深度 OOP

活动 16:用 Java 创建和实现接口

解决方案:

  1. 打开上一课的动物项目。

  2. 创建一个名为动物行为的新接口。

  3. 在此创建两种方法void move()和void makeSound()。

  4. 创建一个名为Cow的新公共类,并实现动物行为接口。重写这两个方法,但暂时将其保留为空。

  5. Inside the Cow class, create two fields, as follows:

    public class Cow implements AnimalBehavior, AnimalListener {
    String sound;
    String movementType;

    编辑被重写的方法,使其如下所示:

    @Override
    public void move() {
        this.movementType = "Walking";
        this.onAnimalMoved();
    }
    @Override
    public void makeSound() {
        this.sound = "Moo";
        this.onAnimalMadeSound();
    }
  6. 使用以下方法创建另一个名为AnimalListener的接口:

    public interface AnimalListener {
       void onAnimalMoved();
       void onAnimalMadeSound();
    }
  7. Cow类也实现这个接口。确保覆盖接口中的两个方法。

  8. 将这两个方法编辑为如下所示:

    @Override
       public void onAnimalMoved() {
           System.out.println("Animal moved: " + this.movementType);
       }
    @Override
    public void onAnimalMadeSound() {
        System.out.println("Sound made: " + this.sound);
    }
  9. 最后,创建一个main方法来测试您的代码:

    public static void main(String[] args){
       Cow myCow = new Cow();
       myCow.move();
       myCow.makeSound();
    }
    }
  10. 运行Cow类并查看输出。应该是这样的:

```java
Animal moved: Walking
Sound made: Moo
```

活动 17:使用 instanceof 和 Typecasting

解决方案:

  1. 导入随机包生成随机员工:

    import java.util.Random;
  2. 创建一个EmployeeLoader类,该类将用作数据源,如下所示:

    public class EmployeeLoader {
  3. 声明一个静态伪随机发生器如下:

    private static Random random = new Random(15);
  4. 生成一个新的随机挑选的员工,如下所示:

    public static Employee getEmployee() {
            int nextNumber = random.nextInt(4);
            switch(nextNumber) {
                case 0:
                    // A sales person with total sales between 5000 and 1550000
                    double grossSales = random.nextDouble() * 150000 + 5000;
                    return new SalesWithCommission(grossSales);
                case 1:
                    return new Manager();
                case 2:
                    return new Engineer();
                case 3:
                    return new Sales();
                default:
                    return new Manager();
            }
        }
  5. 使用扩展了SalesSalesWithCommission类创建另一个文件。添加一个构造函数,将总销售额作为 double 接收,并将其存储为字段。还添加了一个名为getCommission的方法,该方法返回一个双精度值,即总销售额乘以 15%(0.15):

    public class SalesWithCommission extends Sales implements Employee {
        private final double grossSales;
        public SalesWithCommission(double grossSales) {
            this.grossSales = grossSales;
        }
        public double getCommission() {
            return grossSales * 0.15;
        }
    }
  6. 使用main()方法编写一个类ShowSalaryAndCommission,在for循环中反复调用getEmployee(),并打印员工薪资和税费信息。如果该员工是SalesWithCommission的实例,也可以打印他的佣金:

    public class ShowSalaryAndCommission {
        public static void main (String [] args) {
            for (int i = 0; i < 10; i++) {
                Employee employee = EmployeeLoader.getEmployee();
                System.out.println("--- " + employee.getClass().getName());
                System.out.println("Net Salary: " + employee.getNetSalary());
                System.out.println("Tax: " + employee.getTax());
                if (employee instanceof SalesWithCommission) {
                    // Cast to sales with commission
                    SalesWithCommission sales = (SalesWithCommission) employee;
                    System.out.println("Commission: " + sales.getCommission());
                }
            }
        }
    }

活动 18:理解 Java 中的类型转换

解决方案:

  1. 打开我们的动物项目。

  2. 创建一个名为AnimalTest的新类,并在其中创建 main 方法:

    public class AnimalTest {
       public static void  main(String[] args){
       }
    }
  3. main方法中,创建两个变量:

    Cat cat = new Cat();
    Cow cow = new Cow();
  4. 打印类别的所有者:

    System.out.println(cat.owner);
  5. Upcast the cat to Animal and try to print the owner once more. What error do you get? Why?

    Animal animal = (Animal)cat;
    System.out.println(animal.owner);

    错误消息如下所示:

    图 5.7:访问用于向上转换的子类变量时的异常

    原因:因为我们做了一个向上转换,我们不能再访问子类的变量了。

  6. 打印奶牛的声音:

    System.out.println(cow.sound);
  7. Try to upcast the cow to Animal. Why error do you get? Why?

    Animal animal1 = (Animal)cow;

    错误消息如下所示:

    图 5.8:将奶牛向上转换为动物时的例外情况

    原因:Cow 不是从 Animal 类继承的,因此它们不共享同一个层次结构树。

  8. 动物下放到cat1并再次打印所有者:

    Cat cat1 = (Cat)animal;
    System.out.println(cat1.owner);
  9. The full AnimalTest class should look like this:

    public class AnimalTest {
       public static void  main(String[] args){
           Cat cat = new Cat();
           Cow cow = new Cow();
           System.out.println(cat.owner);
    
           Animal animal = (Animal)cat;
           //System.out.println(animal.owner);
           System.out.println(cow.sound);
           //Animal animal1 = (Animal)cow;
           Cat cat1 = (Cat)animal;
           System.out.println(cat1.owner);
       }
    }

    结果如下:

图 5.9:AnimalTest 类的输出

活动 19:用 Java 实现抽象类和方法

解决方案:

  1. 创建一个名为医院的新项目并打开它。

  2. src文件夹中,创建一个名为Person

    public abstract class Patient {
    }

    的抽象类

  3. Create an abstract method that returns the type of person in the hospital. Name this method String getPersonType(), returning a String:

    public abstract String getPersonType();

    我们已经完成了抽象类和方法。现在,我们将继续从中继承并实现这个抽象方法。

  4. 创建一个名为 Doctor 的新类,该类继承自 Person 类:

    public class Doctor extends Patient {
    }
  5. 重写我们的博士类中的getPersonType抽象方法。返回字符串“Arzt”。这是医生的德语:

    @Override
    public String getPersonType() {
       return "Arzt";
    }
  6. Create another class called Patient to represent the patients in the hospital. Similarly, make sure that the class inherits from Person and overrides the getPersonType method. Return "Kranke". This is German for Patient:

    public class People extends Patient{
       @Override
       public String getPersonType() {
           return "Kranke";
       }
    }

    现在,我们有两个班。现在我们将使用第三个测试类测试代码。

  7. 创建名为HospitalTest的第三个类。我们将使用这个类来测试前面创建的两个类。

  8. HospitalTest类中,创建方法:

    public class HospitalTest {
       public static void main(String[] args){
    
       }
    }
  9. 方法中,创建医生的一个实例和患者的另一个实例:

    Doctor doctor = new Doctor();
    People people = new People();
  10. Try calling the getPersonType method for each of the objects and print it out to the console. What is the output?

```java
String str = doctor.getPersonType();
String str1 = patient.getPersonType();
System.out.println(str);
System.out.println(str1);
```

结果如下:

图 5.10:调用 getPersonType()时的输出

活动 20:使用抽象类封装公共逻辑

解决方案:

  1. 创建一个抽象类GenericEmployee,该类具有一个构造函数,该构造函数接收工资总额并将其存储在字段中。应该实现 Employee 接口,有两种方法:GetGrossalary()和getNetSalary()。第一个将只返回传递给构造函数的值。后者将返回工资总额减去调用**getTax()**方法

    public abstract class GenericEmployee implements Employee {
        private final double grossSalary;
        public GenericEmployee(double grossSalary) {
            this.grossSalary = grossSalary;
        }
        public double getGrossSalary() {
            return grossSalary;
        }
        @Override
        public double getNetSalary() {
            return grossSalary - getTax();
        }
    }

    的结果

  2. 创建每种类型员工的新通用版本:通用工程师。它需要一个获得总工资并将其传递给超级构造函数的构造函数。还需要实现**getTax()**方法,为每个类返回正确的税值:

    public class GenericEngineer extends GenericEmployee {
        public GenericEngineer(double grossSalary) {
            super(grossSalary);
        }
        @Override
        public double getTax() {
            return (22.0/100) * getGrossSalary();
        }
    }
  3. 创建每种类型员工的新通用版本:通用管理器。它需要一个获得总工资并将其传递给超级构造函数的构造函数。还需要实现**getTax()**方法,为每个类返回正确的税值:

    public class GenericManager extends GenericEmployee {
        public GenericManager(double grossSalary) {
            super(grossSalary);
        }
        @Override
        public double getTax() {
            return (28.0/100) * getGrossSalary();
        }
    }
  4. 创建每种类型员工的新通用版本:通用销售。它需要一个获得总工资并将其传递给超级构造函数的构造函数。还需要实现**getTax()**方法,为每个类返回正确的税值:

    public class GenericSales extends GenericEmployee {
        public GenericSales(double grossSalary) {
            super(grossSalary);
        }
        @Override
        public double getTax() {
            return (19.0/100) * getGrossSalary();
        }
    }
  5. 创建每种类型员工的新通用版本:GenericSalesWithCommission。它需要一个获得总工资并将其传递给超级构造函数的构造函数。它还需要实现getTax()方法,为每个类返回正确的税值。记住也要在GenericSalesWithCommission类上接收销售总额,并添加计算佣金的方法:

    public class GenericSalesWithCommission extends GenericEmployee {
        private final double grossSales;
        public GenericSalesWithCommission(double grossSalary, double grossSales) {
            super(grossSalary);
            this.grossSales = grossSales;
        }
        public double getCommission() {
            return grossSales * 0.15;
        }
        @Override
        public double getTax() {
            return (19.0/100) * getGrossSalary();
        }
    }
  6. 将新方法GetEmployeeWithAlary添加到您的EmployeeLoader类中。此方法将生成 70000 到 120000 之间的随机薪资,并在返回之前分配给新创建的员工。请记住,在创建GenericSalesWithCommission员工

    public static Employee getEmployeeWithSalary() {
            int nextNumber = random.nextInt(4);
            // Random salary between 70,000 and 70,000 + 50,000
            double grossSalary = random.nextDouble() * 50000 + 70000;
            switch(nextNumber) {
                case 0:
                    // A sales person with total sales between 5000 and 1550000
                    double grossSales = random.nextDouble() * 150000 + 5000;
                    return new GenericSalesWithCommission(grossSalary, grossSales);
                case 1:
                    return new GenericManager(grossSalary);
                case 2:
                    return new GenericEngineer(grossSalary);
                case 3:
                    return new GenericSales(grossSalary);
                default:
                    return new GenericManager(grossSalary);
            }
        }
    }

    时,还要提供销售总额

  7. 为循环编写一个应用程序,从内部多次调用GetEmployeeWithAlary方法。此方法与上一个活动中的方法类似:打印所有员工的净工资和税金。如果该员工是GenericSalesWithCommission**的实例,也可以打印其佣金。

    public class UseAbstractClass {
        public static void main (String [] args) {
            for (int i = 0; i < 10; i++) {
                Employee employee = EmployeeLoader.getEmployeeWithSalary();
                System.out.println("--- " + employee.getClass().getName());
                System.out.println("Net Salary: " + employee.getNetSalary());
                System.out.println("Tax: " + employee.getTax());
                if (employee instanceof GenericSalesWithCommission) {
                    // Cast to sales with commission
                    GenericSalesWithCommission sales = (GenericSalesWithCommission) employee;
                    System.out.println("Commission: " + sales.getCommission());
                }
            }
        }
    }
    ```** 

第 6 课:数据结构、数组和字符串

活动 21:寻找数组中的最小数

解决方案:

  1. 在名为ExampleArray:

    public class ExampleArray {
      public static void main(String[] args) {
      }
    }

    的新类文件中设置main方法

  2. 创建一个包含 20 个数字的数组:

    double[] array = {14.5, 28.3, 15.4, 89.0, 46.7, 25.1, 9.4, 33.12, 82, 11.3, 3.7, 59.99, 68.65, 27.78, 16.3, 45.45, 24.76, 33.23, 72.88, 51.23};
  3. 将最小浮动设置为第一个数字

    double min = array[0];
  4. 创建 for 循环以检查数组

    for (doublefloat f : array) {
    }

    中的所有数字

  5. 使用 if 根据最小值测试每个数字。如果小于最小值,则将该数字设为新的最小值:

    if (f < min)
    min = f;
    }
  6. After the loop completes, print out the minimum number:

    System.out.println("The lowest number in the array is " + min);
    }
    }

    完整的代码应该是这样的。

    public class ExampleArray {
            public static void main(String[] args) {
                double[] array = {14.5, 28.3, 15.4, 89.0, 46.7, 25.1, 9.4, 33.12, 82, 11.3, 3.7, 59.99, 68.65, 27.78, 16.3, 45.45, 24.76, 33.23, 72.88, 51.23};
                double min = array[0];
                for (double f : array) {
                    if (f < min)
                        min = f;
                }
                System.out.println("The lowest number in the array is " + min);
            }
    }

活动 22:带运算符数组的计算器

解决方案:

  1. 创建一个类运算符,该类将包含根据字符串确定要使用的运算符的逻辑。在这个类中创建一个公共常量字段默认 _ 操作符,它将成为操作符类的一个实例。然后创建另一个名为操作符的常量字段,其类型为操作符的数组,并使用您拥有的每个操作符的实例对其进行初始化:

    public class Operators {
        public static final Operator DEFAULT_OPERATOR = new Operator();
        public static final Operator [] OPERATORS = {
            new Division(),
            new Multiplication(),
            DEFAULT_OPERATOR,
            new Subtraction(),
        };
  2. 操作符类中,添加一个名为findOperator公共静态方法,该方法将操作符作为字符串接收,并返回操作符的实例。在其内部,迭代可能的运算符数组,并使用匹配方法为每个运算符返回所选运算符,如果不匹配任何运算符,则返回默认运算符:

    public static Operator findOperator(String operator) {
            for (Operator possible : OPERATORS) {
                if (possible.matches(operator)) {
                    return possible;
                }
            }
            return DEFAULT_OPERATOR;
        }
    }
  3. 使用 DynamicOperator 类创建一个新的计算器,该类有三个字段:操作数 1和运算符 2作为运算符**

    public class CalculatorWithDynamicOperator {
        private final double operand1;
        private final double operand2;
        // The current operator
        private final Operator operator;

    类型的和**运算符****

  4. 添加一个构造函数,以字符串形式接收类型为双精度运算符的三个参数:操作数 1操作数 2。在构造函数中,使用Operators.findOperator方法设置运算符字段

    public CalculatorWithDynamicOperator(double operand1, double operand2, String operator) {
            this.operand1 = operand1;
            this.operand2 = operand2;
            this.operator = Operators.findOperator(operator);
        }
        public double operate() {
            return operator.operate(operand1, operand2);
        }

    ,而不是使用 if-else 来选择运算符

  5. 添加一个main方法,在该方法中多次调用计算器类并打印结果:

    public static void main (String [] args) {
            System.out.println("1 + 1 = " + new CalculatorWithDynamicOperator(1, 1, "+").operate());
            System.out.println("4 - 2 = " + new CalculatorWithDynamicOperator(4, 2, "-").operate());
            System.out.println("1 x 2 = " + new CalculatorWithDynamicOperator(1, 2, "x").operate());
            System.out.println("10 / 2 = " + new CalculatorWithDynamicOperator(10, 2, "/").operate());
        }
    }

活动 23:与 ArrayList 合作

解决方案:

  1. java.util

    import java.util.ArrayList;
    import java.util.Iterator;

    导入ArrayList迭代器

  2. 创建一个名为StudentsArray

    public class StudentsArray extends Student{

    的新类

  3. 方法中定义学生对象的数组列表。插入 4 个学生实例,用我们前面创建的不同类型的构造函数实例化:

    public static void main(String[] args){
           ArrayList<Student> students = new ArrayList<>();
           Student james = new Student();
           james.setName("James");
           Student mary = new Student();
           mary.setName("Mary");
           Student jane = new Student();
           jane.setName("Jane");
           Student pete = new Student();
           pete.setName("Pete");
           students.add(james);
           students.add(mary);
           students.add(jane);
           students.add(pete);
  4. 为您的列表创建一个迭代器,并打印每个学生的姓名:

           Iterator studentsIterator = students.iterator();
           while (studentsIterator.hasNext()){
               Student student = (Student) studentsIterator.next();
               String name = student.getName();
               System.out.println(name);
           }    
  5. Clear all the students:

           students.clear();
       }
    }

    最终代码应如下所示:

    import java.util.ArrayList;
    import java.util.Iterator;
    public class StudentsArray extends Student{
       public static void main(String[] args){
           ArrayList<Student> students = new ArrayList<>();
           Student james = new Student();
           james.setName("James");
           Student mary = new Student();
           mary.setName("Mary");
           Student jane = new Student();
           jane.setName("Jane");
           students.add(james);
           students.add(mary);
           students.add(jane);
           Iterator studentsIterator = students.iterator();
           while (studentsIterator.hasNext()){
               Student student = (Student) studentsIterator.next();
               String name = student.getName();
               System.out.println(name);
           }
    
           students.clear();
       }
    }

    结果如下:

图 6.30:StudentsArray 类的输出

活动 24:输入一个字符串,并将其长度和作为数组输出

解决方案:

  1. 导入java.util.Scanner包:

    import java.util.Scanner;
  2. 创建一个名为NameTell的公共类和一个方法:

    public class NameTell
    {
      public static void main(String[] args)
      {
  3. 使用扫描仪下一行在提示输入您的姓名:

    System.out.print("Enter your name:");
    Scanner sc = new Scanner(System.in);
    String name = sc.nextLine();

    处输入字符串

  4. 计算字符串的长度并找到第一个字符:

    int num = name.length();
    char c = name.charAt(0);
  5. 打印输出:

    System.out.println("\n Your name has " + num + " letters including spaces.");
    System.out.println("\n The first letter is: " + c);
      }
    }

结果如下:

图 6.31:NameTell 类的输出

活动 25:计算器读取输入

解决方案:

  1. 创建一个名为CommandLineCalculator的新类,其中包含一个**main()**方法:

    import java.util.Scanner;
    public class CommandLineCalculator {
        public static void main (String [] args) throws Exception {
            Scanner scanner = new Scanner(System.in);
  2. 使用无限循环保持应用程序运行,直到用户请求退出。

    while (true) {
                printOptions();
                String option = scanner.next();
                if (option.equalsIgnoreCase("Q")) {
                    break;
                }
  3. 收集用户输入以决定要执行的操作。如果动作为QQ,则退出循环:

    System.out.print("Type first operand: ");
                double operand1 = scanner.nextDouble();
                System.out.print("Type second operand: ");
                double operand2 = scanner.nextDouble();
                Operator operator = Operators.findOperator(option);
                double result = operator.operate(operand1, operand2);
                System.out.printf("%f %s %f = %f\n", operand1, operator.operator, operand2, result);
                System.out.println();
            }
        }
  4. If the action is anything else, find an operator and request two other inputs that will be the operands covering them to double:

      private static void printOptions() {
            System.out.println("Q (or q) - To quit");
            System.out.println("An operator. If not supported, will use sum.");
            System.out.print("Type your option: ");
        }
    }

    对找到的操作员调用操作方法,并将结果打印到控制台。

活动 26:删除字符串中的重复字符

解决方案:

  1. 创建一个独特的类,如下所示:

    public class Unique {
  2. 创建一个名为removeDups的新方法,该方法接受并返回一个字符串。这就是我们的算法的发展方向。此方法应为公共静态

    public static String removeDups(String string){
  3. 在方法内部,检查字符串是否为 null、空或长度为 1。如果这些情况中有一个是真的,那么只需返回原始字符串,因为不需要进行检查:

    if (string == null)
               return string;
           if (string == "")
               return string;
           if (string.length() == 1)
               return string;
  4. 创建一个名为结果的空字符串。这将是要返回的唯一字符串:

    String result = "";
  5. 创建从0到传入方法的字符串长度的 for 循环。在循环的中,获取字符串当前索引处的字符。将变量命名为c**。同时创建一个名为isDuplicate布尔值,并将其初始化为false。当我们遇到副本时,我们会将其更改为true

    for (int i = 0; i < string.length() ; i++){
               char c = string.charAt(i);
               boolean isDuplicate = false;
    ```** 
  6. 为从结果的0长度()的循环创建另一个嵌套的。在循环的中,也可以获得结果当前索引处的字符。命名为d**。比较c和 d。如果它们相等,则将isDuplicate设置为truebreak。关闭回路的内部,进入回路的第一个回路。检查是否为重复是否为假。如果是,则在结果中追加c。跳出循环的第一个并返回结果。我们的算法到此结束:

    for (int j = 0; j < result.length(); j++){
                   char d = result.charAt(j);
                   if (c  == d){ //duplicate found
                       isDuplicate = true;
                       break;
                   }
               }
               if (!isDuplicate)
                   result += ""+c;
           }
           return result;
       }
    ```** 
  7. Create a main() method as follows:

    public static void main(String[] args){
           String a = "aaaaaaa";
           String b = "aaabbbbb";
           String c = "abcdefgh";
           String d = "Ju780iu6G768";
           System.out.println(removeDups(a));
           System.out.println(removeDups(b));
           System.out.println(removeDups(c));
           System.out.println(removeDups(d));
       }
    }

    结果如下:

图 6.32:唯一类的输出

完整代码如下:

public class Unique {
   public static String removeDups(String string){
       if (string == null)
           return string;
       if (string == "")
           return string;
       if (string.length() == 1)
           return string;
      String result = "";
       for (int i = 0; i < string.length() ; i++){
           char c = string.charAt(i);
           boolean isDuplicate = false;
           for (int j = 0; j < result.length(); j++){
               char d = result.charAt(j);
               if (c  == d){ //duplicate found
                   isDuplicate = true;
                   break;
               }
           }
           if (!isDuplicate)
               result += ""+c;
       }
       return result;
   }
public static void main(String[] args){
       String a = "aaaaaaa";
       String b = "aaabbbbb";
       String c = "abcdefgh";
       String d = "Ju780iu6G768";
       System.out.println(removeDups(a));
       System.out.println(removeDups(b));
       System.out.println(removeDups(c));
       System.out.println(removeDups(d));
   }
}

结果如下:

Figure 6.30: Output of Unique class

图 6.33:唯一类的输出

第 7 课:Java 集合框架和泛型

活动 27:使用具有初始容量的数组从 CSV 读取用户

解决方案:

  1. 使用**main()**方法

    public class UseInitialCapacity {
      public static final void main (String [] args) throws Exception {
      }
    }

    创建一个名为UseInitialCapacity的类

  2. 添加一个常量字段,该字段将作为阵列的初始容量。它也将在阵列需要增长时使用:

    private static final int INITIAL_CAPACITY = 5;
  3. 添加一个将调整数组大小的静态方法。它接收两个参数:一个用户数组和一个表示数组新大小的int。它还应该返回一个用户数组。使用System.arraycopy实现调整大小算法,就像您在上一个练习中所做的那样。请注意,新大小可能小于传入数组的当前大小:

    private static User[] resizeArray(User[] users, int newCapacity) {
      User[] newUsers = new User[newCapacity];
      int lengthToCopy = newCapacity > users.length ? users.length : newCapacity;
      System.arraycopy(users, 0, newUsers, 0, lengthToCopy);
      return newUsers;
    }
  4. 编写另一个静态方法,将用户从 CSV 文件加载到数组中。它需要确保阵列具有接收从文件加载的用户的能力。您还需要确保在完成加载用户后,阵列末尾不包含额外的插槽:

    public static User[] loadUsers(String pathToFile) throws Exception {
      User[] users = new User[INITIAL_CAPACITY];
      BufferedReader lineReader = new BufferedReader(new FileReader(pathToFile));
      try (CSVReader reader = new CSVReader(lineReader)) {
        String [] row = null;
        while ( (row = reader.readRow()) != null) {
          // Reached end of the array
          if (users.length == reader.getLineCount()) {
            // Increase the array by INITIAL_CAPACITY
            users = resizeArray(users, users.length + INITIAL_CAPACITY);
          }
          users[users.length - 1] = User.fromValues(row);
        } // end of while
    
        // If read less rows than array capacity, trim it
        if (reader.getLineCount() < users.length - 1) {
          users = resizeArray(users, reader.getLineCount());
        }
      } // end of try
    
      return users;
    }
  5. main方法中,调用 load users 方法并打印加载的用户总数:

    User[] users = loadUsers(args[0]);
    System.out.println(users.length);
  6. Add imports:

    import java.io.BufferedReader;
    import java.io.FileReader;

    结果如下:

    27

活动 28:使用向量读取真实数据集

解决方案:

  1. 开始之前,请将您的CSV 加载程序更改为支持无头文件。为此,添加一个新的构造函数,该构造函数接收一个布尔值,告知它是否应该忽略第一行:

    public CSVReader(BufferedReader reader, boolean ignoreFirstLine) throws IOException {
      this.reader = reader;
      if (ignoreFirstLine) {
        reader.readLine();
      }
    }
  2. 更改旧构造函数以调用新构造函数,并传递 true 以忽略第一行。这将避免您返回并更改任何现有代码:

    public CSVReader(BufferedReader reader) throws IOException {
      this(reader, true);
    }
  3. 使用main方法

    public class CalculateAverageSalary {
      public static void main (String [] args) throws Exception {
      }
    }

    创建一个名为CalculateeAgregalary的类

  4. 创建另一个从 CSV 读取数据并将工资加载到向量的方法。方法应在末尾返回向量:

    private static Vector loadWages(String pathToFile) throws Exception {
      Vector result = new Vector();
      FileReader fileReader = new FileReader(pathToFile);
      BufferedReader bufferedReader = new BufferedReader(fileReader);
      try (CSVReader csvReader = new CSVReader(bufferedReader, false)) {
        String [] row = null;
        while ( (row = csvReader.readRow()) != null) {
          if (row.length == 15) { // ignores empty lines
            result.add(Integer.parseInt(row[2].trim()));
          }
        }
      }
      return result;
    }
  5. main方法中,调用loadSweels方法,并将加载的工资存储在向量中。还存储应用程序启动的初始时间:

    Vector wages = loadWages(args[0]);
    long start = System.currentTimeMillis();
  6. 初始化三个变量以存储所有工资的最低、最高和总和:

    int totalWage = 0;
    int maxWage = 0;
    int minWage = Integer.MAX_VALUE;
  7. 在 for each 循环中,处理所有工资,存储最小值、最大值并将其添加到总和中:

    for (Object wageAsObject : wages) {
      int wage = (int) wageAsObject;
      totalWage += wage;
      if (wage > maxWage) {
        maxWage = wage;
      }
      if (wage < minWage) {
        minWage = wage;
      }
    }
  8. 最后,打印加载的工资数量以及加载和处理工资所需的总时间。同时打印平均工资、最低工资和最高工资:

    System.out.printf("Read %d rows in %dms\n", wages.size(), System.currentTimeMillis() - start);
    System.out.printf("Average, Min, Max: %d, %d, %d\n", totalWage / wages.size(), minWage, maxWage);
  9. Add imports:

    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.util.Vector;

    结果如下:

    Read 32561 rows in 198ms
    Average, Min, Max: 57873, 12285, 1484705

活动 29:迭代用户向量

解决方案:

  1. 使用main方法

    public class IterateOnUsersVector {
      public static void main(String [] args) throws IOException {
      }
    }

    创建一个名为IterateOnUsersVector的新类

  2. 在 main 方法中,调用UsersLoader.loadUsersInVector,将从命令行传递的第一个参数作为要从中加载的文件,并将数据存储在向量中:

    Vector users = UsersLoader.loadUsersInVector(args[0]);
  3. 对每个循环使用**迭代用户向量,并将用户信息打印到控制台:

    for (Object userAsObject : users) {
      User user = (User) userAsObject;
      System.out.printf("%s - %s\n", user.name, user.email);
    }
    ```** 
  4. Add imports:

    import java.io.IOException;
    import java.util.Vector;

    结果如下:

    Bill Gates - william.gates@microsoft.com
    Jeff Bezos - jeff.bezos@amazon.com
    Marc Benioff - marc.benioff@salesforce.com
    Bill Gates - william.gates@microsoft.com
    Jeff Bezos - jeff.bezos@amazon.com
    Sundar Pichai - sundar.pichai@google.com
    Jeff Bezos - jeff.bezos@amazon.com
    Larry Ellison - lawrence.ellison@oracle.com
    Marc Benioff - marc.benioff@salesforce.com
    Larry Ellison - lawrence.ellison@oracle.com
    Jeff Bezos - jeff.bezos@amazon.com
    Bill Gates - william.gates@microsoft.com
    Sundar Pichai - sundar.pichai@google.com
    Jeff Bezos - jeff.bezos@amazon.com
    Sundar Pichai - sundar.pichai@google.com
    Marc Benioff - marc.benioff@salesforce.com
    Larry Ellison - lawrence.ellison@oracle.com
    Marc Benioff - marc.benioff@salesforce.com
    Jeff Bezos - jeff.bezos@amazon.com
    Marc Benioff - marc.benioff@salesforce.com
    Bill Gates - william.gates@microsoft.com
    Sundar Pichai - sundar.pichai@google.com
    Larry Ellison - lawrence.ellison@oracle.com
    Bill Gates - william.gates@microsoft.com
    Larry Ellison - lawrence.ellison@oracle.com
    Jeff Bezos - jeff.bezos@amazon.com
    Sundar Pichai - sundar.pichai@google.com

活动 30:使用哈希表对数据进行分组

解决方案:

  1. 创建一个名为GroupWageByEducation的类,使用方法:

    public class GroupWageByEducation {
      public static void main (String [] args) throws Exception {
      }
    }
  2. 创建一个静态方法,该方法创建并返回一个哈希表,其中包含字符串类型的键和整数类型向量的值:

    private static Hashtable<String, Vector<Integer>> loadWages(String pathToFile) throws Exception {
      Hashtable<String, Vector<Integer>> result = new Hashtable<>();
      return result;
    }
  3. 在创建哈希表和返回它之间,从 CSV 加载行,确保它们具有正确的格式:

    FileReader fileReader = new FileReader(pathToFile);
    BufferedReader bufferedReader = new BufferedReader(fileReader);
    try (CSVReader csvReader = new CSVReader(bufferedReader, false)) {
      String [] row = null;
      while ( (row = csvReader.readRow()) != null) {
        if (row.length == 15) {
        }
      }
    }
  4. 中,如果循环中,则获取记录的教育水平和工资:

    String education = row[3].trim();
    int wage = Integer.parseInt(row[2].trim());
  5. 哈希表中找到对应于当前教育水平的向量,并将新工资添加到其中:

    // Get or create the vector with the wages for the specified education
    Vector<Integer> wages = result.getOrDefault(education, new Vector<>());
    wages.add(wage);
    // Ensure the vector will be in the hashtable next time
    result.put(education, wages);
  6. 在 main 方法中,调用您的loadsweels方法,将命令行中的第一个参数作为要从中加载数据的文件传递到:

    Hashtable<String,Vector<Integer>> wagesByEducation = loadWages(args[0]);
  7. 对于每个循环,使用哈希表**条目进行迭代,对于每个条目,获取相应工资的向量,并为其初始化最小、最大和求和变量:

    for (Entry<String, Vector<Integer>> entry : wagesByEducation.entrySet()) {
      Vector<Integer> wages = entry.getValue();
      int totalWage = 0;
      int maxWage = 0;
      int minWage = Integer.MAX_VALUE;
    }
    ```** 
  8. 初始化变量后,迭代所有工资并存储最小值、最大值和总和值:

    for (Integer wage : wages) {
      totalWage += wage;
      if (wage > maxWage) {
        maxWage = wage;
      }
      if (wage < minWage) {
        minWage = wage;
      }
    }
  9. 然后,打印为指定条目找到的信息,该信息表示教育程度:

    System.out.printf("%d records found for education %s\n", wages.size(), entry.getKey());
    System.out.printf("\tAverage, Min, Max: %d, %d, %d\n", totalWage / wages.size(), minWage, maxWage);
  10. Add imports:

```java
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Hashtable;
import java.util.Map.Entry;
import java.util.Vector;
```

结果如下:

```java
1067 records found for education Assoc-acdm
        Average, Min, Max: 193424, 19302, 1455435
433 records found for education 12th
        Average, Min, Max: 199097, 23037, 917220
1382 records found for education Assoc-voc
        Average, Min, Max: 181936, 20098, 1366120
5355 records found for education Bachelors
        Average, Min, Max: 188055, 19302, 1226583
51 records found for education Preschool
        Average, Min, Max: 235889, 69911, 572751
10501 records found for education HS-grad
        Average, Min, Max: 189538, 19214, 1268339
168 records found for education 1st-4th
        Average, Min, Max: 239303, 34378, 795830
333 records found for education 5th-6th
        Average, Min, Max: 232448, 32896, 684015
576 records found for education Prof-school
        Average, Min, Max: 185663, 14878, 747719
514 records found for education 9th
        Average, Min, Max: 202485, 22418, 758700
1723 records found for education Masters
        Average, Min, Max: 179852, 20179, 704108
933 records found for education 10th
        Average, Min, Max: 196832, 21698, 766115
413 records found for education Doctorate
        Average, Min, Max: 186698, 19520, 606111
7291 records found for education Some-college
        Average, Min, Max: 188742, 12285, 1484705
646 records found for education 7th-8th
        Average, Min, Max: 188079, 20057, 750972
1175 records found for education 11th
        Average, Min, Max: 194928, 19752, 806316
```

活动 31:对用户进行排序

解决方案:

  1. 编写一个 comparator 类,按 ID 对用户进行比较:

    import java.util.Comparator;
    public class ByIdComparator implements Comparator<User> {
      public int compare(User first, User second) {
        if (first.id < second.id) {
          return -1;
        }
        if (first.id > second.id) {
          return 1;
        }
        return 0;
      }
    }
  2. 编写一个 comparator 类通过电子邮件对用户进行比较:

    import java.util.Comparator;
    public class ByEmailComparator implements Comparator<User> {
      public int compare(User first, User second) {
        return first.email.toLowerCase().compareTo(second.email.toLowerCase());
      }
    }
  3. 编写一个 comparator 类来按名称比较用户:

    import java.util.Comparator;
    public class ByNameComparator implements Comparator<User> {
      public int compare(User first, User second) {
        return first.name.toLowerCase().compareTo(second.name.toLowerCase());
      }
    }
  4. 创建一个名为SortUsers的新类,使用main方法加载通过电子邮件键入的唯一用户:

    public class SortUsers {
      public static void main (String [] args) throws IOException {
        Hashtable<String, User> uniqueUsers = UsersLoader.loadUsersInHashtableByEmail(args[0]);
      }
    }
  5. 加载用户后,将用户转移到一个用户向量中以保持顺序,因为哈希表不这样做:

    Vector<User> users = new Vector<>(uniqueUsers.values());
  6. 要求用户选择他想要对用户进行排序的字段,并从标准输入中收集输入:

    Scanner reader = new Scanner(System.in);
    System.out.print("What field you want to sort by: ");
    String input = reader.nextLine();
  7. 使用开关语句中的输入选择要使用的比较器。如果输入无效,打印友好消息并退出:

    Comparator<User> comparator;
    switch(input) {
      case "id":
        comparator = newByIdComparator();
        break;
      case "name":
        comparator = new ByNameComparator();
        break;
      case "email":
        comparator = new ByEmailComparator();
        break;
      default:
        System.out.printf("Sorry, invalid option: %s\n", input);
        return;
    }
  8. 告诉用户您将按哪个字段进行排序,并对用户向量进行排序:

    System.out.printf("Sorting by %s\n", input);
    Collections.sort(users, comparator);
  9. 使用为每个循环打印用户:

    for (User user : users) {
      System.out.printf("%d - %s, %s\n", user.id, user.name, user.email);
    }
  10. 添加导入:

```java
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Scanner;
import java.util.Vector;
```

结果如下:

5 unique users found.
What field you want to sort by: email
Sorting by email
30 - Jeff Bezos, jeff.bezos@amazon.com
50 - Larry Ellison, lawrence.ellison@oracle.com
20 - Marc Benioff, marc.benioff@salesforce.com
40 - Sundar Pichai, sundar.pichai@google.com
10 - Bill Gates, william.gates@microsoft.com

第 8 课:Java 高级数据结构

活动 32:用 Java 创建自定义链表

解决方案:

  1. 创建一个名为SimpleObjLinkedList的类。

    public class SimpleObjLinkedList {
  2. 创建一个名为 Node 的类,该类表示链表中的每个元素。每个节点都将有一个需要保存的对象,并且它将有一个对下一个节点的引用。LinkedList类将有一个对 head 节点的引用,并将能够使用node.getNext()遍历到下一个节点。头部是第一个元素,我们可以通过在当前节点中移动next移动到下一个元素。这样,我们可以遍历到列表的最后一个元素:

    static class Node {
    Object data;
    Node next;
    Node(Object d) {
    data = d;
    next = null;
    }
    Node getNext() {
    return next;
    }
    void setNext(Node node) {
    next = node;
    }
    Object getData() {
    return data;
    }
    }
  3. 实现一个**toString()**方法来表示这个对象。从 head 节点开始,迭代所有节点,直到找到最后一个节点。在每次迭代中,构造存储在每个节点中的对象的字符串表示:

    public String toString() {
    String delim = ",";
    StringBuffer stringBuf = new StringBuffer();
    if (head == null)
    return "LINKED LIST is empty";    
    Node currentNode = head;
    while (currentNode != null) {
    stringBuf.append(currentNode.getData());
    currentNode = currentNode.getNext();
    if (currentNode != null)
    stringBuf.append(delim);
    }
    return stringBuf.toString();
    }
  4. 执行**添加(对象项)方法,可以将任何项/对象添加到此列表中。通过传递newItem=new Node(item)**项,构造一个新的 Node 对象。从 head 节点开始,爬网到列表的末尾。在最后一个节点中,将下一个节点设置为我们新创建的节点(newItem。增加索引:

    // appends the specified element to the end of this list.    
    public void add(Object element) {
    // create a new node
    Node newNode = new Node(element);
    //if head node is empty, create a new node and assign it to Head
    //increment index and return
    if (head == null) {
    head = newNode;
    return;
    }
    Node currentNode = head;
    // starting at the head node
    // move to last node
    while (currentNode.getNext() != null) {
    currentNode = currentNode.getNext();
    }
    // set the new node as next node of current
    currentNode.setNext(newNode);
    }
  5. 执行get(整数索引方法)根据索引从列表中检索项目。索引不得小于 0。编写逻辑以爬网到指定的索引,标识节点,并从节点返回值。

    public Object get(int index) {
    // Implement the logic returns the element
    // at the specified position in this list.
    if (head == null || index < 0)
    return null;        
    if (index == 0){
    return head.getData();
    }    
    Node currentNode = head.getNext();
    for (int pos = 0; pos < index; pos++) {
    currentNode = currentNode.getNext();
    if (currentNode == null)
    return null;
    }
    return currentNode.getData();
    }
  6. 执行删除(整数索引)方法,根据索引从列表中删除该项。写入逻辑以爬网到指定索引之前的节点并标识该节点。在此节点中,将下一个设置为获取下一个()。如果找到并删除了元素,则返回 true。如果未找到元素,则返回 false:

    public boolean remove(int index) {
    if (index < 0)
    return false;
    if (index == 0)
    {
    head = null;
    return true;
    }
    Node currentNode = head;
    for (int pos = 0; pos < index-1; pos++) {
    if (currentNode.getNext() == null)
    return false;
    currentNode = currentNode.getNext();
    }    
    currentNode.setNext(currentNode.getNext().getNext());
    return true;
    }
  7. Create a member attribute of type Node (pointing to the head node). Write a main method, create an object of SimpleObjLinkedList, and add five strings, one after the other ("INPUT-1", "INPUT-2", "INPUT-3", "INPUT-4","INPUT-5"), to it respectively. Print the SimpleObjLinkedList object. In the main method, get the item from the list using get(2) and print the value of the item retrieved. Also, remove the item from list remove(2) and print the value of the list. One element should have been deleted from the list:

    Node head;    
        public static void main(String[] args) {
            SimpleObjLinkedList list = new SimpleObjLinkedList();
            list.add("INPUT-1");
            list.add("INPUT-2");
            list.add("INPUT-3");
            list.add("INPUT-4");
            list.add("INPUT-5");
            System.out.println(list);
            System.out.println(list.get(2));
            list.remove(3);
            System.out.println(list);
    }
    }

    结果如下:

    [INPUT-1 ,INPUT-2 ,INPUT-3 ,INPUT-4 ,INPUT-5 ]
    INPUT-3
    [INPUT-1 ,INPUT-2 ,INPUT-3 ,INPUT-5 ]

活动 33:实现 BinarySearchTree 类中的方法,以查找 BST 中的最高值和最低值

解决方案:

  1. 以我们在上一个练习中使用的相同类为例:BinarySearchTree。添加新方法int****getLow(),查找 BST 中的最低值并返回。正如我们了解的 BST 一样,最左边的节点将是所有值中最低的。迭代所有左节点,直到到达一个空的左节点并获得其根的值:

        /**
         * As per BST, the left most node will be lowest of the all. iterate all the
         * left nodes until we reach empty left and get the value of it root.
         * @return int lowestValue
         */
        public int getLow() {
            Node current = parent;
            while (current.left != null) {
                current = current.left;
            }
            return current.data;
        }
  2. 添加新方法int****getHigh(),在 BST 中查找最高值并返回。正如我们了解的 BST 一样,最右边的节点将是所有值中最高的。迭代所有正确的节点,直到到达一个空的正确节点并获得其根的值:

        /**
         * As per BST, the right most node will be highest of the all. iterate all
         * the right nodes until we reach empty right and get the value of it root.
         * @return int highestValue
         */
        public int getHigh() {
            Node current = parent;
            while (current.right != null) {
                current = current.right;
            }
            return current.data;
        }
  3. In the main method, construct a BST, add values to it, and then print the highest and lowest values by calling getLow() and getHigh():

    /**
         * Main program to demonstrate the BST functionality.
         * - Adding nodes
         * - finding High and low 
         * - Traversing left and right
         * @param args
         */
        public static void main(String args[]) {
            BinarySearchTree bst = new BinarySearchTree();
            // adding nodes into the BST
            bst.add(32);
            bst.add(50);
            bst.add(93);
            bst.add(3);
            bst.add(40);
            bst.add(17);
            bst.add(30);
            bst.add(38);
            bst.add(25);
            bst.add(78);
            bst.add(10);
            //printing lowest and highest value in BST
            System.out.println("Lowest value in BST :" + bst.getLow());
            System.out.println("Highest value in BST :" + bst.getHigh());
        }

    结果如下:

    Lowest value in BST :3
    Highest value in BST :93

活动 34:使用枚举保存学院系详细信息

解决方案:

  1. 使用enum关键字创建DeptEnumenum。添加两个私有属性(字符串 deptNameint deptNo来保存要保存在枚举中的值。重写构造函数以获取首字母缩写和deptNo并将其放置在成员变量中。添加依附于构造函数的枚举常量:

        public enum DeptEnum {
        BE("BACHELOR OF ENGINEERING", 1), BCOM("BACHELOR OF COMMERCE", 2), BSC("BACHELOR OF SCIENCE",
                3), BARCH("BACHELOR OF ARCHITECTURE", 4), DEFAULT("BACHELOR", 0);
        private String acronym;
        private int deptNo;
        DeptEnum(String accr, int deptNo) {
            this.accronym = acr;
            this.deptNo = deptNo;
        }
  2. deptNamedeptNo

        public String getAcronym() {
            return acronym;
        }
        public int getDeptNo() {
            return deptNo;
        }

    添加吸气剂方法

  3. 让我们编写一个main方法和示例程序来演示枚举的使用:

    public static void main(String[] args) {
    // Fetching the Enum using Enum name as string
    DeptEnum env = DeptEnum.valueOf("BE");
    System.out.println(env.getAcronym() + " : " + env.getDeptNo());
    // Printing all the values of Enum
    for (DeptEnum e : DeptEnum.values()) {
    System.out.println(e.getAcronym() + " : " + e.getDeptNo());    }
    // Compare the two enums using the the equals() method or using //the == operator.                
    System.out.println(DeptEnum.BE == DeptEnum.valueOf("BE"));
    }
    }
  4. 输出:

    BACHELOR OF ENGINEERING : 1
    BACHELOR OF ENGINEERING : 1
    BACHELOR OF COMMERCE : 2
    BACHELOR OF SCIENCE : 3
    BACHELOR OF ARCHITECTURE : 4
    BACHELOR : 0
    True

活动 35:实施反向查找

解决方案:

  1. 创建一个 enum应用程序,声明常量 BE、BCOM、BSC 和 BARC,以及它们的完整表单和部门号。

    public enum App {
        BE("BACHELOR OF ENGINEERING", 1), BCOM("BACHELOR OF COMMERCE", 2), BSC("BACHELOR OF SCIENCE", 3), BARCH("BACHELOR OF ARCHITECTURE", 4), DEFAULT("BACHELOR", 0);
  2. 同时声明两个私有变量accronymdeptNo

        private String accronym;
        private int deptNo;
  3. 创建一个参数化构造函数,并使用作为参数传递的值分配变量accronymdeptNo

        App(String accr, int deptNo) {
            this.accronym = accr;
            this.deptNo = deptNo;
        }
  4. 声明一个返回变量accronym的公共方法getAccronym()和一个返回变量deptNo的公共方法getDeptNo()

        public String getAccronym() {
            return accronym;
        }
        public int getDeptNo() {
            return deptNo;
        }
  5. 实现反向查找,以获取课程名称,并在应用程序枚举中搜索相应的首字母缩略词。

        //reverse lookup 
        public static App get(String accr) {
            for (App e : App.values()) {
                if (e.getAccronym().equals(accr))
                    return e;
            }
            return App.DEFAULT;
        }
  6. Implement the main method, and run the program.

        public static void main(String[] args) {
    
            // Fetching Enum with value of Enum (reverse lookup)
            App noEnum = App.get("BACHELOR OF SCIENCE");
            System.out.println(noEnum.accronym + " : " + noEnum.deptNo);
            // Fetching Enum with value of Enum (reverse lookup)
    
            System.out.println(App.get("BACHELOR OF SCIENCE").name());
        }
    }

    您的输出应类似于:

    BACHELOR OF SCIENCE : 3
    BSC

第九课:异常处理

活动 36:处理数字用户输入错误

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 创建一个类加法器,然后点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 创建一个名为加法器

    import java.util.Scanner;
    public class Adder {

    的类

  5. 在**main()方法中,使用循环的从用户处读取值:

       public static void main(String[] args) {
           Scanner input = new Scanner(System.in);
           int total = 0;
           for (int i = 0; i < 3; i++) {
               System.out.print("Enter a whole number: ");
    ```** 
  6. 在同一循环中,检查是否输入了有效值。如果该值有效,请添加一个 try 块以计算三个数字的总和。

               boolean isValid = false;
               while (!isValid) {
                   if (input.hasNext()) {
                       String line = input.nextLine();
                       try {
                           int newVal = Integer.parseInt(line);
                           isValid = true;
                           total += newVal;
  7. catch 块应该提示用户输入有效的数字。

    } catch (NumberFormatException e) {
                           System.out.println("Please provide a valid whole number");
                       }
                   }
               }
           }
  8. Print the sum:

    System.out.println("Total is " + total);
       }
    }

    将结果打印到控制台。以下是无错误案例的示例输出:

    Enter a whole number: 10
    Enter a whole number: 11
    Enter a whole number: 12
    Total is 33

    下面是带有错误的运行的示例输出:

    Enter a whole number: 10
    Enter a whole number: hello
    Please provide a valid whole number
    11.1
    Please provide a valid whole number
    11
    Enter a whole number: 12
    Total is 33

活动 37:用 Java 编写自定义异常

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 输入过山车,类名为,点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 创建异常类TooyangException

    class TooYoungException extends Exception {
       int age;
       String name;
       TooYoungException(int age, String name) {
           this.age = age;
           this.name = name;
       }
    }
  5. 在**main()**中,创建一个循环,读取访问者的姓名:

    public class RollerCoasterWithAge {
       public static void main(String[] args) {
           Scanner input = new Scanner(System.in);
           while (true) {
               System.out.print("Enter name of visitor: ");
               String name = input.nextLine().trim();
               if (name.length() == 0) {
                   break;
               }
  6. try块,读取访客的年龄,如果年龄在 15 岁以下,抛出TooyangException,打印乘坐过山车的访客姓名:

               try {
                   System.out.printf("Enter %s's age: ", name);
                   int age = input.nextInt();
                   input.nextLine();
                   if (age < 15) {
                       throw new TooYoungException(age, name);
                   }
                   System.out.printf("%s is riding the roller coaster.\n", name);
  7. catch 块将显示为 15 岁以下访客显示的消息:

               } catch (TooYoungException e) {
                   System.out.printf("%s is %d years old, which is too young to ride.\n", e.name, e.age);
               }
           }
       }
    }

活动 38:处理块中的多个异常

解决方案:

  1. 右键点击src文件夹,选择新建****类

  2. 输入过山车,类名为年龄和高度,点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 创建异常类TooyangException

    class TooYoungException extends Exception {
       int age;
       String name;
       TooYoungException(int age, String name) {
           this.age = age;
           this.name = name;
       }
    }
  5. 创建异常类TooShortException

    class TooShortException extends Exception {
       int height;
       String name;
       TooShortException(int height, String name) {
           this.height = height;
           this.name = name;
       }
    }
  6. 在**main()**中,创建一个循环,读取访问者的姓名:

    public class RollerCoasterWithAgeAndHeight {
       public static void main(String[] args) {
           Scanner input = new Scanner(System.in);
           while (true) {
               System.out.print("Enter name of visitor: ");
               String name = input.nextLine().trim();
               if (name.length() == 0) {
                   break;
               }
  7. try块,读取游客年龄,15 岁以下抛出ToYoungException,130 岁以下抛出TooShortException,打印过山车游客姓名:

               try {
                   System.out.printf("Enter %s's age: ", name);
                   int age = input.nextInt();
                   input.nextLine();
                   if (age < 15) {
                       throw new TooYoungException(age, name);
                   }
                   System.out.printf("Enter %s's height: ", name);
                   int height = input.nextInt();
                   input.nextLine();
                   if (height < 130) {
                       throw new TooShortException(height, name);
                   }
                   System.out.printf("%s is riding the roller coaster.\n", name);
               } 
  8. catch 块将显示为 15 岁以下或 130 岁以下的访客显示的信息:

    catch (TooYoungException e) {
                   System.out.printf("%s is %d years old, which is too young to ride.\n", e.name, e.age);
               } catch (TooShortException e) {
                   System.out.printf("%s is %d cm tall, which is too short to ride.\n", e.name, e.height);
               }
           }
       }
    }

活动 39:处理多个自定义异常

解决方案:

  1. 右键-c 点击src文件夹,选择新建|

  2. 输入过山车,类名为年龄和高度,点击确定

  3. 导入java.util.Scanner包:

    import java.util.Scanner;
  4. 创建异常类TooyangException

    class TooYoungException extends Exception {
       int age;
       String name;
       TooYoungException(int age, String name) {
           this.age = age;
           this.name = name;
       }
    }
  5. 创建异常类TooShortException

    class TooShortException extends Exception {
       int height;
       String name;
       TooShortException(int height, String name) {
           this.height = height;
           this.name = name;
       }
    }
  6. 在**main()**中,创建一个循环,读取访问者的姓名:

    public class Main {
       public static void main(String[] args) {
           Scanner input = new Scanner(System.in);
           while (true) {
               System.out.print("Enter name of visitor: ");
               String name = input.nextLine().trim();
               if (name.length() == 0) {
                   break;
               }
  7. try块,读取游客年龄,15 岁以下抛出ToYoungException,130 岁以下抛出TooShortException,打印过山车游客姓名:

               try {
                   System.out.printf("Enter %s's age: ", name);
                   int age = input.nextInt();
                   input.nextLine();
                   if (age < 15) {
                       throw new TooYoungException(age, name);
                   }
                   System.out.printf("Enter %s's height: ", name);
                   int height = input.nextInt();
                   input.nextLine();
                   if (height < 130) {
                       throw new TooShortException(height, name);
                   }
                   System.out.printf("%s is riding the roller coaster.\n", name);
               } 
  8. 异常

    catch (TooYoungException e) {
                   System.out.printf("%s is %d years old, which is too young to ride.\n", e.name, e.age);
               } 

    创建一个 catch 块

  9. TooShortException

    catch (TooShortException e) {
                   System.out.printf("%s is %d cm tall, which is too short to ride.\n", e.name, e.height);
               } 

    创建一个 catch 块

  10. 创建一个 finally 块,打印用于护送访客离开场所的消息:

```java
finally {
               System.out.printf("Escorting %s outside the premises.\n", name);
           }
       }
   }
}
```