воскресенье, 26 февраля 2012 г.

Переменные в Java

Что может быть проще, скажете Вы? И тут бывают свои подводные камни.
Для начала, какие переменные бывают:
  • Локальные переменные - объявленные внутри методов класса или блоков кода.
  • Параметры - объявленные в описании метода.
  • Поля - объявленные в самом классе.

Локальные переменные и параметры нам интересны чуть меньше - с ними намного реже возникают проблемы. 
Поля, они же свойства, они же просто переменные с большой областью видимости и собственными нюансами применения. На них и сконцентрируем основное внимание.
Общее для переменных.
Говоря о Java, невозможно не упомянуть Sun Naming Code Conventions for Java , в котором оговариваются требования и рекомендации к именованию переменных:
  • Допустимые символы - 0-9, a-z, A-Z, "_", "$";
  • Имя переменной не должно начинаться с символов доллара или подчёркивания;
  • Константы рекомендуется называть полностью заглавными буквами, с символом подчёркивания между слов;
  • Имена переменных должны быть короткими, но осмысленными;

Формат определения таков: [модификаторы] [тип поля] [название поля];

Тип поля может быть любым, абсолютно. Начиная от примитивов int, boolean, float, char, short, double, long; продолжая классами String, Integer, Boolean, MyClass; и заканчивая совсем абстрактными понятиями, такими как Object или Class.

Самое простое и понятное описано, остались модификаторы, как раз и составляющие основную изюминку полей. Сразу оговоримся - исключением является модификатор final, который иногда имеет смысл применять и для локальных переменных.

Модификаторы доступа:
  • public - переменная будет видна всем, без ограничений.
  • protected - переменная будет видна внутри текущего пакета и всем подклассам(наследникам) текущего класса.
  • default - переменная будет видна только внутри текущего пакета.
  • private - переменная будет видна только внутри нашего объекта.
Как примечание: default не является ключевым словом в Java, это всего лишь понятие, чтобы обозначить состояние, когда не задано другого модификатора доступа.
Помимо этого, модификатор доступа может быть только один - выбирайте правильный. Существует сложившаяся традиция, большинство переменных класса объявлять private, а для доступа к ним создавать дополнительные методы - сеттеры и геттеры. Исключением, пожалуй, являются только константы, которые вполне могут оставаться public.

Модификаторы использования памяти:
static 
Превращает поле в статическое, иначе сказать - в поле класса. Здесь самое время показать разницу между полем объекта и полем класса в действии:

  1. class A {
  2.     private int simpleField;
  3.     private static int classField;
  4.     public int getSimpleField() {
  5.         return simpleField;
  6.     }
  7.     public void setSimpleField(int simpleField) {
  8.         this.simpleField = simpleField;
  9.     }
  10.     public int getClassField() {
  11.         return classField;
  12.     }
  13.     public void setClassField(int classField) {
  14.         A.classField = classField;
  15.     }
  16. }
  17. class Test {
  18.     public static void main(String[] args) {
  19.         A obj1 = new A();
  20.         A obj2 = new A();
  21.         obj1.setSimpleField(1);
  22.         obj1.setClassField(2);
  23.         obj2.setSimpleField(3);
  24.         obj2.setClassField(4);
  25.         System.out.print(obj1.getSimpleField()+" ");
  26.         System.out.print(obj1.getClassField()+" ");
  27.         System.out.print(obj2.getSimpleField()+" ");
  28.         System.out.println(obj2.getClassField());
  29.         //Output: 1 4 3 4
  30.     }
  31. }
Как видим, поля без модификатора static имеют уникальное для каждого объекта значение, а статическое поле оказалось общим для обоих экземпляров класса. Это отражается даже в синтаксисе доступа к полю - присмотритесь к Setter-методам.
Что происходит физически? Да то же самое - обычные поля получают каждое свой участок памяти, статическое же поле в памяти выделяется всего один раз. 

final
Модификатор запрещает изменение поля, и чаще употребляется в связке с модификатором static, образуя аналог констант. Тут следует быть внимательнее - полностью блокируются только примитивные переменные, для более сложных типов это лишь означает, что переменная будет иметь неизменяемый указатель на конкретный класс.

  1. class Test {
  2.     public static void main(String[] args) {
  3.         final List<Integer> list = new ArrayList<Integer>();
  4.         list.add(1);
  5.         list.add(2);
  6.         list.add(3);
  7.         for (int i = 0; i < list.size(); i++) {
  8.             System.out.print(list.get(i)+" ");
  9.             //Output: 1 2 3
  10.         }
  11.     }
  12. }
Таким образом запрещено лишь изменение самого указателя (новое list = new ArrayList<Integer>(); ). Чтобы добиться полностью неизменого состояния объекта, в данном случае, даже недостаточно использовать Collections.unmodifiableList - он лишь создаст неизменяемый View на изменяемый List, таким образом, оставляя надежду только за классами-обёртками, полностью скрывающими свои внутренности от нашего пытливого взора.

ThreadLocal
Чтобы окончательно завершить обзор переменных - следует упомянуть и ThreadLocal переменные, которые будут созданы только один раз во всём потоке исполнения. Это НЕ модификатор, а отдельный класс - потому особо останавливаться на этом сейчас не будем.

volatile 
Если коротко - означает, что эта переменная будет изменяться разными потоками. Что это даёт? Даже в разных потоках поле будет иметь одно и то же значение(чего не гарантирует static).
Если более развёрнуто, физически - переменная будет записана только в основной памяти, и все исполняемые потоки  будут вынуждены использовать не свою "быструю" память-кеш, а стучаться чуть дальше, соответственно и чуть медленнее.
Есть и ещё один минус - синхронизируются только атомарные операции с полем(чтение и запись) а все остальные (даже операция ++ не является атомарной и выполняется в три действия) могут выполняться с абсолютно непредсказуемым результатом. Для решения такого рода проблем созданы synchronized методы/блоки и целый пакет объектов, безопасно работающих с потоками (java.util.concurrent.atomic.).



Говоря открыто - многопоточность в Java достойна целой серии обширных статей Но, если Вы - начинающий разработчик и не связаны с написанием действительно сложных приложений, требующих многопоточности - зачастую будет достаточно джентельменского набора, перечисленного выше.

Комментариев нет:

Отправить комментарий