приватный финальный статический атрибут против приватного финального атрибута - PullRequest
278 голосов
/ 12 сентября 2009

В Java какая разница между:

private final static int NUMBER = 10;

и

private final int NUMBER = 10;

Оба типа private и final, разница - атрибут static.

Что лучше? И почему?

Ответы [ 20 ]

293 голосов
/ 12 сентября 2009

Как правило, static означает "связанный с типом , а не экземпляром типа."

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

Test x = new Test();
Test y = new Test();
x.instanceVariable = 10;
y.instanceVariable = 20;
System.out.println(x.instanceVariable);

выводит 10: y.instanceVariable и x.instanceVariable являются отдельными, потому что x и y относятся к разным объектам.

Вы можете обращаться к статическим элементам через ссылки, хотя это плохая идея. Если бы мы сделали:

Test x = new Test();
Test y = new Test();
x.staticVariable = 10;
y.staticVariable = 20;
System.out.println(x.staticVariable);

тогда это вывело бы 20 - есть только одна переменная, а не одна на экземпляр. Было бы яснее написать это так:

Test x = new Test();
Test y = new Test();
Test.staticVariable = 10;
Test.staticVariable = 20;
System.out.println(Test.staticVariable);

Это делает поведение намного более очевидным. Современные IDE обычно предлагают изменить второй листинг на третий.

Нет никаких причин иметь такую ​​декларацию, как

private final int NUMBER = 10;

Если это не может измениться, нет смысла иметь одну копию на экземпляр.

39 голосов
/ 21 августа 2012

Для final , ему могут быть присвоены различные значения во время выполнения при инициализации. Например

Class Test{
  public final int a;
}

Test t1  = new Test();
t1.a = 10;
Test t2  = new Test();
t2.a = 20; //fixed

Таким образом, каждый экземпляр имеет различное значение поля a .

Для static final все экземпляры имеют одинаковое значение и не могут быть изменены после первой инициализации.

Class TestStatic{
      public static final int a;
}

TestStatic t1  = new TestStatic();
t1.a = 10;
TestStatic t2  = new TestStatic();
t1.a = 20;   // ERROR, CAN'T BE ALTERED AFTER THE FIRST INITIALIZATION.
26 голосов
/ 12 сентября 2009
Переменная

A static остается в памяти в течение всего времени жизни приложения и инициализируется во время загрузки класса. Не-static переменная инициализируется каждый раз, когда вы создаете new объект. Обычно лучше использовать:

private static final int NUMBER = 10;

Почему? Это уменьшает объем памяти на экземпляр. Возможно, это также благоприятно для попаданий в кэш. И это просто имеет смысл: static следует использовать для вещей, которые являются общими для всех экземпляров (a.k.a. объекты) определенного типа (a.k.a. class).

16 голосов
/ 12 сентября 2009

static означает «связанный с классом»; без него переменная связана с каждым экземпляром класса. Если он статический, это означает, что у вас будет только один в памяти; если нет, у вас будет один для каждого создаваемого вами экземпляра. static означает, что переменная будет оставаться в памяти до тех пор, пока класс загружен; без него переменная может быть gc'd, когда ее экземпляр.

12 голосов
/ 17 сентября 2012

Читая ответы, я не нашел ни одного реального теста, который действительно доходил до сути. Вот мои 2 цента:

public class ConstTest
{

    private final int         value             = 10;
    private static final int  valueStatic       = 20;
    private final File        valueObject       = new File("");
    private static final File valueObjectStatic = new File("");

    public void printAddresses() {


        System.out.println("final int address " +
                ObjectUtils.identityToString(value));
        System.out.println("final static int address " +
                ObjectUtils.identityToString(valueStatic));
        System.out.println("final file address " + 
                ObjectUtils.identityToString(valueObject));
        System.out.println("final static file address " + 
                ObjectUtils.identityToString(valueObjectStatic));
    }


    public static void main(final String args[]) {


        final ConstTest firstObj = new ConstTest();
        final ConstTest sndObj = new ConstTest();

        firstObj.printAdresses();
        sndObj.printAdresses();
    }

}

Результаты для первого объекта:

final int address java.lang.Integer@6d9efb05
final static int address java.lang.Integer@60723d7c
final file address java.io.File@6c22c95b
final static file address java.io.File@5fd1acd3

Результаты для 2-го объекта:

final int address java.lang.Integer@6d9efb05
final static int address java.lang.Integer@60723d7c
final file address java.io.File@3ea981ca
final static file address java.io.File@5fd1acd3

Вывод:

Как я и думал, Java делает разницу между примитивными и другими типами. Примитивные типы в Java всегда «кэшируются», то же самое для строковых литералов (не новых объектов String), поэтому нет разницы между статическими и нестатическими членами.

Однако имеется дублирование памяти для нестатических элементов, если они не являются экземплярами примитивного типа.

Изменение значения valueStatic на 10 пойдет даже дальше, поскольку Java будет давать одинаковые адреса двум переменным типа int.

9 голосов
/ 10 июня 2016

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

Рассмотрим следующий пример:

public class TestClass {
    private final static double NUMBER = Math.random();

    public TestClass () {
        System.out.println(NUMBER);
    }
}

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

Однако при попытке использовать следующий пример:

public class TestClass {
    private final double NUMBER = Math.random();

    public TestClass () {
        System.out.println(NUMBER);
    }
}

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

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

2 голосов
/ 28 января 2015

Кроме ответа Джона, если вы используете static final, он будет вести себя как своего рода «определение». Как только вы скомпилируете класс, который его использует, он будет сожжен в скомпилированном файле .class. Проверьте мою ветку об этом здесь .

Для вашей основной цели: если вы не используете НОМЕР по-разному в разных экземплярах класса, я бы посоветовал использовать final и static. (Вы просто должны помнить, чтобы не копировать скомпилированные файлы классов без учета возможных проблем, подобных описанным в моем примере. В большинстве случаев этого не происходит, не волнуйтесь :))

Чтобы показать, как использовать разные значения в экземплярах, проверьте этот код:

public class JustFinalAttr {
  public final int Number;

  public JustFinalAttr(int a){
    Number=a;
  }
}

...System.out.println(new JustFinalAttr(4).Number);
2 голосов
/ 04 января 2016

Вот мои два цента:

final           String CENT_1 = new Random().nextInt(2) == 0 ? "HEADS" : "TAILS";
final   static  String CENT_2 = new Random().nextInt(2) == 0 ? "HEADS" : "TAILS";

Пример:

package test;

public class Test {

    final long OBJECT_ID = new Random().nextLong();
    final static long CLASSS_ID = new Random().nextLong();

    public static void main(String[] args) {
        Test[] test = new Test[5];
        for (int i = 0; i < test.length; i++){
            test[i] = new Test();
            System.out.println("Class id: "+test[i].CLASSS_ID);//<- Always the same value
            System.out.println("Object id: "+test[i].OBJECT_ID);//<- Always different
        }
    }
}

Ключ в том, что переменные и функции могут возвращать разные значения. Поэтому конечные переменные могут быть назначены с разными значениями.

2 голосов
/ 13 сентября 2009

Как уже говорил Джон, статическая переменная, также называемая переменной класса, - это переменная, которая существует во всех экземплярах класса.

Я нашел пример этого здесь :

public class StaticVariable
{
  static int noOfInstances;
  StaticVariable()
  {
    noOfInstances++;
  }
  public static void main(String[] args)
  {
    StaticVariable sv1 = new StaticVariable();
    System.out.println("No. of instances for sv1 : " + sv1.noOfInstances);

    StaticVariable sv2 = new StaticVariable();
    System.out.println("No. of instances for sv1 : "  + sv1.noOfInstances);
    System.out.println("No. of instances for st2 : "  + sv2.noOfInstances);

    StaticVariable sv3 = new StaticVariable();
    System.out.println("No. of instances for sv1 : "  + sv1.noOfInstances);
    System.out.println("No. of instances for sv2 : "  + sv2.noOfInstances);
    System.out.println("No. of instances for sv3 : "  + sv3.noOfInstances);
  }
}

Вывод программы приведен ниже:

Как мы видим в этом примере, каждый объект имеет свою собственную копию переменной класса.

C:\java>java StaticVariable
No. of instances for sv1 : 1
No. of instances for sv1 : 2
No. of instances for st2 : 2
No. of instances for sv1 : 3
No. of instances for sv2 : 3
No. of instances for sv3 : 3
2 голосов
/ 20 июня 2011

Из тестов, которые я сделал, статические конечные переменные не совпадают с конечными (нестатическими) переменными! Конечные (нестатические) переменные могут отличаться от объекта к объекту !!! Но это только в том случае, если инициализация выполняется внутри конструктора! (Если он не инициализирован из конструктора, то это только пустая трата памяти, поскольку он создает окончательные переменные для каждого создаваемого объекта, которые нельзя изменить.)

Например:

class A
{
    final int f;
    static final int sf = 5;

    A(int num)
    {
        this.f = num;
    }

    void show()
    {
        System.out.printf("About Object: %s\n Final: %d\n Static Final: %d\n\n", this.toString(), this.f, sf);
    }

    public static void main(String[] args)
    {
        A ob1 = new A(14);
        ob1.show();

        A ob2 = new A(21);
        ob2.show();

    }
}

На экране отображается:

Об объекте: A @ addbf1 Финал: 14 Статический финал: 5

Об объекте: A @ 530daa Финал: 21 Статический финал: 5

Анонимный студент 1 курса информатики, Греция

...