Синтаксис второй формы предоставляет удобный синтаксис создания и вызова методов с эффектом, напоминающим списки аргументов переменной длины языка С.
Такой список способен содержать неизвестное заранее количество аргументов неизвестного типа. Так как абсолютно все классы унаследованы от общего корневого класса Object, можно создать метод, принимающий в качестве аргумента массив Object, и вызывать его следующим образом:
//. initialization/VarArgs.java
// Использование синтаксиса массивов
// для получения переменного списка параметров.
class А { int i; }
public class VarArgs {
static void printArray(Object[] args) { for(Object obj • args)
System.out print(obj + " "); System out printlnO,
}
public static void main(String[] args) { printArray(new Object[]{
new Integer(47), new Float(3 14), new Double(ll.ll)
}).
printArray(new 0bject[]{"pa3", "два", "три" }); pri ntArray (new Object[]{new AO, new AO, new AO});
}
} /* Output: (Sample) 47 3.14 11.11 раз два три
А@1а46еЗО А@3е25а5 A@19821f *///:-
Видно, что метод print() принимает массив объектов типа Object, перебирает его элементы и выводит их. Классы из стандартной библиотеки Java при печати выводят осмысленную информацию, однако объекты классов в данном примере выводят имя класса, затем символ @ и несколько шестнадцатеричных цифр. Таким образом, по умолчанию класс выводит имя и адрес объекта (если только вы не переопределите в классе метод toString() — см. далее).
До выхода Java SE5 переменные списки аргументов реализовывались именно так. В Java SE5 эта долгожданная возможность наконец-то была добавлена в язык — теперь для определения переменного списка аргументов может использоваться многоточие, как видно в определении метода printArray:
//: initialization/NewVarArgs java // Создание списков аргументов переменной длины // с использованием синтаксиса массивов.
public class NewVarArgs {
static void printArray(Object... args) { for(Object obj • args)
System out.print(obj + " "); System.out.printlnO;
}
public static void main(String[] args) {
// Можно передать отдельные элементы printArray(new Integer(47), new Float(3 14), new Doubledl. 11));
printArray(47. 3 14F, 11.11): printArray("раз", "два", "три"): printArray(new АО. new АО, new АО): // Или массив.
printArray((Object[])new Integer[]{ 1, 2, 3, 4 }); printArray(), // Пустой список тоже возможен
}
} /* Output- (lb% match) 47 3 14 11.11 47 3 14 11.11 раз два три
A@lbab50a А@сЗс749 A@150bd4d 12 3 4 *///•-
Резюме
Такой сложный механизм инициализации, как конструктор, показывает, насколько важное внимание в языке уделяется инициализации. Когда Бьерн Страуструп разрабатывал С++, в первую очередь он обратил внимание на то, что низкая продуктивность С связана с плохо продуманной инициализацией, которой была обусловлена значительная доля ошибок. Аналогичные проблемы возникают и при некорректной финализации. Так как конструкторы позволяют гарантировать соответствующие инициализацию и завершающие действия по очистке (компилятор не позволит создать объект без вызова конструктора), тем самым обеспечивается полная управляемость и защищенность программы.
В языке С++ уничтожение объектов играет очень важную роль, потому что объекты, созданные оператором new, должны быть соответствующим образом разрушены. В Java память автоматически освобождается сборщиком мусора, и аналоги деструкторов обычно не нужны. В таких случаях сборщик мусора Java значительно упрощает процесс программирования и к тому же добавляет так необходимую безопасность при освобождении ресурсов. Некоторые сборщики мусора могут проводить завершающие действия даже с такими ресурсами, как графические и файловые дескрипторы. Однако сборщики мусора добавляют издержки во время выполнения программы, которые пока трудно реально оценить из-за сложившейся исторически медлительности интерпретаторов Java. И хотя в последнее время язык Java намного улучшил свою производительность, проблема его «задумчивости» все-таки наложила свой отпечаток на возможность решения языком некоторого класса задач.
Так как для всех объектов гарантированно используются конструкторы, на последние возлагаются дополнительные обязанности, не описанные в этой главе. В частности, гарантия конструирования действует и при создании новых классов с использованием композиции или наследования , и для их поддержки требуются некоторые дополнения к синтаксису языка. Композиция и наследование, а также их влияние на конструкторы, рассматриваются в следующих главах.
Управление доступом
Важнейшим фактором объектно-ориентированной разработки является отделение переменных составляющих от постоянных.
Это особенно важно для библиотек. Пользователь {программист-клиент) библиотеки зависит от неизменности некоторого аспекта вашего кода. С другой стороны, создатель библиотеки должен обладать достаточной свободой для проведения изменений и улучшений, но при этом изменения не должны нарушить работоспособность клиентского кода.
Читать дальше