Статические поля и методы

From AsIsWiki
Revision as of 11:50, 4 April 2015 by Alex (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Форум

Назад | Оглавление | Дальше


Contents

Статические поля

Во всех предыдущих примерах, при объявлении метода main() использовался модификатор static. Рассмотрим действие этого модификатора.

Поле, имеющее модификатор static, существует в одном экземпляре. Если поле не статическое, то каждый объект содержит его копию. Например, требуется присвоить уникальный идентификатор каждому сотруднику. Добавим в класс Employee поле id и статическое поле nextId:

class Employee {
    ...
    private int id;
    private static int nextId = 1;
}

Теперь каждый объект Employee имеет свое поле id, кроме того, есть поле nextId, которое одновременно принадлежит всем экземплярам класса.

Например, если существует тысяча объектов Employee, то в них есть тысяча полей id, по одному в каждом объекте. При этом, существует только один экземпляр статического поля nextId. Даже если не создан ни один объект Employee, статическое поле nextId существует. Оно принадлежит классу, а не конкретному объекту.

В большинстве объектно-ориентированных языков статические поля называются полями класса - class field. Термин "статический" унаследован от языка C++.

Добавим в класс Employee следующий метод:

public void setId() {
    id = nextId;
    nextId++;
}

Допустим, требуется задать идентификационный номер объекту harry:

harry.setId();

Теперь значение поля id объекта harry задано, а значение статического поля nextId увеличено на единицу.


Константы

В отличие от статических переменных, статические константы используются гораздо чаще. Например, класс Math имеет статическую константу PI:

public class Math {
    ...
    public static final double PI = 3.14159265358979323846;
    ...
}

Обращение к статической константе:

Math.PI

Без ключевого слова static, константа PI была бы обычным полем экземпляра класса Math. То есть, для вызова PI пришлось бы создавать объект Math, причем каждый подобный объект имел бы свою копию константы PI.

Еще одна часто используемая статическая переменная - System.out из класса System:

public class System {
    ...
    public static final PrintStream out = ... ;
    ...
}

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

out = new PrintStream( ... );  // Error - поле out изменить нельзя

В классе System есть метод setOut(), позволяющий присвоить полю System.out другой поток. Метод setOut - платформенно-ориентированный, он реализован средствами, отличными от Java. Платформенно-ориентированные методы могут обходить механизмы контроля Java. Это очень специфическое решение, которое не следует повторять в своих программах.


Статические методы

Статические методы выполняться, даже если экземпляр класса не существует. Например, метод pow() из класса Math - статический. Выражение Math.pow(x, y) вычисляет x^y. При выполнении своей задачи, этот метод не использует ни одного экземпляра класса Math. Т.е. он не имеет неявного параметра. В статических методах объект this не используется.

Статические методы не работают с объектами; с их помощью невозможно получить доступ к полям экземпляра. Но, статические методы имеют доступ к статическим полям класса.

public static int getNextId() {
    return nextId;  // возвращает статическое поле
}

Для вызова метода нужно указать имя класса:

int n = Employee.getNextId();

Если из описания метода убрать ключевое слово static, то для вызова метода потребуется создать объект типа Employee.

Для вызова статического метода можно использовать и объекты. Например, вместо Employee.getNextId() можно указать harry.getNextId(), при условии, что harry - это экземпляр класса Employee. Но, для вычисления результата, метод getNextId() не обращается к объекту harry, и подобное выражение усложняет восприятие программы. Рекомендуется для вызова статических методов использовать имена классов, а не объекты.

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

  • Когда методу не нужен доступ к информации о состоянии объекта, поскольку все необходимые параметры задаются явно: Math.pow(3, 4);
  • Когда методу нужен доступ лишь к статическим полям класса: Employee.getNextId().

Статические поля и методы в Java и C++ отличаются только синтаксически. В C++ для доступа к статическому полю или методу, находящемуся вне области видимости, используется выражение: Math::PI.

Термин "static" - исторический курьез. Сначала ключевое слово static было введено в языке C для обозначения локальных переменных, которые не уничтожались при выходе из блока. В этом контексте static имеет смысл: переменная продолжает существовать после выхода из блока, а также при повторном входе в него. Затем static в C приобрело второе значение - глобальные переменные и функции, к которым нельзя получить доступ из других файлов. В итоге, в C++ это слово обозначает переменные и функции, принадлежащие классу, но не принадлежат ни одному объекту этого класса. Именно это значение имеет static в Java.


Порождающие методы

Класс NumberFormat использует порождающие методы для создания объектов, соответствующих различным стилям форматирования:

NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();

NumberFormat percentFormatter = NumberFormat.getPercentInstance();

double x = 0.1;

System.out.println(currencyFormatter.format(x));  // выводит 0,1 руб.

System.out.println(percentFormatter.format(x));   // выводит 10%

Почему же не использовать для этой цели конструктор? Тому есть две причины:

  • Конструктору нельзя присвоить произвольное имя. Его имя всегда должно совпадать с именем класса. В примере с классом NumberFormat имеет смысл применять разные имена для разных типов форматирования.
  • При использовании конструктора, тип объекта фиксирован. Если же применяются порождающие методы, они возвращают объект DecimalFormat, который наследует свойства NumberFormat.


Метод main()

Статические методы можно вызывать, даже если соответствующий объект еще не создан. Например, для вызова метода Math.pow() объект Math не нужен. По этой причине метод main() объявляется как статический:

public class Application {
    public static void main(String[] args) {
        // здесь создаются объекты
    }
}

Каждый класс может содержать метод main(). Его можно использовать для независимого тестирования классов. Например, метод main() можно добавить в класс Employee:

class Employee {

    // Constructor
    public Employee(String n, double s, int year, int month, int day) {
        ...
    }
  
    public static void main(String[] args) {
        // отладочный модуль
        Employee e = new Employee("Romeo", 50000, 2003, 3, 31);
        e.raiseSalary(10);
        System.out.println(e.getName() + " " + e.getSalary());
    }
    ...
}

Для тестирования класса Employee следует выполнить команду:

java Employee

Приложение, частью которого является класс Employee, запускается так:

java Application

В этом случае, метод main() класса Employee выполнен не будет.

Следующая версия класса Employee содержит статическое поле nextId и статический метод getNextId(). Массив заполняется тремя объектами Employee, а затем выводится информация о сотрудниках. В заключение на экран выводится очередной доступный идентификатор:

public class StaticTest {

    public static void main(String[] args) {

        // fill the staff array with three Employee objects
        Employee[] staff = new Employee[3];

        staff[0] = new Employee("Tom", 40000);
        staff[1] = new Employee("Dick", 60000);
        staff[2] = new Employee("Harry", 65000);

        // print out information about all Employee objects
        for (Employee e : staff) {
            e.setId();
            System.out.println("name = " + e.getName()
                    + ", id = " + e.getId()
                    + ", salary = " + e.getSalary());
        }

        int n = Employee.getNextId();  // calls static method
        System.out.println("Next available id = " + n);
    }
}

class Employee {

    public Employee(String n, double s) {
        name = n;
        salary = s;
        id = 0;
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public int getId() {
        return id;
    }

    public void setId() {
        id = nextId;  // set id to next available id
        nextId++;
    }

    public static int getNextId() {
        return nextId;  // returns static field
    }

    public static void main(String[] args)  // unit test
    {
        Employee e = new Employee("Harry", 50000);
        System.out.println(e.getName() + " " + e.getSalary());
    }

    private String name;
    private double salary;
    private int id;
    private static int nextId = 1;
}

Класс Employee имеет статический метод main() для модульного тестирования:

java Employee

Тестирование всей программы:

java StaticTest



Форум

Назад | Оглавление | Дальше

Personal tools
Namespaces

Variants
Actions
Navigation
Tools