В этой лекции мы продолжаем рассмотрение элементов Java 5 и C# 2.0 на основе описывающих их стандартов [1] и [2,3].
В обоих языках класс может наследовать только одному классу и реализовывать несколько интерфейсов. Интерфейс может наследовать многим интерфейсам.
Классы, которые не должны иметь наследников, помечаются в Java как final, а в C# — как sealed.
В Java все классы (но не интерфейсы!) считаются наследниками класса Примитивные типы не являются его наследниками, в отличие от своих классов-оберток. |
В C# все классы, структурные, перечислимые и При этом, однако, типы значений (перечислимые и структурные типы, наследники Структурный тип может реализовывать один или несколько интерфейсов, но не может наследовать классу или другому структурному типу. |
При
Если же необходимо получить доступ к соответствующему полю предка, нужно использовать разные подходы в зависимости от того, статическое это поле или нет, т.е. относится ли оно к самому классу или к его объектам. К статическому полю можно обратиться, указав его полное имя, т.е. ClassName., к нестатическому полю из кода класса-потомка можно обратиться с помощью конструкций super. в Java и base. в C# (естественно, если оно не перекрыто в каком-то классе, промежуточном между данными предком и потомком). Конструкции super в Java и base в C# можно использовать и для обращения к this, являющийся ссылкой на объект, в котором вызывается данная
Основная выгода от использования
Статические
В Java все нестатические Но в Java, в отличие от C#, можно вызывать статические |
В C# нестатические Для декларации виртуального Элемент, не помеченный как |
Приводимые ниже примеры на обоих языках иллюстрируют разницу в работе виртуальных и невиртуальных
class A
{
public void m()
{
System.out.println("A.m() called");
}
public static void n()
{
System.out.println("A.n() called");
}
}
class B extends A
{
public void m()
{
System.out.println("B.m() called");
}
public static void n()
{
System.out.println("B.n() called");
}
}
public class C
{
public static void main(String[] args)
{
A a = new A();
B b = new B();
A c = new B();
a.m();
b.m();
c.m();
System.out.println("-----");
a.n();
b.n();
c.n();
}
}
|
using System;
class A
{
public virtual void m()
{
Console.WriteLine("A.m() called");
}
public void n()
{
Console.WriteLine("A.n() called");
}
}
class B : A
{
public override void m()
{
Console.WriteLine("B.m() called");
}
public new void n()
{
Console.WriteLine("B.n() called");
}
}
public class C
{
public static void Main()
{
A a = new A();
B b = new B();
A c = new B();
a.m();
b.m();
c.m();
Console.WriteLine("-----");
a.n();
b.n();
c.n();
}
}
|
Представленный в примере код выдает следующие результаты. A.m() called B.m() called B.m() called ----- A.n() called B.n() called A.n() called |
Если в приведенном примере убрать модификатор Представленный в примере код выдает следующие результаты. A.m() called B.m() called B.m() called ----- A.n() called B.n() called A.n() called |
Элементы или члены (members) пользовательских типов могут быть
В обоих языках поля можно только перекрывать в наследниках, а
У каждого элемента класса могут присутствовать модификаторы, определяющие доступность этого элемента из разных мест программы, а также его контекст — относится ли он к объектам этого класса (нестатический элемент) или к самому классу (static ).
Для указания доступности в обоих языках могут использоваться модификаторы public, protected и private, указывающие, соответственно, что данный элемент доступен везде, где доступен содержащий его тип, доступен только в описаниях типов-наследников содержащего типа или только в рамках описания самого содержащего типа. Доступность по умолчанию, без указания модификатора, трактуется в рассматриваемых языках различно.
Нестатические abstract. Вместо кода у абстрактного
final, а в C#, как sealed.
В обоих языках можно использовать
В Java для этого предусмотрен механизм Java Native Interface,
В Java, помимо перечисленных членов типов, имеются |
В C# члены типов, помимо Нестатические |
Для многих из дополнительных разновидностей членов типов, имеющихся в C#, есть аналогичные идиомы в
|
Пример объявления |
public class A2
{
public static final double PHI =
1.61803398874989;
}
|
public class A2
{
public const double Phi =
1.61803398874989;
}
|
Если Эти соглашения широко используются в разработке Java-программ, и такие Они и стали основанием для введения специальной конструкции для описания |
Обращение к При перегрузке Пример объявления |
public class MyArrayList
{
private int[] items = new int[10];
private int size = 0;
public int getSize()
{
return size;
}
public int getCapacity()
{
return items.Length;
}
public void setCapacity(int value)
{
int[] newItems = new int[value];
System.arraycopy
(items, 0, newItems, 0, size);
items = newItems;
}
public static void main(String[] args)
{
MyArrayList l = new MyArrayList();
System.out.println(l.getSize());
System.out.println(l.getCapacity());
l.setCapacity(50);
System.out.println(l.getSize());
System.out.println(l.getCapacity());
}
}
|
using System;
public class MyArrayList
{
private int[] items = new int[10];
private int size = 0;
public int Size
{
get { return size; }
}
public int Capacity
{
get { return items.Length; }
set
{
int[] newItems = new int[value];
Array.Copy
(items, newItems, size);
items = newItems;
}
}
public static void Main()
{
MyArrayList l = new MyArrayList();
Console.WriteLine( l.Size );
Console.WriteLine( l.Capacity );
l.Capacity = 50;
Console.WriteLine( l.Size );
Console.WriteLine( l.Capacity );
}
}
|
|
В C# может быть определен только один индексер для типа и данного набора типов параметров. Т.е. нет возможности определять Обращение к индексеру объекта (или класса, т.е. статическому) производится так, как будто этот объект (класс) был бы массивом, индексированным набором индексов соответствующих типов. При перегрузке индексера в наследниках перегружаются Индексеры должны быть нестатическими. Обращение к индексеру класса-предка в индексере наследника организуется с помощью конструкции Пример декларации и использования индексера приведен ниже. |
public class MyArrayList
{
int[] items = new int[10];
int size = 0;
public int getItem(int i)
{
if (i < 0 || i >= 10) throw new
IllegalArgumentException();
else return items[i];
}
public void setItem(int i, int value)
{
if (i < 0 || i >= 10) throw new
IllegalArgumentException();
else items[i] = value;
}
public static void main(String[] args)
{
MyArrayList l = new MyArrayList();
l.setItem(0, 23);
l.setItem(1, 75);
l.setItem(1, l.getItem(1)-1);
l.setItem(0,
l.getItem(0) + l.getItem(1));
System.out.println (l.getItem(0));
System.out.println (l.getItem(1));
}
}
|
using System;
public class MyArrayList
{
int[] items = new int[10];
int size = 0;
public int this[int i]
{
get
{
if (i < 0 || i >= 10) throw new
IndexOutOfRangeException();
else return items[i];
}
set
{
if (i < 0 || i >= 10) throw new
IndexOutOfRangeException();
else items[i] = value;
}
}
public static void Main()
{
MyArrayList l = new MyArrayList();
l[0] = 23;
l[1] = 75;
l[0] += (--l[1]);
Console.WriteLine(l[0]);
Console.WriteLine(l[1]);
}
}
|
При этом класс Класс источника public void addEventListener
(EventListener)
public void removeEventListener
(EventListener).
Можно заметить, что такой способ реализации обработки В приведенном ниже примере все |
Если же программист хочет реализовать какое-то специфическое хранение обработчиков В приведенном ниже примере одно из При перегрузке |
public class MouseEventArgs { ... }
public class MouseEventObject
extends java.util.EventObject
{
MouseEventArgs args;
MouseEventObject
(Object source, MouseEventArgs args)
{
super(source);
this.args = args;
}
}
public interface MouseEventListener
extends java.util.EventListener
{
void mouseUp(MouseEventObject e);
void mouseDown(MouseEventObject e);
}
import java.util.ArrayList;
public class MouseEventSource
{
private ArrayList<MouseEventListener>
listeners = new ArrayList
<MouseEventListener >();
public synchronized void
addMouseEventListener
(MouseEventListener l)
{ listeners.add(l); }
public synchronized void
removeMouseEventListener
(MouseEventListener l)
{ listeners.remove(l); }
protected void notifyMouseUp
(MouseEventArgs a)
{
MouseEventObject e =
new MouseEventObject(this, a);
ArrayList<MouseEventListener> l;
synchronized(this)
{
l =
(ArrayList<MouseEventListener>)
listeners.clone();
for(MouseEventListener el : l)
el.mouseUp(e);
}
}
protected void notifyMouseDown
(MouseEventArgs a)
{
MouseEventObject e =
new MouseEventObject(this, a);
ArrayList<MouseEventListener> l;
synchronized(this)
{
l =
(ArrayList<MouseEventListener>)
listeners.clone();
for(MouseEventListener el : l)
el.mouseDown(e);
}
}
}
public class HandlerConfigurator
{
MouseEventSource s =
new MouseEventSource();
MouseEventListener listener =
new MouseEventListener()
{
public void mouseUp
(MouseEventObject e) { ... }
public void mouseDown
(MouseEventObject e) { ... }
};
public void configure()
{
s.addMouseEventListener(listener);
}
}
|
public class MouseEventArgs { ... }
public delegate void MouseEventHandler
(object source, MouseEventArgs e);
public class MouseEventSource
{
public event MouseEventHandler MouseUp;
private MouseEventHandler mouseDown;
public event
MouseEventHandler MouseDown
{
add
{
lock(this)
{ mouseDown += value; }
}
remove
{
lock(this)
{ mouseDown -= value; }
}
}
protected void
OnMouseUp(MouseEventArgs e)
{
MouseUp(this, e);
}
protected void
OnMouseDown(MouseEventArgs e)
{
mouseDown(this, e);
}
}
public class HandlerConfigurator
{
MouseEventSource s =
new MouseEventSource();
public void UpHandler
(object source, MouseEventArgs e)
{ ... }
public void DownHandler
(object source, MouseEventArgs e)
{ ... }
public void Configure()
{
s.MouseUp += UpHandler;
s.MouseDown += DownHandler;
}
}
|
В C# 2.0 введена возможность декларации различной доступности у таких Для этого можно описать public int Property
{
get { … }
protected set { … }
}
|
|
В Java никакие Вообще, в этом языке имеются только
|
Некоторые Переопределяемые унарные Переопределяемые бинарные Можно определять также Некоторые
Если в типе Аналогичным образом автоматически переопределяются составные Ниже приведен пример переопределения и использования Другая тонкость — необходимость приведения объектов типа using System;
public class MyInt
{
int n = 0;
public MyInt(int n) { this.n = n; }
public override bool Equals(object obj)
{
MyInt o = obj as MyInt;
if (o == null) return false;
return o.n == n;
}
public override int GetHashCode()
{ return n; }
public override string ToString()
{ return n.ToString(); }
public static bool AreEqual
(MyInt i1, MyInt i2)
{
if ((object)i1 == null)
return ((object)i2 == null);
else return i1.Equals(i2);
}
public static bool operator ==
(MyInt i1, MyInt i2)
{ return AreEqual(i1, i2); }
public static bool operator !=
(MyInt i1, MyInt i2)
{ return !AreEqual(i1, i2); }
public static bool operator true
(MyInt i)
{ return i.n > 0; }
public static bool operator false
(MyInt i)
{ return i.n <= 0; }
public static MyInt operator ++
(MyInt i)
{ return new MyInt(i.n + 1); }
public static MyInt operator --
(MyInt i)
{ return new MyInt(i.n - 1); }
public static MyInt operator
(MyInt i1, MyInt i2)
{ return new MyInt(i1.n i2.n); }
public static MyInt operator |
(MyInt i1, MyInt i2)
{ return new MyInt(i1.n | i2.n); }
public static implicit operator int
(MyInt i)
{ return i.n; }
public static explicit operator
MyInt (int i)
{ return new MyInt(i); }
public static void Main()
{
MyInt n = (MyInt)5;
MyInt k = (MyInt)(n - 3 * n);
Console.WriteLine("k = " + k +
" , n = " + n);
Console.WriteLine("n == n : " +
(n == n));
Console.WriteLine("n == k : " +
(n == k));
Console.WriteLine(
"(++k) (n++) : " +
((++k) (n++)));
Console.WriteLine("k = " + k +
" , n = " + n);
Console.WriteLine(
"(++n) (k++) : " +
((++n) (k++)));
Console.WriteLine("k = " + k +
" , n = " + n);
}
}
|
Аналогом деструктора в Java является Так же, как и деструктор в C#, этот |
Деструктор предназначен для освобождения каких-либо ресурсов, связанных с объектом и не освобождаемых автоматически средой .NET, либо для оптимизации использования ресурсов за счет их явного освобождения. Деструктор вызывается автоматически при уничтожении объекта в ходе работы механизма управления памятью .NET. В этот момент объект уже должен быть помечен сборщиком мусора как неиспользуемый. Деструктор оформляется как особый |
public class MyFileReader
{
java.io.FileReader input;
public MyFileReader(String path)
throws FileNotFoundException
{
input = new java.io.FileReader
(new java.io.File(path));
}
protected void finalize()
{
System.out.println("Destructor");
try { input.close(); }
catch (IOException e)
{
e.printStackTrace();
}
}
}
|
using System;
public class MyFileStream
{
System.IO.FileStream input;
public MyFileStream(string path)
{
input = System.IO.File.Open
(path, System.IO.FileMode.Open);
}
~MyFileStream()
{
Console.WriteLine("Destructor");
input.Close();
}
}
|
Эти блоки выполняются вместе с |
|
Статические |
Статический Оформляется он как |
public class A
{
static
{
System.out.println("Loading A");
}
static int x = 1;
static
{
System.out.println("x = " + x);
x++;
}
static int y = 2;
static
{
y = x + 3;
System.out.println("x = " + x);
System.out.println("y = " + y);
}
public static void main(String[] args)
{}
}
|
using System;
public class A
{
static A()
{
Console.WriteLine("Loading A");
Console.WriteLine("x = " + x);
x++;
y = x + 3;
Console.WriteLine("x = " + x);
Console.WriteLine("y = " + y);
}
static int x = 1;
static int y = 2;
public static void Main() {}
}
|
Приведенный выше код выдает результат Loading A x = 1 x = 2 y = 5 |
Приведенный выше код выдает результат Loading A x = 1 x = 2 y = 5 |
В Java нестатические Такая конструкция используется, например, для определения классов итераторов для коллекций — объект-итератор всегда связан с объектом-коллекцией, которую он итерирует. В то же время, пользователю не нужно знать, какого именно типа данный итератор, — достаточно, что он реализует общий интерфейс всех итераторов, позволяющий проверить, есть ли еще объекты, и получить следующий объект. Получить этот объект внутри декларации вложенного типа можно с помощью конструкции При создании объекта такого вложенного класса необходимо указать объект объемлющего класса, к которому тот будет привязан. |
В C# модификатор |
public class ContainingClass
{
static int counter = 1;
static int ecounter = 1;
int id = counter++;
class EmbeddedClass
{
int eid = ecounter++;
public String toString()
{
return "" +
ContainingClass.this.id +
'.' + eid;
}
}
public String toString()
{
return "" + id;
}
public static void main
(String[] args)
{
ContainingClass
c = new ContainingClass()
, c1 = new ContainingClass();
System.out.println(c);
System.out.println(c1);
EmbeddedClass
e = c.new EmbeddedClass()
, e1 = c.new EmbeddedClass()
, e2 = c1.new EmbeddedClass();
System.out.println(e);
System.out.println(e1);
System.out.println(e2);
}
}
|
|
В C# класс может определить различные реализации для Для этого при определении таких using System;
public interface I1
{
void m();
}
public interface I2
{
void m();
}
public class A : I1, I2
{
public void m()
{
Console.WriteLine("A.m() called");
}
void I1.m()
{
Console.WriteLine
("I1.m() defined in A called");
}
void I2.m()
{
Console.WriteLine
("I2.m() defined in A called");
}
public static void Main()
{
A f = new A();
I1 i1 = f;
I2 i2 = f;
f.m();
i1.m();
i2.m();
}
}
Результат работы приведенного выше примера следующий. A.m() called I1.m() defined in A called I2.m() defined in A called |
Последовательность выполнения
О правилах, определяющих эту последовательность, можно судить по результатам работы следующих примеров.
public class A
{
static
{
System.out.println
("Static initializer of A");
}
{
System.out.println
("Initializer of A");
}
static int sinit()
{
System.out.println
("Static field initializer of A");
return 0;
}
static int init()
{
System.out.println
("Field initializer of A");
return 0;
}
static int sf = sinit();
int f = init();
public A()
{
System.out.println
("Constructor of A");
}
}
public class B extends A
{
static int sf = sinit();
int f = init();
static
{
System.out.println
("Static initializer of B");
}
{
System.out.println
("Initializer of B");
}
static int sinit()
{
System.out.println
("Static field initializer of B");
return 0;
}
static int init()
{
System.out.println
("Field initializer of B");
return 0;
}
public B()
{
System.out.println
("Constructor of B");
}
}
public class C
{
public static void main(String[] args)
{
B b = new B();
}
}
|
using System;
public class A
{
static A()
{
Console.WriteLine
("Static constructor of A");
}
static int sinit()
{
Console.WriteLine
("Static field initializer of A");
return 0;
}
static int init()
{
Console.WriteLine
("Field initializer of A");
return 0;
}
static int sf = sinit();
int f = init();
public A()
{
Console.WriteLine
("Constructor of A");
}
}
public class B : A
{
static int sf = sinit();
int f = init();
static B()
{
Console.WriteLine
("Static constructor of B");
}
static int sinit()
{
Console.WriteLine
("Static field initializer of B");
return 0;
}
static int init()
{
Console.WriteLine
("Field initializer of B");
return 0;
}
public B()
{
Console.WriteLine
("Constructor of B");
}
}
public class C
{
public static void Main()
{
B b = new B();
}
}
|
Результат работы приведенного примера такой. Static initializer of A Static field initializer of A Static field initializer of B Static initializer of B Initializer of A Field initializer of A Constructor of A Field initializer of B Initializer of B Constructor of B |
Результат работы приведенного примера такой. Static field initializer of B Static constructor of B Field initializer of B Static field initializer of A Static constructor of A Field initializer of A Constructor of A Constructor of B |
В Java элемент типа, помимо доступности, указываемой с помощью модификаторов Именно пакетная доступность используется в Java по умолчанию.
Типы, не вложенные в другие, могут быть либо |
В C# доступность элемента типа по умолчанию — Имеется дополнительный модификатор доступности — Кроме того, в C# можно использовать комбинированный модификатор Типы, не вложенные в другие, могут быть либо В C# все элементы типов могут быть помечены модификатором new для точного указания на то, что такой элемент — новый и никак не соотносится с элементами предков данного типа с тем же именем или той же сигнатурой. |
В Java для полей классов дополнительно к модификаторам доступности и контекста могут использоваться модификаторы |
Поля классов или структурных типов в C# могут иметь в качестве дополнительных модификаторов |
Модификатор Модификатор |
Модификатор Такие поля используются для представления постоянных в ходе работы программы объектов и значений, типы которых не допустимы для поддерживаемых языком |
Поля, помеченные модификатором В Java имеются соглашения о том, как должен быть оформлен интерфейс класса, объекты которого могут быть сохранены или переданы по сети — эти соглашения можно найти в документации по интерфейсу Стандартный механизм Java, обеспечивающий сохранение и восстановление объектов, реализующих интерфейс
Значение модификатора Значение модификатора |
В последних версиях обоих языков введены шаблонные, т.е. имеющие
Ниже приводятся примеры декларации шаблонного getTypeName() необязательно, поскольку он вычисляется из контекста вызова. Если вычислить типовые аргументы вызова
public class A
{
public static <T> String getTypeName
(T a)
{
if(a == null) return "NullType";
else return
a.getClass().getName();
}
public static void main(String[] args)
{
String y = "ABCDEFG";
System.out.println( getTypeName(y) );
System.out.println
( getTypeName(y.length()) );
System.out.println
( A.<Character>getTypeName
(y.charAt(1)) );
}
}
|
using System;
public class A
{
public static string getTypeName<T>
(T a)
{
if(a == null) return "NullType";
else return
a.GetType().FullName;
}
public static void Main()
{
string y = "ABCDEFG";
Console.WriteLine( getTypeName(y) );
Console.WriteLine
( getTypeName(y.Length) );
Console.WriteLine
( getTypeName<char>(y[1]) );
}
}
|
В Java в качестве типовых аргументов могут использоваться только ссылочные типы. Примитивный тип не может быть аргументом шаблона — вместо него нужно использовать соответствующий |
В C# любой тип может быть аргументом шаблона. |
В Java типовые аргументы являются элементами конкретного объекта — они фактически представляют собой набор дополнительных параметров |
В C# каждый экземпляр шаблонного класса, интерфейса или структурного типа с определенными аргументами имеет свой набор |
public class A<T>
{
public static int c = 0;
public T t;
}
public class B
{
public static void main
(String[] args)
{
A.c = 7;
System.out.println( A.c );
}
}
|
using System;
public class A<T>
{
public static int c = 0;
public T t;
}
public class B
{
public static void Main()
{
A<string>.c = 7;
Console.WriteLine( A<int>.c );
Console.WriteLine( A<string>.c );
}
}
|
В C# можно определить и использовать шаблонные public delegate bool Predicate<T>
(T value);
public class I
{
public bool m(int i)
{ return i == 0; }
public void f()
{
Predicate<int> pi = m;
Predicate<string> ps =
delegate(string s)
{ return s == null; };
}
}
|
В обоих языках имеются конструкции для указания ограничений на
Ограничения, требующие от типа-параметра наследовать некоторому другому типу, позволяют использовать
В Java можно указать, что тип-параметр данного шаблона должен быть наследником некоторого класса и/или реализовывать определенные интерфейсы. В приведенном ниже примере параметр |
В C# можно указать, что тип-параметр должен быть ссылочным, типом значения, наследовать определенному классу и/или определенным интерфейсам, а также иметь В приведенном ниже примере параметр |
public class A
{
public int m() { ... }
}
public interface B
{
public String n();
}
public class C<T extends A B>
{
T f;
public String k()
{
return f.n() + (f.m()*2);
}
}
|
public class A { ... }
public class B<T, U, V>
where T : class
where U : A, IList<T>, new()
where V : struct
{ ... }
|
Кроме того, в Java можно использовать неопределенные типовые параметры (wildcards) при описании типов. Неопределенный Неопределенные В приведенном ниже примере первый public class A
{
public void addAll
(Collection<?> c)
{ ... }
public <T> void addAll
(Collection<? extends T> c)
{ ... }
public <T> void addToCollection
(T e, Collection<? super T> c)
{ ... }
}
|
В обоих языках (в Java — начиная с версии 5) имеются конструкции, позволяющие описывать
В Java нужно указать тип элемента массива, многоточие и имя параметра. |
В C# нужно указать модификатор params, тип самого массива и имя параметра. |
public class A
{
public int f(int ... a)
{
return a.length;
}
public static void main(String[] args)
{
A a = new A();
System.out.println
( a.f(new int[]{9, 0}) );
System.out.println
( a.f(1, 2, 3, 4) );
}
}
|
using System;
public class A
{
public int f(params int[] a)
{
return a.Length;
}
public static void Main
{
A a = new A();
Console.WriteLine
( a.f(new int[]{9, 0}) );
Console.WriteLine
( a.f(1, 2, 3, 4) );
}
}
|
В Java требуется указывать некоторые Точнее, все исключения делятся на два вида — проверяемые (checked) и непроверяемые (unchecked). Непроверяемыми считаются исключения, возникновение которых может быть непреднамеренно — обращение к
Все классы проверяемых исключений, возникновение которых возможно при работе Если некоторый |
В C# исключения, возникновение которых возможно при работе |
public void m(int x)
throws MyException
{
throw new MyException();
}
public void n(int x)
throws MyException
{
m(x);
}
public void k(int x)
{
try { m(x); }
catch(MyException e)
{ ... }
}
|
|
В Java все параметры Поскольку все типы, кроме примитивных, являются |
В C# можно определить параметры Параметры, передаваемые по ссылке, помечаются модификаторов При вызове |
using System;
public class A
{
public void f
(int a, ref int b, out int c)
{
c = b - a;
b += a;
}
public static void Main()
{
A a = new A();
int n = 3, m = 0;
Console.WriteLine
("n = " + n + " , m = " + m);
a.f(1, ref n, out m);
Console.WriteLine
("n = " + n + " , m = " + m);
}
}
|
В обоих языках (в Java — начиная с версии 5) имеются встроенные средства для некоторого их расширения, для описания так называемых метаданных — данных, описывающих элементы кода. Это специальные модификаторы у типов, элементов типов и параметров
Такие данные служат для указания дополнительных
Метаданные служат встроенным механизмом расширения языка, позволяя описывать простые дополнительные
В обоих языках аннотации могут иметь структуру —
В приведенных ниже примерах определяются несколько типов аннотаций, которые затем используются для разметки элементов кода. Класс A помечен аннотацией, имеющей указанные значения n() помечен еще одной аннотацией. Параметр n() также помечен простой аннотацией.
@interface SimpleMethodAnnotation {}
@interface SimpleParameterAnnotation{}
@interface ComplexClassAnnotation
{
int id();
String author() default
"Victor Kuliamin";
String date();
}
@interface AdditionalMethodAnnotation
{
String value() default "";
}
@ComplexClassAnnotation
(
id = 126453,
date = "23.09.2005"
)
public class A
{
@SimpleMethodAnnotation
public void m() { ... }
@SimpleMethodAnnotation
@AdditionalMethodAnnotation
( value = "123" )
public void n
(@SimpleParameterAnnotation int k)
{ ... }
}
|
class SimpleMethodAttribute
: Attribute
{}
class SimpleParameterAttribute
: Attribute
{}
class ComplexClassAttribute
: Attribute
{
public int id;
public String author =
"Victor Kuliamin";
public String date;
}
class AdditionalMethodAttribute
: Attribute
{
public String value = "";
}
[ComplexClassAttribute
(
id = 126453,
date = "23.09.2005"
)]
public class A
{
[SimpleMethodAttribute]
public void m() { ... }
[SimpleMethodAttribute,
AdditionalMethodAttribute
(value = "123")]
public void n
([SimpleParameterAttribute] int k)
{ ... }
}
|
В Java аннотации могут помечать также пакеты (т.е. использоваться в директиве декларации пакета, к которому относится данный файл), декларации локальных переменных и |
В C# могут быть описаны глобальные атрибуты, помечающие сборку, в рамках кода которой они встречаются. Такие атрибуты помещаются вне описаний пространств имен. Также атрибутами могут помечаться отдельные |
Аннотационный тип декларируется с модификатором Такой тип не может иметь |
Атрибутный тип описывается как класс, наследующий Такой тип не может иметь |
При определении значений Помимо |
Позиционные параметры определяются при помощи Именованные параметры определяются как доступные для чтения и записи нестатические поля и При определении значений В атрибутном типе могут быть описаны такие же элементы, как и в обычном классе. Ниже приведен пример использования позиционного параметра. class ClassAttribute : Attribute
{
public ClassAttribute(int id)
{
this.id = id;
}
int id;
public string value;
}
[ClassAttribute(4627, value = "")]
public class A { ... }
|
|
У атрибутов может указываться цель, к которой они привязываются. Для атрибутов, помещаемых вне рамок пространств имен, указание такой цели — [assembly :
MyAttribute
(
id = 4627,
author = "Victor Kuliamin"
)]
В C# принято соглашение, согласно которому окончание Поэтому, имея атрибутный тип В последнем случае компилятор будет пытаться найти атрибутный тип с именем |
Как в Java, так и в C# возможно создание многопоточных приложений. Вообще говоря, каждая программа на этих языках представляет собой набор
Каждый java.lang.Thread в Java и System.Threading.Thread в C#). Для запуска некоторого кода в виде отдельного
В Java это можно сделать двумя способами. Первый — определить класс-наследник Другой способ — определить класс, реализующий интерфейс В обоих случаях для запуска выполнения |
В C# также можно использовать два способа. С помощью первого можно создать обычный Для этого нужно определить В первом варианте на основе этого Этот делегат передается в качестве аргумента
|
class T extends Thread
{
int id = 0;
public T(int id)
{ this.id = id; }
public void run()
{
System.out.println
("Thread " + id + " is working");
}
}
public class A
{
public static void main(String[] args)
{
Thread th1 = new T(1),
th2 = new T(2),
th3 = new Thread(
new Runnable() {
public void run()
{
System.out.println
("Runnable is working");
}
});
th1.start();
th2.start();
th3.start();
}
}
|
using System;
using System.Threading;
class T
{
int id;
public T(int id)
{ this.id = id; }
public void m()
{
Console.WriteLine
("Thread " + id + " is working");
}
}
public class A
{
static void m()
{
Console.WriteLine
("Nonparameterized thread" +
" is working");
}
static void m(object o)
{
Console.WriteLine
("Thread with object " + o +
" is working");
}
public static void Main()
{
Thread th1 = new Thread(
new ThreadStart(m)),
th2 = new Thread(
new ThreadStart(new T(1).m)),
th3 = new Thread(
new ParameterizedThreadStart(m));
th1.Start();
th2.Start();
th3.Start(2);
}
}
|
При разработке приложений, основанных на параллельном выполнении нескольких
В обоих языках имеются конструкции, которые реализуют синхронизационный примитив, называемый монитором (monitor). Монитор представляет собой объект, позволяющий
Для синхронизации используется конструкция, гарантирующая, что некоторый участок кода в каждый момент времени выполняется не более чем одним synchronized в Java или lock в C#.
public class PingPong extends Thread
{
boolean odd;
PingPong (boolean odd)
{ this.odd = odd; }
static int counter = 1;
static Object monitor = new Object();
public void run()
{
while(counter < 100)
{
synchronized(monitor)
{
if(counter%2 == 1 odd)
{
System.out.print("Ping ");
counter++;
}
if(counter%2 == 0 !odd)
{
System.out.print("Pong ");
counter++;
}
}
}
}
public static void main
(String[] args)
{
Thread th1 = new PingPong (false),
th2 = new PingPong (true);
th1.start();
th2.start();
}
}
|
using System;
using System.Threading;
public class PingPong
{
bool odd;
PingPong (bool odd) { this.odd = odd; }
static int counter = 1;
static object monitor = new object();
public void Run()
{
while(counter < 100)
{
lock(monitor)
{
if(counter%2 == 1 odd)
{
Console.Write("Ping ");
counter++;
}
if(counter%2 == 0 !odd)
{
Console.Write("Pong ");
counter++;
}
}
}
}
public static void Main()
{
Thread th1 = new Thread(
new ThreadStart(
new PingPong(false).Run)),
th2 = new Thread(
new ThreadStart(
new PingPong(true).Run));
th1.Start();
th2.Start();
}
}
|
Кроме того, в Java любой Такой модификатор эквивалентен помещению всего тела В Java также имеется стандартный механизм использования любого объекта ссылочного типа в качестве монитора для создания более сложных Для этого в классе Однако это механизм достаточно сложен в использовании и не очень эффективен. Для реализации более сложной синхронизации лучше пользоваться библиотечными классами из пакетов |
Обе платформы, как Java, так и .NET, в большой степени опираются на библиотеки готовых компонентов. Сообщество разработчиков ведет постоянную работу по выработке стандартных интерфейсов компонентов для решения различных задач в разных предметных областях. По достижении определенной степени зрелости такие интерфейсы включаются в стандартные библиотеки.
Для Java этот процесс начался значительно раньше и захватил гораздо больше участников в силу более раннего появления платформы и ее большей открытости и стандартизованности. Поэтому далеко не все часто используемые библиотеки классов распространяются в составе платформ
В данном разделе дается краткий обзор основных библиотек, подробности см. в [10] и [11].
Основные классы языка Java содержатся в пакете |
Основные классы C# содержатся в пространстве имен |
Базовым классом для всех ссылочных типов Java служит класс Этот класс содержит следующие
|
Базовым типом для всех типов C# служит класс
|
Класс Поля
Другой класс, содержащий |
Данные о среде выполнения можно получить с помощью класса В нем имеются Управлять стандартным вводом-выводом можно с помощью класса Он содержит |
Для работы со строками используются классы Класс |
Для работы со строками используются классы |
В пакете Все числовые классы наследуют классу Эти классы содержат Для форматированного представления чисел используются классы |
Обертками примитивных типов C# служат следующие структурные типы из пространства имен
Они также содержат аналогичные Для работы с форматированным строковым представлением чисел используются |
Набор математических функций и Для генерации псевдослучайных чисел можно использовать как Для более сложных вычислений можно использовать классы пакета |
Набор математических функций и Для генерации псевдослучайных чисел используется класс |
Он содержит Класс |
Классом, представляющим Текущий Этот класс тоже содержит |
В пакете
|
Аналогичные интерфейсы в C# следующие.
В C#, в отличии от Java, где шаблонные типы всегда имеют и непривязанный к типам-аргументам интерфейс, шаблоны с типизированным интерфейсом просто добавляются в библиотеки к уже существовавшим там до появления C# 2.0 нетипизированным классам и интерфейсам. |
В пакете Все эти классы, а также классы, находящиеся в пакете |
Для C# аналогичную роль в механизме рефлексии играют классы |
Для анализа структуры и элементов массивов во время работы программы можно использовать Для манипуляций с массивами — поиска, сортировки, сравнения и пр. — используются |
Аналогом обоих классов Java для работы с массивами служит класс Он содержит как |
В обоих языках имеются механизмы, позволяющие определять слабые ссылки. Объект, хранящийся по слабой ссылке, считается сборщиком мусора недоступным по ней и поэтому может быть уничтожен при очередном запуске процедуры сборки мусора, если обычных ссылок на него не осталось.
Слабые ссылки удобны для организации хранилищ объектов, которые не должны мешать их уничтожению, если эти объекты стали недоступны во всех других местах, т.е. ссылки на них остались только из такого хранилища.
Пакет Например, класс Другие классы представляют более хитрые виды ссылок. |
В C# слабые ссылки реализуются при помощи класса |
Пакет
Много полезных классов-коллекций можно найти вне стандартных библиотек, например, в библиотеке Jakarta Commons [12], части обширного проекта Apache Jakarta Project [13]. |
Набор интерфейсов и классов коллекций C# находится в пространствах имен Базовые интерфейсы коллекций — Интерфейс отображений и классы, реализующие отображения — Интерфейс и классы списков —
|
Для определения линейного порядка на объектах типа Наиболее часто применяется его реализация для строк — |
Аналогичный интерфейс в C# — Его реализация для строк — абстрактный класс |
Классы Первые два класса используются для представления информации о календаре, объекты класса Класса для представления временных интервалов в стандартной библиотеке нет. Гораздо более широкий набор средств для работы с датами и временами предоставляет библиотека Joda [14]. |
Для представления различных календарей используются подклассы Для представления моментов времени — Для интервалов времени — Для форматированного текстового представления дат и времени — |
Классы |
Аналоги в C# — Еще одни аналоги находятся в пространстве имен |
Для построения и анализа форматированных строк, содержащих данные различных типов, полезен класс С помощью реализации интерфейса |
В C# преобразование в форматированную строку осуществляется Обычно в качестве объектов, задающих форматирование, используются объекты классов |
В пакете С помощью объектов класса Currency представляют различные валюты. Информация о региональных или национально-специфических настройках и параметрах представляется в виде объектов класса Набор объектов, имеющих локализованные варианты, например, строки сообщений на разных языках, может храниться с помощью подклассов |
Аналогичную роль в C# играют классы пространства имен Для хранения наборов объектов вместе с их аналогами для нескольких культур используются объекты класса |
Работа с различными кодировками текста организуется при помощи классов пакета |
Работа с различными кодировками текста организуется при помощи классов пространства имен |
Интерфейс и классы пакета |
|
Класс Более гибкую работу с регулярными выражениями можно реализовать с помощью классов пакета |
В C# работа с регулярными выражениями может быть организована при помощи классов пространства имен |
Пакет Пакет |
Аналогичные функции выполняют классы пространства имен |
Пакет |
Аналогичные классы содержатся в пространстве имен |
Пакет |
Аналоги классов из |
Классы и интерфейсы, лежащие в основе На основе этой модели реализованы библиотеки элементов управления графического пользовательского интерфейса (graphical user interface, GUI) Java. Одна из этих библиотек (самая старая и не очень эффективная) размещается в пакете Другая, более новая и демонстрирующая большую производительность — в пакете Одной из наиболее эффективных библиотек графических элементов управления на Java на данный момент считается библиотека SWT (Standard Widget Toolkit [15]), на основе которой разрабатывается расширяемая среда разработки приложений Eclipse [16]. |
Классы и интерфейсы, лежащие в основе Эти классы, в основном, расположены в сборке System. Библиотека элементов GUI находится в пространстве имен Библиотека классов общего назначения для работы с графикой находится в пространстве |
Интерфейсы и классы для разработки сетевого ПО и организации связи между приложениями, работающими на разных машинах, находятся в пакетах |
Аналогичные классы и интерфейсы находятся в пространствах имен |
Пакеты |
Аналогичные классы и интерфейсы находятся в пространстве имен |
Пакеты |
Аналогичные библиотеки в .NET находятся в пространстве имен Определенные там интерфейсы являются основой ADO.NET. |
Пакет |
Интерфейс и реализация аналогичной службы директорий ActiveDirectory on Microsoft находятся в пространстве имен |
В этой лекции мы продолжаем рассмотрение элементов Java 5 и C# 2.0 на основе описывающих их стандартов [1] и [2,3].
В обоих языках класс может наследовать только одному классу и реализовывать несколько интерфейсов. Интерфейс может наследовать многим интерфейсам.
Классы, которые не должны иметь наследников, помечаются в Java как final, а в C# — как sealed.
В Java все классы (но не интерфейсы!) считаются наследниками класса Примитивные типы не являются его наследниками, в отличие от своих классов-оберток. |
В C# все классы, структурные, перечислимые и При этом, однако, типы значений (перечислимые и структурные типы, наследники Структурный тип может реализовывать один или несколько интерфейсов, но не может наследовать классу или другому структурному типу. |
При
Если же необходимо получить доступ к соответствующему полю предка, нужно использовать разные подходы в зависимости от того, статическое это поле или нет, т.е. относится ли оно к самому классу или к его объектам. К статическому полю можно обратиться, указав его полное имя, т.е. ClassName., к нестатическому полю из кода класса-потомка можно обратиться с помощью конструкций super. в Java и base. в C# (естественно, если оно не перекрыто в каком-то классе, промежуточном между данными предком и потомком). Конструкции super в Java и base в C# можно использовать и для обращения к this, являющийся ссылкой на объект, в котором вызывается данная
Основная выгода от использования
Статические
В Java все нестатические Но в Java, в отличие от C#, можно вызывать статические |
В C# нестатические Для декларации виртуального Элемент, не помеченный как |
Приводимые ниже примеры на обоих языках иллюстрируют разницу в работе виртуальных и невиртуальных
class A
{
public void m()
{
System.out.println("A.m() called");
}
public static void n()
{
System.out.println("A.n() called");
}
}
class B extends A
{
public void m()
{
System.out.println("B.m() called");
}
public static void n()
{
System.out.println("B.n() called");
}
}
public class C
{
public static void main(String[] args)
{
A a = new A();
B b = new B();
A c = new B();
a.m();
b.m();
c.m();
System.out.println("-----");
a.n();
b.n();
c.n();
}
}
|
using System;
class A
{
public virtual void m()
{
Console.WriteLine("A.m() called");
}
public void n()
{
Console.WriteLine("A.n() called");
}
}
class B : A
{
public override void m()
{
Console.WriteLine("B.m() called");
}
public new void n()
{
Console.WriteLine("B.n() called");
}
}
public class C
{
public static void Main()
{
A a = new A();
B b = new B();
A c = new B();
a.m();
b.m();
c.m();
Console.WriteLine("-----");
a.n();
b.n();
c.n();
}
}
|
Представленный в примере код выдает следующие результаты. A.m() called B.m() called B.m() called ----- A.n() called B.n() called A.n() called |
Если в приведенном примере убрать модификатор Представленный в примере код выдает следующие результаты. A.m() called B.m() called B.m() called ----- A.n() called B.n() called A.n() called |
Элементы или члены (members) пользовательских типов могут быть
В обоих языках поля можно только перекрывать в наследниках, а
У каждого элемента класса могут присутствовать модификаторы, определяющие доступность этого элемента из разных мест программы, а также его контекст — относится ли он к объектам этого класса (нестатический элемент) или к самому классу (static ).
Для указания доступности в обоих языках могут использоваться модификаторы public, protected и private, указывающие, соответственно, что данный элемент доступен везде, где доступен содержащий его тип, доступен только в описаниях типов-наследников содержащего типа или только в рамках описания самого содержащего типа. Доступность по умолчанию, без указания модификатора, трактуется в рассматриваемых языках различно.
Нестатические abstract. Вместо кода у абстрактного
final, а в C#, как sealed.
В обоих языках можно использовать
В Java для этого предусмотрен механизм Java Native Interface,
В Java, помимо перечисленных членов типов, имеются |
В C# члены типов, помимо Нестатические |
Для многих из дополнительных разновидностей членов типов, имеющихся в C#, есть аналогичные идиомы в
|
Пример объявления |
public class A2
{
public static final double PHI =
1.61803398874989;
}
|
public class A2
{
public const double Phi =
1.61803398874989;
}
|
Если Эти соглашения широко используются в разработке Java-программ, и такие Они и стали основанием для введения специальной конструкции для описания |
Обращение к При перегрузке Пример объявления |
public class MyArrayList
{
private int[] items = new int[10];
private int size = 0;
public int getSize()
{
return size;
}
public int getCapacity()
{
return items.Length;
}
public void setCapacity(int value)
{
int[] newItems = new int[value];
System.arraycopy
(items, 0, newItems, 0, size);
items = newItems;
}
public static void main(String[] args)
{
MyArrayList l = new MyArrayList();
System.out.println(l.getSize());
System.out.println(l.getCapacity());
l.setCapacity(50);
System.out.println(l.getSize());
System.out.println(l.getCapacity());
}
}
|
using System;
public class MyArrayList
{
private int[] items = new int[10];
private int size = 0;
public int Size
{
get { return size; }
}
public int Capacity
{
get { return items.Length; }
set
{
int[] newItems = new int[value];
Array.Copy
(items, newItems, size);
items = newItems;
}
}
public static void Main()
{
MyArrayList l = new MyArrayList();
Console.WriteLine( l.Size );
Console.WriteLine( l.Capacity );
l.Capacity = 50;
Console.WriteLine( l.Size );
Console.WriteLine( l.Capacity );
}
}
|
|
В C# может быть определен только один индексер для типа и данного набора типов параметров. Т.е. нет возможности определять Обращение к индексеру объекта (или класса, т.е. статическому) производится так, как будто этот объект (класс) был бы массивом, индексированным набором индексов соответствующих типов. При перегрузке индексера в наследниках перегружаются Индексеры должны быть нестатическими. Обращение к индексеру класса-предка в индексере наследника организуется с помощью конструкции Пример декларации и использования индексера приведен ниже. |
public class MyArrayList
{
int[] items = new int[10];
int size = 0;
public int getItem(int i)
{
if (i < 0 || i >= 10) throw new
IllegalArgumentException();
else return items[i];
}
public void setItem(int i, int value)
{
if (i < 0 || i >= 10) throw new
IllegalArgumentException();
else items[i] = value;
}
public static void main(String[] args)
{
MyArrayList l = new MyArrayList();
l.setItem(0, 23);
l.setItem(1, 75);
l.setItem(1, l.getItem(1)-1);
l.setItem(0,
l.getItem(0) + l.getItem(1));
System.out.println (l.getItem(0));
System.out.println (l.getItem(1));
}
}
|
using System;
public class MyArrayList
{
int[] items = new int[10];
int size = 0;
public int this[int i]
{
get
{
if (i < 0 || i >= 10) throw new
IndexOutOfRangeException();
else return items[i];
}
set
{
if (i < 0 || i >= 10) throw new
IndexOutOfRangeException();
else items[i] = value;
}
}
public static void Main()
{
MyArrayList l = new MyArrayList();
l[0] = 23;
l[1] = 75;
l[0] += (--l[1]);
Console.WriteLine(l[0]);
Console.WriteLine(l[1]);
}
}
|
При этом класс Класс источника public void addEventListener
(EventListener)
public void removeEventListener
(EventListener).
Можно заметить, что такой способ реализации обработки В приведенном ниже примере все |
Если же программист хочет реализовать какое-то специфическое хранение обработчиков В приведенном ниже примере одно из При перегрузке |
public class MouseEventArgs { ... }
public class MouseEventObject
extends java.util.EventObject
{
MouseEventArgs args;
MouseEventObject
(Object source, MouseEventArgs args)
{
super(source);
this.args = args;
}
}
public interface MouseEventListener
extends java.util.EventListener
{
void mouseUp(MouseEventObject e);
void mouseDown(MouseEventObject e);
}
import java.util.ArrayList;
public class MouseEventSource
{
private ArrayList<MouseEventListener>
listeners = new ArrayList
<MouseEventListener >();
public synchronized void
addMouseEventListener
(MouseEventListener l)
{ listeners.add(l); }
public synchronized void
removeMouseEventListener
(MouseEventListener l)
{ listeners.remove(l); }
protected void notifyMouseUp
(MouseEventArgs a)
{
MouseEventObject e =
new MouseEventObject(this, a);
ArrayList<MouseEventListener> l;
synchronized(this)
{
l =
(ArrayList<MouseEventListener>)
listeners.clone();
for(MouseEventListener el : l)
el.mouseUp(e);
}
}
protected void notifyMouseDown
(MouseEventArgs a)
{
MouseEventObject e =
new MouseEventObject(this, a);
ArrayList<MouseEventListener> l;
synchronized(this)
{
l =
(ArrayList<MouseEventListener>)
listeners.clone();
for(MouseEventListener el : l)
el.mouseDown(e);
}
}
}
public class HandlerConfigurator
{
MouseEventSource s =
new MouseEventSource();
MouseEventListener listener =
new MouseEventListener()
{
public void mouseUp
(MouseEventObject e) { ... }
public void mouseDown
(MouseEventObject e) { ... }
};
public void configure()
{
s.addMouseEventListener(listener);
}
}
|
public class MouseEventArgs { ... }
public delegate void MouseEventHandler
(object source, MouseEventArgs e);
public class MouseEventSource
{
public event MouseEventHandler MouseUp;
private MouseEventHandler mouseDown;
public event
MouseEventHandler MouseDown
{
add
{
lock(this)
{ mouseDown += value; }
}
remove
{
lock(this)
{ mouseDown -= value; }
}
}
protected void
OnMouseUp(MouseEventArgs e)
{
MouseUp(this, e);
}
protected void
OnMouseDown(MouseEventArgs e)
{
mouseDown(this, e);
}
}
public class HandlerConfigurator
{
MouseEventSource s =
new MouseEventSource();
public void UpHandler
(object source, MouseEventArgs e)
{ ... }
public void DownHandler
(object source, MouseEventArgs e)
{ ... }
public void Configure()
{
s.MouseUp += UpHandler;
s.MouseDown += DownHandler;
}
}
|
В C# 2.0 введена возможность декларации различной доступности у таких Для этого можно описать public int Property
{
get { … }
protected set { … }
}
|
|
В Java никакие Вообще, в этом языке имеются только
|
Некоторые Переопределяемые унарные Переопределяемые бинарные Можно определять также Некоторые
Если в типе Аналогичным образом автоматически переопределяются составные Ниже приведен пример переопределения и использования Другая тонкость — необходимость приведения объектов типа using System;
public class MyInt
{
int n = 0;
public MyInt(int n) { this.n = n; }
public override bool Equals(object obj)
{
MyInt o = obj as MyInt;
if (o == null) return false;
return o.n == n;
}
public override int GetHashCode()
{ return n; }
public override string ToString()
{ return n.ToString(); }
public static bool AreEqual
(MyInt i1, MyInt i2)
{
if ((object)i1 == null)
return ((object)i2 == null);
else return i1.Equals(i2);
}
public static bool operator ==
(MyInt i1, MyInt i2)
{ return AreEqual(i1, i2); }
public static bool operator !=
(MyInt i1, MyInt i2)
{ return !AreEqual(i1, i2); }
public static bool operator true
(MyInt i)
{ return i.n > 0; }
public static bool operator false
(MyInt i)
{ return i.n <= 0; }
public static MyInt operator ++
(MyInt i)
{ return new MyInt(i.n + 1); }
public static MyInt operator --
(MyInt i)
{ return new MyInt(i.n - 1); }
public static MyInt operator
(MyInt i1, MyInt i2)
{ return new MyInt(i1.n i2.n); }
public static MyInt operator |
(MyInt i1, MyInt i2)
{ return new MyInt(i1.n | i2.n); }
public static implicit operator int
(MyInt i)
{ return i.n; }
public static explicit operator
MyInt (int i)
{ return new MyInt(i); }
public static void Main()
{
MyInt n = (MyInt)5;
MyInt k = (MyInt)(n - 3 * n);
Console.WriteLine("k = " + k +
" , n = " + n);
Console.WriteLine("n == n : " +
(n == n));
Console.WriteLine("n == k : " +
(n == k));
Console.WriteLine(
"(++k) (n++) : " +
((++k) (n++)));
Console.WriteLine("k = " + k +
" , n = " + n);
Console.WriteLine(
"(++n) (k++) : " +
((++n) (k++)));
Console.WriteLine("k = " + k +
" , n = " + n);
}
}
|
Аналогом деструктора в Java является Так же, как и деструктор в C#, этот |
Деструктор предназначен для освобождения каких-либо ресурсов, связанных с объектом и не освобождаемых автоматически средой .NET, либо для оптимизации использования ресурсов за счет их явного освобождения. Деструктор вызывается автоматически при уничтожении объекта в ходе работы механизма управления памятью .NET. В этот момент объект уже должен быть помечен сборщиком мусора как неиспользуемый. Деструктор оформляется как особый |
public class MyFileReader
{
java.io.FileReader input;
public MyFileReader(String path)
throws FileNotFoundException
{
input = new java.io.FileReader
(new java.io.File(path));
}
protected void finalize()
{
System.out.println("Destructor");
try { input.close(); }
catch (IOException e)
{
e.printStackTrace();
}
}
}
|
using System;
public class MyFileStream
{
System.IO.FileStream input;
public MyFileStream(string path)
{
input = System.IO.File.Open
(path, System.IO.FileMode.Open);
}
~MyFileStream()
{
Console.WriteLine("Destructor");
input.Close();
}
}
|
Эти блоки выполняются вместе с |
|
Статические |
Статический Оформляется он как |
public class A
{
static
{
System.out.println("Loading A");
}
static int x = 1;
static
{
System.out.println("x = " + x);
x++;
}
static int y = 2;
static
{
y = x + 3;
System.out.println("x = " + x);
System.out.println("y = " + y);
}
public static void main(String[] args)
{}
}
|
using System;
public class A
{
static A()
{
Console.WriteLine("Loading A");
Console.WriteLine("x = " + x);
x++;
y = x + 3;
Console.WriteLine("x = " + x);
Console.WriteLine("y = " + y);
}
static int x = 1;
static int y = 2;
public static void Main() {}
}
|
Приведенный выше код выдает результат Loading A x = 1 x = 2 y = 5 |
Приведенный выше код выдает результат Loading A x = 1 x = 2 y = 5 |
В Java нестатические Такая конструкция используется, например, для определения классов итераторов для коллекций — объект-итератор всегда связан с объектом-коллекцией, которую он итерирует. В то же время, пользователю не нужно знать, какого именно типа данный итератор, — достаточно, что он реализует общий интерфейс всех итераторов, позволяющий проверить, есть ли еще объекты, и получить следующий объект. Получить этот объект внутри декларации вложенного типа можно с помощью конструкции При создании объекта такого вложенного класса необходимо указать объект объемлющего класса, к которому тот будет привязан. |
В C# модификатор |
public class ContainingClass
{
static int counter = 1;
static int ecounter = 1;
int id = counter++;
class EmbeddedClass
{
int eid = ecounter++;
public String toString()
{
return "" +
ContainingClass.this.id +
'.' + eid;
}
}
public String toString()
{
return "" + id;
}
public static void main
(String[] args)
{
ContainingClass
c = new ContainingClass()
, c1 = new ContainingClass();
System.out.println(c);
System.out.println(c1);
EmbeddedClass
e = c.new EmbeddedClass()
, e1 = c.new EmbeddedClass()
, e2 = c1.new EmbeddedClass();
System.out.println(e);
System.out.println(e1);
System.out.println(e2);
}
}
|
|
В C# класс может определить различные реализации для Для этого при определении таких using System;
public interface I1
{
void m();
}
public interface I2
{
void m();
}
public class A : I1, I2
{
public void m()
{
Console.WriteLine("A.m() called");
}
void I1.m()
{
Console.WriteLine
("I1.m() defined in A called");
}
void I2.m()
{
Console.WriteLine
("I2.m() defined in A called");
}
public static void Main()
{
A f = new A();
I1 i1 = f;
I2 i2 = f;
f.m();
i1.m();
i2.m();
}
}
Результат работы приведенного выше примера следующий. A.m() called I1.m() defined in A called I2.m() defined in A called |
Последовательность выполнения
О правилах, определяющих эту последовательность, можно судить по результатам работы следующих примеров.
public class A
{
static
{
System.out.println
("Static initializer of A");
}
{
System.out.println
("Initializer of A");
}
static int sinit()
{
System.out.println
("Static field initializer of A");
return 0;
}
static int init()
{
System.out.println
("Field initializer of A");
return 0;
}
static int sf = sinit();
int f = init();
public A()
{
System.out.println
("Constructor of A");
}
}
public class B extends A
{
static int sf = sinit();
int f = init();
static
{
System.out.println
("Static initializer of B");
}
{
System.out.println
("Initializer of B");
}
static int sinit()
{
System.out.println
("Static field initializer of B");
return 0;
}
static int init()
{
System.out.println
("Field initializer of B");
return 0;
}
public B()
{
System.out.println
("Constructor of B");
}
}
public class C
{
public static void main(String[] args)
{
B b = new B();
}
}
|
using System;
public class A
{
static A()
{
Console.WriteLine
("Static constructor of A");
}
static int sinit()
{
Console.WriteLine
("Static field initializer of A");
return 0;
}
static int init()
{
Console.WriteLine
("Field initializer of A");
return 0;
}
static int sf = sinit();
int f = init();
public A()
{
Console.WriteLine
("Constructor of A");
}
}
public class B : A
{
static int sf = sinit();
int f = init();
static B()
{
Console.WriteLine
("Static constructor of B");
}
static int sinit()
{
Console.WriteLine
("Static field initializer of B");
return 0;
}
static int init()
{
Console.WriteLine
("Field initializer of B");
return 0;
}
public B()
{
Console.WriteLine
("Constructor of B");
}
}
public class C
{
public static void Main()
{
B b = new B();
}
}
|
Результат работы приведенного примера такой. Static initializer of A Static field initializer of A Static field initializer of B Static initializer of B Initializer of A Field initializer of A Constructor of A Field initializer of B Initializer of B Constructor of B |
Результат работы приведенного примера такой. Static field initializer of B Static constructor of B Field initializer of B Static field initializer of A Static constructor of A Field initializer of A Constructor of A Constructor of B |
В Java элемент типа, помимо доступности, указываемой с помощью модификаторов Именно пакетная доступность используется в Java по умолчанию.
Типы, не вложенные в другие, могут быть либо |
В C# доступность элемента типа по умолчанию — Имеется дополнительный модификатор доступности — Кроме того, в C# можно использовать комбинированный модификатор Типы, не вложенные в другие, могут быть либо В C# все элементы типов могут быть помечены модификатором new для точного указания на то, что такой элемент — новый и никак не соотносится с элементами предков данного типа с тем же именем или той же сигнатурой. |
В Java для полей классов дополнительно к модификаторам доступности и контекста могут использоваться модификаторы |
Поля классов или структурных типов в C# могут иметь в качестве дополнительных модификаторов |
Модификатор Модификатор |
Модификатор Такие поля используются для представления постоянных в ходе работы программы объектов и значений, типы которых не допустимы для поддерживаемых языком |
Поля, помеченные модификатором В Java имеются соглашения о том, как должен быть оформлен интерфейс класса, объекты которого могут быть сохранены или переданы по сети — эти соглашения можно найти в документации по интерфейсу Стандартный механизм Java, обеспечивающий сохранение и восстановление объектов, реализующих интерфейс
Значение модификатора Значение модификатора |
В последних версиях обоих языков введены шаблонные, т.е. имеющие
Ниже приводятся примеры декларации шаблонного getTypeName() необязательно, поскольку он вычисляется из контекста вызова. Если вычислить типовые аргументы вызова
public class A
{
public static <T> String getTypeName
(T a)
{
if(a == null) return "NullType";
else return
a.getClass().getName();
}
public static void main(String[] args)
{
String y = "ABCDEFG";
System.out.println( getTypeName(y) );
System.out.println
( getTypeName(y.length()) );
System.out.println
( A.<Character>getTypeName
(y.charAt(1)) );
}
}
|
using System;
public class A
{
public static string getTypeName<T>
(T a)
{
if(a == null) return "NullType";
else return
a.GetType().FullName;
}
public static void Main()
{
string y = "ABCDEFG";
Console.WriteLine( getTypeName(y) );
Console.WriteLine
( getTypeName(y.Length) );
Console.WriteLine
( getTypeName<char>(y[1]) );
}
}
|
В Java в качестве типовых аргументов могут использоваться только ссылочные типы. Примитивный тип не может быть аргументом шаблона — вместо него нужно использовать соответствующий |
В C# любой тип может быть аргументом шаблона. |
В Java типовые аргументы являются элементами конкретного объекта — они фактически представляют собой набор дополнительных параметров |
В C# каждый экземпляр шаблонного класса, интерфейса или структурного типа с определенными аргументами имеет свой набор |
public class A<T>
{
public static int c = 0;
public T t;
}
public class B
{
public static void main
(String[] args)
{
A.c = 7;
System.out.println( A.c );
}
}
|
using System;
public class A<T>
{
public static int c = 0;
public T t;
}
public class B
{
public static void Main()
{
A<string>.c = 7;
Console.WriteLine( A<int>.c );
Console.WriteLine( A<string>.c );
}
}
|
В C# можно определить и использовать шаблонные public delegate bool Predicate<T>
(T value);
public class I
{
public bool m(int i)
{ return i == 0; }
public void f()
{
Predicate<int> pi = m;
Predicate<string> ps =
delegate(string s)
{ return s == null; };
}
}
|
В обоих языках имеются конструкции для указания ограничений на
Ограничения, требующие от типа-параметра наследовать некоторому другому типу, позволяют использовать
В Java можно указать, что тип-параметр данного шаблона должен быть наследником некоторого класса и/или реализовывать определенные интерфейсы. В приведенном ниже примере параметр |
В C# можно указать, что тип-параметр должен быть ссылочным, типом значения, наследовать определенному классу и/или определенным интерфейсам, а также иметь В приведенном ниже примере параметр |
public class A
{
public int m() { ... }
}
public interface B
{
public String n();
}
public class C<T extends A B>
{
T f;
public String k()
{
return f.n() + (f.m()*2);
}
}
|
public class A { ... }
public class B<T, U, V>
where T : class
where U : A, IList<T>, new()
where V : struct
{ ... }
|
Кроме того, в Java можно использовать неопределенные типовые параметры (wildcards) при описании типов. Неопределенный Неопределенные В приведенном ниже примере первый public class A
{
public void addAll
(Collection<?> c)
{ ... }
public <T> void addAll
(Collection<? extends T> c)
{ ... }
public <T> void addToCollection
(T e, Collection<? super T> c)
{ ... }
}
|
В обоих языках (в Java — начиная с версии 5) имеются конструкции, позволяющие описывать
В Java нужно указать тип элемента массива, многоточие и имя параметра. |
В C# нужно указать модификатор params, тип самого массива и имя параметра. |
public class A
{
public int f(int ... a)
{
return a.length;
}
public static void main(String[] args)
{
A a = new A();
System.out.println
( a.f(new int[]{9, 0}) );
System.out.println
( a.f(1, 2, 3, 4) );
}
}
|
using System;
public class A
{
public int f(params int[] a)
{
return a.Length;
}
public static void Main
{
A a = new A();
Console.WriteLine
( a.f(new int[]{9, 0}) );
Console.WriteLine
( a.f(1, 2, 3, 4) );
}
}
|
В Java требуется указывать некоторые Точнее, все исключения делятся на два вида — проверяемые (checked) и непроверяемые (unchecked). Непроверяемыми считаются исключения, возникновение которых может быть непреднамеренно — обращение к
Все классы проверяемых исключений, возникновение которых возможно при работе Если некоторый |
В C# исключения, возникновение которых возможно при работе |
public void m(int x)
throws MyException
{
throw new MyException();
}
public void n(int x)
throws MyException
{
m(x);
}
public void k(int x)
{
try { m(x); }
catch(MyException e)
{ ... }
}
|
|
В Java все параметры Поскольку все типы, кроме примитивных, являются |
В C# можно определить параметры Параметры, передаваемые по ссылке, помечаются модификаторов При вызове |
using System;
public class A
{
public void f
(int a, ref int b, out int c)
{
c = b - a;
b += a;
}
public static void Main()
{
A a = new A();
int n = 3, m = 0;
Console.WriteLine
("n = " + n + " , m = " + m);
a.f(1, ref n, out m);
Console.WriteLine
("n = " + n + " , m = " + m);
}
}
|
В обоих языках (в Java — начиная с версии 5) имеются встроенные средства для некоторого их расширения, для описания так называемых метаданных — данных, описывающих элементы кода. Это специальные модификаторы у типов, элементов типов и параметров
Такие данные служат для указания дополнительных
Метаданные служат встроенным механизмом расширения языка, позволяя описывать простые дополнительные
В обоих языках аннотации могут иметь структуру —
В приведенных ниже примерах определяются несколько типов аннотаций, которые затем используются для разметки элементов кода. Класс A помечен аннотацией, имеющей указанные значения n() помечен еще одной аннотацией. Параметр n() также помечен простой аннотацией.
@interface SimpleMethodAnnotation {}
@interface SimpleParameterAnnotation{}
@interface ComplexClassAnnotation
{
int id();
String author() default
"Victor Kuliamin";
String date();
}
@interface AdditionalMethodAnnotation
{
String value() default "";
}
@ComplexClassAnnotation
(
id = 126453,
date = "23.09.2005"
)
public class A
{
@SimpleMethodAnnotation
public void m() { ... }
@SimpleMethodAnnotation
@AdditionalMethodAnnotation
( value = "123" )
public void n
(@SimpleParameterAnnotation int k)
{ ... }
}
|
class SimpleMethodAttribute
: Attribute
{}
class SimpleParameterAttribute
: Attribute
{}
class ComplexClassAttribute
: Attribute
{
public int id;
public String author =
"Victor Kuliamin";
public String date;
}
class AdditionalMethodAttribute
: Attribute
{
public String value = "";
}
[ComplexClassAttribute
(
id = 126453,
date = "23.09.2005"
)]
public class A
{
[SimpleMethodAttribute]
public void m() { ... }
[SimpleMethodAttribute,
AdditionalMethodAttribute
(value = "123")]
public void n
([SimpleParameterAttribute] int k)
{ ... }
}
|
В Java аннотации могут помечать также пакеты (т.е. использоваться в директиве декларации пакета, к которому относится данный файл), декларации локальных переменных и |
В C# могут быть описаны глобальные атрибуты, помечающие сборку, в рамках кода которой они встречаются. Такие атрибуты помещаются вне описаний пространств имен. Также атрибутами могут помечаться отдельные |
Аннотационный тип декларируется с модификатором Такой тип не может иметь |
Атрибутный тип описывается как класс, наследующий Такой тип не может иметь |
При определении значений Помимо |
Позиционные параметры определяются при помощи Именованные параметры определяются как доступные для чтения и записи нестатические поля и При определении значений В атрибутном типе могут быть описаны такие же элементы, как и в обычном классе. Ниже приведен пример использования позиционного параметра. class ClassAttribute : Attribute
{
public ClassAttribute(int id)
{
this.id = id;
}
int id;
public string value;
}
[ClassAttribute(4627, value = "")]
public class A { ... }
|
|
У атрибутов может указываться цель, к которой они привязываются. Для атрибутов, помещаемых вне рамок пространств имен, указание такой цели — [assembly :
MyAttribute
(
id = 4627,
author = "Victor Kuliamin"
)]
В C# принято соглашение, согласно которому окончание Поэтому, имея атрибутный тип В последнем случае компилятор будет пытаться найти атрибутный тип с именем |
Как в Java, так и в C# возможно создание многопоточных приложений. Вообще говоря, каждая программа на этих языках представляет собой набор
Каждый java.lang.Thread в Java и System.Threading.Thread в C#). Для запуска некоторого кода в виде отдельного
В Java это можно сделать двумя способами. Первый — определить класс-наследник Другой способ — определить класс, реализующий интерфейс В обоих случаях для запуска выполнения |
В C# также можно использовать два способа. С помощью первого можно создать обычный Для этого нужно определить В первом варианте на основе этого Этот делегат передается в качестве аргумента
|
class T extends Thread
{
int id = 0;
public T(int id)
{ this.id = id; }
public void run()
{
System.out.println
("Thread " + id + " is working");
}
}
public class A
{
public static void main(String[] args)
{
Thread th1 = new T(1),
th2 = new T(2),
th3 = new Thread(
new Runnable() {
public void run()
{
System.out.println
("Runnable is working");
}
});
th1.start();
th2.start();
th3.start();
}
}
|
using System;
using System.Threading;
class T
{
int id;
public T(int id)
{ this.id = id; }
public void m()
{
Console.WriteLine
("Thread " + id + " is working");
}
}
public class A
{
static void m()
{
Console.WriteLine
("Nonparameterized thread" +
" is working");
}
static void m(object o)
{
Console.WriteLine
("Thread with object " + o +
" is working");
}
public static void Main()
{
Thread th1 = new Thread(
new ThreadStart(m)),
th2 = new Thread(
new ThreadStart(new T(1).m)),
th3 = new Thread(
new ParameterizedThreadStart(m));
th1.Start();
th2.Start();
th3.Start(2);
}
}
|
При разработке приложений, основанных на параллельном выполнении нескольких
В обоих языках имеются конструкции, которые реализуют синхронизационный примитив, называемый монитором (monitor). Монитор представляет собой объект, позволяющий
Для синхронизации используется конструкция, гарантирующая, что некоторый участок кода в каждый момент времени выполняется не более чем одним synchronized в Java или lock в C#.
public class PingPong extends Thread
{
boolean odd;
PingPong (boolean odd)
{ this.odd = odd; }
static int counter = 1;
static Object monitor = new Object();
public void run()
{
while(counter < 100)
{
synchronized(monitor)
{
if(counter%2 == 1 odd)
{
System.out.print("Ping ");
counter++;
}
if(counter%2 == 0 !odd)
{
System.out.print("Pong ");
counter++;
}
}
}
}
public static void main
(String[] args)
{
Thread th1 = new PingPong (false),
th2 = new PingPong (true);
th1.start();
th2.start();
}
}
|
using System;
using System.Threading;
public class PingPong
{
bool odd;
PingPong (bool odd) { this.odd = odd; }
static int counter = 1;
static object monitor = new object();
public void Run()
{
while(counter < 100)
{
lock(monitor)
{
if(counter%2 == 1 odd)
{
Console.Write("Ping ");
counter++;
}
if(counter%2 == 0 !odd)
{
Console.Write("Pong ");
counter++;
}
}
}
}
public static void Main()
{
Thread th1 = new Thread(
new ThreadStart(
new PingPong(false).Run)),
th2 = new Thread(
new ThreadStart(
new PingPong(true).Run));
th1.Start();
th2.Start();
}
}
|
Кроме того, в Java любой Такой модификатор эквивалентен помещению всего тела В Java также имеется стандартный механизм использования любого объекта ссылочного типа в качестве монитора для создания более сложных Для этого в классе Однако это механизм достаточно сложен в использовании и не очень эффективен. Для реализации более сложной синхронизации лучше пользоваться библиотечными классами из пакетов |
Обе платформы, как Java, так и .NET, в большой степени опираются на библиотеки готовых компонентов. Сообщество разработчиков ведет постоянную работу по выработке стандартных интерфейсов компонентов для решения различных задач в разных предметных областях. По достижении определенной степени зрелости такие интерфейсы включаются в стандартные библиотеки.
Для Java этот процесс начался значительно раньше и захватил гораздо больше участников в силу более раннего появления платформы и ее большей открытости и стандартизованности. Поэтому далеко не все часто используемые библиотеки классов распространяются в составе платформ
В данном разделе дается краткий обзор основных библиотек, подробности см. в [10] и [11].
Основные классы языка Java содержатся в пакете |
Основные классы C# содержатся в пространстве имен |
Базовым классом для всех ссылочных типов Java служит класс Этот класс содержит следующие
|
Базовым типом для всех типов C# служит класс
|
Класс Поля
Другой класс, содержащий |
Данные о среде выполнения можно получить с помощью класса В нем имеются Управлять стандартным вводом-выводом можно с помощью класса Он содержит |
Для работы со строками используются классы Класс |
Для работы со строками используются классы |
В пакете Все числовые классы наследуют классу Эти классы содержат Для форматированного представления чисел используются классы |
Обертками примитивных типов C# служат следующие структурные типы из пространства имен
Они также содержат аналогичные Для работы с форматированным строковым представлением чисел используются |
Набор математических функций и Для генерации псевдослучайных чисел можно использовать как Для более сложных вычислений можно использовать классы пакета |
Набор математических функций и Для генерации псевдослучайных чисел используется класс |
Он содержит Класс |
Классом, представляющим Текущий Этот класс тоже содержит |
В пакете
|
Аналогичные интерфейсы в C# следующие.
В C#, в отличии от Java, где шаблонные типы всегда имеют и непривязанный к типам-аргументам интерфейс, шаблоны с типизированным интерфейсом просто добавляются в библиотеки к уже существовавшим там до появления C# 2.0 нетипизированным классам и интерфейсам. |
В пакете Все эти классы, а также классы, находящиеся в пакете |
Для C# аналогичную роль в механизме рефлексии играют классы |
Для анализа структуры и элементов массивов во время работы программы можно использовать Для манипуляций с массивами — поиска, сортировки, сравнения и пр. — используются |
Аналогом обоих классов Java для работы с массивами служит класс Он содержит как |
В обоих языках имеются механизмы, позволяющие определять слабые ссылки. Объект, хранящийся по слабой ссылке, считается сборщиком мусора недоступным по ней и поэтому может быть уничтожен при очередном запуске процедуры сборки мусора, если обычных ссылок на него не осталось.
Слабые ссылки удобны для организации хранилищ объектов, которые не должны мешать их уничтожению, если эти объекты стали недоступны во всех других местах, т.е. ссылки на них остались только из такого хранилища.
Пакет Например, класс Другие классы представляют более хитрые виды ссылок. |
В C# слабые ссылки реализуются при помощи класса |
Пакет
Много полезных классов-коллекций можно найти вне стандартных библиотек, например, в библиотеке Jakarta Commons [12], части обширного проекта Apache Jakarta Project [13]. |
Набор интерфейсов и классов коллекций C# находится в пространствах имен Базовые интерфейсы коллекций — Интерфейс отображений и классы, реализующие отображения — Интерфейс и классы списков —
|
Для определения линейного порядка на объектах типа Наиболее часто применяется его реализация для строк — |
Аналогичный интерфейс в C# — Его реализация для строк — абстрактный класс |
Классы Первые два класса используются для представления информации о календаре, объекты класса Класса для представления временных интервалов в стандартной библиотеке нет. Гораздо более широкий набор средств для работы с датами и временами предоставляет библиотека Joda [14]. |
Для представления различных календарей используются подклассы Для представления моментов времени — Для интервалов времени — Для форматированного текстового представления дат и времени — |
Классы |
Аналоги в C# — Еще одни аналоги находятся в пространстве имен |
Для построения и анализа форматированных строк, содержащих данные различных типов, полезен класс С помощью реализации интерфейса |
В C# преобразование в форматированную строку осуществляется Обычно в качестве объектов, задающих форматирование, используются объекты классов |
В пакете С помощью объектов класса Currency представляют различные валюты. Информация о региональных или национально-специфических настройках и параметрах представляется в виде объектов класса Набор объектов, имеющих локализованные варианты, например, строки сообщений на разных языках, может храниться с помощью подклассов |
Аналогичную роль в C# играют классы пространства имен Для хранения наборов объектов вместе с их аналогами для нескольких культур используются объекты класса |
Работа с различными кодировками текста организуется при помощи классов пакета |
Работа с различными кодировками текста организуется при помощи классов пространства имен |
Интерфейс и классы пакета |
|
Класс Более гибкую работу с регулярными выражениями можно реализовать с помощью классов пакета |
В C# работа с регулярными выражениями может быть организована при помощи классов пространства имен |
Пакет Пакет |
Аналогичные функции выполняют классы пространства имен |
Пакет |
Аналогичные классы содержатся в пространстве имен |
Пакет |
Аналоги классов из |
Классы и интерфейсы, лежащие в основе На основе этой модели реализованы библиотеки элементов управления графического пользовательского интерфейса (graphical user interface, GUI) Java. Одна из этих библиотек (самая старая и не очень эффективная) размещается в пакете Другая, более новая и демонстрирующая большую производительность — в пакете Одной из наиболее эффективных библиотек графических элементов управления на Java на данный момент считается библиотека SWT (Standard Widget Toolkit [15]), на основе которой разрабатывается расширяемая среда разработки приложений Eclipse [16]. |
Классы и интерфейсы, лежащие в основе Эти классы, в основном, расположены в сборке System. Библиотека элементов GUI находится в пространстве имен Библиотека классов общего назначения для работы с графикой находится в пространстве |
Интерфейсы и классы для разработки сетевого ПО и организации связи между приложениями, работающими на разных машинах, находятся в пакетах |
Аналогичные классы и интерфейсы находятся в пространствах имен |
Пакеты |
Аналогичные классы и интерфейсы находятся в пространстве имен |
Пакеты |
Аналогичные библиотеки в .NET находятся в пространстве имен Определенные там интерфейсы являются основой ADO.NET. |
Пакет |
Интерфейс и реализация аналогичной службы директорий ActiveDirectory on Microsoft находятся в пространстве имен |
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.