Курс

"Программирование Java"

Введение в классы

Класс определяется с помощью ключевого слова сlass:

public class User {}

 

В данном случае класс называется User. После названия класса идут фигурные скобки, между которыми помещается тело класса - то есть его поля и методы

Любой объект, созданный на основе класса, может обладать двумя основными характеристиками: состояние - некоторые данные, которые хранит объект, и поведение - действия, которые может совершать объект.

Для хранения состояния объекта в классе применяются поля или переменные класса. Для определения поведения объекта в классе применяются методы. Например, класс User, который представляет человека, мог бы иметь следующее определение:

class User {

     

    String name;        // имя

    int age;            // возраст

    void displayInfo(){

        System.out.printf("Name: %s \tAge: %d\n", name, age);

    }

}

В классе User определены два поля: 

name представляет имя человека;

age - его возраст. 

И также определен метод displayInfo, который ничего не возвращает и просто выводит эти данные на консоль.

 

Теперь используем данный класс. Для этого определим следующую программу:

public class Main{

      

    public static void main(String[] args) {

         

        User user1;

    }

}

class User {

    String name;    // имя

    int age;        // возраст

    void displayInfo(){

        System.out.printf("Name: %s \tAge: %d\n", name, age);

    }

}

Как правило, классы определяются в разных файлах. В данном случае для простоты мы определяем два класса в одном файле. Стоит отметить, что в этом случае только один класс может иметь модификатор public (в данном случае это класс Main), а сам файл кода должен называться по имени этого класса, то есть в данном случае файл должен называться Main.java

Класс представляет новый тип, поэтому мы можем определять переменные, которые представляют данный тип. Так, здесь в методе main определена переменная user1, которая представляет класс User. Но пока эта переменная не указывает ни на какой объект и по умолчанию она имеет значение null. По большому счету мы ее пока не можем использовать, поэтому вначале необходимо создать объект класса User.

Для создания объекта User используется выражение new User (). Оператор new выделяет память для объекта User. И затем вызывается конструктор по умолчанию, который не принимает никаких параметров. В итоге после выполнения данного выражения в памяти будет выделен участок, где будут храниться все данные объекта User. А переменная user1 получит ссылку на созданный объект

 

public class Main{

      

    public static void main(String[] args) {

         

        User user1 = new User (); // создание объекта

        user1.displayInfo();

         

        // изменяем имя и возраст

        user1.name = "Alex";

        user1.age = 25;

        user1.displayInfo();

    }

}

class User {

     

    String name;    // имя

    int age;        // возраст

    void displayInfo(){

        System.out.printf("Name: %s \tAge: %d\n", name, age);

    }

}

 

Пакеты

Классов в программе может быть много и для того чтобы не было путаницы и конфликтов с названиями классов их распределяют по пакетам. Пакеты позволяют организовать логическую структуру классов.

Как правило, названия пакетов соответствуют физической структуре проекта, то есть организации каталогов, в которых находятся файлы с исходным кодом. А путь к файлам внутри проекта соответствует названию пакета этих файлов

Например, если классы принадлежат пакету mypack, то эти классы помещаются в проекте в папку mypack

Классы необязательно определять в пакеты. Если для класса пакет не определен, то считается, что данный класс находится в пакете по умолчанию, который не имеет имени

Чтобы указать, что класс принадлежит определенному пакету, надо использовать директиву package, после которой указывается имя пакета:

package название пакета

 

Если нам надо использовать классы из других пакетов, то нам надо подключить эти пакеты и классы. Исключение составляют классы из пакета java.lang (например, String), которые подключаются в программу автоматически.

Чтобы использовать класс из другого пакета, его нужно импортировать через директиву import. 

import java.util.*; // импорт всех классов из пакета java.util

 

Модификаторы доступа

При работе с классами и объектами очень важно такое понятие, как модификаторы доступа. 

В Java есть 4 модификатор доступа:

public: публичный, общедоступный класс или член класса. Поля и методы, объявленные с модификатором public, видны другим классам из текущего пакета и из внешних пакетов.

private: закрытый класс или член класса, противоположность модификатору public. Закрытый класс или член класса доступен только из кода в том же классе.

protected: такой класс или член класса доступен из любого места в текущем классе или пакете Ключевое слово protected используется для определения видимости членов класса в самом классе и в его наследниках.

Модификатор по умолчанию: отсутствие модификатора у поля или метода класса предполагает применение к нему модификатора по умолчанию. Такие поля или методы видны всем классам в текущем пакете.

 

Вернемся к нашему классу User

class User {

    String name;    // имя

    int age;        // возраст

    void displayInfo(){

        System.out.printf("Name: %s \tAge: %d\n", name, age);

    }

}

Все поля и методы имеют модификатор по умолчанию, значит обратиться к этим полям мы можем только из текущего пакета

Возникает вопрос, зачем нужно скрывать поля и методы класса?

Например, возьмем поле age

Если другой класс имеет прямой доступ к этому полю, то есть вероятность, что в процессе работы программы ему будет передано некорректное значение, например, отрицательное число. Подобное изменение данных не является желательным


 

Использование различных модификаторов гарантирует, что данные не будут искажены или изменены не надлежащим образом

Подобное сокрытие данных внутри некоторой области видимости называется ИНКАПСУЛЯЦИЕЙ. 

Разберем инкапсуляцию подробнее

Для этого вернемся к нашему классу и добавим дополнительные методы, которые будут изменять и выводить данные из полей

Для изменения:

public void setAge (int age){

this.age = age;

}

Для вывода:

public int getAge (){

return this.age;

}

 

Все методы можно разделить на 2 категории:

  1. Возвращающие

  2. Не возвращающие значения

Методы, которые не возвращают значения пишутся с ключевым словом void

Например метод setAge() метод в качестве параметра принимает число и изменяет поле внутри класса.

И метод getAge() который будет возвращать нам возраст пользователя. 

Если метод возвращает значение, то мы обязательно должны указывать ТИП возвращаемого значения и использовать ключевое слово return в теле метода.

В методе используется ключевое слово this, которое показывает, что мы обращаемся к текущему полю объекта с соответствующем именем

Само поле age мы закроем от доступа и это позволит нам избежать некорректного заполнения возраста, указав ему модификатор доступа privet;

class User {

    String name;    // имя

    privet int age;        // возраст

    void displayInfo(){

        System.out.printf("Name: %s \tAge: %d\n", name, age);

    }

 

    public int getAge (){

        return this.age;

    }

 

    public void setAge (int age){

this.age = age;

    }

}

Методы, которые позволяют получить значения полей объекта или изменить эти поля называются геттеры и сеттеры соответственно

Ключевое слово static

В языке Java модификатор static применяется к внутренним классам, методам, полям и блокам инициализации. И тогда мы получаем соответственно статические классы, статические методы, статические поля и статические блоки инициализации

 

Статические поля

Иногда бывает нужно, чтобы поле класса имело одинаковое значение для всех объектов данного класса и при изменении значения поля все методы видели новое значение. В таком случае это поле нужно объявить с ключевым словом static (статический) и оно станет общим для всех экземпляров класса

 

Наиболее часто статические поля используют как константы. Например, библиотечный класс Math имеет статическую константу PI:

 

 class Math {

        ...

        public static final double PI = 3.14159265358979323846;

        ...

}

 

С одной стороны модификатор доступа public позволяет использовать константу везде, а с другой стороны, добавив слово final, мы гарантируем, что это поле невозможно изменить. Применять общедоступные статические поля без final очень опасно, так как любой объект сможет изменить их значения

 

Статические члены класса создаются в памяти сразу при загрузке класса, то есть не нужно создавать экземпляр класса, чтобы их использовать. Обращение происходит через ИмяКласса.поле или ИмяКласса.метод().

 

Блоки статической инициализации

 

Класс может содержать блоки статической инициализации. Они присваивают значения статическим полям или выполняют иную необходимую логику. Статические блоки обычно применяют, когда простой инициализации в объявлении статического поля недостаточно. Например, создание статического массива зачастую должно осуществляться одновременно с его инициализацией. Ниже приведен пример инициализации статического массива со степенями двойки:

 

public class Example{

        static int[] arr = new int[4]; //статический массив степеней 2

 

        static { //статический блок

                arr[0] = 1;

                for (int i = 1; i < arr.length; i++)

                        arr[i] = arr[i - 1] * 2;

        }

 

        public static void main(String[] args) {

                for (int i = 0; i < arr.length; i++)

                       System.out.print(arr[i] + " ");

        }

}

 

В данном примере гарантированно, что массив будет создан до выполнения статического блока

 

 

 

Статические методы

Как уже было сказано статический метод можно вызвать, используя тип класса, в котором эти методы описаны, то есть не нужно каждый раз создавать новый объект для доступа к таким методам. 

Класс java.lang.Math — хороший пример, в котором почти все методы статичны.

 

Когда использовать статические методы:

Когда вы создаете класс-утилиту с часто используемыми простыми методами

Или вы пишете программу, работающую с файлами, и часто приходится показывать размер файла, то можно вынести такой метод в отдельный вспомогательный класс

Когда пишите метод, который не зависит от полей своего класса, то есть работает только с входными параметрами

 

При использовании статических методов есть ограничения. Вы не можете получить доступ к нестатическим членам класса внутри статического метода. Результатом компиляции приведенного ниже кода будет ошибка:

 

public class Example{

        private int count = 0;

 

        public static void main(String args[]){

                System.out.println(count); //compile time error

        }

}