Общи характеристики на езика


Java поддържа частично константни обекти и методи



страница3/5
Дата25.02.2017
Размер432.83 Kb.
#15745
ТипЛекция
1   2   3   4   5

Java поддържа частично константни обекти и методи


В C++, могат да се декларират формални параметри на функциите или връщаните стойности като const, с което се защитава тялото на функцията да променя аргументите и викащият да модифицира връщаната стойност. Това важи и за деклариране на член функциите, с което се забранява да се променят членовете на обекта в тялото на функцията.

Модификаторът final се използва в Java за да се осигурят константни, само за четене стойности. Този модификатор не осигурява ограничаването на промяната на обектите, когато те се предават като аргументи, ограничаването на връщаните стойности да бъдат само за четене и ограничаването на методите да модифицират обектите, с които работят.

Този пропуск е по-малкият проблем в Java спрямо C++, главно поради разликата между класа String и масивът от символи, но може да стане източник на грешки. В частност няма начин да се гарантира, че метод, който не трябва да променя обект, няма да го промени по невнимание на програмиста.

Низ (String)

    1. Низовете в Java се различават от масивите със символи на C++


Java включва класа String, който е константен тип. String не е еквивалент на масив от символи, въпреки че може да се създаде обект String с използване на масив от символи. Класът трябва да се използва вместо масив от символи, тъй като той не може да се презапише по грешка на програмиста при предаването на стойността му като параметър на метод.

С и С++ нямат вградена поддръжка на низове. Стандартната техника сред С и С++ програмистите е да се използват символни масиви, завършващи с нула. В Java низовете са имплементирани като обекти от първи клас (String и StringBuffer), което означава, че са в ядрото на Java. Java имплементацията на обекта низ има няколко предимства:



  • Начинът, по който се създават и се прави достъп до елементите на низовете, е уеднаквен и може да се прилага за всички низове на всички системи;

  • Тъй като низовите класове в Java са дефинирани като част от самия език, а не като някакво външно разширение (STL), те функционират по дефинирания в езика начин, независимо как са създадени;

  • Класовете на Java за представяне на низове извършват значителни проверки по време на изпълнение, което елиминира допускането на грешки и проблеми на тестването;

Въпреки че е лесно да се видят предимствата на низовете в Java, конвертирането на С++ код, който използва често завършващи с нула символни масиви може да е проблемно. Заедно с указателите, символните низове са един от най-големите проблеми при прехвърлянето на код между езиците. Трябва да се анализира много задълбочено в детайлите на програмния код и да се разбере начина на използването им в кода на C++, за да се прехвърли правилно на Java. Проблемът е, че символните масиви са често използвани в почти всяка С++ програма. Пример 1.13 съдържа проста С++ функция, която работи с символни низове, завършващи с нула.

Пример 1.13. C++ функцията ReverseIt.

char* ReverseIt(const char* szText) {


  int len = strlen(szText);
  char* dest = new char[len];

  for (i = (len - 1); i >= 0; i--)


    dest[len - i - 1] = szText[i];
  return dest;
}

Функцията ReverseIt получава като аргумент масив от символи и връща масив от символи подредени в обратен ред. Програмата може да има и вариант с преминаване през размера на масива, или да се работи с указатели към символи. Въпреки че кодът в случая работи добре, спецификата на С++ го прави уязвим към потенциални грешки. Java версията на reverseIt, която вече е член на класа Reverse е показана в Пример 1.14.



Пример 1.14. Java класът Reverse.

class Reverse {


  String reverseIt(String s) {
    int i, len = s.length();
    StringBuffer dest = new StringBuffer(len);

    for (i = (len - 1); i >= 0; i--)


      dest.append(s.charAt(i));
    return dest.toString();
  }
}

Java класът reverseIt не използва масиви. В Java низовете са обекти от ядрото на езика, представени от класовете String и StringBuffer. Те са прости типове данни, които могат да се използват също както целите числа или числата с плаваща запетая. Всички модификации на низовете в Java трябва да стават чрез методите, дефинирани в класовете String и StringBuffer, както се вижда в метода reverseIt.


    1. Програмни оператори на езика


  1. В голямата си част имат подобно действие с тези на езика С++. Има разлика във вътрешното представяне на оператора за цикъл for, който се преобразува в еквивалентен while:



  2. for(i = 0; i < 10; i++)

  3. {

  4. System.out.println(i);

  5. }



  6. i = 0;

  7. while (i < 10)

  8. {

  9. System.out.println(i);

  10. i++;

  11. }





  12. Не съществува оператор goto. Механизмът за безусловно предаване на управлението е break label или continue label, който се използва за предаване на управлението във вътрешността на вложени цикли. Оператори от подобен вид не е нужно да се използват, поради което няма да се разглеждат засега.


    1. Пакетите в Java (Packages) заменят именните пространства


Пакет – Съвкупност от логически свързани класове и интерфейси, осигуряващи защита от колизии и управление на именните пространства (в C++ се използва namespace).

За да се опрости търсенето и използването на класовете, както и контрола на достъпа, програмистът трябва да групира свързаните класове и интерфейси в пакети (Packages).

Проблемите с дублирането на иманата на програмните елементи и тяхното групиране в именни пространства, решавани с namespace в С++, в Java се решават с обединяването на класовете в пакети с наименования.

С концепцията за пакетирането при Java се осигурява адресиране на имената, при което се разграничава пространството, в което е разположен класа. Еднаквите имена в различните пакети са абсолютно уникални. Основното, което трябва да се осигури е отделянето на свързаните класове в общ пакет.

Java не решава общия проблем на колизиите между имена. Разширяването на базов клас и свързаната с това колизия на имената с наследения клас ще предизвика колизия. Например, ако се наследят множество от класове, разработени от някой разработчик, които са базови за разработката и се използва метод с име foo в производния клас, ще възникнат проблеми при добавяне на метод със същото име в следващите версии на програмата, ако в класовете на разработчика се появи метод с име foo.

Java използва пакет вместо именно пространство. Пакетите обединяват също и компонентите на библиотека с определено име на библиотеката. За използването на библиотеката в дадена програма се използва директивата import и компилаторът осигурява достъп до тези компоненти.

Пример: Пакет за четене на данни от входен файл:

package FilePackage;

import java.util.*; // Date class

import java.io.*; // System & File IO
public class FileTest {

private void fileRead()

{

String str;

try { // C:\\jdev\\EclipseWorkspace\\TestApp\\FileApp\\FilePackage\\

RandomAccessFile fin = new RandomAccessFile ("
\\FileTest.java","r");


while((str = fin.readLine()) != null ) {

System.out.println(str);

}

fin.close();

} catch(EOFException e) {

System.out.println(

"End of file encountered");

}

catch(FileNotFoundException e) {

System.out.println(

"FileNotFound");

}

catch(IOException e) {

System.out.println(

"IOException encountered");

}

}

public static void main(String[] args) {

System.out.println("Hello, it's: ");

System.out.println(new Date());

FileTest oFile = new FileTest();

oFile.fileRead();

}

}

Run at:


Tue Feb 14 22:06:48 EET 2006

<съдържание на файла>

Структури и обединения
В С/С++ има три типа съставни типове данни : класове, структури и обединения. Java поддържа само един от тези типове данни – класовете. Java принуждава програмистите да използват класове, когато им е нужна функционалността на структурите или обединенията. Въпреки че изглежда като повече работа за програмиста, всъщност се оказва по-последователен подход, защото класовете могат да имитират поведението на структури и обединения с лекота. Още повече, поддръжката на структури и обединения би поставило под въпрос цялата концепция на Java като обектно-ориентиран език. Създателите на Java всъщност са искали да запазят езика лесен, затова са намерили смисъл в премахването на аспектите на езика, които се припокриват.

Превръщането на структури и обединения в Java класове е лесно. Разгледайте пример 1.4, който съдържа С структурата “полярни координати”.


Пример 1.4. C структура «полярни координати».

typedef struct polar {


  float angle;
  float size;
} POLAR;

Забележете, че тази структура ползва typedef за да създаде типа polar. Както видяхме по-рано typedef декларациите не са нужни в Java, защото всичко там е обект с уникален тип. Java не поддържа и концепцията на структурите. Пример 1.5 съдържа Java версията на типа “полярни координати”.


Пример 1.5. Java класа «полярни координати».

class polar {


  float angle;
  float size;
}

Като добавка към промяната на декларацията от typedef struct в class, трябва да се отбележи, че дефиницията на класа Java не завършва с точка и запетая. Това е малка, но доста често недоглеждана разлика между С++ и Java. Точката и запетаята не са нужни при дефинициите на класове в Java.



Конструктори

Java има конструктори, подобни на конструкторите в C++. Може да се дефинира подразбиращ се конструктор и, ако не се дефинира такъв, същият се създава автоматично. Ако не се дефинира друг конструктор, той не се образува автоматично, както в C++. Няма копиращ конструктор, тъй като всички аргументи се предават чрез референция.



Деструктори

В Java няма деструктори. Не съществува обхват на променливите, който да определя края на времето на живот на променливите, създадени от програмиста. Животът на динамичните променливи се определя от менажера на паметта ( garbage collector ). Работата с паметта се разглежда по-нататък. Съществува метод finalize, който е член на всеки клас. Той е подобен на деструктора на C++, но се извиква от garbage collector и е предназначен да освобождава ресурсите (като файлове, връзки (sockets), канали за връзка (ports), URLs и др.). Ако има нужда от действия при приключване в дадена точка, те се реализират в методи, които се извикват на определеното място на програмата, без да се разчита на finalize( ). Всички обектите в C++ ще бъдат освободени, но не всички обекти в Java се освобождават от менажера на паметта. Тъй като Java не поддържа деструктори, може да се създаде метод за имплицитно освобождаване на базовия клас и членовете на класа.



Предекларация на методи

Методите се предекларират в Java по начин, подобен на този, използван в C++ за функциите. Разликата е в липсата на подразбиращи се аргументи, които се използват понякога в C++ за предекларация.



Функции и методи

В С++ кодът е организиран като функции, които могат да са и глобални подпрограми, достъпни за дадена програма. С++ добавя и класове и така предостави тоест методи, които са функции, свързани с класове. Методите на класове в С++ са много подобни на тези в Java. Поради съвместимостта към С, нищо не може да ограничи програмистите на С++ да използват функции. Резултатът е смесица от използване на функции и методи, което прави програмите понякога неясни.

Java няма глобални (извън класовете) функции. Като по-чист обектно-ориентиран език от С++, Java принуждава програмистите да свързват всички под-програмни обръщения с класове. Няма ограничение, което се налага от това правило, като то само подобрява организацията на изходния код. Използването на функциите не е грешно, но те не се вписват добре в концепцията за обектно-ориентирано програмиране, която е залегнала в ядрото на Java.

Факт е, че във всеки С/С++ код има в една или друга степен използване на глобални функции и това е важна част при прехвърлянето на код от С/С++ на Java. Преобразуването налага по-скоро някои организационни промени. Смисълът на функциите е да съхрани код в отделни процедури, които след това да могат да се викат от главната програма или от други функции. Този подход лесно може да се имплементира в Java, без да се налага “обективизация” на кода. Решението е да се преместят всички С/С++ функции в организационни Java класове, които съдържат само методи. Например, съвкупността от функции в 1.6 съдържа няколко прототипа на функции за криптиране/декриптиране на низове.



Пример 1.6. Прототипи на функции за криптиране/декриптиране на C.

char EncryptChar(char c, int key);


char DecryptChar(char c, int key);
char* EncryptString(const char* s, int key);
char* DecryptString(const char* s, int key);

Тези функции са глобални С функции, които криптират или де криптират символни низове. Естествено, в С/С++ няма чиста концепция за низ. Най-доброто, което може да получите е масив от символи. Директна конверсия между функциите и методи в Java е показана в пример 1.7.



Пример 1.7. Методи за криптиране/декриптиране на низове в Java, включени в класа Crypt.

class Crypt {


  public static char encryptChar(char c, int key) {
    // код за криптиране на символ
  }

  public static char decryptChar(char c, int key) {


    // код за декриптиране на символ
  }

  public static String encryptString(String s, int key) {


    // код за криптиране на низ
  }

  public static String decryptString(String s, int key) {


    // код за декриптиране на низ
  }
}

В Java е нужно методите да бъдат пакетирани в клас, например Crypt. Като се декларират като public static, те се правят видими от цялото приложение на Java. Основния аспект на Java версията е, че имплементациите на функциите са дефинирани в класа на Java, защото не се поддържа организацията на вложени/изходни файлове. Цялата информация за класа се включва директно в неговата дефиниция. Стандартната конвенция за именуване в Java е методите да започват с малки букви. За да се използват методите, те трябва да се реферират заедно с класа си:

char c = Crypt.encryptChar('a', 7);

Единствената друга промяна на С функциите е използването на обекти String вместо указателите към символи, защото Java не поддържа указатели. По-нататък се разглеждат указателите и референциите по-подробно.



Java не поддържа подразбиращи се аргументи на функциите (default arguments).
В Java не се използва ключовата дума virtual за функциите, защото всички не статични методи се свързват динамично. Затова не е нужно да се специфицира от програмиста (изрично) този метод на свързване. Причината за съществуване на ключовата дума virtual в C++ е във възможността да се управлява при необходимост ефективността на програмата като при използването и се получава определено намаляване. В кода, където не се използва, управлението на ефективността се осигурява със статични методи. В Java може да се използва ключовата дума final, която указва на компилатора, че методът не може да се предекларира в наследника поради което същият трябва да се свърже статично и да се използва по подобен начин като inline методите на C++ и не-виртуални обръщения. Тези оптимизации са задължителни за компилатора.

Конвертиране на процедурно към ООП

Въпреки че класа Crypt предоставя работеща Java версия на С функциите, извършването на подобна конверсия не винаги е достатъчно. Класът Crypt показва как може да се запази процедурността в един клас на Java. Java е обектно-ориентиран език и концепцията му като такъв трябва да се спазва винаги, когато е възможно.

Анализирайки класа Crypt се вижда, че някои части могат да се променят, за да стане класа по-обектно-ориентиран. Пример 1.8 показва модифициран изходен код на “обективизиран” клас Crypt.

Пример 1.8. Преработен клас Crypt.

class Crypt {


  int key;

  Crypt(int k) {


    key = k;
  }

  void setKey(int k) {


    key = k;
  }

  int getKey() {


    return key;
  }

  char encryptChar(char c) {


    // код за криптиране на символ
  }

  char decryptChar(char c) {


    // код за декриптиране на символ
  }

  String encryptString(String s) {


    // код за криптиране на низ
  }

  String decryptString(String s) {


    // код за декриптиране на низ
  }
}

В тази версия на Crypt, ключът за криптиране е преместен от параметър на методите в член-променлива. Добавен е и конструктор, който инициализира тази член-променлива. Също така съществуват и методи за достъп (четене и подмяна) за ключа за криптиране. Променени са и public static декларациите на методите за криптиране/декриптиране, което изисква създаването на клас за използването им. Това има смисъл, защото класът вече има член-променлива (ключа) , която се използва от всички методи.

Преработения вариант на Crypt има много повече връзки с програмирането на Java. Естествено, няма да върви по-бързо, нито да криптира по-добре, но не е това целта. Целта е да се спазват обектно-ориентираните начини за разработка, които са фундаментална част от езика Java.

Предефиниране на оператори

Предефинирането на оператори, което се смята за характерна особеност на С++, не се поддържа от Java. Подобна функционалност може да се имплементира като методи в класовете на Java, синтактичното удобство на предефинирането на операторите липсва. В защита на Java трябва да се отбележи, че предефинирането понякога може да направи неясни програмите, които ги ползват. Разработчиците на Java са решили да не се поддържа, за да се запази езика колкото може по-прост.

Въпреки че дефинирането на оператори е сравнително използваема особеност на С++, то е много зависимо от типовете на класовете, които го ползват. Като пример, по-базовите С++ класове като такива, представящи низове, използват много често предефинирането на оператори, докато други може въобще да не ги ползват. Обемът на работата при преработване на С++ код в Java много зависи от това до каква степен кодът зависи от тази особеност на езика.

Единствения начин за превод на предефинирани С++ оператори на Java е да се създадат методи със същата функционалност. Методите на Java ще имат различни имена от предефинираните оператори. Това означава, че ще е нужно внимателно да се прегледа кода, да се определи на кои точно места се използват тези оператори и да се преобразуват в извикване на методи.

Пример 1.9 съдържа клас “комплексно число” с предефинирани оператори.

Пример 1.9. C++ клас за представяне на комплексни числа с предефинирани оператори.

class Complex {
  float real;
  float imag;

  Complex(float r, float i);

  Complex operator+(const Complex& c) const {
    return Complex(real + c.real, imag + c.imag);
  }

  Complex operator-(const Complex& c) const {


    return Complex(real - c.real, imag - c.imag);
  }
};

С++ класа съдържа предефинирани оператори за събиране и изваждане на комплексни числа. Ето пример как би се ползвал този клас:

Complex c1(3.0, 4.0);
Complex c2(5.0, 2.5);
Complex c3 = c2 - c1;

Изваждането на два обекта от тип Complex е синтактично еднакво с изваждането на две променливи от прости типове. Тази възможност на езика С++ добавя сравнително голямо усложняване, която разработчиците на Java са искали да избегнат. Въпреки че не може да се предостави същия синтактичен подход в Java, могат да бъдат имплементирани методи с подобна функционалност. Пример 1.10 съдържа Java версията на класа Complex, като предефинираните оператори са заменени с методи.



пример 1.10. Java класа Complex.

class Complex {


  float real;
  float imag;

  Complex(float r, float i) {


    real = r;
    imag = i;
  }

  Complex add(Complex c) {


    return (new Complex(real + c.real, imag + c.imag));
  }

  Complex subtract(Complex c) {


    return (new Complex(real - c.real, imag - c.imag));
  }
}

Най-очевидната промяна в Java версията е преименуването на методите за предефиниране на оператори на add и substract. Java Complex класа би се ползвал така:

Complex c1 = new Complex(3.0, 4.0);
Complex c2 = new Complex(5.0, 2.5);
Complex c3 = c2.subtract(c1);

Вижда се, че операцията изваждане не е толкова интуитивна, когато се ползва подхода на Java. Въпреки това тя е работоспособна. Важна особеност е, че обектите от клас Complex се създават с оператора new. Това е резултат от разлика в управлението на паметта между С++ и Java, която се разглежда по-късно, когато се описват указателите.



Автоматични промени на типове

Автоматичните подмени на типове представлява подмяната на типове (type casting), което се случва понякога в С/С++. Като пример, в С++ е позволено да се присвои реално число на променлива, декларирана като цяло число, което би довело до загуба на информация. Java не поддържа подобна автоматична промяна на типовете. В Java, ако би последвала загуба на информация, се изисква специално указание за смяна на типа към новия тип.

Следното е пример за автоматична подмяна на типа в С++:

float f = 3.1412;


int i = f;

Някои компилатори на С++ биха генерирали предупреждение в този случай, но това не се счита за грешка. В Java, от друга страна, се генерира грешка при компилация. Тя лесно се поправя с изрично указание за подмяна на типа, като това;

float f = 3.1412;
int i = (int)f;

Аргументи на командния ред

Аргументите на командния ред, подавани от системата в Java програма се различават по няколко причини от тези в С++ програмите. Първо, броят на аргументите в двата езика е различен. В С и С++ се подава два аргумента: argc и argv. Първият указва броя на аргументите, записани във втория. argv е указател към масив от указатели към символи( или масив от символни низове), които указват стойностите на аргументите. В Java системата подава една стойност към програмата: Args е масив от String обекти, които съдържат аргументите на командния ред.

В С и С++ аргументите на командния ред съдържат и името, което е използвано, за да се извика програмата. То се появява като първи аргумент и се използва рядко. В Java името на програмата е известно, тъй като то съвпада с името на класа и това прави излишно подаването на тази информация като аргумент от командната линия. Затова, системата за реално време (run time system) на Java подава само аргументите, които следват след името на програмата при извикването й.

Пример 1.11 съдържа примерна програма, която отпечатва аргументите на командния ред.

Пример 1.11. C++ програма, която изпечатва аргументите на командния ред.

#include


#include

void main(int argc, char* argv[])


{
  for(int i = 1; i < argc; i++)
    cout << argv[i] << "\n";
}

Тази програма преминава през всеки аргумент, използвайки for цикъл като отпечатва всеки един в стандартния поток за изход. Ще отбележим, че цикълът стартира от 1, за да се избегне отпечатването на името на програмата. Пример 1.12 съдържа Java еквивалента – класа ArgPrint.



Пример 1.12. Java класа ArgPrint.

public class ArgPrint {


  public static void main(String[] args) {
    for (int i = 0; i < args.length; i++)
      System.out.println(args[i]);
  }
}

Класът ArgPrint съдържа един метод – main. Това е Java еквивалента на функцията main в С/С++. Той бива извикан, когато програмата се изпълнява. Методът main приема за параметър масив от обекти от тип String. Използването му отбелязва една друга интересна разлика между Java и С++ - масивите. Всички Java масиви имат член-променлива, наречена length, която може да се използва за определяне размера на масива. В този случай length се използва за преминаване през масива с аргументи, за да бъдат отпечатани.



    1. Каталог: Course%20II -> Term%20Two -> OOP%20II
      OOP%20II -> Структурни диаграми по uml 0: Класова диаграма (Class diagram) Символи: (private) частен елемент: атрибут или метод
      Course%20II -> Приоритети на Европейския съюз в областта на компютърните и комуникационните технологии
      Term%20Two -> Конспект за изпита по дисциплината "Микропроцесорна техника"
      Term%20Two -> Б wm initdialog
      Course%20II -> Конспект по Основи на компютърните комуникации
      Term%20Two -> Задача: Езикът L е задaден чрез регулярен израз: r = ( a+b ba )( b + ab a ) ( b+ ab a) + a + b ba


      Сподели с приятели:
1   2   3   4   5




©obuch.info 2024
отнасят до администрацията

    Начална страница