The "pie of OOP"

The "pie of OOP" (see textbook) has 3 slices, illustrated here through examples:

Encapsulation

public class Circle {
   private double radius;
   public void setRadius(double value) { radius = value; }
   public double getRadius() { return radius; }
   public double doCircumference() { return 2 * Math.PI * radius; }
   public double doArea() { return Math.PI * radius * radius; }
}
The properties of a circle are encapsulated in the Circle class. The thing that it has (its state) is a radius. The things that it does (its behaviors) are to return and reset its radius and to calculate and return its area and circumference. Everything that a circle is and does are gathered together (encapsulated) in one place.

Inheritance

abstract class Employee {        /* Method bodies are omitted for clarity */
   private String employeeName;
   public Employee(String name) {  }
   public String getName() {  }  
   public abstract double getPay();
}
An Employee represents any kind of employee you can think of, such as employee paid by salary, hours worked, or widgets produced. Although conceptually an employee's pay can be calculated, this method cannot be implemented here because there is no one pay calculation algorithm that applies to any and all kinds of employees. So we declare it to be abstract. You cannot create an Employee object using the new operator.
public class HourlyEmployee extends Employee { /* Method bodies are omitted for clarity */
   private double hourlyRate;
   private int hours;
   public HourlyEmployee(String name, double rate, int hrs) {  }
   public double getPay () {   }
   public double getPayRate() {   }
   public void setPayRate(double newRate) {   }
   public int getHours() {   }
   public void setHours(int hrs) {   }
} 
public class SalaryEmployee extends Employee { /* Method bodies are omitted for clarity */
   private double salary;
   public SalaryEmployee(String name, double salry) {   }
   public double getPay() {   }
   public double getSalary() {   }
   public void setSalary(double newSalary) {   }
}  
Notice that HourlyEmployee and SalaryEmployee are able to implement the getPay() method each in their unique way.

Now we have a total of three classes: Employee, HourlyEmployee, and SalaryEmployee. HourlyEmployee, and SalaryEmployee both extend Employee. This means they inherit the (non-private) properties that Employee has.

In particular, every HourlyEmployee inherits the getName() method from Employee and inherits the rights and responsibilities of the getPay() method, although it has to define this method itself. We call Employee its parent, or super, class. The SalaryEmployee also inherits those properties from Employee.

Polymorphism

The term "polymorphism", broken into its three parts, is: poly = many, morph = forms, ism = practice. This is the practice of assigning many forms to the same method. To demonstrate this, we need a client to use the classes defined above:
public class EmployeeDemo {
   public static void main(String[] args) {
      Employee list[] = new Employee[4];  // Creates array but not employees
      list[0] = new HourlyEmployee("Joe Grushecky",12.5, 40);
      list[1] = new SalaryEmployee("Jane Asher", 1125.0);
      list[2] = new HourlyEmployee("Jessamyn Davis", 15.0, 40);
      list[3] = new SalaryEmployee("Johnny Goode", 950.0);
      for (Employee emp : list) {
         System.out.println( emp.getName() + "  " + emp.getPay() );
      }
   }
}
Since very HourlyEmployee is a Employee, and every SalaryEmployee is a Employee, the assignments to the four array elements are fine. The polymorphic magic happens inside the for loop!

During the first loop iteration, emp refers to list[0]. The call to emp.getPay() calls which of the three versions of getpay(): the one in Employee, or HourlyEmployee, or SalaryEmployee?

During the second loop iteration, emp refers to list[1]. The call to emp.getPay() calls which of the three versions of getpay(): the one in Employee, or HourlyEmployee, or SalaryEmployee?

During the third loop iteration, emp refers to list[2]. The call to emp.getPay() calls which of the three versions of getpay(): the one in Employee, or HourlyEmployee, or SalaryEmployee?

During the fourth loop iteration, emp refers to list[3]. The call to emp.getPay() calls which of the three versions of getpay(): the one in Employee, or HourlyEmployee, or SalaryEmployee?

The decision of which of the three versions (the "many forms") to call occurs dynamically, at runtime. This is polymorphism. You can see it requires inheritance.