Общая структура программы
Программа на любом из двух рассматриваемых языков представляет собой набор пользовательских типов данных — в основном, классов и интерфейсов, с их методами. При запуске программы выполняется определенный метод некоторого типа. В ходе работы программы создаются объекты различных типов и выполняются их методы (операции над ними). Объектами особого типа представляются различные потоки выполнения, которые могут быть запущены параллельно.
Во избежание конфликтов по именам и для лучшей структуризации программ пользовательские типы размещаются в специальных пространствах имен, которые в Java называются пакетами (packages), а в C# — пространствами имен (namespaces). Имена пакетов и пространств имен могут состоять из нескольких идентификаторов, разделенных точками. Из любого места можно сослаться на некоторый тип, используя его длинное имя, состоящее из имени содержащего его пространства имен или пакета, точки и имени самого типа.
В обоих случаях программный код компилируется в бинарный код, исполняемый виртуальной машиной. Правила размещения исходного кода по файлам несколько отличаются.
Код пользовательских типов Java размещается в файлах с расширением .Java. При этом каждый файл относится к тому пакету, чье имя указывается в самом начале файла с помощью декларации package mypackage; При отсутствии этой декларации код такого файла попадает в пакет с пустым именем. |
Код пользовательских типов C# размещается в файлах с расширением .cs. Декларация пространства имен начинается с конструкции namespace mynamespace { и заканчивается закрывающей фигурной скобкой. Все типы, описанные в этих фигурных скобках, попадают в это пространство имен. Типы, описанные вне декларации пространства имен, попадают в пространство имен с пустым именем. Пространства имен могут быть вложены в другие пространства имен. При этом следующие декларации дают эквивалентные результаты. namespace A.B { … } namespace A { namespace B { … } } | ||
В одном файле может быть описан только один общедоступный (public) пользовательский тип верхнего уровня (т.е. не вложенный в описание другого типа), причем имя этого типа должно совпадать с именем файла без расширения. В том же файле может быть декларировано сколько угодно необщедоступных типов. | В одном файле можно декларировать много типов, относящихся к разным пространствам имен, элементы одних и тех же пространств имен могут описываться в разных файлах. | ||
Пользовательский тип описывается полностью в одном файле. | Пользовательский тип описывается целиком в одном файле, за исключением частичных типов (введены в C# 2.0), помеченных модификатором partial — их элементы можно описывать в разных файлах, и эти описания объединяются, если не противоречат друг другу. | ||
Чтобы ссылаться на типы, декларированные в других пакетах, по их коротким именам, можно воспользоваться директивами импорта. Если в начале файла после декларации пакета присутствует директива import Java.util.ArrayList; то всюду в рамках этого файла можно ссылаться на тип ArrayList по его короткому имени. Если же присутствует директива import Java.util.*; то в данном файле можно ссылаться на любой тип пакета Java.util по его короткому имени. Директива import static Java.lang.Math.cos; (введена в Java 5) позволяет в рамках файла вызывать статический метод cos() класса java.lang.Math просто по его имени, без указания имени объемлющего типа. Во всех файлах по умолчанию присутствует директива import java.lang.*; Таким образом, на типы из пакета java.lang можно ссылаться по их коротким именам (если, конечно, в файле не декларированы типы с такими же именами — локально декларированные типы всегда имеют преимущество перед внешними). |
Чтобы ссылаться на типы, декларированные в других пространствах имен, по их коротким именам, можно воспользоваться директивами использования. Директива using System.Collections; делает возможным ссылки с помощью короткого имени на любой тип (или вложенное пространство имен) пространства имен System.Collections в рамках кода пространства имен или типа, содержащего эту директиву или в рамках всего файла, если директива не вложена ни в какое пространство имен. Можно определять новые имена (синонимы или алиасы) для декларированных извне типов и пространств имен. Например, директива using Z=System.Collections.ArrayList; позволяет затем ссылаться на тип System.Collections.ArrayList по имени Z. | ||
Файлы должны располагаться в файловой системе определенным образом. Выделяется одна или несколько корневых директорий, которые при компиляции указываются в опции -sourcepath компилятора. Файлы из пакета без имени должны лежать в одной из корневых директорий. Все остальные должны находиться в поддиректориях этих корневых директорий так, чтобы имя содержащего пакета, к которому файл относится, совпадало бы с именем содержащей сам файл директории относительно включающей ее корневой (с заменой точки на разделитель имен директорий). | Нет никаких ограничений на именование файлов и содержащихся в них типов, а также на расположение файлов в файловой системе и имена декларированных в них пространств имен. | ||
Результаты компиляции располагаются в файлах с расширением .class, по одному типу на файл. Хранящие их директории организуются по тому же принципу, что и исходный код, — в соответствии с именами пакетов, начиная от некоторого (возможно другого) набора корневых директорий. Указать компилятору корневую директорию, в которую нужно складывать результаты компиляции, можно с помощью опции -d. Чтобы эти типы были доступны при компиляции других, корневые директории, содержащие соответвующие им .class-файлы, должны быть указаны в опции компилятора -classpath. В этой же опции могут быть указаны архивные файлы с расширением .jar, в которых много .class файлов хранится в соответствии со структурой пакетов. |
Результат компиляции C# программы — динамически загружаемая библиотека (с расширением .dll в системе Windows) или исполняемый файл (.exe), имеющие особую структуру. Такие библиотеки называются сборками (assembly). Для того чтобы использовать типы, находящиеся в некоторой сборке с расширением .dll, достаточно указать ее файл компилятору в качестве внешней библиотеки. | ||
Входной точкой программы является метод public static void main (String[]) одного из классов. Его параметр представляет собой массив строк, передаваемых как параметры командной строки при запуске. При этом полное имя класса, чей метод main() выбирается в качестве входной точки, указывается в качестве параметра виртуальной машине при запуске (параметры командной строки следуют за ним). |
Входной точкой программы является метод public static void Main () одного из классов. Такой метод может также иметь параметр типа string[] (представляющий параметры командной строки, как и в Java) и/или возвращать значение типа int. Класс, чей метод выбирается в качестве входной точки, указывается в качестве стартового класса при сборке исполняемого файла. Собранный таким образом файл всегда будет запускать метод Main() указанного класса. |
Ниже приведены программы на обоих языках, вычисляющие и печатающие на экране значение факториала неотрицательного целого числа (0! = 1, n! = 1*2*...*n), передаваемого им в качестве первого аргумента командной строки. Также приводятся командные строки для их компиляции и запуска.
В отличие от Java, параметры компилятора и способ запуска программ в C# не стандартизованы. Приведена командная строка для компилятора, входящего в состав Microsoft Visual Studio 2005 Beta 2. Предполагается, что директории, в которых находятся компиляторы, указаны в переменной окружения $path или %PATH%, и все команды выполняются в той же директории, где располагаются файлы с исходным кодом.
Компилятор Java и Java-машина располагаются в поддиректории bin той директории, в которую устанавливается набор для разработки Java Development Kit. Компилятор C# располагается в поддиректории Microsoft.NET\Framework\v<номер версии установленной среды .NET> системной директории Windows (обычно Windows или WINNT).
public class Counter { public int factorial(int n) { if(n == 0) return 1; else if(n > 0) return n * factorial(n - 1); else throw new IllegalArgumentException( "Argument should be >= 0, " + "current value n = " + n); } public static void main(String[] args) { int n = 2; if(args.length > 0) { try { n = Integer.parseInt(args[0]); } catch(NumberFormatException e) { n = 2; } } if(n < 0) n = 2; Counter f = new Counter(); System.out.println(f.factorial(n)); } } | using System; public class Counter { public int Factorial(int n) { if (n == 0) return 1; else if (n > 0) return n * Factorial(n - 1); else throw new ArgumentException( "Argument should be >= 0, " + "current value n = " + n); } public static void Main(string[] args) { int n = 2; if (args.Length > 0) { try { n = Int32.Parse(args[0]); } catch (Exception) { n = 2; } } if (n < 0) n = 2; Counter f = new Counter(); Console.WriteLine(f.Factorial(n)); } } |
Компиляция javac Counter.java | Компиляция csc.exe Counter.cs |
Выполнение java Counter 5 | Выполнение Counter.exe 5 |
Результат 120 | Результат 120 |