Тема 1 — Език за програмиране Java
Java е езикът, наследил синтаксиса на C++ и го е пречистил от редица исторически проблеми — директен достъп до памет, неинициализирани указатели, множествено наследяване с диамантения проблем. Резултатът е платформено независим, строго типизиран обектно-ориентиран език, чийто компилатор генерира не машинен код, а байткод (bytecode) — набор от инструкции за виртуалната машина.
1. Характеристики на езика
Java е проектиран с ясна цел: да позволи писане на сигурен, преносим и лесен за поддръжка код. Основните характеристики, формулирани от Sun Microsystems, са:
- Прост — намалена сложност спрямо C++; без указатели, без деструктори, без предефиниране на оператори;
- Обектно-ориентиран — всичко е обект; единствените изключения са осемте прости типа;
- Платформено независим — компилира се до байткод, изпълняван от JVM (Java Virtual Machine);
- Разпределен — вградена поддръжка на мрежови операции;
- Защитен — без директен достъп до памет; байткодът се верифицира преди изпълнение;
- Портируем — едни и същи
.classфайлове се изпълняват на всяка платформа с JVM; - Много-нишков — вградена поддръжка на нишки (
Thread,Runnable); - Динамичен — класовете се зареждат при нужда по времена на изпълнение.
2. Виртуалната машина (JVM)
JVM е абстрактна изчислителна машина, изпълняваща байткода в .class файловете. Тя осигурява принципа Write Once, Run Anywhere — едно и също компилирано приложение работи на всяка операционна система, за която съществува JVM имплементация.
Архитектура на JVM по спецификацията Java SE 7. Класзареждащата подсистема (Class Loader) зарежда .class файловете в Runtime Data Areas, откъдето изпълнителният механизъм (Execution Engine) ги изпълнява.
Основни подсистеми:
- Class Loader — зарежда, свързва и инициализира класовете динамично; йерархия: Bootstrap → Extension → Application;
- Runtime Data Areas — Method Area (класова информация), Heap (обекти), Stack (нишкови рамки), PC Register;
- Execution Engine — включва интерпретатор и JIT (Just-In-Time) компилатор, оптимизиращ горещия код до машинни инструкции при изпълнение;
- Garbage Collector — автоматично освобождава неизползвана памет в Heap.
3. Главна програма
Java е напълно обектно-ориентиран език — глобалните функции от C/C++ не съществуват. Терминологичното съответствие:
| C++ понятие | Java еквивалент |
|---|---|
| Член-променлива | Поле (field) |
| Член-функция | Метод (method) |
| Глобална функция | Не съществува |
| Базов клас | Object (системен) |
Всяко приложение се записва в .java файл, чието име съвпада с публичния клас:
public class HelloWorldApp { public static void main(String[] args) { System.out.println("Hello World!"); }}Цикъл компилация → изпълнение:
javac HelloWorldApp.java → HelloWorldApp.class → java HelloWorldAppКласзареждащата машина търси .class файлове в директориите, зададени чрез CLASSPATH. Пакетният път foo.bar.baz се превежда до <CLASSPATH>/foo/bar/baz.
4. Статични методи и атрибути
Нормалните полета и методи на класа принадлежат на конкретния обект — всяка инстанция има свое копие. Декларираните със static членове принадлежат на самия клас: съществуват преди да е създаден какъвто и да е обект, и се споделят между всички инстанции.
4.1 Статични атрибути
Статичното поле се инициализира веднъж при зареждане на класа. Всички обекти четат и пишат в едно и също място в паметта.
public class Counter { public static int count = 0; // едно копие за целия клас public int id; // всяка инстанция има свое id
public Counter() { count++; // увеличава общия брояч id = count; // задава уникален номер на обекта }}Counter a = new Counter(); // count = 1, a.id = 1Counter b = new Counter(); // count = 2, b.id = 2Counter c = new Counter(); // count = 3, c.id = 3
System.out.println(Counter.count); // 3 — достъп през класаSystem.out.println(a.count); // 3 — компилира се, но е подвеждащоДостъпът през ClassName.field е препоръчителен — ясно показва, че полето е класово, не инстанционно.
4.2 Статични методи
Статичният метод се извиква без обект. Именно затова може да достъпва само статични членове директно — при извикването няма this, няма конкретна инстанция.
public class MathUtils { public static final double PI = 3.14159265; // статична константа private double scale; // инстанционно поле
public static double circleArea(double r) { return PI * r * r; // OK: PI е статично }
public static double scaled(double x) { return x * scale; // ГРЕШКА: scale е нестатично — няма инстанция }}double area = MathUtils.circleArea(5.0); // извикване без обект4.3 Какво не може да прави статичен метод
| Опит | Резултат |
|---|---|
| Достъп до нестатично поле директно | Компилационна грешка |
Използване на this | Компилационна грешка |
| Извикване на нестатичен метод директно | Компилационна грешка |
| Достъп до нестатичен член през обект | Допустимо — обектът е аргумент |
public class Example { private int value = 10;
public static void bad() { System.out.println(value); // ГРЕШКА: value не е static System.out.println(this.value); // ГРЕШКА: this не съществува тук }
public static void good(Example obj) { System.out.println(obj.value); // OK: достъп чрез явен обект }}4.4 Разлика от C++
В C++ статичното поле се декларира в класа, но се дефинира отделно в .cpp файл:
// C++:class Test { static int count; }; // само декларацияint Test::count = 0; // дефиниция в .cppВ Java дефиницията и инициализацията са на едно място — в тялото на класа. Достъпът използва . вместо ::.
// Java:public class Test { public static int count = 0; // декларация и инициализация заедно}// Test::count не съществува — Test.count5. Аргументи на командния ред
Разлики от C/C++:
| C/C++ | Java | |
|---|---|---|
| Брой аргументи | argc (int) + argv[] (char**) | Един масив от String |
| Включва ли се името на програмата | Да | Не |
public class ArgPrint { public static void main(String[] args) { for (int i = 0; i < args.length; i++) System.out.println(args[i]); }}args.length е статично поле на масива, налично без допълнителна декларация.
6. Стандартен вход/изход
Извеждане чрез System.out (статично поле от тип PrintStream):
System.out.println("text"); // с нов редSystem.out.print("text"); // без нов редSystem.out.printf("%d, %.2f\n", i, d); // форматиран изход (Java 5.0+)При конкатенация с + не-String операнди се преобразуват автоматично чрез toString().
Четене — клас Scanner (Java 5.0+):
Scanner kb = new Scanner(System.in);String input = kb.next();double dbl = kb.nextDouble();int integer = kb.nextInt();Форматиран вход — StreamTokenizer разграничава числа, думи и коментари; String.split("\\s") разделя по whitespace.
7. Прости типове данни
Java дефинира осем прости типа, съхранявани в стека и предавани по стойност:
| Тип | Размер | Особеност |
|---|---|---|
boolean | 1 bit | Само true/false — не е int! |
byte | 8 bit | Знаков |
short | 16 bit | Знаков |
char | 16 bit | Unicode (не ASCII) |
int | 32 bit | По подразбиране за целочислени литерали |
long | 64 bit | Суфикс L: 13L |
float | 32 bit | Суфикс f: 3.14f |
double | 64 bit | По подразбиране за реални литерали |
Числови константи (Java 7+):
int octal = 01230; // осмичнаint hex = 0xFFFF; // шестнадесетичнаlong lng = 13L;byte one = (byte)0b00000001; // двоичнаfloat pi = 3.14_159_265f; // _ за четимостЗадължително е явно преобразуване при загуба на точност: byte b = (byte)i.
7.1 Заграждащи класове (Wrapper classes)
Boolean, Byte, Short, Character, Integer, Long, Float, Double обгръщат простите типове в референтни обекти — задължително за колекции и generics:
Integer classInt = new Integer(20);int simpleInt = classInt.intValue();
// Java 5.0+ — автоматично boxing/unboxing:Integer boxed = 5; // boxing: int → Integerint i = new Integer(5); // unboxing: Integer → intList<Integer> nums = new ArrayList<>();nums.add(33); // boxingint n = nums.get(0); // unboxing8. Референтни типове
Типовете, чиито обекти се алокират в heap и се предават чрез референция:
Array— масиви;class— класове;interface— интерфейси.
Полетата на клас, ако не са инициализирани явно, получават автоматично нулеви стойности: 0, false, ' ' или null за референции.
9. Предаване на параметри
В Java всичко се предава по стойност. За референтни типове предаваната стойност е самата референция — поради което:
- полетата на предадения обект могат да се променят от метода;
- самата референция не може да се замени в извикания метод.
public class ArrayPass { public void f(int[] byValue) { byValue[0] = 10; // промяна се вижда при викащия byValue = null; // само локален ефект — нищо не се променя навън } public void g() { int[] arr = new int[]{5, 0, 0}; f(arr); // arr: {10, 0, 0} — byValue[0] се е променил // arr != null — присвояването byValue=null нямаше ефект }}За дълбоко копиране се ползват System.arraycopy() или clone().
Резюме
- Java компилира до платформено независим байткод, изпълняван от JVM; принципът Write Once, Run Anywhere се осигурява от Class Loader и Execution Engine (с JIT оптимизация).
- Всичко е обект —
mainе статичен метод на клас, глобални функции не съществуват. - Осемте прости типа се съхраняват в стека и се предават по стойност; всички останали са референтни и живеят в heap.
staticдекларира класови членове, достъпни без инстанция.- Предаването на параметри е по стойност — за референтни типове съдържанието може да се промени, но не и самата референция.
- Java 5.0+ въвежда autoboxing/unboxing и
printf, а Java 7+ — двоични литерали и разделители_в числата.