К. Арнольд, Д. Гослинг - Язык программирования Java (1160779), страница 57
Текст из файла (страница 57)
определение метода canDeposit()}Если метод не возвращает никакого значения, то на месте возвращаемого типа ставится ключевое слово void. В противном случае каждыйвозможный путь выполнения его операторов должен возвращать значение, которое может быть присвоено переменной объявленного типа. Кпримеру, метод permissions For не может возвращать значение типа String, поскольку невозможно присвоить объект типа String переменнойтипа Permissions. Однако вы можете объявить, что метод permissionsFor возвращает значение String, не изменяя при этом оператор return,поскольку ссылка на объект Permissions может быть присвоена переменной типа Object.2.6.1.
Значения параметровВсе параметры в Java передаются “по значению”. Другими словами, значения переменных-параметров метода являются копиями значений,указанных при его вызове. Если передать методу переменную некоторого типа, то параметр будет представлять собой копию этой переменной;ее изменение внутри метода никак не повлияет на значение переменной в коде за его пределами. Например:class PassByValue {public static void main(String[] args) {double one = 1.0;}System.out.println(“before: one = ” + one);halveIt(one);System.out.println(“after: one = ” + one);public static void halveIt(double arg) {arg /= 2.0; //System.out.println(“halved: arg = ” + arg);}}Приведенные ниже результаты показывают, что деление на два переменной arg в методе halveIt не меняет значения переменной one в методеmain:before: one = 1halved: arg = 0.5after: one = 1Однако, если параметр метода представляет собой ссылку на объект, то “по значению” передается ссылка, а не сам объект! Следовательно, выможете изменять в методе тот объект, на который она ссылается, — значение ссылки остается прежним.
Можно изменять любые поля объектаили вызывать методы, влияющие на его состояние, — произойдет изменение объекта во всех фрагментах программы, где имеется ссылка нанего. Приведенный ниже пример наглядно показывает, чем данный случай отличается от предыдущего:class PassRef {public static void main(String[] args) {Body sirius = new Body(“Sirius”, null);}System.out.println(“before: ” + sirius);commonName(sirius);System.out.println(“after: ” + sirius);public static void commonName(Body bodyRef) {bodyRef.name = “Dog Star”;bodyRef = null;}}Результат будет следующим:before: 0 (Sirius)after: 0 (Dog Star)Обратите внимание на то, что название объекта изменилось, тогда как ссылка bodyRef все равно указывает на объект Body (хотя методcommonName присваивал параметру bodyRef значение null).Приведенная выше диаграмма показывает состояние ссылок непосредственно после вызова commonName в main.
Обе ссылки — sirius (в main)и bodyRef (в commonName) — указывают на один и тот же объект. Когда commonName изменяет значение поля bodyRef.name, то названиеизменяется в объекте, совместно используемом обоими ссылками. Однако при присвоении null ссылке bodyRef изменяется только ее значение,тогда как значение ссылки на объект sirius остается тем же самым; вспомним, что параметр bodyRef является передаваемой по значению копиейsirius. Внутри метода commonName изменяется лишь значение переменной-параметра bodyRef, подобно тому как в методе halveIt изменялосьлишь значение параметра-переменной arg.
Если бы изменение bodyRef относилось и к значению sirius в main, то в строке, начинающейся с “after:”, стояло бы “null”. Тем не менее переменные bodyRef в commonName и sirius в main указывают на один и тот же объект, поэтому изменения,вносимые в commonName, отражаются и в том объекте, на который ссылается sirius.2.6.2. Применение методов для ограничения доступаПользоваться классом Body с несколькими конструкторами стало значительно удобнее, чем его старым вариантом, состоявшим из одних данных;кроме того, мы обеспечили правильное автоматическое присвоение значений idNum.
И все же программист может все испортить, изменяязначение поля idNum после конструирования объекта, — ведь данное поле объявлено как public и открыто для любых действий. Необходимо,чтобы поле idNum содержало данные, доступные только для чтения. Подобные данные в объектах встречаются довольно часто, но в языке Javaне существует ключевого слова, которое сделало бы поле за пределами класса доступным только для чтения.Чтобы сделать поле доступным только для чтения, мы должны скрыть его. Для этого поле idNum объявляется с ключевым словом private, а вкласс добавляется новый метод, с помощью которого код за пределами класса может получить значение этого поля:class Body {private long idNum; // поле стало privatepublic String name = “”;public Body orbits = null;private static long nextID = 0;Body() {idNum = nextID++;}public long id() {return idNum;}}// ...Начиная с этого момента программист, которому понадобилось узнать идентификатор небесного тела, должен вызвать метод id, возвращающийтребуемое значение.
У программиста не остается никакой возможности изменить идентификатор — в сущности, за пределами класса его можнорассматривать как величину, доступную только для чтения. Данное поле может быть изменено только внутренними методами класса Body.Методы, регулирующие доступ к внутренним данным класса, иногда называются методами доступа (accessor methods).
С их помощью такжеможно (и, наверное, нужно) защитить поля name и orbits.Даже если интересы приложения и не требуют полей, доступных только для чтения, объявление полей класса с ключевым словом private исоздание методов для присвоения/получения их значений позволяет вам определить необходимые действия над объектом в будущем. Еслипрограммист может непосредственно обращаться к полям класса, вы неизбежно теряете контроль над тем, какие значения будут принимать этиполя и что происходит в программе при их изменении. По этим причинам в дальнейших примерах этой книги поля public встречаются оченьредко.Упражнение 2.8Объявите поля класса Vehicle с ключевым словом private и опишите соответствующие методы доступа. Для каких полей следует предусмотретьметоды, изменяющие их значения, а для каких — нет?Упражнение 2.9Объявите поля класса LinkedList с ключевым словом private и включите в класс соответствующие методы доступа.
Для каких полей следуетпредусмотреть методы, изменяющие их значения, а для каких — нет?Упражнение 2.10Включите в класс Vehicle метод changeSpeed для задания текущей скорости машины в соответствии с передаваемым значением и метод stop дляобнуления скорости.Упражнение 2.11Включите в класс LinkedList метод для подсчета количества элементов в списке.2.7. Ссылка thisМы уже видели (на стр. ), как в начале работы конструктора происходит явный вызов другого конструктора класса.
Специальная ссылка на объектthis может также применяться внутри нестатических методов; она указывает на текущий объект, для которого был вызван данный метод.this чаше всего используется для передачи ссылки на текущий объект в качестве параметра для других методов. Предположим, в методенеобходимо пополнить список объектов, ожидающих обслуживания. Такой вызов может выглядеть следующим образом:Service.add(this);this неявно добавляется в начало каждой ссылки на поле или метод, если только программист не указал ссылку на другой объект. Например,присвоение значения полю str в следующем классе:class Name {public String str;Name() {str = “”;}}равносильно следующему:this.str = “”;Обычно this используется только в случае необходимости, то есть когда имя поля, к которому вы обращаетесь, скрывается объявлениемпеременной или параметра.
Например:class Moose {String hairdresser;}Moose(String hairdresser) {this.hairdresser = hairdresser;}Поле hairdresser внутри конструктора скрывается присутствием одноименного параметра. Чтобы обратиться к полю hairdresser, а не к параметру,мы ставим перед именем ссылку this, указывая тем самым, что поле принадлежит к текущему объекту. Намеренное скрытие идентификаторов,осуществляемое подобным образом, может быть отнесено к хорошему стилю программирования лишь при идиоматическом использовании вконструкторах и методах доступа.Помимо ссылки this, может также применяться ссылка super, с помощью которой осуществляется доступ к скрытым полям и вызовпереопределенных методов суперкласса.
Ключевое слово super подробно рассматривается в разделе “Переопределение методов и скрытиеполей”.2.8. Перегрузка методовВ языке Java каждый метод обладает определенной сигнатурой, которая представляет собой совокупность имени с количеством и типомпараметров. Два метода могут иметь одинаковые имена, если их сигнатуры отличаются по количеству или типам параметров.
Это называетсяперегрузкой (overloading), поскольку простое имя метода “перегружается” несколькими значениями. Когда программист вызывает метод,компилятор по количеству и типу параметров ищет тот из существующих методов, сигнатура которого подходит лучше всех остальных.Приведем в качестве примера различные методы orbits Around нашего класса Body:public Body orbitsAround() {return orbits;}public void orbitsAround(Body around) {orbits = around;}При подобном стиле программирования перегрузка служит для того, чтобы отличать выборку значения (параметры не передаются) от егозадания (указывается аргумент, представляющий собой новое значение). Количество параметров в двух методах отличается, поэтому выбратьнужный метод будет несложно.