Что может быть проще, скажете Вы? И тут бывают свои подводные камни.
Для начала, какие переменные бывают:
- Локальные переменные - объявленные внутри методов класса или блоков кода.
- Параметры - объявленные в описании метода.
- Поля - объявленные в самом классе.
Локальные переменные и параметры нам интересны чуть меньше - с ними намного реже возникают проблемы.
Поля, они же свойства, они же просто переменные с большой областью видимости и собственными нюансами применения. На них и сконцентрируем основное внимание.
Общее для переменных.
Говоря о 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
Превращает поле в статическое, иначе сказать - в поле класса. Здесь самое время показать разницу между полем объекта и полем класса в действии:
- class A {
- private int simpleField;
- private static int classField;
- public int getSimpleField() {
- return simpleField;
- }
- public void setSimpleField(int simpleField) {
- this.simpleField = simpleField;
- }
- public int getClassField() {
- return classField;
- }
- public void setClassField(int classField) {
- A.classField = classField;
- }
- }
- class Test {
- public static void main(String[] args) {
- A obj1 = new A();
- A obj2 = new A();
- obj1.setSimpleField(1);
- obj1.setClassField(2);
- obj2.setSimpleField(3);
- obj2.setClassField(4);
- System.out.print(obj1.getSimpleField()+" ");
- System.out.print(obj1.getClassField()+" ");
- System.out.print(obj2.getSimpleField()+" ");
- System.out.println(obj2.getClassField());
- //Output: 1 4 3 4
- }
- }
Как видим, поля без модификатора static имеют уникальное для каждого объекта значение, а статическое поле оказалось общим для обоих экземпляров класса. Это отражается даже в синтаксисе доступа к полю - присмотритесь к Setter-методам.
Что происходит физически? Да то же самое - обычные поля получают каждое свой участок памяти, статическое же поле в памяти выделяется всего один раз.
final
Модификатор запрещает изменение поля, и чаще употребляется в связке с модификатором static, образуя аналог констант. Тут следует быть внимательнее - полностью блокируются только примитивные переменные, для более сложных типов это лишь означает, что переменная будет иметь неизменяемый указатель на конкретный класс.
- class Test {
- public static void main(String[] args) {
- final List<Integer> list = new ArrayList<Integer>();
- list.add(1);
- list.add(2);
- list.add(3);
- for (int i = 0; i < list.size(); i++) {
- System.out.print(list.get(i)+" ");
- //Output: 1 2 3
- }
- }
- }
Таким образом запрещено лишь изменение самого указателя (новое list = new ArrayList<Integer>(); ). Чтобы добиться полностью неизменого состояния объекта, в данном случае, даже недостаточно использовать Collections.unmodifiableList - он лишь создаст неизменяемый View на изменяемый List, таким образом, оставляя надежду только за классами-обёртками, полностью скрывающими свои внутренности от нашего пытливого взора.
ThreadLocal
Чтобы окончательно завершить обзор переменных - следует упомянуть и ThreadLocal переменные, которые будут созданы только один раз во всём потоке исполнения. Это НЕ модификатор, а отдельный класс - потому особо останавливаться на этом сейчас не будем.
volatile
Если коротко - означает, что эта переменная будет изменяться разными потоками. Что это даёт? Даже в разных потоках поле будет иметь одно и то же значение(чего не гарантирует static).
Если более развёрнуто, физически - переменная будет записана только в основной памяти, и все исполняемые потоки будут вынуждены использовать не свою "быструю" память-кеш, а стучаться чуть дальше, соответственно и чуть медленнее.
Есть и ещё один минус - синхронизируются только атомарные операции с полем(чтение и запись) а все остальные (даже операция ++ не является атомарной и выполняется в три действия) могут выполняться с абсолютно непредсказуемым результатом. Для решения такого рода проблем созданы synchronized методы/блоки и целый пакет объектов, безопасно работающих с потоками (java.util.concurrent.atomic.).
Говоря открыто - многопоточность в Java достойна целой серии обширных статей Но, если Вы - начинающий разработчик и не связаны с написанием действительно сложных приложений, требующих многопоточности - зачастую будет достаточно джентельменского набора, перечисленного выше.
Комментариев нет:
Отправить комментарий