CodeGym
Posted on April 9, 2019
One of the strongest points of Java is OOP or Object Oriented Programming. That’s the reason why this language has became so popular and suits really well for projects of any scale.
What is Object Oriented Programming? It is not a magic but it looks like magic if you really get in to it. OOP is about how to build your software. A concept or rather a bunch of concepts that allows you to construct some specific interactions and relations between Java objects for effective development and usage of software.
Сlassical version includes 3 + 1 main OOPs concepts. Let’s start with the classics.
The Object
Objects in Java as well as real-world objects have two characteristics, state and behavior.
For example object Human has state (name, gender, sleeping…) and behavior (studying Java, walking, talking...). Any Java object stores its state in fields and exposes its behavior through methods
Encapsulation
Data encapsulation is hiding internal data from the outside world, and accessing it only through publicly exposed methods.What does that mean? What data, hiding from whom? Hiding means restriction direct access to data members (fields) of a class.
How it works in Java:
- Fields are set to private
- Each field of class gets two special methods: a getter and a setter. Getter methods return the field. Setter methods let you change the value of the field in non-direct but legal way.
Example of encapsulation, Java code:
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Test{
public static void main(String[] args) {
Student firstStudent = new Student();
firstStudent.setName("John");
//now you can’t do this: firstStudent.name = “John”, the name field is private!
}
}
Why you should do it?
The main reason is simplification of your code changes. Imagine, you have an application for Hockey School and there is a class HockeyStudent
with two fields: name and age of a student when he joined school. Something like that:
public class HockeyStudent {
public String name;
public int ageOfEnrollment;
}
Public ageOfEnrollment
, no getters or setters… This class is used by many other classes and everything was ok before one guy said, that the int age is not enough. Some hockey players in group are almost one year older than others so it would be more convenient to apart them on two groups depending upon the month they were born. So new field ageOfEnrollment
should be array int[][] the first number is for full years and the second is for months. Now you should refactor all code that uses class Student
! However if your ageOfEnrollment
is private and you have getters and setters everything gets easier. If the requirement for setting the age of a student changes, just update the logic in the setter method setAgeOfEnrollment()
and your classes may continue using Student
without problems!
This example is somewhat artificial, but I hope it explains why using encapsulation is a great idea.
Inheritance
This principle is more understandable even without practicing. Don’t repeat yourself (DRY) could be the motto of inheritance concept.
Inheritance lets to create a child class that inherits the fields and methods of the parent class without redefinition them. Sure you can override the fields and methods of the parent class in child class, but it’s not a necessity. Also you can add new states and behaviors in child class. Parent classes sometimes are called superclasses or base classes, and child classes are known as subclasses. Java extends
keyword uses to implement the principle of inheritance in code.
How it works in Java:
- Create a Parent class.
- Create a Child class using
extends
keyword. - In Child’s class constructor use
super(parentField1, parentField2,...)
method to inherit the parent’s fields.
Constructor is a special method to initialize the newly created object. Constructor has the same name as its class name. There are two types of constructors: default (no-arg constructor) and parameterized constructor. Class must have at least one constructor (it is always is by default) and it can have a lot of them.
Every time you create a new object you call its constructor. In the example above the line
Student firstStudent = new Student();
You call the default constructor Student()
of class Student
using word new
Some rules:
- One class could have only one parent.
- One parent class may have many child classes.
- Child class can have its own child classes.
Example Inheritance, Java code
Let’s create a Phone
class.
public class Phone {
int price;
double weight;
//constructor
public Phone(int price, double weight) {
this.price = price;
this.weight = weight;
}
void orderPhone(){
System.out.println("ordering phone...");
}
}
Well, there are different type of phones, so let’s create two child classes, one for Android phones and the second for iPhones, and add some fields and methods their parent didn’t have. The fields that their parent has, let’s call in constructors using super()
.
Java inheritance example
public class Android extends Phone {
//some new fields
String androidVersion;
int screenSize;
String secretDeviceCode;
//constructor
public Android(int price, double weight, String androidVersion, int screenSize, String secretDeviceCode) {
super (price, weight); //Android inherits Phone’s fields
//this - reference to the current class object
//super - reference to the parent class object
this.androidVersion = androidVersion;
this.screenSize = screenSize;
this.secretDeviceCode = secretDeviceCode;
}
//new method specific for Android only, but not for Phone class
void installNewAndroidVersion() {
System.out.println("installNewAndroidVersion invoked...");
}
}
public class IPhone extends Phone {
int price;
double weight;
boolean fingerPrint;
public IPhone(int price, double weight, boolean fingerPrint) {
super (price, weight, countryProduced);
System.out.println("IPhone constructor was invoked...");
this.fingerPrint = fingerPrint;
}
void deleteIPhoneFromDb() {
System.out.println("deleteIPhoneFromDb invoked...");
}
@Override //This is about polymorphism, see below
void orderPhone(){
System.out.println("ordering my new iPhone and deleting the old one...");
}
}
So, once more: in Java inheritance allows you to extend a class with child classes that inherit the fields and methods of the parent class. It’s an excellent way to achieve code reusability.
Polymorphism
Polymorphism is the ability of an object to take on different forms or rather to act in different ways. Usually polymorphism in Java happens if a parent class reference is used to refer to a child class object.
What does it mean and how it works in Java:
What is polymorphism in Java? In general that means you can use the same method name for different purposes. There are two two types of polymorphism in Java: method overriding (dynamic polymorphism) and method overloading (static polymorphism).
Method overriding
You can override parent’s method in child class forcing it to work in different way. Let’s create a parent class Musician with a play() method.
Java Polymorphism example
public class Musician {
String name;
int age;
//default constructor
public Musician() {
}
//parameterized constructor
public Musician(String name, int age) {
this.name = name;
this.age = age;
}
void play() {
System.out.println("I am playing my instrument...");
}
}
Musicians use different instruments. Let’s create two child classes Pianist
and Violinist
. They both will be playing using their own versions of play()
method thanks to polymorphism. For overriding you can use @Override
notation but it is not necessary.
public class Pianist extends Musician {
String favoritePianoType;
public Pianist(String name, int age, String favoritePianoType) {
super(name, age);
this.favoritePianoType = favoritePianoType;
}
@Override
void play(){
System.out.println("I am playing piano...");
}
}
The violin player could be soloist or orchestra participant. Let’s take it into consideration while overriding our play()
method.
public class Violinist extends Musician {
boolean isSoloist;
public Violinist(String name, int age, boolean isSoloist) {
super(name, age);
this.isSoloist = isSoloist;
}
@Override
void play(){
if (isSoloist)
System.out.println("I am playing violin solo...");
else
System.out.println("I am playing violin in Orchestra...");
}
}
Let’s create a Demo
class where we create three objects, one of every class and check out the result.
public class Demo {
public static void main(String[] args) {
Musician musician = new Musician();
Violinist violinist = new Violinist("John", 32,true);
Pianist pianist = new Pianist("Glen", 30, "Wooden");
System.out.println("Musician said:");
musician.play();
System.out.println("Violinist said:");
violinist.play();
System.out.println("Pianist said:");
pianist.play();
}
}
That’s what we’ve got here:
Musician said:
I am playing my instrument...
Violinist said:
I am playing violin solo…
Pianist said:
I am playing piano...
Every violinist and pianist is a musician, but not vice versa. That means you may use the musician’s play method if you don’t need to create a new one. Or you can call the parent’s method from child’s one using super
keyword. Let’s do it in Pianist’s code:
public class Pianist extends Musician {
String favoritePianoType;
@Override
void play(){
super.play();
System.out.println("I am playing piano...");
}
}
Now let’s call our Demo class. Here the result:
Musician said:
I am playing my instrument...
Violinist said:
I am playing violin solo...
Pianist said:
I am playing my instrument...
I am playing piano...
Method overloading
Method overloading means using various methods with the same name in the same class. They should be different by number, order, or types of their parameters. Say, pianist can play classics piano and electric piano. To play the last one this musician needs electricity. Let’s create two different play()
methods. The first one without parameters, for wooden instrument, and the second with electricity checker for electric piano.
public class Pianist extends Musician {
String name;
int age;
String favoritePianoType;
@Override
void play(){
super.play();
System.out.println("I am playing piano...");
}
void play(boolean isElectricity){
if (isElectricity) {
System.out.println("Electricity on");
System.out.println("I am playing piano...");
}
else System.out.println("I can't play this without electricity");
}
}
By the way, you can use the first play()
method in the second play(boolean)
method this way:
void play(boolean isElectricity){
if (isElectricity) {
System.out.println("Electricity on");
play();
}
else System.out.println("I can't play this without electricity");
}
Let’s add some lines in our Demo
class to check out our overloading:
public class Demo {
public static void main(String[] args) {
Musician musician = new Musician();
Violinist violinist = new Violinist("John", 23,true);
Pianist pianist = new Pianist("Glen", 30, "Wooden");
System.out.println("Musician said:");
musician.play();
System.out.println("Violinist said:");
violinist.play();
System.out.println("Pianist said:");
pianist.play();
System.out.println("Now pianist tries electronic Piano:");
pianist.play(true);
System.out.println("now electricity's gone and electronic Piano player said:");
pianist.play(false);
}
}
Here is the result:
Musician said:
I am playing my instrument...
Violinist said:
I am playing violin solo...
Pianist said:
I am playing my instrument...
I am playing piano...
Now pianist tries electronic Piano:
Electricity on
I am playing my instrument...
I am playing piano...
now electricity's gone and electronic Piano player said:
I can't play this without electricity
Java knows what method should be used according to its parameters and object’s type. That’s polymorphism.
Abstraction
When we describe an object we are trying to build a model of it. For example, we write a racer video game MyRacer with different cars. A player can choose one of them, then update or buy newer. So… What is a car? A car is pretty complicated thing, but if we are trying to create an arcade racing game (not simulator) we shouldn’t describe all thousands of cogs it contains.
We need its model, maximal speed, maneuverability, price, color… And maybe that’s enough. That’s a model of a car for this game.
Later in MyRacer 2 we decided to add tires that affect resilience on a road. Here the model is different because we need more details.
Let’s define Data Abstraction as the process of identifying only the important (or required) characteristics of an object and ignoring the irrelevant details.
There are different levels of abstraction. For example if you are a passenger of a bus, you should know how your bus looks and where it goes but you shouldn’t know how to drive it. If you are a bus driver you don’t need to know how to create a new bus, you should know how to drive it. But if you are a bus constructor you should go onto the lower level of abstraction…The details about the bus architecture are very important to you. Hope you understand what I mean.
How it works in Java:
Let’s build four level of abstraction in Java or rather in OOP from the lowes (the most particular) to the highest (more abstract).
Lower level of abstraction is a particular Object. It is an entity with a set of characteristics inherent in a specific instance of a class. It has specific field values
Template for creating objects is a Class. It’s a description of the set of objects of similar properties and internal structure.
Abstract class is an abstract description of the characteristics of a set of classes (acts as a template for inheritance by other classes). It has a high level of abstraction, therefore, it is impossible to create objects directly from the abstract class, only through the creation of objects from the child classes. Abstract class, may include methods with an implementation but not necessarily.
Interface is a Java programming language construct within which only abstract public methods and static property constants (final static) can be described. That is, as well as on the basis of abstract classes, it is impossible to generate objects on the basis of interfaces.
Btw, in Java 8 or later you can use in interfaces not only abstract methods and constants, but also default and static methods.
In Java interface defines a behavior and abstract class creates hierarchy. One interface may implement different classes.
Java interface example
interface Human {
public void struggle();
public void protect();
}
interface Vulcanian {
int earSharpnessAngle;
public void emotionOff(boolean isOn);
public void telepathy();
}
You can implement more than one interface
Class Spock implements Human, Vulcanian {
public void struggle() {
System.out.println (“I am struggling...”);
}
public void protect() {
System.out.println(“You are under my protection!”);
}
public void emotionOff(boolean isOn){
If (isOn) {
System.out.println(“I am turning off my emotions”);
isOn= !isOn;
}
}
public void telepathy() {
System.out.println(“Connecting to your brain... ”);
}
}
That’s all about the main object oriented programming concepts in Java for beginner students. Besides the 4 main principles of OOP, there are also association, aggregation and composition in Java. You may call them “additional OOP principles” and they deserve their own article.
Previously was published on CodeGym blog.
Posted on April 9, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.