DEV Community

Cover image for ☄Deep Dive into Object Oriented Programming: Part 2
Sahil Bondre
Sahil Bondre

Posted on

☄Deep Dive into Object Oriented Programming: Part 2

In the last post, we started with the basics of Object-Oriented Programming. We made our first class. Added some members and methods to it and initialized an object of the Car class. It's time to dive deeper now.

Earlier, we had defined a refuelTank method to increase the fuel in our car. Now when I have a car object with me, no one stops me from changing the fuelLeft value without using the refuelTank method. I can directly access the member using the dot operator and change its value:

car.fuelLeft = 500;
Enter fullscreen mode Exit fullscreen mode

In the real-life, I cannot magically change the fuel in my car. That's against the laws of physics. But apparently, I can do that in the software version of my car. To solve this, we'll introduce access modifiers.

Access Modifiers

Java has three keywords to solve this problem: public, private and protected. When something is public, everyone can access it. When something is private, it can only be accessed internally. We'll talk about protected in some time.

To prevent someone from magically changing the fuelLeft member, we'll add the private keyword before its definition in the class. This will prevent it from being accessed using the dot operator.

// Car.java
private Integer fuelLeft;

Enter fullscreen mode Exit fullscreen mode

But this creates a different problem. In our main method, we used the fuelLeft member to print the value. Not to magically change the value but just to have a look at its value.

//Main.java

// ERROR!
System.out.println(honda.fuelLeft);
honda.refuelTank(100);
System.out.println(honda.fuelLeft);

Enter fullscreen mode Exit fullscreen mode

Running the above code will give us an error. To preserve the functionality to be able to view the data, we'll create a "getter" method in the Car class:

// Car.java

int getFuelLeft() {
    return fuelLeft;
}
Enter fullscreen mode Exit fullscreen mode

Since this method belongs to and is "inside" the class, it can access the private members. So it just takes the value and returns it. Now we can refactor our main function code to use this method instead of the member:

System.out.println(honda.getFuelLeft());
honda.refuelTank(100);
System.out.println(honda.getFuelLeft());
Enter fullscreen mode Exit fullscreen mode

And voila! Everything works as before with additional security of not changing the fuelLeft member magically.

Refactoring Other Members

If you think about it, all the other members of the Car class should be private by the same argument of not changing their value manually. Let's make all of them private and add getter functions to them:

public class Car {
    private String name;
    private String color;
    private Integer peopleCapacity;
    private Integer fuelLeft;
    private Integer distanceTravelled;

    public String getName() {
        return name;
    }

    public String getColor() {
        return color;
    }

    public Integer getPeopleCapacity() {
        return peopleCapacity;
    }

    public Integer getFuelLeft() {
        return fuelLeft;
    }

    public Integer getDistanceTravelled() {
        return distanceTravelled;
    }
    // other methods...
}
Enter fullscreen mode Exit fullscreen mode

Access of methods

Just like members, we can add access modifiers to our class methods as well. If a class method is private, it cannot be called using the dot operator in the object. It can only be used internally by other methods inside the class. And that is the reason we had

In the last post, we defined our constructor as public. Now let's make sense of it. Our constructor needs to be accessible from anywhere to initialize the object from the class. Hence, we made it public. Let's now define a private method. When we start the engine, the car also checks if all the systems are working in proper condition. Let's call this method checkSystems. This method shouldn't be used by the car user but just called internally by the startEngine method.

// Car.java

private void checkSystems() {
    System.out.println("Checking Systems...");
}

void startEngine() {
    checkSystems();
    System.out.println("Starting Engine!");
}
Enter fullscreen mode Exit fullscreen mode

Now when we call startEngine on the car object from the main method, it'll print "Checking Systems..." too. However, we cannot access the checkSystems method externally:

// Main.java

// ERROR
car.checkSystems();
Enter fullscreen mode Exit fullscreen mode

Now you may wonder, earlier all members and methods in the Car class were neither public nor private; still we could access them. Why is that? If nothing is mentioned, does java make them public by default? The short answer is not really. Let's take a step back and understand what packages are in java.

Packages

Packages in java are used to group similar classes (and sub-packages) together. At the start of each java class, we mention the package name:

package com.godcrampy.oop_deep_dive;
Enter fullscreen mode Exit fullscreen mode

Conventionally, to avoid naming collisions, companies use reverse domain names as a prefix for the package name. So if let's say if a company has its domain as example.com and is developing a utility for helpful string operations, it may name it as com.example.string_util. Packages can contain sub-packages too. So, suppose the string operations utility has a section for generating random names. In that case, it could be in a package called com.example.string_util.random_name.

All the class files are structured in directories according to the package name. For example, let's consider the java project that I am using for this tutorial. I have defined the package name as com.godcrampy.oop_deep_dive. All the source code for my project is in the src/main folder of the repository with the following structure:

    └── src
        └── main
            └── java
                └── com
                    └── godcrampy
                        └── oop_deep_dive
                            ├── Car.java
                            └── Main.java

Enter fullscreen mode Exit fullscreen mode

Now let's move our Car class to a subpackage called vehicle. To do this, I created a new directory called vehicle and moved the class there:

└── src
    └── main
        └── java
            └── com
                └── godcrampy
                    └── oop_deep_dive
                        ├── Main.java
                        └── vehicle
                            └── Car.java

Enter fullscreen mode Exit fullscreen mode

Now we need to update the package declarations of our Car class. So I change the first line of the Car class from package com.godcrampy.oop_deep_dive; to package com.godcrampy.oop_deep_dive.vehicle;

Now, if you are coding along with this tutorial, you may notice that your IDE has already started showing some errors. honda.startEngine, honda.refuelTank and honda.drive methods are not accessible in the main function.

For these methods, we didn't add the public or private keywords. So when a member or method has no access modifier specified, it gets the default access. This means that they can be accessed in some other class only if both the classes are in the same package. In our example, both Main and Car classes were in com.godcrampy.oop_deep_dive package. So everything worked fine before. So when we moved Car to com.godcrampy.oop_deep_dive.vehicle package, all the default methods became inaccessible.

Fixing the defaults

To fix this, I'll make all the default methods in our car class public. So finally, the Car class looks like this:

package com.godcrampy.oop_deep_dive.vehicle;

public class Car {
    private String name;
    private String color;
    private Integer peopleCapacity;
    private Integer fuelLeft;
    private Integer distanceTravelled;

    public String getName() {
        return name;
    }

    public String getColor() {
        return color;
    }

    public Integer getPeopleCapacity() {
        return peopleCapacity;
    }

    public Integer getFuelLeft() {
        return fuelLeft;
    }

    public Integer getDistanceTravelled() {
        return distanceTravelled;
    }

    public Car(String name, String color, Integer peopleCapacity) {
        this.name = name;
        this.color = color;
        this.peopleCapacity = peopleCapacity;
        this.fuelLeft = 100;
        this.distanceTravelled = 0;
    }

    private void checkSystems() {
        System.out.println("Checking Systems...");
    }

    public void startEngine() {
        checkSystems();
        System.out.println("Starting Engine!");
    }

    public void refuelTank(int fuel) {
        fuelLeft += fuel;
    }

    public void drive(int distance) {
        System.out.println("Driving...");
        distanceTravelled += distance;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now all the errors are gone, and we are good to go. If you notice, at the start of the class as well, we added the public keyword: public class Car {. This follows from the same access rules as we discussed before.

In summary:

Acess Modifier Within Class Within Package Outside Package
Private Y N N
Default Y Y N
Public Y Y Y
Protected ? ? ?

The protected keyword is still to be investigated. We will do that in the next post.
That wraps it up for the post. We understood what access modifiers are and how they can be used to improve the security of our code. We also explored packages in java and how to organise our code. You can find the code mentioned in the post here: https://github.com/godcrampy/oop-deep-dive-notes


I hope you liked the post. If this was helpful, please show some love by clicking ♥, 🦄 and saving the post for later. Feel free to express your suggestions and opinoins in the comments below. Oh, BTW do you want to learn Bash? Here's my crash-course post on Shell Scripting:


🌟 I made some Cheat-Sheets
🚀 Follow me on Github | Twitter
📜 Check my Website
😄 Have a wonderful day!

Discussion (0)