Тема 4 — Програмни елементи
Тази глава систематизира елементите, с които се организира Java програмата на ниво по-високо от отделния клас: пакети за именни пространства, конструктори, модификатори за достъп, вградени класове и поведението на Java при типове, указатели и наследяване спрямо C++. Четена паралелно с предишната глава, тя дава пълен поглед върху „шва” между процедурното и обектно-ориентираното мислене.
1. Пакети (Packages)
Пакет — логически свързана съвкупност от класове и интерфейси, осигуряваща защита от колизии и управление на именни пространства. Еквивалент на namespace в C++.
1.1 Ключови думи package и import
package bg.tu_varna; // на първия ред; задължителноimport java.util.*; // всички класове от java.utilimport java.io.*;import static java.lang.Math.*; // директен достъп до sin(), PI и др. (Java 5.0+)Статичен import позволява ползване на статичните членове без префикс:
double circumference = 2 * PI * radius; // вместо Math.PIdouble length = sin(theta) * side; // вместо Math.sin1.2 Именоване и структура
- Имена са йерархични, разделени с точка; съответстват на директории в ОС;
- По конвенция — малки букви; обърнат домейн:
bg.tu_varna.cs; java.langсе импортира автоматично.
1.3 Компилационна единица
Изходният .java файл е компилационна единица. Може да съдържа множество класове, но само един може да е public — и само той е достъпен извън пакета. Пълна спецификация за достъп: bg.tu_varna.FileTest или с import.
1.4 Директива classpath
Компилаторът открива файловете на пакета чрез директивата classpath, която задава корена на дървото от пакети:
# компилиране: -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.awt | GUI компоненти |
java.net | Мрежово програмиране |
java.sql | JDBC работа с бази данни |
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, multiply9. Без автоматични стесняващи преобразувания
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не е нужна. - Референциите са безопасна алтернатива на указателите: без аритметика, без достъп до произволни адреси.
- Множественото наследяване се заобикаля с интерфейси (поведение) или композиция (споделяне на имплементация).