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

ЖАНРЫ

Философия Java3

Эккель Брюс

Шрифт:

При присвоении объектов все меняется. При выполнении операций с объектом вы в действительности работаете со ссылкой, поэтому присвоение «одного объекта другому» на самом деле означает копирование ссылки из одного места в другое. Это значит, что при выполнении команды с = d для объектов в конечном итоге end указывают на один объект, которому изначально соответствовала только ссылка d. Сказанное демонстрирует следующий пример:

//: operators/Assignment java // Присвоение объектов имеет ряд хитростей import static net.mindview.util Print.*,

class Tank {

int level,

}

public class Assignment {

public static void main(String[] args) { Tank tl = new TankO. Tank t2 = new TankO, tl level = 9; t2 level = 47,

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) {

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

57

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.

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