Hi! Today we will talk about classes in Java.
Getting started with classes: writing your own classes, constructors - 1
You could say that classes form the cornerstone of Java programming. When you become a programmer, nearly your entire job will be writing your own classes that have various functions. Let's see what that means and how it works. :) As you know, Java is an object-oriented programming language. All programs consist of objects that in one way or another are related to each other. A class is, essentially, a template for an object. It determines what the object will look like and what functions it will have. Every object is an object of some class. Consider this very simple example:
public class Cat {

    String name;
    int age;

}
Let's say we're writing a program that involves cats for some reason (for example, we have a veterinary clinic that offers access to an online account). We've created a Cat class, and declared two variables in it: String name and int age. These member variables are called fields. Essentially, this is a template for all the cats we will create in the future. Each Cat object will have two variables: a name and an age.
public class Cat {

    String name;
    int age;

    public static void main(String[] args) {
        Cat smudge = new Cat();
        smudge.age = 3;
        smudge.name = "Smudge";

        System.out.println("We created a cat named " + smudge.name + ". His age is " + smudge.age);
    }

}
Here's how it works! We create a cat, give it a name and age, and display it all on the console. Piece of cake. :) More often than not, classes describe things and phenomena in the real world. A cat, a table, a person, a lightning bolt, a page of a book, a wheel—you'll create all these things in your programs using individual classes. For now, let's focus on the variables we declared in the Cat class. They are called fields, or instance variables. Their name really says it all. Each instance (or object) of the Cat class will have these variables. Each cat we create will have its own name variable and its own age variable. This makes sense—it's basically how it is with real cats. :) In addition to instance variables, there are also class variables (static variables). Let's finish our example:
public class Cat {

    String name;
    int age;

    static int count = 0;

    public static void main(String[] args) {
        Cat smudge = new Cat();
        smudge.age = 3;
        smudge.name = "Smudge";
        count++;

        Cat fluffy = new Cat();
        fluffy.age = 5;
        fluffy.name = "Fluffy";
        count++;

        System.out.println("We created a cat named " + smudge.name + ". His age is " + smudge.age);
        System.out.println("We created a cat named " + fluffy.name + ". His age is " + fluffy.age);

        System.out.println("Total number of cats = " + count);
    }
}
Console output: We created a cat named Smudge. His age is 3 We created a cat named Fluffy. His age is 5 Total number of cats = 2 Now our class has a new variable called count. It is responsible for counting the created cats. Every time we create a cat in the main method, we increase this variable by 1. This variable is declared using the keyword static. That means it belongs to the class, not to a specific object of the class. Which, of course, makes sense: each cat's name belongs to that specific cat, but we need one cat counter that applies to them all. This is precisely what the keyword static accomplishes: it makes the count variable a single variable for all the cats. Note: when we display the variable, we aren't using smudge.count or fluffy.count. It doesn't belong to either Smudge or Fluffy; it belongs to the entire Cat class. That's why it's simply count. You could also write Cat.count. That would also be correct. When display the name variable, we wouldn't do the following:
public class Cat {

    String name;
    int age;

    static int count = 0;

    public static void main(String[] args) {
        Cat smudge = new Cat();
        smudge.age = 3;
        smudge.name = "Smudge";
        count++;

        System.out.println("We created a cat named " + name + ". His age is " + smudge.age);

        System.out.println("Total number of cats = " + count);
    }
}
This is an error! Each cat has its own name. The compiler gets confused here. "Output a name to the console? Whose name?" :/"

Methods

In addition to variables, each class has methods. We'll talk about them in more detail in a separate lesson, but the general principles are quite simple. Methods define your class's functionality, i.e. what objects of your class can do. You are already familiar with one of these methods: the main() method. But, as you might remember, the main method is static, which means it belongs to the entire class (the logic is the same as with variables). However, standard, non-static methods can only be called on specific objects we've created. For example, if we want to write a cat class, we need to know what functions a cat in our program should have. On that premise, let's write a couple of methods for our cat:
public class Cat {

    String name;
    int age;

    public void sayMeow() {
        System.out.println("Meow!");
    }

    public void jump() {
        System.out.println("Pounce!");
    }

    public static void main(String[] args) {
        Cat smudge = new Cat();
        smudge.age = 3;
        smudge.name = "Smudge";

        smudge.sayMeow();
        smudge.jump();

    }
}
Check it out! Now our class is much closer to resembling a cat! Now we don't just have a cat with a name ("Smudge") and an age (3). He can also say meow and jump! What kind of cat would it be without that "functionality"? :) We are taking a specific object (smudge) and calling its sayMeow() and jump() methods. Let's look at the console: Meow! Pounce! A real cat! :)

Creating your own classes. Abstraction

In the future, you will have to write your own classes. What do you need to look out for when you write them? If we're talking about variables, then you'll need to make use of something called abstraction. Abstraction is one of the four basic principles of object-oriented programming. It means extracting the most important and significant characteristics of an item, and conversely, casting aside those that are minor or insignificant. For example, let's create a filing cabinet for company employees. To create employee objects, we've written an Employee class. What characteristics are important descriptors of an employee for our company's filing cabinet? Name, date of birth, SSN, and employee ID. But it's unlikely we'll need the employee's height, eye color, or hair color for the company's employee record. Companies don't need this information. So, in the Employee class, we declare the following variables: String name, int age, int socialSecurityNumber, and int employeeId. And we leave out unnecessary information (like eye color). In other words, we create an abstraction. However, if we're making a filing cabinet for modeling agencies, the situation changes dramatically. A model's height, eye color, and hair color are important characteristics, but her SSN is absolutely irrelevant to us. So, in the Model class, we need to create the following variables: String height, String hair, String eyes. That's how abstraction works—it's easy! :)

Constructors

Let's go back to our cat example.
public class Cat {

    String name;
    int age;

    public static void main(String[] args) {
        Cat smudge = new Cat();

        System.out.println("Here the program does something for 2 hours...");

        smudge.age = 3;
        smudge.name = "Smudge";

    }
}
Look over this code and try to figure out what's wrong with our program. Our program had a cat with no name or age for 2 hours! Of course, this is inherently wrong. The veterinary clinic's database shouldn't include cats with no information. Currently, our cat is at the mercy of the programmer. We trust that he won't forget to specify a name and age, and that everything will be okay. If he forgets, the database will have a problem: unnamed cats. How can we solve this problem? We must somehow prevent cats from being created without a name and age. This is where constructors come to the rescue. Let's give an example:
public class Cat {

    String name;
    int age;

    // Constructor for the Cat class
    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat smudge = new Cat("Smudge", 5);
    }
}
Essentially, a constructor is a template for objects of a class. In this case, we indicate that two arguments, a String and an int, must be specified for each cat object. If we try to create a nameless cat now, it won't work.
public class Cat {

    String name;
    int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat smudge = new Cat(); // Error!
    }
}
Now that the class has a constructor, the Java compiler knows what the objects should look like, and doesn't allow objects to be created without specifying the arguments. Now, let's investigate the keyword this, which you see inside the constructor. It's simple too. The keyword this is for indicating a particular object. The code in the constructor
public Cat(String name, int age) {
    this.name = name;
    this.age = age;
}
can be interpreted almost verbatim: "The name of this cat (the one we are creating) = the argument passed in for the constructor's name parameter. The age of this cat (the one we are creating) = the argument passed in for the constructor's age parameter." After the constructor runs, you can verify that all of the necessary values have been assigned to our cat:
public class Cat {

    String name;
    int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat smudge = new Cat("Smudge", 5);
        System.out.println(smudge.name);
        System.out.println(smudge.age);
    }
}
Console output: Smudge 5 When the constructor was called:
Cat smudge = new Cat("Smudge", 5);
This is what actually happened internally:
this.name = "Smudge";
this.age = 5;
And the values of the arguments passed to the constructor were assigned to the smudge object (that's what this refers to in this case). In fact, even if you don't declare any constructors in a class, it will still call a constructor! But how is that possible? О_О Because, all Java classes have a so-called default constructor. It doesn't take any arguments, but it is invoked every time you create any object of any class.
public class Cat {

    public static void main(String[] args) {

        Cat smudge = new Cat(); // The default constructor is invoked here
    }
}
At first glance, this may not be obvious. We created an object, so what? Where is the constructor doing anything here? To see it, let's explicitly write an empty constructor for the Cat class. Inside, it, we'll output some phrase to the console. If the phrase is displayed, then the constructor was invoked.
public class Cat {

    public Cat() {
        System.out.println("A cat has been created!");
    }

    public static void main(String[] args) {

        Cat smudge = new Cat(); // The default constructor is invoked here
    }
}
Console output: A cat has been created! There's the confirmation. The default constructor is always invisibly present in your classes. But you need to know one more thing about it. The default constructor is eliminated from a class once you create a constructor with arguments. In fact, we've already seen proof of this above. It was in this code:
public class Cat {

    String name;
    int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat smudge = new Cat(); // Error!
    }
}
We couldn't create a Cat without a name and age, because we declared a Cat constructor with string and int parameters. This caused the default constructor to immediately vanish from the class. So be sure to remember that if you need several constructors in your class, including a no-argument constructor, you'll have to declare it separately. Our clinic wants to do good deeds and help homeless kittens whose names and ages are unknown. Then our code should look like this:
public class Cat {

    String name;
    int age;

    // For cats with owners
    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // For street cats
    public Cat() {
    }

    public static void main(String[] args) {

        Cat smudge = new Cat("Smudge", 5);
        Cat streetCat = new Cat();
    }
}
Now that we have specified an explicit default constructor, we can create both types of cats. In the constructor, you can assign values directly. You don't have to always take them from arguments. For example, we could label all street cats in the database using "Street cat No. <count>" as a template. :
public class Cat {

    String name;
    int age;

    static int count = 0;

    public Cat() {
        count++;
        this.name = "Street cat No. " + count;
    }

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat streetCat1 = new Cat();
        Cat streetCat2 = new Cat();
        System.out.println(streetCat1.name);
        System.out.println(streetCat2.name);
    }
}
We have a count variable, which counts our street cats. Each time the default constructor is executed, we increase count by 1 and attach this number to the cat's name. The order of arguments is very important for constructors. Let's swap the name and age arguments passed to our constructor.
public class Cat {

    String name;
    int age;

    public Cat(int age, String name) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat smudge = new Cat("Smudge", 10); // Error!
    }
}
We got an error! The constructor clearly stipulates that when a Cat object is created, it must be passed a number and a string, in this order. So, our code doesn't work. Be sure to remember and respect this rule when declaring your own classes:
public Cat(String name, int age) {
    this.name = name;
    this.age = age;
}

public Cat(int age, String name) {
    this.age = age;
    this.name = name;
}
These are two totally different constructors! Now, complete a couple tasks to solidify your understanding of the material. :)
  1. Museum of Antiquities.

    Your task is to design an Artifact class.
    There are three types of artifacts kept at the museum.
    We know nothing about the first type except the serial number assigned by the museum (for example: 212121).
    For the second type, we know the serial number and the culture that created it (for example: 212121, "Aztecs").
    For the third type, we know the serial number, the culture that created it, and the century it was created in (for example: 212121, "Aztecs", 12).

    Create an Artifact class that describes the antiquities kept at the museum, and write the required set of constructors for the class. Then, in the main() method, create one artifact of each kind.

    public class Artifact {
    
        // Write your code here
    
        public static void main(String[] args) {
            // Write your code here
        }
    }
  2. Dating website

    Let's create a user database for a dating website.
    But here's the problem: you forgot the required order of the arguments, and there's no technical documentation available.
    Design a User class, which will have the following fields: name (String), age (short), and height (int).
    Create the appropriate number of constructors, so that the name, age, and height can be specified in any order.

    public class User {
    
        String name;
        short age;
        int height;
    
        // Write your code here
    
        public static void main(String[] args) {
    
        }
    }