Classes and Objects in Java (Detailed)

Adnan Yağmur
20 min readMay 8, 2023

--

Java is one of the most popular programming languages nowadays. It is an object-oriented programming language and hence, it is quite popular in the programming world. The advantages that Java offers in object-oriented programming have made it quite popular among software developers. So, what is object-oriented programming? Knowing about this topic is important to better understand Java.

Object-oriented programming (OOP) is an important concept in the programming world. OOP allows you to write code based on objects in a more organized way by making it possible to base the code on objects. This approach helps you to solve complex problems more easily and create a more modular codebase.

Java stands out as a powerful language in object-oriented programming. In Java, everything is done through objects and classes. Classes allow the bringing together of objects with similar properties, while objects are instances of classes. In Java, every object must be an instance of a class.

For example, let’s say you want to create a car object. Let the car have properties like color, make, model, etc. In Java, you need to create a Car class to define these properties. This class includes the properties and behaviors (methods) of the Car object. The car object, on the other hand, is an instance of the Car class, and corresponds to a real-world car.

Java’s object-oriented structure makes the code more understandable and less prone to errors. Additionally, the object-oriented approach makes it easier for larger teams to work on programming projects together. Writing the code in a more modular way allows different programmers to work on a specific class or object.

The general flow of the article is as follows: Classes Objects Inheritance Polymorphism Abstract Classes and Interfaces

1-) Classes

Overview of Classes

A class can be thought of as a plan or a template that defines the definition of an object. A class defines the properties (variables) and behaviors (methods) of an object. For example, let’s say we create a Car class. The properties of this class could be the car’s color, make, model, etc. Methods can define the car’s behaviors, such as starting it up, stopping it, increasing its speed, etc.

Classes enable related objects to come together. Therefore, the properties and behaviors of classes apply to objects derived from those classes. An object derived from a class can be thought of as an instance of that class. Objects have the properties of the class and can perform the class’s behaviors.

Objects are representations of entities in the real world. For example, a Car object can be thought of as a representation of a car in the real world. Situations where objects interact with each other are usually managed by classes that depend on each other. These interactions can occur when different objects communicate with each other, or when one object performs an operation on another.

Defining and Using Classes

Class definitions are created using a block of code that must include the following attributes:

Class name: Classes have names, which are typically chosen according to naming conventions.

Variables: The class’s variables are data types that define the object’s properties. For example, in a Car class, variables like color, make, and model could be defined.

Methods: The class’s methods are blocks of code that define the object’s behavior. For example, in a Car class, methods like move, brake, and accelerate could be defined.

Constructor: The class’s constructor determines how objects will be created. This method must have the same name as the class.

Example of Class Definition:

The following example shows how a Car class is defined:

public class Car {
// Variables
String color;
String make;
int model;

// Constructor
public Car(String color, String make, int model) {
this.color = color;
this.make = make;
this.model = model;
}

// Methods
public void move() {
System.out.println("The car is moving...");
}

public void brake() {
System.out.println("The car is stopping...");
}

public void accelerate(int speed) {
System.out.println("The car is moving at " + speed + " km/h...");
}
}

The code above defines the Car class. The properties (variables) of this class are color, make, and model. The constructor method allows objects to be created. The methods define the behaviors of Car objects.

The usage of classes is achieved by creating objects from the classes. These objects carry the properties and behaviors of the class and can be used within the program. Once objects are created from the class, they can perform operations using the class’s methods.

Example of Object Creation:

The following example demonstrates creating an object from the Car class:

public class CarApplication { 

public static void main(String[] args) {
// Creating a Car object
Car car1 = new Car("Red", "BMW", 2021);

// Accessing Car properties
System.out.println("Color: " + car1.color);
System.out.println("Make: " + car1.make);
System.out.println("Model: " + car1.model);

// Using Car methods
car1.move();
car1.accelerate(100);
car1.brake();
}
}

In the code above, an object of the Car class is created within the CarApplication class. This object has color, make, and model properties. In addition, the methods move(), accelerate(), and brake() perform the behaviors of the Car object.

Class components (fields, methods, constructors)

Classes are structures that define the properties and functions of objects. Within classes, three basic elements called class components are present: fields, methods, and constructors.

  1. Fields (fields or properties): Fields are variables that define the properties of a class. A class’s fields carry the properties of the class’s objects and determine the state of the objects. Fields can be defined with access modifiers such as public, private, or protected. Fields can be defined within or outside of the class. The following example shows the fields defined in the Car class:
public class Car {
// Variables
public String color;
private String make;
protected int model;
}

In the example above, it is shown that the Car class has three different fields named make, model, and color. The make field is defined as public, therefore accessible and changeable by everyone. The model field, on the other hand, is defined as private, meaning it can only be used within the Car class. The color field is defined as protected, so it can be accessed and modified by the subclass of the Car class.

2. Methods: Methods are functions that determine the behavior of a class. A class’s methods allow objects to perform operations. Methods can access and use the class’s fields. Like fields, methods can be defined with access modifiers such as public, private, or protected. The following example shows two different methods defined in the Car class:

public class Car {

//Fields
public String make;
private int model;
protected String color;

//Methods
public void move() {
System.out.println("The car is moving...");
}

public void brake() {
System.out.println("The car is stopping...");
}

}

In the above example, the Car class has two methods named move() and brake(). The move() method sets the object in motion, while the brake() method slows down or stops the object.

3. Constructors : The constructors are special methods called during the creation of class objects. Constructors are used to determine the initial state of the object. When an object is created, the constructor is automatically executed and sets the state of the object. Constructors are defined with the same name as the class and have no return value. The following example is an instance of the constructor defined in the Car class:

public class Car {
// Fields
public String brand;
private int model;
protected String color;

// Constructor method
public Car(String color, String brand, int model) {
this.color = color;
this.brand = brand;
this.model = model;
}
}

In the example above, the founder of the Car class takes color, brand, and model parameters and initializes the brand and model fields of the class. The word “this” is used to access the fields of the class.

Access modifiers (public, private, protected)

Access modifiers in Java are used to determine the extent to which a class or a class’s members can be accessed by other classes. There are three main access modifiers in Java: public, private, and protected.

  • public: Public access specifier indicates that a class or class member is accessible to everyone. In other words, it can be accessed from anywhere in any way. For example, a class or method declared as public can be used anywhere in the program.
  • private: Private access modifier specifies that a class or a class member can only be accessed by other members within the same class, meaning it cannot be accessed by other classes. It is used to preserve the privacy of variables and methods inside the class.
  • protected: The access specifier “protected” indicates that a class or class member can be accessed by other members within the same class, classes in the same package, or subclasses. This is used to allow subclasses of a class to access certain properties of the superclass.

These access modifiers can be used for any member of the class. For example, the variables or methods of a class can be public, private, or protected.

Access modifiers are one of the fundamental principles of object-oriented programming in Java. This principle determines how classes and objects will interact with each other and which classes or objects can access which data. This increases the security, modularity, and readability of programs.

2) Objects

The creation and usage of objects

Objects are instances created by deriving from a class. These instances inherit the properties and behaviors defined in the class. The creation of objects is done using a constructor method, where the properties of a class are defined and the behaviors of objects are determined.

For example, let’s create a Car class with properties such as brand, model, color, and behaviors such as starting, stopping, accelerating, and slowing down.

public class Car {
String brand; // marka
String model; // model
String color; // renk

public Car(String brand, String model, String color) {
this.brand = brand;
this.model = model;
this.color = color;
}

public void start() { // calistir
System.out.println(brand + " " + model + " started.");
}

public void stop() { // durdur
System.out.println(brand + " " + model + " stopped.");
}

public void accelerate(int speed) { // hizlan
System.out.println(brand + " " + model + " accelerated to " + speed + " km/h.");
}

public void decelerate(int speed) { // yavasla
System.out.println(brand + " " + model + " decelerated to " + speed + " km/h.");
}
}

The above class creates a class called Car, with properties of brand, model, and color. Additionally, the constructor method of the class, i.e., the constructor method, takes parameters that will be used when creating an object named Car and fills the class’s properties with these parameters.

Then, methods that determine the behavior of the class are defined (start(), stop(), accelerate(), decelerate()). These methods become available when a Car object is created.

We can write the following code to create a Car object:

Car car1 = new Car("Ford", "Mustang", "Black");

The keyword “new” is used to determine the location of an object in memory when an instance of the object is created. When an object is created, an area in the heap is allocated, and the properties of the object are stored here based on the structure of the class.

The “new” keyword also provides a reference to the created object. This reference can be used to modify the object, or to call properties or methods within the class.

This code creates an object from the Car class and fills the properties of brand, model, and color with the values “Ford”, “Mustang”, and “Black”.

The created Car object can be managed using the behaviors of the class. For example:

araba1.start();

This code runs the object named car1 and outputs “Ford Mustang started.”

Similarly, we can create another object from the Car class:

Car car2 = new Car("BMW", "M3", "Blue");

This code creates a new object from the Car class and fills the properties of brand, model, and color with the values “BMW”, “M3”, and “Blue”.

This object can be managed using the behaviors of the class as well:

car2.accelerate(50);

This code increases the speed of the car2 object by 50 km/h and outputs “BMW M3 accelerated by 50 km/h.”

Objects are useful for filling the properties of a class with different values and using the behaviors of the class in different ways. In addition, using objects can increase the modularity of a program and make it easier to understand.

Object references and their memory locations

In the Java programming language, objects and references are stored in memory. Memory can be thought of as the area where a computer temporarily stores data.

Objects are stored in a memory area called the heap. Typically, addresses in memory are expressed as hexadecimal (base-16) numbers, such as “0x7fff5b6ba5c0”. The heap area dynamically grows and shrinks, which is important for the program’s memory usage.

Object references, on the other hand, are used to access objects. A reference points to the address of an object in memory. Therefore, references indicate the location of objects in memory.

References are stored in a memory area called the stack. The stack memory area is reserved for data such as temporary variables, method calls, and other local variables.

For example, in the following code:

Car car1 = new Car("Ford", "Mustang", "Black");

“car1” is an object reference and is stored in the stack memory. The “new” keyword creates a Car object in the memory and returns the starting address of this object in the heap memory. This address is then assigned to the “car1” reference variable. The visual below can help you understand this better.

So, the reference variable “car1” points to the starting address of the Car object in the memory. This object is stored in the heap memory.

Object references can change during the lifetime of objects in memory. For example, in this code:

Car car2 = new Car("BMW", "M3", "Blue");
car1 = car2;

The reference variable “car1” points to the object pointed to by the reference variable “car2”. Therefore, the reference variable “car1” now points to the starting address of the BMW M3 car object in the heap memory. This means that object references make the use of objects in memory more flexible.

3-) Inheritance

Inheritance is an OOP concept that allows a class to create a new class by using the properties (fields) and behaviors (methods) of an existing class. This increases code reusability and also improves code maintainability.

In Java, inheritance is implemented using the keyword “extends”. For a class to inherit from another class, the class being inherited from must have a “public” or “protected” access level.

For example, let’s assume we have a class named “Car” and we want to create a subclass named “SportsCar”. The “SportsCar” class will inherit the properties and behaviors of the “Car” class.

public class Car {
public String brand; // marka
protected String model; // model
private String color; // renk

public Car(String brand, String model, String color) {
this.brand = brand;
this.model = model;
this.color = color;
}

public void move() { // hareketEt
System.out.println("Car is moving.");
}
}
public class SportsCar extends Car {
public SportsCar(String brand, String model, String color) {
super(brand, model, color);
}

public void turbo() {
System.out.println("Sports car has a turbo feature.");
}
}

In the example above, the SportsCar class inherits from the Car class. The SportsCar class can use the brand, model, and color properties as well as the move() behavior (method) of the Car class. Additionally, the SportsCar class defines the turbo() behavior (method).

The benefits of inheritance include code reusability, code readability, and more organized and maintainable code. Moreover, you can use the properties of the subclass along with the properties of the superclass.

However, when not used correctly, inheritance can also create disadvantages.

For example, improper use of inheritance can reduce code readability and increase update costs for future changes. It can also unnecessarily complicate subclasses and increase code duplication.

Another disadvantage is that it can increase the dependencies of subclasses. Modifying a class can affect all subclasses that inherit from it, making application maintenance more difficult and risky.

Inheritance can also prevent a program from working correctly in the case of an insufficient class design. For instance, if a class’s properties and behaviors are not properly designed, the subclasses that inherit from it may also carry the same issues.

In conclusion, inheritance can prevent code duplication, organize class hierarchies, and improve code readability when used correctly. However, it can create disadvantages when used improperly. Therefore, proper use and design of inheritance are crucial.

Creating a class hierarchy

Inheritance, which allows subclasses to use the properties of base classes, is also an important concept in the Java programming language. With this mechanism, it is possible to create classes with similar properties and prevent code duplication. Class hierarchy represents the structure of related classes, and this structure enables the relationships between classes to be clearly expressed.

In the class hierarchy, there are superclasses and subclasses. The superclass has a structure that includes the common properties of the subclasses, and the subclasses inherit these common properties. In this way, subclasses can add unique properties by using the properties of the superclass.

The inheritance structure allows the preservation of the properties of a superclass that have been passed down to the subclasses, and the addition of new properties. Thus, code duplication is prevented, and the programming process becomes more efficient. In addition, through class hierarchy, it is also possible to group classes with similar properties together.

When creating a class hierarchy, the access modifiers of the parent classes must be either protected or public for the child classes to be able to use the parent class’s properties. Additionally, child classes cannot directly access private properties of parent classes, but can access them through public or protected methods.

Advantages of inheritance

One advantage of inheritance is preventing code duplication. Creating a new class by using the properties of another class makes the code more concise and readable. Additionally, changes made to a parent class are reflected in all child classes that derive from it, making it easier and faster to implement program changes.

Inheritance also makes code maintenance easier. For example, a change made to a property in a class can affect other classes that inherit from it. In this case, instead of changing each individual class separately, the change made in the parent class can be applied to all child classes.

Inheritance also increases code reusability. Creating new classes using the properties of an existing class makes code reuse easier and speeds up the programming process.

4-) Polymorphism

Polymorphism is a concept that allows different methods with the same name to be implemented in different ways by different classes. Polymorphism reduces code repetition and increases the reusability of code in programming languages. It can also help reduce programming errors. For example, different classes that implement an interface can perform the same functionality in different ways. This makes the code more flexible and easier to change when requirements change.

Polymorphism can also be performed at runtime in Java. For example, it allows you to access a subclass object with a superclass reference and use the methods of the superclass. This way, when the same reference variable is assigned to both superclass and subclass objects, the method to be called is determined at runtime.

In Java, polymorphism is generally implemented in the following two ways:

  1. Method Overloading

Method overloading allows for the definition of methods with the same name but different parameter lists. This allows for the use of the same-named methods with different functionalities.

For example, let’s consider a scenario where you are writing a math library and want to define a method that calculates the sum of two numbers. Instead of writing separate methods for different number types, you can use method overloading to define a method with the same name for all number types:

public class Math {

public int add(int x, int y) {
return x + y;
}

public int add(int x, int y, int z) {
return x + y + z;
}

public double add(double x, double y) {
return x + y;
}

public float add(float x, float y) {
return x + y;
}

// similarly, add methods can be defined for other number types
}

Some points to keep in mind when overloading in Java are:

  • The method name must be the same, but the number or type of parameters must be different. Defining two methods with the same name and parameters will result in an error.
  • Overloading cannot be used for methods that only differ in return type. Two methods with the same return type cannot be defined with the same parameters.
  • Care should be taken with the type and order of parameters during overloading. Using parameters of the same type in different orders can be confusing and can lead to errors in the code.
  • When overloading, using explicit types for the parameters helps to understand which version of the method is being called. For example, if a method accepts both int and double types, it can be difficult to know which version is being called.
  • When overloading, readability and maintainability of the code should be considered. Unnecessarily overloaded methods can reduce the readability of the code and make maintenance more difficult.

2. Method Overriding

Method overriding allows subclasses to write their own customized versions of methods by using the same names, parameters, and return types as those in their superclasses.

For example, let’s define a superclass and a subclass as shown below:

public class Animal {

public void makeSound() { // sesCikar
System.out.println("Animal sound");
}
}
public class Cat extends Animal {

@Override
public void makeSound() { // sesCikar
System.out.println("Meow");
}
}

In this example, a soundMethod is defined in the Animal class, and the Cat class overrides this method by defining its own soundMethod. This allows the same block of code to produce multiple different results when working with different objects. This feature increases the reusability and flexibility of the code and also enables different classes to use the same method or property.

When overriding in Java, some points to keep in mind are:

  • Private methods in the parent class are not subject to the override process. Otherwise, an error will occur.
  • We cannot reduce the access level of the overridden method, but we can increase it.
  • We cannot change the return type or parameters of a method inherited from the parent class.
  • We cannot override static and final methods.
  • The method names we want to override in the parent and child classes must be the same.
  • Constructor methods cannot be overridden.

Usage of object as a parameter

The polymorphic features we obtain also appear when using objects as a parameter. In other words, we can send objects of different types as parameters to a method.

For example, let’s consider a Animal class and its derived Cat and Dog classes:

public class Animal {
public void feed() { // beslen
System.out.println("The animal is being fed.");
}
}

-----------------------------------------------------------------

public class Cat extends Animal {
@Override
public void feed() { // beslen
System.out.println("The cat is eating cat food.");
}
}

-----------------------------------------------------------------

public class Dog extends Animal {
@Override
public void feed() { // beslen
System.out.println("The dog is chewing on a bone.");
}
}

Let’s create an instance of each of these classes and pass them as parameters to a method.

public class Test {
public static void main(String[] args) {
Animal animal1 = new Animal(); // Hayvan
Cat cat1 = new Cat(); // Kedi
Dog dog1 = new Dog(); // Köpek

feedingTime(animal1); // beslenmeZamani
feedingTime(cat1);
feedingTime(dog1);
}

public static void feedingTime(Animal animal) { // beslenmeZamani
animal.feed();
}
}

The output will be as follows:


An animal is being fed.
The cat is eating the cat food.
The dog is chewing on a bone.

So, since the feed() method of the Animal class is overridden, it can be used in the Cat and Dog classes as well. And in the feedingTime() method, even though we receive a parameter of the Animal type, we can send objects of different types with this parameter. Thanks to the polymorphic features, our code becomes more flexible and extensible.

In short, subclasses that extend the Animal class (such as the Cat and Dog classes) can be used as objects of the Animal type.

5-) Abstract Classes and Interfaces

Abstract Classes

Abstract classes in Java are classes that represent unfinished classes and cannot be instantiated as objects. These classes have one or more abstract methods that only contain their signatures without any implementation. Subclasses must implement the abstract methods of the abstract class. In this way, subclasses can override the abstract methods of the abstract class and provide their own functionality.

When defining an abstract class, the keyword “abstract” is used alongside the “class” keyword. For example:

public abstract class Shape {
public abstract void draw(); // ciz
}

In the above example, an abstract class named “Shape” is defined. The “Shape” class has an abstract method named “draw”. Three subclasses, “Triangle”, “Circle”, and “Rectangle”, inherit from the “Shape” class and implement the “draw” method. Each of these subclasses provides a different implementation of the “draw” method to draw its own shape.

public class Triangle extends Shape {
@Override
public void draw() {
System.out.println("Triangle drawn.");
}
}
----------------------------------------------------------------------------
public class Circle extends Shape {
@Override
public void draw() {
System.out.println("Circle drawn.");
}
}
----------------------------------------------------------------------------
public class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("Rectangle drawn.");
}
}

Subclasses must implement abstract methods, otherwise, they are also defined as abstract classes. In the example above, the “Triangle”, “Circle”, and “Rectangle” classes must implement the “draw” method because they inherit from the “Shape” class.

In the main method, objects are created for three different shapes and the “draw” method is called.

public static void main(String[] args) {
Shape triangle = new Triangle(); // Sekil ucgen = new Ucgen();
Shape circle = new Circle(); // Sekil daire = new Daire();
Shape rectangle = new Rectangle(); // Sekil dikdortgen = new Dikdortgen();

triangle.draw(); // ucgen.ciz(); outputs "Triangle drawn."
circle.draw(); // daire.ciz(); outputs "Circle drawn."
rectangle.draw(); // dikdortgen.ciz(); outputs "Rectangle drawn."
}

In this example, the objects “Triangle,” “Circle,” and “Rectangle” are derived from the “Shape” class and implement the “draw” method. This allows for the creation of objects from subclasses without creating objects from the parent class and provides access to the methods of the parent class through these objects.

Methods with bodies can also be defined, as shown in the following example:

public abstract class Shape {
public abstract double calculateArea(); // abstract method

public double calculatePerimeter() {
// concrete method
// perimeter calculation operations can be done here
}
}

Interfaces

Interfaces are contracts that define a specific set of behaviors that a class can implement. An interface contains one or more method signatures, but their bodies are not defined. Interfaces are used to enforce a specific behavior on a class.

In Java, interfaces are used by a class with the “implements” keyword to implement an interface. A class can implement multiple interfaces.

The use of interfaces increases the readability and reusability of code. Interfaces reduce the dependencies in code and help in writing more scalable code.

Here’s an example:

public interface CanMakeSound {
public void makeSound();
}
----------------------------------------------------------------------
public class Cat implements CanMakeSound {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
----------------------------------------------------------------------
public class Dog implements CanMakeSound {
@Override
public void makeSound() {
System.out.println("Woof Woof");
}
}

In this example, an interface named “CanMakeSound” is defined, which contains a method named “makeSound”. Two classes named “Cat” and “Dog” implement the “CanMakeSound” interface. Both classes implement the “makeSound” method, but each in their own way.

The differences between abstract classes and interfaces are as follows:

  • Abstract classes can only be used through inheritance, while interfaces can be implemented by any class.
  • Abstract classes can have both abstract and concrete properties and methods, while interfaces can only contain abstract methods.
  • Objects of abstract classes can be instantiated, while objects of interfaces cannot be instantiated.
  • Abstract classes are extended by a class through inheritance, while interfaces are implemented by a class and a class can implement multiple interfaces.
  • Abstract classes have additional features such as enabling code sharing between classes, while interfaces only allow a class to implement a specific behavior.
  • Abstract classes can hold variables and methods in their subclasses, while interfaces can only hold method signatures.

Therefore, an abstract class can be used when you need to use common code in its subclasses or when you need to create an object. On the other hand, an interface is useful for different classes that require similar behavior.

An Overview:

Classes and objects are one of the fundamental building blocks when programming in the Java programming language. Java’s object-oriented programming paradigm allows for the creation, management, and interaction of objects. Classes can be thought of as templates used for creating objects. Each class has its own properties and functions, which are inherited by the objects.

By using classes and objects in Java, you can break down large and complex projects into smaller and more manageable parts. Objects can interact with each other and communicate with each other, along with their own properties and functions. This makes the code more readable and understandable when programming.

Classes and objects can be used in many different applications in Java. For example, classes and objects can be used in software development, web programming, mobile application development, and game programming, among many other areas.

Note:

AI tools have been used to translate this blog post, with the help of Chat-gpt 3 and Hashnode Rix.

--

--

No responses yet