Привет всем, я Сяо Цай, Сяо Цай, который хочет быть Цай Буцаем в интернет-индустрии. Она может быть мягкой или жесткой, как она мягкая, а белая проституция жесткая!Черт~ Не забудьте поставить мне тройку после прочтения!
Эта статья в основном знакомит
Java中多态的用法
При необходимости вы можете обратиться к
Если это поможет, не забудьтекак❥
Официальный аккаунт WeChat открыт,Хорошая едаДля тех, кто не обратил внимания, не забудьте обратить внимание!
Сегодня пятница, а я как обычно пришел в компанию. Сядь за свой стол и включи компьютер,"又是搬砖的一天"
. Подумай еще,"熟练"
Я открыл Идею, посмотрел сегодняшние потребности и набрал код. Эй, кто написал эти коды, как они появились в моем коде, и они все еще ожидают отправки, я помню, что я их не писал, поэтому я посмотрел на это с большим интересом:
Разве это не полиморфно?» Тот, кто писал тест на моем компьютере, не мог не чувствовать себя странно.
"你看看这会输出什么结果?"
Взрыв голоса пришел сзади. Потому что я думал о результате вывода, мне не волнуло источник звука. Я продолжал смотреть на код и пришел к выводу:
polygon() before cal()
square.cal(), border = 2
polygon() after cal()
square.square(), border = 4
Я думаю в душе: По крайней мере, Java-разработчик — это хорошо, хотя обычно в движении, некоторые базовые навыки еще есть. Не могу не гордиться ~
"这就是你的答案吗?看来你也不咋的"
Голос вдруг прозвучал снова, на этот раз я не спокоен, Нима! Я также подумал об этом ответе в уме, кто может его увидеть, и это заставляет людей хотеть выполнить набор поз Awei 18."你是谁啊?"
Он повернул голову с оттенком сомнения и гнева. почему никто? Я не выдерживаю своих сомнений,"小菜,醒醒,你怎么上班时间就睡着了"
Заснуть на работе? Я открыл глаза и посмотрел на окрестности, это оказалось сном, я вздохнул с облегчением. Я увидел начальника отделения, стоящего передо мной, спящего в рабочее время.Тебе нездоровится что ли? Вчера я написал кучу ошибок и не исправил их, а сегодня я представил некоторые беспорядочные вещи. Я думаю, что ваша производительность в этом месяце не такая, как вы хотите, и, исходя из вашей производительности, я должен начать думать об этом для отдела. .
"我不是,我没有,我也不知道怎么就睡着了,你听我解释啊!"
Я еще не успел произнести это слово.心里的花我要带你回家,在那深夜酒吧哪管它是真是假,请你尽情摇摆忘记钟意的他,你是最迷人噶,你知道吗
, зазвенел будильник, я встала вдруг, спина была слегка мокрая, лоб слегка вспотел, проверила телефон, суббота, 8:30, оказалось, что это сон!
Странно, как тебе может присниться такой странный сон, это слишком страшно. Затем я подумал о части кода во сне: мой результат неверен? По памяти набрал еще раз на компе, и результаты такие:
/*
polygon() before cal()
square.cal(), border = 0
polygon() after cal()
square.square(), border = 4
*/
square.cal(), border
Результат на самом деле 0, а не 2. Я теперь даже не полиморфен? Перед компьютером и мобильным телефоном вы не знаете, правильно ли вы придумали ответ! Независимо от того, так ли это, давайте рассмотрим полиморфизм с помощью Xiaocai!
У некоторых друзей может быть больше, чем несколько сомненийsquare.cal(), border
Результат 0, а почему бы и нет square.square(), border = 4
Сомневается первый вывод. Тогда у нас есть сомнения, давайте вместе!
полиморфизм
В объектно-ориентированных языках программирования полиморфизм является третьей фундаментальной особенностью после абстракции данных и наследования.
Полиморфные не только улучшают организационную структуру и читабельность кода, но и позволяют создавать масштабируемые программы. Эффект заключается в устранении полиморфности между типами耦合关系
.
1. Преобразование вверх
в соответствии с里氏代换原则
: Там, где может появиться базовый класс, должен появиться подкласс.
Объект может использоваться либо как его собственный тип, либо как его базовый тип. И эта практика обращения со ссылкой на объект как со ссылкой на его базовый тип называется -向上转型
.因为父类在子类的上方,子类要引用父类,因此称为向上转型
.
public class Animal {
void eat() {
System.out.println("Animal eat()");
}
}
class Monkey extends Animal {
void eat() {
System.out.println(" Monkey eat()");
}
}
class test {
public static void start(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
Monkey monkey = new Monkey();
start(monkey);
}
}
/* OUTPUT:
Monkey eat()
*/
вышесказанноеtest
в классеstart()
метод получаетAnimal
, который, естественно, также получает ссылки отAnimal
экспортный класс. перечислитьeat()
метод, естественно использоватьMonkey
Определенныйeat()
метод без преобразования типов. потому что изMonkey
переход доAnimal
Только уменьшить интерфейс, не болееAnimal
меньше интерфейсов.
Если провести неуместную аналогию:Имущество твоего отца будет тебе по наследству, а твое имущество останется твоим, и вообще твое имущество будет не меньше, чем твоего отца.
забыть тип объекта
существуетtest.start()
В методе передается определениеAnimal
, но проходя вMonkey
, который кажется забытымMonkey
тип объекта, то почему бы просто не поставитьtest
Методы в классе определены какvoid start(Monkey monkey)
, разве это не выглядело бы более интуитивно понятным.
Интуитивность может быть ее преимуществом, но она приносит другие проблемы:Animal
больше, чем одинMonkey
Экспортируемый класс, на этот раз приходитpig
, то необходимо ли определять метод какvoid start(Monkey monkey)
, Перегрузить очень легко, мальчик, но это слишком хлопотно. Лень — это натура разработчиков.
Так что есть多态
поколение
2. Преимущество выявлено
вызов методав центре静态绑定
и动态绑定
. Что такое привязка: связывание вызова метода с одним и тем же телом метода называется привязкой.
-
静态绑定
:Также известен какРаннее связывание.是在程序执行前进行把绑定。我们平时听到"静态"的时候,不难免想到static
Ключевые словаstatic
Переменные, измененные ключевыми словами, становятся статическими переменными, которые инициализируются перед выполнением программы.前期绑定
Это метод связывания по умолчанию в процедурных языках.Например, в языке C есть только один вызов метода, который является ранним связыванием.
наводят на размышления:
public static void start(Animal animal) {
animal.eat();
}
существуетstart()
Переданный методAnimal
ссылка на объект, если есть несколькоAnimal
экспортированный класс, затем выполнитеeat()
Как узнать, какой метод вызывается при вызове метода. если прошло前期绑定
Тогда это невозможно. Так что есть后期绑定
.
-
动态绑定
:Также известен как后期绑定
. Он связан в соответствии с типом объекта во время работы программы, поэтому его также можно вызвать运行时绑定
. И Java основана на собственном механизме позднего связывания, так что он может определить тип объекта во время выполнения, чтобы вызвать правильный метод.
резюме:
За исключением Javastatic
иfinal
За исключением модифицированных методов, все они относятся к позднему связыванию.
разумное правильно
очевидно через动态绑定
реализовать多态
разумно. Таким образом, нам нужно только передать ссылку на базовый класс при разработке интерфейса, чтобы эти коды могли правильно работать для всех производных классов базового класса.
вMonkey
,Pig
,Dog
всеAnimal
экспортный класс
Animal animal = new Monkey()
Похоже на неправильное присвоение, но через наследование,Monkey
этоAnimal
Если мы позвонимanimal.eat()
метод, друзья, не разбирающиеся в полиморфизме, часто ошибочно думают, что вызовAnimal
изeat()
метод, но в конце концов он называетсяMonkey
мой собственныйeat()
метод.
Animal
Как базовый класс, его роль состоит в том, чтобы установить общедоступный интерфейс для экспортируемого класса. все изAnimal
Наследование от производного класса может иметь собственное уникальное поведение.
Масштабируемость
Благодаря полиморфизму мы можем добавлять в систему столько новых типов, сколько захотим, не перегружая ее.void start(Animal animal)
метод.
В хорошо разработанной программе ООП большинство или все методы будут следоватьstart()
Модель метода, который работает только с интерфейсом базового класса, такая программа имеетМасштабируемостьМы можем наследовать новые типы данных от общего базового класса, чтобы добавить некоторую функциональность, манипулирование этим базовым интерфейсом не потребует никаких изменений, которые можно применить к новому классу.
Вышел из строя?
Давайте сначала рассмотрим модификаторы разрешений:
сфера | текущий класс | с пакетом | потомки | другие пакеты |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
- общедоступный: виден всем классам
- защищенный: этот класс, этот пакет и подклассы видны
- по умолчанию: этот класс и этот пакет видны
- частный: этот класс виден
Неудача частных методов:
После просмотра давайте посмотрим на набор кода:
public class PrivateScope {
private void f() {
System.out.println("PrivateScope f()");
}
public static void main(String[] args) {
PrivateScope p = new PrivateOverride();
p.f();
}
}
class PrivateOverride extends PrivateScope {
private void f() {
System.out.println("PrivateOverride f()");
}
}
/* OUTPUT
PrivateScope f()
*/
Вы чувствуете себя немного странно, почему это называется в это время?f()
определяется в базовом классе, а не так, как описано выше, через动态绑定
, который вызывает экспортируемый классPrivateOverride
определено вf()
. Я не знаю, будете ли вы осторожны, если обнаружите это в базовом классеf()
Модификация методаprivate. Да, это проблема,PrivateOverride
определено вf()
метод является совершенно новым методом, потому чтоprivate
Из-за этого он не виден подклассам и, естественно, не может быть перегружен.
в заключении:
только не-private
Модифицированные методы могут быть переопределены только
Когда мы пишем код через Idea, заголовок переопределенного метода может быть помечен@Override
Аннотация, если это не переопределенный метод, аннотировать@Override
Аннотация сообщит об ошибке:
Это также может быть хорошим напоминанием о том, что мы не переопределяем метод, а совершенно новый метод.
сбой, вызванный доменом:
Когда маленькие друзья увидят это, они начнут думать, что все (кромеprivate
модификация) может происходить полиморфно. Но реальность не такова.Только обычные вызовы методов могут быть полиморфными.. Вот где полиморфизм.
Давайте еще раз взглянем на следующий набор кода:
class Super {
public int field = 0;
public int getField() {
return field;
}
}
class Son extends Super {
public int field = 1;
public int getField() {
return field;
}
public int getSuperField() {
return super.field;
}
}
class FieldTest {
public static void main(String[] args) {
Super sup = new Son();
System.out.println("sup.field:" + sup.field + " sup.getField():" + sup.getField());
Son son = new Son();
System.out.println("son.field:" + son.field + " son.getField:" + son.getField() + " son.getSupField:" + son.getSuperField());
}
}
/* OUTPUT
sup.field:0 sup.getField():1
son.field:1 son.getField:1 son.getSupField:0
*/
Из приведенного выше кода мы видимsup.field
Значение вывода неSon
определены в объекте, ноSuper
определяется само собой. Это немного противоречит известному нам полиморфизму.
Не совсем, когдаSuper
объект превращается вSon
При ссылке любые операции доступа к домену будут разрешены компилятором и поэтому не являются полиморфными. В этом примере этоSuper.field
иSon.field
выделены разные места для хранения, в то время какSon
класс изSuper
класс экспортируется, поэтомуSon
фактически состоит из двухfield
домен:свой собственный+Super
из.
Хотя эта проблема представляется головной болью, мы обычно устанавливаем все домены для частных, которые не могут быть доступны напрямую и могут быть доступны только по методам вызова.
Неисправность, вызванная статическим:
Видя это, друзья должны иметь общее представление о полиморфизме, но не относитесь к этому легкомысленно, есть еще одна ситуация, когда произойдет сбой.То есть, если метод статичен, его поведение не является полиморфным.
Старые правила, давайте посмотрим на этот набор кодов:
class StaticSuper {
public static void staticTest() {
System.out.println("StaticSuper staticTest()");
}
}
class StaticSon extends StaticSuper{
public static void staticTest() {
System.out.println("StaticSon staticTest()");
}
}
class StaticTest {
public static void main(String[] args) {
StaticSuper sup = new StaticSon();
sup.staticTest();
}
}
/* OUTPUT
StaticSuper staticTest()
*/
Статические методы связаны с классами, а не с объектами
3. Конструктор и полиморфизм
Первое, что нам нужно понять, это то, что конструкторы не полиморфны, потому что на самом деле ониstatic
метод, ноstatic
Объявление является неявным.
Вернемся к загадочному коду в начале:
где вывод:
/*
polygon() before cal()
square.cal(), border = 0
polygon() after cal()
square.square(), border = 4
*/
Мы видим, что первый вывод — это базовый класс.polygon
Метод моноконики.
Это связано с тем, что конструктор базового класса всегда вызывается во время создания производного класса и постепенно привязывается к иерархии наследования, чтобы можно было вызвать конструктор каждого базового класса.
Потому что у конструктора есть особая задача: проверить, можно ли правильно сконструировать объект. Производный класс может обращаться только к своим собственным членам, но не к членам базового класса (члены базового класса обычно являются частными типами). Только конструкторы базового класса имеют право инициализировать свои собственные элементы. Поэтому должны быть вызваны все конструкторы, иначе невозможно корректно сконструировать полный объект.
Действуйте следующим образом:
- Вызовите конструктор базового класса, этот шаг будет продолжать рекурсивно, сначала для создания корня этой иерархии, затем следующего уровня класса экспорта, ..., до нижней части класса экспорта
- Начальные методы членов вызываются в порядке объявления
- вызвать экспортированный класс, чтобы построить его тело
Если провести неуместную аналогию:Ты пришел первым без отца, твой отец пришел первым без твоего деда, вот как создается постепенная восходящая связь
Полиморфное поведение внутри конструкторов
Вы когда-нибудь задумывались, что происходит, если динамически связанный метод создаваемого объекта вызывается из конструктора? Вызов динамической привязки определяется во время выполнения, поскольку объект не может узнать, принадлежит ли он классу, в котором находится метод, или классу, производному от этого класса. Если вы хотите вызвать динамически связываемый метод внутри конструктора, используется переопределенное определение этого метода. Однако, поскольку переопределенный метод вызывается до того, как объект будет полностью построен, это может привести к скрытым ошибкам, которые трудно найти.
индекс проблем:
Динамически связанный вызов метода уходит вглубь иерархии наследования, он может вызывать метод в производном классе, если мы делаем это внутри конструктора, то возможен вызов метода, и этот метод выполняет манипуляции Члены могут не были инициализированы, что обязательно приведет к катастрофе.
Чувствительные маленькие партнеры не думают о коде начала:
Результат:
/*
polygon() before cal()
square.cal(), border = 0
polygon() after cal()
square.square(), border = 4
*/
мы делаемsquare
Когда объект инициализируется, он сначалаpolygon
инициализация объекта, вpolygon
В конструкторе естьcal()
метод, в это время используется механизм динамической привязки, и вызовsquare
изcal()
, но в это времяborder
Эта переменная не была инициализирована, значение по умолчанию типа int равно 0, поэтомуsquare.cal(), border = 0
Вывод. Увидев это, вам, ребята, кажется, что вы видите небо за облаками!
Фактический процесс инициализации этого набора кодов:
- Инициализируйте пространство хранения, выделенное для объекта, двоичными нулями, прежде чем что-либо еще произойдет
- При вызове конструктора базового класса будет вызван переопределенный.
cal()
метод, из-за шага 1 значение границы равно 0 - Методы инициализации элементов вызываются в том порядке, в котором они объявлены.
- вызвать тело конструктора экспортируемого класса
Уф~ Наконец-то закончил обзор полиморфизма, к счастью, это был сон, и мою тарелку никто не нашел. Я не знаю, такой же ли ты, как Сяокай перед компьютером и мобильным телефоном.Если да, то поспешите сделать обзор с Сяокаем, чтобы другие не узнали, что вы не полиморфны!
不知道下次又会做什么样的梦~
Если вы будете усердно работать сегодня, завтра вы сможете сказать на одну вещь меньше, чтобы попросить о помощи!
Я Сяо Цай, человек, который учится у вас.
💋
Официальный аккаунт WeChat открыт,Хорошая еда, Для тех, кто не обратил внимание, не забудьте обратить внимание!