Delegation code

Апкастинг

Приведение из подкласса в суперкласс называется восходящим . Как правило, апкастинг неявно выполняется компилятором.

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

Чтобы продемонстрировать восходящее вещание, давайте определим класс Animal :

public class Animal {

    public void eat() {
        // ... 
    }
}

Теперь давайте расширим Животное :

public class Cat extends Animal {

    public void eat() {
         // ... 
    }

    public void meow() {
         // ... 
    }
}

Теперь мы можем создать объект класса Cat и назначить его ссылочной переменной типа Cat :

Cat cat = new Cat();

И мы также можем назначить его ссылочной переменной типа Animal :

Animal animal = cat;

В приведенном выше задании имеет место неявное повышение. Мы могли бы сделать это явно:

animal = (Animal) cat;

Но нет необходимости делать явное приведение дерева наследования. Компилятор знает, что cat является Животным и не отображает никаких ошибок.

Обратите внимание, что ссылка может ссылаться на любой подтип объявленного типа. Используя upcasting, мы ограничили количество методов, доступных для экземпляра Cat , но не изменили сам экземпляр

Теперь мы не можем сделать ничего специфичного для Cat – мы не можем вызвать meow() в переменной animal

Используя upcasting, мы ограничили количество методов, доступных для экземпляра Cat , но не изменили сам экземпляр. Теперь мы не можем сделать ничего специфичного для Cat – мы не можем вызвать meow() в переменной animal .

Хотя Cat объект остается Cat объектом, вызов meow() вызовет ошибку компилятора:

// animal.meow(); The method meow() is undefined for the type Animal

Чтобы вызвать meow() нам нужно опустить animal , и мы сделаем это позже.

Но теперь мы опишем, что дает нам это предсказание. Благодаря апкастингу мы можем воспользоваться преимуществами полиморфизма.

3.1. Полиморфизм

Давайте определим еще один подкласс класса Animal , a Dog :

public class Dog extends Animal {

    public void eat() {
         // ... 
    }
}

Теперь мы можем определить метод feed () , который обрабатывает всех кошек и собак, как животных :

public class AnimalFeeder {

    public void feed(List animals) {
        animals.forEach(animal -> {
            animal.eat();
        });
    }
}

Мы не хотим, чтобы AnimalFeeder заботился о том, какое животное находится в списке – кошка или Собака . В методе feed() все они являются животными .

Неявная передача происходит, когда мы добавляем объекты определенного типа в список animals :

List animals = new ArrayList<>();
animals.add(new Cat());
animals.add(new Dog());
new AnimalFeeder().feed(animals);

Мы добавляем кошек и собак, и они неявно переводятся в тип Animal . Каждая Кошка является Животным и каждая Собака является Животным . Они полиморфны.

Кстати, все объекты Java полиморфны, потому что каждый объект, по крайней мере, является Объектом . Мы можем назначить экземпляр Animal ссылочной переменной типа Object , и компилятор не будет жаловаться:

Object object = new Animal();

Вот почему все объекты Java, которые мы создаем, уже имеют Object конкретные методы, например, toString() .

Также распространена передача на интерфейс.

Мы можем создать Новый интерфейс и сделать Cat реализовать его:

public interface Mew {
    public void meow();
}

public class Cat extends Animal implements Mew {
    
    public void eat() {
         // ... 
    }

    public void meow() {
         // ... 
    }
}

Теперь любой объект Cat также может быть передан в Mew :

Mew mew = new Cat();

Cat – это Мяу , апкастинг является законным и выполняется неявно.

Это/| Кошка является Новым , Животным , Объектом и Кошкой . В нашем примере он может быть назначен ссылочным переменным всех четырех типов.

3.2. Переопределение

В приведенном выше примере метод eat() переопределен. Это означает, что, хотя eat() вызывается для переменной типа Animal , работа выполняется методами, вызываемыми на реальных объектах – кошках и собаках:

public void feed(List animals) {
    animals.forEach(animal -> {
        animal.eat();
    });
}

Если мы добавим некоторые записи в наши классы, мы увидим, что вызываются методы Cat ‘и Dog :

web - 2018-02-15 22:48:49,354  INFO com.baeldung.casting.Cat - cat is eating
web - 2018-02-15 22:48:49,363  INFO com.baeldung.casting.Dog - dog is eating

Подводить итоги:

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

Как работают параметризованные конструкторы

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

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

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

Например, конструкторы ниже являются разными и вполне могут существовать в одном классе и иметь разную логику:

Заметьте, что конструкторы ниже для компилятора одинаковы и вызовут ошибку при запуске программы (тип, количество и порядок следования параметров идентичны):

Разберём пример применения параметризованного конструктора класса Cat с добавленным полем имени:

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

Для этого применяется вызов конструктора из конструктора с использованием ключевого слова this, которое означает ссылку на текущий объект. Обратиться к конструктору из другого конструктора можно через вызов this () — так будет выполнен конструктор без параметров. Если же нужен конструктор с параметрами, их указывают в скобках.

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

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

Вот пример оптимизации кода первого из конструкторов:

Обратите внимание: Несколько слов насчёт ключевого слова this. Синтаксис языка Java не запрещает использовать имена параметров или локальных переменных, совпадающие с именами переменных экземпляра (класса)

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

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

При выборе имён параметров рекомендую в первую очередь ориентироваться на читаемость кода. Чтобы, взглянув на этот код спустя некоторое время, вы сразу могли понять, что здесь происходит.

Абстрактные классы

Последнее обновление: 20.04.2018

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

При определении абстрактных классов используется ключевое слово abstract:

public abstract class Human{

    private String name;
	
	public String getName() { return name; }
}

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

Human h = new Human();

Кроме обычных методов абстрактный класс может содержать абстрактные методы. Такие методы определяются с помощью
ключевого слова и не имеют никакой реализации:

public abstract void display();

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

Зачем нужны абстрактные классы? Допустим, мы делаем программу для обслуживания банковских операций и определяем в ней три класса:
Person, который описывает человека, Employee, который описывает банковского служащего, и класс Client, который представляет клиента банка.
Очевидно, что классы Employee и Client будут производными от класса Person, так как оба класса имеют некоторые общие поля и методы. И так как
все объекты будут представлять либо сотрудника, либо клиента банка, то напрямую мы от класса Person создавать объекты не будем.
Поэтому имеет смысл сделать его абстрактным.

public class Program{
     
	public static void main(String[] args) {
			
		Employee sam = new Employee("Sam", "Leman Brothers");
		sam.display();
		Client bob = new Client("Bob", "Leman Brothers");
		bob.display();
	}
}
abstract class Person {
    
    private String name;
	
    public String getName() { return name; }
   
    public Person(String name){
    
        this.name=name;
    }
 
    public abstract void display();
}

class Employee extends Person{

    private String bank;
    
    public Employee(String name, String company) {
    
        super(name);
        this.bank = company;
    }
    
    public void display(){
        
        System.out.printf("Employee Name: %s \t Bank: %s \n", super.getName(), bank);
    }
}

class Client extends Person
{
    private String bank;
    
    public Client(String name, String company) {
    
        super(name);
        this.bank = company;
    }
    
    public void display(){
        
        System.out.printf("Client Name: %s \t Bank: %s \n", super.getName(), bank);
    }
}

Другим хрестоматийным примером является система геометрических фигур. В реальности не существует геометрической фигуры как таковой. Есть круг, прямоугольник,
квадрат, но просто фигуры нет. Однако же и круг, и прямоугольник имеют что-то общее и являются фигурами:

// абстрактный класс фигуры
abstract class Figure{
    
    float x; // x-координата точки
    float y; // y-координата точки
 
    Figure(float x, float y){
        
        this.x=x;
        this.y=y;
    }
    // абстрактный метод для получения периметра
    public abstract float getPerimeter();
    // абстрактный метод для получения площади
    public abstract float getArea();
}
// производный класс прямоугольника
class Rectangle extends Figure
{
    private float width;
    private float height;
 
    // конструктор с обращением к конструктору класса Figure
    Rectangle(float x, float y, float width, float height){
        
        super(x,y);
        this.width = width;
        this.height = height;
    }
	
    public float getPerimeter(){
        
        return width * 2 + height * 2;
    }
	
    public float getArea(){
        
        return width * height;
    }
}

НазадВперед

Недостатки Использования JNI

Мост JNI действительно имеет свои подводные камни.

Основным недостатком является зависимость от базовой платформы; мы, по сути, теряем функцию “написать один раз, запустить в любом месте” Java. Это означает, что нам придется создавать новую библиотеку для каждой новой комбинации платформы и архитектуры, которую мы хотим поддерживать. Представьте себе, какое влияние это могло бы оказать на процесс сборки, если бы мы поддерживали Windows, Linux, Android, macOS…

JNI не только добавляет уровень сложности нашей программе. Это также добавляет дорогостоящий уровень связи между кодом, запущенным в JVM, и нашим собственным кодом: нам нужно преобразовать данные, которыми обмениваются в обоих направлениях между Java и C++, в процесс маршалинга/немаршалинга.

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

Список оружия и цена разблокировки

  • Стигийский меч (Stygian Blade): оружие по умолчанию;
  • Ищущий сердца лук (Heart-Seeking Bow): стоит 1 ключ;
  • Щит Хаоса (Shield of Chaos): стоит 3 ключа;
  • Вечное копье (Eternal Spear): стоит 4 ключа;
  • Двойные кулаки Мальфона (Twin Fists of Malphon): стоит 8 ключей;
  • Адамантовое орудие (Adamant Rail): стоит 8 ключей, сначала нужно разблокировать все остальное оружие.

Вам нужно будет использовать хтонические ключи (Chthonic Keys), чтобы разблокировать все оружие в игре. Их можно получить в комнатах с символами ключей на дверях, или торгуя с Несчастным брокером (Wretched Broker).

Как только вы начнете новый забег, войдите в комнату со Скелли (Skelly), чтобы найти все оружие, выстроенное по бокам. Подойдите к оружию, которое хотите разблокировать, затем нажмите кнопку R, чтобы взять его, при условии, что у вас достаточно ключей. Разблокированное Хтоническими ключами оружие останется с вами, вы сможете получить все оружие, если будете продолжать играть.

Универсальные методы (Generic methods)

По аналогии с универсальными классами (дженерик-классами), можно создавать универсальные методы (дженерик-методы), то есть методы, которые принимают общие типы параметров. Универсальные методы не надо путать с методами в дженерик-классе. Универсальные методы удобны, когда одна и та же функциональность должна применяться к различным типам. (Например, есть многочисленные общие методы в классе java.util.Collections.)

Рассмотрим реализацию такого метода:

Нам в первую очередь интересно это:

«<T>» размещено после ключевых слов «public» и «static», а затем следуют тип возвращаемого значения, имя метода и его параметры. Такое объявление отлично от объявления универсальных классов, где универсальный параметр указывается после имени класса. Тело метода вполне обычное – в цикле все элементы списка устанавливаются в одно значение (val). Ну и в main()-методе происходит вызов нашего универсального метода:

Стоит обратить внимание на то, что здесь не задан явно тип параметра. Для IntList – это Integer и 100 тоже упаковывается в Integer

Компилятор ставит в соответствие типу Т – Integer.

Возможны ошибки, связанные с импортом List из java.awt вместо java.util

Важно помнить, что список из java.util является универсальным типом а список из java.awt — нет

А сейчас вопрос – какая (-ие) из нижеприведённых строк откомпилируется без проблем?

Перед ответом на этот вопрос следует учесть, что List – интерфейс, ArrayList наследуется от List; Number — абстрактный класс и Integer наследуется от Number.

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

Условие одинаковости дженерик-типов может показаться не совсем логичным. В частности хотелось бы использовать конструкцию под номером 3. Почему же это не допускается?

Будем думать от обратного – допустим 3-ий вариант возможен. Рассмотрим такой код:

Первая строка кода смотрится вполне логично, т.к. ArrayList наследуется от List , а Integer наследуется от Number. Однако допуская такую возможность мы получили бы ошибку в третьей строке этого кода, ведь динамический тип IntList — ArrayList <Integer>, т.е. происходит нарушение типобезапасности (присвоение значение Float там, где ожидается Integer) и в итоге была бы получена ошибка компилятора. Дженерики созданы, чтобы избегать ошибок такого рода, поэтому существует данное ограничение. Но тем не менее это неудобное ограничение и Java поддерживает маски для его обхода.

Интерфейс List и его реализации — ArrayList и LinkedList

List — это упорядоченная коллекция, наиболее часто используемая в Java Collections Framework. Этот интерфейс контролирует, где вставлен каждый элемент списка. При работе с List пользователю доступны элементы списка по их целочисленному индексу (позиции в списке).

Работа с интерфейсом List

Интерфейс List имеет две стандартные реализации — ArrayList и LinkedList.

Смысл в том, что можно написать и другие реализации, но в JDK уже есть две, которые доступны «‎‎из коробки».

ArrayList содержит внутри себя массив, длина которого будет увеличиваться автоматически при добавлении в него новых элементов.

Вторая имплементация интерфейса List — класс LinkedList. Это список с двумя связями, где каждый элемент содержит ссылку на предшествующий и следующий элементы списка.

Вторая имплементация интерфейса List — класс LinkedList

Для наглядности различий в цикломатической сложности между двумя классами рассмотрим таблицу:

Различия в цикломатической сложности между классами LinkedList и ArrayList

Как видите, основные преимущества класса LinkedList связаны с операциями и . Их цикломатическая сложность всегда будет равна . В случае добавления лучше использовать LinkedList, а вот для — ArrayList.

Так же дела обстоят с оператором . Цикломатическая сложность этой операции для LinkedList будет ниже только в том случае, если сам индекс будет равен нулю. Для всех остальных случаев разумнее использовать именно ArrayList.

Это же касается и операции : для класса LinkedList ее цикломатическая сложность будет вдвое меньше, чем для ArrayList. Забегая наперед скажу, что именно ArrayList используют в подавляющем случае работ в интерфейсе List.

Параметры

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

public class Employee {

    private String firstName = null;
    private String lastName  = null;
    private int    birthYear = 0;


    public Employee(String first,
        String last,
        int    year   ) {

        firstName = first;
        lastName  = last;
        birthYear = year;
    }
    

}

В этом примере объявлены 3 параметра: первый, последний и год. Внутри тела значения этих трех параметров присваиваются полям объекта Employee.

Разрывы строки на Java после каждого параметра являются необязательными. Компилятор здесь игнорирует разрывы строк. Вы также можете написать объявление параметра в одну строку, если хотите, например:

public Employee(String first, String last, int year ) {
    firstName = first;
    lastName  = last;
    birthYear = year;
}

Чтобы вызвать этот конструктор, который принимает три параметра, вы должны создать экземпляр объекта Employee следующим образом:

Employee employee = new Employee("Jack", "Daniels", 2000);

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

Параметр конструктора может иметь то же имя, что и поле. В этом случае у компилятора возникают проблемы, зная, на что вы ссылаетесь. По умолчанию, если параметр (или локальная переменная) имеет то же имя, что и поле в том же классе, параметр (или локальная переменная) «затеняет» поле. Посмотрите на этот пример:

public class Employee {

    private String firstName = null;
    private String lastName  = null;
    private int    birthYear = 0;


    public Employee(String firstName,
        String lastName,
        int    birthYear ) {

        firstName = firstName;
        lastName  = lastName;
        birthYear = birthYear;
    }
    
}

Внутри конструктора класса Employee идентификаторы firstName, lastName и birthYear теперь ссылаются на параметры конструктора, а не на поля Employee с одинаковыми именами. Таким образом, конструктор теперь просто устанавливает параметры, равные им самим. Поля Сотрудника никогда не инициализируются.

Чтобы сообщить компилятору, что вы имеете в виду поля класса Employee, а не параметры, поместите ключевое слово this и точку перед именем поля:

public class Employee {

    private String firstName = null;
    private String lastName  = null;
    private int    birthYear = 0;


    public Employee(String firstName,
        String lastName,
        int    birthYear ) {

        this.firstName = firstName;
        this.lastName  = lastName;
        this.birthYear = birthYear;
    }
    
}

Теперь поля Employee правильно инициализируются внутри конструктора.

Применение метасимвольных аргументов

Представьте, что мы хотим добавить метод для сравнения средних значений массивов в класс из примера 3. Причем типы массивов могут быть разные:

Так как параметризованный тип, какой тип параметра вы укажете для , когда создадите параметр метода типа ? Напрашивается следующий вариант:

Но это не сработает, так как в этом случае метод будет принимать аргументы только того же типа, что и существующий объект:

Чтобы создать обобщенную версию метода , следует воспользоваться другим средством обобщений Jаvа – метасимвольным аргументом. Метасимвольный аргумент обозначается знаком ? и представляет неизвестный тип.

Мета символ не оказывает никакого влияния на тип создаваемых объектов класса . Это определяется оператором в объявлении класса Average. Мета символ просто совпадает с любым достоверным объектом класса .

Метасимвольные аргументы могут быть ограничены почти таким же образом, как и параметры типов

Ограничивать метасимвольный аргумент особенно важно при создании обобщенного типа, оперирующего иерархией классов. Например:

Устраните ложь из своей жизни

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

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

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

Чтобы добиться успеха, вы должны сначала искоренить три вида лжи:

  1. Стать богатыми могут только везунчики или дети богачей.
  2. Стать богатым сложно.
  3. В период кризиса трудно найти возможности.

Адамантовое орудие (Adamant Rail)

Это винтовка. У игроков есть установленное количество боеприпасов. Их необходимо перезаряжать вручную после того, как боеприпасы закончатся. Обычная атака — это очередь, которая может сделать игру похожей на шутер. Специальная атака — это граната, которую можно запустить для нанесения AoE-урона. Атака рывком — это очередь из двух выстрелов в том направлении, в котором вы указываете.

Аспект Загрея

Аспект Загрея увеличивает ваш максимальный боезапас для оружия до 12 дополнительных боеприпасов. Этот аспект хорош в начале игры, но он стал неактуальным из-за некоторых улучшений, доступных у Молота Дедала. Смена боеприпасов — единственное преимущество. Хотя обновления могут сделать этот аспект неактуальным, он все же может быть очень полезным, если вы планируете использовать специальные улучшенные атаки.

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

Аспект Эрис

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

Этот аспект особенно хорошо работает с дарами Ареса и Диониса. Сочетание Проклятия Агонии Ареса для основной атаки с Пьяным процветанием Диониса для особой может нанести много урона. Хотя эти два эффекта являются хорошей комбинацией, практически любая комбинация даров будет хорошо работать с этим аспектом, поскольку увеличивает урон по всем направлениям.

Аспект Гестии

К этому аспекту может быть немного сложно привыкнуть. Если игрок вручную перезаряжается до того, как у него закончатся боеприпасы, следующий выстрел получит огромное увеличение дальности и урона. Это добавляет до 150 урона и 150% дальности при полной прокачке. Это позволяет вам одним выстрелом убить некоторых врагов. Проблема в том, что ручная перезарядка — это постоянное отслеживание нужного момента. Чтобы получить эффект против более сильных врагов, вы будете перезаряжаться после каждого выстрела, что утомительно и требует много времени. Вы можете случайно пропустить свой усиленный выстрел.

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

  • Уникальное оружие и прокачка — вышел трейлер новой постапокалиптической игры про мрачное будущее
  • Появился геймплей шутера про Вторую мировую войну с сюжетом и редким оружием
  • Сегодня в Steam выйдет украинский шутер с фантастическим оружием, паркуром и прокачкой (обновлено)

Использование метода newInstance() класса Constructor

Мы увидели метод newInstance класса Class, который мы использовали для создания объекта. Точно так же конструктор класса также состоит из метода newInstance(), который можно использовать для создания объектов. Другие могут использовать конструкторы Java по умолчанию с помощью этого метода, мы также можем вызвать параметризованные конструкторы.

import java.lang.reflect.*;

public class ObjectCreation
{
   private String FirstString = "Hello World";
   ObjectCreation()
   {
   }
   public void changeMessage(String message)
   {
       this.FirstString = message;
   }
   public static void main(String[] args)
   {
       try
       {
           Constructor<ObjectCreation> constructor = ObjectCreation.class.getDeclaredConstructor();
           ObjectCreation objectCreation = constructor.newInstance();
           objectCreation.changeMessage("Welcome to the world of programming");
           System.out.println(objectCreation.FirstString);
       }
       catch (Exception e)
       {
           e.printStackTrace();
       }
   }
}

Вывод:

Welcome to the world of programming.

Это 5 различных способов создания объектов в Java, которые используются чаще других. Каждый метод имеет свои преимущества и недостатки. В конце концов, выбор за вами.

Привет, Мир JNI

Далее, давайте посмотрим, как JNI работает на практике.

В этом уроке мы будем использовать C++ в качестве родного языка и G++ в качестве компилятора и компоновщика.

Мы можем использовать любой другой компилятор по вашему выбору, но вот как установить G++ на Ubuntu, Windows и Mac OS:

  • Ubuntu Linux – выполнить команду “sudo apt-get install build-essential” в терминале
  • Windows – Установка MinGW
  • macOS – запустите команду “g++” в терминале, и если ее еще нет, она установит ее.

3.1. Создание класса Java

Давайте начнем создавать нашу первую программу JNI с реализации классического “Hello World”.

Для начала мы создадим следующий класс Java, который включает в себя собственный метод, который будет выполнять эту работу:

package com.baeldung.jni;

public class HelloWorldJNI {

    static {
        System.loadLibrary("native");
    }
    
    public static void main(String[] args) {
        new HelloWorldJNI().sayHello();
    }

    // Declare a native method sayHello() that receives no arguments and returns void
    private native void sayHello();
}

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

В качестве альтернативы, в этой тривиальной программе мы могли бы вместо этого загрузить библиотеку непосредственно перед вызовом нашего собственного метода, потому что мы больше нигде не используем собственную библиотеку.

3.2. Реализация метода в C++

Теперь нам нужно создать реализацию нашего собственного метода в C++.

В C++ определение и реализация обычно хранятся в файлах .h и .cpp соответственно.

Во-первых, для создания определения метода мы должны использовать флаг -h компилятора Java :

javac -h . HelloWorldJNI.java

Это приведет к созданию файла com_baeldung_jni_HelloWorldJNI.h со всеми собственными методами, включенными в класс, переданными в качестве параметра, в данном случае только один:

JNIEXPORT void JNICALL Java_com_baeldung_jni_HelloWorldJNI_sayHello
  (JNIEnv *, jobject);

Как мы видим, имя функции автоматически генерируется с использованием полного имени пакета, класса и метода.

Кроме того, кое-что интересное, что мы можем заметить, заключается в том, что мы получаем два параметра, передаваемых нашей функции; указатель на текущий JNIEnv; , а также объект Java, к которому прикреплен метод, экземпляр нашего класса HelloWorldJNI .

Теперь нам нужно создать новый файл .cpp для реализации функции sayHello . Здесь мы будем выполнять действия, которые выводят “Hello World” на консоль.

Мы назовем наш файл .cpp тем же именем, что и файл .h, содержащий заголовок, и добавим этот код для реализации собственной функции:

JNIEXPORT void JNICALL Java_com_baeldung_jni_HelloWorldJNI_sayHello
  (JNIEnv* env, jobject thisObject) {
    std::cout << "Hello from C++ !!" << std::endl;
}

3.3. Компиляция И Связывание

На данный момент у нас есть все части, которые нам нужны, и есть связь между ними.

Нам нужно создать нашу общую библиотеку из кода C++ и запустить ее!

Для этого мы должны использовать компилятор G++, не забывая включать заголовки JNI из нашей установки Java JDK .

Версия Ubuntu:

g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o

Версия для Windows:

g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o

Версия для macOS;

g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin com_baeldung_jni_HelloWorldJNI.cpp -o com_baeldung_jni_HelloWorldJNI.o

Как только мы скомпилируем код для нашей платформы в файл com_baeldung_jni_HelloWorldJNI.o , мы должны включить его в новую общую библиотеку. Что бы мы ни решили назвать, это аргумент, переданный в метод System.LoadLibrary .

Мы назвали наш “родной”, и мы загрузим его при запуске нашего Java-кода.

Затем компоновщик G++ связывает объектные файлы C++ в нашу мостовую библиотеку.

Версия Ubuntu:

g++ -shared -fPIC -o libnative.so com_baeldung_jni_HelloWorldJNI.o -lc

Версия для Windows:

g++ -shared -o native.dll com_baeldung_jni_HelloWorldJNI.o -Wl,--add-stdcall-alias

Версия для macOS:

g++ -dynamiclib -o libnative.dylib com_baeldung_jni_HelloWorldJNI.o -lc

И это все!

Теперь мы можем запустить нашу программу из командной строки.

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

java -cp . -Djava.library.path=/NATIVE_SHARED_LIB_FOLDER com.baeldung.jni.HelloWorldJNI

Вывод на консоль:

Hello from C++ !!

Перестаньте думать, что скажут другие

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

Многокорпусная яхта не пострадала во время цунами и продается за £ 30 млн: фото

Серьезное финансовое решение: трехэтапный метод борьбы с импульсивными покупками

Прибыль без конкурентов: разведение шиншилл как бизнес — стоит ли открывать

Есть еще идеология, которая утверждает, что, чтобы заработать деньги, нужно вставать в семь утра и идти или в офис, или на свое рабочее место. Людям трудно принять идею о том, что есть деятельность, которую удобно осуществлять из дома и которая может принести заметный доход.

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

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

Лучше всего вообще никому ничего не объяснять, а поступать так, как считаете нужным.

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: