Web/main/_posts/2019-10-14-java_4.md

216 lines
12 KiB
Markdown
Raw Normal View History

---
layout: blog
title: "☕ Java Tutorial, Part 4: Classes and Objects"
description: 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.
tags: [Java Tutorials]
discuss: 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:
```java
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]
```java
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:
```java
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:
```java
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:
```java
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:
```java
public class Table {
public int legAmount;
}
```
Now, each table *instance* can have its amount of legs modified as well as displayed like so:
```java
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...
```java
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:
```java
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:
```java
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:
```java
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...
```java
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.
```java
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:
```java
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:
```java
// 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:
```java
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](https://gist.github.com/Ellpeck/462022597659f554fdd75663359480d3) or [look at my solution](https://gist.github.com/Ellpeck/7a0f31306d05473c10e8bca1685510a4).
Next time, we'll be talking about arrays and lists.[^5] Happy coding!
[^1]: Java's default `Math` class already has an `abs` 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).
[^2]: Note that I explain why we're omitting the `static` keyword from now on a bit later in this tutorial.
[^3]: 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.
[^4]: 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.
[^5]: 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