Web/_posts/2019-10-14-java_4.md
Ell 08fc481589
All checks were successful
/ web (push) Successful in 1m1s
move website content to main directory
2024-06-14 17:49:53 +02:00

12 KiB

layout title description tags discuss archived
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.
Programming
https://twitter.com/Ellpeck/status/1183857460660101133 true

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!


  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 ↩︎