Классы. Объектно-ориентированное программирование
Классы и объекты
C# является полноценным объектно-ориентированным языком. Это значит, что программу на C# можно представить в виде взаимосвязанных взаимодействующих между собой объектов.
Описанием объекта является класс , а объект представляет экземпляр этого класса. Можно еще провести следующую аналогию. У нас у всех есть некоторое представление о человеке, у которого есть имя, возраст, какие-то другие характеристики. То есть некоторый шаблон — этот шаблон можно назвать классом. Конкретное воплощение этого шаблона может отличаться, например, одни люди имеют одно имя, другие — другое имя. И реально существующий человек (фактически экземпляр данного класса) будет представлять объект этого класса.
В принципе ранее уже использовались классы. Например, тип string , который представляет строку, фактически является классом. Или, например, класс Console , у которого метод WriteLine() выводит на консоль некоторую информацию. Теперь же посмотрим, как мы можем определять свои собственные классы.
По сути класс представляет новый тип, который определяется пользователем. Класс определяется с помощью ключевого слова сlass :
После слова class идет имя класса и далее в фигурных скобках идет собственно содержимое класса. Например, определим в файле Program.cs класс Person, который будет представлять человека:
Однако такой класс не особо показателен, поэтому добавим в него некоторую функциональность.
Поля и методы класса
Класс может хранить некоторые данные. Для хранения данных в классе применяются поля . По сути поля класса — это переменные, определенные на уровне класса.
Кроме того, класс может определять некоторое поведение или выполняемые действия. Для определения поведения в классе применяются методы.
Итак, добавим в класс Person поля и методы:
В данном случае в классе Person определено поле name , которое хранит имя, и поле age , которое хранит возраст человека. В отличие от переменных, определенных в методах, поля класса могут иметь модификаторы, которые указываются перед полем. Так, в данном случае, чтобы все поля были доступны вне класса Person поля определены с модификатором public .
При определении полей мы можем присвоить им некоторые значения, как в примере выше в случае переменной name . Если поля класса не инициализированы, то они получают значения по умолчанию. Для переменных числовых типов это число 0.
Также в классе Person определен метод Print() . Методы класса имеют доступ к его поля, и в данном случае обращаемся к полям класса name и age для вывода их значения на консоль. И чтобы этот метод был виден вне класса, он также определен с модификатором public .
Создание объекта класса
После определения класса мы можем создавать его объекты. Для создания объекта применяются конструкторы . По сути конструкторы представляют специальные методы, которые называются так же как и класс, и которые вызываются при создании нового объекта класса и выполняют инициализацию объекта. Общий синтаксис вызова конструктора:
Сначала идет оператор new , который выделяет память для объекта, а после него идет вызов конструктора .
Конструктор по умолчанию
Если в классе не определено ни одного конструктора (как в случае с нашим классом Person), то для этого класса автоматически создается пустой конструктор по умолчанию, который не принимает никаких параметров.
Теперь создадим объект класса Person:
Для создания объекта Person используется выражение new Person() . В итоге после выполнения данного выражения в памяти будет выделен участок, где будут храниться все данные объекта Person. А переменная tom получит ссылку на созданный объект, и через эту переменную мы можем использовать данный объект и обращаться к его функциональности.
Обращение к функциональности класса
Для обращения к функциональности класса — полям, методам (а также другим элементам класса) применяется точечная нотация точки — после объекта класса ставится точка, а затем элемент класса:
Например, обратимся к полям и методам объекта Person:
Источник
Создание экземпляра класса.
Для того чтобы создать экземпляр класса необходимо объявить переменную, тип которой соответствует имени класса или суперкласса. После чего нужно присвоить этой переменной значение, вызвав конструктор создаваемого класса с помощью оператора new. Например, можно создать экземпляр класса Rectangle следующим образом:
Rectangle rect = new Rectangle(20, 10); |
После этого можно вызывать методы этого класса для объекта rect, указав имя метода через точку:
rect.setWidth(20); System.out.println(«Новаяширина: «+rect.getWidth()); |
ВАРИАНТЫ ЗАДАНИЙ
1. Создать класс, описывающий модель окружности (Circle). В классе должны быть описаны нужные свойства окружности и методы для получения, изменения этих свойств. Протестировать работу класса в классе CircleTest, содержащим метод статический main(String[] args).
2. Создать класс, описывающий тело человека(Human). Для описания каждой части тела создать отдельные классы(Head, Leg, Hand). Описать необходимые свойства и методы для каждого класса. Протестировать работу класса Human.
3. Создать класс, описывающий книгу(Book). В классе должны быть описаны нужные свойства книги(автор, название, год написания и т. д.)и методы для получения, изменения этих свойств. Протестировать работу класса в классе BookTest, содержащим метод статический main(String[] args).
ЛАБОРАТОРНАЯ РАБОТА №3
НАСЛЕДОВАНИЕ В JAVA
ЦЕЛЬ ЛАБОРАТОРНОЙ РАБОТЫ:
Цель данной лабораторной работы — изучить понятие наследования, и научиться реализовывать наследование в Java.
ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ:
Одним из ключевых аспектов объектно-ориентированного программирования является наследование. С помощью наследования можно расширить функционал уже имеющихся классов за счет добавления нового функционала или изменения старого. Например, имеется следующий класс Person, описывающий отдельного человека:
public class Person < private String name; private String surname; public String getName() < return name; >public String getSurname() < return surname; >public Person(String name, String surname) < this.name=name; this.surname=surname; >public void displayInfo() < System.out.println("Имя: " + name + " Фамилия: " + surname); >> |
И, возможно, впоследствии мы решили расширить имеющуюся систему и классов, добавив в нее класс, описывающий сотрудника предприятия — класс Employee. Так как этот класс реализует тот же функционал, что и класс Person, так как сотрудник — это также и человек, то было бы рационально сделать класс Employee производным (или наследником) от класса Person, который, в свою очередь, называется базовым классом или родителем:
Чтобы объявить один класс наследником от другого, надо использовать после имени класса-наследника ключевое слово extends, после которого идет имя базового класса. Для класса Employee базовым является Person, и поэтому класс Employee наследует все те же поля и методы, которые есть в классе Person.
В классе Employee могут быть определены свои методы и поля, а также конструктор. Способность к изменению функциональности, унаследованной от базового класса, назвывается полиморфизмом и является одинм из ключевых аспектов объектно-ориентированного программирования наряду с наследованием и инкапсуляцией.
Например, переопределим метод displayInfo() класса Person в классе Employee:
class Employee extends Person < private String company; public Employee(String name, String surname, String company) < super(name, surname); this.company=company; >public void displayInfo() < super.displayInfo(); System.out.println("Компания: " + company); >> |
Класс Employee определяет дополнительное поле для хранения компании, в которой работает сотрудник. Кроме того, оно также устанавливается в конструкторе.
Так как поля name и surname в базовом классе Person объявлены с модификатором private, то мы не можем к ним напрямую обратиться из класса Employee. Однако в данном случае нам это не нужно. Чтобы их установить, мы обращаемся к конструктору базового класса с помощью ключевого слова super, в скобках после которого идет перечисление передаваемых аргументов.
С помощью ключевого слова super мы можем обратиться к любому члену базового класса — методу или полю, если они не определены с модификатором private.
Также в классе Employee переопределяется метод displayInfo() базового класса. В нем с помощью ключевого super также идет обращение к методу displayInfo(), но уже базового класса, и затем выводится дополнительная информация, относящаяся только к Employee.
Используя обращение к методом базового класса, можно было бы переопределить метод displayInfo() следующим образом:
public void displayInfo() |
При этом нам необязательно переопределять все методы базового класса. Например, в данном случае мы не переопределяем методы getName() и getSurname(). Поэтому для этих методов класс-наследник будет использовать реализацию из базового класса. И в основной программе мы можем эти методы использовать:
public static void main(String[] args) |
Хотя наследование очень интересный и эффективный механизм, но в некоторых ситуациях его применение может быть нежелательным. И в этом случае можно запретить наследование с помощью ключевого слова final. Например:
public final class Person |
Если бы класс Person был бы определен таким образом, то следующий код был бы ошибочным и не сработал, так как мы тем самым запретили наследование:
classEmployeeextendsPerson |
Кроме запрета наследования можно также запретить переопределение отдельных методов. Например, в примере выше переопределен метод displayInfo(), запретим его переопределение:
public class Person < //. public final void displayInfo()< System.out.println("Имя: " + name + " Фамилия: " + surname); >> |
В этом случае в классе Employee надо будет создать метод с другим именем для вывода информации об объекте.
Абстрактные классы
Кроме обычных классов в Java есть абстрактные классы. Абстрактный класс похож на обычный класс. В абстрактном классе также можно определить поля и методы, в то же время нельзя создать объект или экземпляр абстрактного класса. Абстрактные классы призваны предоставлять базовый функционал для классов-наследников. А производные классы уже реализуют этот функционал.
При определении абстрактных классов используется ключевое слово abstract:
public abstract class Human < privateint height; private double weight; publicintgetHeight() < return height; >public double getWeight() < return weight; >> |
Кроме обычных методов абстрактный класс может содержать абстрактные методы. Такие методы определяются с помощью ключевого слова abstract и не имеют никакого функционала:
public abstractvoiddisplayInfo(); |
Производный класс обязан переопределить и реализовать все абстрактные методы, которые имеются в базовом абстрактном классе. Также следует учитывать, что если класс имеет хотя бы один абстрактный метод, то данный класс должен быть определен как абстрактный.
Зачем нужны абстрактные классы? Допустим, мы делаем программу для обсулживания банковских операций и определяем в ней три класса: Person, который описывает человека, Employee, который описывает банковского служащего, и класс Client, который представляет клиента банка. Очевидно, что классы Employee и Client будут производными от класса Person, так как оба класса имеют некоторые общие поля и методы. И так как все объекты будут представлять либо сотрудника, либо клиента банка, то напрямую мы от класса Person создавать объекты не будем. Поэтому имеет смысл сделать его абстрактным.
public abstract class Person < private String name; private String surname; public String getName() < return name; >public String getSurname() < return surname; >public Person(String name, String surname) < this.name=name; this.surname=surname; >public abstract void displayInfo(); > class Employee extends Person < private String bank; public Employee(String name, String surname, String company) < super(name, surname); this.bank=company; >public void displayInfo() < System.out.println("Имя: " + super.getName() + " Фамилия: " + super.getSurname() + " Работает в банке: " + bank); >> class Client extends Person < private String bank; public Client(String name, String surname, String company) < super(name, surname); this.bank=company; >public void displayInfo() < System.out.println("Имя: " + super.getName() + " Фамилия: " + super.getSurname() + " Клиентбанка: " + bank); >> |
ВАРИАНТЫ ЗАДАНИЙ
1. Создать абстрактный класс, описывающий посуду(Dish). С помощью наследования реализовать различные виды посуды, имеющие свои свойства и методы. Протестировать работу классов.
2. Создать абстрактный класс, описывающий собак(Dog). С помощью наследования реализовать различные породы собак. Протестировать работу классов.
3. Создать абстрактный класс, описывающий мебель. С помощью наследования реализовать различные виды мебели. Также создать класс FurnitureShop, моделирующий магазин мебели. Протестировать работу классов.
ЛАБОРАТОРНАЯ РАБОТА №4
ИНТЕРФЕЙСЫВ JAVA .
ЦЕЛЬ ЛАБОРАТОРНОЙ РАБОТЫ:
Цель данной лабораторной работы — изучить понятие интерфейса, научиться создавать интерфейсы в Java и применять их в программах.
ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ:
Механизм наследования очень удобен, но он имеет свои ограничения. В частности мы можем наследовать только от одного класса, в отличие, например, от языка С++, где имеется множественное наследование.
В языке Java подобную проблему позволяют решить интерфейсы. Интерфейсы определяют некоторый функционал, не имеющий конкретной реализации, который затем реализуют классы, применяющие эти интерфейсы. И один класс может применить множество интерфейсов.
Чтобы определить интерфейс, используется ключевое слово interface.
Определим следующий интерфейс:
public interface Printable |
Интерфейс может определять различные методы, которые, так же как и абстрактные методы абстрактных классов не имеют реализации. В данном случае объявлен только один метод.
Все методы интерфейса не имеют модификаторов доступа, но фактически по умолчанию доступ public, так как цель интерфейса — определение функционала для реализации его классом. Поэтому весь функционал должен быть открыт для реализации.
И также при объявлении интерфейса надо учитывать, что только один интерфейс в файле может иметь тип доступа public. А его название должно совпадать с именем файла. Остальные интерфейсы (если такие имеются в файле java) не должны иметь модификаторов доступа.
Интерфейс может определять различные методы, которые, так же как и абстрактные методы абстрактных классов не имеют реализации. В данном случае объявлен только один метод.
Все методы интерфейса не имеют модификаторов доступа, но фактически по умолчанию доступ public, так как цель интерфейса — определение функционала для реализации его классом. Поэтому весь функционал должен быть открыт для реализации.
И также при объявлении интерфейса надо учитывать, что только один интерфейс в файле может иметь тип доступа public. А его название должно совпадать с именем файла. Остальные интерфейсы (если такие имеются в файле java) не должны иметь модификаторов доступа.
Чтобы класс применил интерфейс, надо использовать ключевое слово implements:
class Book implements Printable < String name; String author; int year; Book(String name, String author, int year)< this.name = name; this.author = author; this.year = year; >public void print() < System.out.printf("Книга '%s' (автор %s) былаизданав %d году \n", name, author, year); >> |
При этом надо учитывать, что если класс применяет интерфейс, то он должен реализовать все методы интерфейса, как в случае выше реализован метод print.
Потом в главном классе мы можем использовать данный класс и его метод print:
Book b1 = new Book(«Война и мир», «Л. Н. Толстой», 1863); b1.print(); |
В тоже время мы не можем напрямую создавать объекты интерфейсов, поэтому следующий код не будет работать:
Printable pr = new Printable(); pr.print(); |
Одним из преимуществ использоваия интерфейсов является то, что они позволяют добавить в приложение гибкости. Например, в дополнение к классу Book определим еще один класс, который будет реализовывать интерфейс Printable:
public class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >public void print() < System.out.printf("Журнал '%s'\n", name); >> |
Класс Book и класс Journal связаны тем, что они реализуют интерфейс Printable. Поэтому мы динамически в программе можем создавать объекты Printable как экземпляры обоих классов:
Printable printable = new Book(«Войнаимир», «Л. Н. Толстой», 1863); printable.print(); printable = new Journal(«Хакер»); printable.print(); |
И также как и в случае с классами, интерфейсы могут использоваться в качестве типа параметров метода или в качестве возвращаемого типа:
public static void main(String[] args) < Printable printable = createPrintable("Компьютерра",false); printable.print(); read(new Book("Отцы и дети", "И. Тургенев", 1862)); read(new Journal("Хакер")); >static void read(Printable p) < p.print(); >static Printable createPrintable(String name, boolean option) < if(option) return new Book(name, "неизвестен", 2015); else return new Journal(name); |
Метод read() в качестве параметра принимает объект интерфейса Printable, поэтому в этот метод мы можем передать как объект Book, так и объект Journal.
Метод createPrintable() возвращает объект Printable, поэтому также мы вожем возвратить как объект Book, так и Journal.
ВАРИАНТЫ ЗАДАНИЙ
1. Создать интерфейс Nameable, с методом getName(), возвращающим имя объекта, реализующего интерфейс. Проверить работу для различных объектов (например, можно создать классы, описывающие разные сущности, которые могут иметь имя: планеты, машины, животные и т. д.).
2. Реализовать интерфейс Priceable, имеющий метод getPrice(), возвращающий некоторую цену для объекта. Проверить работу для различных классов, сущности которых могут иметь цену.
Источник