В чем разница между общедоступным, защищенным, частным и частным в Java? - PullRequest
2846 голосов
/ 18 октября 2008

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

Ответы [ 24 ]

5186 голосов
/ 18 октября 2008

Официальное руководство может вам пригодиться.

            │ Class │ Package │ Subclass │ Subclass │ World
            │       │         │(same pkg)│(diff pkg)│ 
────────────┼───────┼─────────┼──────────┼──────────┼────────
public      │   +   │    +    │    +     │     +    │   +     
────────────┼───────┼─────────┼──────────┼──────────┼────────
protected   │   +   │    +    │    +     │     +    │         
────────────┼───────┼─────────┼──────────┼──────────┼────────
no modifier │   +   │    +    │    +     │          │    
────────────┼───────┼─────────┼──────────┼──────────┼────────
private     │   +   │         │          │          │    

 + : accessible         blank : not accessible
428 голосов
/ 19 октября 2008

(Предостережение: я не программист на Java, я программист на Perl. У Perl нет формальной защиты, поэтому, возможно, поэтому я так хорошо понимаю проблему :))

Частный

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

Пакет Приват

Может быть просмотрено и использовано только пакетом , в котором он был объявлен. Это значение по умолчанию в Java (что некоторые считают ошибкой).

Protected

Пакет Private + может просматриваться подклассами или членами пакета.

Открытый

Каждый может это увидеть.

Издатель

Видимо за пределами кода, которым я управляю. (Хотя это не синтаксис Java, это важно для этого обсуждения).

C ++ определяет дополнительный уровень, называемый «друг», и чем меньше вы знаете об этом, тем лучше.

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

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

Если вы хотите, чтобы пользователи могли настраивать поведение, а не делать внутренние объекты общедоступными, чтобы они могли их переопределить, часто лучше поместить эти кишки в объект и сделать этот интерфейс общедоступным. Таким образом, они могут просто подключить новый объект. Например, если вы писали проигрыватель компакт-дисков и хотели, чтобы бит "go find info об этом компакт-диске" был настраиваемым, вместо того, чтобы делать эти методы общедоступными, вы поместили бы всю эту функциональность в свой собственный объект и сделали бы доступным только объект-получатель / установщик объекта , Таким образом, скупость на разоблачение своих внутренностей способствует хорошей композиции и разделению проблем

Лично я придерживаюсь только "частного" и "публичного". У многих ОО-языков это есть. «Защищенный» может быть удобен, но это действительно обман. Как только интерфейс становится более приватным, он становится вне вашего контроля, и вам нужно искать код других людей, чтобы найти применение.

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

Многие языки считают иерархическую природу публичного / защищенного / частного слишком ограничивающей и не соответствующей действительности. Для этого есть концепция класса черты , но это еще одно шоу.

359 голосов
/ 10 ноября 2015

Вот лучшая версия таблицы. (Доказательство будущего с колонкой для модулей.)

Java Access Modifiers

Пояснения

  • A закрытый член (i) является только доступным в том же классе, что и объявленный.

  • Член с без модификатора доступа (j) доступен только внутри классов в одном пакете.

  • Член защищенный (k) доступен во всех классах в одном пакете и в подклассах в других пакетах.

  • Член public (l) доступен для всех классов (если только он не находится в модуле , который не экспортирует пакет, в котором он объявлен).


Какой модификатор выбрать?

Модификаторы доступа - это инструмент, помогающий предотвратить случайное нарушение инкапсуляции (*) . Спросите себя, хотите ли вы, чтобы член был чем-то внутренним для класса, пакета, иерархии классов или не внутренним, и выберите соответствующий уровень доступа.

Примеры:

  • Поле long internalCounter, вероятно, должно быть закрытым, так как оно изменчиво и подробно о реализации.
  • Класс, который должен создаваться только в фабричном классе (в том же пакете), должен иметь конструктор с ограниченным пакетом, так как не должно быть возможности вызывать его напрямую из пакета.
  • Внутренний void beforeRender() метод, вызываемый непосредственно перед рендерингом и используемый как ловушка в подклассах, должен быть защищен.
  • Метод void saveGame(File dst), который вызывается из кода GUI, должен быть открытым.

(*) Что такое инкапсуляция?

181 голосов
/ 10 января 2013
                | highest precedence <---------> lowest precedence
*———————————————+———————————————+———————————+———————————————+———————
 \ xCanBeSeenBy | this          | any class | this subclass | any
  \__________   | class         | in same   | in another    | class
             \  | nonsubbed     | package   | package       |    
Modifier of x \ |               |           |               |       
————————————————*———————————————+———————————+———————————————+———————
public          |       ✔       |     ✔     |       ✔       |   ✔   
————————————————+———————————————+———————————+———————————————+———————
protected       |       ✔       |     ✔     |       ✔       |   ✘   
————————————————+———————————————+———————————+———————————————+———————
package-private |               |           |               |
(no modifier)   |       ✔       |     ✔     |       ✘       |   ✘   
————————————————+———————————————+———————————+———————————————+———————
private         |       ✔       |     ✘     |       ✘       |   ✘    
148 голосов
/ 19 октября 2008

Простое правило. Начните с объявления всего частного. И затем прогресс по отношению к публике, когда потребности возникают, и дизайн оправдывает это.

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

Как правило, я стараюсь избегать переопределения реализаций методов путем создания подклассов; слишком легко испортить логику. Объявите абстрактно защищенные методы, если вы собираетесь переопределить его.

Кроме того, используйте аннотацию @Override при переопределении, чтобы предотвратить поломку при рефакторинге.

103 голосов
/ 13 сентября 2012

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

Доступ по умолчанию (определяется отсутствием ключевого слова) также называется package-private . Исключение: в интерфейсе нет модификатора, означающего публичный доступ; Модификаторы, кроме public, запрещены. Константы перечисления всегда общедоступны.

Резюме

Разрешен ли доступ к участнику с этим спецификатором доступа?

  • Member is private: Только если член определен в том же классе, что и вызывающий код.
  • Участник является частным пакетом: только если вызывающий код находится в пакете, который непосредственно входит в состав участника.
  • Member is protected: тот же пакет или если член определен в суперклассе класса, содержащего вызывающий код.
  • Участник public: Да.

Какие спецификаторы доступа применяются к

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

Для классов в верхней области разрешены только public и package-private. Этот выбор дизайна, предположительно, связан с тем, что protected и private будут избыточными на уровне пакета (наследование пакетов отсутствует).

Все члены доступа доступны для членов класса (конструкторы, методы и статические функции-члены, вложенные классы).

Похожие: Доступность Java Class

Заказать

Спецификаторы доступа могут быть строго заказаны

общедоступный> защищенный> частный пакет> личный

означает, что public обеспечивает максимальный доступ, private - наименьший. Любая возможная ссылка на приватного участника также действительна для пакетного частного участника; любая ссылка на закрытый элемент пакета действительна для защищенного члена и т. д. (Предоставление доступа к защищенным членам другим классам в том же пакете считалось ошибкой.)

Примечания

  • Методы класса разрешают доступ к закрытым членам других объектов того же класса. Точнее, метод класса C может обращаться к закрытым членам C на объектах любого подкласс C. Java не поддерживает ограничение доступа по экземпляру, только по классу. (Сравните со Scala, который поддерживает его, используя private[this].)
  • Вам нужен доступ к конструктору для создания объекта. Таким образом, если все конструкторы являются закрытыми, класс может быть создан только с помощью кода, живущего внутри класса (обычно это статические фабричные методы или инициализаторы статических переменных). Аналогично для частных или защищенных конструкторов.
    • Наличие только частных конструкторов также означает, что класс не может быть разделен на подклассы извне, поскольку Java требует, чтобы конструкторы подкласса неявно или явно вызывали конструктор суперкласса. (Однако он может содержать вложенный класс, который подклассирует его.)

Внутренние классы

Вы также должны учитывать вложенные области видимости, такие как внутренние классы. Примером сложности является то, что у внутренних классов есть члены, которые сами могут принимать модификаторы доступа. Таким образом, вы можете иметь закрытый внутренний класс с открытым членом; можно ли получить доступ к члену? (См. Ниже.) Общее правило состоит в том, чтобы смотреть на область действия и рекурсивно думать, можете ли вы получить доступ к каждому уровню.

Однако это довольно сложно, и для полной информации, обратитесь к Спецификации языка Java . (Да, в прошлом были ошибки компилятора.)

Чтобы почувствовать, как они взаимодействуют, рассмотрим этот пример. Возможно «утечка» частных внутренних классов; обычно это предупреждение:

class Test {
    public static void main(final String ... args) {
        System.out.println(Example.leakPrivateClass()); // OK
        Example.leakPrivateClass().secretMethod(); // error
    }
}

class Example {
    private static class NestedClass {
        public void secretMethod() {
            System.out.println("Hello");
        }
    }
    public static NestedClass leakPrivateClass() {
        return new NestedClass();
    }
}

Выход компилятора:

Test.java:4: secretMethod() in Example.NestedClass is defined in an inaccessible class or interface
        Example.leakPrivateClass().secretMethod(); // error
                                  ^
1 error

Некоторые связанные вопросы:

79 голосов
/ 18 декабря 2012

Как правило:

  • private: область действия класса.
  • default (или package-private): объем пакета.
  • protected: package scope + child (как пакет, но мы можем подкласс его из разных пакетов). Защищенный модификатор всегда сохраняет отношение «родитель-потомок».
  • public: везде.

В результате, если мы разделим право доступа на три права:

  • (D) irect (вызывается из метода внутри того же класса).
  • (R) eference (вызывать метод, используя ссылку на класс или через синтаксис "точка").
  • (I) наследование (через подклассы).

тогда у нас есть простая таблица:

+—-———————————————+————————————+———————————+
|                 |    Same    | Different |
|                 |   Package  | Packages  |
+—————————————————+————————————+———————————+
| private         |   D        |           |
+—————————————————+————————————+———————————+
| package-private |            |           |
| (no modifier)   |   D R I    |           |
+—————————————————+————————————+———————————+
| protected       |   D R I    |       I   |
+—————————————————+————————————+———————————+
| public          |   D R I    |    R  I   |
+—————————————————+————————————+———————————+
47 голосов
/ 27 октября 2012

очень коротко

  • public: доступно везде.
  • protected: доступно для классов того же пакета и подклассов, находящихся в любом пакете.
  • по умолчанию (модификатор не указан): доступен для классов того же пакета.
  • private: доступно только для одного класса.
39 голосов
/ 16 ноября 2013

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

  • Предположим, что у нас есть 2 класса; Father и Son, каждый в своей упаковке:

    package fatherpackage;
    
    public class Father
    {
    
    }
    
    -------------------------------------------
    
    package sonpackage;
    
    public class Son extends Father
    {
    
    }
    
  • Давайте добавим защищенный метод foo() к Father.

    package fatherpackage;
    
    public class Father
    {
        protected void foo(){}
    }
    
  • Метод foo() можно вызывать в 4 контекстах:

    1. Внутри класса, который находится в том же пакете, где определено foo() (fatherpackage):

      package fatherpackage;
      
      public class SomeClass
      {
          public void someMethod(Father f, Son s)
          {
              f.foo();
              s.foo();
          }
      }
      
    2. Внутри подкласса, на текущем экземпляре через this или super:

      package sonpackage;
      
      public class Son extends Father
      {
          public void sonMethod()
          {
              this.foo();
              super.foo();
          }
      }
      
    3. Для ссылки, тип которой совпадает с классом:

      package fatherpackage;
      
      public class Father
      {
          public void fatherMethod(Father f)
          {
              f.foo(); // valid even if foo() is private
          }
      }
      
      -------------------------------------------
      
      package sonpackage;
      
      public class Son extends Father
      {
          public void sonMethod(Son s)
          {
              s.foo();
          }
      }
      
    4. Для ссылки, тип которой является родительским классом, и это внутри пакета, в котором определено foo() (fatherpackage) [Это может быть включено в контекст №. 1]:

      package fatherpackage;
      
      public class Son extends Father
      {
          public void sonMethod(Father f)
          {
              f.foo();
          }
      }
      
  • Следующие ситуации недопустимы.

    1. Для ссылки, тип которой является родительским классом, и это вне пакета, в котором определено foo() (fatherpackage):

      package sonpackage;
      
      public class Son extends Father
      {
          public void sonMethod(Father f)
          {
              f.foo(); // compilation error
          }
      }
      
    2. Не подкласс внутри пакета подкласса (Подкласс наследует защищенные члены от своего родителя и делает их закрытыми для не подклассов):

      package sonpackage;
      
      public class SomeClass
      {
          public void someMethod(Son s) throws Exception
          {
              s.foo(); // compilation error
          }
      }
      
27 голосов
/ 22 января 2014

Частный

  • Методы, переменные и конструкторы

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

  • Класс и интерфейс

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

Примечание

Переменные, которые объявлены закрытыми, могут быть доступны вне класса, если в классе присутствуют общедоступные методы получения. Переменные, методы и конструкторы, которые объявлены защищенными в суперклассе, могут быть доступны только подклассам в другом пакете или любом классе в пакете класса защищенных членов. <ч />

Защищенный

  • Класс и интерфейс

Модификатор защищенного доступа не может применяться к классу и интерфейсам.

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

Примечание

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

<ч />

Public

Класс, метод, конструктор, интерфейс и т. Д., Объявленные как public, могут быть доступны из любого другого класса.

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

  • Различные пакеты

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

Из-за наследования классов все открытые методы и переменные класса наследуются его подклассами.

<ч />

По умолчанию -No ключевое слово:

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

  • В тех же пакетах

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

Примечание

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

Похожие ответы

Ссылки ссылки

http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html http://www.tutorialspoint.com/java/java_access_modifiers.htm

...