Kotlin | Вопросы собесов
2.52K subscribers
28 photos
1.03K links
Download Telegram
🤔 Чем отличаются data-классы и sealed-классы?

data class — класс для хранения данных с автогенерацией equals(), hashCode(), copy().
sealed class — ограниченная иерархия классов, используется для when.

🚩`data class` – для хранения данных

data class автоматически создаёт:
equals() и hashCode() → сравнение объектов по значениям.
copy() → удобное копирование с изменением параметров.
toString() → красивый вывод.
data class User(val id: Int, val name: String)

fun main() {
val user1 = User(1, "Alice")
val user2 = user1.copy(name = "Bob") // Создаём копию с новым именем

println(user1) // User(id=1, name=Alice)
println(user2) // User(id=1, name=Bob)
}


🚩`sealed class` – для ограниченных иерархий классов

sealed class используется, когда есть фиксированное число подклассов.
sealed class NetworkState {
object Loading : NetworkState()
data class Success(val data: String) : NetworkState()
data class Error(val message: String) : NetworkState()
}

fun handleState(state: NetworkState) {
when (state) {
is NetworkState.Loading -> println("Загрузка...")
is NetworkState.Success -> println("Данные: ${state.data}")
is NetworkState.Error -> println("Ошибка: ${state.message}")
}
}


🚩Можно ли использовать `data class` внутри `sealed class`?

Да! Это лучший вариант для управления состояниями:
sealed class Result {
object Loading : Result()
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 В чем разница между LinkedList и ArrayList?

`ArrayList` хранит элементы в виде массива и обеспечивает быстрый доступ по индексу, но медленно вставляет и удаляет элементы в середине списка, так как элементы нужно сдвигать. `LinkedList` основан на узлах, где каждый узел ссылается на следующий, что делает вставку и удаление быстрыми операциями, но доступ к элементам по индексу медленнее, так как нужно пройти через каждый узел. `ArrayList` лучше подходит для доступа к данным, а `LinkedList` — для частого изменения структуры данных.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 В каких случаях наследование полезно, а в каких нет?

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

🚩Когда наследование полезно

🟠Для повторного использования кода
Если у вас есть несколько классов, которые имеют общие свойства или методы, наследование позволяет вынести эти общие элементы в родительский класс. Это помогает избежать дублирования кода.

   open class Animal(val name: String) {
fun eat() {
println("$name ест.")
}
}

class Dog(name: String) : Animal(name) {
fun bark() {
println("$name лает.")
}
}

class Cat(name: String) : Animal(name) {
fun meow() {
println("$name мяукает.")
}
}

// Использование:
val dog = Dog("Бобик")
dog.eat() // "Бобик ест."
dog.bark() // "Бобик лает."


🟠Для создания иерархий классов (например, типологии)
Наследование отлично подходит для задач, связанных с созданием деревьев типов или категорий. Например, у вас есть базовый класс Shape (Форма), и от него наследуются конкретные фигуры: Circle, Rectangle, Triangle.

   open class Shape {
open fun draw() {
println("Рисуется форма.")
}
}

class Circle : Shape() {
override fun draw() {
println("Рисуется круг.")
}
}

class Rectangle : Shape() {
override fun draw() {
println("Рисуется прямоугольник.")
}
}

// Использование:
val shapes: List<Shape> = listOf(Circle(), Rectangle())
for (shape in shapes) {
shape.draw()
}


🟠Для использования полиморфизма
Полиморфизм позволяет использовать родительские классы для работы с объектами наследников. Это полезно, если у вас есть методы, которые работают с разными типами объектов, но с единым интерфейсом. Вызов методов draw() у всех объектов, не задумываясь об их конкретном типе, благодаря полиморфизму.

   fun render(shape: Shape) {
shape.draw()
}

render(Circle()) // "Рисуется круг."
render(Rectangle()) // "Рисуется прямоугольник."


🟠Когда концептуально существует отношение "is-a" (является)
Если наследник действительно является вариантом (подтипом) родительского класса, наследование логично. Например: Собака является животным (Dog is an Animal). Прямоугольник является фигурой (Rectangle is a Shape).

🚩Когда наследование не полезно

🟠Когда отношение "is-a" отсутствует
Если объекты не имеют строгого отношения "является", наследование использовать нельзя. Например: Класс Car (Машина) и Boat (Лодка) лучше объединить через общие свойства (интерфейсы или композицию), чем делать лодку наследником машины.

   open class Bird {
open fun fly() {
println("Птица летит.")
}
}

class Penguin : Bird() {
override fun fly() {
throw UnsupportedOperationException("Пингвин не умеет летать!")
}
}


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

   class Car : Engine, Wheels

Используйте композицию:

   class Car {
private val engine = Engine()
private val wheels = Wheels()
}


🟠Сложные иерархии классов
Если вы создаете слишком глубокие или разветвленные иерархии, код становится сложным для понимания, тестирования и изменения. Например, изменение в базовом классе может неожиданно затронуть всех наследников.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что такое Garbage Collector Roots?

Garbage Collector Roots в контексте JVM (Java Virtual Machine) — это набор объектов, которые служат отправными точками для сборщика мусора при анализе достижимых объектов. К ним относятся активные потоки, статические поля классов, локальные переменные и активные блоки Java-кода.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Что при создании классов в Kotlin изменились по сравнению с Java?

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

🚩Объявление классов и конструкторов

В Kotlin объявление классов и их конструкторов значительно упрощено.
В Java
public class Person {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}


В Kotlin
class Person(val name: String, val age: Int)


🟠Статические члены
В Java для объявления статических членов используется ключевое слово static. В Kotlin вместо этого используются companion object.
В Java
public class MyClass {
public static final String CONSTANT = "constant";

public static void staticMethod() {
// Some code
}
}


В Kotlin
class MyClass {
companion object {
const val CONSTANT = "constant"

@JvmStatic
fun staticMethod() {
// Some code
}
}
}


🟠Data классы
Kotlin предоставляет специальный тип классов — data классы, которые автоматически генерируют методы equals(), hashCode(), toString(), copy(), и componentN().
В Java
public class User {
private String name;
private int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public boolean equals(Object o) {
// Implementation
}

@Override
public int hashCode() {
// Implementation
}

@Override
public String toString() {
// Implementation
}
}


В Kotlin
data class User(val name: String, val age: Int)


🟠Свойства и методы доступа
В Kotlin свойства объявляются напрямую, и методы доступа (геттеры и сеттеры) генерируются автоматически.
В Java
public class Rectangle {
private int width;
private int height;

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}

public int getHeight() {
return height;
}

public void setHeight(int height) {
this.height = height;
}
}


В Kotlin
class Rectangle(var width: Int, var height: Int)


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Отличие исключений Kotlin / Java?

В Kotlin нет checked исключений, все исключения — unchecked. Это уменьшает кодовую нагрузку (throws, catch) и упрощает API. В Java нужно явно обрабатывать checked исключения.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
🤔 Как с MVI обрабатывать события, которые не нужно хранить?

В MVI (Model-View-Intent) есть проблема: события, которые не нужно хранить в State, могут повторно отобразиться при пересоздании экрана (например, при повороте экрана или навигации назад).
Показ Toast / Snackbar
Навигация (openScreen())
Ошибки, которые нужно показать один раз

🟠Используем `SingleLiveEvent` (для ViewModel)
Это LiveData, которая отправляет событие только один раз и не пересылает его новым подписчикам.
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)

override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner) { value ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(value)
}
}
}

override fun setValue(value: T?) {
pending.set(true)
super.setValue(value)
}
}


ViewModel с SingleLiveEvent
class MyViewModel : ViewModel() {
val eventShowToast = SingleLiveEvent<String>()

fun onButtonClicked() {
eventShowToast.value = "Привет, это Toast!"
}
}


Activity/Fragment подписывается
viewModel.eventShowToast.observe(viewLifecycleOwner) { message ->
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}


🟠Используем `SharedFlow` (современный подход)
В StateFlow все события хранятся в State (что не подходит для одноразовых событий).
Вместо этого используем SharedFlow с replay = 0, чтобы событие не повторялось.

ViewModel с SharedFlow
class MyViewModel : ViewModel() {
private val _events = MutableSharedFlow<UiEvent>()
val events = _events.asSharedFlow()

fun onButtonClicked() {
viewModelScope.launch {
_events.emit(UiEvent.ShowToast("Привет, это Toast!"))
}
}
}

sealed class UiEvent {
data class ShowToast(val message: String) : UiEvent()
object NavigateToNextScreen : UiEvent()
}


Подписка в UI:
lifecycleScope.launchWhenStarted {
viewModel.events.collect { event ->
when (event) {
is UiEvent.ShowToast -> Toast.makeText(context, event.iss.onessage, Toast.LENGTH_SHORT).show()
is UiEvent.NavigateToNextScreen -> findNavController().navigate(R.id.nextFragment)
}
}
}


🟠Используем `EventWrapper` (обходной путь для StateFlow)
Если в State всё-таки нужно хранить событие, но не показывать его повторно, можно использовать EventWrapper.
class EventWrapper<out T>(private val content: T) {
private var hasBeenHandled = false

fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) null else {
hasBeenHandled = true
content
}
}
}


ViewModel с StateFlow:
data class UiState(val message: EventWrapper<String>? = null)

class MyViewModel : ViewModel() {
private val _state = MutableStateFlow(UiState())
val state = _state.asStateFlow()

fun onButtonClicked() {
_state.value = UiState(message = EventWrapper("Привет, это Toast!"))
}
}


UI подписывается и проверяет EventWrapper:
viewModel.state.collect { state ->
state.message?.getContentIfNotHandled()?.let { message ->
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Можно ли что-то из компонентов не описывать в Manifest?

Да, все компоненты, которые не должны быть доступны извне, могут не указываться, особенно:
- Activity, используемая только внутри;
- BroadcastReceiver или Service, регистрируемые динамически;
- ContentProvider — если не требуется внешнего доступа.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1🤔1
🤔 Как подключить BroadcastReceiver, чтобы он смог получать сообщения?

Подключение BroadcastReceiver в Android состоит из двух основных шагов: создание самого ресивера и его регистрация. Ресивер можно зарегистрировать как статически в манифесте, так и динамически в коде.

🚩Создание BroadcastReceiver

Создадим простой BroadcastReceiver, который будет реагировать на определённое событие, например, на получение SMS.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Сообщение получено!", Toast.LENGTH_SHORT).show();
}
}


🚩Регистрация ресивера

🟠Статическая регистрация в манифесте
Можно зарегистрировать ресивер в файле AndroidManifest.xml. Это удобно, когда вы хотите, чтобы ресивер всегда был активен и слушал определённые системные события, например, перезагрузку устройства или получение SMS.
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.myapp">

<application
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>

</application>

</manifest>


🟠Динамическая регистрация в коде
Иногда нужно регистрировать ресивер только на время выполнения определённой активности или службы. В этом случае лучше использовать динамическую регистрацию.
import android.content.IntentFilter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
private MyBroadcastReceiver myBroadcastReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myBroadcastReceiver = new MyBroadcastReceiver();
}

@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(myBroadcastReceiver, filter);
}

@Override
protected void onStop() {
super.onStop();
unregisterReceiver(myBroadcastReceiver);
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 В чём основной плюс MVVM?

Главный плюс MVVM — разделение обязанностей:
- UI (View) отделён от логики (ViewModel);
- бизнес-логика тестируется отдельно;
- упрощает масштабирование и поддержку;
- особенно эффективно в Android с LiveData, StateFlow, DataBinding.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1🤔1
🤔 Расскажи подробно, что из себя представляет каждый компонент Аndroid-приложения?

Android-приложение состоит из четырёх основных компонентов:
Activity – UI-экран приложения.
Service – фоновая работа без UI.
BroadcastReceiver – слушает системные и пользовательские события.
ContentProvider – делится данными между приложениями.

🚩`Activity` – экран приложения (UI)

Отображает интерфейс пользователя.
Обрабатывает взаимодействие (нажатия, свайпы, ввод текста).
Управляется системой через жизненный цикл (Lifecycle).
Пример Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // Загружаем XML-разметку
}
}


🚩`Service` – фоновая работа без UI

Выполняет длительные фоновые задачи (музыка, загрузка файлов).
Может работать даже если приложение закрыто.
НЕ имеет UI (не рисует View).
Пример Service
class MyService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Thread {
// Долгая фоновая задача
}.start()
return START_STICKY
}

override fun onBind(intent: Intent?): IBinder? = null
}


Запуск сервиса
startService(Intent(this, MyService::class.java))


Остановка сервиса
stopService(Intent(this, MyService::class.java))


🚩`BroadcastReceiver` – обработка событий (системных и пользовательских)

- Получает события системы (BOOT_COMPLETED, BATTERY_LOW).
- Слушает события других приложений.
- Может быть динамическим (через код) или статическим (AndroidManifest.xml).
Пример BroadcastReceiver (отключение Wi-Fi)
class NetworkReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
println("Сеть изменилась!")
}
}
}


Регистрируем BroadcastReceiver в AndroidManifest.xml
<receiver android:name=".NetworkReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>


Можно зарегистрировать динамически в коде
val receiver = NetworkReceiver()
registerReceiver(receiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))


🚩`ContentProvider` – обмен данными между приложениями

- Дает доступ к данным *другим приложениям.
- Позволяет безопасно работать с БД (Room, SQLite).
- Используется для Contacts, MediaStore, Calendar.

Пример ContentProvider (чтение контактов)
val cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)
cursor?.use {
while (it.moveToNext()) {
val name = it.getString(it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
println("Контакт: $name")
}
}


Запрос данных через Uri
val uri = Uri.parse("content://com.example.provider/table")
contentResolver.query(uri, null, null, null, null)


🚩Как компоненты взаимодействуют между собой?

Пример: Activity запускает Service через Intent
startService(Intent(this, MyService::class.java))

Пример: Service отправляет Broadcast, а Receiver его ловит
sendBroadcast(Intent("com.example.CUSTOM_EVENT"))


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Зачем при повороте экрана пересоздаётся Activity?

Поворот экрана вызывает изменение конфигурации (orientation, screen size). Android по умолчанию пересоздаёт Activity, чтобы загрузить соответствующий ресурсный файл и адаптировать интерфейс.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4
🤔 Если профайлер показывает тебе что какой-нибудь фрейм занял 120 миллисекунд, что это значит?

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

🚩Разбор проблемы

🟠Что значит "фрейм" в этом контексте?
В Android интерфейс обновляется 60 раз в секунду (частота 60 FPS). Это значит, что каждый кадр (фрейм) должен рендериться не дольше 16,67 мс (1000 мс / 60 FPS).

🟠Почему 120 мс — это плохо?
Если рендеринг кадра занимает 120 мс, то за это время устройство должно было бы нарисовать 7 кадров (120 / 16,67 ≈ 7). Однако оно успело обработать только один, что приводит к заметному подтормаживанию.

🟠Что может вызывать такие задержки?
Тяжёлые вычисления в основном потоке (UI Thread) – например, сложные математические операции, работа с JSON, парсинг файлов.
Долгие операции с рендерингом – сложные векторные изображения, перегруженные Canvas.draw() или анимации.
Синхронные вызовы I/O (чтение файлов, базы данных, сети) – если, например, в onDraw() идёт обращение к диску или базе данных.
Неоптимальный layout – глубокая иерархия ViewGroup, частые перерасчёты макетов (measure/layout).

🚩Как исправить?

🟠Перенести тяжёлые вычисления в фоновый поток
Coroutines, Executors, WorkManager
🟠Использовать профайлер Android Studio
для поиска "узких мест".
🟠Оптимизировать рендеринг
избегать сложных onDraw(), использовать ViewStub, RecyclerView.
🟠Пересмотреть макет
убрать ненужные ViewGroup, использовать ConstraintLayout.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как запросить, чтобы система выделила больше ОЗУ на приложение?

Нельзя напрямую "попросить" больше памяти, но:
- Указать в manifest:
- Это увеличит лимит на heap, но злоупотреблять нельзя — система может завершить процесс.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
🤔 Какой класс можно использовать что бы ловить разные жесты?

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

🚩Как использовать GestureDetector?

Создайте экземпляр GestureDetector, передав Context и слушателя (GestureDetector.OnGestureListener).
Передавайте события касания в gestureDetector.onTouchEvent(event) из метода onTouchEvent().
class GestureActivity : AppCompatActivity(), GestureDetector.OnGestureListener {
private lateinit var gestureDetector: GestureDetector

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
gestureDetector = GestureDetector(this, this)
}

override fun onTouchEvent(event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
}

override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
val deltaX = e2!!.x - e1!!.x
if (deltaX > 0) Log.d("Gesture", "Swipe Right") else Log.d("Gesture", "Swipe Left")
return true
}

override fun onDown(e: MotionEvent?) = true
override fun onShowPress(e: MotionEvent?) {}
override fun onSingleTapUp(e: MotionEvent?) = false
override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float) = false
override fun onLongPress(e: MotionEvent?) {}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Если профайлер показывает, что какой-нибудь фрейм занял 120 миллисекунд, что это значит?

В Android каждый UI-кадр (frame) должен отрисовываться не дольше 16.67 мс (при 60 FPS).
Если профайлер показывает, что фрейм занял 120 мс, это означает:
- Задержка в отрисовке — кадр "просел".
- Произошёл jank — визуальный лаг, фриз или «дёргание» UI.
- Система не успела отрисовать кадр вовремя, и пользователь видит паузу.
Причины могут быть: тяжелая логика в onDraw, в ViewModel, плохая работа RecyclerView, тяжелые изображения и т.д.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3
🤔 Опиши как лучше всего использовать модификаторы доступа в Java?

В Java есть 4 модификатора доступа, которые контролируют видимость полей, методов и классов:

🚩`private` – самый безопасный (использовать по умолчанию)

Поля почти всегда должны быть private, чтобы инкапсулировать данные. Методы тоже лучше делать private, если они не должны использоваться извне.
public class User {
private String name; // Доступен только внутри класса

public User(String name) {
this.name = name;
}

private void logUserAction() { // Только внутри класса
System.out.println(name + " совершил действие");
}
}


🚩`package-private` (без модификатора) – для классов внутри пакета

Используется, если класс не должен быть доступен за пределами пакета. Полезно для вспомогательных классов.
class FileHelper { //  Доступен только в этом пакете
static void readFile(String path) {
System.out.println("Читаем файл: " + path);
}
}


🚩`protected` – для наследников, но не для всех

protected лучше использовать только в abstract классах и для методов, которые должны быть переопределены. Не используй protected для полей → нарушает инкапсуляцию!
abstract class Animal {
protected void makeSound() { // Наследники могут переопределять
System.out.println("Какой-то звук");
}
}

class Dog extends Animal {
@Override
protected void makeSound() {
System.out.println("Гав-гав!");
}
}


🚩`public` – только если класс/метод нужен везде

Класс можно делать public, только если он должен быть доступен во всех пакетах. Не делай поля public → нарушает инкапсуляцию!
public class UserManager { //  Доступен везде
public void createUser() { // Можно вызывать в любом месте
System.out.println("Создание пользователя...");
}
}


🚩`final` и `static` – дополняем модификаторы

final class → класс нельзя наследовать.
final method → метод нельзя переопределить.
final field → переменная неизменяема (константа).
public final class MathUtils {
public static final double PI = 3.14159;
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Можно ли избежать вызова onPause и onStop?

Нет. Эти методы вызываются автоматически системой при смене фокуса, сворачивании, смене экрана. Их нельзя отменить, но можно переопределить.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1