Book Image

Java Fundamentals

By : Gazihan Alankus, Rogério Theodoro de Brito, Basheer Ahamed Fazal, Vinicius Isola, Miles Obare
Book Image

Java Fundamentals

By: Gazihan Alankus, Rogério Theodoro de Brito, Basheer Ahamed Fazal, Vinicius Isola, Miles Obare

Overview of this book

Since its inception, Java has stormed the programming world. Its features and functionalities provide developers with the tools needed to write robust cross-platform applications. Java Fundamentals introduces you to these tools and functionalities that will enable you to create Java programs. The book begins with an introduction to the language, its philosophy, and evolution over time, until the latest release. You'll learn how the javac/java tools work and what Java packages are - the way a Java program is usually organized. Once you are comfortable with this, you'll be introduced to advanced concepts of the language, such as control flow keywords. You'll explore object-oriented programming and the part it plays in making Java what it is. In the concluding chapters, you'll get to grips with classes, typecasting, and interfaces, and understand the use of data structures, arrays, strings, handling exceptions, and creating generics. By the end of this book, you will have learned to write programs, automate tasks, and follow advanced courses on algorithms and data structures or explore more advanced Java courses.
Table of Contents (12 chapters)
Java Fundamentals
Preface

Lesson 4: Object-Oriented Programming


Activity 12: Creating a Simple Class in Java

Solution:

  1. Create a new project in the IDE named Animals.

  2. In the project, create a new file named Animal.java under the src/ folder.

  3. Open Animal.java and paste in the following code:

    public class Animal {
        
    }
  4. Inside the curly braces, create the following instance variables to hold our data, as shown here:

    public class Animal {
            int legs;
            int ears;
            int eyes;
            String family;
            String name;
      
        }
  5. Below the instance variables, define two constructors. One will take no arguments and initialize legs to 4, ears to 2, and eyes to 2. The second constructor will take the value of legs, ears, and eyes as arguments and set those values:

    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:

    Note

    The methods that set values in an object are called setters, while those that get those values are called getters.

    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;
        }
    }

    We have finished constructing our Animal class. Let's continue and create a few instances of this class.

  7. Create a new file named Animals.java and copy the following code into it, as shown here:

    public class Animals {
        
           public static void main(String[] args){
            
           }
    }
  8. Create two objects of the Animal class:

    public class Animals {
            public static void main(String[] args){
                Animal cow = new Animal();
                Animal goat = new Animal();
            }
    }
  9. Let's create another animal with 2 legs, 2 ears and 2 eyes:

    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:

    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());
    
        }
    }

    The output of the preceding code is as follows:

    Cow
    Goat
    Anatide

    Figure 4.9: Output of the Animal class

Activity 13: Writing a Calculator Class

Solution:

  1. Create a Calculator class as follows:

    public class Calculator {
  2. Create three fields double operand1, double operand2, and String operator. Add a constructor that sets all three fields.

    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. In this class, add an operate method that will check what operator is ("+", "-", "x" or "/") and executes the correct operation, returning the result:

    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. Write a main() method as follows:

    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());
        }
    }

Activity 14: Creating a Calculator Using Java

Solution:

  1. Create a class Operator that has one String field initialized in the constructor that represents the operator. This class should have a default constructor that represents the default operator, which is sum. The Operator class should also have a method called operate that receives two doubles and return the result of the operator as a double. The default operation is sum:

    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. Create another class named Subtraction. It extends from Operator and override the operate method with each operation that it represents. It also need a no-argument constructor that calls super passing the operator that it represents:

    public class Subtraction extends Operator {
    
        public Subtraction() {
            super("-");
        }
    
        @Override
        public double operate(double operand1, double operand2) {
            return operand1 - operand2;
        }
    
    }
  3. Create another class named Multiplication. It extends from Operator and override the operate method with each operation that it represents. It also need a no-argument constructor that calls super passing the operator that it represents:

    public class Multiplication extends Operator {
    
        public Multiplication() {
            super("x");
        }
    
        @Override
        public double operate(double operand1, double operand2) {
            return operand1 * operand2;
        }
    
    }
  4. Create another class named Division. It extends from Operator and override the operate method with each operation that it represents. It also need a no-argument constructor that calls super passing the operator that it represents:

    public class Division extends Operator {
    
        public Division() {
            super("/");
        }
    
        @Override
        public double operate(double operand1, double operand2) {
            return operand1 / operand2;
        }
    
    }
  5. As the previous Calculator class, this one will also have an operate method, but it will only delegate to the operator instance. Last, write a main method that calls the new calculator a few times, printing the results of the operation for each time:

    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);
        }
    
    }

Activity 15: Understanding Inheritance and Polymorphism in Java

Solution:

  1. Create a Cat class that inherits from Animal:

     public class Cat extends Animal {
  2. Create instance variables owner, numberOfTeeth, and age as follows:

    //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);
    }
    }

    The output is as follows

    Cat
    Puppy
    2
    4
    2

Lesson 5: OOP in DepthActivity 16: Creating and Implementing Interfaces in Java

Solution:

  1. Open our Animals project from the previous lesson.

  2. Create a new interface called AnimalBehavior.

  3. In this create two methods void move() and void makeSound().

  4. Create a new public class called Cow and implement the AnimalBehavior interface. Override the two methods, but leave them blank for now.

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

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

    Edit the overridden methods to look like this:

    @Override
    public void move() {
        this.movementType = "Walking";
        this.onAnimalMoved();
    }
    
    @Override
    public void makeSound() {
        this.sound = "Moo";
        this.onAnimalMadeSound();
    }
  6. Create another interface called AnimalListener with the following methods:

    public interface AnimalListener {
       void onAnimalMoved();
       void onAnimalMadeSound();
    }
  7. Let the Cow class also implement this interface. Make sure that you override the two methods in the interface.

  8. Edit the two methods to look like this:

    @Override
       public void onAnimalMoved() {
           System.out.println("Animal moved: " + this.movementType);
       }
    
    @Override
    public void onAnimalMadeSound() {
        System.out.println("Sound made: " + this.sound);
    }
  9. Finally, create a main method to test your code:

    public static void main(String[] args){
       Cow myCow = new Cow();
       myCow.move();
       myCow.makeSound();
    }
    }
  10. Run the Cow class and view the output. It should look something like this:

    Animal moved: Walking
    Sound made: Moo

Activity 17: Using instanceof and Typecasting

Solution:

  1. Import Random package to generate random employees:

    import java.util.Random;
  2. Create an EmployeeLoader class that will serve as a datasource as follows:

    public class EmployeeLoader {
  3. Declare a static pseudo-random generator as follows:

    private static Random random = new Random(15);
  4. Generate a new randomly picked employee as follows:

    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. Create another file with SalesWithCommission class that extends Sales. Add a constructor that receives the gross sales as double and store it as a field. Also add a method called getCommission which returns a double that is the gross sales times 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. Write a class ShowSalaryAndCommission with main() method, that calls getEmployee() repeatedly inside a for loop and print the information about the Employee salary and tax. And if the employee is an instance of SalesWithCommission, also print his commission:

    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());
                }
            }
        }
    }

Activity 18: Understanding Typecasting in Java

Solution:

  1. Open our Animals project.

  2. Create a new class called AnimalTest and, inside it, create the main method:

    public class AnimalTest {
    
       public static void  main(String[] args){
    
       }
    }
  3. Inside the main method, create two variables:

    Cat cat = new Cat();
    Cow cow = new Cow();
  4. Print the owner of the cat:

    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);

    The error message is as follows:

    Figure 5.7: Exception while accessing the variables of the subclass for upcasting

    Reason: Since we did an upcast, we cannot access the variables of the subclass anymore.

  6. Print the sound of the cow:

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

    Animal animal1 = (Animal)cow;

    The error message is as follows:

    Figure 5.8: Exception while upcasting cow to Animal

    Reason: Cow does not inherit from the Animal class, so they don't share the same hierarchical tree.

  8. Downcast the animal to cat1 and print the owner again:

    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);
       }
    }

    The output is as follows:

    Figure 5.9: Output of the AnimalTest class

Activity 19: Implementing Abstract Classes and Methods in Java

Solution:

  1. Create a new project called Hospital and open it.

  2. Inside the src folder, create an abstract class called 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();

    We have finished our abstract class and method. Now, we will continue to inherit from it and implement this abstract method.

  4. Create a new class called Doctor that inherits from the Person class:

    public class Doctor extends Patient {
    }
  5. Override the getPersonType abstract method in our Doctor class. Return the string "Arzt". This is German for Doctor:

    @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";
       }
    }

    Now, we have the two classes. We will now test our code using a third test class.

  7. Create a third class called HospitalTest. We will use this class to test the two classes we created previously.

  8. Inside the HospitalTest class, create the main method:

    public class HospitalTest {
       public static void main(String[] args){
          
       }
    }
  9. Inside the main method, create an instance of Doctor and another instance of Patient:

    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?

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

    The output is as follows:

    Figure 5.10: Output on calling getPersonType()

Activity 20: Use abstract class to Encapsulate Common Logic

Solution:

  1. Create an abstract class GenericEmployee that has a constructor that receives the gross salary and stores that in a field. It should implement the Employee interface and have two methods: getGrossSalary() and getNetSalary(). The first will just return the value passed into the constructor. The latter will return the gross salary minus the result of calling getTax() method:

    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. Create a new generic version of each type of employee: GenericEngineer. It will need a constructor that receives gross salary and pass it to the super constructor. It also needs to implement the getTax() method, returning the correct tax value for each class:

    public class GenericEngineer extends GenericEmployee {
    
        public GenericEngineer(double grossSalary) {
            super(grossSalary);
        }
    
        @Override
        public double getTax() {
            return (22.0/100) * getGrossSalary();
        }
    
    }
  3. Create a new generic version of each type of employee: GenericManager. It will need a constructor that receives gross salary and pass it to the super constructor. It also needs to implement the getTax() method, returning the correct tax value for each class:

    public class GenericManager extends GenericEmployee {
    
        public GenericManager(double grossSalary) {
            super(grossSalary);
        }
    
        @Override
        public double getTax() {
            return (28.0/100) * getGrossSalary();
        }
    
    }
  4. Create a new generic version of each type of employee: GenericSales. It will need a constructor that receives gross salary and pass it to the super constructor. It also needs to implement the getTax() method, returning the correct tax value for each class:

    public class GenericSales extends GenericEmployee {
    
        public GenericSales(double grossSalary) {
            super(grossSalary);
        }
    
        @Override
        public double getTax() {
            return (19.0/100) * getGrossSalary();
        }
    
    }
  5. Create a new generic version of each type of employee: GenericSalesWithCommission. It will need a constructor that receives gross salary and pass it to the super constructor. It also needs to implement the getTax() method, returning the correct tax value for each class. Remember to also receive the gross sales on the GenericSalesWithCommission class, and add the method that calculates the commission:

    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. Add a new method getEmployeeWithSalary to your EmployeeLoader class. This method will generate a random salary between 70,000 and 120,000 and assign to the newly created employee before returning it. Remember to also provide a gross sales when creating a GenericSalesWithCommission employee:

    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. Write an application that calls the getEmployeeWithSalary method multiple times from inside for loop. This method will work like the one in the previous activity: print the net salary and tax for all employees. If the employee is an instance of GenericSalesWithCommission also print his commission.

    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());
                }
            }
        }
    
    }