JavaRush/Java блог/Java Developer/Различия между ранним и поздним связыванием в Java
Автор
Василий Малик
Senior Java-разработчик в CodeGym

Различия между ранним и поздним связыванием в 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-методы связываются при помощи статического связывания, а виртуальные – динамического. Аналогично, лучший пример статического связывания – перегрузка методов, а переопределение – динамического. Источник
Комментарии (36)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Anonymous #2978477
Уровень 1
11 января, 17:59
Странный пример для самопроверки. В методы с аргументами Set и HashSet вообще нельзя передать аргумент типа Collection. Если удалить метод принимающий Collection, то код вообще не скомпилируется.
wokku
Уровень 51
5 июля 2023, 09:46
Раннее связывание (Early Binding) также известно как статическое связывание. В этом случае компилятор заранее определяет, какой метод будет вызван. Это происходит на этапе компиляции, поэтому также называется статическим связыванием. Раннее связывание используется, когда методы не переопределены.
public class Animal {
    public void sound() {
        System.out.println("This is Animal sound");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.sound(); // Раннее связывание
    }
}
В этом случае, когда мы вызываем метод sound() для объекта animal, компилятор заранее знает, какую реализацию метода использовать, и связывает вызов с реализацией из класса Animal. Позднее связывание (Late Binding) также известно как динамическое связывание. Этот процесс происходит во время выполнения программы, когда точная реализация метода определяется динамически. Позднее связывание используется в Java для методов, которые были переопределены в подклассах (override).
public class Animal {
    public void sound() {
        System.out.println("This is Animal sound");
    }
}

public class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("This is Dog sound");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.sound(); // Позднее связывание
    }
}
Здесь, когда мы вызываем метод sound() для объекта animal, который ссылается на объект типа Dog, конкретная реализация метода определяется во время выполнения. Несмотря на то что тип ссылки — Animal, вызывается переопределённый метод из класса Dog.
Suzuya Jūzō
Уровень 44
22 апреля 2023, 08:12
import java.util.*;
public class StaticBinding {
    public static void main(String[] args) {
        Collection collection = new HashSet();
        //print(collection);
        /*
        Если методы static - output: Collection
        Статические или / и перегруженные методы связываются статически т.е.
        во время компиляции и по ТИПУ ПЕРЕМЕННОЙ,
        т.е. "collection" относится к Collection.
         */

        new StaticBinding().print(collection);
        /*
        Если методы не static - output: Collection
        Почему? Здесь тоже самое - нестатические методы перегружены,
        а соответственно действует ранее связывание,
        т.е. "collection" относится к Collection.
        */
    }
    public void print(Collection collection) {
        System.out.println("Collection");
    }
    public void print(Set set) {
        System.out.println("Set");
    }
    public void print(HashSet hashSet) {
        System.out.println("HashSet");
    }
}
И еще: Полиморфизм поддерживается только для обычных вызовов методов. Например, прямое обращение к полю будет обработано на стадии компиляции (по типу переменной).
partiec
Уровень 33
15 апреля 2023, 17:58
Статью надо еще раз перевести - с неоправданно-заумного на ̶п̶а̶ц̶а̶н̶с̶к̶и̶й̶ обычный русский.
Arthur Yasak (Patient)
Уровень 2
1 февраля 2023, 15:52
Collection в ответе?
Alexander Minaev
Уровень 27
9 мая 2023, 09:25
да
30 августа 2022, 12:22
"В этой программе вы увидите, что привязка виртуальных методов не происходит во время компиляции при помощи статического связывания, поскольку в этом случае вызывался бы метод из суперкласса, как происходит со статическими методами, которые связываются рано. Если будет вызван метод из подкласса, то для связывания функции использовался конкретный объект во время выполнения, а, следовательно, для связывания виртуальных функций используется динамическое связывание." Вот подобная формулировка это одна из самых основных причин, почему изучать надо на английском, так как простые вещи русские переводчики могут завернуть в инверсионные макароны с кучей сложноподчиненных предложений, объясняя результат кода до самого кода.
Yaroslav Kisly
Уровень 51
29 марта 2023, 16:35
Согласен, формулировка просто мрак!
Митяй Митяич
Уровень 24
30 июня 2022, 07:58
Я бы назвал не код, а класс или подкласс.
Matvey Tratsevsky
Уровень 25
16 июня 2022, 18:01
Вообще не понимаю чем в данном случае category отличается от premium? Методы же ничем не различаются в классах
Anonymous #2436575 Android Developer в AllPets
2 июля 2022, 07:10
Статические методы существуют в единственном экземпляре их нельзя переопределить. В данном случае для компилятора это два различных метода и без явного указания, что нам нужен именно метод подкласса, он его не увидит, так как его перекрывает родительский.
Anonymous #2436575 Android Developer в AllPets
2 июля 2022, 07:11
Проще говоря когда методы с одинаковыми сигнатурами является не статиками - они переопределяются в классах наследниках и вызываются согласно подклассу, когда такие методы статичны - всегда вызывается только родительский класс если иное не указано явно, так как компилятор при создании статика ничего не знает о подклассах, они еще не существуют.
Алексей
Уровень 12
2 марта 2023, 08:09
Храни тебя Бог! Благодаря этому комментарию понял, о чём статья
hamster🐹 ClipMaker в TikTok
9 января 2022, 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]