Рубрики
Без рубрики

Концепции ООП в Java

Одной из сильных сторон Java является ООП или объектно-ориентированное программирование. Вот почему этот язык стал таким популярным и действительно хорошо подходит для проектов любого масштаба.. Помечено как начинающие, java, кодирование, обучение.

Одной из сильных сторон Java является ООП или объектно-ориентированное программирование. Вот почему этот язык стал таким популярным и действительно хорошо подходит для проектов любого масштаба.

Что такое Объектно-ориентированное программирование? Это не магия, но это выглядит как магия, если вы действительно в нее вникнете. ООП – это то, как создавать ваше программное обеспечение. Концепция или, скорее, набор концепций, который позволяет вам создавать некоторые специфические взаимодействия и отношения между объектами Java для эффективной разработки и использования программного обеспечения.

Классическая версия включает в себя 3 + 1 основные концепции OOPs. Давайте начнем с классики.

Объект

Объекты в Java, как и объекты реального мира, имеют две характеристики: состояние и поведение.

Например, объект “Человек” имеет состояние (имя, пол, сон …) и поведение (изучение Java, ходьба, разговор …). Любой объект Java сохраняет свое состояние в полях и раскрывает свое поведение с помощью методов

Инкапсуляция

Инкапсуляция данных – это сокрытие внутренних данных от внешнего мира и доступ к ним только с помощью общедоступных методов. Что это значит? Какие данные, от кого скрываются? Скрытие означает ограничение прямого доступа к элементам данных (полям) класса.

Как это работает в Java:

  1. Поля имеют значение “закрытые”
  2. Каждое поле класса получает два специальных метода: геттер и сеттер. Методы получения возвращают поле. Методы настройки позволяют изменять значение поля не прямым, но законным способом.

Пример инкапсуляции, код Java:

public class Student {
private int age;
private String name;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

public class Test{
public static void main(String[] args) {
Student firstStudent = new Student();
firstStudent.setName("John");
//now you can't do this:  firstStudent.name = "John", the name field is private!
}
}

Почему вы должны это делать?

Основная причина – упрощение изменений вашего кода. Представьте, у вас есть заявка в хоккейную школу и там есть класс Хоккейный студент с двумя полями: имя и возраст ученика, когда он поступил в школу. Что-то вроде того:

public class HockeyStudent {
public String name;
public  int ageOfEnrollment;
}

Общественность возраст зачисления , без добытчиков или сеттеров… Этот класс используется многими другими классами, и все было в порядке, пока один парень не сказал, что среднего возраста недостаточно. Некоторые хоккеисты в группе почти на год старше других, поэтому было бы удобнее разделить их на две группы в зависимости от месяца, в котором они родились. Итак, новое поле возраст зачисления должен быть массивом int[][] первое число указано для полных лет, а второе – для месяцев. Теперь вам следует провести рефакторинг всего кода, использующего класс Студент ! Однако, если ваш возраст зачисления является частным, и у вас есть добытчики и сеттеры, все становится проще. Если требование для установки возраста учащегося изменится, просто обновите логику в методе setter установленный Возраст Зачисления () и ваши классы могут продолжать использовать Студент без проблем!

Этот пример несколько искусственный, но я надеюсь, что он объясняет, почему использование инкапсуляции – отличная идея.

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

Этот принцип более понятен даже без практики. Не повторяйся (СУХОЙ) может быть девизом концепции наследования.

Наследование позволяет создать дочерний класс, который наследует поля и методы родительского класса без их переопределения. Конечно, вы можете переопределить поля и методы родительского класса в дочернем классе, но в этом нет необходимости. Также вы можете добавлять новые состояния и поведение в дочерний класс. Родительские классы иногда называют суперклассами или базовыми классами, а дочерние классы известны как подклассы. Ключевое слово Java расширяет используется для реализации принципа наследования в коде.

Как это работает в Java:

  1. Создайте родительский класс.
  2. Создайте дочерний класс, используя ключевое слово extends .
  3. В конструкторе дочернего класса используйте super(родительское поле 1, родительское поле 2,...) метод наследования родительских полей.

Конструктор – это специальный метод для инициализации вновь созданного объекта. Конструктор имеет то же имя, что и имя его класса. Существует два типа конструкторов: конструктор по умолчанию (конструктор без аргументов) и параметризованный конструктор. Класс должен иметь хотя бы один конструктор (по умолчанию он всегда есть), и их может быть много.

Каждый раз, когда вы создаете новый объект, вы вызываете его конструктор. В приведенном выше примере строка

Student firstStudent = new Student();

Вы вызываете конструктор по умолчанию Ученик() из класса Студент использование слова новое

Некоторые правила:

  1. В одном классе может быть только один родитель.
  2. У одного родительского класса может быть много дочерних классов.
  3. Дочерний класс может иметь свои собственные дочерние классы.

Пример наследования, код Java

Давайте создадим класс Телефон .

public class Phone {
    int price;
    double weight;

//constructor
public Phone(int price, double weight) {
        this.price = price;
        this.weight = weight;
    }

    void orderPhone(){
        System.out.println("ordering phone...");
    }
}

Ну, есть разные типы телефонов, поэтому давайте создадим два дочерних класса, один для телефонов Android, а второй для iPhone, и добавим некоторые поля и методы, которых не было у их родителей. Поля, которые есть у их родителя, давайте вызовем конструкторы, используя super() .

Пример наследования Java

public class Android extends Phone {

//some new fields
String androidVersion;
int screenSize;

    String secretDeviceCode;

//constructor
    public Android(int price, double weight, String androidVersion, int screenSize, String secretDeviceCode) {
        super (price, weight); //Android inherits Phone's fields

        //this - reference to the current class object
        //super - reference to the parent class object

        this.androidVersion = androidVersion;
        this.screenSize = screenSize;
        this.secretDeviceCode = secretDeviceCode;
    }

    //new method specific for Android only, but not for Phone class
    void installNewAndroidVersion() {
        System.out.println("installNewAndroidVersion invoked...");

    }

}

public class IPhone extends Phone {
    int price;
    double weight;

    boolean fingerPrint;

    public IPhone(int price, double weight, boolean fingerPrint) {
        super (price, weight, countryProduced);
        System.out.println("IPhone constructor was invoked...");
        this.fingerPrint = fingerPrint;
    }

    void deleteIPhoneFromDb() {
        System.out.println("deleteIPhoneFromDb invoked...");
    }

@Override //This is about polymorphism, see below
void orderPhone(){
        System.out.println("ordering my new iPhone and deleting the old one...");
    }
}

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

Полиморфизм

Полиморфизм – это способность объекта принимать разные формы или, скорее, действовать по-разному. Обычно полиморфизм в Java возникает, если ссылка на родительский класс используется для ссылки на объект дочернего класса.

Что это значит и как это работает в Java:

Что такое полиморфизм в Java? В общем, это означает, что вы можете использовать одно и то же имя метода для разных целей. В Java существует два типа полиморфизма: переопределение методов (динамический полиморфизм) и перегрузка методов (статический полиморфизм).

Переопределение метода

Вы можете переопределить родительский метод в дочернем классе, заставив его работать по-другому. Давайте создадим родительский класс Musician с помощью метода play().

Пример полиморфизма Java

public class Musician {
    String name;
    int age;

    //default constructor
    public Musician() {
    }

    //parameterized constructor
    public Musician(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void play() {
        System.out.println("I am playing my instrument...");
    }
}

Музыканты используют разные инструменты. Давайте создадим два дочерних класса Пианист и Скрипач . Они оба будут играть, используя свои собственные версии метода play() благодаря полиморфизму. Для переопределения вы можете использовать @Переопределение обозначение но в этом нет необходимости.

public class Pianist extends Musician {

    String favoritePianoType;

    public Pianist(String name, int age, String favoritePianoType) {
        super(name, age);
        this.favoritePianoType = favoritePianoType;
    }


    @Override
void play(){
        System.out.println("I am playing piano...");
    }
}

Скрипач мог быть солистом или участником оркестра. Давайте примем это во внимание, переопределяя наш метод play() .

public class Violinist extends Musician {
    boolean isSoloist;

public Violinist(String name, int age, boolean isSoloist) {
            super(name, age);
            this.isSoloist = isSoloist;
        }


    @Override
void play(){
if (isSoloist)
        System.out.println("I am playing violin solo...");
else
System.out.println("I am playing violin in Orchestra...");

    }
}

Давайте создадим Демонстрационный класс, в котором мы создадим три объекта, по одному из каждого класса, и проверим результат.

public class Demo {
    public static void main(String[] args) {
  Musician musician = new Musician();
        Violinist violinist = new Violinist("John", 32,true);
  Pianist pianist = new Pianist("Glen", 30, "Wooden");

        System.out.println("Musician said:");
        musician.play();
        System.out.println("Violinist said:");
        violinist.play();
  System.out.println("Pianist said:");
  pianist.play();
    }
}

Вот что у нас здесь есть:

Музыкант сказал: Я играю на своем инструменте… Скрипач сказал: Я играю соло на скрипке… Пианист сказал: Я играю на пианино…

Каждый скрипач и пианист – музыкант, но не наоборот. Это означает, что вы можете использовать метод игры музыканта, если вам не нужно создавать новый. Или вы можете вызвать родительский метод из дочернего, используя ключевое слово super . Давайте сделаем это в коде пианиста:

public class Pianist extends Musician {

    String favoritePianoType;

    @Override
    void play(){
        super.play();
        System.out.println("I am playing piano...");
    }
}

Теперь давайте назовем наш демонстрационный класс. Вот результат:

Музыкант сказал: Я играю на своем инструменте… Скрипач сказал: Я играю соло на скрипке… Пианист сказал: Я играю на своем инструменте… Я играю на пианино…

Перегрузка способа

Перегрузка методов означает использование различных методов с одинаковыми именами в одном и том же классе. Они должны отличаться количеством, порядком или типами своих параметров. Скажем, пианист может играть на классическом пианино и электрическом пианино. Чтобы сыграть последнюю песню, этому музыканту нужно электричество. Давайте создадим два разных метода play() . Первый без параметров, для деревянного инструмента, а второй с проверкой электричества для электрического пианино.

public class Pianist extends Musician {

    String name;
    int age;
    String favoritePianoType;

    @Override
    void play(){
        super.play();
        System.out.println("I am playing piano...");
    }
    void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("Electricity on");
            System.out.println("I am playing piano...");
        }
        else System.out.println("I can't play this without electricity");
    }
}

Кстати, вы можете использовать первый метод play() во втором методе play(логическое значение) таким образом:

void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("Electricity on");
            play();
        }
        else System.out.println("I can't play this without electricity");
    }

Давайте добавим несколько строк в наш Демонстрационный класс, чтобы проверить нашу перегрузку:

public class Demo {
    public static void main(String[] args) {

        Musician musician = new Musician();
        Violinist violinist = new Violinist("John", 23,true);
        Pianist pianist = new Pianist("Glen", 30, "Wooden");

        System.out.println("Musician said:");
        musician.play();
        System.out.println("Violinist said:");
        violinist.play();
        System.out.println("Pianist said:");
        pianist.play();
        System.out.println("Now pianist tries electronic Piano:");
        pianist.play(true);
        System.out.println("now electricity's gone and electronic Piano player said:");
        pianist.play(false);
    }
}

Вот результат:

Музыкант сказал: Я играю на своем инструменте… Скрипач сказал: Я играю соло на скрипке… Пианист сказал: Я играю на своем инструменте… Я играю на пианино… Теперь пианист пробует электронное пианино: Электричество включено Я играю на своем инструменте… Я играю на пианино… теперь электричества нет, и электронный пианист сказал: Я не могу играть в это без электричества

Java знает, какой метод следует использовать в соответствии с его параметрами и типом объекта. Это полиморфизм.

Абстракция

Когда мы описываем объект, мы пытаемся построить его модель. Например, мы пишем видеоигру о гонщиках Мой гонщик с разными машинами. Игрок может выбрать один из них, а затем обновить или купить более новый. Так… Что такое автомобиль? Автомобиль – довольно сложная штука, но если мы пытаемся создать аркадную гоночную игру (не симулятор), мы не должны описывать все тысячи винтиков, которые в ней содержатся.

Нам нужна его модель, максимальная скорость, маневренность, цена, цвет… И, может быть, этого достаточно. Это модель автомобиля для этой игры.

Позже в моем Racer2 мы решили добавить шины, которые влияют на устойчивость на дороге. Здесь модель другая, потому что нам нужно больше деталей.

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

Существуют разные уровни абстракции. Например, если вы пассажир автобуса, вы должны знать, как выглядит ваш автобус и куда он едет, но вы не должны знать, как им управлять. Если вы водитель автобуса, вам не нужно знать, как создать новый автобус, вы должны знать, как им управлять. Но если вы конструктор автобусов, вам следует перейти на более низкий уровень абстракции… Подробности об архитектуре шины очень важны для вас. Надеюсь, вы понимаете, что я имею в виду.

Как это работает в Java:

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

  1. Более низкий уровень абстракции – это конкретный Объект . Это сущность с набором характеристик, присущих определенному экземпляру класса. Он имеет определенные значения полей

  2. Шаблоном для создания объектов является Класс . Это описание множества объектов со схожими свойствами и внутренней структурой.

  3. Абстрактный класс представляет собой абстрактное описание характеристик набора классов (действует как шаблон для наследования другими классами). Он обладает высоким уровнем абстракции, поэтому невозможно создавать объекты непосредственно из абстрактного класса, только путем создания объектов из дочерних классов. Абстрактный класс, может включать методы с реализацией, но не обязательно.

  4. Интерфейс – это конструкция языка программирования Java, в которой могут быть описаны только абстрактные общедоступные методы и константы статических свойств (конечные статические). То есть, как и на основе абстрактных классов, невозможно генерировать объекты на основе интерфейсов.

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

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

Пример интерфейса Java

interface Human {
    public void struggle();
    public void protect();
}

interface Vulcanian {
int earSharpnessAngle;
    public void emotionOff(boolean isOn);
    public void telepathy();
}

Вы можете реализовать несколько интерфейсов

Class Spock implements Human, Vulcanian {
public void struggle() {
System.out.println ("I am struggling...");
}
    public void protect() {
System.out.println("You are under my protection!");
}
public void emotionOff(boolean isOn){
If (isOn) {
System.out.println("I am turning off my emotions");
isOn= !isOn;
}
}
    public void telepathy() {
System.out.println("Connecting to your brain... ");
}

}

Вот и все об основных концепциях объектно-ориентированного программирования на Java для начинающих студентов. Помимо 4 основных принципов ООП, в Java существуют также ассоциация, агрегация и композиция. Вы можете назвать их “дополнительными принципами ООП”, и они заслуживают отдельной статьи.

Ранее было опубликовано в блоге CodeGym .

Оригинал: “https://dev.to/codegym_cc/oop-concepts-in-java-44c9”