Классы или опять ООП на примере Java

Сегодня я решил чуть подробнее написать о классах. Это тема явно животрепещущая. Вопросы на эту тему не раз встречались в комментах ко многим статьям в этом блоге. Вопросы на эту тему я часто слышу от своих студентов. И очень хорошо вижу, что многие из них не до конца понимают эту тему. Итак, попробуем разобраться как можно более досконально в этом не очень сложном для понимания вопросе.

И начнем, пожалуй, издалека. Что вообще такое объектно-ориентированное программирование и для чего оно нужно? Чтобы ответить на этот вопрос, давайте посмотрим вокруг. Все, что находится рядом с нами — это объекты. Например на моем столе лежит ноутбук, на котором я набираю эту статью, телефон, на котором я тестирую свои программы, тарелка на которой лежали оладьи и т.д. Каждая из этих вещей обладает какими-то свойствами, каждая из чего-то состоит, каждая в той или иной степени сложна и обладает какими-то возможностями. Таким образом делаем вывод:

Наш мир состоит из объектов. Мы привыкли обращаться с объектами.

Было бы не плохо перенести привычные нам концепции в программирование, правда? Собственно именно именно для этого и появилось ООП. Глубоко вдаваться в теоретические основы не буду. Что такое полиморфизм, наследование, инкапсуляция, абстракция вы можете подробно узнать из wikipedia :-).

Я постараюсь объяснить эти понятия на пальцах (на примерах из жизни).

Наследование — предполагает создание новых классов на основе уже существующих. Например вы разработали свою электробритву. Начали ее производство, а потом решили добавить к ней триммер. Вы не трогаете основную реализацию вашего устройства «электробритва», вы просто добавляете к ней триммер, внося в нее минимальные изменения. При этом основной функционал бритвы не изменяется. таким образом вы унаследовали от класса «бритва» весь имеющийся функционал в класс «бритва с триммером», расширили его и стали создавать новые объекты :).

Теперь пример с кодом:

	class Shaver{
		int mRazors;
		Shaver(int razorCount){
			this.mRazors=razorCount;
		}

		void shave(int stubble){
			while(stubble>=mRazors){
				stubble-=this.mRazors;
			}
		}
	}

Попробуем его разобрать как можно подробнее. Итак, у нас есть забавный класс бритва (Shaver). У класса, как водится, есть одноименный конструктор, переменная mRazors в которой хранится количество лезвий и метод shave(). Давайте попробуем разобраться что будет происходить, когда мы создаем экземпляр этого класса например вот так:

Shaver myShaver = new Shaver(3);

Как я уже писал здесь, описание класса — есть ни что иное как шаблон, по которому будут создаваться объекты. Итак, по описанию создастся экземпляр класса Shaver (т.е. будут инициализированы все переменные, описанные в классе), а затем вызовется конструктор, который у нас принимает в качестве параметра целочисленное значение — количество лезвий. У нас лезвий три штуки. В конструкторе присутствует такая запись:

this.mRazors=razorCount;

То есть новосозданной бритве мы передали три лезвия. А что такое слово this? Это указатель на текущий объект. Т.е. в данном случае на конкретный экземпляр класса Shaver, для которого вызывается конструктор. По сути это аналог такой вот записи:

mRazors=razorCount;

Для чего же тогда нужно это this в принципе? А представим себе такой вот код:

	Shaver(int mRazors){
		this.mRazors=mRazors;
	}

Здесь у нас в качестве параметра в конструктор передается переменная с таким же именем, как и поле класса. И как теперь компилятору понять куда что присваивать? А используя указатель на текущий объект this достаточно просто определить что именно и куда мы хотим присвоить.

Ну и собственно сам процесс бритья описан в методе shave(). Передаем в него в качестве параметра щетину и начинаем брить до тех пор, пока щетина больше чем ноль. Кстати, можем чего лишнего отхватить! 🙂

Но возвращаемся к наследованию. Давайте таки попробуем наследовать наш класс. Для этого создаем вот такой вот:

	class ShaverWithTrimmer extends Shaver{

		ShaverWithTrimmer(int razorCount) {
			super(razorCount);
			this.mRazors++;
		}

	}

Это наш класс — наследник бритва с триммером и говорит нам об этом ключевое слово extends. Ура! Что есть в этом классе? Пока что только другой конструктор. А что значит непонятное super()? Это просто вызов конструктора из родительского класса. Т.е. когда мы создадим объект «бритва с триммером» например вот так:

ShaverWithTrimmer myTrimShaver = new ShaverWithTrimmer(3)

произойдет примерно следующее, создастся экземпляр класса ShaverWithTrimmer который унаследует все поля и методы от своего класса родителя. Затем вызовется конструктор класса ShaverWithTrimmer в котором вызовется конструктор родительского класса, а затем после выполнения конструктора родительского класса количество лезвий в нашей бритве увеличится на 1 (это как раз то самое лезвие триммера 🙂 ) (Ух какое предложение, сам офигеваю 🙂 )

Теперь давайте поменяем процесс бритья. Сделаем отдельный метод trim() — подравнять. И переопределим метод бритья. Выглядеть наш класс теперь будет вот так:

	class ShaverWithTrimmer extends Shaver {

		ShaverWithTrimmer(int razorCount) {
			super(razorCount);
			this.mRazors++;
		}

		void trim() {
			// Тут как-то ровняем
		}

		@Override
		void shave(int stubble) {
			super.shave(stubble);
			this.trim();
	}
	}

Давайте теперь разберемся. @Override — указывает компилятору что мы хотим перегрузить переопределить метод класса родителя. Что такое super.shave(subble)? Это мы вызвали метод класса родителя (фактически Shaver.shave()). А затем this.trim() — подровняли :-). Вот оно наследование в действии.

Полиморфизм — предполагает что по-разному реализованными объектами можно управлять используя один и тот же интерфейс независимо от того как реализованы эти объекты. Возьмем в качестве примера все многообразие мобильных телефонов. Вы можете единообразно совершать звонки с любого телефона: набрать на клавиатуре номер (клавиатура, кстати, может быть реализована в виде обычных кнопок или кнопок на тачскрине), нажать кнопку вызова и телефон постарается позвонить на набранный вами номер. И абсолютно не важно кто производитель телефона, какие у него кнопки и т.д. ведь все телефоны обладают сходим интерфейсом, а значит мы легко можем управлять любым из них зная основные принципы работы с телефонами.

Ну и вернемся к нашему примеру с бритвами в котром возможен такой вот код:

		Shaver [] shavers = new Shaver[2];
		shavers[0] = new Shaver(3);
		shavers[1] = new ShaverWithTrimmer(3);
		for (Shaver s: shavers){
			s.shave(0);
		}

Что тут происходит? Прежде всего создается массив бритв из двух элементов. В первый элемент мы записываем новую бритву без триммера, во второй новую бритву с триммером. Оп-па! Вроде же бритва с триммером и бритва без триммера это разные вещи? А мы так легко их обе запихали в массив элементы которого — просто бритва без триммера. А дело в том, что бритва с триммером наследуется у класса бритва. А значит такое присвоение возможно. Правда напрямую использовать мы можем только те методы и поля, которые есть у класса бритва (но это логично, ведь у класса бритва нет метода trim() вообще!). Дальше в цикле для всех элементов массива мы вызываем метод shave(). (т.е. управляем различными объектам с разной реализацией с помощью одного интерфейса) А самое интересное то, что для для простой бритвы вызовется метод shave из класса Shaver, а для бритвы с триммером метод Shave из класса ShaverWithTrimmer! И это очень удобно!

Т.е. объекты реализованы по разному, а управляются с использованием одного и того же метода, хотя в каждом случае происходят разные вещи (в первом случае ничего не ровняется а во втором еще и подравниваем).

Инкапсуляция — предполагает что вы можете не знать, как именно работает тот или иной объект для того чтобы пользоваться им. Например вам вовсе не обязательно знать детали реализации архитектуры процессора в вашем телефоне или планшете, для того чтоб играть на нем в Angry Birds. Т.е. инкапсуляция — это когда вы скрываете от пользователя то как именно реализованы ваши классы, выставляя напоказ лишь интерфейс для взаимодействия с классом. Ну здесь я думаю примеров из кода даже не надо 🙂

Фууух…

Думаю на сегодня хватит. Если что-то не понятно, если что-то плохо написано, или где-то ошибка пишите в комментах! Буду рад дискуссии!

Вам понравилось? Было полезно? Поделитесь!

Опубликовать в Facebook
Опубликовать в Google Buzz
Опубликовать в Google Plus
Опубликовать в LiveJournal
Опубликовать в Мой Мир
Опубликовать в Одноклассники
Опубликовать в Яндекс
Запись опубликована в рубрике Java основы языка, Знакомимся с Java, Новичку, Программирование с метками , , , . Добавьте в закладки постоянную ссылку.

30 комментариев на «Классы или опять ООП на примере Java»

  1. Артём говорит:

    Спасибо, всё описано очень доходчиво)

  2. Николай говорит:

    Спасибо Большое! В одно время даже начал думать, что новых заметок уже не будет 😉

  3. Максим говорит:

    Хорошая статья.
    Прочел два раза.. Тяжко.. =))
    Смысл и возможности классов уяснил. Трудно усваивается описание и синтаксис.
    Чесно, с ходу не смог бы описать какой-нибудь класс. =)
    И опять же- хорошо бы комменты к коду.

    • Eugene говорит:

      Это как вы так решили взяться за разработку для Android, без базовых знаний ООП? Мне реально страшно смотреть на ваш код, если вы даже класс описать не можете.

      • davidmd говорит:

        Ну может просто синтаксис непонятен. конкретно в java

      • damager82 говорит:

        Все мы с чего-то начинали )
        Вот человек как раз и начал с Java, а потом возьмется за Android. Надо верить в людей )

  4. Aleksey говорит:

    Для новичка — все прекрасно и доходчиво…
    Хочу больше базовой информации для работы с графикой и SQL очень надо реализовать приложение (красивое для обработки БД).

    P.S. БД находится на удаленном сервере, доступ через интернет…

    Заранее спасибо за помощь…

    • davidmd говорит:

      Я сам пока что с удаленными б.д. не работал 🙁 но планирую к лету заняться подробнее.

  5. Костя говорит:

    Очень хорошая статья…… умение объяснять доходчиво это талант, я так не умею 🙂

  6. Илья Бородеев говорит:

    Очень примечательная статейка. Просто «ням» для новичка в ООП. Спасибо автору!

  7. sergej говорит:

    Отличная статья, спасибо Вам!

  8. m0m0k0 говорит:

    Здравствуйте!

    не понял вот эту строку:

    «for (Shaver s: shavers){
    s.shave(0);

    объясните плиз синтаксически…

    • davidmd говорит:

      Это аналог цикла foreach 🙂

      • Леван говорит:

        Можно поподробнее я тоже не понял, я так понял он перебирает все классы Shaver (в нашем случае 2) и обоим в конструктор передает 0. Верно?

        • davidmd говорит:

          Если быть точным, данный код перебирает все объекты в массиве shavers, и для каждого из них вызывает конструктор. При этом для объектов массива, которые являются экземплярами класса Shaver вызовется конструктор из этого класса, а для объектов являющихся экземплярами ShaverWithTrimmer конструктор из этого класса.

  9. Илья Бородеев говорит:

    Здравствуйте, Dаvimd! Возможно, мой вопрос не входит в рамки данной темы, но очень нужна помощь. Собственно, сам вопрос. Какое отличие между виртуальными и статическими методами? Хотя бы общие принципы. Заранее спасибо!

    • davidmd говорит:

      Статические методы принадлежат не объекту, а самому классу, виртуальные методы для Java вообще не применимы. Вообще виртуальные методы — это те, которые определены таким образом чтоб их конкретная реализация зависела от потомка класса. Т.е. виртуальный метод должен быть реализован и переопределен где-то в классе-потомке. Блин фигово как-то объясняю 🙁

  10. Илья Бородеев говорит:

    Спасибо!

  11. Герман говорит:

    По моему @Override — это переопределение, а не перегрузка.

  12. Андрей Sapfil говорит:

    Было бы здорово увидеть подобные объяснения на тему «интерфейсов». Чем отличается extens от implements. Что такое private , protected.
    Так же очень полезно узнать подробности о таких ключевых словах, как final, static…

    • Андрей Sapfil говорит:

      Упс простите. Public, private, protected уже рассмотрены.
      А вот @Override и вправду надо поправить. Ведь перегрузка и переопределение — это две большие разницы.

  13. Drooke говорит:

    Познавательно, благодарю. Вот что-то по функциям Java для новичков ничего на сайте нету.

  14. IgorOk говорит:

    «Ну и собственно сам процесс бриться описан в методе shave().»
    Не «бриться», а «бритья».

  15. Маратбек говорит:

    Благодарю за статью. Всё просто и понятно написано. Продолжайте своё дело.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *