Полиморфизм. Абстрактные классы. Интерфейсы
Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Полиморфизм. Абстрактные классы. Интерфейсы.
1) Виртуальные методы на C++, Java. Полиморфизм.
Полиморфизм – разнообразие форм биологических существ. Нечто одинаковое по форме,
но совершенно разные по поведению.
Имеем базовый класс Record и производный Sprint с полем dec. Выполним перегрузку метода
Numbersec. Numbersec объявим виртуальнам методом.
#include "stdafx.h"
class Record
{
public:
Record(int m,int s); // конструктор с параметрами
virtual int Numbersec(); // виртуальный метод
protected:
int min,sec;
};
Record::Record(int m,int s)
{
min=m;
sec=s;
}
int Record::Numbersec()
{
int k;
k=60*min+sec;
return k;
}
class Sprint : public Record
{
public:
Sprint(int m,int s,int d);
int Numbersec(); // перегруженный виртуальный метод // в производном классе ост. виртуальным
private:
int dec;
};
Sprint::Sprint(int m,int s,int d):Record(m,s)
{
dec=d;
1
}
int Sprint::Numbersec() // перегрузка с округлением
{
int k;
k=60*min+sec;
if(dec>=5)
k++;
return k;
}
int _tmain(int argc, _TCHAR* argv[])
{
Record a(2,7);
Sprint b(2,7,8);
int m,k;
// объект вызывает собственный метод класса
m=a.Numbersec(); // m=127
k=b.Numbersec (); // m=128
return 0;
}
Убрав virtual в программе получим такой же результат, внутренняя структура заголовка
виртуальных и не виртуальных функций различна.
Отличия в работе виртуальных функций в программах
Добавим не виртуальный метод в Record:
bool qualification, метод возвращает true (разряд выполнен), если результат меньше или равен
2 мин. 7 сек.
Метод qualification класса Record, наследуется без перегрузки в Sprint.
#include "stdafx.h"
class Record
{
public:
Record(int m,int s);
int Numbersec();
bool qualification();
protected:
int min,sec;
};
2
Record::Record(int m,int s)
{
min=m;
sec=s;
}
int Record::Numbersec()
{
int k;
k=60*min+sec;
return k;
}
bool Record::qualification()
{
int k;
k=Numbersec(); // результат в секундах
if(k<=127)
return true; // выполнил
else
return false; // не выполнил
}
class Sprint : public Record
{
public:
Sprint(int m,int s,int d);
int Numbersec(); // перегруженный метод
private:
int dec;
};
Sprint::Sprint(int m,int s,int d):Record(m,s)
{
dec=d;
}
int Sprint::Numbersec()
{
int k;
k=60*min+sec;
if(dec>=5)
k++;
return k;
}
int _tmain(int argc, _TCHAR* argv[])
{
3
Record a(2,7);
Sprint b(2,7,8);
int m,k;
bool f1,f2;
f1=a.qualification(); // f1=true
f2=b.qualification(); // f2=true ! qualification вызвал метод Numbersec из базового класса
return 0;
}
Если Numbersec объявить виртуальным
{
public:
Record(int m,int s);
virtual int Numbersec();
bool qualification();
protected:
int min,sec;
};
......
bool f1,f2;
f1=a.qualification(); // f1=true
f2=b.qualification(); // f2=false !! qualification вызвал метод Numbersec из произв. класса
Принцип вызова:
class One // базовый класс
{
......
void F1();
void F2();
void F3();
virtual int Fv(); // виртуальная функция перегружаемая
.......
};
void One::F1()
{
.....
F2();
....
}
void One::F2()
{
.....
F3();
....
4
}
void One::F3()
{
.....
Fv(); // вызов виртуальной функции
....
}
int One::Fv() // виртуальная функция в базовом классе
{
return 1;
}
class Two: public One // производный класс
{
....
int Fv(); // перегрузка виртуальной функции в производном классе
}
int Two::Fv() // виртуальная функция в производном классе
{
return 2;
}
Функции F1(), F2(), F3() не перегружаются в Two, а наследуются
int main(int argc, char* argv[])
{
Two b;
b.F1(); // вызов наследуемой функции
}
Цепочки вызовов:
b.F1() -> F2() -> F3() -> Fv() –виртуальная, вернет 2 (b. объект Two)
не виртуальная Fv() вернет 1 для b.F1().
Вызов виртуальной функции определяется классом вызывающего объекта.
b.F1(); b - объект класса Two
Не виртуальной функции - классом функции, которая вызывает виртуальную.
b.F1(); --> F2() -> F3() --> Fv(); F3() - функция класса One
На Java все функции виртуальные! Ключевое слово virtual отсутствует
class Record
{
5
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
public int Numbersec()
{
return 60*min+sec;
}
public boolean qualification()
{
int k;
k=Numbersec();
if(k<=127)
return true;
else
return false;
}
}
class Sprint extends Record
{
private int dec;
Sprint(int m,int s,int d)
{
super.(m,s);
dec=d;
}
public int Numbersec()
{
int k;
k=60*min+sec;
if(dec>=5)
k++;
return k;
}
}
public class lab7
{
public static void main (String args[] )
{
6
Record a;
a=new Record(2,7);
Sprint b;
b=new Sprint(2,7,8);
boolean f1,f2;
f1=a.qualification(); // f1=true
f2=b.qualification(); // f2=false
}
}
2) Вызов виртуальной функции на С++ после присваивания указателей.
Пусть Numbersec не виртуальная функция, перегружена в Sprint с округлением.
вызов метода динамическим объектом:
int k;
Record *b; // указатели на базовый класс
Sprint *d;
// указатель на производный класс
b=new Record(2,7);
d=new Sprint(2,7,8);
b=d; // присваивание указателю базового класса указателя производного
k=b->Numbersec(); // k=127 вызов из базового класса
Если Numbersec() виртуальная,
k=b->Numbersec(); // k=128 вызов из производного класса
3) Таблицы виртуальных функций для полиморфных классов.
Принципы вызова виртуальных фукций.
Имеется базовый класс Base и производный Derive с перегруженной функцией F1() и
собственной F3().
class Base
{
public:
int F1() // не виртуальная
{
return 1;
}
};
class Derive: public Base
{
public:
7
int F1() // перегружена, не виртуальная
{
return -1;
}
int F3() // собственная производного
{
return 3;
}
};
int main()
{
int k;
Derive *d;
Base *b;
d=new Derive;
b=d; // присваиваются только поля и методы базового класса
k=b ->F1();// в b содержится только F1 из базового, которая вызывается k=1
// k=b->F3(); // этой функции в базовом нет
}
Пусть F1 виртуальная. Добавим в базовый виртуальную F2(), которая в производном не
перегружается, но наследуется.
Если есть хотя бы одна виртуальная функция, класс называется полиморфным, при
компиляции в базовом классе создается скрытый указатель, *__vptr, который
наследуется и в каждом классе создается по таблице виртуальных функций.
class Base
{
public:
FunctionPointer *__vptr; // автоматически
virtual int F1() // первая виртуальная функция перегружается
{
return 1;
}
virtual int F2() // вторая виртуальная функция задана в базовом классе в производном не
перегружается
{
return 2;
}
};
class Derive: public Base
{
public:
int F1() // перегружена
8
{
return -1 ;
}
int F3() // собственная
{
return 3 ;
}
};
Создаются две виртуальные таблицы Virtual Table.
Указатель *__vptr, созданный в Base наследуется.
Классы:
Base:
*__vptr;
----------->
virtual int F1()[1] {return 1;};
[1] <------------virtual int F2()[2] {return 2;};
[2] <-----------Derive:public Base
*__vptr; наследуется
----------->
virtual int F1()[3] {return -1;}; // перегружена
Base Virtual Table
F1()
F2()
Derive Virtual Table
[3] <----------F1()
[2]
<---------F2()
int F3(){ return 3;}
В каждой таблице каждая виртуальная функция указывает на ближайший экземпляр,
если есть в самом классе, то на нее, нет, в базовом для класса, если нет, в базовом для
базового и т.д. На верхнем уровне она обязательно есть. Эта функция и вызывается при
вызове виртуальной функции.
В таблице виртуальных функций базового класса обе виртуальные функции указывают на
экземпляры базового класса [1] [2].
В таблице виртуальных функций производного класса F1() указывает на собственную [3]
F2() на функцию базового класса (ближайшую) [2].
Вызовы:
int main()
{
int k,m,n;
Derive *d;
9
Base *b;
d=new Derive;
b=d; // скрытый указатель копируется в b, становится указателем производного
класса !
k=b ->F1();// k=-1 F1 производного класса из виртуальной таблицы производного
m=b->F2(); // m=2 из базового, ближайшая по виртуальной таблице производного
// n=b->F3(); // ошибка, F3 в базовом нет
}
При присваивании b=d; указатель *__vptr устанавливается на виртуальную
таблицу d!
k=b ->F1(); По таблице виртуальных функций для производного класса ближайшая F1 –
собственная. k=-1.
m=b->F2(); По таблице виртуальных функций для производного класса ближайшая F2 –
из базового. m=2
// n=b->F3(); F3 в базовом нет b не может вызвать.
В базовом наследуемая не виртуальная функция F(), которая вызывает перегружаемую
виртуальную функцию F1().
class Base
{
public:
FunctionPointer *__vptr;
virtual int F1() // виртуальная функция перегружается
{
return 1;
}
int F() // не виртуальная функция наследуется
{
int k;
k=F1(); // вызов виртуальной функции
return k+1;
}
};
class Derive: public Base
{
public:
int F1() // перегруженная виртуальная
{
return -1 ;
}
};
int main()
10
{
int k;
Derive d;
k=d.F(); // k=0 вызывается F1 из производного.
Вызывает объект d – указатель *__vptr устанавливается на виртуальную таблицу Derive.
Далее в цепочке вызовов через F() вызывается виртуальная функция F1(). При вызове из
таблицы для Derive находится нужный экземпляр – собственная F1().
class A
{
public:
virtual int F() {return 1;}
};
class B:public A
{
public:
int F() { return -1;}
};
class C:public B
{
private:
int unused;
};
int _tmain(int argc, _TCHAR* argv[])
{
C с;
int k;
k=с.F(); // k=-1 функция из B ближайшая
return 0;
}
A:
*__vptr;
virtual int F()[1]
----------->
{return 1;};
[1] <-------------
B:public A
*__vptr; наследуется
----------->
virtual int F()[2] {return -1;}; // перегружена
C:public B
*__vptr; наследуется
ближайшая в B
A Virtual Table
F()
B Virtual Table
[2] <----------F()
----------->
[12] <-----------
C Virtual Table
F()
11
int F() {return 1;} // невиртуальная
int _tmain(int argc, _TCHAR* argv[])
{
C x;
int k;
k=с.F(); // k=-1 функция из B
return 0;
}
Вызовется метод из B, т.к. B базовый для C по наследованию.
4) Абстрактные классы на C++, Java. Чисто виртуальные функции
C++, абстрактные функции на Java
C++
Абстрактный класс на C++ – класс содержащий по крайней мере одну чисто
виртуальную функцию.
class Pimer
{
.....
virtual int Fun(int c)=0; // чисто виртуальная функция, тело функции равно нулю
....
}
Пример пустой виртуальной функции (не чисто виртуальной)
class Pimer // не абстрактный класс
{
.....
virtual int Fun(int c);
....
}
....
int Primer::Fun(int c) // функция виртуальная, но не чисто виртуальная !
{
}
Если класс абстрактный, то нельзя создавать объектов данного класса.
int main(int argc, char* argv[])
{
Primer a; // ошибка нельзя создать объект абстрактного класса
....
12
Чисто виртуальная функция переопределяется в производных классах для которых
создаются объекты.
Пример.
Имеются три класса фигур – круг, квадрат, равносторонний треугольник. У всех классов есть
общее – координаты центра, длина (радиус для круга, длина стороны для прямоугольника и
треугольника). Площадь для всех классов вычисляется по разному.
Объявляем общим абстрактным классом геометрическую фигуру, а круг, квадрат и
треугольник производными классами.
#include
class Figure // абстрактный класс
{
protected:
double X,Y,L; // координаты центра и длина
public:
double GetLen();
Figure(double xx,double yy,double ll); // конструктор
virtual double S()=0; // чисто виртуальная функция
};
Figure::Figure(double xx,double yy,double ll)
{
X=xx;
Y=yy;
L=ll;
}
double Figure:: GetLen()
{
return L;
}
class Circle:public Figure // производный не абстрактный класс
{
public:
Circle(double xx,double yy,double ll); // конструктор не наследуется
double S();
};
Circle::Circle(double xx,double yy,double ll):Figure(xx,yy,ll) // вызов базового
{
}
double Circle::S() // перегрузка чисто виртуальной функции
{
13
return 3.1415926*L*L;
}
class Square:public Figure // производный неабстрактный класс
{
public:
Square(double xx,double yy,double ll);
double S();
};
Square::Square(double xx,double yy,double ll):Figure(xx,yy,ll)
{
}
double Square::S() // перегрузка чисто виртуальной функции
{
return L*L;
}
class Triangle:public Figure // производный неабстрактный класс
{
public:
Triangle(double xx,double yy,double ll);
double S();
};
Triangle::Triangle(double xx,double yy,double ll):Figure(xx,yy,ll)
{
}
double Triangle::S() // перегрузка чисто виртуальной функции
{
return L*L*sqrt(3.0)/8;
}
int _tmain(int argc, _TCHAR* argv[])
{
double yc,ys,yt;
Circle c(3,4,5);
yc=c.S();
Square s(3,4,5);
ys=s.S();
Triangle t(3,4,5);
yt=t.S();
//
Figure f(3,4,5); нельзя создавать объекты абстрактных классов
return 0;
}
14
Java
На Java чисто виртуальные функции называются абстрактными. Абстрактный класс
на Java – класс содержащий по крайней мере одну абстрактную функцию.
package lab7_1;
abstract class Figure // абстрактный класс
{
protected double X,Y,L; // координаты центра и длина
public double GetLen()
{
return L;
}
public Figure(double xx,double yy,double ll) // конструктор
{
X=xx;
Y=yy;
L=ll;
}
public abstract double S(); // абстрактная функция
}
class Circle extends Figure // производный не абстрактный класс
{
public Circle(double xx,double yy,double ll)
{
super(xx,yy,ll); // вызов базового конструктора
}
public double S() // перегрузка абстрактной функции
{
return 3.1415926*L*L;
}
};
class Square extends Figure // производный не абстрактный класс
{
public Square(double xx,double yy,double ll)
{
super(xx,yy,ll); // вызов базового конструктора
}
public double S() // перегрузка абстрактной функции
{
return L*L;
}
};
class Triangle extends Figure // производный не абстрактный класс
15
{
public Triangle(double xx,double yy,double ll)
{
super(xx,yy,ll); // вызов базового конструктора
}
public double S()// перегрузка абстрактной функции
{
return L*L*Math.sqrt(3.0)/8;
}
};
public class lab7
{
public static void main (String args[] )
{
double yc,ys,yt;
Circle c=new Circle(3,4,5);
yc=c.S();
Square s=new Square(3,4,5);
ys=s.S();
Triangle t=new Triangle(3,4,5);
yt=t.S();
// Figure f=new Figure(3,4,5); нельзя создавать объекты абстрактных классов
}
}
Пусть имеется класс Record для фиксации результатов по бегу на длинные дистанции (min,
sec) и производный класс Sprint с десятыми долями секунды (min, sec, dec) для результатов по
бегу на короткие дистанции.
В модели соревнований возможны три типа спортсменов:
бегун на короткие дистанции (Sprinter),
бегун на длинные дистанции (Stayer),
спортсмен – двоеборец (Combine).
У всех есть фамилия (string Fam).
У Sprinter - 1 результат класса Sprint ,
У Stayer – 1 результат класса Record,
У Combine – 1 результат типа Sprint и 1 результат типа Record.
Создается абстрактный базовый класс Runner (бегун), а Sprinter, Stayer и Combine
производные.
#include "stdafx.h"
#include
class Record
16
{
protected:
int min,sec;
public:
Record(int m,int s);
int Numbersec();
};
Record::Record(int m,int s) {
min=m;
sec=s;
}
int Record::Numbersec()
{
return 60*min+sec;
}
class Sprint : public Record
{
public:
Sprint(int m,int s,int d);
int Numbersec();
private:
int dec;
};
Sprint::Sprint(int m,int s,int d):Record(m,s)
{
dec=d;
}
int Sprint::Numbersec() // перегрузка метода
{
int k;
k=60*min+sec;
if(dec>=5)
k++;
return k;
}
class Runner // абстрактный класс для спортсменов
{
protected:
char Fam[30];
public:
Runner(char *f); // конструктор с параметрами
virtual int Totalsec()=0; // чисто виртуальная функция - результат в сек.
17
};
Runner::Runner(char *f) // конструктор - определена только фамилия
{
strcpy(Fam,f);
}
class Stayer:public Runner // один результат Record - длинная дистанция
{
protected:
Record Rezult; // 1 бег на длинную дистанцию
public:
Stayer(char *f,int m,int s); // конструктор
int Totalsec(); // перегрузка чисто виртуальной функции
};
Stayer::Stayer(char *f,int m,int s):Runner(f) // вызов базового конструктора
{
Rezult=Record(m,s);
}
int Stayer::Totalsec()
{
return Rezult.Numbersec(); // 1 результат бега на длинную дистанцию
}
class Sprinter:public Runner // один результат Sprint
{
protected:
Sprint Rezult; // 1 бег на короткую дистанцию
public:
Sprinter(char *f,int m,int s,int d);
int Totalsec(); // перегрузка чисто виртуальной функции
};
Sprinter::Sprinter(char *f,int m,int s,int d):Runner(f)
{
Rezult=Sprint(m,s,d);
}
int Sprinter::Totalsec()
{
return Rezult.Numbersec(); // 1 результат бег на короткую дистанцию
}
class Combine:public Runner // 1 результат Record, 1 - Sprint
{
protected:
18
Record Rezult1; // 1 бег на длинную дистанцию
Sprint Rezult2; // 1 бег на короткую дистанцию
public:
Combine(char *f,int m1,int s1,int m2,int s2,int d);
int Totalsec(); // перегрузка чисто виртуальной функции
};
Combine::Combine(char *f,int m1,int s1,int m2,int s2,int d):Runner(f)
{
Rezult1=Record(m1,s1);
Rezult2=Sprint(m2,s2,d);
}
int Combine::Totalsec()
{
return Rezult1.Numbersec()+Rezult2.Numbersec();
// сумма бега на короткую и длинную дистанции
}
int _tmain(int argc, _TCHAR* argv[])
{
int m,n,k;
//
Runner a; нельзя создать объект абстрактного класса
Stayer b("Иванов",2,7);
Sprinter c("Петров",2,7,8);
Combine d("Сидоров",2,7,2,7,8);
m=b.Totalsec(); // m=127
n=c.Totalsec(); // n=128 с округлением
k=d.Totalsec(); // k=127+128=255
return 0;
}
5) Интерфейсы на Java.
Интерфейс представляет собой аналог абстрактного класса, в котором есть только
абстрактные методы. Класс реализует (implements) интерфейс, если он перегружает
абстрактные методы интерфейса.
Java
package lab7_2;
interface Calculate // интерфейс доступен только для классов пакета lab7_2
// public interface Calculate доступ для любых классов
{
int Numbersec(); // абстрактный метод для классов, реализующих интерфейс
}
19
class Record
implements Calculate // реализация интерфейса Calculate
{
protected int min,sec;
public int Numbersec() // реализованный метод в интерфейсе
{
return min*60+sec;
}
public void Init(int m,int s)
{
min=m;
sec=s;
}
}
class Sprint extends Record
implements Calculate // реализует интерфейс Calculate
{
private int dec;
public int Numbersec() // метод с округлением по dec
{
if(dec>=5)
return min*60+sec+1;
else
return min*60+sec;
}
public void Init(int m,int s,int d)
{
min=m;
sec=s;
dec=d;
}
}
public class lab7
{
public static void main(String[] args)
{
Record a=new Record();
a.Init(3, 10);
Sprint b=new Sprint();
b.Init(3, 10,8);
int m,k;
m=a.Numbersec(); // вызов метода, реализованного в интерфейсе
k=b.Numbersec(); // вызов метода, реализованного в интерфейсе
System.out.printf("%d %d \n",m,k); // 190 191
}
}
20
Абстрактный класс на Java
В абстрактном классе могут быть, кроме абстрактных, обычные функции.
abstract class Runner
{
....
string Getfam()
{
return Fam;
}
}
В интерфейсах могут быть только заголовки методов.
Наследоваться может только один класс, а реализовываться несколько интерфейсов(Аналог
множественного наследования).
Java
class Sprint extends Record // наследование
implements Calculate, Calculate2 // реализует интерфейсы Calculate, Calculate2
{
................
6) Клонирование объектов на Java. Встроенный интерфейс
клонирования. Мелкое и глубокое копирование на Java
Клонирование объекта - копирование полей объекта в другой объект.
На C++ проблема присваивания динамических объектов решена перегрузкой оператора
присваивания с глубоким копированием.
При присваивании на java объектов копируется ссылка.
Java
class Record
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
21
public void Putmin(int m)
{
min=m;
}
public class lab7
{
public static void main(String[] args)
{
Record a=new Record(3,10);
Record b=new Record(4,23);
a=b;
// копируется ссылка a.min=4 a.sec=23
b.Putmin(12); // a.min=12 т.к. a,b ссылаются на одну и ту же область
}
}
public static void main(String[] args)
{
Record a=new Record(3,10);
Record b=new Record(4,23);
a=b;
// копируется ссылка a.min=4 a.sec=23
b.Putmin(12); // a.min=12 т.к. a,b ссылаются на одну и ту же область
}
Java
Добавим интерфейс клонирования.
package lab7_6;
class Record
implements Cloneable // реализация встроенного интерфейса
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
public void Putmin(int m)
{
min=m;
}
public Object clone() // перегрузка метода интерфейса Cloneable из суперкласса
{
try
{
return (Record)super.clone();
}
22
catch(CloneNotSupportedException e)
{
}
return this;
}
}
public class lab7
{
public static void main(String[] args)
{
Record a=new Record(3,10);
Record b=new Record(4,23);
a=(Record)b.clone();
// a - копия b a.min=4 a.sec=23
b.Putmin(12); // a.min=4
}
}
В основном классе есть поля вспомогательного класса
Java
package lab7_6;
class Record
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
public void Putmin(int m)
{
min=m;
}
}
class Sportsman
implements Cloneable // реализация клонирования
{
public String Fam;
public Record rd;
// результат спортсмена
public Sportsman(String f,int m,int s)
{
Fam=f;
rd=new Record(m,s);
23
}
public Object clone() // перегрузка интерфейса клонирования
{
try
{
Sportsman clone=(Sportsman)super.clone();
return clone;
}
catch(CloneNotSupportedException e)
{
}
return this;
}
}
public class lab7
{
public static void main(String[] args)
{
Sportsman a=new Sportsman("Иванов",5,15);
Sportsman b=new Sportsman("Петров",6,20);
a=(Sportsman)b.clone(); // a.Fam="Петров" a.rd.min=6 a.rd.sec=20
b.Fam="Сидоров"; // a.Fam="Петров" OK
b.rd.min=8; // a.min=8 ! копировалась ссылка rd !
}
}
В реализованном методе происходит мелкое (shallow) клонирование. Поле rd (типа
Record) является ссылкой в Sportsman и при клонировании Sportsman копируется адрес.
Оба поля указывают на одну область памяти и изменение поля в b автоматически
меняет в a.
Исправление. Record также делается Cloneable.
package lab7_6;
class Record
implements Cloneable // возможность клонирования
{
protected int min,sec;
public Record(int m,int s)
{
min=m;
sec=s;
}
public void Putmin(int m)
{
min=m;
24
}
public Object clone() // перегрузка интерфейса клонирования
{
try
{
return (Record)super.clone();
}
catch(CloneNotSupportedException e)
{
}
return this;
}
}
class Sportsman
implements Cloneable // реализация клонирования
{
public String Fam;
public Record rd;
// результат спортсмена
public Sportsman(String f,int m,int s)
{
Fam=f;
rd=new Record(m,s);
}
public Object clone() // перегрузка клонирования
{
try
{
Sportsman clone=(Sportsman)super.clone();
clone.rd=(Record)rd.clone(); // клонирование поля rd (Record) !
return clone;
}
catch(CloneNotSupportedException e)
{
}
return this;
}
}
public class lab7
{
public static void main(String[] args)
{
Sportsman a=new Sportsman("Иванов",5,15);
Sportsman b=new Sportsman("Петров",6,20);
a=(Sportsman)b.clone();
25
b.Fam="Сидоров";
b.rd.min=8; // a.rd.min=6 прежнее значение
}
}
В реализованном методе происходит глубокое (deep) клонирование.
26