Чтение онлайн

ЖАНРЫ

Программирование на Java

Вязовик Н.А.

Шрифт:

public void empty {}

Если в заголовке метода указан тип возвращаемого значения, а не void, то в теле метода обязательно должно встречаться return -выражение. При этом компилятор проводит анализ структуры метода, чтобы гарантировать, что при любых операторах ветвления возвращаемое значение будет сгенерировано. Например, следующий пример является некорректным:

// пример вызовет ошибку компиляции

public int get {

if (condition) {

return 5;

}

}

Видно, что хотя тело метода содержит return -выражение, однако

не при любом развитии событий возвращаемое значение будет сгенерировано. А вот такой пример является верным:

public int get {

if (condition) {

return 5;

}

else {

return 3;

}

}

Конечно, значение, указанное после слова return, должно быть совместимо по типу с объявленным возвращаемым значением (это понятие подробно рассматривается в лекции 7).

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

public void calculate(int x, int y) {

if (x<=0 || y<=0) {

return;

// некорректные входные

// значения, выход из метода

}

... // основные вычисления

}

Выражений return (с параметром или без для методов с/без возвращаемого значения) в теле одного метода может быть сколько угодно. Однако следует помнить, что множество точек выхода в одном методе может заметно усложнить понимание логики его работы.

Объявление конструкторов

Формат объявления конструкторов похож на упрощенное объявление методов. Также выделяют заголовок и тело конструктора. Заголовок состоит, во-первых, из модификаторов доступа (никакие другие модификаторы недопустимы). Во-вторых, указывается имя класса, которое можно расценивать двояко. Можно считать, что имя конструктора совпадает с именем класса. А можно рассматривать конструктор как безымянный, а имя класса – как тип возвращаемого значения, ведь конструктор может породить только объект класса, в котором он объявлен. Это исключительно дело вкуса, так как на формате объявления никак не сказывается:

public class Human {

private int age;

protected Human(int a) {

age=a;

}

public Human(String name, Human mother,

Human father) {

age=0;

}

}

Как видно из примеров, далее следует перечисление входных аргументов по тем же правилам, что и для методов. Завершает заголовок конструктора throws-выражение (в примере не использовано, см. лекцию 10 "Исключения"). Оно имеет особую важность для конструкторов, поскольку сгенерировать ошибку – это для конструктора единственный способ не создавать объект. Если конструктор выполнился без ошибок, то объект гарантированно создается.

Тело конструктора пустым быть не может и поэтому всегда описывается в фигурных скобках (для простейших реализаций скобки могут быть пустыми).

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

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

Однако логика работы конструкторов имеет и некоторые важные особенности. Поскольку

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

public class Parent {

private int x, y;

public Parent {

x=y=0;

}

public Parent(int newx, int newy) {

x=newx;

y=newy;

}

}

public class Child extends Parent {

public Child {

super;

}

public Child(int newx, int newy) {

super(newx, newy);

}

}

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

Проследим мысленно весь алгоритм создания объекта. Он начинается при исполнении выражения с ключевым словом new, за которым следует имя класса, от которого будет порождаться объект, и набор аргументов для его конструктора. По этому набору определяется, какой именно конструктор будет использован, и происходит его вызов. Первая строка его тела содержит вызов родительского конструктора. В свою очередь, первая строка тела конструктора родителя будет содержать вызов к его родителю, и так далее. Восхождение по дереву наследования заканчивается, очевидно, на классе Object, у которого есть единственный конструктор без параметров. Его тело пустое (записывается парой пустых фигурных скобок), однако можно считать, что именно в этот момент JVM порождает объект и далее начинается процесс его инициализации. Выполнение начинает обратный путь вниз по дереву наследования. У самого верхнего родителя, прямого наследника от Object, происходит продолжение исполнения конструктора со второй строки. Когда он будет полностью выполнен, необходимо перейти к следующему родителю, на один уровень наследования вниз, и завершить выполнение его конструктора, и так далее. Наконец, можно будет вернуться к конструктору исходного класса, который был вызван с помощью new, и также продолжить его выполнение со второй строки. По его завершении объект считается полностью созданным, исполнение выражения new будет закончено, а в качестве результата будет возвращена ссылка на порожденный объект.

Проиллюстрируем этот алгоритм следующим примером:

public class GraphicElement {

private int x, y;

// положение на экране

public GraphicElement(int nx, int ny) {

super;

// обращение к конструктору

// родителя Object

System.out.println("GraphicElement");

x=nx;

y=ny;

}

}

public class Square extends GraphicElement {

private int side;

public Square(int x, int y, int nside) {

super(x, y);

System.out.println("Square");

side=nside;

}

}

public class SmallColorSquare extends Square {

private Color color;

public SmallColorSquare(int x, int y, Color c) {

super(x, y, 5);

System.out.println("SmallColorSquare");

color=c;

}

}

После выполнения выражения создания объекта на экране появится следующее:

GraphicElement

Square

SmallColorSquare

Поделиться с друзьями: