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

понедельник, 24 ноября 2008 г.

3.2 Модификаторы доступа (Выпуск 14)

Модификаторы доступа определяют, могут ли классы использовать разнообразные элементы (сами классы, переменные-поля классов, методы классов и коснтрукторы) и как они могут их использовать.

Существует три модификатора доступа:

  • public
  • private
  • protected

Элементы (см. выше) могут иметь один и более модификаторов, либо не иметь их вовсе. Однако, среди всевозможных модификаторов элемента количество модификаторов доступа долно быть не больше одного.

Если элемент не имеет модификатора, то доступ к нему определяется по умолчанию, то есть default. Однако, ключевого слова default не существует! Другие наименования доступа по умолчанию, которые вы можете встретить в литературе, включают в себя friendly, package.

Ниже мы приводим примеры легально объявленых элементов с модификаторами или без них. Легальность некоторых объявлений зависит от контекста.

class Parser { ... }
public class EightDimensionalComplex { ... }
private int i;
Graphics offScreenGC;
protected double getChiSquared() { ... }
private class Horse { ... }

А вот примеры нелегальных объявлений элементов:

public protected int x; // Не более одного модификатора доступа!
default Button getBtn() {...} // "default" - это не ключевое слово


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

Глава 3: Модификаторы (Выпуск 14)

Модификаторы - это ключевые слова в Java, которые сообщают компилятору информацию о природе кода, данных или классов. Например, модификаторы определяют, является ли некий элемент (класс, метод, переменная) приватным или публичным, статичным или финальным.

3.1 Обзор модификаторов

Наиболее распространённые модификаторы - модификаторы доступа: public, protected, private. О них мы поговорим сразу в следующей статье. Все остальные модификаторы не подпадают ни под какую чёткую классификацию. Мы их просто перечислим:

  • final
  • abstract
  • static
  • native
  • transient
  • synchronized
  • volatile

Каждому из этих модификаторов будет посвящена отдельная статья в будущем.


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

среда, 5 ноября 2008 г.

Ответы на тест по операторам (Выпуск 13)

Вопросы теста можно посмотреть здесь.
  1. Ответ: С
    Вычисление производится в таком порядке: x = a + b; a = a + 1; b = b + 1; Поэтому x = 6 + 7 то есть 13. После чего переменные a и b инкрементируются до значений 7 и 8 соответственно.
  2. Ответ: B, С
    В варианте A использование оператора ! незаконно, поскольку x является переменной типа int, а не boolean. На этом часто попадаются C и C++ программисты. В варианте B выражение (x<3) имеет тип boolean, поэтому перед ним можно использовать оператор !. В ответе C поразрядный оператор ~ применен к целочисленному x, значение которого равно 6, что в битовом виде имеет форму 0..0110 (на месте двух точек должны стоять 27 нулей). После применения оператора ~ x примет значение 1..1001 (на месте двух точек подставьте 27 единиц).
  3. Ответ: A
    Битовое представление -1 состоит из единиц во всех разрядах. В варианте A эта строка из единиц сдвигается вправо на 5 позиций и освободившиеся 5 первых позиций заполняются нулями. Это в десятичном выражении имеет положительное значение 134217727. В варианте B аналогичный сдвиг происходит на 32 разряда и результат состоит из строки, у которой во всех позициях нули, то есть 0. Вариант C недопустим, потому что результат выражения x <<< 5 имеет тип int и не может быть присвоен переменной типа byte без явного приведения типов, которое в данном случае отсутствует. Наконец, в варианте D при сдвиге все освободившиеся позиции принимают значение наиболее значимого разряда исходного значения, то есть -1. Результат операции не отличается от исходного значения и равен -1.
  4. Ответ: A, C, E
    Варианты A и C эквивалентны, поскольку x+=y эквивалентно x = x+y. Выражение x+y "суммирует" строки и целое число, в результате из целого числа будет создан объект строкового типа и потом будет создана новая строка (ещё один новый объект строкового типа) со значением "Hello9". В варианте B сравнение x == y незаконно, потому что целочисленный тип не может сравниваться с указателем. В варианте D в результате вычисления выражения y+x также, как и в вариантах A и C создаётся новая строка. Но в последствии она присваивается переменной целочисленного типа, а это недопустимо. Вариант E самый оригинальный. В нём важны два момента: условный оператор :? и укороченный оператор &&. Левый операнд укороченого оператора, то есть выражение (x != null), всегда равен false. Следовательно правый операнд, то есть выражение (x.length() > 0), никогда не будет вычисляться, поскольку всё выражение заведомо равняется значению false. Это значение false затем используется в выражении условного оператора, результат которого будет равен 0 (операнд после двоеточия).
  5. Ответ: A, E
    Хотя int и float и не являются совместимыми с точки зрения присваиваний, они могут легко использоваться в левых и правых частях операторов сравнения, коим и является оператор ==. При этом int 100 приводится к float 100.0 и запросто сравнивается с другим float 100.0F. Поэтому вариант A проходит. В варианте B код не откомпилируется, потому что int и Integer (указатель) не могут быть приведены даже в случае оператора сравнения. В варианте C код компилируется и сравнение может быть проведено. Но выражение x == y сравнивает два различных указателя, и поэтому результат теста равен false. Аналогичные рассыждения можно провести для варианта D. Вариант E кажется эквивалентным варианту D, но это на самом деле не так. В варианте E для строковой константы "100" создаётся объект строкового типа, а x и y являются указателями на этот объект. То есть тест (x == y) равняется true и мы получаем сообщение "Equal".
  6. Ответ: AВ четвёртой строке кода результат вычисления (s.length()>5) равен false (поскольку длина строки "Hello" равна 5). Таким образом, всё выражения условия if равно false. В результате никаких манипуляций над строкой не производится и она остаётся прежней.
  7. Ответ: B
    Побитовый XOR оператор ^ возвращает 0, если оба биты одинаковы (оба равны 0 или оба равны 1), и 1 - в противном случае. Таким образом 00001010^00001111=00000101, то есть 5 в десятичном исчислении.
  8. Ответ: CВ операторе ?: используются операнды различных типов: 99.99 (double) и 9 (int). Поэтому int приводится к double и результат всего выражения будет типа double. Поскольку x = 4, то тест (x > 4) равен false. Следовательно, результат равен значению после двоеточия, то есть 9.0 (то есть типа double).
  9. Ответ: B
    Вычисление достаточно очевидно, поскольку участвуют только положительные числа. Деление 10 на 3 даёт 3 и в остатке 1, что и является ответом. Если бы мы имели дело с дробными числами, то алгоритм вычисления остатка от деления y на x был бы таким: вычитать x из y до тех пор, пока y не станет меньше x. Как только это случится, y - остаток от деления. Для отрицательных чисел можно забыть о знаках, но иметь в виду, что знак остатка от деления равен знаку делимого (левого операнда).
  10. Ответ: AОператор присваивания op= высчитывает левое выражение только один раз. Таким образом, декремент --x происходит только раз и в результате получается 0, а не -1. То есть не возникает никаких ошибок выхода за границы (out-of-bounds). В нулевой позиции массива находится значение Fred. При этом декремент имеет место перед операцией +=. Хотя объекты типа String не могут изменять своего значения (immutable), указатели, коими являются элементы массивы, могут изменяться. То есть мы можем создать новый объект в нулевой позиции массива name, который будет иметь значение "Fred.", то есть строка Fred, заканчивающаяся точкой.


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

воскресенье, 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


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