Object Oriented Java
Introduction to Classes
All programs require one or more classes that act as a model for the world.
For example, a program to track student test scores might have Student
, Course
, and Grade
classes. Our real-world concerns, students and their grades, are inside the program as classes.
We represent each student as an instance, or object, of the Student
class. This is object-oriented programming (OOP) because programs are built around objects and their interactions. An object contains a state and behavior.
Classes are a blueprint for objects. Blueprints detail the general structure. For example, all students have an ID, all courses can enroll a student, etc.
An instance is the thing itself. This student has an ID of 42
, this course enrolled that student, etc.
Let's review with another example, a savings account at a bank.
What should a savings account know?
- The balance of money available.
What should a savings account do?
-
Deposit money.
-
Withdraw money.
Imagine that two people have accounts that are instances of the SavingsAccount
class. They share behavior (the ability to deposit/withdraw) but have an individual state (their balances), and even with the same balance these accounts are separate entities.
Let's put that same concept into a store class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Running the above code should give the following output:
Our first shop sells lemonade at 0.99 per unit.
Our second shop has 12 units remaining.
Class Syntax
The fundamental concept of object-oriented programming is the class. A class is the set of instructions that describe how an instance can behave and what information it contains. Java has pre-defined classes such as System
, which we have used previously to log text to our screen, but we also need to write our own classes for the custom needs of a program.
Here is a definition of a Java class:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
This example defines a class
named Car
. public
is an access level modifier that allows other classes to interact with this class. For now, all classes will be public
.
This class has a main()
method, which lists the tasks performed by the program. main()
runs when we execute the compiled Car.class file.
Constructors
In order to create an object (an instance of a class), we need a constructor method. The constructor is defined within the class.
Let's take a look at the Car
class with a constructor. The constructor, Car()
, shares the same name as the class:
1 2 3 4 5 6 7 8 9 10 |
|
To create an instance, we need to call or invoke the constructor within main()
. The following example assigns a Car
instance to the variable ferrari
:
1 2 3 4 5 6 7 8 9 10 11 |
|
Instance Fields
In our first example, we ended with printing an instance of Store
, which looked something like Store@6bc7c054
. The first part, Store
, refers to the class, and the second part, @6bc7c054
refers to the instance's location in the computer's memory.
We don't care about memory location, but our instances have no other characteristics! When an object is created, the constructor sets the initial state of the obkect. The state is made up of associated data that represents the characteristics of an object.
We'll add data to an object by introducing instance variables, or instance fields.
We want Car
instances of different colors, so we declare a String color
instance field. Often times, instance variables are described as a "has-a" relationship with the object. For example, a Car
"has-a" color
. Another way to think of that is that instance variables are nouns and adjectives associated with an object. What qualities other than color
might a car have?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
The declaration is within the class and the instance variable will be available for assignment inside the constructor.
Fields are a type of state each instance will possess. One instance may have "red"
as its color
, another "blue"
, etc. It's the job of the constructor to give these instance fields initial value.
Contructor Parameters
To create objects with dynamic, individual states, we'll use a combination of the constructor method and instance fields.
In order to assign a value to an instance variable, we need to alter our constructor method to include parameters so that it can access the data we want to assign to an instance. We've already seen a parameter in the main()
method: String[] args
, but this is the first time we're using the parameter value within a method body.
The Car
constructor now has a parameter: String carColor:
:
1 2 3 4 5 6 7 8 9 10 11 |
|
When a String
value gets passed into Car
, it is assigned to the parameter carColor
. Then, inside the constructor, carColor
will be assigned as the value to the instance variable color
.
Our method also has a signature which defines the name and parameters of the method. In the above example, the signature is Car(String carColor)
.
Assigning Values to Instance Fields
Now that the constructor has a parameter, we must pass values into the method call. These values are referred to as arguments; once they are passed in, they will be used to give the instance fields intial value.
Here we create an instance, ferrari
, in the main()
method with "red"
as its color
field:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
We pass the String value "red"
to our constructor method call: new Car("red");
.
The type of value given to the invocation must match the type declared by the parameter.
Inside the constructor, the parameter carColor
refers to the value passed in during the invocation "red"
. This value is assigned to the instance field color
.
color
has already been declared, so we don't specify the type during assignment.
The object, ferrari
, holds the state of color
as an instance field referencing the value "red"
.
We access the value of this field with the dot operator (.
):
1 2 3 4 5 6 7 |
|
Using Multiple Fields
Objects are not limited to a single instance field. We can declare as many fields as are necessary for the requirements of our program.
Let's change Car
instances so they have multiple fields.
We'll add a boolean isRunning
, that indicates if the car engine is on and an int velocity
, that indicates the speed at which the car is travelling.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
The constructor now has multiple parameters to receive values for the new fields. We still specify the type as well as the name for each parameter.
Ordering matters! We must pass values into the constructor invocation in the same order that they're listed in the parameters.
1 2 3 4 5 |
|
Classes Review
Java is an object-orriented programming language where every program has at least one class. Programs are often built from many classes and objects, which are the instances of a class.
Classes define the state and behavior of their instances. Behavior comes from methods defined in the class. State comes from instance fields declared inside the class.
Classes are modeled on the real-world things we want to represent in our program. Later we will explore how a program can be made from multiple classes. For now, our programs are a single class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Introduction to Methods
A method is a block of code which only runs when it is called. You can pass data, known as parameters into a method. Methods are used to perform certain actions, and they are also known as functions.
Methods are repeatable, modular blocks of code, used to accomplish specific tasks. We have the ability to define our own methods that will take input, do something with it, and return the kind of output we want.
Method decomposition is a way we can use methods to break fown a large problem into smaller, more manageable problems.
Methods are very reusable. Imagine we wrote a sandwich-making program that used 20 lines of code to make a single sandwich. Our program would become very long very quickly if we were making multiple sandwiches. By creating a makeSandwich()
method, we can make a sandwich anytime simply by calling it.
We will be using the following code to build a method later on:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
Defining Methods
If we were to define a checkBalance()
method for the Savings Account example above, it would look like the following:
1 2 3 4 |
|
The first line, public void checkBalance()
, is the method declaration. It gives the program some information about the method:
-
public
means that other classes can access this method. -
The
void
keyword means that there is no specific output from the method. We will see methods that are notvoid
later in these notes, but for now, all of our methods will bevoid
. -
checkBalance()
is the name of the method.
Every method has its own unique method signature which is comprised of the method's name and its parameter type. In this example, the method signature checkBalance()
.
The two print statements are inside the body of the method, which is defined by the curly braces: {
and }
.
Anything we can do in our main()
method, we can do in other methods! All of the java tools we have learned, like the math and comparison operators, can be used to make interesting and useful methods.
Calling Methods
When we add a non-static method to a class, it becomes available to use on an object of that class. In order to have our methods get executed, we must call the method on the object we created.
Let's add a non-static startEngine()
method to our Car
class from earlier notes. Inside the main()
method, we'll call startEngine()
on the myFastCar
object:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Let's take a closer look at the method call:
myFastCar.startEngine();
First, we reference our object myFastCar
. Then, we use the dot operator (.
) to call the method startEngine()
. Noter that we must include parentheses ()
after our method name in order to call it.
If we run the above program, we get the following output:
Starting the car!
Vroom!
That was one fast car!
Code generally runs in a top-down order where code execution starts at the top of a program and ends at the bottom; however, methods are ignored by the compiler unless they are being called.
When a method is called, the compiler executes every statement contained within the method. Once all method instructions are executed, the top-down order of execution continues. This is why Starting the car!
and Vroom!
are outputted before That was one fast car!
.
Scope
A method is a task that an object of a class performs.
We mark the domain of this task using curly braces {
and }
. Everything inside the curly braces is part of the task. This domain is called the scope of a method. We can't access variables that are declared inside a method in code that is outside the scope of that method.
Looking at the Car
class again:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
The variable message
, which is declared and initialized inside of drive
, cannot be used inside of main()
! It only exists within the scope of the drive()
method.
However, milesDriven
, which is declared at the top of the class, can be used inside all methods in the class, since it is in the scope of the whole class.
Adding Parameters
We saw how a method's scope prevents us from using variables declared in one method in another method. What if we had some information in one method that we needed to pass into another method?
Similar to how we added parameters to constructors, we can customize all other methods to accept parameters. For example, in the following code, we create a startRadio()
method that accepts a Double
parameter, stationNum
, and a String
parameter called stationName
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Adding parameter values impacts our method's signature. Like constructor signatures, the method signature includes the method name as well as the parameter types of the method. The signature of the above method is startRadio(double, String)
.
In the main()
method, we call the startRadio()
method on the myCar
object and probide a double
argument of 103.7
and String
argument of Meditation Station
, resulting in the following output:
Turning on the radio to 103.7, Meditation Station!
Enjoy!
Note that when we call on a method with multiple parameters, the arguments given in the call must be placed in the same order as the parameters appear in the signature. If the argument types do not match the parameter types, we'll receive an error.
Reassigning Instance Fields
Earlier, we thought about a Savings Account as a type of object wer could represent in Java.
Two of the methods we need are depositing and withdrawing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
These methods would change the valuie of the variable balance
. We can reassign balance to be a new value by using our assignment operator, =
, again.
1 2 3 4 |
|
nNow, when we call deposit()
, it should change the value of the instance field balance
:
1 2 3 4 5 6 |
|
The code first prints 2000
, the initial value of myAccount.balance
, and then prints 2100
, which is the value of myAccount.balance
after the deposit()
method has run.
Change instance fields is how we change the state of an object and make our objects more flexible and realistic.
Returns
Remeber, variables only exist in the scope that they were declared in. We can use a value outside of the method it was created in if we return it from the method.
We return a value by using the keyword return
:
1 2 3 4 5 |
|
This method, called numberOfTires()
, returns 4
. Once the return statement is executed, the compiler exits the function. Any code that exists after the return statement in a function is ignored.
In past exercises, when creating new methods, we use the keyword void
. Here, we are replacing void
with int
, to signify that the return type is an int
.
The void
keyword (which means "completely empty") indicates that no value is returned after calling that method.
A non-void method, like numberOfTires()
returns a value when it is called. We can use datatype keywords (such as int
, char
, etc.) to specify the type of value the method should return. The return value's type must match the return type of the method. If the return expression is compatible with the return type, a copy of that value gets returned in a process known as return by value.
Unlike void mehods, non-void methods can be used as either a variable value or as part of an expression like so:
1 2 3 4 |
|
Within main()
, we called the numberOfTires()
method on myCar
. Since the method returns an int
value of 4
, we store the value in an integer value called numTires
. If we printed numTires
, we would see 4
.
The toString()
method
When we print out Objects, we often see a String
that is not very helpful in determining what the object represents. In earlier notes, we saw that printing our Store
object would output something like:
Store@6bc7c123
Where Store
is the name of the object and @6bc7c123
is its position in memory.
This doesn't tell us anything about what the Store
sells, the price, or the other instance fields we've defined. We can add a method to our classes that makes this printout more descriptive.
When we define a toString()
method for a class, we can return a String
that will print when we print the object:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
When this runs the command System.out.println(myCar)
will print This is a red car!
, which tells us about the Object myCar
.
Methods Review
Methods are a powerful way to abstract tasks away and make them repeatable. They allow us to define behavior for classes, so that the Objects we create can do the things we expect them to. Let's review everything we have learned about methods so far:
-
Defining a method: Methods have a method signature that declares their return type, name, and parameters.
-
Calling a method: Methods are invoked with a
.
and()
. -
Parameters: Inputs to the method and their types are declared in parentheses in the method signature.
-
Changing Instance Fields: Methods can be used to change the value of an instance field.
-
Scope: Variables only exist within the domain that they are created in.
-
Return: The type of the variables that are output are declared in the method signature.
Basic Calculator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|