Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Языки программирования. Практический сравнитель...doc
Скачиваний:
31
Добавлен:
09.09.2019
Размер:
2.68 Mб
Скачать

18.2. Язык Java

На первый взгляд синтаксис и семантика языка Java аналогичны принятым в языке C++. Однако в то время как C++ сохраняет почти полную совмести­мость с языком С, Java отказывается от совместимости ради устранения трудностей, связанных с проблематичными конструкциями языка С. Несмот­ря на внешнее сходство, языки Java и C++ весьма различны, и программу на C++ не так легко перенести на Java.

В основном языки похожи в следующих областях:

• Элементарные типы данных, выражения и управляющие операторы.

• Функции и параметры.

• Объявления класса, члены класса и достижимость.

• Наследование и динамический полиморфизм.

• Исключения.

В следующих разделах обсуждается пять областей, где проект Java сущест­венно отличается от C++: семантика ссылки, полиморфные структуры дан­ных, инкапсуляция, параллелизм и библиотеки. В упражнениях мы просим вас изучить другие различия между языками.

18.3. Семантика ссылки

Возможно, наихудшее свойство языка С (и C++) — неограниченное и чрез­мерное использование указателей. Причем операции с указателями не только трудны для понимания, они чрезвычайно предрасположены к ошибкам, как описано в гл. 8. Ситуация в языке Ada намного лучше, потому что строгий контроль соответствия типов и уровни доступа гарантируют, что использова­ние указателей не приведет к разрушению системы типов, однако структуры данных по-прежнему должны формироваться с помощью указателей.

Язык Java (подобно Eifiel и Smalltalk) использует семантику ссылки вместо семантики значения.

При объявлении переменной непримитивного типа память не выделя­ется; вместо этого выделяется неявный указатель. Чтобы реально выделить память для переменной, необходим второй шаг. Покажем теперь, как семан­тика ссылки работает в языке Java.

Массивы

Если вы объявляете массив в языке С, то выделяется память, которую вы за­просили (см. рис. 18.2а):

C


inta_c[10];

в то время как в языке Java вы получаете только указатель, который может ис­пользоваться для обращений к массиву (см. рис. 18.26):

Java

int[ ] a Java;

Для размещения массива требуется дополнительная команда (см. рис. 18.2 в):

a Java = new int[10];

Java

хотя допустимо объединять объявления с запросом памяти:

Java

int[ ] a Java = new int[10];

Если вы сравните рис. 18.2 с рис. 8.4, вы увидите, что массивы в Java скорее по­добны структурам, определенным в языке C++ как int *a, а не как int a []. Раз­личие заключается в том, что указатель является неявным, поэтому вы не дол­жны заботиться об операциях с указателями или о выделении памяти. К тому же, в случае массива, переменная будет дескриптором массива (см. рис. 5.4), что дает возможность проверять границы при обращениях к массиву.

Отметим, что синтаксис Java проще читается, чем синтаксис C++: a_java is of type int [], что означает «целочисленный массив»; в языке C++ тип компо­нентов int и указание «масивности» [10] располагаются по разные стороны от имени переменной.

При использовании семантики ссылки разыменование указателя является неявным, поэтому после того, как массив создан, вы обращаетесь к нему как обычно:

for (i = 1; i< 10;i++)

Java

a_java[i] = i;

Конечно, косвенный доступ может быть значительно менее эффективен, чем прямой, если его не оптимизирует компилятор.

Отметим, что выделение памяти для объекта и присваивание его перемен­ной могут быть выполнены в любом операторе, в результате чего появляется следующая возможность:

Java

int[ ] a_ Java = new int[10];

a_Java = new int[20];

Переменная a_ Java, которая указывала на массив из десяти элементов, теперь указывает на массив из двадцати элементов, и исходный массив становится «мусором» (см. рис. 8.7). Согласно модели Java сборщик мусора должен нахо­диться внутри JVM.

Динамические структуры данных

Как можно создавать списки и деревья без указателей?! Объявления для свя­занных списков в языках C++ и Ada, описанные в разделе 8.2, казалось бы, показывали, что нам нужен указательный тип для описания типа следующего поля next:

typedef struct node *Ptr;

C

typedef struct node {

int data;

Ptr next;

} node;

Но в Java каждый объект непримитивного типа автоматически является ука­зателем

class Node {

Java

int data;

Node next;

}

Поле next — это просто указатель на узел, а не сам узел, поэтому в объявлении нет никакой цикличности. Объявление списка — это просто:

Java


Node head;

Этот оператор создает указатель переменной с нулевым значением (см. рис. 18.3,а). Подразумевая, что имеется соответствующий конструктор (см. раздел 15.3) для Node, следующий оператор создает узел в головной части списка (см. рис. 18.3,6):

Java


head = new Node(10, head);

Проверка равенства и присваивание

Поведение операторов присваивания и проверки равенства в языках с семан­тикой ссылки может оказаться неожиданным для программистов, которые работали на языке с семантикой значения. Рассмотрим объявления Java:

Java

String s1 = new String("Hello");

String s2 = new String("Hello");

В результате получается структура данных, показанная на рис. 18.4. Теперь предположим, что мы сравниваем строковые переменные:

Java

if (s1 == s2) System.out.println("Equai");

else System.out.println("Not equal");

программа напечатает Not equal (He равно)! Причина этого хорошо видна из рис. 18.4: переменные являются указателями с разными значениями, и тот факт, что они указывают на равные массивы, не имеет значения. Точно так же, если мы присваиваем одну строку другой s1 = s2, будут присвоены указа­тели, но никакого копирования значений при этом не будет. В этом случае, конечно, s1 == s2 будет истинно. Java делает различие между мелкими копиро­ванием и проверкой равенства и глубокими копированием и сравнением. По­следние объявлены в классе Object — общем классе-прародителе — и названы clone и eguals. Предопределенный класс String, например, переопределяет эти операции, поэтому s1.equals(s2) будет истинно. Вы можете также пере­определить эти операции, чтобы создать глубокие операции для своих клас­сов.

Подведем итог использования семантики ссылки в Java:

• Можно безопасно управлять гибкими структурами данных.

• Программирование упрощается, потому что не нужны явные указатели.

• Есть определенные издержки, связанные с косвенностью доступа к структурам данных.