Модификаторы доступа java таблица. Java-модификаторы. Дополнение: модификаторы доступа для внешних классов

Хочу продолжить делиться приобретенными знаниями и своими впечатлениями от подготовки к экзамену. Огромное спасибо всем тем, кто дал рекомендации к нулевой части этой серии! Сегодня я поговорю еще немножко о модификаторах доступа и их взаимоотношениях с наследованием и пакетами, рассмотрю varargs и перечисления, а также массивы и способы их инициализации. Я надеюсь, что хабражители снова откликнутся и дополнят то, о чем я забыл упомянуть или попросту не знал.

Продолжаем готовиться к экзамену под катом.

Методы, поля, локальные переменные и их модификаторы

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

  1. Если метод или поле имеют модификатор public, то они потенциально доступны всей вселенной.
  2. Если метод или поле имеют модификатор доступа private, то они доступны только в рамках класса. Такие члены класса не наследуются, поэтому их невозомжно заместить в подклассах. Помните об этом.
  3. Если метод или поле имеют модификатор доступа по умолчанию, то они доступны только в рамках пакета.
  4. Если метод или поле имеют модификатор доступа protected, то они, прежде всего, доступны самому классу и его наследникам. Кроме того, доступ к этим членам класса могут получить их собратья по пакету.

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

Хочу также обратить внимание на некоторые особенности, которые возникают при использовании доступа по умолчанию и модификатора protected. Рассмотрим следующуий пример. Пусть имеется базовый класс, объявленный в пакете test. Этот класс обладает двумя полями. Первое объявлено с доступом по умолчанию, второе - protected.

Package org.kimrgrey.scjp.test; public class BaseClass { int defaultValue; protected int protectedValue; public BaseClass() { this.defaultValue = 1; this.protectedValue = 1; } }

Если объявить в этом пакете класс SamePackageAccess, который не будет наследоваься от BaseClass, то он все равно получит доступ и к полю defaultValue, и к полю protectedValue. Об этой особенности модификатора protected стоит помнить: члены класса, объявленные как protected, в рамках пакета доступны как через наследование, так и через ссылку. Пример:

Package org.kimrgrey.scjp.test; public class SamePackageAccess { public SamePackageAccess() { BaseClass a = new BaseClass(); a.defaultValue = 2; a.protectedValue = 2; } }

В случае с наследованием в этом пакете доступ по-прежнему сохраняется к обоим полям, причем как по ссылке, так и через наследование.

Package org.kimrgrey.scjp.test; public class SamePackageSubclass extends BaseClass { public SamePackageSubclass() { this.defaultValue = 3; this.protectedValue = 3; BaseClass a = new BaseClass(); a.defaultValue = 3; a.protectedValue = 3; } }

Теперь давайте посмотрим, что будет, если мы выйдем за пределы пакета. Первое, что случится - мы потеряем доступ к полю, объявленному без явного указания модификатора доступа. Его не будут видеть абсолютно все классы вне родного пакета, в том числе и прямые наследники BaseClass. Поле же с модификатором protected будет доступно через наследование всем своим подклассам. Однако даже наследник не сможет его использовать через ссылку. Кроме того, будучи однажды унаследованным классом вне пакета, поле становится закрытым для любых классов, за исключением дальнейших наследников.

Package org.kimrgrey.scjp.main; import org.kimrgrey.scjp.test.BaseClass; public class OtherPackageSubclass extends BaseClass { public OtherPackageSubclass() { this.defaultValue = 10; // Line 8: не получим доступ, потому что другой пакет this.protectedValue = 10; BaseClass a = new BaseClass(); a.protectedValue = 10; // Line 12: по ссылке не могут обращаться даже наследники BaseClass } }

В этом примере содержится также еще одна важная деталь. Предположим, вас спрашивают, что же случиться если скомпилировать приведенный выше код? И дают следующие варианты ответа:

  1. Код будет успешно скомпилирован
  2. Возникнет ошибка компиляции на строке с номером 8
  3. Возникнет ошибка компиляции на строке с номером 12
В этом случае, если явно не указано обратное, выбрать нужно все правильные варианты ответов: 2 и 3, - а не останавливаться на первом подходящем. Внимательно читайте все ответы и проверяйте их на корректность. Именно этот подход работает лучше всего. Прочитав и поняв суть вопроса, проверяйте и анализируйте именно ответы, а не код, приведенный в формулировке.

Среди модификаторов, связанных с наследованием, следует также рассмотреть final. На методы final действует также, как на классы: запрещает их переопределение наследниками. При этом расширять сам класс, в котром находится final метод, по-прежнему можно.

Разрешается применять модификатор final к полям, аргументам методов и локальным переменным. В случае примитивных типов будет запрещено любое изменение значения переменной, кромее ее инициализации. Тут следует помнить, что моментом инициализации локальных переменных считается первое присваивание им значения в рамках метода. До этого переменную использовать нельзя: получите ошибку при компиляции. Помеченное final поле также придется явным образом инициализировать. Это можно сделать либо непосредственно при объявлении, в инициализационном блоке, либо в конструкторе того класса, в котором оно объявлено. Оставлять инициализацию final полей на совести наследников не разрешается. В случае ссылок модификатор final запретит переприсваивать ссылку. Сам объект, на который ссылка указывает, все еще можно изменять: вызывать изменяющие его состояния методы, присваивать полям новое значение и так далее.

Важно помнить, что к локальным переменным неприменимы никакие модификаторы, кроме final. Поэтому, если вы видите в объявлении локальной переменной что-то вроде private int a , то можно смело говорить, что это не скомпилируется. А что же с полями?

  1. К полям, как я уже говорил, применимы все четыре уровня доступа.
  2. Поле может быть помечено как final.
  3. Поле может быть помечено как transient.
  4. Поле может быть помечено как static.
  5. Поле может быть помечено как volatile.
  6. Поле не может быть помечено как abstract.
  7. Поле не может быть помечено как synchronized.
  8. Поле не может быть помечено как strictfp.
  9. Поле не может быть помечено как native.
Некоторые из модификаторов, упомянутых выше, я раньше не описывал. Постараюсь рассмотреть их позже, в соответсвующих темах (transient будет рассмотрен в рамках сериализации, а synchronized и volatile - в многопоточности).

Методы с переменным количеством аргументов

  1. Когда вы указываете параметр vararg, то базовым типом может быть любой тип: примитивный или нет.
  2. Чтобы объявить такой параметр, вы пишите тип, потом три точки, пробел, затем имя массива, который будет использоваться в рамках метода: void f (int... a) . Можно также разделить тип, три точки и идентифкаторы пробелами, так: void f(int ... a) . Внимательно следите за точками. Авторы экзамена любят переносить их за идентификатор. Такой подход не работает.
  3. В метод могут передаваться другие параметры, но в этом случае параметр vararg должен быть последним: void f (double x, int... a)
  4. В методе может быть один и только один vararg параметр.
Для наглядности приведу хороший пример вопроса по этой теме. Выберите такое объявление метода doSomething(), чтобы приведенный ниже код был удачно скомпилирован?

Package org.kimrgrey.scjp.main; public class Application { public static void main(String args) { doSomething(1); doSomething(1, 2); } }

  1. static void doSomething(int... values) {}
  2. static void doSomething(int values) {}
  3. static void doSomething(int x, int... values) {}

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

Package org.kimrgrey.scjp.main; public class Application { private static void f (int... a) { for (int i = 0; i < a.length; ++i) { System.out.println(a[i]); } } public static void main(String args) { f(new int {1, 2 ,3}); } }

Все чудесно соберется и отработает. Формального объяснения этому я не знаю, но предполагаю, что это связано с тем, что vararg-параметр является всего лишь синтаксическим сахаром и воспринимается компилятором как ссылка на массив, поэтому никаких проблем не возникает.

Перечисления

  1. У перечислений могут быть конструкторы.
  2. У перечислений могут быть поля.
  3. У перечислений могут быть методы.
  4. Если перечисление объявляется вне класса, оно может получить только два уровня доступа: public или по умолчанию.
  5. У перечислений есть статический метод values() , который возвращает массив, содержащий все возможные значения перечисления, причем строго в том порядке, в котором они были объявлены.
Для каждого из значений в рамках перечисления вы можете объявить свое собственное «тело» - его специфическое описание. При этом специфичные для значения версии методов перегружают вариант, который используется для всего перечисления в целом. Это позволяет менять поведение членов перечисления в зависимости от нужд приложения. Пример:

Package org.kimrgrey.scjp.main; import static java.lang.System.*; enum Currency { UNKNOWN, USD { public String getStringCode() { return "USD"; } public int getSomethingElse() { return 10; } }, UAH { public String getStringCode() { return "UAH"; } }, RUR { public String getStringCode() { return "RUR"; } }; public String getStringCode() { return ""; } } public class Application { private static void f (int... a) { for (int i = 0; i < a.length; ++i) { out.println(a[i]); } } public static void main(String args) { out.println(Currency.USD.getStringCode()); // out.println(Currency.USD.getSomethingElse()); } }

В результате выполения этого кода в стандартный поток вывода будет помещена строка «USD». Обратите внимание на метод getSomethingElse(). Он объявлен для значения USD, однако не упоминается для всего перечисления. Не смотря на то, что в объявлении стоит public, никто из вне доступ к этому методу получить не сможет. Если строку за номером 44 раскомментировать, то код даже не скомпилируется.

Немного о массивах

В Java допустимы два варианта объявления массивов. Квадратные скобки могут быть размещены после имени типа, так: int a , - или после идентификатора, так: int a . Важно понимать, что оба способа абсолютно равноправны с точки зрения синтаксиса, хотя первый из них и является рекомендуемым. Таким образом, String s - это ни что иное, как двумерный массив строк. Скомпилируется без вопросов.

При объявлении массива нельзя указать его размер, так как память выделяется только в момент создания массива: int a = new int . Поэтому код int a вызовет ошибку компиляции. В случае с массивом ссылок на объекты важно помнить, что при создании массива сами объекты не создаются. К примеру, код Thread threads = new Thread создаст массив из двадцати null"ов, никаких конструкторов вызываться не будет.

При построении многомерных массивов о них нужно думать, как о массивах, каждый элемент которых ссылается снова на массив. Абстрактные конструкции вроде матриц и кубов упрощают программирование, но могут усложнить сдачу экзамена. К примеру, конструкция int a = new int вполне допустима и создаст двумерный массив, элементы которого могут быть проинициализированы позже: a = new int , - причем совсем не обязательно массивами равной длины: a = new int .

Для того, чтобы проинициализировать массив быстро (не элемент за элементом), можно применять синтаксис вроде этого: int x ={1, 2, 3} . В фигурных скобках могут стоять не только константы, но и переменные и даже выражения. Можно также создавать анонимные массивы, что часто используется когда нужно передать строго определенный массив в функцию: f(new int {2, 4, 8}) . Если вы видите на экзамене такую конструкцию, то обязательно присмотритесь внимательнее. Есть вероятность, что будет написано что-то вроде этого: f(new int {2, 4, 8}) . Такой код не будет скомпилирован, так как размер анонимного массива вычисляется исходя из его объявления и не должен указываться явным образом.

На этом я закончу на сегодня. В ближайшее время обязательно поговорим об особенностях некоторых важных операций в Java (присваивание, сравнение, instanceOf, арифметика), а также о неявных классах и потоках.

Мы поговорим о модификаторах: какие бывают модификаторы, области видимости, модификаторы для классов, полей, методов. Думаю, будет не скучно.

Модификаторы в Java – это ключевые слова, которые придают классу, полю класса или методу определенные свойства.

Для обозначения видимости класса его методов и полей есть 4 модификатора доступа:

  • private члены класса доступны только внутри класса;
  • package-private или default (по умолчанию) члены класса видны внутри пакета;
  • protected члены класса доступны внутри пакета и в классах-наследниках;
  • public члены класса доступны всем.

Если Вы помните , то в конце, когда мы уже импортировали класс Cat, у нас все равно была ошибка компиляции.

Все дело в том, что мы не прописали никаких модификаторов доступа к нашим полям и методам и они имеют свойство по умолчанию (члены класса видны внутри пакета). Чтобы исправить ошибку компиляции для нашего кода и наконец то запустить его, нужно сделать наш конструктор и методы public. Тогда их можно будет вызывать с других пакетов.

Вы можете начать задаваться вопросом: а для чего все это нужно? Почему не сделать видимость кода из любого пакета или класса, а нужно разграничить доступ? Эти вопросы сами пропадут, когда придет время писать сложные и громоздкие проекты. Сейчас, когда мы пишем приложения, у которых функционал ограничен одним или двумя классами, то смысла что либо ограничить вроде как не видно.

Представьте, что у Вас есть класс который отображает объект некоего продукта. Например машина. У машины может быть цена. Вы создали поле цена и еще множество других полей, кучу методов которые отвечают за функционал. Все вроде хорошо. Ваш класс машина является частью огромного проекта и все довольны. Но допустим, что кто-то по ошибке или специально создал экземпляр класса автомобиль и поставил отрицательную цену. Разве может товар иметь отрицательную цену? Это очень примитивный пример и вряд ли такое может случиться в реальной жизни, но думаю, идея понятна. Иногда нужно дать доступ не напрямую, а через определенные методы. Может быть, что код отвечает за функционал другого кода, и Вы не хотите, чтобы кто-то изменял и редактировал часть Вашего. Для этого всего и есть ограничение доступа.

Модификатор доступа у конструкторов, методов и полей может быть любой. Класс может быть только либо public, либо default, причем в одном файле может находиться только один public класс.

Пока об модификаторах доступа будет достаточно. В статье «Объектно ориентированное программирование» мы о них поговорим подробнее, а сейчас давайте поговорим о других модификаторах которых, к стати, немало.

Сейчас на очереди модификатор static . Его можно применять перед методом, полем и даже классом, когда хотим объявить вложенный класс. В Java можно писать классы внутри других классов и если модификатор перед классом внутри класса static, то такой класс называют вложенным, если другой модификатор или по умолчанию, то такой класс называется внутренним. О вложенных и внутренних классах будет отдельная статья, поскольку там не все так просто.

static модификатор перед методом или полем говорит о том, что они не принадлежат к экземпляру данного класса. Что это означает для нас? Когда мы описали поле класса или метод как static, его можно вызвать без использования экземпляра класса. То есть вместо такой конструкции: Cat cat = new Cat(); cat.method(), можно написать просто Cat.method(). При условии, что метод объявлен как static. Статические переменные едины для всех объектов класса. У них одна ссылка.

    public class Modificators {

    static int anotherStaticField = 5 ;

    public static void myStaticMethod() {

    someField = "My field" ;

    //nonStaticField = ""; ошибка компиляции

    //нельзя использовать нестатические поля

    //в статических методах

    public void myNonStaticMethod() {

    anotherStaticField = 4 ; //ститические поля можно использовать

    //в нестатических методах

    //main метод тоже имеет модификатор static

    new Modificators() .myNonStaticMethod () ;

    Modificators.myStaticMethod () ; //вызов статических методов и полей

    //через имяКласса.метод

Еще одно важное замечание, которое нужно сказать по поводу static модификаторов: статические поля инициализируются во время загрузки класса. Часто в разного рода тестах по Java можно встретить такой код:

Вопрос: что будет выведено на консоль? Нужно помнить, что static блок будет выведен первым при любом раскладе. Далее будет идти блок по умолчанию. Далее смотрите на скрин консоли:

Следующий модификатор, который мы рассмотрим будет final.

Думаю, слово final говорит само за себя. Применяя final модификатор Вы говорите, что поля не могут быть изменены, методы переопределены, а классы нельзя наследовать (о наследовании будет отдельная статья). Этот модификатор применяется только к классам, методам и переменным (также и к локальным переменным).

С модификатором final к методам и классам мы будем говорить в статье ООП.

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

Модификатор synchronized — говорит о том, что метод может быть использован только одним потоком одновременно. Хотя, возможно, это Вам ни о чем не говорит, полезность этого модификатора будет видно, когда мы будем изучать многопоточность.

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

Модификатор volatile — используется при многопоточности. Когда поле с модификатором volatile будет использоваться и изменяться несколькими потоками, данный модификатор гарантирует, что поле будет изменяться по очереди и путаницы с ним не возникнет.

Модификатор native перед объявлением метода указывает что метод написан на другом языке программирования. Обычно на языке C.

Модификатор strictfp — Обеспечивает выполнение операций над числами типа float и double (с плавающей запятой) по стандарту IEEE 754. Или говоря проще, гарантирует что в пределах метода результаты вычислений будут одинаковыми на всех платформах.

Я еще не говорил о модификаторе abstract . О нем скажу вкратце, так как без знаний основ объектно ориентированного программирования говорить о нем не вижу смысла.

Класс, который имеет модификатор abstract не может создать экземпляр. Единственная цель для него быть расширенным. Класс abstract может содержать как абстрактные методы, а также и обычные.

Подробнее о модификаторе abstract будем говорить в статье ООП.

На этом можно и закончить статью о модификаторах. Многое о них не было сказано. Но это из-за того, что у нас еще нет понятий ООП. Через несколько статей, мы дополним знания о модификаторах и заполним пробелы.

Модификаторы доступа public , private , protected

Как было рассказано ранее, наличие модификатора public перед методом или полем означает, что его можно использовать где угодно, а модификатора private - что его можно использовать только внутри данного класса. Например:

Class A { private int myValue1; public int myValue2; ... void f() { ... myValue1 = 1; // внутри класса можно использовать любое его поле myValue2 = 2; ... A a; a.myValue1; // так тоже можно... } } ... A a; a.myValue1 = 1; // ошибка компиляции - private поле a.myValue2 = 2; // public поле можно использовать везде

Рассказ о protected. Часть 1.

Если член (поле или метод) класса объявлен с модификатором protected , то он доступен не только внутри самого класса, но и внутри всех классов-наследников:

Class Base { protected int myValue; ... } class Derived extends Base { ... myValue = 1; Derived d = ...; d.myValue = 2; // это поле тоже можно использовать Base b = ...; b.myValue = 3; // ошибка компиляции ... }

protected - "прозрачное" для наследников private поле (метод). Поэтому в данном примере произойдёт ошибка, причём в независимости от того, каким объектом на самом деле является b (компилятор этого не может знать). Большинство ошибок прав доступа выявляются именно на стадии компиляции.

Этот рассказ был бы правильным, если бы в Java существовали только три модификатора прав доступа - public , private и protected - но это не совсем так.

Пакеты (Packages)

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

Когда мы до этого писали файл Main.java, мы компилировали его в той же папке, запуская компилятор строчкой javac Main.java . Но если наш файл лежит в каком-то другом каталоге (например dir1/dir2), нам нужно не просто прописать путь в командной строке, но указать это и в самом файле следующим образом:

Package dir1.dir2; // директива package может идти только первой строкой в файле public class Main { ... }

Для того, чтобы теперь скомпилировать наш Main.java мы вводим javac dir1/dir2/Main.java , при этом создается файл Main.class, лежащий в dir1/dir2. А для того, чтобы запустить наш класс, нужно ввести строку java dir1.dir2.Main (все разделители пути превращаются в точки).

Таким образом, имя пакета, указанного в файле, совпадает с относительным путём каталога, в котором он находится. Пакет содержит только те файлы, которые лежат в самом каталоге. Файлы из подкаталогов не являются файлами корневого пакета.

dir1.dir2.Main называется полным именем класса (fully qualified name). При необходимости в программе можно прописывать полные имена классов:

Org.amse.np.list.List l = new org.amse.np.list.List;

Но зачастую удобнее импортировать нужный нам класс или группу классов, чтобы использовать их короткие имена:

Package org.amse.np.hw10; import org.amse.np.list.List; // импортируем класс List import org.amse.np.dllist.*; // импортируем все классы из пакета dllist (классы подкаталогов не импортируются!) class Main { ... List l = new List(); }

В любой класс всегда импортируются все классы пакета, в котором находится данный класс, и часть стандартной библиотеки Java (как если бы мы каждый раз писали в начале нашего файла строку import java.lang.*;), что позволяет использовать такие стандартные классы и методы как String , System.out.println() и т.п.

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

  • рекомендуется всегда использовать package
  • все имена package должны начинаться со строчной буквы
  • коммерческие организации используют пакеты, начинающиеся с com (например com.borland), остальные - с org (наример org.amse)

Однако package - это не только способ разделения на каталоги, но и важная сущность языка.

Права доступа по умолчанию

Отсутствие явно указанного модификатора прав доступа перед членом класса означает, что этот член будет доступен во всех классах из данного пакета и не доступен вне пакета. Этот уровень доступа также называют package local.

Таким образом, в Java есть всего четыре уровня доступа: public, private, protected и default (package local)

Рассказ о protected. Часть 2

В Java правилом является то, что у метода класса-наследника при перекрытии должны быть права не более ограничительные, чем у метода класса-родителя:

Class Base { public void f() { ... } } class Derived extends Base { private void f() { // ошибка прав доступа во время компиляции... } }

По ограничительности уровни доступа можно распределить так:

Private < protected, default < public

Если верить первой части рассказа о protected, то нельзя сказать, какой уровень доступа - protected или default - более ограничительный. Можно придумать случаи, когда будет доступно поле default и не доступно поле protected и наоборот.

Чтобы не возникало неоднозначностей с правами доступа, например при наследовании, в Java protected-уровень включает ещё и default. То есть protected член доступен всем подклассам, плюс классам внутри пакета.

Иерархия уровней доступа выглядит следующим образом:

Private < default < protected < public

Таким образом, при использовании модификатора protected мы позволяем всем классам из данного пакета иметь доступ к нашему полю/методу, что в большинстве случаев является не той ситуацией, к которой мы стремимся.

Вложенные классы

Мы рассмотрим вложенные классы лишь вскользь, как ещё один способ ограничения использования классов.

Существует два основных типа вложенных классов: static классы (nested)(?) и просто классы-члены (inner). Общий синтаксис:

Class Outer { class Inner { ... } ... }

Достаточно часто применяются вложенные private static классы:

Class List { private static class ListElement { ... } }

При компиляции класса с вложенным классом создаются два файла, в данном случае: List.class и List$ListElement.class. При этом полное имя вложенного класса будет выглядеть следующим образом: org.amse.np.list.List.ListElement

Class Outer { static class Inner { int myValue; ... } ... Inner i = new Inner(); // экземпляр вложенного класса можно создать внутри самого класса... } ... Outer.Inner i = new Outer.Inner(); // ...или снаружи

Статические вложенные классы ведут себя так же, как обычные классы, но они имеют доступ к другим статическим членам данного класса.

Нестатический вложенный класс привязан не к самому окружающему классу, а к его экземпляру, поэтому помимо всех своих полей он ещё имеет неявное поле this , указывающее на конкретный экземпляр, частью которого является вложенный класс. Такой вложенный класс имеет доступ ко всем полям и методам окружающего его класса.

Class Outer { class Inner { ... } ... Inner i = new Inner(); // создается неявное поле, куда записывается Outer.this } Outer o = ...; Inner i = o.new Inner();

Дополнение: модификаторы доступа для внешних классов

По мимо обычных внешних public классов в Java можно также создавать так называемые local классы. Они могут лежать в одном файле с public классом и являются аналогами вложенных классов.

Public class A { ... } class B { ... }

В одном файле может быть только один public класс и сколько угодно local. Файл должен называться так же, как и public класс.

Последнее обновление: 20.04.2018

Все члены класса в языке Java - поля и методы, свойства - имеют модификаторы доступа. В прошлых темах мы уже сталкивались с модификатором public . Модификаторы доступа позволяют задать допустимую область видимости для членов класса, то есть контекст, в котором можно употреблять данную переменную или метод.

В Java используются следующие модификаторы доступа:

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

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

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

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

Рассмотрим модификаторы доступа на примере следующей программы:

Public class Program{ public static void main(String args) { Person kate = new Person("Kate", 32, "Baker Street", "+12334567"); kate.displayName(); // норм, метод public kate.displayAge(); // норм, метод имеет модификатор по умолчанию kate.displayPhone(); // норм, метод protected //kate.displayAddress(); // ! Ошибка, метод private System.out.println(kate.name); // норм, модификатор по умолчанию System.out.println(kate.address); // норм, модификатор public System.out.println(kate.age); // норм, модификатор protected //System.out.println(kate.phone); // ! Ошибка, модификатор private } } class Person{ String name; protected int age; public String address; private String phone; public Person(String name, int age, String address, String phone){ this.name = name; this.age = age; this.address = address; this.phone = phone; } public void displayName(){ System.out.printf("Name: %s \n", name); } void displayAge(){ System.out.printf("Age: %d \n", age); } private void displayAddress(){ System.out.printf("Address: %s \n", address); } protected void displayPhone(){ System.out.printf("Phone: %s \n", phone); }}

В данном случае оба класса расположены в одном пакете - пакете по умолчанию, поэтому в классе Program мы можем использовать все методы и переменные класса Person, которые имеют модификатор по умлчанию, public и protected. А поля и методы с модификатором private в классе Program не будут доступны.

Если бы класс Program располагался бы в другом пакете, то ему были бы доступны только поля и методы с модификатором public.

Модификатор доступа должен предшествовать остальной части определения переменной или метода.

Инкапсуляция

Казалось бы, почему бы не объявить все переменные и методы с модификатором public , чтобы они были доступны в любой точке программы вне зависимости от пакета или класса? Возьмем, например, поле age, которое представляет возраст. Если другой класс имеет прямой доступ к этому полю, то есть вероятность, что в процессе работы программы ему будет передано некорректное значение, например, отрицательное число. Подобное изменение данных не является желательным. Либо же мы хотим, чтобы некоторые данные были достуны напрямую, чтобы их можно было вывести на консоль или просто узнать их значение. В этой связи рекомендуется как можно больше ограничивать доступ к данным, чтобы защитить их от нежелательного доступа извне (как для получения значения, так и для его изменения). Использование различных модификаторов гарантирует, что данные не будут искажены или изменены не надлежащим образом. Подобное сокрытие данных внутри некоторой области видимости называется инкапсуляцией .

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

Public class Program{ public static void main(String args) { Person kate = new Person("Kate", 30); System.out.println(kate.getAge()); // 30 kate.setAge(33); System.out.println(kate.getAge()); // 33 kate.setAge(123450); System.out.println(kate.getAge()); // 33 } } class Person{ private String name; private int age; public Person(String name, int age){ this.name = name; this.age = age; } public String getName(){ return this.name; } public void setName(String name){ this.name = name; } public int getAge(){ return this.age; } public void setAge(int age){ if(age > 0 && age < 110) this.age = age; } }

И затем вместо непосредственной работы с полями name и age в классе Person мы будем работать с методами, которые устанавливает и возвращают значения этих полей. Методы setName, setAge и наподобие еще называют мьютейтерами (mutator), так как они изменяют значения поля. А методы getName, getAge и наподобие называют аксессерами (accessor), так как с их помощью мы получаем значение поля.

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

Access level modifiers determine whether other classes can use a particular field or invoke a particular method. There are two levels of access control:

  • At the top level— public , or package-private (no explicit modifier).
  • At the member level— public , private , protected , or package-private (no explicit modifier).

A class may be declared with the modifier public , in which case that class is visible to all classes everywhere. If a class has no modifier (the default, also known as package-private ), it is visible only within its own package (packages are named groups of related classes — you will learn about them in a later lesson.)

At the member level, you can also use the public modifier or no modifier (package-private ) just as with top-level classes, and with the same meaning. For members, there are two additional access modifiers: private and protected . The private modifier specifies that the member can only be accessed in its own class. The protected modifier specifies that the member can only be accessed within its own package (as with package-private ) and, in addition, by a subclass of its class in another package.

The following table shows the access to members permitted by each modifier.

Access Levels
Modifier Class Package Subclass World
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

The first data column indicates whether the class itself has access to the member defined by the access level. As you can see, a class always has access to its own members. The second column indicates whether classes in the same package as the class (regardless of their parentage) have access to the member. The third column indicates whether subclasses of the class declared outside this package have access to the member. The fourth column indicates whether all classes have access to the member.

Access levels affect you in two ways. First, when you use classes that come from another source, such as the classes in the Java platform, access levels determine which members of those classes your own classes can use. Second, when you write a class, you need to decide what access level every member variable and every method in your class should have.

Let"s look at a collection of classes and see how access levels affect visibility. The following figure shows the four classes in this example and how they are related.

The following table shows where the members of the Alpha class are visible for each of the access modifiers that can be applied to them.

Visibility
Modifier Alpha Beta Alphasub Gamma
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

Tips on Choosing an Access Level:

If other programmers use your class, you want to ensure that errors from misuse cannot happen. Access levels can help you do this.

  • Use the most restrictive access level that makes sense for a particular member. Use private unless you have a good reason not to.
  • Avoid public fields except for constants. (Many of the examples in the tutorial use public fields. This may help to illustrate some points concisely, but is not recommended for production code.) Public fields tend to link you to a particular implementation and limit your flexibility in changing your code.