От какого класса наследуются все классы java
Перейти к содержимому

От какого класса наследуются все классы java

  • автор:

Наследование

Для всех классов в Kotlin родительским суперклассом является класс Any . Он также является родительским классом для любого класса, в котором не указан какой-либо другой родительский класс.

class Example // Неявно наследуется от Any 

У Any есть три метода: equals() , hashCode() и toString() . Эти методы определены для всех классов в Kotlin.

По умолчанию все классы в Kotlin имеют статус final, который блокирует возможность наследования. Чтобы сделать класс наследуемым, его нужно пометить ключевым словом open .

open class Base // Класс открыт для наследования 

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

open class Base(p: Int) class Derived(p: Int) : Base(p) 

Если у класса есть основной конструктор, базовый тип может (и должен) быть проинициализирован там же, с использованием параметров основного конструктора.

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

class MyView : View

Переопределение методов класса

Kotlin требует явно указывать модификаторы и для членов, которые могут быть переопределены, и для самого переопределения.

open class Shape < open fun draw() < /*. */ >fun fill() < /*. */ >> class Circle() : Shape() < override fun draw() < /*. */ >> 

Для Circle.draw() необходим модификатор override . В случае её отсутствия компилятор выдаст ошибку. Если у функции типа Shape.fill() нет модификатора open , объявление метода с такой же сигнатурой в производном классе невозможно, с override или без. Модификатор open не действует при добавлении к членам final класса (т.е. класса без модификатора open ).

Член класса, помеченный override , является сам по себе open, т.е. он может быть переопределён в производных классах. Если вы хотите запретить возможность переопределения такого члена, используйте final .

open class Rectangle() : Shape() < final override fun draw() < /*. */ >> 

Переопределение свойств класса

Переопределение свойств работает также, как и переопределение методов; все свойства, унаследованные от суперкласса, должны быть помечены ключевым словом override , а также должны иметь совместимый тип. Каждое объявленное свойство может быть переопределено свойством с инициализацией или свойством с get -методом.

open class Shape < open val vertexCount: Int = 0 >class Rectangle : Shape()

Вы также можете переопределить свойство val свойством var , но не наоборот. Это разрешено, поскольку свойство val объявляет get -метод, а при переопределении его как var дополнительно объявляется set -метод в производном классе.

Обратите внимание, что ключевое слово override может быть использовано в основном конструкторе класса как часть объявления свойства.

interface Shape < val vertexCount: Int >class Rectangle(override val vertexCount: Int = 4) : Shape // Всегда имеет 4 вершины class Polygon : Shape < override var vertexCount: Int = 0 // Может быть установлено любое количество >

Порядок инициализации производного класса

При создании нового экземпляра класса в первую очередь выполняется инициализация базового класса (этому шагу предшествует только оценка аргументов, передаваемых в конструктор базового класса) и, таким образом, происходит до запуска логики инициализации производного класса.

open class Base(val name: String) < init < println("Инициализация класса Base") >open val size: Int = name.length.also < println("Инициализация свойства size в класса Base: $it") >> class Derived( name: String, val lastName: String, ) : Base(name.replaceFirstChar < it.uppercase() >.also < println("Аргументы, переданные в конструктор класса Base: $it") >) < init < println("Инициализация класса Derived") >override val size: Int = (super.size + lastName.length).also < println("Инициализация свойства size в классе Derived: $it") >> fun main()

Это означает, что свойства, объявленные или переопределенные в производном классе, не инициализированы к моменту вызова конструктора базового класса. Если какое-либо из этих свойств используется в логике инициализации базового класса (прямо или косвенно через другую переопределенную open реализацию члена класса), это может привести к некорректному поведению или сбою во время выполнения. Поэтому при разработке базового класса следует избегать использования членов с ключевым словом open в конструкторах, инициализации свойств и блоков инициализации ( init ).

Вызов функций и свойств суперкласса

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

open class Rectangle < open fun draw() < println("Рисование прямоугольника") >val borderColor: String get() = "black" > class FilledRectangle : Rectangle() < override fun draw() < super.draw() println("Заполнение прямоугольника") >val fillColor: String get() = super.borderColor > 

Во внутреннем классе доступ к суперклассу внешнего класса осуществляется при помощи ключевого слова super , за которым следует имя внешнего класса: super@Outer .

class FilledRectangle: Rectangle() < override fun draw() < val filler = Filler() filler.drawAndFill() >inner class Filler < fun fill() < println("Filling") >fun drawAndFill() < super@FilledRectangle.draw() // Вызывает реализацию функции draw() класса Rectangle fill() println("Нарисованный прямоугольник заполнен $цветом") // Используется реализация get()-метода свойства borderColor в классе > > > 

Правила переопределения

В Kotlin правила наследования реализации определены следующим образом: если класс наследует многочисленные реализации одного и того члена от ближайших родительских классов, он должен переопределить этот член и обеспечить свою собственную реализацию (возможно, используя одну из унаследованных).

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

open class Rectangle < open fun draw() < /* . */ >> interface Polygon < fun draw() < /* . */ >// члены интерфейса открыты ('open') по умолчанию > class Square() : Rectangle(), Polygon < // Компилятор требует, чтобы функция draw() была переопределена: override fun draw() < super.draw() // вызов Rectangle.draw() super.draw() // вызов Polygon.draw() > > 

Это нормально, наследоваться одновременно от Rectangle и Polygon , но так как у каждого из них есть своя реализация функции draw() , мы должны переопределить draw() в Square и обеспечить нашу собственную реализацию этого метода для устранения получившейся неоднозначности.

© 2015—2024 Open Source Community

Наследование Java с примерами

Наследование Java дает возможность одному классу наследовать свойства другого класса. Также называется расширением класса .

Когда один класс наследуется от другого класса, эти два класса принимают определенные роли. Подкласс расширяет суперкласс. Или подкласс наследует от суперкласса. Подкласс – это специализация суперкласса, а суперкласс – это обобщение одного или нескольких подклассов.

Как метод повторного использования кода

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

Вот диаграмма, иллюстрирующая класс с именем Vehicle, который имеет два подкласса, называемые Car и Truck.

объяснение наследования

Класс Vehicle является суперклассом легковых и грузовых автомобилей. Автомобиль и Грузовик – подклассы Автомобиля. Класс Vehicle может содержать те поля и методы, которые нужны всем транспортным средствам (например, номерной знак, владелец и т. д.), Тогда как Car и Truck могут содержать поля и методы, специфичные для легковых и грузовых автомобилей.

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

Например, вам нужно ссылаться на объекты Car и Truck как объекты Vehicle? Вам нужно обрабатывать объекты Car и Truck одинаково? Тогда имеет смысл иметь общий суперкласс Vehicle для двух классов. Если вы никогда не обрабатываете объекты Car и Truck одним и тем же способом, нет смысла иметь для них общий суперкласс, кроме, возможно, совместного использования кода между ними (чтобы избежать написания дублирующего кода).

Классовые иерархии

Суперклассы и подклассы образуют структуру наследования. На вершине иерархии классов суперклассы. В нижней части иерархии – подклассы. Иерархия может иметь несколько уровней, то есть несколько уровней суперклассов и подклассов. Подкласс сам может быть суперклассом других подклассов и т. д.

Основы

Когда класс наследует от суперкласса, он наследует части методов и полей суперкласса. Подкласс также может переопределять (переопределять) унаследованные методы. Поля не могут быть переопределены, но могут быть «затенены» в подклассах. Как все это работает, рассказывается далее в этом тексте.

Что унаследовано?

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

Поля и методы с модификаторами доступа по умолчанию (пакет) могут быть доступны для подклассов, только если он находится в том же пакете, что и суперкласс. На частные поля и методы суперкласса никогда нельзя ссылаться непосредственно, только через методы, достижимые из подкласса (например, методы по умолчанию (пакет), защищенные и публичные).

Конструкторы не наследуются подклассами, но конструктор подкласса должен вызывать конструктор в суперклассе.

Единичное наследование

Механизм наследования позволяет наследовать класс только от одного суперкласса (единичное наследование). В некоторых языках программирования, таких как C ++, подкласс может наследоваться от нескольких суперклассов (множественное).

Так как множественный вариант может создать некоторые странные проблемы, например, суперклассы содержат методы с одинаковыми именами и параметрами, он был исключен в Java.

Объявление

Объявляется с использованием ключевого слова extends:

public class Vehicle < protected String licensePlate = null; public void setLicensePlate(String license) < this.licensePlate = license; >>
public class Car extends Vehicle < int numberOfSeats = 0; public String getNumberOfSeats() < return this.numberOfSeats; >>

Класс Car в этом примере расширяет класс Vehicle, то есть Car наследуется от Vehicle. Поскольку Car расширяет Vehicle, защищенное поле licensePlate из Vehicle наследуется Car. Когда licensePlate наследуется, оно становится доступным внутри экземпляра Car.

В поле licensePlate на самом деле не ссылаются из класса Car в приведенном выше коде, но можно, если мы захотим:

public class Car extends Vehicle < int numberOfSeats = 0; public String getNumberOfSeats() < return this.numberOfSeats; >public String getLicensePlate() < return this.licensePlate; >>

Ссылка происходит внутри метода getLicensePlate(). Во многих случаях имело бы смысл поместить этот метод в класс Vehicle, где находится поле licensePlate.

Приведение типов

Можно ссылаться на подкласс как на экземпляр одного из его суперклассов. Например, используя определения класса из примера в предыдущем разделе, можно ссылаться на экземпляр класса Car как на экземпляр класса Vehicle. Так как Car расширяет (наследует) Vehicle, он также называется Vehicle.

Вот пример кода Java:

Car car = new Car(); Vehicle vehicle = car;
  1. Сначала создается экземпляр автомобиля.
  2. Экземпляр Car присваивается переменной типа Vehicle.
  3. Теперь переменная Vehicle (ссылка) указывает на экземпляр Car. Это возможно, потому что Car наследуется от Vehicle.

Как видите, можно использовать экземпляр некоторого подкласса, как если бы он был экземпляром его суперкласса. Таким образом, вам не нужно точно знать, к какому подклассу относится объект. Например, вы можете рассматривать экземпляры Грузовика и Автомобиля как экземпляры Транспортного средства.

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

Upcasting и Downcasting

Вы всегда можете привести объект подкласса к одному из его суперклассов, либо из типа суперкласса к типу подкласса, но только если объект действительно является экземпляром этого подкласса (или экземпляром подкласса этого подкласса). Таким образом, этот пример downcasting действителен:

Car car = new Car(); // upcast to Vehicle Vehicle vehicle = car; // downcast to car again Car car2 =(Car) vehicle;

Однако следующий приведенный ниже пример недопустим. Компилятор примет его, но во время выполнения, выдаст исключение ClassCastException.

Truck truck = new Truck(); // upcast to Vehicle Vehicle vehicle = truck; // downcast to car again Car car =(Car) vehicle;

Объект «Грузовик» может быть передан объекту «Автомобиль», но позже он не может быть передан объекту «Автомобиль». Это приведет к исключению ClassCastException.

Переопределяющие методы

В подклассе вы можете переопределить методы, определенные в суперклассе:

public class Vehicle < String licensePlate = null; public void setLicensePlate(String licensePlate) < this.licensePlate = licensePlate; >>
public class Car extends Vehicle < public void setLicensePlate(String license) < this.licensePlate = license.toLowerCase(); >>

Обратите внимание, как и класс Vehicle, и класс Car определяют метод setLicensePlate(). Теперь каждый раз, когда setLicensePlate() вызывается для объекта Car, вызывается метод, определенный в классе Car. Метод в суперклассе игнорируется.

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

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

Аннотация @override

Если вы переопределяете метод в подклассе, и метод внезапно удаляется или переименовывается или его сигнатура изменяется в суперклассе, метод в подклассе больше не переопределяет метод в суперклассе. Было бы хорошо, если бы компилятор мог сказать вам, что переопределяемый метод больше не переопределяет метод в суперклассе, верно?

Для этого и нужна аннотация @ override. Вы размещаете ее над методом, который переопределяет метод в суперклассе:

public class Car extends Vehicle < @Override public void setLicensePlate(String license) < this.licensePlate = license.toLowerCase(); >>

Вызов методов суперкласса

Если вы переопределяете метод в подклассе, но по-прежнему должны вызывать метод, определенный в суперклассе, используйте ссылку super, например:

public class Car extends Vehicle < public void setLicensePlate(String license) < super.setLicensePlate(license); >>

В приведенном выше примере кода метод setLicensePlate() в классе Car вызывает метод setLicensePlate() в классе Vehicle.

Вы можете вызывать реализации суперкласса из любого метода в подклассе, как описано выше. Он не должен быть из самого переопределенного метода. Например, вы могли бы также вызвать super.setLicensePlate() из метода в классе Car с именем updateLicensePlate(), который не переопределяет метод setLicensePlate().

Пример инструкции

Java содержит инструкцию с именем instanceof. Она может определить, является ли данный объект экземпляром некоторого класса:

Car car = new Car(); boolean isCar = car instanceof Car;

После выполнения этого кода переменная isCar будет содержать значение true.

Инструкция instanceof также может использоваться для определения того, является ли объект экземпляром суперкласса своего класса. Вот пример, который проверяет, является ли объект Car экземпляром Vehicle:

Car car = new Car(); boolean isVehicle = car instanceof Vehicle;

Предполагая, что класс Car расширяет (наследует от) класс Vehicle, переменная isVehicle будет содержать значение true после выполнения этого кода. Объект Car также является объектом Vehicle, поскольку Car является подклассом Vehicle.

Как видите, инструкция instanceof может использоваться для изучения иерархии наследования. Тип переменной, используемый с ней, не влияет на ее результат. Посмотрите на этот пример:

Car car = new Car(); Vehicle vehicle = car; boolean isCar = vehicle instanceof Car;

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

Вот тот же пример, но с использованием объекта Truck вместо объекта Car:

Truck truck = new Truck(); Vehicle vehicle = truck; boolean isCar = vehicle instanceof Car;

После выполнения этого кода isCar будет содержать значение false. Объект Truck не является объектом Car.

Как наследуются

Поля

Как упоминалось ранее, в Java поля не могут быть переопределены в подклассе. Если вы определяете поле в подклассе с тем же именем, что и в суперклассе, поле в подклассе будет скрывать поле в суперклассе. Если подкласс попытается получить доступ к полю, он получит доступ к полю в подклассе.

Однако, если подкласс вызывает метод в суперклассе, который обращается к полю с тем же именем, что и в подклассе, это поле в суперклассе, к которому осуществляется доступ.

Вот пример, который иллюстрирует, как поля в подклассах скрывают поля в суперклассах:

public class Vehicle < String licensePlate = null; public void setLicensePlate(String licensePlate) < this.licensePlate = licensePlate; >public String getLicensePlate() < return licensePlate; >>
public class Car extends Vehicle < protected String licensePlate = null; @Override public void setLicensePlate(String license) < super.setLicensePlate(license); >@Override public String getLicensePlate() < return super.getLicensePlate(); >public void updateLicensePlate(String license) < this.licensePlate = license; >>

Обратите внимание, как для обоих классов определено поле licensePlate.

И класс Vehicle, и класс Car имеют методы setLicensePlate() и getLicensePlate(). Методы в классе Car вызывают соответствующие методы в классе Vehicle. В результате оба набора методов получают доступ к полю licensePlate в классе Vehicle.

Однако метод updateLicensePlate() в классе Car напрямую обращается к полю licensePlate. Таким образом, он получает доступ к полю licensePlate класса Car. Следовательно, вы не получите тот же результат, если вызовете setLicensePlate(), как при вызове метода updateLicense().

Посмотрите на следующие строки кода:

Car car = new Car(); car.setLicensePlate("123"); car.updateLicensePlate("abc"); System.out.println("license plate: " + car.getLicensePlate());

Этот код распечатает текст 123.

Метод updateLicensePlate() устанавливает значение номерного знака в поле licensePlate в классе Car. Однако метод getLicensePlate() возвращает значение поля licensePlate в классе Vehicle. Следовательно, значение 123, которое устанавливается как значение для поля licensePlate в классе Vehicle с помощью метода setLicensePlate(), является тем, что выводится на печать.

Конструкторы

Механизм наследования не включает конструкторы. Другими словами, конструкторы суперкласса не наследуются подклассами. Подклассы могут по-прежнему вызывать конструкторы в суперклассе, используя конструкцию super().

Фактически, конструктор подкласса должен вызывать один из конструкторов в суперклассе как самое первое действие внутри своего тела. Вот как это выглядит:

public class Vehicle < public Vehicle() < >>
public class Car extends Vehicle < public Car() < super(); //здесь выступают другие инициализации >>

Обратите внимание на вызов super() внутри конструктора Car. Этот вызов super() выполняет конструктор в классе Vehicle.

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

Если класс не имеет какого-либо явного конструктора, компилятор вставляет неявный без аргументов. Таким образом, класс всегда имеет конструктор.

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

public class Vehicle

Если конструктор явно не вызывает конструктор в суперклассе, компилятор вставляет неявный вызов конструктора no-arg в суперклассе. Это означает, что следующая версия класса Car фактически эквивалентна версии, показанной ранее:

public class Car extends Vehicle < public Car() < >>

Фактически, поскольку конструктор теперь пуст, мы могли бы опустить его, и компилятор вставил бы его и неявный вызов конструктора no-arg в суперклассе. Вот как тогда будут выглядеть два класса:

public class Vehicle
public class Car extends Vehicle

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

Если бы класс Vehicle не имел конструктора без аргументов, но имел другой, который принимает параметры, компилятор жаловался бы. Класс Car затем должен был бы объявить конструктор, а внутри него вызвать конструктор в классе Vehicle.

Вложенные классы

Те же правила наследования применяются к вложенным классам. Если объявлены закрытыми, не наследуются. Вложенные классы с модификатором доступа по умолчанию (пакет) доступны только для подклассов, если подкласс находится в том же пакете, что и суперкласс. С модификатором защищенного или открытого доступа всегда наследуются подклассами.

class MyClass < class MyNestedClass < >>
public class MySubclass extends MyClass < public static void main(String[] args) < MySubclass subclass = new MySubclass(); MyNestedClass nested = subclass.new MyNestedClass(); >>

Обратите внимание, как можно создать экземпляр вложенного класса MyNestedClass, который определен в суперклассе(MyClass) посредством ссылки на подкласс(MySubclass).

Финальные классы

Класс может быть объявлен окончательным(final):

public final class MyClass

Последний класс не может быть продлен. Другими словами, вы не можете наследовать от финального класса.

Абстрактные классы

Класс может быть объявлен абстрактным, который не содержит полную реализацию того, что должен делать. Таким образом, это не может быть реализовано. Другими словами, вы не можете создавать объекты абстрактного класса.

Они предназначены для расширения / создания полной реализации. Таким образом, вполне возможно расширить абстрактный класс. Правила наследования такие же для них, как и для неабстрактных классов.

От какого класса наследуются все классы java

Исходя из схемы выше, реализуем 4 класса: Pixel , Snake , Food , Bonus .

Иллюстрация проблемы множественного наследования

Получаем дилемму: если объект класса Enemy вызывает метод canBeEaten () , определенный в классе Pixel и при этом не переопределенный в классе Enemy , а классы Snake и Food определили этот метод каждый по-своему. Возникает вопрос: от какого класса должен наследовать свое поведение экземпляр класса Enemy : Snake или Food ? Такая конфигурация наследования называется «проблемой множественного наследования».

В Java для решения этой проблемы применяются интерфейсы.

Что такое интерфейс?

Интерфейс – это объект языка Java схожий по своей сути с абстрактным классом. Для определения интерфейса в языке есть отдельное ключевое слово interface.

Интерфейс не обязан иметь в себе сигнатуры методов. Пустые интерфейсы называются интерфейсами-маркерами. Классическим примером интерфейса-маркера можно считать Cloneable из пакета java.lang .

Отличия абстрактного класса от интерфейса

Если интерфейс так похож на абстрактный класс, зачем тогда в языке присутствуют обе эти конструкции? Для этого есть несколько причин:

  1. Идеологическая. В парадигме ООП все классы – существительные. Абстрактный класс не исключение. Следовательно, он описывает общие свойства объектов. Интерфейс же — это контракт. Он описывает общие действия доступные всем классам, реализующим его. Интерфейс не располагает реализацией – он лишь гарантирует наличие действия у объекта. Именно поэтому в некоторых нотациях принято именовать интерфейсы как Noun-able.
  2. Практическая. Класс может расширять только один класс и имплементировать множество интерфейсов. С помощью интерфейса можно решить проблему множественного наследования.

Пример интерфейса

Создадим интерфейс, определяющий возможность перемещаться, и имплементируем его:

Схема отношений классов без проблемы множественного наследования

Проведем рефакторинг в соответствии со схемой. Вынесем из абстрактного класса метод canBeEaten () в отдельный интерфейс и имплементируем его в абстрактном классе. Таким образом, мы реализовали контракт о том, что все классы дочерние от абстрактного обязаны у себя реализовать метод canBeEaten () что решает проблему выбора родителя для неопределенного метода и, как следствие, саму проблему ромба.

interface Eatable < boolean canBeEaten(); >abstract class Pixel implements Eatable < > 

Добавим также классу Snake интерфейс-маркер, чтобы отличать «живые» объекты. Например, змеек и, возможно, других персонажей от еды.

interface Alivable<> class Snake extends Pixel implements Movable, Alivable < > 

Таким образом, видно, что класс может расширять только один другой класс, но имплементировать множество интерфейсов.

​​Ссылки

Тема данной статьи достаточно обширна, и в ней остался не раскрыт ряд вопросов: применение интерфейсов маркеров или примесей, однако я надеюсь, что мне удалось осветить основные аспекты наследования в Java.

Подведем итог. В этой статье мы:

  1. Научились создавать интерфейсы, абстрактные классы и методы.
  2. Поняли, чем отличается абстрактный класс от интерфейса.
  3. Познакомились с проблемой множественного наследования, а также методами ее решения.

Материалы по теме

  • ☕ ТОП-20 бесплатных учебных курсов по Java для новичков
  • ��‍��️ Стань востребованным Java-разработчиком вместе с Kata Academy
  • ☕ Разбираемся, почему в Java утекает память несмотря на сборщик мусора

Какие два класса не наследуются от Object?

azerphoenix

Ответ действительно быстрее находится поисковиком.
1) Класс Object не наследуется от себя же, так как он является суперклассом. От него наследуются другие классы.

А точно ли вопрос звучал именно так?

Какие два класса не наследуются от Object?

Просто например, тип данных enum тоже не наследуется от Object, потому что наследуется от Enum.

Ответ написан более двух лет назад

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *