Подтвердить что ты не робот

Статические Vs. Динамическое связывание в Java

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

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

Я нашел пример статического привязки онлайн, который дает следующий пример:

public static void callEat(Animal animal) {
    System.out.println("Animal is eating");
}

public static void callEat(Dog dog) {
    System.out.println("Dog is eating");
}

public static void main(String args[])
{
    Animal a = new Dog();
    callEat(a);
}

И что это напечатает "животное есть", потому что вызов callEat использует статическое связывание, но я не уверен, почему это считается статической привязкой.

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

4b9b3361

Ответ 1

Из сообщения в блоге Javarevisited:

Вот несколько важных различий между статическим и динамическим связыванием:

  1. Статическое связывание в Java происходит во время компиляции, в то время как динамическое связывание происходит во время выполнения.
  2. private, final и static методы и переменные используют статическую привязку и связаны компилятором, тогда как виртуальные методы связаны во время выполнения на основе объекта времени выполнения.
  3. Статическая привязка использует информацию о Type (class в Java) для привязки, а динамическая привязка использует объект для разрешения привязки.
  4. Перегруженные методы связываются с использованием статического связывания, а переопределенные методы связываются с использованием динамического связывания во время выполнения.

Вот пример, который поможет вам понять как статическое, так и динамическое связывание в Java.

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

public class StaticBindingTest {  
    public static void main(String args[]) {
        Collection c = new HashSet();
        StaticBindingTest et = new StaticBindingTest();
        et.sort(c);
    }
    //overloaded method takes Collection argument
    public Collection sort(Collection c) {
        System.out.println("Inside Collection sort method");
        return c;
    }
    //another overloaded method which takes HashSet argument which is sub class
    public Collection sort(HashSet hs) {
        System.out.println("Inside HashSet sort method");
        return hs;
    }
}

Вывод: метод сортировки внутри коллекции

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

public class DynamicBindingTest {   
    public static void main(String args[]) {
        Vehicle vehicle = new Car(); //here Type is vehicle but object will be Car
        vehicle.start(); //Car start called because start() is overridden method
    }
}

class Vehicle {
    public void start() {
        System.out.println("Inside start method of Vehicle");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("Inside start method of Car");
    }
}

Вывод: метод внутреннего запуска автомобиля

Ответ 2

Подключение вызова метода к телу метода называется привязкой. Как сказал Маулик, "Static binding использует информацию типа (класс в Java) для привязки, тогда как динамическое связывание использует Object для разрешения привязки". Итак, этот код:

public class Animal {
    void eat() {
        System.out.println("animal is eating...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {
        Animal a = new Dog();
        a.eat(); // prints >> dog is eating...
    }

    @Override
    void eat() {
        System.out.println("dog is eating...");
    }
}

Результат: собака ест..., потому что использует ссылку на объект, чтобы найти, какой метод использовать. Если мы изменим приведенный выше код на это:

class Animal {
    static void eat() {
        System.out.println("animal is eating...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {

        Animal a = new Dog();
        a.eat(); // prints >> animal is eating...

    }

    static void eat() {
        System.out.println("dog is eating...");
    }
}

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

Ответ 3

Компилятор знает только, что тип "a" равен Animal; это происходит во время компиляции, из-за чего оно называется статическим привязкой (перегрузка метода). Но если это динамическое связывание, он будет вызывать метод класса Dog. Ниже приведен пример динамической привязки.

public class DynamicBindingTest {

    public static void main(String args[]) {
        Animal a= new Dog(); //here Type is Animal but object will be Dog
        a.eat();       //Dog eat called because eat() is overridden method
    }
}

class Animal {

    public void eat() {
        System.out.println("Inside eat method of Animal");
    }
}

class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println("Inside eat method of Dog");
    }
}

Вывод: Внутри есть метод собаки

Ответ 4

Существуют три основных различия между статической и динамической привязкой при разработке компиляторов и как переменные и процедуры переносятся в среду среды выполнения. Эти различия заключаются в следующем:

Статическая привязка. В статической привязке обсуждаются три следующие проблемы:

  • Определение процедуры

  • Объявление имени (переменной и т.д.)

  • Область декларации

Динамическое связывание. Три проблемы, возникающие в динамической привязке, следующие:

  • Активация процедуры

  • Связывание имени

  • Срок службы привязки

Ответ 5

Давайте обсудим разницу между статической и динамической привязкой в ​​Java.

Статическое связывание происходит во время компиляции, тогда как динамическое связывание происходит во время выполнения.

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

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

Ответ 6

С помощью статического метода в родительском и дочернем классах: статическое связывание

public class test1 {   
    public static void main(String args[]) {
        parent pc = new child(); 
        pc.start(); 
    }
}

class parent {
    static public void start() {
        System.out.println("Inside start method of parent");
    }
}

class child extends parent {

    static public void start() {
        System.out.println("Inside start method of child");
    }
}

// Output => Inside start method of parent

Динамическое связывание:

public class test1 {   
    public static void main(String args[]) {
        parent pc = new child();
        pc.start(); 
    }
}

class parent {
   public void start() {
        System.out.println("Inside start method of parent");
    }
}

class child extends parent {

   public void start() {
        System.out.println("Inside start method of child");
    }
}

// Output => Inside start method of child

Ответ 7

Ну, чтобы понять, как на самом деле работает статическое и динамическое связывание? или как они идентифицируются компилятором и JVM?

Давайте рассмотрим пример ниже, где Mammal является родительским классом, у которого есть метод speak() а класс Human расширяет Mammal, переопределяет метод speak() и затем снова перегружает его с помощью speak(String language).

public class OverridingInternalExample {

    private static class Mammal {
        public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); }
    }

    private static class Human extends Mammal {

        @Override
        public void speak() { System.out.println("Hello"); }

        // Valid overload of speak
        public void speak(String language) {
            if (language.equals("Hindi")) System.out.println("Namaste");
            else System.out.println("Hello");
        }

        @Override
        public String toString() { return "Human Class"; }

    }

    //  Code below contains the output and bytecode of the method calls
    public static void main(String[] args) {
        Mammal anyMammal = new Mammal();
        anyMammal.speak();  // Output - ohlllalalalalalaoaoaoa
        // 10: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V

        Mammal humanMammal = new Human();
        humanMammal.speak(); // Output - Hello
        // 23: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V

        Human human = new Human();
        human.speak(); // Output - Hello
        // 36: invokevirtual #7 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:()V

        human.speak("Hindi"); // Output - Namaste
        // 42: invokevirtual #9 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:(Ljava/lang/String;)V
    }
}

Когда мы скомпилируем вышеприведенный код и попробуем посмотреть байт-код с помощью javap -verbose OverridingInternalExample, мы увидим, что компилятор генерирует таблицу констант, где он присваивает целочисленные коды каждому вызову метода и байтовому коду для программы, которую я извлек и включил в самой программе (см. комментарии ниже при каждом вызове метода)

Program Bytecode

Из приведенного выше кода видно, что байт-коды humanMammal.speak(), human.speak() и human.speak("Hindi") совершенно разные (invokevirtual #4, invokevirtual #7, invokevirtual #9), потому что Компилятор может различать их на основе списка аргументов и ссылки на класс. Поскольку все это разрешается статически во время компиляции, поэтому перегрузка метода известна как статический полиморфизм или статическое связывание.

Но байт-код для anyMammal.speak() и humanMammal.speak() одинаков (invokevirtual #4), потому что согласно компилятору оба метода вызываются по ссылке Mammal.

Итак, теперь возникает вопрос, если оба вызова метода имеют один и тот же байт-код, тогда как JVM узнает, какой метод вызвать?

Ну, ответ скрыт в самом байт-коде, и это invokevirtual команд invokevirtual. JVM использует инструкцию invokevirtual для вызова Java-эквивалента виртуальных методов C++. В C++, если мы хотим переопределить один метод в другом классе, нам нужно объявить его как виртуальный, но в Java все методы по умолчанию являются виртуальными, потому что мы можем переопределить каждый метод в дочернем классе (кроме закрытых, конечных и статических методов).).

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

  1. Указатель на таблицу, которая снова содержит методы объекта и указатель на объект Class. например, [говорить(), говорить (String) объект класса]
  2. Указатель на память, выделенную в куче для данных этих объектов, например, значений переменных экземпляра.

Таким образом, все ссылки на объекты косвенно содержат ссылку на таблицу, которая содержит все ссылки на методы этого объекта. Java заимствовал эту концепцию из C++, и эта таблица называется виртуальной таблицей (vtable).

Vtable - это структура типа массива, которая содержит имена виртуальных методов и их ссылки на индексы массива. JVM создает только одну виртуальную таблицу на класс, когда загружает класс в память.

Поэтому всякий раз, когда JVM сталкивается с invokevirtual команд invokevirtual, он проверяет invokevirtual таблицу этого класса на предмет ссылки на метод и вызывает конкретный метод, который в нашем случае является методом из объекта, а не ссылкой.

Поскольку все это разрешается только во время выполнения и во время выполнения, JVM узнает, какой метод вызывать, поэтому переопределение метода называется динамическим полиморфизмом или просто полиморфизмом или динамическим связыванием.

Вы можете прочитать его более подробно в моей статье Как JVM обрабатывает перегрузку и переопределение метода внутри системы.

Ответ 8

Потому что компилятор знает привязку во время компиляции. Например, если вы вызываете метод на интерфейсе, то компилятор не может знать, и привязка разрешается во время выполнения, потому что фактический объект, имеющий метод, вызываемый на нем, может быть одним из нескольких. Поэтому это время выполнения или динамическая привязка.

Ваш вызов привязан к классу Animal во время компиляции, потому что вы указали тип. Если вы передадите эту переменную в другой метод где-то в другом месте, никто не узнает (кроме вас, потому что вы ее написали), каков будет фактический класс. Единственный ключ - объявленный тип Animal.