Основы объектно ориентированного программирования

Наконец то мы добрались до темы: основы объектно ориентированного программирования (ООП) в языке Java. Это очень полезная и нужная тема, без знаний которой продолжать программировать мы не сможем.

При изучении статьи: «Классы, методы и конструкторы в Java» мы уже давали определение ООП и установили, что данная парадигма строится на трех китах:

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

Еще выделяют такую концепция как абстракция — выделение важных свойств объекта и представление важных аспектов предметной области в виде совокупности заимодействующих друг с другом объектов.

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

Код    
  1. package com.interfaces;
  2.  
  3. //вместо class пишем interface
  4. public interface InterfaceExample {//в интерфейсе находятся методы
  5.     //в интерфейсе можно объявлять константы
  6.    
  7.     String CONST = "My constant";//по умолчанию, все поля static, final public
  8.    
  9.     void myMethod();//по умолчанию все методы public, abstract
  10.    
  11.     String anotherMethod(String variable);
  12.  
  13.     //начаная с Java 8 в интерфейсах возможна реализация методов
  14.     //но пока будем считать, что методы можно только объявлять
  15. }

 

Для имплементации интерфейса нужно использовать ключевое слово implement. Тогда класс, который имплементирует интерфейс обязан реализовать все его методы.

Код    
  1. package com.interfaces;
  2.  
  3. //класс может имплементировать любое количество интерфейсов
  4. public class InterfaceImplementationClass implements InterfaceExample {
  5.  
  6.     @Override
  7.     public void myMethod() {
  8.         // TODO Auto-generated method stub
  9.        
  10.     }
  11.     //аннотация @Override говорит о том что метод переопределен
  12.     @Override
  13.     public String anotherMethod(String variable) {
  14.         // TODO Auto-generated method stub
  15.         return null;
  16.     }
  17.  
  18. }

Если не переопределить методы в классе, компилятор будет выдавать ошибку и запустить программу будет невозможно.

Абстрактный класс немного похожий на интерфейс с учетом того, что класс не имплементируется, а наследуется. В Java нет множественного наследования. Класс может унаследоваться только от одного класса. Отчасти, для множественного наследования и создали интерфейсы. 

Код    
  1. package com.absclasses;
  2.  
  3. public abstract class AbstractClass {
  4.    
  5.     public static int myVar = 0;//может содержать поля
  6.    
  7.     public abstract void myMethod();//методы без реализации
  8.    
  9.     public int myMethodWithBody(int variable){//методы с реализацией
  10.         return variable;
  11.     }
  12.  
  13. }
Код    
  1. package com.absclasses;
  2.  
  3. public class AbstractClassExtension extends AbstractClass {
  4.  
  5.    
  6.     @Override
  7.     public void myMethod() {//реализовать обязательно только абстрактный метод
  8.         // TODO Auto-generated method stub
  9.        
  10.     }
  11.    
  12.     public static void main(String[] args){
  13.         AbstractClassExtension abs = new AbstractClassExtension();
  14.         System.out.println(abs.myVar);//все поля и методы наследуются
  15.         System.out.println(abs.myMethodWithBody(4));
  16.     }
  17.  
  18. }

Вывод программы:

0
4

Так как мы уже вплотную подобрались к наследованию, предлагаю его и рассмотреть. Как Вы уже поняли в Java механизм наследование реализован абстрактным классом и интерфейсом. В джава наследоваться можно не только от абстрактного класса. Пропишите ключевое слово extends и наследуйте любой класс в которого конструктор не private.

Давайте посмотрим на популярный пример:

Код    
  1. package com.absclasses;
  2.  
  3. public class Animal {
  4.    
  5.     public String name;
  6.     public int countOfLegs;
  7.     public String voice;
  8.     public String someFieldWithValue = "My animal value";
  9.     public void introduction(){
  10.         System.out.println("Hello I'm " + name + " I have " + countOfLegs + " legs");
  11.     }
  12.    
  13.     public String getVoice(){
  14.         return voice;
  15.     }
  16.    
  17.     public void someMethodFromParent(){
  18.         System.out.println("I love people");
  19.     }
  20.  
  21. }

 

Его наследник:

Код    
  1. package com.absclasses;
  2.  
  3. public class Cat extends Animal {//можно унаследоваться от кота и получить еще более конкретное животное
  4.    
  5.     public void gettingParentField(){
  6.         System.out.println(super.someFieldWithValue);//ключевое слово super для получение полей и методов родителя
  7.         super.someMethodFromParent();
  8.     }
  9.    
  10.    
  11.     public void catsOwnMethod(){
  12.         System.out.println("I'm catching mouse");
  13.     }
  14.    
  15.     public static void main(String[] args){
  16.         Cat cat = new Cat();
  17.         cat.countOfLegs = 4;
  18.         cat.name = "Cat";
  19.         cat.voice = "Meaou";
  20.         cat.introduction();
  21.         System.out.println(cat.getVoice());
  22.         cat.catsOwnMethod();
  23.         cat.gettingParentField();
  24.     }
  25. }

 

Результат выполнения кода:ооп наследование

Для того, чтобы получить конструктор, метод или поле родительского класса, необходимо использовать ключевое слово super. Каждый экземпляр класса имеет неявную ссылку на себя самого. Вместо использования идентификатора имени атрибута вы можете написать this.id, но это необязательно, поскольку эти два объявления эквивалентны.

В статье модификаторы я не полностью раскрыл тему модификаторов доступа поскольку тогда Вы еще не знали ООП. Сейчас можно дополнить эту тему.

При наследовании можно изменять модификаторы доступа в сторону большей видимости: private -> default -> protected -> public. Идти можно только в таком порядке. Если Вы попытаетесь изменять модификаторы в сторону меньшей видимости, то получите ошибку компиляции и не сможете запустить свою программу.

Еще обещалось уделить внимание модификатору abstract. Теперь, когда мы выучили абстрактные классы, стало понятно, что это за модификатор. Класс, который имеет этот модификатор должен быть унаследован. Abstract является противоположностью final, от которых не можно унаследоваться. Запомните: абстрактный класс может не содержать абстрактных методов, но если в класса есть хотя бы один абстрактный метод, он должен быть объявлен как abstract. Создать экземпляр abstract класса нельзя.

Что еще нужно знать о наследовании:

  • статические методы не наследуются. Их нельзя переопределить. Статический метод вызывается для класса, в котором он определен.
  • конструктор наследника всегда вызывает конструктор родителя. Конструктор суперкласса может быть вызван непосредственно из конструктора наследника используя ключевое слово super. Если такой вызов явным образом не задавался программистом, компилятор поместит вызов конструктора по умолчанию (конструктор без параметров).Если родитель не имеет конструктора по умолчанию, может возникнуть ошибка компиляции.
  • ключевое слово super нельзя использовать в статических методах.
Код    
  1. package com.absclasses;
  2.  
  3. public class A {
  4.  
  5.     public static void staticMethod() {
  6.         System.out.println("I'm static method of A class");
  7.     }
  8.  
  9.     public void nonStaticMethod() {
  10.         System.out.println("I'm method of A class");
  11.     }
  12.  
  13.     public A(String b) {
  14.         System.out.println("My superclass");
  15.     }
  16.  
  17.     public static void main(String[] args) {
  18.         B b = new B("string");
  19.         A ab = new B("");
  20.         b.nonStaticMethod();
  21.         ab.nonStaticMethod();
  22.         A.staticMethod();
  23.         B.staticMethod();
  24.     }
  25. }
  26.  
  27. class B extends A {
  28.    
  29.     public B(String b) {//обязательное добавление конструктора с параметром
  30.         //так как родитель имеет только его
  31.         super(b);
  32.         // TODO Auto-generated constructor stub
  33.        
  34.     }
  35.  
  36.     public static void staticMethod() {
  37.         System.out.println("I'm static method of B class");
  38.     }
  39.  
  40.     public void nonStaticMethod() {
  41.         System.out.println("I'm method of B class");
  42.     }
  43.  
  44.     /*
  45.      * public B() {//невозможно добавить конструктор по умолчанию когда такового
  46.      * нет у родителя // TODO Auto-generated constructor stub }
  47.      */
  48.  
  49.    
  50.  
  51. }

Результат выполнения:

наследование

О наследовании поговорили. Теперь пришла очередь инкапсуляции.

Как уже говорилось ранее, инкапсуляция — это сокрытие реализации и предоставление доступа к коду не напрямую, а через определенные методы. Мы уже затрагивали тему ограничения доступа в модификаторах и я приводил пример на автомобилях. Теперь рассмотрим техническую сторону инкапсуляции.

В классах представлениях принято делать private поля и получать к ним доступ с помощью так называемых геттеров и сеттеров. Это методы объявлены как public которые соответственно задают значение поля и получают его. Думаю, это отличный пример инкапсуляции. Его мы и рассмотрим на практике. Мы возьмем наш пример с классом Cat и переделаем его по канонам ООП. Хотя бы попытаемся)).

Код    
  1. package com.incapsulation;
  2.  
  3. public class Cat {
  4.  
  5.     private String color;//мы делаем ограничение доступа к полям
  6.     private int weight;
  7.     private String sex;
  8.  
  9.     public String getColor() {//и предоставляем методы которые дают возможность работать с ними
  10.         return color;
  11.     }
  12.  
  13.     public void setColor(String color) {
  14.         this.color = color;
  15.     }
  16.  
  17.     public int getWeight() {
  18.         return weight;
  19.     }
  20.  
  21.     public void setWeight(int weight) {//если кто-то укажет весь меньше 0, тогда программа пропишет
  22.         //вес по умолчанию: 4
  23.         if (weight < 0)
  24.             this.weight = 4;
  25.         else
  26.             this.weight = weight;
  27.     }
  28.  
  29.     public String getSex() {
  30.         return sex;
  31.     }
  32.  
  33.     public void setSex(String sex) {//если пол не совпадает с ключевыми словами, тогда задаем значение мужской пол
  34.         if (sex.equals("man") || sex.equals("woman"))
  35.             this.sex = sex;
  36.         else
  37.             this.sex = "man";
  38.  
  39.     }
  40.  
  41. }

 

Теперь мы можем быть спокойны и не бояться, что наш код будет использован неправильно: кота не сделают очень худым и ему не дадут непонятный пол.

На очереди полиморфизм. Я долго писал объяснение но, прочитав, сам ничего не понял. Тогда я решил показать все на примере. Давайте вернемся к нашим животным и я попытаюсь показать, как работает полиморфизм.

Код    
  1. package com.poli;
  2.  
  3. public interface Animal {
  4.  
  5.     void showMeYourVoice();
  6. }

 

Код    
  1. package com.poli;
  2.  
  3. public class Lion implements Animal {
  4.  
  5.     @Override
  6.     public void showMeYourVoice() {
  7.         System.out.println("Roarrr");
  8.        
  9.     }
  10.  
  11. }

 

Код    
  1. package com.poli;
  2.  
  3. public class Dog implements Animal {
  4.  
  5.     @Override
  6.     public void showMeYourVoice() {
  7.         System.out.println("Woof - woof");
  8.        
  9.     }
  10.  
  11. }

 

Код    
  1. package com.poli;
  2.  
  3. public class Duck implements Animal {
  4.  
  5.     @Override
  6.     public void showMeYourVoice() {
  7.         System.out.println("Crya - crya");
  8.        
  9.     }
  10.  
  11. }

 

Код    
  1. package com.poli;
  2.  
  3. public class Main {
  4.  
  5.     public static void main(String[] args) {
  6.         Animal lion = new Lion();
  7.         Animal dog = new Dog();
  8.         Animal duck = new Duck();
  9.         Animal[] animalArray = {lion, dog, duck};//мы помещаем в масив Animal другие классы и компилятор не ругается.
  10.         //главное, чтобы эти классы имплементировали интерфейс. Так можно делать и с абстрактными классами.
  11.         for (int i = 0; i < animalArray.length; i++) {
  12.             animalArray[i].showMeYourVoice();//выведет специализированный метод для каждого класса.
  13.         }
  14.  
  15.     }
  16.  
  17. }

 

Один интерфейс и разные классы, которые его имплементируют позволяют вызывать один метод и получать разную реализацию. Еще один пример покажет Вам что такое перегрузка метода (не путать с переопределением).

Код    
  1. package com.poli;
  2.  
  3. public class MethodOverloading {
  4.    
  5.     public void method(){
  6.         System.out.println("Method without parameters");
  7.     }
  8.    
  9.     public void method(String oneParameter){
  10.         System.out.println("Method with one parameter");
  11.     }
  12.    
  13.     public void method(int intParam){
  14.         System.out.println("Method with int parameter");
  15.     }
  16.    
  17.     public void method(int intParam, String stringParam, long longParam){
  18.         System.out.println("Method with 3 parameters");
  19.     }
  20.    
  21.     public void method(String... value){
  22.         System.out.println("Method with one and more parameters");
  23.     }
  24.  
  25.     public static void main(String[] args) {
  26.         MethodOverloading meOverloading = new MethodOverloading();
  27.         meOverloading.method();
  28.         meOverloading.method(3);
  29.         meOverloading.method("One param");
  30.         meOverloading.method(3, "", 3);
  31.         meOverloading.method("","","");
  32.        
  33.  
  34.     }
  35.  
  36. }

 

Полагаю, объяснения излишни. Код говорит сам за себя. Результат выполнения:

Method without parameters
Method with int parameter
Method with one parameter
Method with 3 parameters
Method with one and more parameters

На этом можно и закончить основы объектно ориентированного программирования. Такую тему нельзя освоить одной статьей за один раз, но полагаю, что суть понятия стала более-менее ясна. Практикуйтесь, читайте материал и старайтесь писать читабельный и понятный код.

Так как кода становится все больше, некоторые исходники и буду сбрасывать в свой гитхаб профиль. Ссылка будет в конце статьи. Это сохранит Вам много времени: вместо того, чтобы копировать и вставлять код из статьи можно будет открыть готовый проект и работать с ним. Код тут.

 

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