23

23


printC'l tl level " + tl level + \ t2 level " + t2 level);

tl = t2;

print("2- tl level- " + tl level +

t2.level• " + t2 level), tl.level = 27.

print("3 tl.level- " + tl level + t2.level. " + t2 level).

}

} /* Output

1 tl level. 9. t2.level. 47

2 tl level- 47. t2 level. 47

3: tl.level. 27. t2 level: 27

*/// ~

Класс Tank предельно прост, и два его экземпляра (tl и t2) создаются внутри метода main(). Переменной level для каждого экземпляра придаются различные значения, а затем ссылка t2 присваивается tl, в результате чего tl изменяется. Во многих языках программирования можно было ожидать, что tl и t2 будут независимы все время, но из-за присвоения ссылок изменение объекта tl отра¬жается на объекте t2! Это происходит из-за того, что tl и t2 содержат одинако¬вые ссылки, указывающие на один объект. (Исходная ссылка, которая содержа¬лась в tl и указывала на объект со значением 9, была перезаписана во время присвоения и фактически потеряна; ее объект будет вскоре удален сборщиком мусора.)

Этот феномен совмещения имен часто называют синонимией (aliasing), и именно она является основным способом работы с объектами в Java. Но что делать, если совмещение имен нежелательно? Тогда можно пропустить при¬своение и записать

tl.level = t2 level;

При этом программа сохранит два разных объекта, а не «выбросит» один из них, «привязав» ссылки tl и t2 к единственному объекту. Вскоре вы поймете, что прямая работа с полями данных внутри объектов противоречит принципам объектно-ориентированной разработки. Впрочем, это непростой вопрос, так что пока вам достаточно запомнить, что присвоение объектов может таить в себе немало сюрпризов.

Совмещение имен во время вызова методов

Совмещение имен также может происходить при передаче объекта методу:

// operators/PassObject java

// Передача объектов методам может работать

// не так. как вы привыкли.

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

class Letter { char c;

}

public class PassObject {

static void f(Letter y) { y.c = 'z';

}

public static void main(String[] args) { Letter x = new LetterO; x.c = 'a';

printCl; x.c; " + x.c); f(x);

print("2: x.c: " + x.c);

}

} /* Output 1: x.c: a 2: x.c: z */ ///

Во многих языках программирования метод f() создал бы копию своего па¬раметра Letter у внутри своей области действия. Но из-за передачи ссылки строка

у.с = 'z';

на самом деле изменяет объект за пределами метода f().

Совмещение имен и решение этой проблемы — сложные темы. Будьте очень внимательными в таких случаях во избежание ловушек.

Арифметические операторы

Основные математические операторы остаются неизменными почти во всех языках программирования: сложение (+), вычитание (-), деление (/), умноже¬ние (*) и остаток от деления нацело (%). Деление нацело обрезает, а не округ¬ляет результат.

В Java также используется укороченная форма записи для того, чтобы одно¬временно произвести операцию и присвоение. Она обозначается оператором с последующим знаком равенства и работает одинаково для всех операторов языка (когда в этом есть смысл). Например, чтобы прибавить 4 к переменной х и присвоить результат х, используйте команду х += 4.

Следующий пример демонстрирует использование арифметических опе¬раций:

//: operators/MathOps.java // Демонстрация математических операций, import java.util.*;

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

public class MathOps {

public static void main(String[] args) {

// Создание и раскрутка генератора случайных чисел

Random rand = new Random(47); int i, j, k;

// Выбор значения от 1 до 100: j = rand.nextlnt(lOO) + 1: printC'j : " + j): к = rand.nextlnt(lOO) + 1. printC'k : " + k): i = J + k;

printC'j + к : " + i); 1 - J - k:

printC'j - к :" + i); i = к / j,

printC'k / j : " + i): i = к * j;

printC'k * j • " + i): i = к % j;

printC'k % j . " + i). j X= k:

printC'j %/ к • " + j); // Тесты для вещественных чисел float u.v.w; // также можно использовать double v = rand.nextFloatO; printC'v . " + v); w = rand.nextFloatO; print("w : " + w), u = v + w;

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

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

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

printC'v / w : " + u): // следующее также относится к типам // char, byte, short, int, long и double: u += v:

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). Операторы инкремента и декремента не только изменяют перемен¬ную, но и устанавливают ей в качестве результата новое значение.