Урок 5. Модификаторы доступа Java


В этой статье из моего бесплатного курса по Java я буду обсуждать модификаторы видимости (доступа). В Java существует четыре модификатора доступа, о трех из которых я расскажу в этой статье: public, private и default. Четвертый модификатор protected, который связан с более продвинутой темой (наследование), поэтому я пока пропущу его. Есть также много модификаторов без доступа. В этой статье я остановлюсь только на статическом модификаторе.

Модификатор public

Модификатор public означает, что метод, переменная или класс доступны из любого другого класса. Например, класс Person, который мы использовали в предыдущих примерах, может обращаться к классу Name и использовать его, поскольку класс Name является открытым. Обратите внимание, что я поместил класс Name в отдельный пакет только для демонстрационных целей.

package ru.upread.javacourse;

import ru.upread.javacourse.attributes.Name;
public class Person {
    private Name personName;
}
Модификатор private

Модификатор private означает, что метод или переменная доступны только из класса, в котором они были объявлены. Сделайте все переменные и методы приватными, пока вам не понадобится сделать их публичными. Это очень важно, особенно для переменных. Вы не хотите, чтобы другие классы копались в вашем классе Person и заставляли их менять имя вашего объекта Person всякий раз, когда им это нравится.

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

Подход, ориентированный на данные

Обычно используемая альтернатива для того, чтобы сделать все ваши атрибуты общедоступными, заключается в предоставлении так называемых общедоступных методов «получения и установки», которые позволяют другим объектам получать прямой доступ и изменять ваши личные атрибуты. Этот подход преподается как «объектно-ориентированное программирование» многими книгами и курсами по Java. Для меня это всегда было похоже на запертую дверь с ключом и надписью «Пожалуйста, не открывайте эту дверь».

Мне потребовалось несколько лет, чтобы понять, что я был не единственным, кто был сбит с толку, но на самом деле все эти «умные книги» и учителя были неправы! Не позволяйте им обмануть вас! Ошибочно полагать, что вы можете эффективно инкапсулировать класс, в то же время предоставляя открытые методы, позволяющие напрямую работать с его внутренними объектами. Избегайте такого подхода, ориентированного на данные.

Объектно-ориентированный подход

Вместо этого используйте объектно-ориентированный подход. Сосредоточьтесь на предоставлении функциональности с точки зрения бизнеса, независимо от внутренних деталей класса.

Разрабатывая свой класс, поставьте себя на место того, кто будет использовать ваш класс. Сделайте так, чтобы вызывающий мог использовать методы класса как можно проще. Чтобы достичь этого, сосредоточьтесь на том, что должен делать класс, а не на том, как это будет достигнуто.

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

Чем меньше клиент «знает», тем более гибким остается ваш код - любой метод, который не является общедоступным, можно легко изменить, не затрагивая другой код.

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

Рассматривайте каждый публичный метод как открытую безопасную дверь; как потенциальная угроза вашему классу.

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

Я буду продолжать говорить об этих объектно-ориентированных принципах на протяжении всего курса, так как они очень важны для хорошего дизайна кода.

Модификатор package-private

Третий модификатор, который я собираюсь обсудить, - это модификатор package-private. Он также называется модификатором «по умолчанию», потому что этот модификатор никогда не объявляется. Вместо этого он используется как запасной вариант, когда не объявляются другие модификаторы видимости.

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

package ru.upread.javacourse;

class Name {
	/*
	* Модификатор Package-Private для класса Name
	*/
}
Этот модификатор редко используется разработчиками Java без веской причины. Вообще говоря, используйте модификатор уровня по умолчанию всякий раз, когда вам нужны классы одного и того же пакета для использования метода, но вы не хотите, чтобы классы вне этого пакета использовали этот метод. Например, представьте, что у класса Car есть метод диагностирования, который вы хотите использовать для использования класса Mechanic из того же пакета. Но ориентированная на продажи автомобильная компания, для которой вы пишете код, не хочет, чтобы класс Customer возился с этим методом, потому что это повредит его доходам.

На мой взгляд, в модификаторе Package-Private есть недостаток: поскольку ключевого слова нет, неясно, является ли отсутствующий модификатор ошибкой со стороны программиста или плановым модификатором приватности пакета. Если вы забудете вставить модификатор, вы будете создавать проблемы для вашей программы в будущем, но вы не будете знать. От компилятора не будет никаких предупреждений о том, что существует «отсутствующий модификатор», так как это допустимая практика программирования. Если вы хотели, чтобы ваш класс или участники были общедоступными, при попытке получить к ним доступ за пределами пакета вы не сможете. Еще более опасно, когда вы забыли установить частный модификатор и спустя месяцы ваш метод или переменная используется где-то еще, без вашего ведома.

С другой стороны, если вы намеревались использовать модификатор package-private, вы намеренно не используете модификатор видимости. Другой программист может не понять этого и попытаться «исправить» ваш код, добавив модификатор, который, как он полагает, вы хотите. Вот почему я рекомендую, если вы намеренно используете модификатор package-private (который в некоторых случаях очень полезен), а затем оставьте комментарий, обозначающий ваши намерения.

Пример кода

Теперь, когда вы знаете о модификаторах видимости, давайте применим их к примеру кода. Во-первых, мы собираемся создать новый метод @Test в нашем классе PersonTest. Этот метод будет называться shouldReturnNumberOfPersons и будет содержать три объекта типа Person с именами «person1», «person2» и «person3».

package ru.upread.javacourse;

import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class PersonTest {

    @Test
    public void shouldReturnNumberOfPersons {
         Person person1 = new Person();
         Person person2 = new Person();
         Person myPerson = new Person();
         assertEquals(3, myPerson.numberOfPersons());
    }
}
Далее мы собираемся использовать метод assertEquals(), чтобы проверить, равно ли число созданных объектов Person 3.

Давайте начнем писать код, чтобы этот метод работал в нашем классе Person. В нашем классе Person я создал переменную экземпляра типа int с именем personCounter. Затем в конструкторе по умолчанию мы добавляем 1 к personCounter при каждом вызове этого конструктора. Логически, каждый раз, когда создается Person, этот конструктор будет вызываться, поэтому, если мы добавляем 1 к personCounter каждый раз, когда вызывается конструктор, мы считаем количество созданных нами объектов Person. Мы никогда не инициализировали personCounter, но это все равно должно работать, потому что значение по умолчанию для int равно 0.

public class Person {
	private int personCounter;

	public Person() {
		personCounter = personCounter + 1;
	}
}
Как добавленное примечание, есть фактически три способа добавить 1 к personCounter. Первый способ, который мы сделали выше. Второе:

personCounter + = 1;
который может увеличивать personCounter на любое желаемое значение. Третий вариант самый короткий, но работает, только если вы хотите увеличить на 1:

personCounter ++;
Все эти три параметра принимают значение personCounter, увеличивают его на 1, а затем сохраняют это новое значение в новой версии personCounter. Теперь давайте напишем наш метод numberOfPersons (), чтобы вернуть personCounter:

public static int numberOfPersons() {
    return personCounter;
}
Если мы выполним этот код, наш тест не пройден, потому что наш метод numberOfPersons() вернул 1. Можете ли вы догадаться, почему?

Каждый раз, когда мы создавали новый объект Person, мы сохраняли объект и все его значения в отдельной переменной person. Поэтому каждый раз, когда мы создаем новый объект, все его переменные экземпляра сбрасываются конструктором и сохраняются как часть нового объекта. Таким образом, для каждого из наших объектов Person значение personCounter инициализируется равным 0, а затем увеличивается на 1.

Модификатор static

Это подводит нас к нашему решению, статическому модификатору. Как вы помните из предыдущей статьи, модификатор static связывает метод или переменную с классом в целом, а не с каждым отдельным объектом. Обычно, если вы создаете сотню объектов Person, у каждого будет своя собственная переменная personCounter, но с этим модификатором все сто объектов будут иметь одну общую переменную personCounter. Таким образом, наш personCounter сохранит одно и то же значение независимо от того, сколько объектов Person мы создаем.

Таким образом, наш personCounter сохранит одно и то же значение независимо от того, сколько объектов Person мы создаем.

Во-первых, мы добавим этот модификатор в нашу переменную personCounter, и мы также собираемся добавить его в наш метод numberOfPersons(), так как у нас никогда не должно быть метода экземпляра, возвращающего статическую переменную, и наоборот.

public class Person {
    private Name personName;
    private static int personCounter;

    [...]

    public static int numberOfPersons() {
         return personCounter;
    }
}
Сделав метод и переменную статическими, мы теперь можем точно подсчитать и вернуть количество созданных объектов Person. Наша переменная связана с классом, и поскольку к ней нельзя получить доступ из-за закрытого тега, у нас есть открытый метод numberOfPersons(), который позволяет внешнему коду получать доступ, но не изменять значение personCounter.

Спасибо за прочтение!
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, уроки по java, java




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




Программы на заказ: пишем для диплома
Чтение из файла для макроса Corel Draw
Принцип единой ответственности (SRP)