Привет! Сегодня мы поговорим о классах в Java. Классы — это, можно сказать, основа основ программирования на Java. Когда ты станешь программистом, твоей основной задачей, собственно, будет написание собственных классов с разным функционалом. Давай разберемся, что же это за штука такая и как она работает:) Как ты же знаешь, Java — это объектно-ориентированный язык программирования. Все программы состоят из объектов, которые как-то связываются между собой. Класс — это, по сути, шаблон для объекта. Он определяет, как объект будет выглядеть и какими функциями обладать. Каждый объект является объектом какого-то класса. Рассмотрим самый простой пример:
public class Cat {

    String name;
    int age;

}
Допустим, мы пишем программу, и в этой программе нам для чего-то нужны кошки (например, у нас ветеринарная клиника с возможностью онлайн-записи). Мы создали класс Cat, и указали для него две переменные — строку name, и число age. Такие переменные классов называются полями. По сути — это шаблон для всех кошек, которых мы создадим в будущем. У каждой кошки (объекта класса Cat) будет две переменных — имя и возраст.
public class Cat {

    String name;
    int age;

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Барсик";

        System.out.println("Мы создали кота по имени " + barsik.name + ", его возраст - " + barsik.age);
    }

}
Вот так это работает! Мы создали кота, присвоили ему имя и возраст, и вывели все это в консоль. Ничего сложного:) Классы чаще всего описывают предметы и явления окружающего мира. Кошка, стол, человек, молния, страница книги, колесо — все это в твоей программе будет создаваться с помощью отдельных классов. Теперь давай обратим внимание на переменные, которые мы создали в классе Cat. Они называются полями, или переменными экземпляров. В названии, собственно, раскрыта вся их суть. Эти переменные будут у каждого экземпляра (объекта) класса Cat. У каждого кота, которого мы создадим, будет своя переменная name и своя переменная age. Логично, в общем-то — с настоящими котами все так и есть:) Кроме переменных экземпляров существуют и другие — переменные классов, или статические. Дополним наш пример:
public class Cat {

    String name;
    int age;

    static int count = 0;

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Барсик";
        count++;

        Cat vasia = new Cat();
        vasia.age = 5;
        vasia.name = "Вася";
        count++;

        System.out.println("Мы создали кота по имени " + barsik.name + ", его возраст - " + barsik.age);
        System.out.println("Мы создали кота по имени " + vasia.name + ", его возраст - " + vasia.age);

        System.out.println("Общее количество котов = " + count);
    }
}
Вывод в консоль: Мы создали кота по имени Барсик, его возраст - 3 Мы создали кота по имени Вася, его возраст - 5 Общее количество котов = 2 Теперь у нас в классе появилась новая переменная — count (количество). Она отвечает за подсчет созданных котов. Каждый раз, когда в методе main мы создаем кота — мы увеличиваем эту переменную на 1. Эта переменная обозначена ключевым словом static. Это значит, что она принадлежит классу, а не конкретному объекту класса. Что, конечно, логично: если имя у каждого кота должно быть свое, то счетчик котов нам нужен один на всех. Именно этого позволяет добиться слово static — переменная count одна для всех котов. Обрати внимание: когда мы выводим ее в консоль, мы не пишем barsik.count или vasia.count. Она не принадлежит ни Барсику, ни Васе — она принадлежит всему классу Cat. Поэтому — просто count. Можно также написать Cat.count — это тоже будет правильно. С выводом в консоль переменной name у нас бы такое не прошло:
public class Cat {

    String name;
    int age;

    static int count = 0;

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Барсик";
        count++;

        System.out.println("Мы создали кота по имени " + name + ", его возраст - " + barsik.age);

        System.out.println("Общее количество котов = " + count);
    }
}
Ошибка! name у каждого кота свое. В этом месте компилятор сбивается с толку. "Вывести в консоль имя? А чье имя-то? :/"

Методы

Кроме переменных, у каждого класса есть методы. О них мы поговорим в отдельной лекции более подробно, но общие моменты довольно просты. Методы — это функционал твоего класса; то, что объекты этого класса умеют делать. С одним из методов ты уже знаком — это метод main(). Но метод main, как ты помнишь, является статическим — то есть он принадлежит всему классу (логика такая же, как с переменными). А обычные, нестатические методы, можно вызывать только на конкретных объектах, которые мы создали. Например, если мы хотим написать класс для кошки — нам надо понять, какими функциями кошка должна обладать в нашей программе. Исходя из этого, напишем для нее пару методов:
public class Cat {

    String name;
    int age;

    public void sayMeow() {
        System.out.println("Мяу!");
    }

    public void jump() {
        System.out.println("Прыг-скок!");
    }

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Барсик";

        barsik.sayMeow();
        barsik.jump();

    }
}
Ну вот, теперь наш класс гораздо больше похож на описание настоящей кошки! У нас теперь не просто кот Барсик с именем и возрастом. Он еще умеет мяукать и прыгать! Какой же кот без такого "функционала" :) Берем конкретный объект — barsik, и вызываем у него методы sayMeow() и jump(). Смотрим в консоль: Мяу! Прыг-скок! Настойщий кот!:)

Создание собственных классов. Абстракция

В будущем тебе придется писать собственные классы. На что нужно обратить внимание при их написании? Если мы говорим о переменных — то здесь тебе нужно пользоваться такой вещью как абстракция. Абстракция — один из четырех основных принципов объектно-ориентированного программирования. Она означает выделение главных, наиболее значимых характеристик предмета, и наоборот — отбрасывание второстепенных, незначительных. Например, мы создаем картотеку работников компании. Для создания объектов "работник" мы написали класс Employee. Какие характеристики важны для описания работника в картотеке компании? ФИО, дата рождения, номер социального страхования, ИНН. Но вряд ли в карточке работника компании нам нужны его рост, цвет глаз и волос. Компании эта информация ни к чему. Поэтому для класса Employee мы зададим переменные String name, int age, int socialInsuranceNumber и int taxNumber, а от лишней для нас информации (типа цвета глаз) откажемся, абстрагируемся. А вот если мы создаем картотеку фотомоделей для модельного агентства — ситуация резко меняется. Для описания фотомодели нам как раз очень важны рост, цвет глаз и цвет волос, а вот номер ИНН для нее нам абсолютно не важен. Поэтому в классе Model нам нужно создать переменные String height, String hair, String eyes. Вот так и работает абстракция, все просто!:)

Конструкторы

Давай вернемся с нашему примеру с кошками.
public class Cat {

    String name;
    int age;

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

        System.out.println("Тут в программе в течение 2-х часов что-то происходит...");

        barsik.age = 3;
        barsik.name = "Барсик";

    }
}
Посмотри на этот код, и попробуй догадаться, что с нашей программой не так. На протяжении двух часов в нашей программе существовал кот без имени и возраста! Конечно, это в корне неверно. В базе данных ветеринарной клиники не должно быть котов без информации о них. Сейчас мы отдаем это на откуп программиста. Не забудет он указать имя и возраст — все будет ок. Забудет — в базе будет ошибка, неизвестные коты. Как нам решить эту проблему? Надо каким-то образом запретить создавать котов без имени и возраста. Здесь нам на помощь приходят функции-конструкторы. Приведем пример:
public class Cat {

    String name;
    int age;

    //конструктор для класса Cat
    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {

        Cat barsik = new Cat("Barsik", 5);
    }
}
Конструктор — это, по сути, шаблон для объектов класса. В данном случае мы указывем, что для каждого объекта 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 barsik = new Cat(); //ошибка!
    }
}
Теперь, когда в классе есть конструктор, компилятор Java знает, как должны выглядеть объекты, и не позволяет создавать объекты без указанных в нем аргументов. Теперь давай разберемся с ключевым словом this, которое ты видишь внутри конструктора. С ним тоже все просто. "this" по-английски — "этот, этого". То есть это слово для указывания на конкретный предмет. Код в конструкторе
public Cat(String name, int age) {
    this.name = name;
    this.age = age;
}
можно перевести почти дословно: "name для этого кота (которого мы создаем) = аргументу name, который указан в конструкторе. age для этого кота (которого мы создаем) = аргументу age, который указан в конструкторе." После срабатывания конструктора можешь проверить, что нашему коту присвоились все нужные значения:
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 barsik = new Cat("Барсик", 5);
        System.out.println(barsik.name);
        System.out.println(barsik.age);
    }
}
Вывод в консоль: Барсик 5 Когда конструктор отработал:
Cat barsik = new Cat("Барсик", 5);
Внутри него по факту произошло следующее:
this.name = "Барсик";
this.age = 5;
И объекту barsik (он и является this) присвоились значения из аргументов конструктора. На самом деле, если не указать в классе никаких конструкторов — у него все равно будет срабатывать конструктор! Но как такое возможно? О_О Дело в том, что в Java у всех классов есть так называемый конструктор по умолчанию. У него нет никаких аргументов, но он срабатывает каждый раз при создании любого объекта любого класса.
public class Cat {

    public static void main(String[] args) {

        Cat barsik = new Cat(); //вот здесь сработал конструктор по умолчанию
    }
}
На первый взгляд это не заметно. Ну создали объект и создали, где тут работа конструктора? Чтобы это увидеть, давай прямо руками напишем для класса Cat пустой конструктор, а внутри него выведем какую-нибудь фразу в консоль. Если она выведется — значит конструктор отработал.
public class Cat {

    public Cat() {
        System.out.println("Создали кота!");
    }

    public static void main(String[] args) {

        Cat barsik = new 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 barsik = new Cat(); //ошибка!
    }
}
Мы не смогли создать кота без имени и возраста, потому что определили конструктор для Cat: строка + число. Дефолтный конструктор сразу после этого исчез из класса. Поэтому обязательно запомни: если тебе в твоем классе нужно несколько конструкторов, включая пустой, его нужно создать отдельно. Например, наша ветеринарная клиника хочет делать добрые дела и помогать бездомным котикам, про которых мы не знаем ни имени, ни возраста. Тогда наш код должен выглядеть так:
public class Cat {

    String name;
    int age;

    //для домашних котов
    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //для уличных котов
    public Cat() {
    }

    public static void main(String[] args) {

        Cat barsik = new Cat("Barsik", 5);
        Cat streetCat = new Cat();
    }
}
Теперь, когда мы явно указали конструктор по умолчанию — мы можем создавать котов обоих типов. В конструкторе можно присваивать значения и явно, а не только брать их из аргументов. Например, мы можем записывать всех уличных котов в базу данных под именем "Уличный кот номер..." :
public class Cat {

    String name;
    int age;

    static int count = 0;

    public Cat() {
        count++;
        this.name = "Уличный кот номер " + 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);
    }
}
У нас есть переменная count, которая является счетчиком уличных котов. Каждый раз при выполнении конструктора по умолчанию мы увеличиваем ее на 1 и присваем этот номер в качестве имени кота. Для конструктора очень важен порядок следования аргументов. Поменяем в нашем конструкторе аргументы имени и возраста местами.
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 barsik = new Cat("Барсик", 10); //ошибка!
    }
}
Ошибка! Конструктор четко описывает: при создании объекта Cat ему должны быть переданы число и строка, именно в таком порядке. Поэтому наш код не срабатывает. Обязательно запомни это и учитывай про создании своих собственных классов:
public Cat(String name, int age) {
    this.name = name;
    this.age = age;
}

public Cat(int age, String name) {
    this.age = age;
    this.name = name;
}
Это два абсолютно разных конструктора! Теперь реши несколько задач на закрепление материала:)
  • Музей древностей.
Твоя задача — спроектировать класс Artifact. Артефакты, которые хранятся в музее, бывают трех видов. Первый — про которые неизвестно ничего, кроме порядкового номера, присвоенному музеем (например: 212121). Второй — про которые известен порядковый номер и культура, которой он был создан (например: 212121, "Ацтеки"). Третий вид — про которые известен порядковый номер, культура, которой он был создан, и точный век его создания (например: 212121, "Ацтеки", 12). Создай класс Artifact, описывающий хранящиеся в музее древности, и напиши необходимое количество конструкторов для него. В методе main() создай по одному артефакту каждого вида.
public class Artifact {

    //напишите ваш код здесь

    public static void main(String[] args) {
        //напишите ваш код здесь
    }
}
  • Сайт знакомств
Вы создаете базу данных пользователей для сайта знакомств. Да вот беда — вы подзабыли в каком порядке их нужно указывать, а технического задания под рукой нет. Спроектируйте класс User, у которого будут поля — имя (String) возраст (short) и рост (int). Создайте для него неоходимое количество конструкторов, чтобы имя, возраст и рост можно было указывать в любом порядке.
public class User {

    String name;
    short age;
    int height;

    //напишите ваш код здесь

    public static void main(String[] args) {

    }
}