След като разгледахме типовете данни в езика Java, установихме, че те се делят на примитивни и на референтни. Казахме, че всеки примитивен тип си има и свой еквивалентен референтен, който го обвива. В теорията на обектноориентираното програмиране основно място заема идеята за обектите, като начин да се опише заобикалящия ни свят. В този ред на мисли, за да бъде дефиниран даден обект Х, който има качества У и Z и може да извършва еди какви си действия {x,y,z}, то той трябва да бъде създаден по предварително определен конкретен шаблон. Затова от тук нататък нека да приемем за определение следното твърдение:
Клас наричаме съвкупност от описания на възможните състояния, в които може да се намира и на възможните действия, които може да извършва, даден тип обект от реалността.
От горното определение следва, че наистина класът е просто описание на всичко, което всъщност е даденият тип – типични белези, типично поведение и тн. – т.е всичко, което е характерно за екземплярите от дадения вид. Когато по изградените шаблони (класове) създаваме конкретни представители от конкретния вид, то казваме, че сме създали обект. Новосъздаденият обект е от тип „името на този клас“. Така че можем да дадем следното определение за обект:
Обект е конкретен екземпляр, създаден по предварително направената дефиниция на даден клас.
Какво трябва да съдържа един клас?
Всеки клас съдържа описание за това какви данни са необходими, за да се дефинира един пълен обект от дадения тип и да се опише напълно всяко негово състояние – дебел, тънък, пълен, студен, топъл, висок, нисък, слаб, черен, жълт, лъже, не лъже, има криле, няма криле и тн. Класът съдържа просто дефиниция на тези качества под формата на променливи, а конкретните стойности на променливите се носят от самите обекти, създадени по- късно по този модел. Освен променливите, с които се описва състоянието на обектите, може да се опшише и поведението им. ООП разполага със средство за това – методи в класа. Методите са всички възможни действия, които даденият тип може да извърши по принцип – лаене, говорене, писане, четене, рисуване и тн. Дали конкретният обект, създаден по-късно по тази дефиниция, ще реши да ги извършва или не, е лично негова работа- ако реши ще пеее, ако реши ще пише и тн.
Елементи на класа:
Декларация – това е редът, на който декларираме класа с неговото име и модификатор за достъп. Името на класа по стандарт е с главна буква. Например:
public class Person{ ............. }
Модификаторите за достъп са 3 ключови думи в Java, които се поставят пред променливи, класове и методи по време на декларация, с цел да зададат видимост на дадения деклариран тип. Те са: private, protected и public. Първият, поставен пред дадения тип, означава видимост само в рамките на класа, вторият – в класа, в наследниците му и в пакета на класа му и трятият – навсякъде: в класа, в пакета, в наследниците, в други пакети, които са в рамките на проекта. Има и четвърти вариант – да не бъде поставен никакъв модификатор, т.нар. package-private – при него видимостта е само в класа и в пакета. Долната таблица ги обобщава:
Access Levels | ||||
Modifier | Class | Package | Subclass | World |
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
no modifier | Y | Y | N | N |
private | Y | N | N | N |
- Тяло на класа: в тялото се описва класът – вътре са всички променливи и методи. То в заключено межу две къдрави скоби:
public class Person { .....тяло..... }
- Конструктор – псевдометод, с който се създават впоследствие нови обекти от този клас. Изисквания към конструктора:
- Името му съвпада с името на класа;
- Няма тип на връщания резултат;
- Модификатор public или protected;
- Може да приема аргументи, може и да не приема(по подразбиране).
Важно: Ако изрично не напишем конструктор Java си създава по подразбиране конструктор за всеки клас – той е без параметри(default constructor). Ако създадем какъвто и да е друг конструкто, то този по подразбиране вече не съществува!
Пример:
public Person() { ….. констр. без параметри }
public Person(String name){ …. //констр. с параметри }
- Полета : всички променливи, с помощта на които се описва даденият клас. Те отразяват състоянието на обекта или помагат на опредлени методи в класа. Всеки обект си има своя стойност, записана в тези полета- един обект не може да споделя с друг обща стойност на тези полета, освен ако те не са статични. Статичните полета (декларират се с кл. дума static пред себе си) са такива полета, които са общи за всички инстанции на класа.
Пример:
private String name; protected int yearOfBirth; private static int numb = 5;
- Методи: методите са начин, за да се опише поведението, което е присъщо на представителите на дадения клас. Метод се декларира така:
public void speаk() { System.out.println("Hi, my name is " + name); }
- Get and Set методи– използват се от гледна точка на скриването на данни, за да осигури регламентиран и контролиран достъп до полетата на класа. В литературата се наричат още аксесори и мутатори. Това са методи, чрез които се достъпва даденото поле за четене или за модификация. По конвенция започват винаги с get или със set, следвани от името на полето. Например
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
Ключовата дума this, поставена пред името на полето, означава достъп до нестатичен елемент(поле, метод) на класа, в който се намираме.
this.name = name ; -> Тук първото name е полето на класа, а второто – параметъра, предаден през метода setName.
От гледна точна на т.нар. капсулация на данни (един от основните принципи на ООП), стратегията е следната: всяко поле се декларира с модификатор за достъпп private и за всяко се пишат get и set методи с модификатор public. По този начин винаги, когато искаме да модифицираме стойността на дадено поле, ще трябва да го правим през метод. Ако искаме да направим валидация преди промяна на стойността му, например полето може да заема стойности само положителни числа от 1 до 10, то можем да направим провека преди да зададем (set) новата стойност на полето, предадена като аргумент на set метода.
Един готов клас:
package javaExamples; public class Person { private String name; private int yearOfBirth; public Person() { this.name = ""; this.yearOfBirth = 0; } public Person(String name, int yearOfbirth) { this.name = name; this.yearOfBirth = yearOfbirth; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getYearOfBirth() { return yearOfBirth; } public void setYearOfBirth(int yearOfBirth) { this.yearOfBirth = yearOfBirth; } public void speak() { System.out.println("Hi, my name is " + getName()); } }
2. Наследяване:
Вече разбирате, че целият Java свят е изграден от обекти, изобщо обектно-ориентираното програмиране е изграден от обекти и взаимодействието между тях. Както в реалния свят, така и тук, съществува йерархичност между обектите. Обектите представляват определени видове, съответно всеки вид може да има и свой подвид, така, както е в реалния свят. В обектноориентираните езици всеки клас може да има безброй много наследници. Специално в Java, един клас може да наследни НЕ повече от един клас(single inheritance). Това е много важна особеност, защото това го отличава силно от С++, например, който език е пример за обектно ориентиран език, който поддържа множествено наследяване (multiple inheritance). Когато един клас наследява друг клас съществуват определени правила:
- Дъщерният клас разполага с всички public и protected променливи (полета) и методи на класа родител.
- Родителският клас НЕ разполага с променливите и методите на дъщерния клас, независимо от модификаторите им за достъп.
- Дъщерният клас задължително трябва да „разшири“ родителския- да съдържа нови полета, или нови методи(функционалност).
- Родителският клас не трябва да е final.
- Родителският клас трябва да е с модификатор за достъп, различен от private.
Пример:
public class Worker extends Person { // worker’s fields.. public Worker() { super(); } // worker’s methods…. }
Както виждате наследяването става с ключовата дума extends след декларацията на новия клас, а след нея се посочва кой е наследяваният клас. Извикването на super(); на първият ред в конструктора на новият клас е задължително – с него се извиква конструктора на родителския клас, за да може при създаване на инстанции на новия клас, да се инициализират не само данните му, но и тези на родителския клас, тъй като на практика и той самият ги притежава.
3. Създаване и използване на обекти:
- Създаване и инициализиране на обекти:
Обекти се създават не –по различно от обикновени променливи. Например нека person е променлива от тип новосъздадения Person :
Person person ;
На горния ред е просто създаване на променлива от този тип, но типът е референтен, а стойността по подразбиране за референтните типове е null. Така че в момента стойността на person e null, тоест празна, тоест не сочи на никъде.
За да го инициализираме, ще извикаме ключовата дума new, с която се заделя памет за обекта, последвана от конструктора на класа. Както споменахме по-горе, той е специален метод, който слуши за да се инициализират полетата на класа (name, age и др.)
Person person = new Person();
Класът имаше 2 конструктора – единият с и един без параметри. При инициализиеране на обекта можем да ползваме който и да е от дефинираните конструктори.
Person person = new Person(“Ivan”,25);
- Извикване на метод на обект:
Извикването на метод на обект става по следния начин:
obj.objMethod();
Тоест, за да достъпим метода през обекта, използваме точка.
Ако методът е статичен, тогава той може да се достъпи само с името на класа :
Person.someStaticMethod();
Важно:
- Инициализирайте обектите тогава, когато е необходимо.
- Внимавайте за неинициализирани обекти. Това ще доведе до nullPointerException – това е грешка, която ще прекъсне изпълнението на вашата програма. Получава се когато се опитвате да достъпите поле или метод на неинициализиран (празен или null) обект.
- След като вашият метод приключи изпълнението си, обектът излиза от обхват и системата за почистване на неизползвани обекти в Heap-а – Garbage Collector ще го унищожи и почисти.
Задача 1:
Да се напише клас фирма (Company), който има следните член променливи:
- Име на фирмата – тип string;
- Дата на създаване на фирмата – тип string;
- Булстат – уникален 10 знаков код включващ букви и цифри – тип string.
За всички член променливи напишете get и set методи, като за последния направете проверка за дължина на string-а в set-метода му(length = 10).
Да се напише клас Фирма-ЕТ, който да наследява класа Фирма и да има следните член променливи:
- Име на собственика, учредил фирмата- тип string;
- Първоначален капитал – тип double;
- Актуален капитал – тип double;
За всички член променливи напишете get и set методи.
Класът Фирма-ЕТ трябва да има метод, който изчислява печалбата към днешна дата на фирмата. Той трябва да бъде нестатичен, да не приема параметри, и трябва да връща като резултат число double, което да бъде разликата межу актуалния и първоначалния капитал на съответната фирма.
Задача 2:
1.Създайте клас Car. Като член променливи има марка, модел, цвят, мощност, тип двигател, тип скоростна кутия, година на производство. Създайте гет и сет методи. Създайте конструктор с и без параметри.
2.Създайте метод, който по подаден масив от коли и параметър буква(char), “прочиства” масива от коли и връща нов масив от коли, на които марките им започват с подадената като параметър буква.
3.Създайте втори метод, който по подаден параметър масив от коли, връща отново масив от коли, но сортиран по възходящ или по низходящ ред, в зависимост от марката на колата. Нека редът да се определя от параметър на метода.
4.Създайте трети метод, който по подаден масив от коли проверява дали измежду колите има повтарящи се и връща “прочистен” масив от коли, в който няма повтарящи се коли.
5.Създайте клас Test с main метод, в който създайте няколко коли и изтествайте трите метода дали работят правилно.
Явор Томов, Даниел Джолев
Bravo, mnogo dobre razbiraemo!