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

Тема 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 имплементация.

Архитектура на Java Virtual Machine (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 = 1
Counter b = new Counter(); // count = 2, b.id = 2
Counter 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.count

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

Разлики от 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 дефинира осем прости типа, съхранявани в стека и предавани по стойност:

ТипРазмерОсобеност
boolean1 bitСамо true/false — не е int!
byte8 bitЗнаков
short16 bitЗнаков
char16 bitUnicode (не ASCII)
int32 bitПо подразбиране за целочислени литерали
long64 bitСуфикс L: 13L
float32 bitСуфикс f: 3.14f
double64 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 → Integer
int i = new Integer(5); // unboxing: Integer → int
List<Integer> nums = new ArrayList<>();
nums.add(33); // boxing
int n = nums.get(0); // unboxing

8. Референтни типове

Типовете, чиито обекти се алокират в 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+ — двоични литерали и разделители _ в числата.