Различия между ранним и поздним связыванием в Java

Статья из группы Java Developer
участников
Чтобы выяснить, в чем состоит различие между ранним (статическим) и поздним (динамическим) связыванием в Java, нужно сначала понять, что такое это самое связывание. Связывание означает наличие связи между ссылкой и кодом. Например, переменная, на которую вы ссылаетесь, привязана к коду, в котором она определена. Аналогично, вызываемый метод привязан к месту в коде, где он определен.
Различия между ранним и поздним связыванием в Java - 1
Существует два типа связывания методов в языке Java: ранее связывание (его ещё называют статическим) и позднее (соответственно, динамическое) связывание. Вызов метода в Java означает, что этот метод привязывается к конкретному коду или в момент компиляции, или во время выполнения, при запуске программы и создании объектов. Можно понять из названия, статическое связывание носит более статический характер, так как происходит во время компиляции, то есть код «знает», какой метод вызывать после компиляции исходного кода на Java в файлы классов. А поскольку это относится к ранней стадии жизненного цикла программы, то называется также ранним связыванием (early binding). С другой стороны, динамическое связывание происходит во время выполнения, после запуска программы виртуальной машиной Java. В этом случае то, какой метод вызвать, определяется конкретным объектом, так что в момент компиляции информация недоступна, ведь объекты создаются во время выполнения. А поскольку это происходит на поздней стадии жизненного цикла программы, то называется в языке Java поздним связыванием (late binding).
Итак, фундаментальное различие между статическим и динамическим связыванием в Java состоит в том, что первое происходит рано, во время компиляции на основе типа ссылочной переменной, а второе – позднее, во время выполнения, с использованием конкретных объектов.
Давайте рассмотрим еще несколько отличий, чтобы лучше разобраться с этим, а, кроме того, мочь ответить на этот очень популярный вопрос, который задают на собеседованиях по Java.

Раннее и позднее связывание в Java

Существует множество различий статического и динамического связывания в языке Java, но важнейшее – то, как их использует JVM. Задумывались ли вы когда-нибудь, каким образом JVM решает, какой метод вызвать, если в области видимости содержится более одного метода с одним именем? Если вы когда-либо использовали перегрузку или переопределение методов, то знаете, что в Java может быть несколько методов с одним именем. В случае с Java виртуальная машина JVM использует как статическое, так и динамическое связывание для выбора нужного метода.

Пример статического и динамического связывания в Java

В этой программе вы увидите, что привязка виртуальных методов не происходит во время компиляции при помощи статического связывания, поскольку в этом случае вызывался бы метод из суперкласса, как происходит со статическими методами, которые связываются рано. Если будет вызван метод из подкласса, то для связывания функции использовался конкретный объект во время выполнения, а, следовательно, для связывания виртуальных функций используется динамическое связывание.
public class Main {
  public static void main(String[] args) {

    // Пример статического и динамического связывания в Java
    Insurance current = new CarInsurance();

    // Динамическое связывание на основе объекта
    int premium = current.premium();

    // Статическое связывание на основе класса
    String category = current.category();

    System.out.println("premium : " + premium);
    System.out.println("category : " + category);
  }
}

class Insurance{
  public static final int LOW = 100;

  public int premium(){
    return LOW;
  }

  public static String category(){
    return "Insurance";
  }

}

class CarInsurance extends Insurance{
  public static final int HIGH = 200;

  public int premium(){
    return HIGH;
  }

  public static String category(){
    return "Car Insurance";
  }

}
Результаты выполнения:

premium : 200
category : Insurance
Как вы видите, вызов метода premium() привел к выполнению метода из подкласса, в то время как вызов метода category() привел к выполнению метода суперкласса. Это происходит из-за того, что premium() – виртуальный метод, который разрешается при помощи позднего связывания, в то время как category() – статический метод, который разрешается при помощи статического связывания во время компиляции по имени класса.
Интересно читать о Java? Вступайте в группу Java Developer!

Различия между ранним и поздним связыванием в языке Java

Теперь, когда вы разобрались и понимаете, как в языке Java связываются вызовы методов и как функционирует статическое и динамическое связывание, давайте еще раз перечислим ключевые различия между ранним и поздним связыванием в языке Java:
  1. Статическое связывание происходит во время компиляции, а динамическое – во время выполнения.

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

  3. Статическое связывание используется в языке Java для разрешения перегруженных методов, в то время как динамическое связывание используется в языке Java для разрешения переопределенных методов.

  4. Аналогично, приватные, статические и терминальные методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять, а все виртуальные методы разрешаются при помощи динамического связывания.

  5. В случае статического связывания используются не конкретные объекты, а информация о типе, то есть для обнаружения нужного метода используется тип ссылочной переменной. С другой стороны, при динамическом связывании для нахождения нужного метода в Java используется конкретный объект.
Вот неплохое упражнение, основанное на понятиях статического и динамического связывания в языке Java. Сможете ли вы ответить на вопрос: "Что будет выведено при выполнении следующей программы?"
Различия между ранним и поздним связыванием в Java - 2
Что выведет эта программа? Collection, Set или HashSet? Вот и все, что мы хотели рассказать вам о различиях между ранним (статическим) и поздним (динамическим) связыванием в языке Java. Это один из лучших вопросов для телефонного собеседования по языку Java, поскольку оно предоставляет немало возможностей проверки глубины знаний кандидата. Всегда помните, что приватные, статические и final-методы связываются при помощи статического связывания, а виртуальные – динамического. Аналогично, лучший пример статического связывания – перегрузка методов, а переопределение – динамического. Источник
Комментарии (26)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Митяй Митяич
Уровень 24
30 июня, 07:58
Я бы назвал не код, а класс или подкласс.
Matvey Tratsevsky
Уровень 25, Минск
16 июня, 18:01
Вообще не понимаю чем в данном случае category отличается от premium? Методы же ничем не различаются в классах
Anonymous #2436575 Android Developer в AllPets
2 июля, 07:10
Статические методы существуют в единственном экземпляре их нельзя переопределить. В данном случае для компилятора это два различных метода и без явного указания, что нам нужен именно метод подкласса, он его не увидит, так как его перекрывает родительский.
Anonymous #2436575 Android Developer в AllPets
2 июля, 07:11
Проще говоря когда методы с одинаковыми сигнатурами является не статиками - они переопределяются в классах наследниках и вызываются согласно подклассу, когда такие методы статичны - всегда вызывается только родительский класс если иное не указано явно, так как компилятор при создании статика ничего не знает о подклассах, они еще не существуют.
hamster🐹 ClipMaker в TikTok
9 января, 10:03
Если изменить
Insurance current = new CarInsurance();
на
CarInsurance current = new CarInsurance();
то вызовется именно статический метод класса CarInsurance, поскольку для статических методов происходит статическое связывание по типу переменной (не по типу объекта)
Галкин Юрий
Уровень 41, Москва
14 августа 2021, 15:52
Похоже ошибка: > Аналогично, приватные, статические и -=терминальные=_ методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять, а все виртуальные методы разрешаются при помощи динамического связывания. Потому что есть такая информация: > A common misconception is that declaring a method as final improves efficiency by allowing the compiler to directly insert the method wherever it is called (see inline expansion). Because the method is loaded at runtime, compilers are unable to do this. Only the runtime environment and JIT compiler know exactly which classes have been loaded, and so only they are able to make decisions about when to inline, whether or not the method is final.[5]
Лёхансан Junior Java Developer в Senla
27 июля 2021, 15:30
У меня все сложилось вот в такую картинку:
Галкин Юрий
Уровень 41, Москва
14 августа 2021, 15:59
Хорошая картинка, но есть ошибка: Названы вирутальными только public, тогда как > В Java виртуальная функция означает функцию, которая может быть переопределена в своих подклассах. Таким образом, все нестатические методы Java являются виртуальной функцией ну или тут > Виртуальный метод (виртуальная функция) — в объектно-ориентированном программировании метод (функция) класса, который может быть переопределён в классах-наследниках так, что конкретная реализация метода для вызова будет определяться во время исполнения. Таким образом, программисту необязательно знать точный тип объекта для работы с ним через виртуальные методы: достаточно лишь знать, что объект принадлежит классу или наследнику класса, в котором объявлен метод. Одним из переводов слова virtual с английского языка может быть «фактический», что больше подходит по смыслу. т.e. это именно нестатические, нечастные или неконечные методы
Александр Java Developer в ООО ИТМ
12 мая 2020, 19:51
Попытаюсь объяснить суть доступным языком по поводу связывания. Если в чем-то не прав, то поправьте, пожалуйста. Допустим, программа требует ввести какое-либо число (не обговорено, какое именно). Поставим задачу так, что для вывода числа на экран мы воспользовались методами, имеющие одинаковую реализацию. За исключением того, что принимают разные типы данных. Например, методы: public void outputNumber(int number), public void outputNumber(double number). Условились этим моментом. Теперь перейдем к главному. Если мы заранее дадим понять JVM, с каким типом данных будем работать, то произойдет раннее (статическое) связывание. То есть, допустим, мы напишем вот так: int number = 5; outputNumber(number); Из кода понятно, что мы создали переменную number с типом данных int. Тогда JVM на этапе компиляции будет понимать, в какой метод посылать ей значение переменной. Рассмотрим теперь такой вариант. Допустим, наш метод получает значение из какой-либо части кода. Причем нам неизвестно, какой тип данных переменная имеет (нам не важно, каким способом передается значение в метод. Там может быть всё, что угодно. Для простоты отписал переменную number. Причем (number) не обязательно int). outputNumber(number); Тогда JVM и определяет во время исполнения кода, какое значение передается в метод. То есть, происходит позднее (динамическое) связывание. Надеюсь, что всё, что я написал, является верным. Прочитав еще кое-какой материал, я пришел к выводу, что выше описал только раннее связывание (перегрузку методов). Динамическое связывание проявляется при переопределении методов. Если кто-то желает разобраться - оставьте комментарий под моим.
Роман Кончалов
Уровень 28, Россия
Expert
1 октября 2021, 09:47
Молодец, что сам исправил)
barracuda
Уровень 41, Санкт-Петербург, Россия
Expert
5 мая 2020, 22:18
Хорошая статейка. Спасибо!
АGeek
Уровень 25, Москва, Россия
Expert
9 мая 2019, 17:51
Спасибо. Очень полезно.
АGeek
Уровень 25, Москва, Россия
Expert
24 января 2020, 07:42
ничего себе, я это уже читал оказывается)
Sekator
Уровень 41, Староконстантинов, Украина
26 января 2020, 07:59
да походу мы тут все по кругу ходим. извилины вяжем наверное так🤣
Радик
Уровень 35, Казань
25 февраля 2019, 20:01
Я не понял п.4 : "Аналогично, приватные, статические и терминальные методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять..." Нельзя переопределять? А разве в примере с классом Insurance мы не переопределили метод category() ?
As Is
Уровень 23, Россия
14 марта 2019, 07:45
Заметь, что метод category() статический, а статический метод переопределить нельзя. В классе CarInsurance свой метод category() не подозревающий о методе category() из класса Insurance.
Alex
Уровень 17, Минск, Беларусь
9 июня 2020, 14:26
Да, все верно. Можно проверить - если в классе CarInsurance обозначить метод category() аннотацией @Override- будет ошибка, т.к. метод category() не является переопределенным.
vinsler
Уровень 35, Санкт-Петербург, Россия
Expert
12 февраля 2019, 20:53
Короче, перечитал много раз, много интересных и умных и красивых слов. Для тех кто в танке поясню на пальцах, потому что всем все очевидно, но никто так и не понял.
VarA var = new VarB;
Слева от знака = пишется переменная/класс/итп откуда берется описание/названия полей/методов/итп. Справа от знака = после new пишется откуда берутся реализация этих полей/методов/итп. слово static означает что-то типа единственный экземпляр, объект из которого создать нельзя, однако если создать объект с вариацией как сверху, то реализация со словом static будет браться оттуда, где пишутся названия.
Владимир Java Developer в EPAM
21 февраля 2021, 12:21
Спасибо, теперь все стает на свои места, долго загонялся и не мог понять как это работает!