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

Источник: Medium Данное руководство объясняет концепцию модификаторов доступа в Java на примере каждого типа и их согласование с принципами объектно-ориентированного программирования. Кофе-брейк #244. Модификаторы доступа в Java. 11 вопросов на интервью про enum в Java - 1В Java модификаторы доступа (Access Modifiers) играют решающую роль в управлении видимостью и доступностью классов, методов и переменных. Они являются неотъемлемой частью принципов объектно-ориентированного программирования (ООП), обеспечивая инкапсуляцию, сокрытие данных и модульность в приложениях. В Java есть четыре типа модификаторов доступа: private, public, default и protected. Каждый модификатор имеет определенные правила, определяющие область действия и доступность элементов в программе Java.

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

Private

Когда элемент помечен как private, он становится доступным только внутри класса, в котором он объявлен. Никакой другой класс, даже в том же пакете или подклассах, не может напрямую обращаться к этому элементу. Модификатор private часто используется, чтобы скрыть детали реализации и поддерживать инкапсуляцию данных, предотвращая прямой доступ и изменение извне класса. Используя методы getter и setter, мы можем обеспечить контролируемый доступ к private-переменным.

public class BankAccount {
   private double balance;
   
   public BankAccount(double initialBalance) {
     this.balance = initialBalance;
   }
   
   // Метод getter для доступа к личному балансу
   public double getBalance() {
     return balance;
   }
   
   // Метод для вывода средств
   public void withdraw(double amount) {
     if (amount > 0 && amount <= balance) {
       balance -= amount;
     }
   }
}
В приведенном выше примере переменная balance помечена как private, что гарантирует, что доступ к ней и ее изменение можно получить только с помощью методов getBalance() и remove(), что обеспечивает надлежащую инкапсуляцию.

Public

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

public class MathUtils {
   public static final double PI = 3.14159;
   
   public static int add(int a, int b) {
     return a + b;
   }
}
В этом примере константа PI и метод add() помечены как public, что позволяет другим классам обращаться к ним напрямую без каких-либо ограничений.

Default

Если модификатор доступа не указан явно, то считается, что элемент имеет доступ по умолчанию (default). Следовательно, это также можно назвать модификатором default, что означает, что он доступен внутри того же пакета, но не из классов вне пакета.

package com.example.calculator;
  class BasicCalculator {
     int add(int a, int b) {
       return a + b;
     }
  }
В этом сценарии методу add() предоставляется доступ по умолчанию (default), поскольку модификатор доступа не указан. Это означает, что к нему могут обращаться другие классы в том же пакете, но не извне пакета.

Protected

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

package com.example.animals;

public class Animal {
    protected String name;
    
    protected void makeSound() {
        System.out.println("Animal makes a sound");
    }
}
В данном примере переменная name и метод makeSound() помечены как protected, что делает их доступными в пределах одного пакета и подклассов класса Animal, даже если подклассы находятся в другом пакете.

В чем разница между default и protected

Вы можете заметить небольшую разницу между модификатором доступа protected и модификатором доступа default. Давайте уточним это, снова расширив приведенные ниже примеры. Сначала определим класс с именем Bird. Класс Bird расширяет класс Animal и остается в другом пакете (com.example.bird), в отличие от класса Animal. Я все еще могу получить доступ к методу makeSound() класса Animal, и его можно вызвать, как показано ниже. Этот пример доказывает, что подклассы могут по-прежнему получать доступ к protected-методам, даже если они помещены в другой пакет.

package com.example.bird;

import com.example.animals.Animal;

public class Bird extends Animal {
  
  public void getBirdSound(){
    this.makeSound();
  }
}
Теперь я попытаюсь получить доступ к классу default и методу класса BasicCalculator из другого пакета. Я создаю новый пакет с именем com.example.averagecalculator и помещаю в него класс AverageCalculator. Когда я пытаюсь создать новый экземпляр класса BasicCalculator, компилятор показывает мне предупреждение. ‘com.example.calculator.BasicCalculator’ is not public in ‘com.example.calculator’. Cannot be accessed from outside package.

package com.example.averagecalculator;

import com.example.calculator.BasicCalculator;
  
  public class AverageCalculator {
     
     public int calculateAverage(int a, int b){
       BasicCalculator basicCalculator = new BasicCalculator();
     }
  }
Я меняю модификатор BasicCalculator на public, теперь класс доступен отовсюду. Я хочу вызвать метод add() класса BasicCalculator. Однако метод add() по-прежнему имеет модификатор default. Поэтому я получу такое же предупреждение выше для вызова метода add() класса BasicCalculator.

package com.example.averagecalculator;

import com.example.calculator.BasicCalculator;
  
  public class AverageCalculator {
     
     public int calculateAverage(int a, int b){
       BasicCalculator basicCalculator = new BasicCalculator();
       return basicCalculator.add(a,b)/2;
     }
  }

Заключение

Модификаторы доступа в Java — это фундаментальный аспект объектно-ориентированного программирования, который позволяет разработчикам контролировать видимость и доступность классов, методов и переменных. Понимание различий между модификаторами доступа private, public, default и protected необходимо для создания хорошо структурированных, безопасных и удобных в сопровождении Java-приложений.

11 вопросов на интервью про enum в Java

Источник: Medium Вашему вниманию предлагается подборка из одиннадцати наиболее распространенных вопросов про enum (перечисления) в Java, которые могут встретиться вам на техническом интервью.

1. Что такое enum (перечисление) в Java и как его объявить?

Enum (перечисление) в Java — это особый тип с фиксированным набором постоянных значений. Вы можете объявить enum, используя ключевое слово enum, за которым следует его наименование и список констант в фигурных скобках. Например:

enum Season {
  SPRING, SUMMER, AUTUMN, WINTER;
}

2. Каковы преимущества использования enum по сравнению с константами

Некоторые преимущества использования enum над константами:
  • Enum повышают безопасность, предотвращая присвоение недопустимых значений переменным типа перечисления.
  • Enum могут быть размещены внутри или вне класса в зависимости от их области действия и видимости.
  • Enum можно легко использовать в операторах switch, не требуя явного приведения или преобразования.
  • Enum не могут расширять какой-либо класс или расширяться каким-либо классом, что обеспечивает безопасность типов и предотвращает проблемы наследования.
  • Объекты enum нельзя создавать с помощью оператора new, что предотвращает дублирование и трату памяти.
  • Enum могут иметь переменные, конструкторы и методы, такие как обычные классы.

3. Как вы получаете доступ и перебираете константы enum?

Чтобы получить доступ к константе enum, вы можете использовать оператор dot, то есть точку (.) после имени enum. Например:

Season season = Season.SPRING;
Для перебора всех констант enum можно использовать метод values(), который возвращает массив всех констант enum. Например:

for (Season s : Season.values()) { 
  System.out.println(s); 
}

4. Как сравнить две константы enum с помощью оператора ==?

Чтобы сравнить две константы enum с помощью оператора ==, вам просто нужно использовать его, как и любой другой примитивный тип. Вот пример:

if (season == Season.SPRING) {
  System.out.println("It's spring!");
}
Этот способ работает, потому что каждая константа enum является одноэлементным объектом, который имеет только один экземпляр в памяти.

5. Как добавить пользовательские методы и поля в enum?

Чтобы добавить пользовательские методы и поля в enum, вам нужно определить их после списка констант и разделить их точкой с запятой (;). Вам также необходимо предоставить конструктор для инициализации полей, если они не являются статическими. Например:

enum Planet {
  MERCURY(3.303e+23, 2.4397e6),
  VENUS(4.869e+24, 6.0518e6),
  EARTH(5.976e+24, 6.37814e6),
  MARS(6.421e+23, 3.3972e6),
  JUPITER(1.9e+27 ,7.1492e7),
  SATURN(5.688e+26 ,6.0268e7),
  URANUS(8.686e+25 ,2.5559e7),
  NEPTUNE(1.024e+26 ,2.4746e7);

// поля
private final double mass; // в килограммах
private final double radius; // в метрах

// конструктор
Planet(double mass, double radius) {
    this.mass = mass;
    this.radius = radius;
}

// метод
public double getSurfaceGravity() {
    return G * mass / (radius * radius);
}
}

6. Как реализовать оператор switch с помощью enum?

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

switch (season) {
case SPRING:
    System.out.println("Flowers bloom");
    break;
case SUMMER:
    System.out.println("Sun shines");
    break;
case AUTUMN:
    System.out.println("Leaves fall");
    break;
case WINTER:
    System.out.println("Snow falls");
    break;
default:
    System.out.println("Invalid season");
}

7. Как вы используете enum с аннотациями и дженериками?

Чтобы использовать enum с аннотациями и дженериками, вам необходимо следовать некоторым правилам:
  • Аннотации могут принимать только примитивные типы или строки в качестве параметров, поэтому вы не можете передавать enum напрямую в качестве параметра аннотации.
  • Однако вы можете передать массив enums в качестве параметра аннотации, заключив их в фигурные скобки ({}).
  • Обобщения могут принимать любой ссылочный тип в качестве параметров, включая enum.
  • Однако вы не можете создавать generic-массивы с enums, поскольку массивы являются ковариантными, а дженерики — инвариантными.

8. Как получить порядковый номер константы enum?

Вы можете использовать метод ordinal(), который возвращает значение int, представляющее позицию константы enum в объявлении. Например:

enum Color {
  RED, GREEN, BLUE;
}

Color c = Color.GREEN;
System.out.println(c.ordinal()); // prints 1

9. Как получить имя константы enum?

Вы можете использовать метод name(), который возвращает значение String, представляющее имя константы enum, как объявлено. Например:

enum Color {
  RED, GREEN, BLUE;
}

Color c = Color.BLUE;
System.out.println(c.name()); // prints BLUE

10. Как преобразовать значение String в константу enum?

Вы можете использовать метод valueOf(), который принимает параметр String и возвращает константу enum с тем же именем. Вот пример:

enum Color {
  RED, GREEN, BLUE;
}

String s = "RED";
Color c = Color.valueOf(s);
System.out.println(c); // prints RED

11. Как реализовать интерфейс с помощью enum?

Вы можете реализовать интерфейс с помощью enum, указав точку с запятой (;) после списка констант, а затем реализовав методы интерфейса для каждой константы. Например:

interface Operation {
  double apply(double x, double y);
}

enum MathOperation implements Operation {
  PLUS { 
    public double apply(double x, double y) { return x + y; }
  },
  MINUS { 
    public double apply(double x, double y) { return x - y; }
  },
  TIMES { 
    public double apply(double x, double y) { return x * y; }
  },
  DIVIDE { 
    public double apply(double x, double y) { return x / y; }
  };
}