One of the most important and fundamental aspects to Java is the Java Object. Since this topic is central to the entire programming language, let's talk about the things you SHOULD know about Objects!

Okay, so what should you know? Well, first I want to talk about why the Java Object is so fundamental to the programming language.

The Java Object

We've already talked about many examples of Objects in Java. These are what I would define as a “noun” in any sentence that would define a business problem. These “nouns” could include words like, User, Library, Vehicle, but what's one Object we haven't yet mentioned? The answer is easier than you may think. It's Object! Did you know that you could set a variable's type in Java to be Object? Well, you can… and it's quite handy!

Everything is an Object

In Java, any object you create (User, Library, Vehicle) is actually of type Object. This concept is what is so fundamental to understand. So let's say you create the object User, it could look like this:

public class User
{
  private String username;
  private String password;

  public String getUsername()
  {
    return username;
  }
  public void setUsername(String username)
  {
    this.username = username;
  }
  public String getPassword()
  {
    return password;
  }
  public void setPassword(String password)
  {
    this.password = password;
  }
}

Now, there's nothing in this code that would indicate that this User object is actually of type Object. Usually, to determine if one particular object inherits properties of another object, you would look to see if your object extends another object. But in the case of our User there is no extends code. So what the heck?!

Well, since all Objects in Java inherit from the Object type, there's no need to explicitly show this in our code. If you don't believe me, try creating a new Object with your STS IDE. Here's a screenshot from when I made the User object above:

Java Object

Notice that the super class of this User object I'm creating automatically defaults to Object? Well, that's because all objects in Java inherit from Object!

Seriously, everything is an Object?

Okay so you believe me, everything inherits from Object, so you may ask “Are there any exceptions to this rule?”. Well, obviously! Aren't there exceptions to every rule that exist in this crazy world?

The exceptions are primitive data types, I've covered this topic in a previous post. Primitives do not inherit from the Object type in Java, but that's it… that's the only exception to the rule. Everything else in Java either directly (or indirectly) inherits from the Object type.

What are the implications to inheriting from Object

What's interesting to note, is that since every object in Java inherits from the Object type, that means that whatever methods are defined (as public or protected) on the Object type should be available to any object that WE create, right? Right!

So what are these methods?

Excellent question, let's take a look at the User object we created above. If we were to instantiate this User and take a look at which methods could be invoked on the User, what would we see? Well, we would expect to see the public methods that we defined right? These would be:

setPassword()
getPassword()
setUsername()
getUsername()

But here's what we actually see:

Java Object

There are a few methods there that we didn't create, these include:


equals()
getClass()
hashCode()
notify()
notifyAll()
toString()
wait()
wait(long timeout)
wait(long timeout, int nanos)

And if you notice in the screenshot above, on the far right of all these methods, is the word Object. This is because all of these methods belong to the Object type. Make sense? It's nice to know what's actually going on here right? So then the only other topic to cover is what all of these methods are used for… I don't want to dive into all of them in detail, so I'll get the ones I don't want to talk about out of the way right now.

Threading Methods

  • notify()
  • notifyAll()
  • wait()
  • wait(long timeout)
  • wait(long timeout, int nanos)

All of these methods are related to threading, and I don't want to explain right now, as the threading topic is likely a whole series of posts. If you've ever heard of the term “Multi-threading”, it has to do with being able to simultaneously run multiple “tasks” all at once. The above methods help facilitate multi-threading. So although they are very interesting in their own rights, I don't think it would be useful to talk about them at this point. Let's look at the methods that remain:

equals()

This is a method you've probably seen before. We use it when comparing two Strings to each other. It's the method that we use when we compare any two objects with each other. But hang on a second, I thought we used the “==” operator to compare objects! You've seen it before:

int someNumber = 478;
int someOtherNumber = 983;

if (someNumber == someOtherNumber)
{
  System.out.println("These numbers are equal");
}
else
{
  System.out.println("These numbers are NOT equal");
}

So why are we talking about a equals() method then? Well, remember when I said that primitive types are the exception to the rule? Well that means that primitive types don't inherit from the Object type right? And if that's the case, then that means that primitive types don't have access to the methods that are defined within Object, which includes equals()!

Very interesting, so then if we want to compare two objects, then we just use the equals method then right? This is true, but there's a bit of work that we'll need to do to ensure that when we do compare two objects, we get the expected result. Consider the following:

User user1 = new User();
User user2 = new User();

user1.setUsername("Trevor Page");
user1.setPassword("aPassword");

user2.setUsername("Trevor Page");
user2.setPassword("aPassword");

System.out.println("Are users equal? " + user1.equals(user2));

What would you expect to see as the output here? We are instantiating two User objects, and we are assigning the exact same username and password to both Users. Then we are just invoking the equals() method to check if both Users are equal. We would assume that since both the username and password are equal, that the result would be “true”. Here's the actual result:

Are users equal? false

Well what the heck! Why aren't they equal? Well, this comes down to inheritance and the default implementation of the equals() method.

Now pay attention, because this is important!

If you do not override the default implementation of the equals() method, then Java will default to the strictest implementation of an equals comparison. And that is the “==” operator!

What does the “==” operator do!

The “==” operator (spoken as “equals equals operator”) compares two objects by their physical memory address. I have a good discussion on what memory is all about in Java in a previous post about Strings, so if you haven't already, please read the section entitled “Memory” in this post.

So how does this apply to our example above with the two Users? Since we haven't overriden the equals() method, it will compare the two objects using the “==” operator, which will compare their addresses in memory. Since I created two objects (via the new keyword), this means we have two separate objects in two separate memory locations. Since they have two unique locations in memory, the equals() comparison will return false.

How do we override the equals() method

In our Users example we would like to compare two users to each other, but we don't want to compare the physical memory addresses, we want to define our own meaning of equals. So what in your mind would constitute equality among Users? I think it would just be if two Users had the same username! Hopefully in any web application, no two users would be allowed to have the same usernames. So if we have two User objects, and they both have the same username, then we could consider them equal.

How do we accomplish this? We override the equals() method on the User object! So our new User object would look like this:

public class User
{
  private String username;
  private String password;

  public String getUsername()
  {
    return username;
  }
  public void setUsername(String username)
  {
    this.username = username;
  }
  public String getPassword()
  {
    return password;
  }
  public void setPassword(String password)
  {
    this.password = password;
  }

  @Override
  public boolean equals(Object obj)
  {
    return this.username.equals(((User)obj).getUsername());
  }
}

Notice the last section of our User object has an @Override over the equals() method. Well, this is how we override the equals method of the super class (the Object class). This is the inheritance I was talking about. In Java we are allowed to “change” the behaviour of a method that we inherit from a parent Class. In this case, the parent Class of our User object is Object. So, because the Object Class has an equals() method, then that means we can change its behaviour in our child class (the User Class). So, since we didn't like how the Object‘s equals() method worked, we override its functionality in our User Class! And this is the code you see above.

So! This is fairly complex stuff if you're new to programming, so allow me to keep explaining. Let's take a look at the code where we actually compare the two Users together, as this may shed some light on this subject…

User user1 = new User();
User user2 = new User();

user1.setUsername("Trevor Page");
user1.setPassword("aPassword");

user2.setUsername("Trevor Page");
user2.setPassword("aPassword");

System.out.println("Are users equal? " + user1.equals(user2));

This code will now output what we would expect:

Are users equal? true

Okay great, so maybe you're thinking “I kind of understand the concepts, but I really don't understand how that's reflected in the code!”. So let me try to explain what's happening.

I want you to concentrate on the most important part of the code above:

user1.equals(user2)

Remember that the User definition (with the @Override equals code) is a Class that represents the blueprint for any User object. So when we instantiate a User object, each one will essentially have its own version of the @Override equals method defined in the User Class.

This means that when we run that important code, we are saying:

Hey! user1! Run your equals() method, and pass in user2 as a parameter. So what we may actually see if we were to debug this is the following (I've commented out the actual code, and instead, replaced the code with what would essentially be replaced at runtime when Java runs the code):

  @Override
//public boolean equals(Object obj)
  public boolean equals(User user2)
  {
// return this.username.equals(((User)obj).getUsername());
    return user1.username.equals(user2.getUsername());
  }

So, what really ends up happening is that we pass in the user2 object as a parameter. Now, what normally happens is the equals method takes an Object as a parameter, but since a User is an Object, Java is okay with you doing this (inheritance). Then we say, this.username, well this just refers to whatever Object the Class represents. So in our case, since we invoked the equals() method of user1, that means we're inside of the user1 equals method, which means that when we say this we really mean user1! But then we say this.username.equals(), doesn't that mean we just invoke the equals method on user1 again, resulting in an endless loop? No! We're invoking equals on user1.username… and username is just a String. So that's perfectly legitimate, as String defines it's own equals() method. Then we just pass in user2 username into the equals method for the String comparison. Which we know that, if both Strings are the same, then it will return true!

Phew, that's some hardcore coding there! If you understand it, then that's amazing and I've done a great job of explaining the concepts. If you don't understand it, then you're probably part of the large portion of the new programmers on this planet. The topics that stem from Java's Object class are quite complex as they require a solid understanding of Object Oriented Programming (more specifically inheritance and polymorphism). So, if you don't get it, take some time to re-read this tutorial and maybe try messing around with the coding examples I've given.

Loose ends

I have yet to talk about the other methods that come from the Object class, such as hashCode() and toString(). So let me touch on these quickly.

toString()

This method is used to return a String representation of our objects. The default implementation of this (from the Object class) will just return a readable version of the physical memory address. So if I were to do a System.out on the user1 object, I would get the following:

com.howtoprogramwithjava.business.User@7d8a992f

This means nothing to us, and it's not helpful, other than if you would like to check to see if two objects reference the same memory location. So that's why we can override the toString() method to give something more meaningful. Let's override it! Please add the following method to our User class

  @Override
  public String toString()
  {
    return "Username: " + this.username + ", password: " + this.password;
  }

Now, if we were to invoke user1.toString(), we wouldn't get a meaningless representation of a memory location, we would get:

Username: Trevor Page, password: aPassword

Hey! That actually makes some sense, and it's useful! So, that's a quick rundown on the toString() method.

hashCode()

This is a more advanced topic that revolves around the use of HashMaps, we've talked about what Maps are back in one of my first posts about data types. So for the sake of completion (and for the sake of not overloading your brain), I'll just say that generally when you override the equals method, it's good practice to also override the hashCode() method. This is so that if your objects are put into a Map, Java will be able to “store” them nicely inside of your HashMap. How do you override hashCode()? Also slightly advanced, but for now try this… In your STS, try right-clicking in your User code then:

Source -> Override/Implement Methods -> Choose HashCode -> OK

Summary

We've covered quite a lot of topics in this Java Object post. So I encourage you to take a break, try to digest this information, and perhaps come BACK and re-read everything so you have a good understanding of these concepts. As I've said, these are more advanced topics, but they are critical to understand if you want to get the hang of programming in Java.

So please, by all means, if you are unclear on something, leave a question in the comments section and I'll get back to you… I promise! I love to help you guys out, so that means I love answering questions. So seriously, scroll down right now, leave a comment!

Take care everyone and I look forward to hearing from you :)

Bonus Q&A

One reader was confused by the @Override of the equals() method, so I tried my best to answer the question in as much detail as I could in the video below, I hope this helps others as well:

Free Java Beginners Course

Start learning how to code today with our free beginners course. Learn more.