Для сохранения данных в массиве типа FailSoftArray по указанному индексу вызывается метод put (), тогда как метод get () извлекает содержимое элемента этого массива по заданному индексу. Если указанный индекс оказывается вне границ массива, метбд put () возвращает логическое значение false, а метод get () — значение errval. Ради простоты в большинстве примеров программ, представленных в этой книге, на члены класса будет в основном распространяться тип доступа по умолчанию. Но не следует забывать, что в реальных объектно-ориентированных программах очень важно ограничивать доступ к членам класса, и в особенности к переменным. Как будет показано в главе 7, при использовании наследования роль средств управления доступом еще более возрастает.
Пример для опробования 6.1. Усовершенствование класса Queue
Модификатор доступа private можно использовать для усовершенствования класса Queue, разработанного в примере для опробования 5.2 из главы 5. В текущей версии этого класса используется тип доступа по умолчанию, который, по существу, делает все члены этого класса открытыми. Это означает, что другие классы могут непосредственно обращаться к элементам базового массива — и даже вне очереди. А поскольку назначение класса, реализующего очередь, состоит в том, чтобы обеспечить принцип доступа “первым пришел — первым обслужен”, то возможность произвольного обращения к элементам массива явно неуместна. В частности, это давало бы возможность недобросовестным программистам изменять индексы в переменных putloc и getloc, искажая тем самым организацию очереди. Подобные недостатки нетрудно устранить с помощью модификатора доступа private.
Последовательность действий
Создайте новый файл Queue. j ava.
Добавьте к массиву q, а также к переменным putloc и getloc модификатор доступа private в классе Queue. В результате код этого класса должен выглядеть так, как показано ниже. // Усовершенствованный класс очереди, предназначенной // для хранения символьных значений, class Queue { // Следующие члены класса теперь являются закрытыми, private char q[]; // Массив для хранения элементов очереди private int putloc, getloc; // Индексы размещения и извлечения // элементов очереди Queue(int size) { q = new char[size+1]; // выделить память для очереди putloc = getloc = 0; } // поместить символ в очередь void put(char ch) { if(putloc==q.length-1) { System.out.println(" - Queue is full."); return; } putloc++; q[putloc] = ch; } // извлечь символ из очереди char get () { if(getloc == putloc) { System.out.println(" - Queue is empty."); return (char) 0; } getloc++; return q[getloc]; } }
Изменение типа доступа к массиву q и переменным putloc и getloc с выбираемого по умолчанию на закрытый (private) никак не скажется на работе тех программ, где класс Queue используется правильно. В частности, этот класс будет по-прежнему взаимодействовать с классом QDemo, созданным в примере для опробования 5.2. В то же время неправильное обращение к классу Queue станет невозможным. Например, следующий фрагмент кода недопустим:Queue test = new Queue(lO); test.q[0] =99; // Ошибка! test.putloc = -100; // He пройдет!
Теперь, когда массив q и переменные putloc и getloc стали закрытыми, класс Queue строго следует принципу “первым пришел — первым обслужен”, по которому действует очередь. Передача объектов методам
В приведенных ранее примерах программ в качестве параметров методам передавались лишь простые типы. Но параметрами могут быть и объекты. Например, в привеское значение true только в том случае, если все три размера обоих параллелепипедов совпадают. А в методе same Volume () сравниваются лишь объемы двух параллелепипедов. Но в обоих случаях параметр ob имеет тип Block. Несмотря на то что Block — это класс, параметры данного типа используются таким же образом, как и параметры встроенных в Java типов данных. Способы передачи аргументов методу
Как показывает приведенный выше пример, передача объекта методу производится очень просто. Но в этом примере показаны не все нюансы данного процесса. В некоторых случаях последствия передачи объекта по ссылке будут отличаться от тех результатов, к которым приводит передача значения обычного типа. Для выяснения причин этих отличий рассмотрим два способа передачи аргументов методу.
Первым способом является вызов по значению. В этом случае значение аргумента копируется в формальный параметр метода. Следовательно, изменения, вносимые в параметр метода, не оказывают никакого влияния на аргумент, используемый для вызова. А вторым способом передачи аргумента является вызов по ссылке. В данном случае параметру метода передается ссылка на аргумент, а не значение аргумента. В методе эта ссылка используется для доступа к конкретному аргументу, указываемому при вызове. Это означает, что изменения, вносимые в параметр, будут оказывать влияние на аргу¬ мент, используемый для вызова метода. Как будет показано далее, в Java используются оба способа. А выбор конкретного способа зависит от того, что именно передается.
Если методу передается простой тип, например int или double, он передается по значению. При этом создается копия аргумента, а то, что происходит с параметром, принимающим аргумент, не распространяется за пределы метода. Рассмотрим в качестве примера следующую программу: // Простые типы данных передаются методам по значению, class Test { /* Этот метод не может изменить значения аргументов, передаваемых ему при вызове. */ void noChange(int i, int j) { i = i + j; j = -j; } } class CallByValue { public static void main (String args.[]) { Test ob = new Test(); int a = 15, b = 20; System.out.println("a and b before calclass="underline" " + a + " " + b); ob.noChange(a, b); System.out.println("a and b after calclass="underline" " + a + " " + b); } } Ниже приведен результат выполнения данной программы.
a and b before calclass="underline" 15 20 a and b after calclass="underline" 15 20 Нетрудно заметить, что действия, выполняемые в теле метода noChange (), не оказывают никакого влияния на значения переменных а и b в вызывающем методе. Если методу передается объект, то ситуация меняется коренным образом, поскольку объекты передаются неявно по ссылке. Не следует забывать, что создание переменной типа класса, по существу, означает формирование ссылки на объект этого класса. И методу на самом деле передается только ссылка, а не сам объект. Поэтому при передаче этой ссылки методу принимающий ее параметр будет ссылаться на тот же самый объект, на который ссылается аргумент. Это означает, что и аргумент, и параметр ссылается на один и тот же объект и что объекты, по существу, передаются методам по ссылке. Таким образом, объект в методе будет оказывать влияние на объект, используемый в качестве аргумента. Для примера рассмотрим следующую программу:
// Объекты передаются методам по ссылке, class Test { int a, b; Test(int i, int j) { a = i; b = j; } /* Передача объекта методу. Теперь переменные ob.a b и ob.b из передаваемого объекта можно изменить. */ void change(Test ob) { ob.a = ob.a + ob.b; ob.b = -ob.b; }
} class CallByRef { public static void main(String args[]) { Test ob = new Test(15, 20); System.out.println("ob.a and ob.b before calclass="underline" " + ob.a + " " + ob.b); ob.change(ob); System.out.println("ob.a and ob.b after calclass="underline" " + ob.a + " " + ob.b); } } Выполнение этой программы дает следующий результат: ob.a and ob.b before calclass="underline" 15 20 ob.a and ob.b after calclass="underline" 35 -20
Как видите, в данном случае действия в методе change () оказывают влияние на объект, используемый в качестве аргумента этого метода.
Не следует, однако, забывать, что когда объект передается методу по ссылке, сама ссылка на него передается по значению. Но поскольку передаваемое значение лишь указывает на объект, копия этого значения будет по-прежнему указывать на тот же самый объект в соответствующем аргументе. Возврат объектов
Метод может возвращать данные любого типа, включая и типы классов. Например, объект приведенного ниже класса ErrorMsg может быть использован для сообщения об ошибке. В этом классе имеется метод getErrorMsg (), который возвращает объект типа String, описывающий ошибку. Объект типа String строится на основании кода ошибки, переданного методу. // Возврат объекта типа String, class ErrorMsg { String msgs[] = { "Output Error", "Input Error", "Disk Full", "Index Out-Of-Bounds" }; // возвратить объект типа String в виде сообщения об ошибке String getErrorMsg(int i) { if(i >=0 & i < msgs.length) return msgs[i]; else return "Invalid Error Code"; } } class ErrMsg { public static void main(String args[]) { ErrorMsg err = new ErrorMsg(); System.out.println(err.getErrorMsg(2)); System.out.println(err.getErrorMsg(19)); } }