Главная страница

Java. Полное руководство. 8-е издание. С. Н. Тригуб Перевод с английского и редакция


Скачать 25.04 Mb.
НазваниеС. Н. Тригуб Перевод с английского и редакция
АнкорJava. Полное руководство. 8-е издание.pdf
Дата28.02.2017
Размер25.04 Mb.
Формат файлаpdf
Имя файлаJava. Полное руководство. 8-е издание.pdf
ТипДокументы
#3236
страница12 из 90
1   ...   8   9   10   11   12   13   14   15   ...   90
ГЛАВА
Более пристальный взгляд на методы и классы
В этой главе продолжим рассмотрение методов и классов, начатое в предыдущей главе. Вначале рассмотрим несколько вопросов, связанных с методами, в том числе перегрузку, передачу параметров и рекурсию. Затем снова обратимся к классами рассмотрим управление доступом, использование ключевого слова s t a t i c и один из наиболее важных встроенных классов Java — S t r i n g Перегрузка методов
Язык Java разрешает определение внутри одного класса двух или более методов с одним именем, если только объявления их параметров различны. В этом случае методы называют перегруженными, а процесс — перегружай методов Перегрузка методов — один из способов поддержки полиморфизма в Java. Тем читателям, которые никогда не использовали язык, допускающий перегрузку методов, эта концепция вначале может показаться странной. Но, как вы вскоре убедитесь, перегрузка методов — одна из наиболее впечатляющих и полезных функциональных возможностей языка При вызове перегруженного метода для определения нужной версии Java использует тип и или количество аргументов метода. Следовательно, перегруженные методы должны различаться по типу и или количеству их параметров. Хотя возвращаемые типы перегруженных методов могут быть различны, самого возвращаемого типа недостаточно для различения двух версий метода. Когда среда исполнения
Java встречает вызов перегруженного метода, она просто выполняет ту его версию, параметры которой соответствуют аргументам, использованным в вызове.
Следующий простой пример иллюстрирует перегрузку метода Демонстрация перегрузки методов OverloadDemo {
void t e s t () Параметры отсутствуют Проверка перегрузки на наличие одного целочисленного параметра t e s t (int а) а " + a);
}
// Проверка перегрузки на наличие двух целочисленных параметров test(int a, int b) аи Часть I. Язык Java
1 1 Проверка перегрузки на наличие параметра типа double
double test(double a) {
System.out.println("double a: " + a);
return a*a;
}
}
class Overload {
public static void main(String a r g s []) {
OverloadDemo ob = new OverloadDemo();
double result;
// вызов всех версий метода test()
ob.test();
ob.test(10);
ob.test(10, 20) ;
result = Результат o b .t e s t (123.25): " + Эта программа создает следующий вывод.
Параметры отсутствуют а аи Ь 10 20
double а Результат o b .t e s t (123.25): Как видите, метод t e s t () перегружается четыре раза. Первая версия не принимает никаких параметров, вторая принимает один целочисленный параметр, третья — два целочисленных параметра, а четвертая — один параметр типа d o u ­
b l e . То, что четвертая версия метода t e s t () возвращает также значение, не имеет никакого значения для перегрузки, поскольку возвращаемый тип никак не влияет на поиск перегруженной версии метода.
П ри вызове перегруженного метода Java ищет соответствие между аргументами, которые были использованы для вызова метода, и параметрами метода. Однако это соответствие необязательно должно быть полным. В некоторых случаях при поиске перегруженных версий может использоваться автоматическое преобразование типов Java. Например, рассмотрим следующую программу Применение автоматического преобразования типов к перегрузке
class OverloadDemo {
void t e s t () Параметры отсутствуют Проверка перегрузки на наличие двух целочисленных параметров
void test(int a, int b) аи Проверка перегрузки на наличие параметра типа double
void test(double a) Внутреннее преобразование test(double) a: " + a);
}
class Overload {
public static void main(String a r g s []) {
OverloadDemo ob = new OverloadDemo();
int i = 88;
Глава 7. Более пристальный взгляд на методы и классы 6 7
ob.test();
o b .t e s t (10, 20);
ob.test(i);
// этот оператор вызовет test(double)
o b.test(123.2); // этот оператор вызовет Программа создает следующий вывод.
Параметры отсутствуют
а и Ь 10 Внутреннее преобразование test(double) а Внутреннее преобразование test(double) а Как видите, эта версия класса O verloadD em o не определяет перегрузку метода t e s t ( i n t ). Поэтому при вызове метода t e s t () с целочисленным аргументом внутри класса O v e r lo a d соответствующий метод отсутствует. Однако Java может автоматически преобразовать тип i n t e g e r в тип d o u b le , и это преобразование может использоваться для разрешения вызова. Поэтому после того, как версия метода t e s t ( i n t ) не обнаружена, Java повышает тип переменной i до d o u b le , а затем вызывает метод t e s t ( d o u b l e ) . Конечно, если бы метод t e s t ( i n t ) был определен, то был бы вызван именно он. Java будет использовать автоматическое преобразование типов только при отсутствии полного соответствия.
Перегрузка методов обеспечивает полиморфизм, поскольку это один из способов реализации в Java концепции один интерфейс, несколько методов. Для пояснения приведем следующие рассуждения. В тех языках, которые не поддерживают перегрузку методов, каждому методу должно быть присвоено уникальное имя. Однако зачастую желательно реализовать, по сути, один и тот же метод для различных типов данных. Например, рассмотрим функцию вычисления абсолютного значения. Обычно в языках, которые не поддерживают перегрузку, существует три или более версий этой функции со слегка различающимися именами. Например, в языке С функция a b s () возвращает абсолютное значение типа i n ­
t e g e r , функция l a b s () — значения типа lo n g i n t e g e r , а функция f a b s () значения с плавающей точкой. Поскольку язык Сне поддерживает перегрузку, каждая функция должна обладать собственным именем, несмотря на то что все три функции выполняют по существу одно и тоже действие. В результате в концептуальном смысле ситуация становится более сложной, чем она есть на самом деле. Хотя каждая функция построена на основе одной и той же концепции, программист вынужден помнить три имени. В Java подобная ситуация не возникает, поскольку все методы вычисления абсолютного значения могут использовать одно и тоже имя. И действительно, стандартная библиотека классов Java содержит метод вычисления абсолютного значения, названный a b s (). Перегруженные версии этого метода для обработки всех числовых типов определены в классе Java M ath. Java выбирает для вызова нужную версию метода a b s () в зависимости от типа аргумента.
Перегрузка методов ценна тем, что позволяет обращаться к схожим методам по общему имени. Таким образом, имя a b s представляет общее действие которое должно выполняться. Выбор конкретной версии для данной ситуации — задача компилятора. Программисту нужно помнить только об общем выполняемом действии. Полиморфизм позволяет свести несколько имен к одному. Приведенный пример весьма прост, но если эту концепцию расширить, легко убедиться в том, что перегрузка может облегчить выполнение более сложных задач.
При перегрузке метода каждая его версия может выполнять любые необходимые действия. Не существует никакого правила, в соответствии с которым перегруженные методы должны быть связаны между собой. Однако со стилистической

1 6 Часть I. Язык точки зрения перегрузка методов предполагает определенную связь. Таким образом, хотя одно и тоже имя можно использовать для перегрузки несвязанных методов, поступать так не следует. Например, имя s q r можно было бы использовать для создания методов, которые возвращают квадрат целочисленного значения и квадратный корень значения с плавающей точкой. Но эти две операции принципиально различны. Такое применение перегрузки методов противоречит ее исходному назначению. В частности, следует перегружать только тесно связанные операции.
П ерегрузка конструкторов
Наряду с перегрузкой обычных методов можно также выполнять перегрузку методов конструкторов. Фактически перегруженные конструкторы станут нормой, а не исключением, для большинства классов, которые вам придется создавать для реальных программ. Чтобы это утверждение было понятным, вернемся к классу
Box, разработанному в предыдущей главе. Его последняя версия имеет следующий вид Box {

double width;
double height;
double depth;
// Это конструктор класса Box.
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// вычисление и возврат значения
double v o l u m e () {
return width * height * Как видите, конструктор
Box
() требует передачи трех параметров. Это означает, что все объявления объектов класса
Box должны передавать конструктору
Box
() три аргумента. Например, следующий оператор недопустим ob = new B o x (Поскольку конструктор
Box
() требует передачи трех аргументов, его вызов без аргументов — ошибка. Эта ситуация порождает три важных вопроса. Что если нужно было просто определить параллелепипед и его начальные размеры не имели значения (или небыли известны Или нужно иметь возможность инициализировать куб, указывая только один размер, который должен использоваться для всех трех измерений При текущем определении класса
Box все эти дополнительные возможности недоступны.
К счастью, решение подобных проблем несложно достаточно перегрузить конструктор
Box ()
, чтобы он учитывал только что описанные ситуации. Ниже приведена программа, которая содержит усовершенствованную версию класса
Box, выполняющую эту задачу В этом примере класс Box определяет три конструктора для

инициализации размеров параллелепипеда различными способами
Глава 7. Более пристальный взгляд на методы и классы 6 9
class Box {
double width;
double height;
double depth;
// конструктор, используемый при указании всех измерений
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// конструктор, используемый, когда ни один из размеров не указан
Box () {
width = -1;
// значение -1 используется для указания = -1; // неинициализированного
depth = -1;
// параллелепипеда конструктор, используемый при создании куба
Box(double len) {
width = height = depth = len;
}
// вычисление и возврат объема
double v o l u m e () {
return width * height * depth;
}
}
class OverloadCons {
public static void main(String a r g s []) {
// создание параллелепипедов с применением различных
// конструкторов myboxl
= new
B o x (10, 20, 15);
Box mybox2
= new
B o x ();
Box mycube
= new
B o x (7);
double vol;
// получение объема первого параллелепипеда
vol = my b o x l .v o Объем myboxl равен " + vol);
// получение объема второго параллелепипеда
vol = Объем mybox2 равен " + vol);
// получение объема куба
vol = m y c u b e .vo l u m e (Объем mycube равен " + Эта программа создает следующий вывод.
Объем myboxl равен 3 000.0 Объем mybox2 равен -1.0
Объем mycube равен Как видите, соответствующий перегруженный конструктор вызывается в зависимости от параметров, указанных при выполнении оператора new.

1 7 Часть I. Язык Использование объектов в качестве параметров
До сих пор в качестве параметров методов мы использовали только простые типы. Однако передача методам объектов и вполне допустима, и достаточно распространена. Например, рассмотрим следующую короткую программу Методам можно передавать объекты
class Test {
int a, b;
Test(int i, int j) {
a = i; b = j ;
}
// возврат значения true, если параметр о равен вызывающему объекту
boolean equals(Test о) {
i f (о.а == а && o.b == b) return true;
else return false;
}
}
class PassOb {
public static void main(String a r g s []) {
Test obi = new T e s t (100, 22);
Test ob2 = new T e s t (100, 22);
Test ob3 = new Test(-1, -1);
System.out.println("obi == o b 2 :
" + o b i .equals(ob2));
System.out.println("obi == o b 3 :
" + o b i Эта программа создает следующий вывод b i = = o b 2 : t r u e o b i == о Ь З : f a l s Как видите, метод e q u a l s () внутри метода T e s t проверяет равенство двух объектов и возвращает результат этой проверки. То есть он сравнивает вызывающий объект стем, который был ему передан. Если они содержат одинаковые значения, метод возвращает значение t r u e . В противном случае он возвращает значение f a l s e . Обратите внимание на то, что параметров методе e q u a l s (указывает T e s t в качестве типа. Хотя T e s t — тип класса, созданный программой, он используется совершенно также, как встроенные типы Одно из наиболее часто встречающихся применений объектов-параметров — в конструкторах. Часто приходится создавать новый объект так, чтобы вначале он не отличался от какого-то существующего объекта. Для этого потребуется определить конструктор, который в качестве параметра принимает объект его класса. Например, следующая версия класса Box позволяет выполнять инициализацию одного объекта другим В этой версии Box допускает инициализацию одного объекта другим
class Box {
double width;
double height;
double depth;
Глава 7. Более пристальный взгляд на методы и классы Обратите внимание на этот конструктор / Он использует объект типа Box.
Box(Box ob) { // передача объекта конструктору
width = ob.width;
height = ob.height;
depth = ob.depth;
}
// конструктор, используемый при указании всех измерений w, double h, double d) {
width = w;
height = h;
depth = d;
}
// конструктор, используемый, если ни одно из изменений не указано o x () {
width = -1;
// значение -1 используется для указания = -1; // неинициализированного
depth = -1;
// параллелепипеда конструктор, используемый при создании куба len) {
width = height = depth = len;
}
// вычисление и возврат объема v o l u m e () {
return width * height * depth;
}
lass 0verloadCons2 {
public static void main(String a r g s []) {
// создание параллелепипедов с применением различных конструкторов myboxl
= new
B o x (10,
20, 15);
Box mybox2
= new
B o x ();
Box mycube
= new
B o x (7);
Box myclone = new Box(myboxl);
// создание копии объекта myboxl
double vol;
// получение объема первого параллелепипеда
vol = Объем myboxl равен " + vol ) ;
// получение объема второго параллелепипеда
vol = Объем mybox2 равен " + vol);
// получение объема куба
vol = mycube.volume();
System, out .println (Объем куба равен " + vol),;
// получение объема клона
vol = myclone.volume();

1 7 2 Часть I. Язык Объем клона равен " + Как вы убедитесь, приступив к созданию собственных классов, чтобы объекты можно было создавать удобными эффективным образом, нужно располагать множеством форм конструкторов.
Более пристальный взгляд на передачу
аргументов
В общем случае существует два способа, которыми компьютерный язык может передавать аргументы подпрограмме. Первый способ — вызов по значению. При использовании этого подхода значение аргумента копируется в формальный параметр подпрограммы. Следовательно, изменения, выполненные в параметре подпрограммы, не влияют на аргумент. Второй способ передачи аргумента — вызов по ссылке При использовании этого подхода параметру передается ссылка на аргумента не его значение. Внутри подпрограммы эта ссылка используется для обращения к реальному аргументу, указанному в вызове. Это означает, что изменения, выполненные в параметре, будут влиять на аргумент, использованный в вызове подпрограммы. Как вы убедитесь, хотя в Java применяется передача по значению для всех аргументов, конкретный эффект будет зависеть оттого, передается ли базовый тип или ссылочный.
Когда методу передается элементарный тип, он передается по значению. Таким образом, создается копия аргумента, и все происходящее с параметром, который принимает аргумент, не оказывает влияния вне метода. Рассмотрим следующую программу Элементарные типы передаются по значению
class T e s t ‘{
void meth(int i, int j) {
i *= 2;
j /= 2;
}
}
class CallByValue {
public static void main(String a r g s []) {
Test ob = new T e s t ();
int a = 15, b = аи перед вызовом
"
+
a + " 1
1
+ b) ;
ob.meth(a,
b ) аи после вызова " +
a + " " + b) Вывод этой программы имеет следующий вида и b перед вызовом 15 2 0 аи после вызова 15 2 0

}
Глава 7. Более пристальный взгляд на методы и классы 7 Как видите, выполняемые внутри метода m e t h () операции не влияют назначения переменных аи Ь, использованных в вызове. Их значения не изменились.
При передаче объекта методу ситуация изменяется коренным образом, поскольку по существу объекты передаются при вызове по ссылке. Следует помнить, что при создании переменной типа класса создается лишь ссылка на объект. Таким образом, при передаче этой ссылки методу, принимающий ее параметр будет ссылаться на тот же объект, на который ссылается аргумент. По сути, это означает, что объекты действуют, как если бы они передавались методам по ссылке. Изменения объекта внутри метода влияют на объект, использованный в качестве аргумента. Рассмотрим такую программу Объекты передаются по ссылке на них Test {

int a, b;
Test(int i, int j) {
a = i ;
b = j;
}
// передача объекта
void meth(Test o) { о .a *= 2;
о .
b / = 2 ;
}
}
class PassObjRe {
public static void main(String a r g s []) {
Test ob = new T e s t (15, аи перед вызовом " +
ob.a + " " + аи после вызова " +
o b .a + " " + o b .b Эта программа создает следующий выводи перед вызовом 15 2 0
ob.a и ob.b после вызова 3 0 Как видите, в данном случае действия внутри метода m e t h () влияют на объект, использованный в качестве аргумента.
Пом ните Когда ссылка на объект передается методу, сама ссылка передается с использованием вызова по значению. Однако поскольку передаваемое значение ссылается на объект, копия этого значения все равно будет ссылаться на тот же объект, что и соответствующий аргумент.
Возврат объектов
Метод может возвращать любой тип данных, в том числе созданные типы классов. Например, в следующей программе метод i c r B y T e n ( ) возвращает объект

Ш
1   ...   8   9   10   11   12   13   14   15   ...   90


написать администратору сайта