static и переопределение?

151
22 мая 2019, 01:10

Все началось с того, что я прочитал про static и про то, что методы помеченные static не переопределяются. Написал вот такой код, да бы убедиться:

public class Main {
    public static void main(String[] args) {
        SuperClass.run();
        SubClass.run();
    }
}
public class SubClass extends SuperClass {
    public static void run(){
        System.out.println("А SubClass и не думает оставать");
    }
}
public class SuperClass {
    public static void run(){
        System.out.println("SuperClass бежит");
    }
}

Вот что он у меня вывел:

SuperClass бежит 
А SubClass и не думает оставать. 

После этого я задумался почему везде написано что нельзя переопределять, если я переопределил? Но почувствовав себя гением, я зашел на Stackoverflow чтобы спросить совета у гуру разрабов.

Увидел на JavaRush вот такой пример:

    class Vehicle{
         public static void  kmToMiles(int km){
              System.out.println("Внутри родительского класса/статического метода");
         }
 }
    class Car extends Vehicle{
         public static void  kmToMiles(int km){
              System.out.println("Внутри дочернего класса/статического метода ");
         } 
}
    public class Demo{
       public static void main(String args[]){
          Vehicle v = new Car();
           v.kmToMiles(10);
      }
}

Вообще не понял зачем мы написали Vehicle v = new Car(); не понял что за конструкция, ибо я всегда создавал объект вот так: Vehicle v = new Vehicle();. Далее в этом же примере идёт v.kmToMiles(10); но зачем вообще было создавать объект и через объект вызывать метод если мы работаем со static и "всё" вызывает класс. Помогите пожалуйста!

Answer 1

Переопределение методов позволяет при вызове метода ориентироваться на его Run-time тип.

В то же время, вызов статических функций определяется в compile-time время.

Например:

class Base{
    public void  kmToMiles(int km){
        System.out.println("Base class");
   }
}
class Derive extends Base{
    public void  kmToMiles(int km){
        System.out.println("Derive class");
    } 
}
public class Demo{
    public static void main(String args[]){
        Base b = new Derive(); // run-time тип - Derive, compile-time тип - Base
        b.kmToMiles(10); // "Derive class"
    }
}

пример в работе

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

В случае же со статическими методами, в момент компиляции будет проверен тип переменной, и так как она объявлена с типом Base - метод будет вызван именно этого типа.

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Base b = new Derive(); // run-time тип - Derive, compile-time тип - Base
        b.kmToMiles(10); // "Base class"
    }
}
class Base{
    public static void  kmToMiles(int km){
        System.out.println("Base class");
   }
}
class Derive extends Base{
    public static void  kmToMiles(int km){
        System.out.println("Derive class");
    } 
}

пример в работе

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

Answer 2

Вот тебе некорректный пример который поможет разобраться:

SubClass subClass = null;
subClass.run()

Тут не будет ошибки NullPointerException как кто-то мог бы подумать, только предупреждение от компилятора. А код выведет "А SubClass и не думает оставать".

SubClass subClass = null;
((SupperClass) subClass).run();

А этот код выведет: "SuperClass бежит"

Все потому, что эти методы так называемы статические методы классов, компилятор превратит код выше в такие однозначные вызовы:

SubClass.run();
SupperClass.run();

А вот методы экземпляров классов можно подменять/переопределять, например подменим метод run() класса Thread на свой собственный:

new Thread() {
    @Override public void run() {
        System.out.println("Hello new thread: " + Thread.currentThread());
    }
}.start();
Thread.sleep(1000);

Логика в классе Thread не знает что мы что-то там подменили, она вызовет вроде свой метод run() а по факту будет исполнен наш.

READ ALSO
транзакция с OneToMany

транзакция с OneToMany

Я новичек, учу Hibernate и вот столкнулся с проблемойПри первом добавлении в БД, создает три таблицы из которых одна отображает связи - казалось...

141
Не получается подключить Tomcat 9 на ubuntu

Не получается подключить Tomcat 9 на ubuntu

Не могу запустить tomcat из папки opt/

121
Как сравнить choiceBox с другим choiceBox?

Как сравнить choiceBox с другим choiceBox?

Я делаю конвертер и мне нужно, чтобы я выбирал в одном choiceBox одну валюту, в другом другую и курс менялсяСами choiceBox`ы у меня есть

146