Заметьте, что значение 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
printC'c: " + Integer.toBinaryString(c));
byte b = 0x7f. // макс шестнадцатеричное знач. byte
printC'b " + Integer toBinaryString(b));
short s = 0x7fff. // макс шестнадцатеричное знач. short
printC's " + Integer.toBinaryString(s));
long nl = 200L; // Суффикс, обозначающий long
long n2 = 2001, // Суффикс, обозначающий long (можно запутаться)
long n3 = 200,
float fl = 1,
float f2 = IF; // Суффикс, обозначающий float float f3 = If, // Суффикс, обозначающий float double dl = Id, // Суффикс, обозначающий double double d2 = ID; // Суффикс, обозначающий double
}
} /* Output- il 101111 12 101111 13: 1111111 c: 1111111111111111 b: 1111111 s- 111111111111111 *///:-
Последний символ обозначает тип записанного литерала. Прописная или строчная буква L определяет тип long (впрочем, строчная I может создать про¬блемы, потому что она похожа на цифру 1); прописная или строчная F соответ¬ствует типу float, а заглавная или строчная D подразумевает тип double.
Шестнадцатеричное представление (основание 16) работает со всеми встро¬енными типами данных и обозначается префиксом Ох или ОХ с последующим числовым значением из цифр 0-9 и букв a-f, прописных или строчных. Если при определении переменной задается значение, превосходящее максимально для нее возможное (независимо от числовой формы), компилятор сообщит вам об ошибке. В программе указаны максимальные значения для типов char, byte и short. При выходе за эти границы компилятор автоматически сделает значе¬ние типом int и сообщит вам, что для присвоения понадобится сужающее при¬ведение.
Восьмеричное представление (по основанию 8) обозначается начальным ну¬лем в записи числа, состоящего из цифр от 0 до 7. Для литеральной записи чи¬сел в двоичном представлении в Java, С и С++ поддержки нет. Впрочем, при ра¬боте с шестнадцатеричныыми и восьмеричными числами часто требуется по¬лучить двоичное представление результата. Задача легко решается методами static toBinaryString() классов Integer и Long.
Экспоненциальная запись
Экспоненциальные значения записываются, по-моему, очень неудачно: 1.39e-47f. В науке и инженерном деле символом е обозначается основание натурального логарифма, равное примерно 2,718. (Более точное значение этой величины можно получить из свойства Math.E.) Оно используется в экспоненциальных выражениях, таких как 1,39хе-47, что фактически значит 1,39х2,718-47. Одна¬ко во время изобретения языка FORTRAN было решено, что е будет обозначать «десять в степени», что достаточно странно, поскольку FORTRAN разрабатывался для науки и техники и можно было предположить, что его создатели обратят внимание на подобную неоднозначность1. Так или иначе, этот обычай был пе¬ренят в С, С++, а затем перешел в Java. Таким образом, если вы привыкли ви¬деть в е основание натурального логарифма, вам придется каждый раз делать преобразование в уме: если вы увидели в Java выражение 1.39e-43f, на самом деле оно значит 1,39х10~43.
Если компилятор может определить тип автоматически, наличие завершаю¬щего суффикса типа не обязательно. В записи
long пЗ = 200;
не существует никаких неясностей, и поэтому использование символа L после значения 200 было бы излишним. Однако в записи
float f4 = le-43f; // десять в степени
компилятор обычно трактует экспоненциальные числа как double. Без завер¬шающего символа f он сообщит вам об ошибке и необходимости использования приведения для преобразования double к типу float.
Поразрядные операторы
Поразрядные операторы манипулируют отдельными битами в целочисленных примитивных типах данных. Результат определяется действиями булевой ал¬гебры с соответствующими битами двух операндов.
Эти битовые операторы происходят от низкоуровневой направленности языка С, где часто приходится напрямую работать с оборудованием и устанав¬ливать биты в аппаратных регистрах. Java изначально разрабатывался для управления телевизионными приставками, поэтому эта низкоуровневая ори¬ентация все еще была нужна. Впрочем, вам вряд ли придется часто использо¬вать эти операторы.
Поразрядный оператор И (&) заносит 1 в выходной бит, если оба входных бита были равны 1; в противном случае результат равен 0. Поразрядный опера¬тор ИЛИ (|) заносит 1 в выходной бит, если хотя бы один из битов операндов был равен 1; результат равен 0 только в том случае, если оба бита операндов были нулевыми. Оператор ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR, л) имеет результатом единицу тогда, когда один из входных битов был единицей, но не оба вместе. По¬разрядный оператор НЕ (~, также называемый оператором двоичного дополне¬ния) является унарным оператором, то есть имеет только один операнд. Пораз¬рядное НЕ производит бит, «противоположный» исходному — если входящий бит является нулем, то в результирующем бите окажется единица, если входя¬щий бит — единица, получится ноль.