JavaRush /Курсы /Java Syntax Pro /Нюансы объектов в Java

Нюансы объектов в Java

Java Syntax Pro
11 уровень , 4 лекция
Открыта

1. Свойства: геттер и сеттер

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

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

Чтобы избежать таких ситуаций, в Java принято все поля класса делать private. Только методы класса могут менять переменные класса, и никакие методы из других классов не имеют доступа к переменным класса напрямую. Вот так.

Если вы хотите, чтобы другие классы могли получать или менять данные внутри объектов вашего класса, вы должны добавить в код вашего класса два метода — get-метод и set-метод. Пример:

Код Примечание
class Person
{
   private String name;

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

   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }
}


private-поле name



Инициализация поля через конструктор


getName()— метод возвращает значение поля name




setName()— метод изменяет значение поля name

Никакой другой класс не сможет изменить значение поля name напрямую. Если кому-то нужно получить значение поля name, ему придется вызвать метод getName() у объекта типа Person. Если какой-то код хочет поменять значение поля name, ему нужно будет вызвать метод setName() у объекта типа Person.

Метод getName() еще называют «геттер поля name», а  метод setName() — «сеттер поля name».

Это очень распространённый подход. В 80-90% всего Java кода вы никогда не увидите публичные переменные класса. Вместо этого они будут объявлены private (ну или protected), и у каждой переменной будут публичные геттеры и сеттеры.

Этот подход делает код длиннее, но надежнее.

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

Допустим, вы хотите создать класс, который описывает точку на плоскости x и y. Вот как это сделал бы программист-новичок:

class Point
{
   public int x;
   public int y;
}

А вот как это бы сделал опытный Java-программист:

Код
class Point {
   private int x;
   private int y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }

   public int getX() {
      return x;
   }

   public void setX(int x) {
      this.x = x;
   }

   public int getY() {
      return y;
   }

   public void setY(int y) {
      this.y = y;
   }
}

Код стал длиннее? Безусловно.

Зато в сеттеры и геттеры можно добавить валидацию параметров. Например, можно следить за тем, чтобы x и y всегда были больше нуля (или не меньше нуля). Пример:

Код Примечание
class Point {
   private int x;
   private int y;

   public Point(int x, int y) {
      this.x = x < 0 ? 0 : x;
      this.y = y < 0 ? 0 : y;
   }

   public int getX() {
      return x;
   }

   public void setX(int x) {
      this.x = x < 0 ?  0 : x;
   }

   public int getY() {
      return y;
   }

   public void setY(int y) {
      this.y = y < 0 ? 0 : y;
   }
}

11
Задача
Java Syntax Pro, 11 уровень, 4 лекция
Недоступна
С крышей или без? Вот в чем вопрос
Ты сделал предзаказ на новенькую Bugatti ровно полгода назад. Сейчас июнь, и было бы неплохо все-таки ездить на кабриолете. Но ты забыл, в каком кузове заказывал машину. Твоя задача — добавить функциональность для получения текущей конфигурации и изменения её при необходимости. Для этого создай гетт
11
Задача
Java Syntax Pro, 11 уровень, 4 лекция
Недоступна
Зарплата
У нас есть класс Programmer, в котором есть очень важное поле — salary. Наша задача следующая: нужно добавить возможность получить и изменить значение этого поля, используя геттер и сеттер. Но есть нюанс: зарплату можно только повышать. Поэтому тебе нужно добавить проверку в сеттер: если значение ар

2. Время жизни объекта

Вы уже знаете, что объекты создаются с помощью оператора new, а вот как объекты удаляются? Не существуют же они вечно — для этого никакой памяти не хватит.

Во многих языках программирования, таких как С++, для удаления объекта есть специальный оператор delete. А как ситуация с этим обстоит в Java?

В Java все устроено немного иначе, и оператора delete в Java нет. Значит ли это, что объекты в Java не удаляются? Нет, удаляются конечно же. Иначе в Java-приложениях быстро закончилась бы память, и ни о каких месяцах беспрерывной работы и речи бы не шло.

В Java процесс удаления объектов полностью автоматизирован – удалением объектов занимается сама Java-машина. Такой процесс называется сборкой мусора (garbage collecting), а механизм, который собирает мусор — сборщиком мусораGarbage Collector или сокращенно GC.

Так как Java-машина узнает, что какой-то объект нужно удалить и когда?

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

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

Циклические ссылки

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

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

Именно поэтому сборщик мусора делит объекты не на «объекты со ссылками» и «объекты без ссылок», а на достижимые и недостижимые.

Достижимые объекты

Сначала в список достижимых добавляются те объекты, которые 100% живые. Например, текущий поток (Thread.current()) или Консоль (System.in).

Затем список достижимых объектов пополняют те, на которые ссылаются первые достижимые объекты. Затем те, на кого ссылаются вторые и т.д.

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


3. Сборка мусора

Фрагментация памяти

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

И легко может случиться ситуация, когда мы не можем создать большой объект (например, массив на миллион элементов), потому что нет большого куска свободной памяти. Т.е. свободная память вроде и есть, и много, но вот большого цельного куска свободной памяти может и не быть

Оптимизация (дефрагментация) памяти

Java-машина решает эту проблему специфическим образом. Выглядит это примерно так:

Память делится на две части. Все объекты создаются (и удаляются) только в одной ее половине. Когда наступает время убрать дырки в памяти, все объекты из первой половины копируются во второю половину. Но копируются уже вплотную друг другу, чтобы дыр не было.

Выглядит этот процесс примерно так:

Этап 1: После создания объектов

Сборка мусора в Java

Этап 2: Появление «дыр»

Сборка мусора в Java

Этап 3: Устранение «дыр»

Сборка мусора в Java

Таким образом, даже не нужно удалять объекты. Java-машина просто копирует все достижимые объекты в новое место, а всю область памяти со старыми объектами объявляет свободной.

Комментарии (242)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
BUSHIDO Уровень 14
25 ноября 2024
Как можно забыть в каком кузове заказал себе Bugatti? 🤔😂
Aidyn Aubakir Уровень 11
22 июля 2024
Проблема в public полях класса - их могут поменять. Решение - геттеры/сеттеры. Теперь с помощью public сеттера можно поменять private поле. Не понимаю, товарищи, если надо было защитить поле, то зачем опять давай доступ к нему, но через метод?
Денис Уровень 21
26 июля 2024
Можно объявить переменную через Final, тогда после этого сеттер станет недоступны и соответственно, поменять значение будет нельзя. А доступ к нему будет по прежнему через геттер
Alexey Уровень 16
28 июля 2024
Это сделано для контроля доступа к переменным, чтобы нельзя было напрямую обратиться к переменной. Это называется инкапсуляцией. Инкапсуляция позволяет скрыть внутреннее состояние объекта и управлять доступом к нему через геттеры/сеттеры. В геттерах/сеттерах можно добавить логирование, валидацию, кэширование, это обеспечит гибкость и контроль над тем, как изменяются свойства объекта. Никто не должен извне видеть свойства объекта напрямую, за исключением статических свойств. Это помогает поддерживать консистентность и надежность кода, а также облегчает его сопровождение и отладку.
vlby Уровень 34
29 июля 2024
Тут такой вопрос - но при создании нового объекта с прайвит полями - - ПРИ наличии соответствующего коннструктора - мы же тем не менее можем задать значения этих полей!
Kirill Уровень 16
2 августа 2024
Сам пытаюсь осмыслить (не знаю насколько правильно я понял), но вот пример: Предприятие набирает, допустим, сварщиков 3 разряда и устанавливает им всем зарплату, которую указываем в соответствующем классе. Но предприятию допустим нужен более квалифицированный сварщик. Предприятие либо нанимает еще одного специалиста или отправляет на обучение имеющегося. За более высокий разряд, должна быть зарплата больше, но если поменять зарплату в основном поле, все сварщики станут получать больше. А так быть не должно. Чтобы этого избежать используется сеттер, который устанавливает зарплату для конкретного объекта, не меняя условия для остальных. Надеюсь я сам правильно понял и смог объяснить.
Wren Уровень 29
8 августа 2024
Потому что когда мы даем доступ к полю через метод, мы можем контролировать, что именно присваивается полю. Задача про программиста была об этом. В поле salary указана зарплата программиста. Через сеттер ее можно изменить, но только в сторону увеличения. Уменьшить ее каким-либо образом у нас не получится - логика сеттера не позволит.
Vladimir Luchin Уровень 14
6 октября 2024
но если поменять зарплату в основном поле, все сварщики станут получать больше Не думаю, что в данном случае эта логика уместна. Потому что мы же будем менять зарплату не в самом классе сварщика, а в объекте этого конкретного сварщика. А у каждого объекта класса СВАРЩИК и так будут разные з/п. Т.е. их можно было бы у одного конкретного сварщика менять напрямую сменой значения переменной, и у других сварщиков при этом она бы не изменилась.
CodeMashine Уровень 17
6 ноября 2024
написано что бы была возможность валидации. например поле "е меил" объекта "Юзер". в метоже/геттере -- можешь настроит валидацию и никто туда непотребщину не запишет.
Kaz Уровень 32
26 июня 2024
"Зато в сеттеры и геттеры можно добавить валидацию параметров." После этой фразы пример, в котором валидация параметров происходит в конструкторе класса. Затем дублируется эта же валидация в сеттере. Как все это понять?
Javarin Уровень 16
3 июля 2024
Приветсвую! В конструторе валидация - чтобы товарищ программист не смог создать объект с переменными меньше нуля. В сетере валидация для того же самого, так как после создания объекта Point, можно устанавливать новые значения переменным, как раз с помощью сетеров.
Kaz Уровень 32
6 июля 2024
Спасибо. Иногда простые вещи без разжевывания не заходят. Действительно, какая разница какая валидация была в конструкторе, если потом в сеттере можно было бы напортачить... Поэтому как раз нормально, что в конструкторе и сеттере одинаковая верификация.
Ilya Klimchev Уровень 17
28 июля 2024
валидация - лишь бесплатный бонус. Главное - устойчивость приложения к изменению кода. Публичные методы объекта - это его интерфейс. И пока интерфейс не меняется, можно как угодно вертеть кишки объекта. Простой пример: карбюратор, инжектор и переменный резистор одинаково инкапсулируются педалью газа.
NordØne Уровень 33
28 мая 2024
Легкая импровизация:

public class Programmer {
    private int salary = 1000;

    //напишите тут ваш код
    public void setSalary(int salary){
        if (salary>this.salary){
        this.salary = salary;
        System.out.println("От души вообще!");
        }
        else
        System.out.println("Вы не можете понижать зарплату ,вы че охуели?");
    }
    
    public int getSalary(){
        return salary;
    }

}
27 марта 2024
this.salary = Math.max(salary, this.salary);😉
madmax Уровень 32
30 марта 2024
супер, мне даже и в голову не пришло, пора начинать мыслить также)
hidden #3334456 Уровень 11
1 апреля 2024
IDEA, кстати, сама иногда подсказывает такие решения, на них можно обращать внимание :)
madmax Уровень 32
2 апреля 2024
пока не поставил ее, никак руки не дойдут, до сих в вскоде сижу, наверное уже пора)
Senya Уровень 17
5 апреля 2024
Причем давно)
AngryPooh Уровень 15
1 мая 2024
я через тернарный сделала : this.salary = this.salary < salary ? salary : this.salary; пора заводить блокнот со всеми функциями с кратким описанием, иначе через какое-то время половину уже и не помнишь))))
Mikhail Tabakaev Уровень 30
16 мая 2024
зачем запоминать, когда есть гугл) да и большая часть методов имеют говорящее название. можно просто примерно помнить, что такую задачу удобнее решить с помощью такой-то либы и с помощью такого паттерна проектирования и т.д.) все придёт с опытом)
anporshnev Уровень 33
21 мая 2024
Выглядит лаконично, использовал такую конструкцию в задачах на сравнение. Но наверно лучше снижать количество операции перезаписи переменных, в данном случае запись осуществляется каждый раз, вне зависимости от условия. Если конечно размышлять о более тяжелых приложениях со сложной логикой и большим количеством операций)
Anonymous #3444663 Уровень 16
24 июня 2024
нет, так не нужно делать. Во-первых, читаемость ниже. На таком примитивном примере это ок, но в реальном коде лучше писать if. Во-вторых вы всегда переопределяете переменную и вызываете метод Math.max, когда сравнение примитивов сработает быстрее и вы просто не зайдете в условие присвоения
Мученик Уровень 15
21 марта 2024
я так понял в примере написан тернарный код в место if else просто потому что это короче и понятней и всё ?
Александра Уровень 23
23 марта 2024
Может быть😁 Я написала через if, я в нем как то увереннее)
Бромгексин Уровень 38
8 марта 2024
задачи легкие но вот в самой лекции немного не понимаю что и как
{Java_Shark} Уровень 28
28 февраля 2024
Всем удачи, понимания и терпения)))
Вадим Шемякин Уровень 11
24 февраля 2024
С геттер и сеттер понятно 😀, а с Время жизни объекта,Циклические ссылки и Достижимые объекты, непонятно но очень интересно.☹️
Vokipal Уровень 17
11 февраля 2024
А почему геттер пишется через public, а сеттер через public void?
ShanShan323 Уровень 25 Expert
11 февраля 2024
Если правильно понял, через get мы просто получаем значение переменной и следовательно public <тип возвращаемой переменной> get, а set мы присваиваем новое значение переменной класса(объекта:D), и нам не нужно ничего возвращать (void). public void set(параметры).
Ruslan Machalov Уровень 14
15 мая 2024
А еще геттер должен указывать тип возращаемой переменной 😉