11

11


printC'u += v . " + u): u -= v;



printC'u -= v : " + u); u *= v:



printC'u *= v : " + u): u /= v;



printC'u /= v : " + u).



}



} /* Output: j • 59 k 56 j + k • 115 j - k • 3 k / j : 0 k * j : 3304 k % j : 56 j %= k : 3 v • 0.5309454 w : 0.0534122 v + w • 0 5843576 v - w : 0.47753322 v * w : 0.028358962



v / w 9 940527 u += v : 10.471473 u -= v 9 940527 u *= v 5 2778773 u /= v : 9 940527 */// ~



Для получения случайных чисел создается объект Random. Если он создается без параметров, Java использует текущее время для раскрутки генератора, что¬бы при каждом запуске программы выдавались разные числа.



Программа генерирует различные типы случайных чисел, вызывая соответ¬ствующие методы объекта Random: nextlnt() и nextFloat() (также можно исполь¬зовать nextLong() и nextDouble()). Аргумент nextlnt() задает верхнюю границу ге¬нерируемых чисел. Нижняя граница равна 0, но для предотвращения возмож¬ного деления на 0 результат смещается на 1.



Унарные операторы плюс и минус



Унарные минус (-) и плюс (+) внешне не отличаются от аналогичнхы бинар¬ных операторов. Компилятор выбирает нужный оператор в соответствии с кон¬текстом использования. Например, команда



х = -а;



имеет очевидный смысл. Компилятор без труда разберется, что значит



х = а * -Ь;



но читающий код может запутаться, так что яснее будет написать так:



х = а * (-Ь):



Унарный минус меняет знак числа на противоположный. Унарный плюс су¬ществует «для симметрии», хотя и не производит никаких действий.



Автоувеличение и автоуменьшение



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



Два наиболее полезных сокращения — это операторы увеличения (инкре¬мента) и уменьшения (декремента) (также часто называемые операторами ав¬томатического приращения и уменьшения). Оператор декремента записывается в виде — и означает «уменьшить на единицу». Оператор инкремента обознача¬ется символами ++ и позволяет «увеличить на единицу». Например, если пере¬менная а является целым числом, то выражение ++а будет эквивалентно (а = а + 1). Операторы инкремента и декремента не только изменяют перемен¬ную, но и устанавливают ей в качестве результата новое значение.



Каждый из этих операторов существует в двух версиях — префиксной и постфиксной. Префиксный инкремент значит, что оператор ++ записывается перед переменной или выражением, а при постфиксном инкременте оператор следует после переменной или выражения. Аналогично, при префиксном дек¬ременте оператор — указывается перед переменной или выражением, а при



постфиксном — после переменной или выражения. Для префиксного инкре¬мента и декремента (то есть ++а и —а) сначала выполняется операция, а затем выдается результат. Для постфиксной записи (а++ и а—) сначала выдается зна¬чение, и лишь затем выполняется операция. Например:



// operators/AutoInc java



import static net mindview util Print *.



public class Autolnc {



i); + ++i) + i++) i). + --i) + i-) i),



// Префиксный инкремент



// Постфиксный инкремент



// Префиксный декремент



// Постфиксный декремент



public static void main(String[] args) { int i = 1; printC'i : print("++i print("i++ printC'i • print C'--i printC'i-- printC'i



} /* Output- i . 1 ++i • 2 i++ . 2 i . 3 -i . 2 1-- 2



i • 1 *///.-



Вы видите, что при использовании префиксной формы результат получает¬ся после выполнения операции, тогда как с постфиксной формой он доступен до выполнения операции. Это единственные операторы (кроме операторов при¬сваивания), которые имеют побочный эффект. (Иначе говоря, они изменяют свой операнд вместо простого использования его значения.)



Оператор инкремента объясняет происхождение названия языка С++; под¬разумевается «шаг вперед по сравнению с С». В одной из первых речей, посвя¬щенных Java, Билл Джой (один из его создателей) сказал, что «Java=C++—» («Си плюс плюс минус минус»). Он имел в виду, что Java — это С++, из кото¬рого убрано все, что затрудняет программирование, и поэтому язык стал гораз¬до проще. Продвигаясь вперед, вы увидите, что отдельные аспекты языка, ко¬нечно, проще, и все же Java не настолько проще С++.



Операторы сравнения



Операторы сравнения выдают логический (boolean) результат. Они проверяют, в каком отношении находятся значения их операндов. Если условие проверки истинно, оператор выдает true, а если ложно — false. К операторам сравнения относятся следующие: «меньше чем» (<), «больше чем» (>), «меньше чем или равно» (<=), «больше чем или равно» (>=), «равно» (==) и «не равно» (!=). «Рав¬но» и «не равно» работают для всех примитивных типов данных, однако ос¬тальные сравнения не применимы к типу boolean.



Проверка объектов на равенство



Операции отношений == и != также работают с любыми объектами, но их смысл нередко сбивает с толку начинающих программистов на Java. Пример:



//: operators/AutoInc.java



public class Equivalence {



public static void main(String[] args) { Integer nl = new Integer(47); Integer n2 = new Integer(47); System.out.println(nl == n2); System out println(nl != n2);



}



} /* Output.



false



true



*///:-



Выражение System.out.println(nl == n2) выведет результат логического срав¬нения, содержащегося в скобках. Казалось бы, в первом случае результат дол¬жен быть истинным (true), а во втором — ложным (false), так как оба объекта типа Integer имеют одинаковые значения. Но в то время как содержимое объек¬тов одинаково, ссылки на них разные, а операторы != и == сравнивают именно ссылки. Поэтому результатом первого выражения будет false, а второго — true. Естественно, такие результаты поначалу ошеломляют.



А если понадобится сравнить действительное содержимое объектов? При¬дется использовать специальный метод equals(), поддерживаемый всеми объек¬тами (но не примитивами, для которых более чем достаточно операторов == и !=). Вот как это делается:



//: operators/EqualsMethod.java



public class EqualsMethod {



public static void main(String[] args) { Integer nl = new Integer(47); Integer n2 = new Integer(47); System.out.println(nl.equal s(n2));



}



} /* Output:



true



*///:-



На этот раз результат окажется «истиной» (true), как и предполагалось. Но все не так просто, как кажется. Если вы создадите свой собственный класс вроде такого:



//: operators/EqualsMethod2 java



// Метод equals() по умолчанию не сравнивает содержимое



class Value { int i;



}



public class EqualsMethod2 {



public static void main(String[] args) {



Value vl = new ValueO.



Value v2 = new ValueO.



vl.i = v2 i = 100;



System out println(vl equals(v2));



}



} /* Output false *///.-



мы вернемся к тому, с чего начали: результатом будет false. Дело в том, что ме¬тод equals() по умолчанию сравнивает ссылки. Следовательно, пока вы не пере¬определите этот метод в вашем новом классе, не получите желаемого результа¬та. К сожалению, переопределение будет рассматриваться только в главе 8, а пока осторожность и общее понимание принципа работы equals() позволит из¬бежать некоторых неприятностей.



Большинство классов библиотек Java реализуют метод equals() по-своему, сравнивая содержимое объектов, а не ссылки на них.



Логические операторы



Логические операторы И (&&), ИЛИ (||) и НЕ (!) производят логические значе¬ния true и false, основанные на логических отношениях своих аргументов. В следующем примере используются как операторы сравнения, так логические операторы:



//: operators/Bool Java



// Операторы сравнений и логические операторы.



import java.util.*;



import static net.mindview.util.Print.*;



public class Bool {



public static void main(String[] args) { Random rand = new Random(47); int i = rand.nextlnt(lOO): int j = rand.nextlnt(lOO); printC'i = " + i); printC'j = " + J): printC'i > j is " + (i > j)); printC'i < j is " + (i < j)); printC'i >= j is " + (i >= j)); printC'i <= j is " + (i <= j)); printC'i == j is " + (i == j)); printC'i != j is " + (i != j)); // В Java целое число (int) не может // интерпретироваться как логический тип (boolean) //! printC'i && j is " + (i && j)); //! printC'i || j is " + (i || j)); //! printC!i is " + !i); printed < 10) && (j < 10) is "



+ (d < 10) && (j < 10)) ); printed < 10) || (j < 10) is "



+ (d < 10) || (J < 10)) );



}



} /* Output: i = 58



J = 55



i > j is true i < j is false i >= j is true i <= j is false i == j is false i != j is true



(i < 10) && (j < 10) is false (i <10) || (j « 10) is false */// ~



Операции И, ИЛИ и НЕ применяются только к логическим (boolean) значе¬ниям. Нельзя использовать в логических выражениях не-Ьоо1еап-типы в качест¬ве булевых, как это разрешается в С и С++. Неудачные попытки такого рода видны в строках, помеченных особым комментарием //! (этот синтаксис позво¬ляет автоматически удалять комментарии для удобства тестирования). После¬дующие выражения вырабатывают логические результаты, используя операто¬ры сравнений, после чего к полученным значениям примененяются логические операции.



Заметьте, что значение boolean автоматически переделывается в подходящее строковое представление там, где предполагается использование строкового типа String.



Определение int в этой программе можно заменить любым примитивным типом, за исключением boolean. Впрочем, будьте осторожны с вещественными числами, поскольку их сравнение проводится с крайне высокой точностью. Число, хотя бы чуть-чуть отличающееся от другого, уже считается неравным ему. Число, на тысячную долю большее нуля, уже не является нулем.



Ускоренное вычисление



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



//. operators/ShortCircuit.java // Демонстрация ускоренного вычисления // при использовании логических операторов, import static net mindview util Print *;



public class ShortCircuit {



static boolean testl(int val) {



print ("testlC + val + ")"); print("результат- " + (val < 1)); return val <1,



}



static boolean test2(int val) {



print("test2(" + val + ")"); print("результат- " + (val < 2)); return val <2,



}



static boolean test3(int val) {



pnnt("test3(" + val + ")"); print("результат: " + (val < 3)). return val <3;



}



public static void main(String[] args) {



boolean b = testl(O) && test2(2) && test3(2); print ("выражение: " + b);



}



} /* Output: testl(O) результат: true test2(2)



результат: false выражение: false *///:-



Каждый из методов test() проводит сравнение своего аргумента и возвраща¬ет либо true, либо false. Также они выводят информацию о факте своего вызова. Эти методы используются в выражении



testl(O) && test2(2) && test3(2)



Естественно было бы ожидать, что все три метода должны выполняться, но результат программы показывает другое. Первый метод возвращает резуль¬тат true, поэтому вычисление выражения продолжается. Однако второй метод выдает результат false. Так как это автоматически означает, что все выражение будет равно false, зачем продолжать вычисления? Только лишняя трата вре¬мени. Именно это и стало причиной введения в язык ускоренного вычисления; отказ от лишних вычислений обеспечивает потенциальный выигрыш в произ¬водительности.



Литералы



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



//: operators/Literals.java



import static net.mindview.util.Print.*:



public class Literals {



public static void main(String[] args) {



int il = 0x2f; // Шестнадцатеричное (нижний регистр)



printC'ii: " + Integer.toBinaryString(il));



int i2 = 0X2F; // Шестнадцатеричное (верхний регистр)



print("i2: " + Integer.toBinaryString(i2));



int i3 = 0177: // Восьмеричное (начинается с нуля)



print("i3: " + Integer.toBinaryString(i3)):



char с = Oxffff; // макс, шестнадцатеричное знач. char