As I was writing my first semester of teaching reflection, I got the idea to kick off a series of student questions called Coding Tangents. In this series, I’ll be tackling student questions with clear, easy-to-follow explanations that demystify common programming language syntax. In particular, I’d like to tackle the difference between public and private in Java today.
The Problem Students Encounter
Often times when we teach Java, we’re stuck leaving a lot of the syntax as a mechanical process. In other words, we tell students that keywords like public
, static
, and private
will be explained to them later. In the meantime, they just have to trust that we will actually explain those concepts later.
One of these pieces of syntax that almost always gets left for a later discussion is private
vs. public
. These keywords are known as access modifiers, and we’ll dig into them in this article.
But first, let’s take a look at an example of some code that is almost certain to raise some questions about access modifiers:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
In order to teach Java, we’re often stuck introducing the language using these horrible five lines of code. After all, this is the bare minimum required to get a Java program running.
As a result, we’re often forced to tell students something along the lines of:
Don’t worry about the outer four lines. Just place whatever code you want to execute in the middle block.
-- Computer Science Professors Everywhere
Of course, this line of reasoning leaves a lot to be desired if you’re a new student. For example, what do any of those four outer lines do? What’s public
? How about static
, String[]
, or System.out.println
?
Luckily, I’m hear to cover the access modifier part today.
The Explanation Students Desire
At this point, let’s talk about access modifiers at a high level.
Access Modifier Overview
In Java, access modifiers are a way to help us from stepping on our own feet. In general, they’re used to set some level of access to a class, method, or variable.
For example, if we want to model something from the real world (say a car), there are certain aspects of that object we probably don’t want to expose to the public (say individual control over the wiper blades). Perhaps under the hood, the wipers are individually controlled, but we’ve built our system such that the switch given to the user has encapsulated that behavior. In other words, both wipers move together as expected.
Had we chosen to expose individual control over each wiper, we may find that many users accidentally break the wiper functionality. After all, if the wipers aren’t perfectly synced, they may smash into each other.
That’s the high level idea behind access modifiers. We use them to expose or hide certain functionality to improve the overall user experience.
Misconceptions
At this point, a lot of students will start thinking that access modifiers are some way to make code more secure from hackers. While this is largely untrue, there is some merit in the argument. Sure, nothing is stopping someone from using a feature like reflection to access private fields and methods. That said, access modifiers can help protect the average user from corrupting an object’s state.
Think about the windshield wiper example. When we turn on our wipers, we expect both of them to move at the same speed. Without restricted access, we could change the default speed of one of the wipers. Then, the next time we’d go to turn on the wipers… BAM! To avoid that problem, we encapsulate (or hide) the fact that we have two individual wipers in a single exposed (public) method.
Encapsulation is the art of reducing a complex state down to a set of exposed behaviors. If I were to ask you to throw a ball, you certainly wouldn’t start by requesting a set of matrix transformations for the rotation of your arm. You’d just throw the ball. That’s the idea behind encapsulation (and abstraction).
In this example, we can use access modifiers to specify which behaviors are exposed. For example, we’d probably want to allow users to access the throw
command but maybe not the rotateArm
or pickUpBall
commands.
Now that we’ve tackled some of the misconceptions, let’s get into the syntax.
Keywords
In Java, there are actually four access modifiers: public
, private
, package-private
(default), and protected
. Each keyword offers a level of code access given by the following table:
public | private | package-private | protected | |
---|---|---|---|---|
Same Class | T | T | T | T |
Different Class in Same Package | T | F | T | T |
Sub-Class in Same Package | T | F | T | T |
Different Class in Different Package | T | F | F | F |
Sub-Class in Different Package | T | F | F | T |
In other words, we can rank the keywords in order of least accessibility:
- private
- package-private (default)
- protected
- public
For the duration of this tutorial, I will not be exploring the package-private
or protected
keywords as they’re a bit more nuanced, but I figured they were important to mention.
Classifying Actions as Public or Private
Using the ball throwing example from before, let’s try to figure out which access modifier would be appropriate in various situations:
- public
- throw
- catch
- toss
- pitch
- private
- rotateArm
- translateVertices
- pickUpBall
- calculateVolume
Notice how all the high level actions are public and the lower level actions are private. That’s because we don’t necessarily want to expose the lower level actions to the public. But, why not? Let’s take a look at another example.
Let’s say that the high level functions rely on some underlying state of the system. For instance, throwing a ball relies on knowing information like the strength of gravity and the properties of the ball. If someone were somehow able to access the lower level actions, they could potentially manipulate these basic assumptions of the world.
What would happen if we were able to access actions like setGravity
or setBall
? How would our high level actions like throw
or catch
change?
Using the setGravity
command, I could tell you that gravity is actually twice as strong as you think it is before telling you to throw the ball. At that point, you’d update your model of the world before significantly increasing the force of your throw to accommodate for the change in gravity. However, in reality, gravity hasn’t actually changed, so instead you overthrow the ball.
This scenario is often what happens when we expose lower level functionalities that don’t trigger automatic updates of dependent properties. In many cases, systems are very complicated, and changing one underlying parameter results in the failure of the system. As a result, we try to encapsulate functionality to cover all our bases.
User-Defined Classes
Up until this point, we've been talking mostly about the philosophy of access modifiers, but what are the real world consequences and how do we actually use them? To help clarify those questions, let's take a moment to write some of our own classes which try to demonstrate the practical differences between public
and private
.
Hello World Revisited
Now that we’ve seen some high level explanations, let’s dig back into our Hello World example.
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Here we can see that we use the public keyword twice: once for the class and again for the main method. In other words, we’ve chosen to expose both the HelloWorld class and the main method to the public.
To make things a little more interesting, let’s wrap the print in its own private method:
public class HelloWorld {
public static void main(String[] args) {
printHelloWorld();
}
private static void printHelloWorld() {
System.out.println("Hello, World!");
}
}
If we try to run this solution, we’ll notice that the behavior hasn’t changed at all. That’s because private methods can be used in their own class. Outside HelloWorld
, however, no one knows printHelloWorld()
even exists. In fact, we could try to call the method directly from another class in the same folder, and we’d find ourselves with an error:
public class CallPrivateMethod {
public static void main(String[] args) {
HelloWorld.printHelloWorld(); // ERROR
}
}
As we can see, we’ve hidden away the printing functionality, so that it can only be used by the HelloWorld
class. If for some reason we made the printHelloWorld()
method public, we could run it just fine.
Windshield Wipers
Now, let's take this concept a step further by actually implementing the windshield wipers in Java (at least at a high level). To start, we'll make a car class that has a private
method for one wiper and a public
method for both wipers:
public class Car {
private boolean[] wipers;
public Car() {
this.wipers = new boolean[2];
}
private void turnOnWiper(int index) {
this.wipers[index] = true;
}
public void turnOnWipers() {
for (int i = 0; i < this.wipers.length; i++) {
this.turnOnWiper(i);
}
}
}
Here, we've created a Car class that stores a private
array of wiper states. For each wiper, their state is either on (true
) or off (false
). To turn a wiper on, we've written a private
method that lets you turn on a wiper by its index. Then, we bring all that together with a public
method that iterates through all the wipers and turns them all on.
Now, ignoring the realistic problem here which is that the wipers are being turned on in series, not parallel, we have a pretty solid solution. If someone were to instantiate a Car, they would only be able to turn on all the wipers at once.
public class CarBuilder {
public static void main(String[] args) {
Car car = new Car();
car.turnOnWipers(); // Turns on wipers!
car.turnOnWiper(1); // Compilation ERROR
car.wipers[0] = false; // Compilation ERROR
}
}
Fun fact: the user doesn't even know how the wipers are implemented, so we have full control to change the underlying architecture at any time. Of course, we still have to provide the same functionality, but how we get there is up to us. In other words, we could potentially change the wiper array to store integers. Then, for each wiper, the integer would correlate to speed.
Now, why don't you try expanding the class yourself. For example, I recommend adding a method to turn off the wipers. You may want to then write a new private method for turning off individual wipers, or you might find it makes more sense to refactor the turnOnWiper
method to take a boolean as well. Since the user never sees those methods, you have full control over the underlying implementation. Happy coding!
Open Forum
Hopefully this helps you understand the difference between the private and public keywords, and why we use them. If not, I’m open to any feedback and questions you may have. Feel free to use the comments below to start a bit of a dialogue. And if this helped you out at all, share it with your friends. I always appreciate the support!
Top comments (4)
Hi there...
Thanks for sharing this. I always like to find material on Basic subject that May help even ppl who dont program for work like me. I think that I grasped what is the use. Stille have 2 question: first is not really related to this subject but i Am not plain satisfied If i dont understand every little piece of code i read... how the pubblic method is supposed to know the lenght of the wipers array if we didnt set any? Am i losing something? Second question, why would i As a developer wanted this to be hidden in the code if only other developer are going to look at the code ? I mean final user Will Never see the code he will just click on a button or perform An action that will trigger my function/class... sorry If these are dumb questions but they really come from A layman.
Ooh these are great questions. It's my mistake on the Wiper example. In an effort to provide a couple method examples, I neglected to provide a constructor. You can assume the array of wipers is defined (length could be anything), but I'll update the example to include a constructor.
To answer your second question, there's a bit more nuance. You're totally right in your example. If your final application is a graphical user interface (GUI), you might not care about public vs. private. After all, the only thing you're exposing is a button.
However, if you're going to release the underlying class structure to the public, you may want to limit which methods you expose. There are a few reasons for that. For one, hiding certain functionalities allows you to limit how much control the user has over the underlying structures. In other words, you protect them from themselves. The other reason is that you're free to rework the underlying structure without messing up your public interface. For example, maybe you create a new data structure which has an exposed sorting method. You're free to change how sorting is accomplished as long as it provides the same functionality to the user.
Also, I just think it's good practice regardless. Having a clean and clearly defined interface between classes makes for a lifetime of painless maintainability.
The HellowWorld example is not the best fit to show how public and private work. It's much more clear with typical data structures like Stack, List etc. or with domain entities like Order, Invoice etc.
I appreciate the feedback, but I’m not sure I agree (at least with the first part). If we’re talking beginners, they aren’t going to be able to handle data structures beyond arrays (maybe ArrayLists, LinkedLists)—especially data structures in java which leverage generic types. If they knew those things already, then they probably wouldn’t need this article.
As for domain entities, to be quite honest, I’ve never heard that term. Granted, if we’re just talking about user-defined classes, then that’s fair. I just felt like it was important to come full circle on the example at the beginning of the article. When I get the chance, I’ll extend the last section to include a better example. Thanks!