12 KiB
layout | title | summary | tags | discuss | |
---|---|---|---|---|---|
blog | ☕ Java Tutorial, Part 4: Classes and Objects | In this Java tutorial for beginners, we cover the basics of creating classes with a constructor, some fields and some methods and creating objects of them. |
|
https://twitter.com/Ellpeck/status/1183857460660101133 |
So you've gotten far enough into Java now that you'll finally be able to learn about what this "object orientation" jazz is that everyone keeps talking about. I feel like this is a somewhat complicated topic to explain, so bear with me.
Classes
So far in the tutorial, we've only ever used classes as basic storage containers for variables and methods. Let's stick with that for another moment while we create a simple Utility
class that has a method we might want to use throughout our program. We'll create it in a new file (Right-Click on src
, New
, Java Class
in IntelliJ) like so:
public class Utility {
}
As you can see, this structure is similar to that of our Main
class: The word public
, followed by the word class
, followed by the name of the class (which, in 99% of cases, should match the file name), followed by curly braces {}
, which, as you might expect by now, contain the content of our class.
So let's add a method to it that, I don't know, returns the absolute value of an integer passed to it. We'll call this one abs
, for "absolute".1
public class Utility {
public static int abs(int num) {
if (num < 0) {
return -num;
} else {
return num;
}
}
}
By now, this code should be pretty easy for you to understand: We've added a method called abs
that takes in an integer parameter called num
, and if num
is negative, then we return the negated value of num (-num
).
Now, because this method is in a different class to the one we might want to use the method in (Main
in our case), we have to explicitly mention the class the method is in by prefixing the class' name followed by a dot .
when calling it, like so:
public class Main {
public static void main(String[] args) {
// This is our usual main method
int abs = Utility.abs(-17);
System.out.println(abs);
}
}
Objects
Let's finally move away from static
methods and into object orientation.2 I'll give you a quick, made-by-me outline of what object orientation even means, based on an example that might be a bit easier for you to understand.
Let's imagine we're not talking about objects, but tables. When you say something like "A table has four legs", you're not talking about a specific table, but rather, you're talking about the concept of what a table is. In Java, this is what a class is. Now, when you say something like "This table has four legs" while pointing to the table on the other side of the room, then you're talking about a very specific table, not just a concept anymore. In Java, this is what an object is. When we now say "object oriented", we merely mean "code that makes use of objects."
Another way to think of it is that classes are blueprints for certain things, and objects are the actual constructions that were created by following those blueprints.
Creating Objects
So let's stick with this table example for now. First, we'll create a class called Table
like so:
public class Table {
}
So now, we have a very basic blueprint of a table.
Now, let's go back into our main class and actually create a specific table like so:
public class Main {
public static void main(String[] args) {
// This is our usual main method
Table myTable;
myTable = new Table();
}
}
As you can see, the structure of line 5 is the same as how we usually declare variables: Table
is the variable type (this is where we usually put int
or String
), and myTable
is the name of the variable. Line 6 is where the new stuff happens, however: We set the variable's value to a newly created instance of the Table
class. This is us creating a specific table following the blueprint that is the Table
class: We write new
, then the class' name, and then opening and closing parentheses ()
.
Fields
What we have so far isn't really that interesting yet - our table doesn't really have any properties yet. But now, we can actually add fields to our class: Certain variables that each object will share, but whose values can also be different for each object. Fields are declared like normal variables, but are placed at the top of a class, prefixed with the public
keyword like so:
public class Table {
public int legAmount;
}
Now, each table instance can have its amount of legs modified as well as displayed like so:
Table myTable = new Table();
myTable.legAmount = 4;
System.out.println("My table has " + myTable.legAmount + " legs");
As you can see, to access an object's fields, all you have to do is write the variable name, followed by a dot .
, followed by the field's name.
The important thing to know here is that this variable is now unique for every table instance you create, meaning the following code...
Table myTable = new Table();
Table myOtherTable = new Table();
myTable.legAmount = 4;
myOtherTable.legAmount = 25;
System.out.println("My table has " + myTable.legAmount + " legs");
System.out.println("My other table has " + myOtherTable.legAmount + " legs");
...will print out My table has 4 legs
, followed by My other table has 25 legs
.
Methods
Obviously, you already know what methods are, but we can finally get rid of the static
keyword for those as well. Similarly to what we can do with fields, we can also add methods to classes that can do unique things based on the object they're being called for. Let's take a look at the following code:
public class Table {
public int legAmount;
public void printLegAmount() {
System.out.println("I have a certain amount of legs");
}
}
As you can see, I've added a (non-static) method to this class called printLegAmount
. Now we can change the code in our Main
class to just call that method instead:
Table myTable = new Table();
myTable.legAmount = 4;
myTable.printLegAmount();
The this
keyword
Now that specific example isn't really that useful yet, because every single table we create will print out I have a certain amount of legs
, when it would really be nicer if the table printed out its amount of legs stored in the legAmount
variable.
That's exactly what the this
keyword is for.3 Let's modify our method from before:
public class Table {
public int legAmount;
public void printLegAmount() {
System.out.println("I have " + this.legAmount + " legs");
}
}
As you can see, this
is being used here similarly to how we were using myTable.legAmount
earlier. That has a good reason: this
also refers to an object. However, the object that this
refers to is the current one. When calling a method like printLegAmount
for a specific object (myTable
in our case), the this
keyword will always refer to that object.
So if we have code like this...
Table myTable = new Table();
Table myOtherTable = new Table();
myTable.legAmount = 4;
myOtherTable.legAmount = 25;
myTable.printLegAmount();
myOtherTable.printLegAmount();
...then the result will be the same as before: My table has 4 legs
, followed by My other table has 25 legs
, because in myTable.printLegAmount()
, this
will be myTable
, and in myOtherTable.printLegAmount()
, this
will be myOtherTable
.
Constructors
Constructors are a special kind of method in Java. You can't really execute them manually, but instead, they're automatically called every time a new instance is created (so every time you use the new
keyword).
Let's look at the following example, which adds a constructor to our Table
class that automatically sets the legAmount
variable to 4.
public class Table {
public int legAmount;
// The constructor
public Table() {
this.legAmount = 4;
}
public void printLegAmount() {
System.out.println("I have " + this.legAmount + " legs");
}
}
As you can see, a constructor is different from other methods in that it doesn't have a name, and that its return type is the same as the class it is in.
So now, any instance of Table
that we create will automatically have a leg amount of 4:
Table myTable = new Table();
myTable.printLegAmount(); // "I have 4 legs"
Constructor parameters
Just like in any other method, we can add a set of variables that the constructor accepts (parameters). In our case, we could make a table request a legAmount
when it's initialized as follows:
// The constructor
public Table(int legs) {
this.legAmount = legs;
}
Now, when creating a new instance of Table
, we have to actually give it a leg amount that it should start with:
Table myTable = new Table(4);
So what about static
?
As you have learned throughout this tutorial, non-static methods and variables inside of classes have a key property: They're unique for every object that is created of that class. So why have we been using static
throughout both our Main
and our Utility
classes?
The static
keyword tells any field or method that it should not be unique for every object that is created. Even if we created an instance of our Main
class (new Main()
), for example, the main
method would not be unique for that instance, because it is marked with the static
keyword. This also means that using the this
keyword would make no sense in a static method (which object would it reference?), which is why doing so doesn't work.
Similarly, if we made the abs
method in our Utility
class non-static, then we could not access it simply by typing Utility.abs()
, because the method would be unique for every object that is created of the class. We could then use the abs
method if we first created an instance of the Utility
class (new Utility()
).
Conclusion
So yea, that's quite a lot of information for you to process. The introduction to objects is a pretty big step, but it also allows for a lot of cool new stuff that you can learn. In the next couple tutorials, we'll expand on the concept of objects and learn some more stuff that you can do with them to make them even more useful.
For now, though, I'll leave you with this little exercise you can do to facilitate what you learned about objects and to get a better understanding of how useful they are:4
Let's imagine you're managing a small car dealership. You want to have a way of managing all of the cars you have in stock. This includes keeping track of their brand names, their horsepower, their license plate texts and their mileage. Currently, you have four different cars in stock, and you want your program to print out all of the information about all of the cars.
If you're stuck, you can get some hints or look at my solution.
Next time, we'll be talking about arrays and lists.5 Happy coding!
-
Java's default
Math
class already has anabs
method that does this same thing, but for the sake of this tutorial, we'll ignore it (because I can't think of any other utility method to add, in all honesty). ↩︎ -
Note that I explain why we're omitting the
static
keyword from now on a bit later in this tutorial. ↩︎ -
In a lot of cases, using the
this
keyword is actually optional and it can just be left out altogether. However, I personally think that it increases code readability quite a bit and that it helps a lot with understanding how objects work, so I advise you to use it. ↩︎ -
This example is one that almost every instructor uses. It might be a bit boring and ridiculous, but it's simple and helps understanding the concept, so I also like using it when teaching people how to code. ↩︎
-
Note that there is also another huge topic to talk about when it comes to object orientation, which is pointers and how Java manages objects and their assignment to variables (including pass-by-reference and the like). I'll cover that soon. I promise. <3 ↩︎