Студопедия КАТЕГОРИИ: АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Java. Абстрактные, виртуальные, конечные и статические методы.
Статические методы и поля Для того чтобы не надо было создавать объект класса Math (и других аналогичных классов) каждый раз, когда надо вызвать sin (и другие подобные функции), введено понятие статических методов (static method; иногда в русском языке они называются статичными). Статический метод (отмечаемый словом static в описании) можно вызвать, не создавая объекта его класса. Поэтому можно писать double x = Math.sin(1); вместо
Math m = new Math(); double x = m.sin(1); Ограничение, накладываемое на статические методы, заключается в том, что в объекте this они могут обращаться только к статическим полям и методам. Статические поля имеют тот же смысл, что и в C++: каждое существует только в одном экземпляре.
Абстрактность В Java все методы являются виртуальными в терминологии C++: при вызове метода, по-разному определённого в базовом и наследующем классах, всегда производится проверка времени выполнения. Абстрактным методом (описатель abstract) в Java называется метод, для которого заданы параметры и тип возвращаемого значения, но не тело. Абстрактный метод определяется в классах-наследниках. В C++ то же самое называется чисто виртуальной функцией. Для того чтобы в классе можно было описывать абстрактные методы, сам класс тоже должен быть описан как абстрактный. Объекты абстрактного класса создавать нельзя. При описании класса Pet мы не можем задать в методе voice () никакой полезный алгоритм, поскольку у всех животных совершенно разные голоса. В таких случаях мы записываем только заголовок метода и ставим после закрывающей список параметров скобки точку с запятой. Этот метод будет абстрактным (abstract), что необходимо указать компилятору модификатором abstract . Если класс содержит хоть один абстрактный метод, то создать его экземпляры, а тем более использовать их, не удастся. Такой класс становится абстрактным, что обязательно надо указать модификатором abstract . Как же использовать абстрактные классы? Только порождая от них подклассы, в которых переопределены абстрактные методы. Зачем же нужны абстрактные классы? Не лучше ли сразу написать нужные классы с полностью определенными методами, а не наследовать их от абстрактного класса? Для ответа снова обратимся к листингу 2.2. Хотя элементы массива singer [] ссылаются на подклассы Dog, Cat, Cow , но все-таки это переменные типа Pet и ссылаться они могут только на поля и методы, описанные в суперклассе Pet . Дополнительные поля подкласса для них недоступны. Попробуйте обратиться, например, к полю k класса Dog , написав singer [0].k . Компилятор "скажет", что он не может реализовать такую ссылку. Поэтому метод, который реализуется в нескольких подклассах, приходится выносить в суперкласс, а если там его нельзя реализовать, то объявить абстрактным. Таким образом, абстрактные классы группируются на вершине иерархии классов. Кстати, можно задать пустую реализацию метода, просто поставив пару фигурных скобок, ничего не написав между ними, например: void voice(){} Получится полноценный метод. Но это искусственное решение, запутывающее структуру класса. Замкнуть же иерархию можно окончательными классами. В языке Java определение метода включает его объявление и реализацию. Определение метода всегда указывается в теле класса. Метод может иметь модификаторы доступа, возвращать значение и получать параметры. Метод может иметь следующие модификаторы: public, protected или private -модификаторы доступа; static - модификатор метода класса. abstract, final, native или synchronized. Для модификаторов доступа метода определены следующие правила: public указывает, что данный метод будет доступен везде, где доступен класс, в котором он определен; protected указывает, что данный метод будет доступен как внутри пакета, содержащего объявление класса, в котором он определен, так и внутри любого подкласса данного класса; private указывает, что данный метод не будет доступен нигде кроме тела класса, в котором он определен. По умолчанию метод считается доступным везде внутри пакета, содержащего класс, в котором он определен, и недоступным ни в каком другом подклассе указанного класса в том случае, если подкласс содержится в другом пакете. Метод, не имеющий модификатора static, называется методом экземпляра. Метод экземпляра может быть вызван только для созданного экземпляра класса или подкласса. Такой метод нельзя вызывать непосредственно, квалифицируя его именем класса. Метод, объявленный с модификатором static, называется статическим методом (или методом класса) и может быть вызван без создания экземпляра класса. Этот метод всегда вызывается непосредственно из класса. Статический метод имеет доступ к другим статическим переменным и методам данного класса. Если статический метод определен как final-метод, то он не может быть переопределен. Например: // Файл A.java package classa; public class A implements B { public A() { } static int b=1; public int Metod1(){return a;} public static int Metod2(){ return 0;} //Статический метод } interface B { final public static int a=1; // Статическая переменная int Metod1(); }
// Файл C.java package classa; public class C extends A { public C() { } static int b=3; public int Metod1(){return a;} public static int Metod2(){return 77;} public static void main(String[] args) { System.out.println(A.Metod2()); System.out.println(C.Metod2()); } } При переопределении методов их модификаторы доступа должны совпадать. Так, нельзя переопределить метод, имеющий модификатор доступа public, методом с модификатором доступа private. Абстрактный метод указывается модификатором abstract. Такой метод никогда не имеет тела метода: вместо фигурных скобок, ограничивающих тело метода, объявление метода завершается точкой с запятой. Абстрактные методы можно объявлять только в абстрактных классах или интерфейсах. Объявление абстрактного метода в классе, не имеющем модификатора abstract, приводит к ошибке компиляции. Любой подкласс абстрактного класса, который сам не является абстрактным классом, должен определять реализацию всех наследуемых не реализованных абстрактных методов. Например: public class A extends AA implements B { public A() { } public int Metod1(){return 0;} public static int Metod2(){return 0;} int Metod3(){return 0;} public int Metod4(){return 0;} } interface B { int Metod1(); abstract int Metod4(); } abstract class AA{ abstract int Metod3(); } Методы, объявленные с модификатором private, не могут быть абстрактными методами, так как они недоступны вне тела класса. Статические методы также не могут выступать в качестве абстрактных методов, так как считаются конечными и не могут быть переопределены. Объявление метода с модификатором final запрещает его последующее переопределение. Такие методы называются конечными методами, и по умолчанию считается, что private-метод всегда является конечным методом. Методы, объявленные с модификатором native, могут иметь реализацию на другом языке программирования. Эти методы используются для написания машинно-зависимого кода. native-методы в Java-программе не имеют тела метода. Synchronized-методы выполняются с блокировкой: для метода класса выполняется блокировка класса, для метода экземпляра - блокировка объекта. Это позволяет предотвратить параллельный доступ к данным из различных потоков в многопоточном приложении. Метод может возвращать значение заданного типа. В этом случае: перед именем метода указывается тип возвращаемого значения; в теле метода присутствует оператор return, определяющий возвращаемое значение. Если метод не имеет возвращаемого значения, то он должен быть объявлен с ключевым словом void, указывающим на отсутствие возвращаемого значения. Если в качестве возвращаемого значения должен быть использован массив, то после имени метода может быть указана пара квадратных скобок или список параметров и пара квадратных скобок. C# и Java. Интерфейсы. Интерфейс определяет набор методов, которые будут реализованы классом. Сам интерфейс не реализует методы. Таким образом, интерфейс — это логическая конст- рукция, которая описывает методы, не устанавливая жестко способ их реализации. В этой главе рассматриваются еще два типа данных С#: структуры и перечисления. Структуры подобны классам, за исключением того, что они обрабатываются как типы значений, а не как ссылочные типы. Перечисления — это списки именованных цело- численных констант. Структуры и перечисления вносят существенный вклад в общую копилку средств и инструментов, составляющих среду программирования С#. Интерфейсы В объектно-ориентированном программировании иногда требуется определить, что класс должен делать, а не как он будет это делать. Вы уже видели такой подход на примере абстрактного метода. Абстрактный метод определяет сигнатуру для метода, но не обеспечивает его реализации. В производном классе каждый абстрактный ме- тод, определенный базовым классом, реализуется по-своему. Таким образом, абст- рактный метод задает интерфейс для метода, но не способ его реализации. Несмотря на всю полезность абстрактных классов и методов, эту идею можно развить. В С# предусмотрена возможность полностью отделить интерфейс класса от его реализации с помощью ключевого слова interface. Интерфейсы синтаксически подобны абстрактным классам. Однако в интерфейсе ни один метод не может включать тело, т.е. интерфейс в принципе не предусматрива- ет какой бы то ни было реализации. Он определяет, что должно быть сделано, но не уточняет, как. Коль скоро интерфейс определен, его может реализовать любое коли- чество классов. При этом один класс может реализовать любое число интерфейсов. Для реализации интерфейса класс должен обеспечить теля (способы реализации) методов, описанных в интерфейсе. Каждый класс может определить собственную реа- лизацию. Таким образом, два класса могут реализовать один и тот же интерфейс раз- личными способами, но все классы поддерживают одинаковый набор методов. Следо- вательно, код, "осведомленный" о наличии интерфейса, может использовать объекты любого класса, поскольку интерфейс для всех объектов одинаков. Предоставляя про- граммистам возможность применения такого средства программирования, как интер- фейс, С# позволяет в полной мере использовать аспект полиморфизма, выражаемый как "один интерфейс — много методов". Интерфейсы объявляются с помощью ключевого слова i n t e r f a c e . Вот как выгля- дит упрощенная форма объявления интерфейса: i n t e r f a c e имя{ тип_возврата имя_метода1 {список_параметров) ; тип_возврата имя_метода2 {список_параметров) ; // . . . тип_возврата имя_методаЫ(список_параметров) ; } Имя интерфейса задается элементом имя. Методы объявляются с использованием лишь типа возвращаемого ими значения и сигнатуры. Все эти методы, по сути, — аб- страктные. Как упоминалось выше, для методов в интерфейсе не предусмотрены спо- собы реализации. Следовательно, каждый класс, который включает интерфейс, дол- жен реализовать все его методы. В интерфейсе методы неявно являются открытыми (public-методами), при этом не разрешается явным образом указывать спецификатор доступа. Рассмотрим пример интерфейса для класса, который генерирует ряд чисел. public interface ISeries { int getNext(); // Возвращает следующее число ряда, void reset (); // Выполняет перезапуск, void setStart(int x) ; // Устанавливает начальное // значение. Этот интерфейс имеет имя ISeries. Хотя префикс " I " необязателен, • многие программисты его используют, чтобы отличать интерфейсы от классов. Интерфейс ISeries объявлен открытым, поэтому он может быть реализован любым классом в любой программе. Помимо сигнатур методов интерфейсы могут объявлять сигнатуры свойств, индек- саторов и событий. События рассматриваются в главе 15, поэтому здесь мы остано- вимся на методах, индексаторах и свойствах. Интерфейсы не могут иметь членов дан- ных. Они не могут определять конструкторы, деструкторы или операторные методы. Кроме того, ни один член интерфейса не может быть объявлен статическим. Реализация интерфейсов Итак, если интерфейс определен, один или несколько классов могут его реализо- вать. Чтобы реализовать интерфейс, нужно указать его имя после имени класса по- добно тому, как при создании производного указывается базовый класс. Формат запи- си класса, который реализует интерфейс, таков: class имя_класса : имя__интерфейса { // тело класса Нетрудно догадаться, что имя реализуемого интерфейса задается с помощью эле- мента имя_интерфейса. Если класс реализует интерфейс, он должен это сделать в полном объеме, т.е. реа- лизация интерфейса не может быть выполнена частично. Классы могут реализовать несколько интерфейсов. В этом случае имена интерфей- сов отделяются запятыми. Класс может наследовать базовый класс и реализовать один или несколько интерфейсов. В этом случае список интерфейсов должно возглавлять имя базового класса. Методы, которые реализуют интерфейс, должны быть объявлены открытыми. Дело в том, что методы внутри интерфейса неявно объявляются открытыми, поэтому их реализации также должны быть открытыми. Кроме того, сигнатура типа в реализации метода должна в точности совпадать с сигнатурой типа, заданной в определении ин- терфейса. Рассмотрим пример реализации интерфейса ISeries, объявление которого приве- дено выше. Здесь создается класс с именем ByTwos, генерирующий ряд чисел, в кото- эом каждое следующее число больше предыдущего на два. // Реализация интерфейса ISeries, class ByTwos : ISeries { int start; int val; public ByTwos() { start = 0; val = 0; public int getNext() { val += 2; return val; public void reset() { val = start; public void setStart(int x) { start = x; val = start; Как видите, класс ByTwos реализует все три метода, определенные интерфейсом ISeries. Иначе и быть не может, поскольку классу не разрешается создавать частич- ную реализацию интерфейса. Рассмотрим пример, демонстрирующий использование класса ByTwos. Вот его код: // Демонстрация использования интерфейса, // реализованного классом ByTwos. using System; class SeriesDemo { public static void Main() { ByTwos ob = new ByTwos() ; for(int i=0; i < 5; Console.WriteLine("Следующее значение равно " + ob.getNext()); Console.WriteLine("ХпПереход в исходное состояние.") ob.reset (); for(int i=0; i < 5; i++) Console.WriteLine("Следующее значение равно " + ob.getNext()); Console.WriteLine("ХпНачинаем с числа 100."); ob.setStart (100); for(int i=0; i < 5; i++) Console.WriteLine("Следующее значение равно " +" ob.getNext()); Чтобы скомпилировать программу SeriesDemo, необходимо включить в процесс компиляции файлы, которые содержат классы ISeries, ByTwos и SeriesDemo. Для создания выполняемой программы компилятор автоматически скомпилирует все три файла. Если эти файлы называются, например, ISeries, cs, ByTwos. cs и SeriesDemo.es, то программа скомпилируется посредством выполнения такой ко- мандной строки: I >csc SeriesDemo.cs ISeries.cs ByTwos.cs В классах, которые реализуют интерфейсы, можно определять дополнительные члены. Например, в представленную ниже версию класса ByTwos добавлен метод getPrevious (), который возвращает предыдущее значение ряда. // Реализация интерфейса ISeries с дополнительно // определяемым методом getPrevious(). class ByTwos : ISeries { int start; int val; int prev; public ByTwos() { start = 0; val = 0; prev = -2; } public int getNextO { prev = val; val += 2; return val; } public void reset() { val = start; prev = start - 2; } public void setStart(int x) { start = x; val = start; prev = val - 2; } // Метод, не объявленный в интерфейсе ISeries, public int getPrevious() { return prev; Обратите внимание на то, что добавление метода getPrevious () потребовало внесения изменений в реализацию методов, определенных интерфейсом ISeries. Но поскольку интерфейс для этих методов остается прежним, при изменении не разру- шается код, написанный ранее. В этом и заключается одно из достоинств использова- ния интерфейсов. Как упоминалось выше, интерфейс может реализовать любое количество классов. Рассмотрим, например, класс Primes, который генерирует ряд простых чисел. Обра- тите внимание на то, что его способ реализации интерфейса ISeries в корне отлича- ется от используемого классом ByTwos. // Использование интерфейса ISeries для реализации // ряда простых чисел. class Primes : ISeries { int start; int val; public Primes () { start = 2; val = 2; } public int getNext() { int i, j; bool isprime; for(i = val; i < 1000000; isprime = true; for(j = 2; j < (i/j + 1) if((i%j)==0) { isprime = false; break; if(isprime) { val = i; break; return val; public void reset() { val = start; public void setStart(int x) { start = x; val = start; Здесь важно понимать, что, хотя классы Primes и ByTwos генерируют разные ряды чисел, оба они реализуют один и тот же интерфейс ISeries. И в этом нет ничего удивительного, поскольку каждый класс волен решить эту задачу так, как "считает" нужным.
Интерфейсы Java
C#. Структуры. Структура (structure) – тип-значение · экземпляр размещается в стеке или непосредственно в другом экземпляре · поля экземпляра размещаются в самом экземпляре · переменная — не указатель · нет разыменования · не выполняется сборка мусора · при присваивании выполняется копирование Синтаксис · Объявление struct ИмяСтруктуры : Интерфейсы { // тело структуры } · Создание ИмяСтруктуры имяПеременной = new ИмяСтруктуры(); ИмяСтруктуры имяПеременной; Правила · неявно наследуются от System.ValueType (наследник System.Object); изменить базовый тип нельзя · неявно объявлены как sealed · могут реализовывать интерфейсы (пользоваться следует осторожно) · не могут содержать конструктор по умолчанию и деструктор · неприменимы модификаторы abstract, virtual, protected Пример struct Point { private double _x; private double _y; public double X { ... } public double Y { ... } public void Offset(double x, double y) { ... } } Point p = new Point(); p.X = -1; p.Y = 10; Console.WriteLine(p); void Console.WriteLine(object value) Использование структур · Главная обязанность – хранение данных · Тип ведет себя подобно элементарному · Тип не будет наследоваться · У типа не будет производных типов · Тип не будет полиморфным · Размер экземпляров мал (16 байт) · Экземпляры не передаются в качестве параметров и не возвращаются из метода Неизменяемые структуры · Представляют неизменяемые атомарные значения · Все поля – readonly · У всех свойств – только аксессор get · Набор перегруженных конструкторов Структуры определяются с помощью ключевого слова struct, например: Язык C# public struct PostalAddress { // Fields, properties, methods and events go here... } Структуры используют большую часть того же синтаксиса, что и классы, однако они более ограничены по сравнению с ними. В объявлении структуры поля не могут быть инициализированы до тех пор, пока они будут объявлены как постоянные или статические. Структура может не объявлять используемый по умолчанию конструктор (конструктор без параметров) или деструктор. Структуры не могут наследовать от классов или других структур. Структуры копируются при присваивании. При присваивании структуры к новой переменной выполняется копирование всех данных, а любое изменение новой копии не влияет на данные в исходной копии. Структуры являются типами значений, а классы — ссылочными типами. В отличие то классов, структуры могут быть созданы без использования нового оператора. Структуры могут объявлять конструкторы, имеющие параметры. Структура не может быть унаследованной от другой структуры или класса и не может быть основой для других классов. Все структуры наследуют непосредственно от System.ValueType, который наследует от System.Object. Структуры могут реализовывать интерфейсы. Структура может использоваться как тип, допускающий значение NULL, и ей можно назначить значение NULL.
C#. Перечисления. Понятие и синтаксис Перечисление (enumeration, enumerated type) – множество именованных целочисленных констант enum Имя : ОсновнойТип { Константа = значение, ... } Правила · неявно наследуются от System.Enum (наследник System.ValueType); изменить базовый тип нельзя · каждая константа — целое число · неявное преобразование к целому типу не определено · неприменимы модификаторы abstract, virtual, protected Пример enum Color : byte { None = 0, Red, Green = 3, Blue } Color color = Color.Green; На самом деле struct Color : System.Enum { public const Color None = (Color)0, public const Color Red = (Color)1, public const Color Green = (Color)3, public const Color Blue = (Color)4 public Byte value__; } Ключевое слово enum используется для объявления перечисления, отдельного типа, состоящего из набора именованных констант, который называется списком перечислителя. Обычно лучше всего определять перечисление прямо в пространстве имен, чтобы всем классам в пространстве имен было одинаково удобно получить к нему доступ. Однако перечисление также может быть вложено в классе или структуре. По умолчанию первому перечислителю задано значение, равное нулю, а значение каждого последующего оператора увеличивается на 1. Например: enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri}; В этом перечислении Sat имеет значение 0, Sun — 1, Mon — 2 и т. д. Для переопределения значений по умолчанию перечисления могут иметь инициализаторы. Пример. enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};
В этом перечислении последовательность элементов принудительно начинается с 1, а не с 0. Однако настоятельно рекомендуется, чтобы перечисление содержало константу со значением 0. Дополнительные сведения см. в разделе Типы перечислений (руководство по программированию в C#). Каждый тип перечисления имеет базовый тип, который может быть любым целым типом, исключая char. По умолчанию базовым типом элементов перечисления является int. Чтобы объявить перечисление другого целого типа, например byte, после идентификатора поставьте двоеточие, а затем укажите тип. enum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri}; Для перечисления утверждены следующие типы: byte, sbyte, short, ushort, int, uint, long или ulong. Переменной типа Days может быть назначено любое значение, входящее в диапазон базового типа; значения не ограничены именованными константами. Значением по умолчанию enum E является значение, созданное выражением (E)0.Примечание. Имя перечислителя может не содержать пробелов. Базовый тип указывает размер хранилища, выделенного для каждого перечислителя. Однако для преобразования из типа enum в целочисленный тип необходимо явное приведение. Например, следующий оператор назначает перечислитель Sun переменной типа int, используя приведение для преобразования enum в int: int x = (int)Days.Sun; При применении System.FlagsAttribute к перечислению, содержащему несколько элементов в сочетании с битовой операцией OR, будет заметно, что атрибут оказывает влияние на действие enum, когда оно используется с некоторыми инструментами. Эти изменения можно заметить при использовании таких средств, как методы класса Console, вычислитель выражений и т. д. |
|||
Последнее изменение этой страницы: 2018-05-27; просмотров: 225. stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда... |