До сега терминът функция бе използван за означаване на именувана подпрограма. По-обичайния термин в Javaе метод, както в “начин да се направи нещо.” Ако искате, може да продължите да мислите в термините на функции. Разликата е само синтактична, но от сега нататък в книгата ще се идползва “метод” а не “функция.”
Методите в Java определят съобщенията, които обектът може да приема. В тази секция ще научите колко просто е да се създават методи.
Основните части на метода са името, аргументите, връщаната стойност и тялото. Ето основната форма:
returnType methodName( /* списък аргументи */ ) {
/* Тяло на метода */
}
Връщаният тип е типът на променливата, която изскача от метода след неговото извикване. Името, както се досещате, го идентифицира. Списъкът аргументи показва нещата, които се дават на метода и типа им.
В Java методи могат да се създават само като част от клас. Метод може да се вика само за обект3 и този метод трябва да може да осъществи извикването. Ако се опитате да извикате неправилен метод на обектще получите грешка при компилация. Методът се вика подобно както се означават член-променливите, както тук: objectName.methodName(arg1, arg2, arg3). Да предположим например че имате метода f( ) който не приема аргументи и връща int. Тогава ако имате обект наречен a за който f( ) може да бъде извикан, това може да стане така:
int x = a.f();
Типът на връщане трябва да е съвместим с типа на x.
Актът на извикване на метод общоприето се означава като изпращане на съобщение към обект. В горния пример съобщението е f( ) и обектът е a. ОО програмиране често в резюме се изразява с “изпращане съобщения на обекти.”
Списъкът аргументи
Списъкът аргументи на метода задава каква информация може да му се дава при извикване. Както можете да познаете тази информация – както всичко друго в Java – има формата на обект. Такаче трябва да зададете вида на обектите и имената, които ще се използват. Както във всяка ситуация в Java с обекти фактически се предават манипулатори.4 Типът на манипулатора трябва да бъде точен, обаче. Ако аргументът се предполага да е String това, което давате трябва да е стринг.
Да кажем че един метод взима за аргумент стринг. Ето какво трябва да се сложи в дефиницията на класа, за да бъде компилирано:
int storage(String s) {
return s.length() * 2;
}
Този метод казва колко байта трябват за да се вмести информацията от конкретен стринг String. (Всеки char в String е 16 бита, или два байта дълъг, за да поддържа Unicode знаци.) Аргументът е от тип String и е наречен s. Щом s е даден на метода, може да го третирате като всеки друг обект. (Може да му изпращате съобщения.) Тук length( ) метода се извиква, който е един от методите на Strings; той връща броя на знаците в стринга.
Може да видите също използвана ключовата дума return която прави две неща. Първо тя значи “излез от метода, свършихме.” Второ, ако методът произвежда стойност, тази стойност се слага точно след return оператора. В този случай връщаната стойност се дава с s.length( ) * 2.
Може да се връща всякакъв тип, но ако не щете да се връща нищо, показвате го с декларация на метода като void. Ето примери:
boolean flag() { return true; }
float naturalLogBase() { return 2.718f; }
void nothing() { return; }
void nothing2() {}
Когато типът на връщане е void, ключовата дума return се използва само да се излезе от метода и затова не е необходима, ако стигнете края на метода. Може да излезете от всяка точка на метода, но ако сте дали тип на връщане не-void компилаторът ще осигури верния тип независимо къде излизате.
В този момент може да изглежда, че програмата е само обекти, чиито методи вземат за аргументи други обекти и това е всичко. Това разбира се е повечето от работата, но в следващата глава ще научите как да вършите работата на ниско ниво, вземайки решения в методите. За тази глава изпращането на съобщения стига.
Построяване на Java програма
Има няколко други неща които трябва да резберете, преди да видите своята първа Java програма.
Видимост на имената
Управлението на имената е проблем във всеки програмен език. Ако използвате име в даден модул и друг програмист го използва в друг модул, как ще ги различните и предотвратите “сблъскването”? В C това особено е проблем, понеже често програмите са неуправляемо море от имена. C++ класовете (на които са базирани Java класовете) вместват функциите в класовете ("манглинг" на имената - б.пр.) така че не може да се получи сблъсък с имената в друг клас. Обаче C++ позволяма и глобални данни и глобални функции, така че то все още е възможно. За да реши проблема C++ въвежда namespaces използвайки допълнителни ключови думи.
Java можа да избегне проблема чрез свеж подход. За да произведе недвусмислено име за библиотека, използвания спецификатор прилича на домейново име в Мрежата. Фактимески създателите на Java искат да използвате домейновото си име отзад напред, понеже тези имена са уникални. Щом моето домейново име е BruceEckel.com, моята библиотека от foibles би била наречена com.bruceeckel.utility.foibles. След обърнатата дамейново име точките представляват поддиректории.
В Java 1.0 и Java 1.1 домейновите разширения com, edu, org, net и т.н. са с главни букви по договаряне, така че името щеше да бъде: COM.bruceeckel.utility.foibles. При разработването на Java 2 беше обаче открито че това създава проблеми и сега цялото име на библиотеката е с малки букви.
Този механизъм в Java означава, че всичките ви файлове имат отделни пространства на имената и всеки клас във файл има уникален идентификатор. (Имената на класове в един файл трябва да са уникални, разбира се.) Така че не се налага да учите специални черти на езика за да го осигурите - механизмът го осигурява заради вас.
Използване на други компоненти
Щом поискате да използвате предварително дефиниран клас във вашата програма, компилаторът трябва да знае къде да го намери. Разбира се класът би могъл да съществува в същия сорс, откъдето се използва. В този случай просто го използвате, даже и да е определен по-късно (от мястото на използване - б.пр.) във файла. Java премахва проблема “forward referencing” така че няма защо да мислите за него.
Как ще е ако класът е определен в друг файл? Мже да си помислите, че компилаторът е достатъчно умен да го потърси и намери, но има проблем. Да допуснем, че искате клас с определено име, но дефиниция за клас с такова име има в повече от един файл. Или още по-лошо, да допуснем че пишете програма и слагате в библиотека клас, чието име се дублира с името на клас, който вече е в библиотеката.
За да се реши въпросът трябва да се премахнат всички потенциални двусмислия. Това се прави чрез указване на Java точно кои класове искате чрез ключовата дума import. import казва на компилатора да вземе package, което е библиотека от класове. (В други езици библиотеката би съдържала също и данни и функции, но помним, че в Java всичко е в рамките на класове.)
Повечето пъти ще използвате класове от стандартните библиотеки на Java които идват с компилатора. С тях няма нужда да се безпокоите с дълги, обърнати домейнови имена; просто пишете, например:
import java.util.ArrayList;
за да кажете на компилатора че искате класа на Java ArrayList. Обаче util може да съдържа множество класове и вие да искате да ги използвате без да ги заявявате един по един. Това лесно се прави чрез използване на ‘*’ за индикация:
import java.util.*;
По-общоприето е да се импортира колекция от класове по този начин, отколкото отделни класове.
Ключовата дума static
Нормално като създавате клас описвате как обектите ще изглеждат и какво поведение ще имат. Фактически нищо не става, докато не създадете клас с new и чак след това са достъпни променливите и методите.
Има две ситуации, където този подход е недостатъчен. Едната е когато искате да има единствени данни за много обекти, без значение колко има създадени и даже ако няма. Другата е ако ви трябва метод, който не е асоцииран с никой клас. Тоест метод, който може да се вика ако няма създадени класове. Може да постигнете и двата ефекта с ключовата дума static. Когато напишете че нещо е static това значи че нещото не е свързано с никакъв конкретен екземпляр (обект - б.пр.) на този клас. Така че и да не сте създали обект може да викате static метод или да използвате static данни. С обикновените, не-static данни и методи трябва да създадете обект и да използвате именно неговите данни и методи, понеже за не-static данни и методи трябва да се знае с кой точно обект работят. Разбира се, тъй като static методите не изискват да е създаден обект за да бъдат използвани, те не могат направо да имат достъп до не-static членове и методи чрез просто извикване без споменаване на имената им (тъй като не-static членовете и методите трябва да са свързани с конкретен обект).
Някои ОО езици използват термините class data и class methods, което значи че данните и методите са на категорията на класа, а не на отделен обект. Понякога литературата за Java използва същите термини.
За да се направи член-променлива static, просто се слага ключовата дума пред дефиницията. Примерът прави static член и го инициализира:
class StaticTest {
static int i = 47;
}
Сега и да направите два StaticTest обектаще има една и съща памет за StaticTest.i. За двата обекта I е общо. Нека:
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
В тази точка и st1.i и st2.i имат една и съща стойност 47 понеже я вземат от едно и също място в паметта.
Има два начина за споменаване на static променлива. Както е по-горе, може да е с името на обект, st2.i. Може също и направо чрез името на класа, което не може да се направи с не-статичните. (Това е предпочитаният начин, доколкото подчертава статичната природа на static променливата.)
StaticTest.i++;
Операторът ++ инкрементира променливата. В тази точка и st1.i и st2.i ще имат стойност 48.
Подобна логика се прилага към статичните методи. Може да се обръщате към тях чрез обекта, както и към всеки метод или да използвате допълнителния синтаксис classname.method( ). Статичният метод се дефинира по подобен начин:
class StaticFun {
static void incr() { StaticTest.i++; }
}
Може да се види че StaticFun методът incr( ) инкрементира static данната i. Може да се извика incr( ) по типичния начин, чрез обект:
StaticFun sf = new StaticFun();
sf.incr();
Или, понеже incr( ) е статичен метод, чрез името на класа:
StaticFun.incr();
Докато static, приложен към член-данни определено променя начина по който се създават данните (една за всички класове срещу една за всеки обект в не-static случая), приложена към методи не е толкова драматична промяната. Важно приложение на static за методи е да позволи да се използват методи без създаване на обект. Това е съществено, както ще видим, при създаването на main( ) метода, който е входната точка за стартиране на програмата.
Като всеки метод и static методът може да създава и използва обекти от неговия си тип, така че static методът често се използва за “овчар” на стадото от екземпляри от неговия си тип.
Сподели с приятели: |