Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Java -texnologiya.doc
Скачиваний:
17
Добавлен:
01.05.2025
Размер:
3 Мб
Скачать
☆

Лекция 10. Операторы и структура кода. Исключения

 

После ознакомления с типами данных в Java, правилами объявления  классов и интерфейсов, а также с массивами, из базовых свойств языка оставляя рассмотреть лишь управление ходом выполнения программы. В этой лек­ция вводятся важные понятия, связанные с данной темой, описываются мет­ки, операторы условного перехода, циклы, операторы break и continue и другие.

Следующая тема посвящена более концептуальным механизмам Java, а именно работе с ошибками или исключительными ситуациями. Рассматриваются причины возникновения сбоев, способы их обработки, объявление собственных типов исключительных ситуаций. Описывается разделение всех ошибок на проверяемые и непроверяемые компилятором, а также ошибкивремени исполнения.

Ключевые слова: метка, проверяемые исключения, непроверяемые исключения

Управление ходом программы

Управление потоком вычислений является фундаментальной осно­вой всего языка программирования. В данной лекции будут рассмотрены основные языковые конструкции и способы их применения.

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

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

Нормальное и прерванное выполнение операторов

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

break

continue

return

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

Нормальное выполнение оператора может быть прервано также при возникновении исключительных ситуаций, которые тоже будут рассмотрены позднее. Явное возбуждение исключительной ситуации с помощью оператора throw также прерывает нормальное выполнение оператора, передает управление выполнением программы далее просто управление! в другое место.

Прерывание нормального исполнения всегда вызывается определенной причиной. Приведем список таких причин:

•         break (без указания метки);

•         break (с указанием метки);

•         continue (без указания метки);

•         continue (с указанием метки);

•         return (с возвратом значения);

•         return (без возврата значения;)

•         throw с указанием объекта Throwable, а также все исключения, вызываемые виртуальной машиной Java.

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

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

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

Блоки и локальные переменные

Блок — это последовательность операторов, объявлений локальных классов или локальных переменных, заключенных в скобки. Область ви­димости локальных переменных и классов ограничена блоком, в котором они определены.

Операторы в блоке выполняются слева направо, сверху вниз. Если все операторы (выражения) в блоке выполняются нормально, то и весь блок выполняется нормально. Если какой-либо оператор выражения завершается ненормально, то и весь блок завершается ненормально.

Нельзя объявлять несколько локальных переменных с одинаковыми именами в пределах видимости блока. Приведенный ниже код вызовет ошибку времени компиляции.

public class Test {

public TestO {

}

public static void main(String[] args) {

Testt- new TestO;

int x;

lbl:{

intx- 0;

System.out,println("x = " + x);

} } }

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

public class Test { static intx = 5; public TestO {} public static void main(String[] args) {

Testt= new TestO;

nt X = 1;

System.out.println("x - " + x);

} }

Ha консоль будет выведено x = 1.

To же самое правило применимо к параметрам методов.

public class Test { static int x; public TestO {

}

public static void main{Sthng[] args) {

Testt = new TestO;

t.test(5);

System,out.println("Mennber value x = " + x);

}

private void test(int x){ this.x = x + 5;

System.out. printlnC'Local value x = " + x); } }

В результате работы этого примера на консоль будет выведено:

Local value х = 5 Member value х= 10

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

public class Test { static int X = 5; public Test() { }

public static void main(String[] args) { Testt = newTestO;

{

int X = 1;

System.out.printlnC'First block x = " + x); } {

intx = 2;

System.out.printlnC'Second blockx =" + x); }

System.out.print("For cycle x = "); for(int X =0;x<5;x++) {

System.out.print("" + x); } } }

Данный пример откомпилируется без ошибок и на консоль будет выведен следующий результат:

First block X = 1 Second block x =2 For cycle X = 0 1 2 3 4

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

Следующий пример кода

public class Test { static int X = 5; public TestO {

}

public static void main(String[] args) {

Testt = nevtfTest{); intx; inty = 5; if{y>3)x=1; System.out.println(x); } }

вызовет ошибку времени компиляции, т.к. возможны условия, при кото­рых переменная х может быть не инициализирована до ее использования (несмотря на то, что в данном случае операторif(y > 3) и следующее за ним выражение х = 1; будут выполняться всегда).

Пустой оператор

Точка с запятой (;) является пустым оператором. Данная конструк­ция вполне применима там, где не предполагается выполнение никаких Действий. Преждевременное завершение пустого оператора невозможно.

Метки

Любой оператор, или блок, может иметь метку. Метку можно указывать в качестве параметра для операторов break и continue. Область видимости метки ограничивается оператором, или блоком, к которому она относится. Так, в следующем примере мы получим ошибку компиляции:

public class Test { static int X = 5; static { }

public TestO {}

public static void main(String[] args) {

Test t = new TestO;

int X = 1;

Lbl1:{if(x ==0) break Lbl1;}

Lbl2:{if(x>0) break Lbl1; } } }

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

Этот пример является вполне корректным:

public class Test { static int X = 5; static {}

public TestO { }

public static void main(String[] args) { Testt = new TestO; int L2 = 0;

Test: for(int i = 0; i< 10;i++) { test: for(int j = 0; j< 10;j++) {

if( i*i > 50) break Test; } } } private void testO {} }

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

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

Оператор if

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

В общем случае конструкция выглядит так:

if (логическое выражение) выражение или блок 1 else выражение или блок 2

Логическое выражение может быть любой языковой конструкцией, которая возвращает булевский результат. Отметим отличие от языка С, в котором в качестве логического выражения могут использоваться различные типы данных, где отличное от нуля выражение трактуется как истинное значение, а ноль — как ложное. В Java возможно использование только логических выражений.

Если логическое выражение принимает значение "истина", то вы­полняется выражение или блок 1, в противном случае - выражение или блок 2. Вторая часть оператора (else) не является обязательной и может быть опущена. Т.е. конструкция if(x = 5) System.out.println("Five") вполне допустима.

Операторы if-else могут каскадироваться.

String test = "smb";

if( test.equals("value1") {} else if (test.equals("value2") {

} else if (test.equals('Value3") {} else {}

Следует помнить, что оператор else относится к ближайшему к не\1 оператору if. В данном случае последнее условие else будет выполняться только если не выполнено предыдущее. Заключительная конструкции else относится к самому последнему условию if и будет выполнена только в том случае, если ни одно из вышеперечисленных условий не будет истинным. Если хотя бы одно из условий выполнено, то все последующее выполняться не будут.

Например:

int X = 5; if( X < 4) {

System.out.prlntln("MeHbme 4"); } else If (x > 4) {

System.out.рг1п11п("Больше 4"); lelseif (x==5){

System.out.println("PaBHo 4"); } else{

System.out.println("flpyroe значение"); }

Предложение "Равно 4' в данном случае напечатано не будет.

Оператор switch

Оператор switch() удобно использовать в случае необходимости множественного выбора. Выбор осуществляется на основе целочислен­ного значения.

Структура оператора:

switch(int value) { case consti:

выражение или блок case const2:

выражение или блок case constn:

выражение или блок default:

выражение или блок }

Причем, фраза default не является обязательной.

В качестве параметра switch может использоваться переменная типа hvte, short, int, char или выражение. Выражение должно в конечном итоге возвращать параметр одного из указанных ранее типов. В операторе gase не могут применяться значения примитивного типа long и ссылочных типов Long, String, Integer, Byte и т.д.

При выполнении оператора switch производится последовательное сравнение значения х с константами, указанными после case, и в случае совпадения выполняется выражение следующего за этим условием. Если р1дражение выполнено нормально и нет преждевременного его заверше­ния, то производится сравнение для последующих case. Если же выраже­ние, следующее за case, завершилось ненормально, то будет прекращено выполнение всего оператора switch.

Если не выполнен ни один оператор case, то выполнится оператор default, если он имеется в данном switch. Если оператора default нет и ни одно из условий case не выполнено, то ни одно из выражений switch так­же выполнено не будет.

Следует обратить внимание, что, в отличие от многозвенного if-else, если какое-либо условие case выполнено, то выполнение switch не прекра­тится, а будут проверяться следующие за ним условия. Если этого необходи­мо избежать, то после кода следующего за оператором case используется оператор break, прерывающий дальнейшее выполнение оператора switch.

После оператора case должен следовать литерал, который может быть интерпретирован как 32-битовое целое значение. Здесь не могут применяться выражения и переменные, если они не являются final static.

Рассмотрим пример:

int X = 2; switch(x) { case 1: case 2:

System.out.printlnC'PaBHo 1 или 2"); break; case 3: case 4:

System.out.printlnC'PaBHo 3 или 4"); break; default:

System.out.println("3Ha4eHHe не определено"); }

fc     fi данном случае на консоль будет выведен результат "Равно 1 или 2". Если же  убрать операторы break, то будут выведены все три строки.

Вот такая конструкция вызовет ошибку времени компиляции.

intx = 5; switch (х) {

case у: // только константы!

break; }

В операторе switch не может быть двух case с одинаковыми значениями.

Т.е. конструкция

switch(x) {

case 1:

System.out.println("One");

break;

case 1:

System.out.println("Two");

break;

case 3:

System.out.println("Tree or other value"); }

недопустима.

Также в конструкции switch может быть применен только один оператор default.

Управление циклами

в языке Java имеется три основных конструкции управления циклами:

•         цикл while;

•         цикл do;

•         цикл for.

Цикл while

Основная форма цикла while может быть представлена так:

while(nor маленькое выражение)

повторяющееся выражение, или блок;

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

Операторы continue и break могут изменять нормальное исполнение тела цикла. Так, если в теле цикла встретился оператор continue, то jjgpaTopbi, следующие за ним, будут пропущены и выполнение цикла «начнется сначала. Если continue используется с меткой и метка принадлежит к данному while, то выполнение его будет аналогичным. Если метка не росится к данному while, его выполнение будет прекращено и управление  будет передано на оператор, или блок, к которому относится метка.

Если встретился оператор break, то выполнение цикла будет прекра­щено.

Если выполнение блока было прекращено по какой-то другой причи­не (возникла исключительная ситуация), то выполнение всего цикла будет прекращено по той же причине.

Рассмотрим несколько примеров:

public class Test { static int X = 5; public TestO {

}

public static void main{String[] args) {

Testt= newTestO;

int X = 0;

while(x < 5) {

X++;

if(x% 2 ==0) continue;

System.out.print(" " + x);

} } }

Ha консоль будет выведено

135

вывод на печать всех четных чисел будет пропущен,

\ Public class Test { static int X = 5; public Test(){

public static void main(String[] args) { Testt = newTestO; int X = 0; int у = 0;

Ibl: whjle(y < 3) { У++;

while(x < 5) { X++;

if(x % 2 == 0) continue Ibl; System.out.println("x=" + x + " y="+y); } } } }

Ha консоль будет выведено

x=1 y=1 x=3 y=2 x=5 y=3

т.е. при выполнении условия if(x % 2 == 0) continue Ibl; цикл no перемен­ной X будет прерван, a цикл по переменной у начнет новую итерацию. Типичный вариант использования выраженияwhile():

int i = 0; while( i++ < 5) {

System.out.printlnC'Counter is " + i); }

Следует помнить, что цикл while() будет выполнен только в том слу­чае, если на момент начала его выполнения логическое выражение будет истинным. Таким образом, при выполнении программы может иметь мс' сто ситуация, когда цикл while() не будет выполнен ни разу.

boolean b = false; while{b) {

System.out.println("Executed"); }

В данном случае строка System.out.println("Executed"); выполнение не будет.

Цикл do

Основная форма цикла do имеет следующий вид:

do

повторяющееся выражение или блок; (логическое выражение)

Цикл do будет выполняться до тех пор, пока логическое выражение будет истинным. В отличие от цикла while, этот цикл будет выполнен, как минимум, один раз.

Типичная конструкция цикла do:

int counter = 0; do{

counter ++;

System.out.printlnC'Counter is " + counter); } while(counter >5);

В остальном выполнение цикла do аналогично выполнению цикла while, включая использование операторов break и continue.

Цикл for

Довольно часто бывает необходимо изменять значение какой-либо временной в заданном диапазоне и выполнять повторяющуюся последовательность операторов с использованием этой переменной. Для выпол­нения такой последовательности действий как нельзя лучше подходит конструкция цикла for.

Основная форма цикла for выглядит следующим образом:

Тог (выражение инициализации; условие; выражение обновления) повторяющееся выражение или блок;

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

Выражение инициализации выполняется до начала выполнения тела цикла. Чаще всего используется как некое стартовое условие (инициализации или объявление переменной).

Условие должно быть логическим выражением и трактуется точно так же, как логическое выражение в цикле while(). Тело цикла выполняется до тех пор, пока логическое выражение истинно. Как и в случае с циклом while, тело цикла может не исполниться ни разу. Это происхо­дит, если логическое выражение принимает значение "ложь" до начала выполнения цикла.

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

Пример использования цикла for()

for(counter=0;counter< 10;counter++) {

System.out. println("Counter is" + counter); }

В данном примере предполагается, что переменная counter была объявлена ранее. Цикл будет выполнен 10 раз и будут напечатаны значе­ния счетчика от 0 до 9.

Разрешается определять переменную прямо в предложении:

for(int cnt = 0;cnt < 10; cnt++) {

System.out. phntln("Counter is " + cnt); }

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

Любая часть конструкции for() может быть опущена. В вырожден­ном случае мы получим оператор for с пустыми значениями

for(;;){}

В данном случае цикл будет выполняться бесконечно. Эта конструк­ция аналогична конструкции while(true){}. Условия, в которых она может быть применена, мы рассмотрим позже.

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

for(i = О, j = 0; i<5;i++, ]+=2) {}

Использование такой конструкции вполне правомерно.

Операторы break и continue

В некоторых случаях требуется изменить ход выполнения программы. В традиционных языках программирования для этих целей применяется оператор goto, однако в Java он не поддерживается. Для этих целей приме-^10утся операторы break и continue.

Оператор continue

Оператор continue может использоваться только в циклах while, do, for. Если в потоке вычислений встречается оператор continue, то выпол­нение текущей последовательности операторов (выражений) должно быть прекращено и управление будет передано на начало блока, содержащего этот оператор.

jntx= (int)(Math.random()*lO); intarr[lO] ={....} for(intcnt=0;cnt<10;cnt++) { if(arr[cnt] == x) continue;}

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

Если оператор continue будет применен вне контекста оператора цикла, то будет выдана ошибка времени компиляции. В случае использо­вания вложенных циклов оператору continue, в качестве адреса перехода, может быть указана метка, относящаяся к одному из этих операторов.

Рассмотрим пример:

public class Test { public Test(){}

public static void main(String[] args) {

Test t = new Test(); for(intj=0;j< 10;j++){ if(i*% 2 ==0) continue; System.out. print("i=" + i); } } }

в результате работы на консоль будет выведено: 13579

При выполнении условия в строке 7 нормальная последовательность выполнения операторов будет прервана и управление будет передано на начало цикла. Таким образом, на консоль будут выводиться только нечет­ные значения.

Оператор break

Этот оператор, как и оператор continue, изменяет последователь­ность выполнения, но не возвращает исполнение к началу цикла, а преры­вает его.

public class Test { public Test{) { }

public static void main(String[] args) { Test t = new TestO; int []x= {1.2,4,0.8}; Inty =8;

for(int cnt=0;cnt < x.length;cnt++) { if(0==x[cnt]) break; Systenn.out.prlntln("y/x = " + y/x[cnt]); } } }

Ha консоль будет выведено:

y/x = 8 y/x = 4 y/x = 2

При этом ошибки, связанной с делением на ноль, не произойдет, т.к. если значение элемента массива будет равно 0, то будет выполнено усло­вие в строке 9 и выполнение цикла for будет прервано.

В качестве аргумента break может быть указана метка. Как и в случае с continue, нельзя указывать в качестве аргумента метки блоков, в которые оператор break не содержится.

Именованные блоки

в реальной практике достаточно часто используются вложенные dfffjiu. Соответственно, может  возникнуть ситуация, когда из вложенно-f|< цикла нужно прервать внешний. Простое использование break или continue не решает этой задачи, однако в Java можно именовать блок кода явно указать операторам, к какому из них относится выполняемое действие. Делается это путем присвоения метки операторам do, while, for.

Метка — это любая допустимая в данном контексте лексема, окончившаяся двоеточием.

Рассмотрим следующий пример:

intarray[][] = {....}; for(inti=0;i<5;i++){ for(]=0;j<4; ]++) {

if(array[i][j] == caseValue) break;

} }

В данном случае при выполнении условия будет прервано выполне­ние цикла по ], цикл по i продолжится со следующего значения. Для того чтобы прервать выполнение обоих циклов, используется метка:

intarray[][] = {....}; outerLoop: for(int i=0;i<5;i++) { for(j=0;j<4; j++){

if(array[i][j] == caseValue) break outerLoop;

} }

Оператор continue также может использоваться с именованными блоками.

Между   операторами   break   и   continue   есть   еще   одно существенное отличие. Оператор break может использоваться с любым именованным блоком, в этом случае его действие в чем-топохоже на действие goto. Оператор continue (как и отмечалось ранее) может быть использован только в теле цикла. То есть такая конструкция будет вполне приемлемой:

1Ь1:{

if( val > maxVal) break Ibl;

}

В то время как оператор continue здесь применять нельзя. В данном слу­чае при выполнении условия if выполнение блока с меткой 1Ы будет пре­рвано, то есть управление будет передано на оператор (выражение), сле­дующий непосредственно за закрывающей фигурной скобкой.

Метки используют пространство имен, отличное от пространства имен классов и методов.

Так, следующий пример кода будет вполне работоспособным:

public class Test { public TestO {}

public static void main(String[] args) {Test t = new TestO;

t.testO;}

void testO { Test: {test: for(int i =0;true;i++) { lf(i % 2 == 0) continue test; if(i> 10) break Test; System.out.print(i + ""); } } } }

Для составления меток применяются те же синтаксические правила, что и для переменных, за тем исключением, что метки всегда оканчива­ются двоеточием. Метки всегда должны быть привязаны к какому-либо блоку кода. Допускается использование меток с одинаковыми именами, но нельзя применять одинаковые имена в пределах видимости блока. Такая конструкция допустима:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]