Присваивание и копирование
В приведенных выше примерах имя массива — это переменная-ссылка (или поле-ссылка), содержащая адрес объекта массива. Поэтому присваивание таких переменных друг другу — это не копирование массивов, а копирование ссылок на объекты. Например.
int ary[][] = new int[][] { {1, 1, 1, 1}, {2, 2, 2}, {1, 2, 3, 4, 5}, }; int copyAry[][] = ary;В данном примере все абсолютно корректно, но copyAry — это не ссылка на копию массива, а еще одна ссылка на тот же массив. Для создания копии придется написать соответствующий метод.
В состав стандартной библиотеки Java входят разнообразные средства работы с массивами. В пакете java.util имеется класс Arrays, который обеспечивает множество полезных операций над массивами (см. документацию).
Резюмируем основные правила
- 1. Массивы являются объектами специфической формы. В частности, любой массив имеет поле length, которое определяет его размер.
- 2. Массивы индексируются от 0.
- 3. Java жестко контролирует выход за границы массива (прерывание IndexOutOfBoundException).
- 4. Массив элементарного типа, например int, — это действительно массив значений (т.е. массив целых чисел). Массив объектов — это массив ссылок на объекты. Т.е. недостаточно создать сам массив, нужно еще создать объекты, входящие в него.
- 5. Существуют два способа создания массива — операцией new и явной инициализацией.
- 6. Для многомерных массивов существует возможность задания разного размера массивов второго, третьего и т.д. измерений, но это "экзотика".
10)
Инкапсуляция
Инкапсуляция является важнейшим свойством объектов, на котором строится объектно-ориентированное программирование. Инкапсуляция заключается в том, что объект скрывает в себе детали, которые несущественны для использования объекта. В традиционном подходе к программированию с использованием глобальных переменных программист не был застрахован от ошибок, связанных с использованием процедур, не предназначенных для обработки данных, связанных с этими переменными. Предположим, например, что имеется «не-ООП» программа, предназначенная для начисления заработной платы сотрудникам некой организации, а в программе имеются два массива. Один массив хранит величину заработной платы, а другой – телефонные номера сотрудников (для составления отчёта для налоговой инспекции). Что произойдёт, если программист случайно перепутает эти массивы? Очевидно, для бухгалтерии начнутся тяжёлые времена. «Жёсткое» связание данных и процедур их обработки в одном объекте позволит избежать неприятностей такого рода. Инкапсуляция и является средством организации доступа к данным только через соответствующие методы.
В нашем примере описание объекта процедура инициализации Init и функции GetX и GetY уже не существуют как отдельные самостоятельные объекты. Это неотъемлемые части объектного типа Location. Если в программе имеется описание нескольких переменных указанного типа, то для каждой переменной резервируется своя собственная область памяти для хранения данных, а указатели на точки входа в процедуру и функции – общие. Вызов каждого метода возможен только с помощью составного имени, явно указывающего, для обработки каких данных предназначен данный метод.
11)
Наследование классов
Наследование классов ( inheritance ) один из существенных атрибутов ООП (объектно-ориентированного программирования). Оно позволяет строить новые классы на базе существующих, добавляя в них новые возможности или переопределяя существующие.
Что такое наследование.
Пусть есть класс A, он имеет поля a1, a2, ..., an и методы f1(), f2(), ... , fm(). Тогда мы можем на его основе построить класс B. Класс B наследует все поля и методы класса A (за исключением конструкторов). Кроме того, в B можно:
- добавить новые поля;
- добавить новые методы;
- переопределить какие-либо методы класса A.
Это в основном, без деталей.
Синтаксис:
class B extends A { . . . // тело класса B}Внутри записываются "дополнения и изменения", вносимые классом B.
Класс A называют базовым классом или суперклассом (superclass), иногда - родительским классом, предком. B - порожденным, дочерним, подклассом (subclass), классом-потомком.
В свою очередь, класс A может быть порожден на базе другого класса, тогда этот класс является предком как для A, так и для B.
От одного класса может быть порождено произвольное количество новых классов. В результате получается иерархия классов, порожденных один от другого.
Пример (наследование полей)
class Base { int a, b, c; . . .} class Derived extends Base { long d, e; . . .}Объекты класса Base имеют три поля (a, b и c), объекты класса Derived - пять полей (a, b, c, d и e).
Пример (наследование методов)
class Base { int f() { . . . } void g(int p) { . . . }} class Derived extends Base { long f1() { . . . } }Класс Base имеет два метода (f() и g(...)), класс Derived - три метода (f(), g(...) и f1()).
Пример (переопределение методов)
class Base { int f() { . . . } void g(int p) { . . . }} class Derived extends B { int f() { . . . } }Класс Base имеет два метода (f() и g(...)) и класс Derived тоже имеет два метода (f() и g(...)). Но для объектов класса Derived будет вызываться не метод f() из Base, а метод f() из Derived. На английском переопределение методов называется overriding.
При переопределении (overriding) метод в порожденном классе должен иметь в точности то же описание, что и метод в базовом классе. В частности, попытка описать "long f()..." или даже "private int f()..." привела бы к ошибке при трансляции программы.
Класс Object
В Java фактически все классы строятся на базе наследования, т.к., даже если не написать "extends имя_класса", будет подразумеваться extends Object. Т.е. класс Object является базовым классом для всех классов Java.
Класс Object имеет ряд методов, при наследовании методы базового класса наследуются его потомками. Следовательно, все классы Java имеют как минимум те методы, которые есть в классе Object.
Взглянем на документацию по классу Object. В классе Object определены методы, большая часть из которых имеют непонятное для нас (на текущий момент) назначение. Но некоторые из них мы можем уже сейчас рассмотреть.
public String toString() Предназначен для формирования некоторого текстового представления объекта.
protected Object clone() throws CloneNotSupportedException Предназначен для создания копии объекта.
public boolean equals(Object obj) Позволяет сравнивать объекты.
public final Class getClass() Выдает класс данного объекта в виде объекта класса Class.
protected void finalize() throws Throwable Этот метод вызывается при удалении объекта из памяти сборщиком мусора.
Все указанные методы, кроме getClass и, частично, toString , не предназначены для непосредственного использования (в том смысле, что их нужно переопределить в порожденных классах).
Метод getClass возвращает специальный объект класса Class , содержащий много полезной информации о классе объекта. Метод toString по умолчанию выдает полное имя класса объекта и его адрес, что можно использовать при отладке программы.
Если почитать документацию по остальным методам, то мы увидим, что она, в основном, определяет, что должен делать тот или иной метод и лишь в конце описывается, как этот метод реализован в классе Object.
Разберем, например, метод equals . Его назначение - сравнивать объекты на равенство. Наличие его в классе Object (базовом для всех остальных классов) позволяет нам применять этот метод для любых объектов, что очень удобно. Но в классе Object он реализован "самым жестким" образом - как сравнение адресов объектов. Т.е. при сравнении двух объектов мы получим равенство только в том случае, если на самом деле это один и тот же объект. Естественно, что чаще всего нам потребуется какая-то другая реализация данного метода.
Наследование классов имеет много связанных с ним нюансов и особенностей. И в дальнейшем рассмотрении мы будем постепенно разбираться с ними.