© 2008 Наталия Македа
Все материалы блога защищены авторским правом. Любая перепечатка или использование материалов этого блога в коммерческих целях возможна лишь с письменного согласия автора. При некоммерческом использовании ссылка на блог обязательна.

воскресенье, 12 октября 2008 г.

Итоги главы 2 и тест по операторам (Выпуск 12)

Резюме главы 2 об операторах:

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

Различие между равенством объектов и равенством ссылок. Функциональность метода equal() в классах Object, String, Boolean (они сравнивают инкапсулированные данные).

Тест

  1. После выполнения кода ниже каковы будут значения переменных x, a, и b?
    1. int x, a = 6, b = 7;
    2. x = a++ + b++;
    1. x = 15, a = 7, b = 8
    2. x = 15, a = 6, b = 7
    3. x = 13, a = 7, b = 8
    4. x = 13, a = 6, b = 7
  2. Какие из нижеследующих выражений допустимы? (Выберите все допустимые)
    1. int x = 6; x = !x;
    2. int x = 6; if (!(x > 3)) {}
    3. int x = 6; x = ~x;
  3. Какие выражения возвратят положительное значение переменной x?
    1. int x = -1; x = x >>> 5;
    2. int x = -1; x = x >>> 32;
    3. byte x = -1; x = x >>> 5;
    4. int x = -1; x = x >> 5;
  4. Какие из нижеследующих выражений допустимы? (Выберите все допустимые)
    1. String x = “Hello”; int y = 9; x += y;
    2. String x = “Hello”; int y = 9; if (x == y) {}
    3. String x = “Hello”; int y = 9; x = x + y;
    4. String x = “Hello”; int y = 9; y = y + x;
    5. String x = null;int y = (x != null) && (x.length() > 0) ? x.length() : 0;
  5. Какие из нижеследующих выражений откомпилируются и напечатают “Equal” во время выполнения? (Выберите все допустимые)
    1. int x = 100; float y = 100.0F;
      if (x == y)
      System.out.println("Equal");
    2. int x = 100; Integer y = new Integer(100);
      if (x == y)
      System.out.println("Equal");
    3. Integer x = new Integer(100);
      Integer y = new Integer(100);
      if (x == y)
      System.out.println("Equal");
    4. String x = new String("100");
      String y = new String("100");
      if (x == y)
      System.out.println("Equal");
    5. String x = "100"; String y = "100";
      if (x == y)
      System.out.println("Equal");
  6. Каков результат выполнения кода ниже?
    1. public class Short {
    2. public static void main(String args[]) {
    3. StringBuffer s = new StringBuffer(“Hello”);
    4. if ((s.length() > 5) &&
    5. (s.append(“ there”).equals(“False”)))
    6. ; // do nothing
    7. System.out.println(“value is “ + s);
    8. }
    9. }
    1. Hello
    2. Hello there
    3. Ошибка компиляции в 4-й или 5-й строке
    4. Ничего не выводится
    5. Исключение NullPointerException
  7. Каков результат выполнения кода ниже?
    1. public class Xor {
    2. public static void main(String args[]) {
    3. byte b = 10; // 00001010 binary
    4. byte c = 15; // 00001111 binary
    5. b = (byte)(b ^ c);
    6. System.out.println(“b contains ” + b);
    7. }
    8. }
    1. b contains 10
    2. b contains 5
    3. b contains 250
    4. b contains 245
  8. Каков результат компиляции и выполнения кода ниже?
    1. public class Conditional {
    2. public static void main(String args[]) {
    3. int x = 4;
    4. System.out.println(“value is “ +
    5. ((x > 4) ? 99.99 : 9);
    6. }
    7. }
    1. value is 99.99
    2. value is 9
    3. value is 9.0
    4. Ошибка компиляции в пятой строке
  9. Каков результат выполнения кода ниже?
    1. int x = 3; int y = 10;
    2. System.out.println(y % x);
    1. 0
    2. 1
    3. 2
    4. 3
  10. Каков результат выполнения кода ниже?
    1. int x = 1;
    2. String [] names = { “Fred”, “Jim”, “Sheila” };
    3. names[--x] += “.”;
    4. for (int i = 0; i < names.length; i++) {
    5. System.out.println(names[i]);
    6. }
    1. Fred.\nJim\nSheila
    2. Fred\nJim.\nSheila
    3. Fred\nJim\nSheila.
    4. Fred\nJim\nSheila
    5. Исключение ArrayIndexOutOfBoundsException


Читать далее!

среда, 24 сентября 2008 г.

2.9 Оператор присваивания (Выпуск 11)

Операторы присваивания устанавливают значение переменной или выражения в новое значение. Присваивание поддерживается рядом других операторов. Простое присваивание использует знак "=". Такие операторы, как, например, "*=", "+=" выполняют композиционную функцию, имеющую значение "вычислить и присвоить". Такие композиционные операторы имеют общую форму op=, где op - любой бинарный небулевый оператор, рассмотренный в данной главе.

Для любых выражений x и y совместимого типа выражение x op= y является эквиваленным выражению x = x op y. Но есть и отличия:

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

Например:

1. byte x = 2;
2. x += 3;

В расширенном варианте это выглядит так:

1. byte x = 2;
2. x = (byte)(x + 3);

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

Выражение x+=2 всего лишь на два символа короче своей расширенной формы x = x + 2. Но представьте себе, что x имеет более сложный вид, например:
target[temp.calculateOffset(1.9F) + depth++].item. И тогда читаемость этого выражения существенно возрастает!

Любое присваивание обладает значением

Все операторы, которые мы обсудили в данной главе возвращают некое значение в результате выполнения операции. Например, выражение 1+2 возвращает значение 3 в результате выполнения операции сложения. И это значение может быть использовано на следующих шагах программы, например, оно может быть присвоено некоторой переменной. Оператор присваивания также возвращает значение. Например, если нам даны три переменные a, b, c, то допустимо выражение a = b = c = 0. Это выражение вычисляется справа налево, то есть ноль сначала присваивается переменной c. И присваивание возвращает значение 0, которое присваивается переменной b. А потом аналогичным образом ноль присваивается переменной a.

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


Читать далее!

2.8 Условный оператор ? : (Выпуск 11)

Условный оператор ? :, известный также, как тройной оператор позволяет закодировать условие if/else в одну строку. Если условие истинно, то результат - левое выражение от двоеточия, иначе - правое. Заметьте, что выражения слева и справа от двоеточия должны быть приводимы к типу переменной, к которой эти выражения присваиваются. Например, пусть a, b, c - это переменные типа int, а x - переменная типа boolean. Тогда выражение a = x ? b : c эквивалентно следующему фрагменту кода:

1. if (x) {
2. a = b;
3. }
4. else {
5. a = c;
6. }

Конечно, вместо a, b, c и x могут быть использованы выражения гораздо более сложного вида.

Некоторые не любят условный оператор и в некоторых компаниях он и вовсе запрещён к использованию внутренними правилами компании. Да, этот оператор позволяет создавать компактный код. Но в большинстве случаев компилятор сгенерирует такой же компактный и эффективный код из более понятной конструкции if/else. Кроме того, приятный и компактный условный оператор приобретает иной лик, если использовать его несколько раз во вложенном виде, например:
a = b ? c ? d : e ? f : g : h ? i : j ? k : l;

Резюмируем тонкости использования условного оператора на примере выражения a = x ? b : c:

  • Типы выражений b и c должны быть совместимыми и даже идентичными, если их привести.
  • Тип выражения x должен быть boolean.
  • Типы выражений b и c должны быть совместимыми с типом переменной a.
  • Переменной a присваивается b, если x = true, и c - в противном случае (x = false).


Читать далее!

среда, 20 августа 2008 г.

2.7 Укороченные (short-circuit) логические операторы (Выпуск 10)

Укороченные (short-circuit) логические операторы && и || предназначаются для логических AND (И) и OR (ИЛИ) операций над выражениями типа boolean. Заметьте, что для XOR (исключающее ИЛИ) операции не существует укороченного логического оператора.

Укороченные логические операторы похожи на операторы & и |, но в отличие от них применяются только к выражениям типа boolean и никогда не применяются к интегральным типам. Тем не менее, && и || обладают замечательным свойством: они укорачивают вычисление выражения, если результат может быть дедуцирован из части выражения (чуть позже я поясню это на примерах). Благодаря этому свойству, операторы && и || широко используются для обработки null-выражений. Они также помогают увеличить эффективность всей программы.

Свойство укорачивания вытекает прямым образом из различий между &&/|| и &/|: последняя пара операторов должна получать на вход значения левого и правого операторов, в то время как для первой пары операторов иногда достаточно знать значение только левого операнда, чтобы вернуть значение всего выражения. Такое поведение укороченных логических операторов базируется на двух математических правилах логической таблицы истинности:

  • выражение с AND оператором ложно (false), если значение хотя бы одного из его операндов ложно;
  • выражение с OR оператором истинно (true), если значение хотя бы одного из его операндов истинно.
Иными словами:
  • false AND X = false
  • true OR X = true
То есть, если левый операнд AND выражения ложен, то и всё выражение ложно, вне зависимости от значения правого операнда. То есть при ложном левом операнде нет нужны вычислять значение правого операнда. Аналогичным образом, если левый операнд OR выражения истиннен, то истинно и всё выражения, вне зависимости от значения правого операнда, которое, следовательно, нам не нужно вычислять.

Рассмотрим пример кода, который выводит сообщение String, если строка не нулевая и более 20 символов длиной:

1. if ((s != null) && (s.length() > 20)) {
2.     System.out.println(s);
3. }

Эта же задача может быть закодиравана по-другому:
 
1. if (s != null) {
2.     if (s.length() > 20) {
3.          System.out.println(s);
4.     }
5. }

Если бы строка s была null, то при вызове метода s.length() мы бы получили NullPointerException. Ни в одном из двух примеров кода, однако, такая ситауция не возникнет. В частности, во втором примере, s.length() не вызывается при s = null, благодаря использованию укороченного оператора &&. Если бы тест (s!=null) возвращал ложь (false), то есть s - несуществующая строка, то и всё выражение гарантированно ложно. Значит, отпадает необхеодимость вычислять значение второго операнда, то есть выражения (s.length()>20).

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

Рассмотрим пример:
// первый пример
1. int val = (int)(2 * Math.random());
2. boolean test = (val == 0) || (++val == 2);
3. System.out.println(“test = “ + test + “\nval = “ + val);

// второй пример
1. int val = (int)(2 * Math.random());
2. boolean test = (val == 0)|(++val == 2);
3. System.out.println(“test = “ + test + “\nval = “ + val);

Первый пример иногда будет выводить на печать вот это:
 
test = true
val = 0


А иногда вот это:
 
test = true
val = 2


Второй пример иногда будет выводить на печать вот это:
 
test = true
val = 1


А иногда вот это:
 
test = true
val = 2


А дело вот в чём. Если val равно 0, то второй операнд (++val) никогда не будет вычислен, то есть val останется равным нулю. Если же изначально val равен единице, то в результате эта переменная будет инкрементирована и мы увидим val = 2. Во втором примере, при использовании неукороченных операторов, инкремент выполняется всегда и результат будет всегда или 1 или 2 в зависимости от случайного значения выбранного на первом шаге. В обоих примерах переменная test принимает значение true, потому что либо val = 0, либо val = 1 и инкрементируется до значения 2.

Резюмируем информацию об укороченных операторах && и ||:
  • Они применяются к операндам типа boolean;
  • Они вычисляют значение правого операнда только если результат не может быть вычислен на основании значения левого операнда:
    • false AND X = false
    • true OR X = true


Читать далее!

воскресенье, 29 июня 2008 г.

Откуда скачать Java (Выпуск 9)

Получила первое письмо от читателя рассылки, в котором вопрос: откуда скачать Java. Скачать Java очень просто:

  1. Идём на сайт java.sun.com.
  2. На открывшейся странице в верхней менюшке наводим курсором на Downloads и из выпадающей менюшке выбираем Java SE (подсвечено оранжевым на рисунке ниже).
  3. На открывшейся странице (см. рис. ниже) нам нужно выбрать (кнопка Download) JDK 6 Update 6 (отмечено красным).
  4. На открывшейся странице необходимо выбрать платформу (Windows, Linux и т.д.). И опция Multi-language, по-моему, там единственная. Отметьте также галочкой License Agreement и нажмите оранжевую кнопку Continue.
  5. Наконец мы добрались до ссылки на скачивание. При этом вы можете выбрать либо пакет для он-лайн инсталляции, либо пакет для офф-лайн инсталляции. Он-лайн инсталляция, конечно, занимает мало (0.36Мб)по сравнению со вторым пакетом (71.49Мб), но это только кажимость. Всё равно все недостающие компоненты будут подкачиваться из интернета в процессе инсталляции. Так что все 70 мег вам так или иначе придётся скачать. Единственная разница в том, что в первом случае (он-лайн инсталляция) вы ещё окажетесь связанными по рукам, если вдруг у вса не окажется под рукой интернета при переустановки джавы. Так что я рекомендую скачивать офф-лайн инсталляцию (отмечено красным карандашом на рис. ниже).
  6. Скачиваемый файл в моём случае (винда) называется jdk-6u6-windows-i586-p.exe. И я егос охранила на диск (Salva, Save).

Скачивание длилось у меня где-то 10 минут.

Потом нужно просто запустить скачанный файл. Установится JRE (Java Run-time Edition), то есть всё то, что необходимо для функционирования джавы под платформу, выбранную на шаге 4. А также устанится JDK (Java Development Kit), то есть всё, что нужно для программирования: документация, библиотеки, примеры...


Читать далее!

2.6 Поразрядные операторы: &, ^ и | (Выпуск 9)

Поразрядные операторы &, ^ и используются для выполнения побитовых операций AND, XOR и OR соответственно. Они применяются к целочисленным типам. Набор битов является эффективным средством экономии памяти, когда некое состояние (например, данные физических устройств) необходимо представить в виде вектора значений типа boolean.

Результат применения операторов &, ^ и к двум операндам описывается таблицами 1, 2 и 3 соответственно.

Таблица 1: Операция AND
Операнд1Операнд2Операнд1 AND Операнд2

0

0

0

0

1

0

1

0

0

1

1

1



Таблица 2: Операция OR
Операнд1Операнд2Операнд1 OR Операнд2

0

0

0

0

1

1

1

0

1

1

1

1



Таблица 3: Операция XOR
Операнд1Операнд2Операнд1 XOR Операнд2

0

0

0

0

1

1

1

0

1

1

1

0


Для AND операции результат будет истинным (1), если оба операнда истинны. Для OR оператора результат истинный, если хотя бы один операнд истинный. Для XOR оператора результат истинный, если один и только один (не оба!) операнд истинный. XOR - это исключающий (eXclusive) OR.

Сейчас мы рассмотрим работу этих операторов не над одним единственным битом, но над битовым представлением числа.

Пример 1.

    00110011
11110000
AND --------
00110000

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

Пример 2.

    00110011
11110000
XOR --------
11000011
   00110011
11110000
OR --------
11110011

Для вычисления результата операций XOR и OR необходимо воспользоваться таблицами 3 и 2 соответсвенно для соответсвующих разрядов первого (верхнего) и второго (нижнего) операндов. Заметьте, что операции AND, OR, XOR коммутативны, то есть результат изменится прежним, даже если мы поменяем операнды местами.

Аналогичным образом таблицы 1, 2, 3 можно применить и к значениям типа boolean, где 1 - true, 0 - false. При этом операнды должны быть булевого типа (например, a==10) и операторы &, ^ и применяются не побитово, а ко всему булевому выражению.

Оба операнда должны быть либо целочисленного типа (и тогда операторы &, ^ и применяются поразрядно), либо булевого типа (и тогда операторы применяются ко всему выражению). В Java нельзя привести тип к boolean. Вместо этого необходимо использовать булевы выражения, например, сравнения.


Читать далее!

среда, 28 мая 2008 г.

2.5 Операторы сравнения (Выпуск 8)

Операторы сравнения, <, >, <=, >=, ==, !=, возвращают результат типа boolean, то есть true или false. Эти операторы обычно используются в условных конструкциях (например, if () или циклы). Существует три типа сравнения:

  1. порядковое (ordinal) тестирует относительные значения числовых операндов.
  2. объектно-ориентированное определяет тип объекта во время исполнения программы.
  3. операторы равенства тестируют, одинаковы ли два значения, в том числе и нечисленные.

Рассмотрим их все подробнее.

Порядковые операторы (<, >, <=, >=)

К порядковым операторам относятся < (меньше), > (больше), <= (меньше либо равно), >= (больше либо равно). Они применяются ко всем численным типам и типу char и возвращают результат boolean. Например, если у нас имеются следующие объявления:

int p = 9;
int q = 65;
int r = -12;
float f = 9.0F;
char c = ‘A’;

то следующие тесты возвратят true:

p < q
f < q
f <= c
c > r
c >= q

Заметьте, что, когда эти операторы используются, применяется арифметическое распространение. Например, будет ошибкой присвоение значение 9.0F типа float переменной c типа char. Но к этой паре может быть применено сравнение! Для этого Java распространяет меньший тип к большему типу. То есть значение 'A' типа char (представляемое значением 65 в Unicode) распространяется до float 65.0F. Сравнение затем выполняется на результирующей паре значений float.

Порядковые сравнения не могут быть применены к объектным типам!

Оператор instanceof

Оператор instanceof тестирует класс объекта в момент исполнения программы (runtime). Левый операнд - любое выражение объектного типа (переменная или массив). Правый операнд - имя класса, интерфейса или массивный тип. Однако, левым операндом не может быть объект типа java.lang.Class, а правым операндом не может быть String.

Фрагмент кода ниже показывает пример использования оператора instanceof. Предположим, что существует класс Person с подклассом Parent:

1. public class Classroom {
2. private Hashtable inTheRoom = new Hashtable();
3. public void enterRoom(Person p) {
4. inTheRoom.put(p.getName(), p);
5. }
6. public Person getParent(String name) {
7. Object p = inTheRoom.get(name);
8. if (p instanceof Parent) {
9. return (Parent)p;
10. }
11. else {
12. return null;
13. }
14. }
15. }

Метод getParent() в строках 6-14 проверяет, содержит ли Hashtable родителя со специфическим именем. Для этого в Hashtable сначал ищется элемент со специфическим именем родителя. А потом проверяется, является ли этот элемент объектом типа Parent. Оператор instanceof проверяет, совпадает ли класс левого операнда с названием класса, заданного правым операндом или является его подклассом.

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

Опертор также используется для тестирования, является ли объект массивом. Поскольку массивы в Java являются объектами, то такое тестирование логично. Но тест проводится в два шага: (1) является ли объект массивом и (2) является ли тип элементов массива (под)классом правого аргумента. Это отражает идею, что массив, скажем, объектов типа Button (кнопки) является массивом объектов типа Component (компоненты), поскольку Button - это Component. Тест для такого массива будет выглядеть так:

if (x instanceof Component[])

Заметьте, что невозможно провести тестирование для "любого массива с элементами любого типа". То есть следующая строка недопустима:

if (x instanceof [])

Недопустимо также тестирование на массив с элементами типа Object:

if (x instanceof Object [])

поскольку элементы массива могут быть примитивного типа и тест, попросту, не сработает.

А как же протестировать, является ли объект массивом без привязки к типу его элементов? Допустим myObject это массив. Тогда следующая строка возвратит true:

myObject.getClass().isArray()

Если левый операнд равен null, то instanceof возвратит false. Никокого исключения не возникнет.

Операторы равенства (== и !=)

Операторы == и != тестируют, соответственно, равенство или неравенство. Для примитивных типов концепция равенства или неравенства достаточно тривиальна. Как и в случае с операторами порядка, операнды распространяются до наибольшего. Например, значение 10.0 типа float равно значению 10 типа byte. Для значений объектного типа, сравниваемая величина - это ссылка на объект, то есть адрес памяти.

Никогда не используйте операторы равенства для сравнения объектных типов (например строк)! Потому что они возвращают true только если адрес памяти совпадает, а не поля объектов совпадают. Для сравнения объектов используется метод equals().

Метод equals() должен был определён в классе сравниваемых объектов. Чтобы быть уверенными в этом, проверьте документацию. В документации должно быть указано, что equals() определён в классе или переопределяет (overrides) метод equals() своего супер-класса. Если документация ничего по этому поводу не говорит, то метод equals() будет, скорее всего, работать неправильно. Метод equals() принимает аргумент типа Object. Но в реальности вы должны передавать ему аргумент того же типа, что и объект, для которого он вызывается. Например, если вы вызываете x.equals(y), то y instanceof x должно возвращать true. Если же это не так, то equals() вернёт false.
Если вы определяете equals() в вашем собственном классе, то вы должны соблюдать три правила:
  • Аргумент для equals() должен быть объект типа Object. Не поддавайтесь искушению вставить аргумент того же типа, что и класс. Иначе вы перегрузите (overload) метод, а не переопределите (override) его. В итоге функциональность других частей кода, зависящих от equals() будет неправильна. Например, итерации по HashMap, то есть containsKey() и get(), будут неправильны.
  • equals() должен быть коммутативным оператором. То есть результат x.equals(y) должен быть равен результату y.equals(x).
  • Если вы лпределяете equals() в своём классе, то вы должны определить и hashCode(), который возвращает одно и то же значение для объектов, сравниваемых методом equals(). Это, опять таки, нужно для правильного функционирования итераций по контейнерам. Минимально допустимое поведение hashCode() - это return 1. Конечно, при такой имплементации потеряются все преимущества "настоящего" хеширования и HashMap будет просто связным списком. Но, по крайней мере, функциональность будет корректной.


Читать далее!