java что такое сигнатура метода java
Включает ли Сигнатура метода тип возвращаемого значения в Java?
Узнайте, почему сигнатуры методов состоят из имени и списка типов параметров в Java.
1. Обзор
Сигнатура метода является только подмножеством всего определения метода в Java. Таким образом, точная анатомия подписи может вызвать путаницу.
В этом уроке мы изучим элементы сигнатуры метода и ее значение в программировании на Java.
2. Сигнатура метода
Давайте подробнее рассмотрим перегрузку метода и то, как она связана с сигнатурами методов.
3. Ошибки Перегрузки
Давайте рассмотрим следующий код :
Как мы видим, код компилируется, поскольку методы имеют разные списки типов параметров. По сути, компилятор может детерминированно привязать любой вызов к одному или другому.
Теперь давайте проверим, законно ли перегружать, добавив следующий метод:
Давайте попробуем то же самое с модификаторами:
Перегрузка путем изменения брошенных исключений может быть проверена путем добавления:
Последнее, что мы можем проверить, – это то, допускает ли изменение имен параметров перегрузку. Давайте добавим следующий метод:
3. Дженерики и стирание типов
Давайте рассмотрим следующий код:
Несмотря на то, что сигнатуры выглядят по-разному, компилятор не может статически привязать правильный метод после удаления типа.
4. Списки параметров и полиморфизм
Сигнатура метода учитывает точные типы. Это означает, что мы можем перегрузить метод, тип параметра которого является подклассом или суперклассом.
Давайте взглянем на следующий код:
Приведенный выше код совершенно легален и будет компилироваться. При вызове этих методов может возникнуть путаница, поскольку нам нужно не только знать точную сигнатуру вызываемого метода, но и то, как Java статически связывается на основе фактических значений.
Давайте рассмотрим несколько вызовов методов, которые в конечном итоге привязываются к sum(Integer, Integer) :
Для первого вызова у нас есть точные типы параметров Integer, Integer. При втором вызове Java автоматически установит int в Integer для нас . Наконец, Java преобразует значение байта 0x1 в int с помощью продвижения типа, а затем автоматически установит его в Integer.
Аналогично, у нас есть следующие вызовы, которые привязываются к sum(Number, Number) :
При первом вызове у нас есть double значения, которые автоматически преобразуются в Double. А затем, с помощью полиморфизма, Double соответствует Числу. Идентично, Float соответствует Номеру для второго вызова.
Теперь рассмотрим следующий вызов метода:
Чтобы изменить привязку по умолчанию, мы можем использовать явное приведение параметров следующим образом:
5. Параметры Vararg
Теперь давайте обратим наше внимание на то, как varargs влияет на эффективную сигнатуру метода и статическую привязку.
Здесь у нас есть перегруженный метод, использующий varargs :
Итак, каковы эффективные сигнатуры методов? Мы уже видели, что sum(Объект, Объект) является сигнатурой для первого. Переменные аргументы по сути являются массивами, поэтому эффективной сигнатурой для второго после компиляции является sum(Object, Object[]).
Сложный вопрос заключается в том, как мы можем выбрать привязку метода, когда у нас есть только два параметра?
Давайте рассмотрим следующие вызовы:
Последнее, что следует отметить здесь, это то, что объявление следующего метода будет конфликтовать с версией vararg:
6. Заключение
В этом уроке мы узнали, что сигнатуры метода состоят из имени и списка типов параметров. Модификаторы, тип возвращаемого значения, имена параметров и список исключений не могут различать перегруженные методы и, таким образом, не являются частью сигнатуры.
Мы также рассмотрели, как стирание типов и varargs скрывают эффективную сигнатуру метода и как мы можем переопределить привязку статического метода Java.
Собеседование по Java — ООП (вопросы и ответы). Часть 1.
Вопросы и ответы по теме ООП (объектно ориентированное программирование) для собеседования по Java.
К списку вопросов по всем темам
Список всех вопросов по ООП
1. Назовите принципы ООП и расскажите о каждом.
2. Дайте определение понятию “класс”.
3. Что такое поле/атрибут класса?
4. Как правильно организовать доступ к полям класса?
5. Дайте определение понятию “конструктор”.
6. Чем отличаются конструкторы по умолчанию, копирования и конструктор с параметрами?
7. Какие модификации уровня доступа вы знаете, расскажите про каждый из них.
8. Расскажите об особенностях класса с единственным закрытым (private) конструктором.
9. О чем говорят ключевые слова “this”, “super”, где и как их можно использовать?
10. Дайте определение понятию “метод”.
11. Что такое сигнатура метода?
12. Какие методы называются перегруженными?
13. Могут ли нестатические методы перегрузить статические?
14. Расскажите про переопределение методов.
15. Может ли метод принимать разное количество параметров (аргументы переменной длины)?
16. Можно ли сузить уровень доступа/тип возвращаемого значения при переопределении метода?
17. Как получить доступ к переопределенным методам родительского класса?
18. Какие преобразования называются нисходящими и восходящими?
19. Чем отличается переопределение от перегрузки?
20. Где можно инициализировать статические/нестатические поля?
21. Зачем нужен оператор instanceof?
22. Зачем нужны и какие бывают блоки инициализации?
23. Каков порядок вызова конструкторов и блоков инициализации двух классов: потомка и его предка?
24. Где и для чего используется модификатор abstract?
25. Можно ли объявить метод абстрактным и статическим одновременно?
26. Что означает ключевое слово static?
27. К каким конструкциям Java применим модификатор static?
28. Что будет, если в static блоке кода возникнет исключительная ситуация?
29. Можно ли перегрузить static метод?
30. Что такое статический класс, какие особенности его использования?
31. Какие особенности инициализации final static переменных?
32. Как влияет модификатор static на класс/метод/поле?
33. О чем говорит ключевое слово final?
34. Дайте определение понятию “интерфейс”.
35. Какие модификаторы по умолчанию имеют поля и методы интерфейсов?
36. Почему нельзя объявить метод интерфейса с модификатором final или static?
37. Какие типы классов бывают в java (вложенные… и.т.д.)
38. Какие особенности создания вложенных классов: простых и статических.
39. Что вы знаете о вложенных классах, зачем они используются? Классификация, варианты использования, о нарушении инкапсуляции.
40. В чем разница вложенных и внутренних классов?
41. Какие классы называются анонимными?
42. Каким образом из вложенного класса получить доступ к полю внешнего класса?
43. Каким образом можно обратиться к локальной переменной метода из анонимного класса, объявленного в теле этого метода? Есть ли какие-нибудь ограничения для такой переменной?
44. Как связан любой пользовательский класс с классом Object?
45. Расскажите про каждый из методов класса Object.
46. Что такое метод equals(). Чем он отличается от операции ==.
47. Если вы хотите переопределить equals(), какие условия должны удовлетворяться для переопределенного метода?
48. Если equals() переопределен, есть ли какие-либо другие методы, которые следует переопределить?
49. В чем особенность работы методов hashCode и equals? Каким образом реализованы методы hashCode и equals в классе Object? Какие правила и соглашения существуют для реализации этих методов? Когда они применяются?
50. Какой метод возвращает строковое представление объекта?
51. Что будет, если переопределить equals не переопределяя hashCode? Какие могут возникнуть проблемы?
52. Есть ли какие-либо рекомендации о том, какие поля следует использовать при подсчете hashCode?
53. Как вы думаете, будут ли какие-то проблемы, если у объекта, который используется в качестве ключа в hashMap изменится поле, которое участвует в определении hashCode?
54. Чем отличается абстрактный класс от интерфейса, в каких случаях что вы будете использовать?
55. Можно ли получить доступ к private переменным класса и если да, то каким образом?
56. Что такое volatile и transient? Для чего и в каких случаях можно было бы использовать default?
57. Расширение модификаторов при наследовании, переопределение и сокрытие методов. Если у класса-родителя есть метод, объявленный как private, может ли наследник расширить его видимость? А если protected? А сузить видимость?
58. Имеет ли смысл объявлять метод private final?
59. Какие особенности инициализации final переменных?
60. Что будет, если единственный конструктор класса объявлен как final?
61. Что такое finalize? Зачем он нужен? Что Вы можете рассказать о сборщике мусора и алгоритмах его работы.
62. Почему метод clone объявлен как protected? Что необходимо для реализации клонирования?
Ответы. Часть 1
1. Назовите принципы ООП и расскажите о каждом.
Объе́ктно-ориенти́рованное программи́рование (ООП) — это методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.
Основные принципы ООП: абстракция, инкапсуляция, наследование, полиморфизм.
Абстракция — означает выделение значимой информации и исключение из рассмотрения незначимой. С точки зрения программирования это правильное разделение программы на объекты. Абстракция позволяет отобрать главные характеристики и опустить второстепенные.
Пример: описание должностей в компании. Здесь название должности значимая информация, а описание обязанностей у каждой должности это второстепенная информация. К примеру главной характеристикой для «директор» будет то, что это должность чем-то управляет, а чем именно (директор по персоналу, финансовый директор, исполнительный директор) это уже второстепенная информация.
Инкапсуляция — свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе. Для Java корректно будет говорить, что инкапсуляция это «сокрытие реализации». Пример из жизни — пульт от телевизора. Мы нажимаем кнопочку «увеличить громкость» и она увеличивается, но в этот момент происходят десятки процессов, которые скрыты от нас. Для Java: можно создать класс с 10 методами, например вычисляющие площадь сложной фигуры, но сделать из них 9 private. 10й метод будет называться «вычислитьПлощадь()» и объявлен public, а в нем уже будут вызываться необходимые скрытые от пользователя методы. Именно его и будет вызывать пользователь.
Наследование — свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым, родительским или суперклассом. Новый класс — потомком, наследником, дочерним или производным классом.
Полиморфизм — свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. Пример (чуть переделанный) из Thinking in Java:
Java / Сигнатура метода и перегрузка методов в Java.
Автор: ksp107 ← к списку ← →
Понятие сигнатура метода используется в Java в различных контекстах и в зависимости от ситуации может определяться по-разному.
В простейшем случае под сигнатурой метода понимают совокупность имени метода и списка типов его аргументов. Например, у первого метода будет сигнатура method(double, int)
Правила языка запрещают в одном классе иметь несколько методов, отличающихся только типом возвращаемого значения. Это нужно для того, чтобы компилятор, анализируя вызов метода, смог сгенерировать корректный код.
Более полное понятие сигнатуры метода включает в себя:
– список типов формальных параметров метода (возможно, параметризованные);
– список типов исключений, выбрасываемых методом;
– тип возвращаемого методом значения (возможно, параметризованный);
– формальные типы-параметры метода.
Именно эта информация сохраняется в бинарном представлении классов (байт-коде) и используется виртуальной машиной Java при вызове методов.
Если использовать некоторые приёмы, мы можем получить в одном классе несколько методов, отличающихся только возвращаемым типом.
Рассмотрим следующий код:
Оказывается, если при перекрытии метода сужать возвращаемый тип, то компилятор в классе-потомке неявно создаст дополнительный метод-заглушку, у которого будет точно такая же сигнатура, как и у исходного метода. Т.е. результат компиляции класса B будет соответствовать следующему условному коду:
Нечто подобное используют обфускаторы кода, правда, с другой целью – усложнить реверс-анализ байт-кода 🙂
Если Вам понравился вопрос, проголосуйте за него
Голосов: 52 Голосовать
Перегрузка, которая запрещена, или bridge-методы в Java
В большинстве моих собеседований на технические позиции есть задача, в которой кандидату необходимо реализовать 2 очень похожих интерфейса в одном классе:
Реализуйте оба интерфейса одним классом, если это возможно. Объясните, почему это возможно или нет.
От переводчика: Эта статья не призывает вас задавать такие же вопросы на интервью. Но если вы хотите быть во всеоружии, когда этот вопрос зададут вам, то добро пожаловать под кат.
Иногда соискатели, которые не очень уверены в ответе, предпочитают решить вместо этой задачу со следующим условием (позже я в любом случае прошу ее решить):
И правда, вторая задача кажется намного проще, и большинство кандидатов отвечают, что включение обоих методов в один и тот же класс невозможно, потому что сигнатуры S.m(int) и V.m(int) одинаковы, в то время как тип возвращаемого значения — разный. И это абсолютно верно.
Однако иногда я задаю другой вопрос, связанный с этой темой:
Как вы думаете, есть ли смысл в том, чтобы допускать реализацию методов с одинаковой сигнатурой, но разными типами в одном классе? Например, в неком гипотетическом языке на базе JVM или хотя бы на уровне JVM?
Это вопрос, ответ на который неоднозначен. Но, не смотря на то, что я не ожидаю ответа на него, правильный ответ существует. Ответить на него смог бы человек, который часто имеет дело с API рефлексии, манипулирует байт-кодом или знаком со спецификацией JVM.
Сигнатура метода Java и дескриптор метода JVM
Сигнатура метода Java (т.е. название метода и типы параметров) применяется только Java компилятором во время компиляции. В свою очередь, JVM разделяет методы в классе с помощью неквалифицированного имени метода (то есть просто имени метода) и дескриптора метода, то есть перечня параметров дескриптора и одного return-дескриптора.
а для void m(int i) следующий:
Таким образом, JVM вполне комфортно себя чувствует с String m(int i) и void m(int i) в одном классе. Все, что нужно, — это сгенерировать соответствующий байт-код.
Кунг-фу с байт-кодом
У нас есть интерфейсы S и V, теперь мы создадим класс SV, который включает оба интерфейса. В Java, если бы это было разрешено, это должно выглядеть так:
Чтобы сгенерировать байт-код, мы используем Objectweb ASM library, достаточно низкоуровневую библиотеку, чтобы получить представление о JVM байт-коде.
Полный исходный код залит на GitHub, здесь же я приведу и поясню только наиболее важные фрагменты.
Начнем с создания ClassWriter для генерации байт-кода.
Теперь мы объявим класс, в который входят интерфейсы S и V.
Хотя наш референсный псевдо-java код для SV не имеет конструкторов, нам все равно нужно генерировать код для него. Если мы не описываем конструкторы на Java, компилятор неявно генерирует пустой конструктор.
В теле методов мы начнем с получения поля System.out с типом java.io.PrintStream и добавления его в стек операндов. Затем загружаем константу ( String или void ) в стек и вызываем команду println в полученной переменной out со строковой константой в качестве аргумента.
и используем jad (декомпилятор Java), чтобы перевести байт-код обратно в исходный код на Java:
Использование сгенерированного класса
Успешная декомпиляция jad по сути ничего нам не гарантирует. Утилита jad оповещает только об основных проблемах в байт-коде, от таких, как размер фрейма, до несоответствия локальных переменных или отсутствующего оператора возврата.
Чтобы использовать сгенерированный класс во время исполнения, нам необходимо каким-то образом загрузить его в JVM и затем создать его экземпляр.
Теперь используем этот class loader и создадим экземпляр класса:
Поскольку наш класс сгенерирован во время исполнения, мы не можем использовать его в исходном коде. Зато мы можем привести его тип к реализованным интерфейсам. А вызов без рефлексии можно осуществить так:
При выполнении кода мы получим следующий вывод:
Кому-то такой вывод покажется неожиданным: мы обращаемся к одному и тому же (с точки зрения Java) методу в классе, но результаты различаются в зависимости от интерфейса, к которому мы привели объект. Сногсшибательно, правда?
Все станет понятно, если принять во внимание лежащий в основе байт-код. Для нашего вызова компилятор генерирует инструкцию INVOKEINTERFACE, и дескриптор метода исходит не из класса, а из интерфейса.
Таким образом, при первом вызове мы получим:
Объект, на котором мы выполнили вызов, можно получить из стека. Это и есть могущество полиморфизма, присущее Java.
Имя ему — bridge-метод
Кто-то спросит: «Так в чем смысл всего этого? Пригодится ли это когда-нибудь?»
Смысл в том, что мы используем всё то же самое (неявно) при написании обычного Java кода. Например, ковариантные возвращаемые типы, дженерики и доступ к private-полям из внутренних классов реализуются с помощью такой же магии байт-кода.
Взгляните на такой интерфейс:
и его реализацию с возвратом ковариантного типа:
Теперь подумаем над этим кодом:
По сути, компилятор генерирует дополнительный метод, выполняющий роль моста между реальным методом, указанным в классе, и методом, используемым при вызове через интерфейс. Отсюда название — bridge-метод. Если бы в Java такое было возможно, конечный код выглядел бы так:
Послесловие
Язык программирования Java и виртуальная машина Java — это не одно и то же: хотя они имеют в названии общее слово и Java является основным языком для JVM, их возможности и ограничения далеко не всегда одинаковы. Знание JVM помогает лучше понимать Java или любой другой основанный на JVM язык, но, с другой стороны, знание Java и его истории помогают понять определенные решения в дизайне JVM.
От переводчика
Вопросы совместимости рано или поздно начинают волновать любого разработчика. В исходной статье затронут важный вопрос о неявном поведении компилятора Java и влиянии его магии на приложения, который нас как разработчиков фреймворка CUBA Platform волнует довольно сильно, — это напрямую влияет на совместимость библиотек. Совсем недавно мы рассказывали о совместимости в реальных приложениях на JUG в Екатеринбурге в докладе «API на переправе не меняют — как построить стабильный API», видео встречи можно найти по ссылке.
Методы в Java
Узнайте все о методах в Java, от базового синтаксиса метода до перегрузки, а также о том, как вызывать методы.
1. введение
В Java методы – это то, где мы определяем бизнес-логику приложения. Они определяют взаимодействие между данными, заключенными в объекте.
2. Синтаксис метода
Во-первых, метод состоит из шести частей:
Давайте рассмотрим пример:
Давайте подробнее рассмотрим каждую из этих шести частей метода Java.
2.1. Модификатор доступа
Модификатор access позволяет нам указать, какие объекты могут иметь доступ к методу. Существует четыре возможных модификатора доступа: public, protected, private и default (также называемый package-private ).
Метод также может включать ключевое слово static | до или после модификатора доступа. Это означает, что метод принадлежит классу, а не экземплярам, и поэтому мы можем вызвать метод, не создавая экземпляр класса. Методы без ключевого слова static известны как методы экземпляра и могут вызываться только на экземпляре класса.
Что касается производительности, статический метод будет загружен в память только один раз – во время загрузки класса – и, таким образом, более эффективен с точки зрения памяти.
2.2. Тип возврата
Давайте рассмотрим пример метода void :
Если мы объявляем тип возвращаемого значения, то мы должны указать оператор return в теле метода. Как только оператор return будет выполнен, выполнение тела метода будет завершено, и если будет больше операторов, они не будут обработаны.
2.3. Идентификатор метода
Идентификатор метода-это имя, которое мы присваиваем спецификации метода. Рекомендуется использовать информативное и описательное название. Стоит отметить, что идентификатор метода может содержать не более 65536 символов (хотя это длинное имя).
2.4. Список параметров
2.5. Список исключений
Итак, давайте рассмотрим более сложный вариант нашего предыдущего метода, который вызывает проверенное исключение:
2.6. Тело метода
Последняя часть метода Java-это тело метода, которое содержит логику, которую мы хотим выполнить. В теле метода мы можем написать столько строк кода, сколько захотим, или вообще ничего в случае статических методов. Если наш метод объявляет тип возвращаемого значения, то тело метода должно содержать оператор return.
3. Сигнатура метода
Итак, давайте напишем простой метод:
Идентификатор метода может быть любым идентификатором. Однако, если мы следуем общепринятым соглашениям о кодировании Java, идентификатор метода должен быть глаголом в нижнем регистре, за которым могут следовать прилагательные и/или существительные.
4. Вызов метода
Давайте продемонстрируем, используя вариант предыдущего примера:
В этом случае вызов метода является:
5. Способ Перегрузки
Перегрузка метода полезна в случаях, подобных приведенному в примере, когда у нас может быть метод, реализующий упрощенную версию той же функциональности.
Наконец, хорошая привычка к дизайну заключается в том, чтобы гарантировать, что перегруженные методы ведут себя аналогичным образом. В противном случае код будет сбивать с толку, если метод с тем же идентификатором будет вести себя по-другому.
6. Заключение
В этом уроке мы рассмотрели части синтаксиса Java, используемые при указании метода в Java.
В частности, мы рассмотрели модификатор доступа, тип возвращаемого значения, идентификатор метода, список параметров, список исключений и тело метода. Затем мы увидели определение сигнатуры метода, как вызвать метод и как перегрузить метод.