Learn Java Programming
2.0 Classes and Objects
Objectives:
The following are the objectives of this lesson. Here is what you will be able to do, at the end of this lesson.
- Define a class in Java
- Understand the real world modeling in Java – Why we define classes
- What is an Object
- What is meant by “Type” of an Object – and what is meant by “State” of the object?
- What is meant by a bean – what criteria should be met to call a Java class as a Bean?
- Why we need to implement the toString() method – what will be the consequence of not implementing the toString() method ?
- How does Java test for object equality – how and when can two objects be equal?
- Why do we need to implement the equals method?What are the factors to be considered while implementing the equals method?
- What is the object’s hashCode?
- Why is it necessary to implement the hashCode method while overriding the equals() method?
- How to implement the hashCode – what is the generally guidelines to be followed while implementing the hashCode method
These objectives may seem overwhelming at first sight, but hold tight. It is in fact easy to master these, once you start to understand the way the rules have been defined in the Java Programming language.
This is going to be an important and interesting chapter. Stay tuned and fasten your seat belts! Its a beautiful day and the weather is just fine. We are on the runaway – just about to fly into Java land. It sure is going to be one great journey! And together we are going to enjoy every minute of it! Okay, lets get started!
Lets start with our first question : WHY
Question: Why are we here, together today?
Obvious Answer: To learn Java programming Question: Why do we want to learn programming?
Obvious Answer: To write programs. To earn a living. Software professionals seem to be paid handsomely and I want a piece of that pie.
Question: That sounds good! Now why are software professionals paid handsomely? Why does this profession pay on average more than the other profession?
Obvious Answer: –
Try answering this question, before you proceed.
Go ahead and make a guess – you got nothing to lose, do you? Why are the software guys so cool?
…
Well, honestly I struggled with the answer. …
It was then I found out that software professionals were paid more than their other peers in other professions, simply because they solve problems compared to other professions,on a much large scale.
And here is a little secret for you – If you want to make more money, solve more problems!
Download Source code 2.0-ClassesAndObjects-Java
Software professionals like you and me, solve real world problems – we also solve business problems – we make businesses more efficient, more profitable, and give better service to the business customers. As a result there is a benefit to humanity in general.
I realized this very late in my career. But if you have realized it by now, congratulations to you. You are certainly smarter than I was at this time!
OK, that was neat – We write programs to solve real world problems.
But, wait a minute now. How can a software programmer solve real world problems?
Let us say that you are a software programmer and have a successful software company called YOU Inc. Now there is a college in your neighborhood that wants you to write software that will help them manage the college administration.
You sign the contract and you realize that you have to deal with Students, Teachers and the Classes. Well if you want to create a system, and lay down some rules for behavior, you certainly will have a tough time enforcing the rules with the students and the teachers. Well, rules are sometimes suffocating, aren’t they?
Imagine trying to control a student who feels that freedom is his or her birthright (whatever freedom may mean to them at the precise moment!). Try controlling physically – Impractical?! Impossible?!
Well, we have a choice – Instead of controlling the student in the real world, let us model the Student into our program and try and control the Student in our program. Its much easier now.
We will call that Student in our program as an Object, which will represent a real student in the physical world.
Notice we have many students – but they all seem to share some common characteristics. Lets list them out.
i) Students have a name
ii) They have a birthday
iii) They are enrolled for a course
Not only the characteristics, they also share some behavior:
i) They attend classes
ii) They take notes
iii) They write exams
Students thus exhibit some characteristics and show some behavior.
If we take this real world student and model into our program, we would have to define a Class for the Student
Class
Characteristics:
Behavior:
Class Student
Characteristics:
Name
Age
Behavior:
Attend Classes
Take Notes
Write Exams
Once we define a class Student, we can have a number of student objects sharing the same similarity with the class “Student”. Every student object will have a name and an age, and will be enrolled for a course. Also every student will attend classes, take notes and write exams. You will now see that once we have student objects in our program, it will be much easier to control these objects.
Classes and Objects, help us model real world entities in our programs.
In programming languages, the Characteristics that you noticed for the Student, we call them as Attributes or Properties. And the Behavior is generally represented as Methods.
class Student {
//properties
//methods
}
For example,
Filename: Student.java
public class Student {
//PROPERTIES
private String name;
private int age;
//METHODS for Behavior
public void attendClass() {
//-- write code here
}
public String takeNotes() {
//-- write code here
}
public boolean writeExams() {
//-- write code here
}
}
Few things to note here:
We make our class Student as a public class so that any other class can make use of this class to create objects of the Student class- When we make a class as public, this class can be used by any other class in our software.
If we had made the class as private – then this class would only be accessed only within the file where it was written. If there were other classes defined in the file Student.java, only those classes would have access to this class.
We have defined the properties as private. This is a by a rule in the programming language that says all properties and attributes must be private. Of course you can break the rule, but rules enforce conformity make our life easy.
But if the properties are private then how do we access the properties outside the class. This we do by means of special methods called the getters and setters
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
If you notice, the methods setName(String name) take a String input parameter called name. We then assign the value of the parameter to the property. To differentiate between the property and the parameter we use the indicator “this”. So “this” name is equal to the parameter name.
Also note the naming convention for the getName() and setName() – Java property names and method names always start with a lower case. For every word in the name, the first letter is capitalized. This is called camel-casing.
Example:
public String printGreetingAndWelcomeMessage() {
}
The method attendClass is defined to return void. To return void means does not return anything
The takeNotes() method returns a String and the writeExams() method returns a boolean (true or false) denoting the result of the exam
The three methods are public methods so they are callable even from other classes
So here is the modified class
Filename: Student.java
public class Student {
private String name;
private int age;
private int admissionNumber;
//-- Getters and Setters
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setAdmissionNumber(int admissionNumber) {
this.admissionNumber = admissionNumber;
}
public int getAdmissionNumber() {
return this.admissionNumber;
}
//-- Methods for the behaviour
public void attendClass() {
//-- write code here
}
public String takeNotes() {
//-- write code here
}
public boolean writeExams() {
//-- write code here
}
}
If you follow the above rules
i. define private properties
ii. define public getter and setter methods using the Java naming conventions
then you have defined a Java Bean.
Going forward, all our classes will follow the naming conventions for Java bean!
Objects
Let us now go ahead and create some Student objects:
How do we create objects? Lets write a program:
Filename: TestStudents.java
public class TestStudents {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("Jack");
s1.setAge(18);
s1.setAdmissionNumber(123);
Student s2 = new Student();
s2.setName("Jill");
s2.setAge(18);
s2.setAdmissionNumber(456);
}
}
Notice the method:
public static void main(String[] args) {
}
The main method is the entry point to the program. This is the method that Java first executes you run your program.
Object Type
If we consider the above code listing, we create two objects s1 and s2.
Both s1 and s2 belong to the Type called Student.
Objects have types and it is the name of the class it belongs to.
The type for s1 and s2 is Student.
Let us compile the programs:
We will make use of the javac compiler to compile the code that we have written so far.
You will now notice that we originally had .java files. Now we have .class files in addition to the .java files. These two files are the compiled code.
Now let us execute the code, using the java command.
You would have noticed that when we execute a .class file, the Java command expects to see the
public static void main(String[] args) method
If this method is not present, the Java Runtime Environment will throw an error:
You would also have noticed that we executed the TestStudents class file successfully, because it had a public static void main(String[] args) method.
Also notice our program does not show you any output – simply because we did not add one!
Let us modify our program to print some text.
Filename: PrintTestStudents.java
public class PrintTestStudents {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("Jack");
s1.setAge(18);
s1.setAdmissionNumber(123);
System.out.println(s1);
Student s2 = new Student();
s2.setName("Jill");
s2.setAge(18);
s2.setAdmissionNumber(456);
System.out.println(s2);
}
}
Compile the program and execute it
Okay, we now have some output – but it is far from being meaningful. We saw some output like this:
Student@19821f
Student@addbf1
This is not what we like – let us print something meaningful. We would have to modify the Student.java class and add the method
public String toString() {
//-- return a string value of the object
}
Before we do just that, let us look at Object state.
Object State
Every object belongs to a Type and has State.
The State of the object is the value of the attributes.
Object s1 has the state that the name is Jack, the age is 18 and the admissionNumber is 123 Object s2 has the state with the name as Jill, the age as 18 and admissionNumber as 456
Generally it is a good idea to print the state of the object in the toString() method.
Modify file:
Student.java as shown below
public class Student {
private String name;
private int age;
private int admissionNumber;
//-- Getters and Setters
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setAdmissionNumber(int admissionNumber) {
this.admissionNumber = admissionNumber;
}
public int getAdmissionNumber() {
return this.admissionNumber;
}
//-- Methods for the behaviour
public void attendClass() {
//-- write code here
}
public String takeNotes() {
//-- write code here
return null;
}
public boolean writeExams() {
//-- write code here
return true;
}
public String toString() {
return this.name + " " + this.age + " " + this.admissionNumber;
}
}
Now compile the code once again and execute the PrintTestStudents class
Now you can see that the output is now meaningful!
Jack 18 123
Jill 18 456
When you pass the object to the System.out.println() method, it calls the toString() method to print the state of the object.
To summarize what we have learned so far:
- We learnt why we write programs (to solve real world problems)
- How do we model real world entities ( as Objects and classes)
- Objects have a Type and a State
- What is a Java bean and coding best practises
- Why we need to implement the toString() method
Object Equality
We have seen so far how to create objects, set the state and print the object state in the toString() method. But how do we compare objects.
For example, is the object s1 above equal to s2?
Filename: TestObjectEquals.java
public class TestObjectEquals {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("Jack");
s1.setAge(18);
s1.setAdmissionNumber(123);
Student s2 = new Student();
s2.setName("Jill");
s2.setAge(18);
s2.setAdmissionNumber(456);
if (s1.equals(s2)) {
System.out.println("Objects are equal");
} else {
System.out.println("Objects are NOT equal");
}
}
}
Executing the program, now tells us that the objects are not equal. This seems to be fine.
Now let us modify the TestObjectEquals class to create another student;
Filename: TestObjectEquals.java
public class TestObjectEquals {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("Jack");
s1.setAge(18);
s1.setAdmissionNumber(123);
Student s2 = new Student();
s2.setName("Jill");
s2.setAge(18);
s2.setAdmissionNumber(456);
Student s3 = new Student();
s3.setName("Jack");
s3.setAge(18);
s3.setAdmissionNumber(123);
if (s1.equals(s2)) {
System.out.println(" s1 and s2 Objects are equal");
} else {
System.out.println("s1 and s2 Objects are NOT equal");
}
if (s1.equals(s3) {
System.out.println(" s1 and s3 Objects are equal");
} else {
System.out.println("s1 and s3 Objects are NOT equal");
}
}
}
Now we both know that s1 and s3 are the same. Let us run our program and confirm that again.
s1 and s2 Objects are NOT equal
s1 and s3 Objects are NOT equal
We get the output s1 and s3 are not equal. How can this be possible?
The Java Virtual Machine that executes the .class file you compiled (when you invoke the java command) calls the
public boolean equals(Object obj) {
}
method on the object. If the object does not have that method, it uses “references” to compare object.
What is a object reference?
To understand object references, we need to first understand the memory heap.
Any program that we run on the computer, is a process. And all process have an memory associated with it. One process cannot access the memory of its other process. Each process is allocated memory on the RAM (the Random Access Memory)
In the example above, we created new objects s1 and s2. We used the “new” operator to create the objects:
Student s1 = new Student();
s1.setName("Jack");
s1.setAge(18);
s1.setAdmissionNumber(123);
Student s2 = new Student();
s2.setName("Jill");
s2.setAge(18);
s2.setAdmissionNumber(456);
The operating system would have allocated two distinct memory on the Heap (the RAM) , and given us a handle to that memory.
s1 and s2 are both the handles we received for that memory.. Note that s1 and s2 are distinct areas of memory.
Then we created an object s3 using the new operator.
Student s3 = new Student(); s3.setName(“Jack”); s3.setAge(18); s3.setAdmissionNumber(123);
Now s3 points to a distinct area in memory – even though the state of s1 and s3 are the same.
That is why the JVM reports that s1 and s3 are not equal.
Then, how do we then make sure that the JVM reports properly when objects state are duplicate?
Object comparison should be based on the state of the object.
Let us add this method to the Student.java
Filename: Student.java
public class Student {
. . .
. . .
public boolean equals(Object obj) {
//-- first check reference
//-- if reference are the same, then just return true
if (this == obj) return true;
//-- Now check for instance
//-- if the object being compared is not a Student, simply return false
if( !(obj instanceof Student) ) return false;
//-- if we have reached so far, we can safely say obj is of Type Student
final Student thatStudent = (Student) obj;
return (this.admissionNumber == thatStudent.getAdmissionNumber());
}
. . .
}
Now the output is what we expect to see!
s1 and s2 Objects are NOT equal
s1 and s3 Objects are equal
The Hashcode
A hashcode is an integer representation of an object. Every object has an internal hashCode. The Java Virtual Machine assigns a hashcode to the object.
Well, what does it have to do with us, anyway?!
The Java programming language mandates that if objects are the same then their hashcode should also be the same.
Well, just a minute ago, we changed the default equality behavior – we made sure that the object state is taken into account while comparing objects. Since the admission number is guaranteed to be unique for each student, we made use of it in the equality comparison.
Now, the rule to be followed is, if you change the equality method, you must also implement the hashcode method. So that two object which are equal, should have the same hashcode.
Here is a simple implementation of the hashcode method:
public int hashCode() {
return admissionNumber * 19;
}
Let us add the hashCode method in the Student.java class
Filename: Student.java
public class Student {
. . .
. . .
. . .
public boolean equals(Object obj) {
//-- first check reference
//-- if reference are the same, then just return true
if (this == obj) return true;
//-- Now check for instance
//-- if the object being compared is not a Student, simply return false
if( !(obj instanceof Student) ) return false;
//-- if we have reached so far, we can safely say obj is of Type Student
final Student thatStudent = (Student) obj;
return (this.admissionNumber == thatStudent.getAdmissionNumber());
}
public int hashCode() {
return admissionNumber * 19;
}
}
Having come so far, you made some great progress on one of the fundamental building blocks of the Java programming language! You now have a firm grasp of the foundation of Objects and classes.
Summary
We saw how to model real world entities in Java – and the foundation of Objects and Classes. We understood the following:
- Understand the real world modeling in Java and why we define classes.
- How to define a class in Java.
- What is an object.
- The difference between Type and State of an object.
- What is meant by a Java Bean and the criteria that should be met to call a Java class as a Bean.
- Why we need to implement the toString() method and what will be the consequence of not implementing the toString() method?
- Why do we need to implement the equals method – In the absence of overriding the equals method, how does Java test for equality?
- What is a hashcode – and why do we need to implement the hashcode method?
Exercises
- Model a Employee in Java, having employeeNumber, name, age, designation, department, date of joining as attributes.
- Write a seperate program to create 3 object instances of Employee – Make it a pure Java bean – Do not implement the toString() method.
- Print the object instances on the console using System.out.println() method – observe the output.
- Now implement the toString() method for the class – print the object state using System.out.println() and observe the output
- Create 2 object instances with the same state – same employeeNumber, name, age, designation, department, date of joining
- Now check if they are the same using the equals() method – Observe the output
- Now implement the equals() method [ and the hashCode() method ]
- Again, check if they are the same using the equals method – Observe the output (Note how Java does equality check in the absence of the equals() method)
If you like the class – please consider making a donation
to help keep us going