Различия между ранним и поздним связыванием в 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-методы связываются при помощи статического связывания, а виртуальные – динамического. Аналогично, лучший пример статического связывания – перегрузка методов, а переопределение – динамического. Источник
Что еще почитать?

Простой способ внедрения зависимостей

Использование Java Arrays.sort() для любого списка объектов

Комментарии (27)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
30 августа 2022
"В этой программе вы увидите, что привязка виртуальных методов не происходит во время компиляции при помощи статического связывания, поскольку в этом случае вызывался бы метод из суперкласса, как происходит со статическими методами, которые связываются рано. Если будет вызван метод из подкласса, то для связывания функции использовался конкретный объект во время выполнения, а, следовательно, для связывания виртуальных функций используется динамическое связывание." Вот подобная формулировка это одна из самых основных причин, почему изучать надо на английском, так как простые вещи русские переводчики могут завернуть в инверсионные макароны с кучей сложноподчиненных предложений, объясняя результат кода до самого кода.
Митяй Митяич Уровень 24
30 июня 2022
Я бы назвал не код, а класс или подкласс.
Matvey Tratsevsky Уровень 25
16 июня 2022
Вообще не понимаю чем в данном случае category отличается от premium? Методы же ничем не различаются в классах
hamster🐹 Уровень 36
9 января 2022
Если изменить

Insurance current = new CarInsurance();
на

CarInsurance current = new CarInsurance();
то вызовется именно статический метод класса CarInsurance, поскольку для статических методов происходит статическое связывание по типу переменной (не по типу объекта)
Галкин Юрий Уровень 41
14 августа 2021
Похоже ошибка: > Аналогично, приватные, статические и -=терминальные=_ методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять, а все виртуальные методы разрешаются при помощи динамического связывания. Потому что есть такая информация: > 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]
Лёхансан Уровень 39
27 июля 2021
У меня все сложилось вот в такую картинку:
Александр Уровень 25
12 мая 2020
Попытаюсь объяснить суть доступным языком по поводу связывания. Если в чем-то не прав, то поправьте, пожалуйста. Допустим, программа требует ввести какое-либо число (не обговорено, какое именно). Поставим задачу так, что для вывода числа на экран мы воспользовались методами, имеющие одинаковую реализацию. За исключением того, что принимают разные типы данных. Например, методы: 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 и определяет во время исполнения кода, какое значение передается в метод. То есть, происходит позднее (динамическое) связывание. Надеюсь, что всё, что я написал, является верным. Прочитав еще кое-какой материал, я пришел к выводу, что выше описал только раннее связывание (перегрузку методов). Динамическое связывание проявляется при переопределении методов. Если кто-то желает разобраться - оставьте комментарий под моим.
barracuda Уровень 41 Expert
5 мая 2020
Хорошая статейка. Спасибо!
АGeek Уровень 25 Expert
9 мая 2019
Спасибо. Очень полезно.
Радик Уровень 35
25 февраля 2019
Я не понял п.4 : "Аналогично, приватные, статические и терминальные методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять..." Нельзя переопределять? А разве в примере с классом Insurance мы не переопределили метод category() ?