Объектно-ориентированное программирование на Java. Платформа Java SE

Тимур Машнин

Эта книга предназначена для тех, кто хочет научиться программировать на языке Java.С этой книгой вы обучитесь объектно-ориентированному программированию на платформе Java SE и научитесь применять принципы ООП на практике.Эта книга охватывает важные аспекты программирования на языке Java, начиная с основ и заканчивая объектно-ориентированным подходом и командной разработкой кода.

Оглавление

Область видимости переменных

В предыдущей лекции мы узнали, как определить метод.

И мы хотим знать, что происходит, когда мы его вызываем.

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

Он называется square и принимает одно значение и возвращает другое значение — квадрат числа.

Важно отметить, что определение метода идентифицирует два контекста — внутри и снаружи.

Внутри мы можем использовать параметры x или y или что угодно.

Но не снаружи.

Извне мы просто знаем название метода, параметры, и тип результата.

Как вычисляется метод, это вопрос внутреннего контекста.

В какой-то момент мы могли бы изменить тело метода.

Здесь мы видим альтернативный способ вычисления квадрата числа.

Но мы не знали бы этого извне, из контекста вызова.

Теперь давайте посмотрим, что происходит, когда мы вызываем метод с заданным значением.

Мы могли бы проанализировать, что происходит, когда мы вызываем square (3).

Но давайте сделаем немного интереснее.

Попробуем оценить выражение square (3) + square (4).

Чтобы получить результат суммы, сначала мы должны вычислить первый операнд, square (3).

И для этого мы перейдем к определению метода, где x теперь равно 3.

Это означает, что мы должны заменить все x на 3.

Таким образом, мы вычисляем 3 умножить на 3.

Результат будет — 9, и это то, что возвращает вызов метода.

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

Затем нам нужно вычислить значение для square (4).

Перейдем к определению метода, но теперь x равно 4.

3 больше не существует.

Поэтому мы заменяем все x на 4, и поэтому умножаем 4 на 4.

Этот вызов метода возвращает 16 вызывающему выражению.

Теперь у нас есть оба операнда, и мы можем сложить 9 и 16.

Во всех этих вычислениях важно отметить, что два вызова одного и того же метода полностью независимы.

Мы использовали x с двумя независимыми значениями.

Сначала 3, а затем 4.

И когда мы использовали 4, 3 уже не существовало.

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

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

Мы использовали идентификаторы или имена в разных целях: для переменных, для методов, для параметров метода и т. д.

Теперь возникает вопрос: если у нас есть переменная с именем «x», а затем у нас есть метод с параметром.

Можно ли назвать этот параметр как «х»?

Или будет какая-то несовместимость?

Можем ли мы использовать одно и то же имя в разных контекстах?

Давайте рассмотрим пример.

Представьте, что у нас есть программа, где есть целочисленная переменная с именем x,

Которую мы инициализируем в значение 1.

И у нас также есть метод «f», который имеет целочисленный параметр.

И мы просто решили назвать его «х».

Вопрос, можем ли мы это сделать?

И если да, то что этот метод вернет в качестве результата?

Ответ на этот вопрос при написании кода на Java — да, мы можем это сделать.

Каким образом, мы управляем двумя x?

Каждый x действителен в определенном контексте, при выполнении определенного сегмента кода.

У нас есть черный x, который действителен, и который существует, и для которого мы сохраняем пространство в памяти, когда объявляем переменную.

Мы также зарезервировали пространство в памяти для z.

И когда мы вызываем f с x плюс 1, значение x равно 1.

1 плюс 1 равно 2, и мы вызываем f с 2.

Далее мы переходим к определению метода.

Вызываем f с 2.

Таким образом, красный x равен 2.

Итак, мы выполняем x плюс x со значением 2.

2 плюс 2 равно 4.

И это то, что этот метод возвращает и что хранится в z.

Теперь помните, что параметр x метода f является просто заполнителем.

Поэтому, если f вызывается с переменной x, а значение x равно 2, f с x возвращает 4.

И с этим нет никаких проблем.

Мы говорим, что первое x является глобальной переменной, тогда как параметр x является локальным для метода.

В этом примере мы видим, что эта локальная переменная — этот параметр — создается дважды: во-первых, для внутреннего вызова f с x плюс 1, со значением 2, — и второй раз для внешнего вызова со значением 4.

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

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

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

Здесь мы видим, как использовать глобальную переменную x и параметр y в теле метода.

В этом случае у нас есть переменная x, которая видна во всем теле метода и за его пределами, и у нас есть переменная y, которая существует и видна только в теле метода.

Вне этого метода y не существует.

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

Здесь переменная y является локальной переменной в методе f.

В этом методе мы используем глобальную переменную x и локальную переменную y.

Этот пример аналогичен предыдущему.

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

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

Когда мы вызываем f для вычисления z, мы вызываем f, где внутри определяется x со значением 2.

Таким образом, мы возвращаем 2 плюс 2, равно 4.

Метод f всегда возвращает 4.

И это то, что мы сохраним в переменной z.

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

Глобальные переменные существуют, начиная с объявления и для остальной части программы.

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

В этом примере показан цикл.

Для циклов также объявляются локальные переменные.

Здесь переменная x цикла for не позволяет нам видеть глобальную переменную при выполнении цикла.

Здесь у нас есть глобальная переменная x и глобальная переменная y.

Они инициализируются 1 и 0 соответственно.

Затем у нас есть глобальная переменная z, которая сохраняет значение y, но после выполнения этого цикла for.

Этот цикл for выполняется дважды.

Один раз для x равного 1 и один раз для x равного 2.

В каждом цикле for, y накапливает значение x.

Таким образом, при первом запуске y получает значение 1, а во втором y получает значение 1 плюс 2, равно 3.

Когда мы выходим из цикла for, локальная переменная x исчезает, остается только глобальная.

y имеет значение 3, и это значение, которое мы сохраняем в z.

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

В этом примере у нас есть глобальная переменная x.

И у нас есть метод с параметром x.

И внутри этого метода у нас есть цикл for с другой переменной x.

Таким образом, в этом случае у нас есть 3 переменных x.

Поэтому, когда мы вызываем f с x плюс 2, в последней строке, где x равно 1, мы вызываем f с 3, чтобы вычислить z.

В методе, параметр x равен 3.

Внутри метода мы объявляем переменную y, инициализированную 0, и затем мы определяем цикл for.

Этот цикл for выполняется два раза, как в предыдущем примере.

Здесь, мы объявляем другую переменную x, которая делает невидимыми предыдущие две переменные x, пока мы не выполним цикл for.

Здесь мы увеличиваем значение y.

y в конце получает 3 и возвращает y плюс x.

Но что это за х?

Это не та переменная x в цикле for, потому что мы вышли из цикла for.

Эта x равна 3 и это параметр метода.

Поэтому возвращается 3 плюс 3.

Это то, что мы возвращаем z, и что добавляется к x, но в этом случае это глобальная переменная x, поэтому мы получаем 7 и присваиваем 7 в z.

Этот пример легко проанализировать.

Метод f определяется в контексте, где x равно 1.

Таким образом, этот метод всегда возвращает 1 независимо откуда он был вызван.

x равно 1 и z также присваивается 1.

Важно отметить, что f получает свое определение в том месте, где он определен.

Если он определен в том месте, где x равно 1, метод f определяется, чтобы вернуть 1.

И это видно в этом примере.

В этом примере у нас есть два метода: f и g.

g вызывает f, и он вызывает его в контексте, где x равно 0.

И здесь нужно учитывать, что метод f был определен в контексте, где x равно 1.

И мы уже сказали, что метод f всегда возвращает 1 независимо от того, где он вызывается.

Так как здесь x равно 1.

Это называется лексической областью действия или статической областью действия в отличие от динамической области действия.

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

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

Теперь, если мы удалим самое верхнее объявление x, переменная x не определяется при объявлении f.

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

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

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

Обычно для всех значений из множества X существуют значения во множестве Y.

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

В этом случае мы говорим о частично определенной функции.

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

Теперь вернемся к Java.

Предположим, мы хотим вычислить квадратный корень из 4.

Здесь есть два результата, плюс 2 и минус 2.

Предположим, что наш метод просто возвращает положительное значение, плюс 2.

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

Теперь, что произойдет, если мы вызовем метод square с аргументом минус 4?

Мы знаем, что решением в этом случае являются не действительные числа, а мнимые числа.

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

Метод не определен для отрицательных чисел.

В математике мы можем определить функции более подробно.

Мы можем настроить область определения в соответствии с тем, что нам нужно.

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

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

Но в программировании мы имеем дело с существующими типами.

Теперь, как мы определяем в Java частично определенные функции или частично определенные методы?

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

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

Это, конечно, не самый удобный способ для решения этой проблемы.

Во-вторых, мы можем проверять значения параметров метода в самом методе или при вызове метода.

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

Смотрите также

а б в г д е ё ж з и й к л м н о п р с т у ф х ц ч ш щ э ю я