While Python includes support for objects, you don’t need to use them to write a program. Java, however, is fundamentally object-oriented. In Java, everything is an object. Objects allow us to collect data and the actions that operate on that data into one entity.
The data is called fields or instance variables. These are things the object needs to store.
The actions are called methods. These are things an object needs to do, or that may be done to an object.
A class provides the blueprint of what an object should look like. When you want to create one, then you create an instance of the class called an object.
Consider the following sample code for the hero in an adventure game. Our hero has two instance variables: hitPoints
(the amount of damage the hero can take until death) and an array of items
representing the inventory. There are also two methods: getHit
that deducts damage from the hit points, and isDead
that tells you if the hero is dead.
public class Hero {
/*
* This class has two instance variables
*/
private int hitPoints;
private Item[] items;
/*
* This class had two methods
*/
public void getHit(int damage) {
this.hitPoints -= damage;
}
public boolean isDead() {
if (hitPoints <= 0) {
return true;
}
else {
return false;
}
}
}
Defining the Hero
class does not create an object. This is just the class, the blueprint for a hero. To instantiate a hero we need a line like Hero bob = new Hero()
. After that line runs, bob
is a hero object. We create an instance of a Hero
object from the blueprint represented by the class.
You may have noticed that in most of the examples so far both methods and instance variables have keywords in front of them like public
and private
. These keywords impact which code, outside of the object, is allowed to access that item.
public
means that the item is accessible to anyone. So, for example, the two methods in our Hero
class above are public. If bob
is our object, then anywhere in the program that has a copy of bob
can call bob.getHit(5)
, because the getHit
method is public.
protected
means that the item is accessible to code in the same package, the same class, and even subclasses. (We’ll talk about subclasses later.)
private
means that data is only accessible inside the class.
If you don’t specify visibility, then it receives package-private. This means the item is accessible inside the package and the class.
Consider this table summarizing the visibility modifiers:
Modifier | Class | Package | Subclass | Everywhere Else |
---|---|---|---|---|
public |
X | X | X | X |
protected |
X | X | X | |
private |
X | |||
none used | X | X |
There are a few rules about visibility followed by convention (meaning unofficial rules followed by most programmers):
Instance variables should be private
. If you need to access them from outside the class code (which is likely) you should write methods to facilitate this.
Methods that will be used from outside the class should be public
.
Methods that will only be used inside the class should be private
. Usually, these are helper methods that you write to assist you when writing the public methods.
Every class needs at least one constructor. A constructor is a special method that is used by the class to instantiate an object. Typically, the constructor allocates and initializes the instance variables. You can have more than one constructor, as long as the different constructors have different parameters. If you don’t include a constructor, then Java will automatically include a default constructor that does almost nothing.
Remember our hero class from above? Here it is again, this time with some constructors. Either constructor can be used to instantiate an object from the Hero
class.
public class Hero {
/*
* This class has two instance variables
*/
private int hitPoints;
private Item[] items;
/*
* This class has two constructors
*/
public Hero() {
this.hitPoints = 10;
this.items = new Item[10];
}
public Hero(int hitPoints) {
this.hitPoints = hitPoints;
this.items = new Item[10];
}
/*
* This class had two additional methods
*/
public void getHit(int damage) {
this.hitPoints -= damage;
}
public boolean isDead() {
if (hitPoints <= 0) {
return true;
}
else {
return false;
}
}
}
Given that instance variables should always be set private, what do you do if you need to modify them from outside the object? You create getters and setters. Getters and setters are methods you add to a class specifically to allow for access to instance variables.
Consider the following example to allow the direct modification of a Hero’s hit points.
public class Hero {
/*
* This class has two instance variables
*/
private int hitPoints;
private Item[] items;
/*
* This class has two constructors
*/
public Hero() {
this.hitPoints = 10;
this.items = new Item[10];
}
public Hero(int hitPoints) {
this.hitPoints = hitPoints;
this.items = new Item[10];
}
/*
* A getter and setter for hitPoints
*/
public int getHitPoints() {
return hitPoints;
}
public void setHitPoints(int hitPoints) {
this.hitPoints = hitPoints;
}
/*
* This class had two additional methods
*/
public void getHit(int damage) {
this.hitPoints -= damage;
}
public boolean isDead() {
if (hitPoints <= 0) {
return true;
} else {
return false;
}
}
}
These allow code that is working with a hero object to modify the hitPoints
of the Hero
. Without providing getters and setters, outside code cannot manipulate hitPoints
, because it is a private
instance variable.
Let’s take a moment to talk about something else you’ve seen so far: static
. When a method or instance variable is declared static
that means that it is accessible as part of the class itself, even if you haven’t instantiated an instance of that class.
The most common reason to declare a method static
is because you are writing a helper function that is useful outside the class. For example, the Math
class provided by Java includes several static methods related to mathematics. If you want to take the power of something, you can simply call the pow
method of the Math
class directly: Math.pow(2,10)
. You don’t need to instantiate a Math
object before calling the method.
The most common reason to declare an instance variable static
is because it is a constant that is useful outside the class. Returning to our Math
example, the Math
class has a static instance variable called PI
that stores the value of the mathematical constant \(\pi\). You can access it using Math.PI
without first needing to create an instance of a Math
object.
You’ve seen a number of the examples so far include a main
method. The main
method is a special static
method. If you tell Java to execute a class, it searches that class for a main
method that is static
and calls that. This is how you specify which code should run first in your program.
The main method is always declared static using the following signature:
public static void main(String[] args)