Към съдържанието

Тема 4 — Програмни елементи

Тази глава систематизира елементите, с които се организира Java програмата на ниво по-високо от отделния клас: пакети за именни пространства, конструктори, модификатори за достъп, вградени класове и поведението на Java при типове, указатели и наследяване спрямо C++. Четена паралелно с предишната глава, тя дава пълен поглед върху „шва” между процедурното и обектно-ориентираното мислене.

1. Пакети (Packages)

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

1.1 Ключови думи package и import

package bg.tu_varna; // на първия ред; задължително
import java.util.*; // всички класове от java.util
import java.io.*;
import static java.lang.Math.*; // директен достъп до sin(), PI и др. (Java 5.0+)

Статичен import позволява ползване на статичните членове без префикс:

double circumference = 2 * PI * radius; // вместо Math.PI
double length = sin(theta) * side; // вместо Math.sin

1.2 Именоване и структура

  • Имена са йерархични, разделени с точка; съответстват на директории в ОС;
  • По конвенция — малки букви; обърнат домейн: bg.tu_varna.cs;
  • java.lang се импортира автоматично.

1.3 Компилационна единица

Изходният .java файл е компилационна единица. Може да съдържа множество класове, но само един може да е public — и само той е достъпен извън пакета. Пълна спецификация за достъп: bg.tu_varna.FileTest или с import.

1.4 Директива classpath

Компилаторът открива файловете на пакета чрез директивата classpath, която задава корена на дървото от пакети:

Terminal window
# компилиране: -classpath задава корена на пакетите
javac -classpath c:\jdev\src c:\jdev\src\FileTest.java
# стартиране: -cp е съкратена форма
java -cp c:\jdev\src FileTest

Когато пакетът е bg.tu_varna, компилаторът очаква файловете в <classpath>/bg/tu_varna/. Имената на пакетите директно определят директорийната структура.

1.5 Стандартни библиотечни пакети

ПакетСъдържание
java.langБазови класове — автоматично импортиран
java.utilКонтейнери, дати, стекове
java.ioФайлове и потоци
java.awtGUI компоненти
java.netМрежово програмиране
java.sqlJDBC работа с бази данни
java.rmiОтдалечени методи (RMI)

2. Структури и обединения

Java поддържа само класовеstruct и union на C/C++ не съществуват. Това принуждава програмистите към ООП. Преобразуване е тривиално:

// C:
typedef struct polar { float angle; float size; } POLAR;
// Java:
public class Polar {
public float angle;
public float size;
// Без точка и запетая след }!
}

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

Конструкторите в Java са подобни на C++. Видове:

  • Подразбиращ се — генерира се автоматично само ако не е дефиниран нито един явен;
  • Явен — с един или повече аргументи;
  • Копиращ конструктор — не е необходим, тъй като обектите се предават чрез референция.

Обектите се алокират в heap с new. Паметта се освобождава от GC (Garbage Collector) при нулев брой референции — без delete.

4. Модификатори за достъп

Разлика от C++: модификаторът се поставя пред всяка декларация поотделно, не като блок:

public class A {
public int x; // достъпен навсякъде
private float v; // само вътре в класа
int packet; // пакетен достъп (без ключова дума) — само в пакета
protected int p; // в пакета + в наследниците
}

Подразбиращ се модификатор е пакетен (package-private) — аналог на friend в C++.

5. Вградени класове (Inner Classes)

5.1 Вграден в клас

class OuterClass {
int outer_x = 20;
class BuiltIn {
void print() { System.out.println("x: " + outer_x); } // директен достъп
}
void test() {
BuiltIn inner = new BuiltIn();
inner.print();
}
}

5.2 Вграден в метод (локален клас)

void test() {
class LocalBuiltIn {
void print() { System.out.println("x: " + outer_x); }
}
new LocalBuiltIn().print();
}

5.3 Анонимни вградени класове

Дефинират клас без явно зададено име — типично за обработчици на събития или callback интерфейси:

addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
msg = "Mouse Pressed.";
repaint();
}
});

6. Деструктори и управление на паметта

Java няма деструктори. Паметта се управлява от Garbage Collector:

  • finalize() е метод в Object, извикван от GC преди освобождаване — не е гарантирано кога или дали изобщо ще бъде извикан;
  • Предназначен е за нe-памет ресурси (файлове, sockets, URL връзки);
  • За гарантирано освобождаване на ресурси се ползва try-with-resources (Java 7+).

7. Методи и функции

7.1 Без глобални функции

Всички подпрограми трябва да са методи в клас. Глобалните C функции се преобразуват в статични методи:

// C++: char EncryptChar(char c, int key);
// Java:
public class Crypt {
public static char encryptChar(char c, int key) { ... }
public static String encryptString(String s, int key) { ... }
}
// Достъп: char c = Crypt.encryptChar('a', 7);

7.2 Без virtual — всичко е виртуално по подразбиране

В Java всички нестатични методи се свързват динамично по подразбиране. virtual не е нужна. Изключение: final метод — свързва се статично.

7.3 Без подразбиращи се аргументи

// C++ — допустимо:
CStudent(const string& name="", int points=0) { ... }
// Java — не съществува; решение: претоварване (overloading)
public Student() { ... }
public Student(String name) { ... }
public Student(String name, int points) { ... }

8. Без предефиниране на оператори

Java не поддържа предефиниране на оператори. Функционалността се имплементира чрез именовани методи:

// C++: Complex c3 = c2 - c1;
// Java:
Complex c3 = c2.subtract(c1); // конвенция: add, subtract, multiply

9. Без автоматични стесняващи преобразувания

Java изисква явно преобразуване при потенциална загуба на информация:

// C++ (с предупреждение): float f = 3.14; int i = f;
// Java — компилационна грешка без явно cast:
float fVar = 3.1412f;
int i = (int) fVar; // задължително!

10. Указатели и референции

10.1 Java няма указатели

  • Обектите се предават с референции, не с указатели;
  • Достъпът до масиви — само с индекси;
  • Нулевата стойност е null (не нулев адрес);
  • Аритметика с референции не се допуска.
  • Изключение: native методи — декларирани с ключовата дума native, те се имплементират на C/C++ и могат да работят с истински указатели извън управляваната памет на JVM.

10.2 Сравнение

C++ референцииJava референции
Инициализация при обявяванеЗадължителнаНезадължителна
Промяна след създаванеНевъзможнаДопустима
Нулева стойностНе съществуваnull
Достъп до произволна паметДопустимНедопустим

10.3 Преобразуване от C++ указатели

// C++:
class CMemTest {
MemBlock* pMem;
CMemTest() { pMem = new MemBlock(); }
~CMemTest() { delete pMem; pMem = NULL; }
};
// Java:
public class CMemTest {
private MemBlock pMem;
public CMemTest() { pMem = new MemBlock(); }
// GC освобождава pMem автоматично
}

11. Множествено наследяване

Java не поддържа директно множествено наследяване. Два заместника:

Интерфейси:

public class InputDevice implements Clickable, Draggable {
public void click() { ... }
public void drag() { ... }
}

Композиция:

public class ClassDerivative {
ClassBaseA objA = new ClassBaseA();
ClassBaseB objB = new ClassBaseB();
public void methodA() { objA.methodA(); }
public void doSomethingFromB() { objB.doSomething(); }
}

12. Абстрактни класове

Абстрактният клас в Java дефинира общ интерфейс за йерархия от класове, без да инстанциира обекти директно. Чрез ключовата дума abstract се обявяват методи, чиято имплементация е задължение на конкретните наследници.

Следващият пример изгражда йерархия от геометрични фигури: CPoint и CColor са помощни класове; CShape е абстрактната база; CCircle е конкретен наследник.

12.1 Помощни класове CPoint и CColor

public class CPoint {
private double myX;
private double myY;
public CPoint(double x, double y) {
myX = x;
myY = y;
}
public CPoint() {
myX = 0.;
myY = 0.;
}
public double getX() {
return myX;
}
public double getY() {
return myY;
}
public void setPoint(double x, double y) {
myX = x;
myY = y;
}
}
public class CColor {
private int red;
private int green;
private int blue;
public CColor() {
red = 255; green = 0; blue = 125;
}
public CColor(int r, int g, int b) {
red = r; green = g; blue = b;
}
public static long RGB2Long(int r, int g, int b) {
return (r | (g << 4) | (b << 8));
}
public int getR() { return red; }
public int getG() { return green; }
public int getB() { return blue; }
public void setRGB(int r, int g, int b) {
red = r; green = g; blue = b;
}
}

12.2 Абстрактният базов клас CShape

CShape носи общите атрибути (цвят и позиция) и декларира абстрактните методи area() и perimeter(), чиято имплементация се делегира на конкретните наследници:

public abstract class CShape {
private CColor color;
private CPoint origin;
protected CShape(final CColor col, final CPoint org) {
origin = new CPoint(org.getX(), org.getY());
color = new CColor(col.getR(), col.getG(), col.getB());
}
protected CShape(final CPoint org) {
origin = new CPoint(org.getX(), org.getY());
color = new CColor(0, 0, 0);
}
protected CShape() {
origin = new CPoint(0.0, 0.0);
color = new CColor(0, 0, 0);
}
public abstract double area();
public abstract double perimeter();
public CColor getColor() {
return color;
}
public void setColor(CColor obj) {
color = obj;
}
}

12.3 Конкретен наследник CCircle

CCircle разширява CShape и предоставя реални имплементации на двата абстрактни метода, използвайки константата Math.PI от java.lang.Math:

public class CCircle extends CShape {
private double radius;
public CCircle() {
super();
radius = 0.0;
}
public CCircle(final CColor col, final CPoint org, double rad) {
super(col, org);
radius = rad;
}
public double area() {
return Math.PI * radius * radius;
}
public double perimeter() {
return 2 * Math.PI * radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double r) {
radius = r;
}
}

Резюме

  • Пакетите организират класовете в именни пространства и управляват достъпа — package/import са аналог на namespace/#include.
  • В Java няма глобални функции, деструктори, подразбиращи се аргументи или предефиниране на оператори.
  • Всички нестатични методи са динамично свързани по подразбиране — virtual не е нужна.
  • Референциите са безопасна алтернатива на указателите: без аритметика, без достъп до произволни адреси.
  • Множественото наследяване се заобикаля с интерфейси (поведение) или композиция (споделяне на имплементация).