Методы java

Недостатки Java

Всё это зву­чит хоро­шо, но есть у Java и недо­стат­ки, весь­ма суще­ствен­ные.


Не самая высо­кая про­из­во­ди­тель­ность. У С и С++ есть толь­ко ком­пи­ля­тор, кото­рый пере­во­дит про­грам­му в машин­ный код. У Java тако­го нет, и всё дела­ет вир­ту­аль­ная маши­на. Полу­ча­ет­ся, что для выпол­не­ния Java-кода нуж­но делать двой­ную рабо­ту: про­цес­сор запус­ка­ет JVM, а JVM выпол­ня­ет сам код. Из-за тако­го под­хо­да Java-программы в полтора-два раза мед­лен­нее, чем тот же код, напи­сан­ный на С++.

Плат­ная лицен­зия. С 2019 года ком­па­ния Oracle, кото­рая вла­де­ет лицен­зи­ей на Java, раз­де­ли­ла её на две части: плат­ную и бес­плат­ную. Если вам нуж­на дол­гая под­держ­ка сре­ды раз­ра­бот­ки и ста­биль­ная рабо­та про­грамм — бери­те плат­ную лицен­зию, ино­гда ста­биль­ность важ­нее. Если вам не нуж­на под­держ­ка и вы може­те уста­но­вить каж­дую новую бес­плат­ную сбор­ку само­сто­я­тель­но — отда­вать день­ги не нуж­но.

Гро­мозд­кий код. Раз­ра­бот­чи­ки Java ста­ви­ли сво­ей целью упро­стить про­грам­ми­ро­ва­ние на С++, и им это уда­лось. Цена, кото­рую при­шлось за это запла­тить, — боль­шие и длин­ные кон­струк­ции в язы­ке, кото­рые часто меша­ют пони­ма­нию сути того, что про­ис­хо­дит. Срав­ни­те код, кото­рый рису­ет пира­мид­ку из звёз­до­чек, на Java и Python:

Кто и для чего использует Java

Для нача­ла — спи­сок ком­па­ний и тех­но­ло­гий, кото­рые исполь­зу­ют Java в каче­стве основ­но­го язы­ка про­грам­ми­ро­ва­ния:

  • Amazon,
  • LinkedIn,
  • eBay,
  • Yahoo!
  • OpenOffice,
  • IBM, Intel и Oracle.

Раз­ра­бот­ка под Android. Java до сих пор оста­ёт­ся основ­ным язы­ком мобиль­ной раз­ра­бот­ки для этой опе­ра­ци­он­ной систе­мы, несмот­ря на рас­ту­щую попу­ляр­ность Kotlin. Android Studio — офи­ци­аль­ная сре­да Java-разработки, при­знан­ная ком­па­ни­ей Google.

Тер­ми­на­лы и пла­тёж­ные систе­мы. Бла­го­да­ря вир­ту­аль­ной машине Java-код может рабо­тать на обо­ру­до­ва­нии, кото­рое сто­ит в пла­тёж­ных тер­ми­на­лах и бан­ко­ма­тах.

Рабо­та с финан­са­ми. Если нужен про­стой и надёж­ный сайт по обра­бот­ке пла­те­жей или пере­во­ду денег — исполь­зуй­те Java. Встро­ен­ные систе­мы без­опас­но­сти помо­гут избе­жать несанк­ци­о­ни­ро­ван­ных дей­ствий про­грам­мы.

Usages

There are some situations when using the Void type can be interesting.

3.1. Reflection

First, we could use it when doing reflection. Indeed, the return type of any void method will match the Void.TYPE variable that holds the Class<Void> value mentioned earlier.

Let’s imagine a simple Calculator class:

Some methods are returning an integer, some are not returning anything. Now, let’s say we have to retrieve, by reflection, all methods that don’t return any result. We’ll achieve this by using the Void.TYPE variable:

As we can see, only the clear() and print() methods have been retrieved.

3.2. Generics

Another usage of the Void type is with generic classes. Let’s suppose we are calling a method which requires a Callable parameter:

But, the Callable we want to pass doesn’t have to return anything. Therefore, we can pass a Callable<Void>:

We could have either use a random type (e.g. Callable<Integer>) and return null or no type at all (Callable), but using Void states our intentions clearly.

We can also apply this method to lambdas. As a matter of fact, our Callable could have been written as a lambda. Let’s imagine a method requiring a Function, but we want to use a Function that doesn’t return anything. Then we just have to make it return Void:

How to Avoid Using It?

Now, we’ve seen some usages of the Void type. However, even if the first usage is totally fine, we might want to avoid using Void in generics if possible. Indeed, encountering a return type that represents the absence of a result and can only contain null can be cumbersome.

We’ll now see how to avoid these situations. First, let’s consider our method with the Callable parameter. In order to avoid using a Callable<Void>, we might offer another method taking a Runnable parameter instead:

So, we can pass it a Runnable which doesn’t return any value and thus get rid of the useless return null:

But then, what if the Defer class is not ours to modify? Then we can either stick to the Callable<Void> option or create another class taking a Runnable and deferring the call to the Defer class:

By doing that, we encapsulate the cumbersome part once and for all in our own method, allowing future developers to use a simpler API.

Of course, the same can be achieved for Function. In our example, the Function doesn’t return anything, thus we can provide another method taking a Consumer instead:

Then, what if our function doesn’t take any parameter? We can either use a Runnable or create our own functional interface (if that seems clearer):

Then, we overload the defer() method again:

Пункт 5: избегайте использования var, если названия переменных не содержат достаточной информации о типе для понимания кода

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

Избегайте следующего:

Вместо этого используйте:


Для конструкции, приведенной ниже, var также станет удачным способом упрощения кода без потери информативности.

Избегайте:

Используйте следующий код:

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

Избегайте:

Используйте:

Подумаем, к примеру, над использованием класса . Этот класс имеет статический метод , который возвращает new Selector и открывает его. Но здесь запросто можно подумать, что метод может возвращать тип boolean, в зависимости от успешности открытия имеющегося селектора, или даже возвращать void. Использование здесь var приведет к потери информации и путанице в коде.

Suppliers

The Supplier functional interface is yet another Function specialization that does not take any arguments. It is typically used for lazy generation of values. For instance, let’s define a function that squares a double value. It will receive not a value itself, but a Supplier of this value:

This allows us to lazily generate the argument for invocation of this function using a Supplier implementation. This can be useful if the generation of this argument takes a considerable amount of time. We’ll simulate that using Guava’s sleepUninterruptibly method:

Another use case for the Supplier is defining a logic for sequence generation. To demonstrate it, let’s use a static Stream.generate method to create a Stream of Fibonacci numbers:

The function that is passed to the Stream.generate method implements the Supplier functional interface. Notice that to be useful as a generator, the Supplier usually needs some sort of external state. In this case, its state is comprised of two last Fibonacci sequence numbers.

To implement this state, we use an array instead of a couple of variables, because all external variables used inside the lambda have to be effectively final.

Other specializations of Supplier functional interface include BooleanSupplier, DoubleSupplier, LongSupplier and IntSupplier, whose return types are corresponding primitives.

Бинарные специализации функциональных интерфейсов

Существуют бинарные специализации (binary specializations) интерфейсов , , и , которые принимают на вход два элемента.

Интерфейс Функциональный дескриптор Описание
BiConsumer<T, U> (T, U) -> void Представляет операцию, которая принимает на вход два элемента и не возвращает результат.
BiFunction<T, U, R> (T, U)  -> R Представляет операцию, которая принимает на вход два элемента и возвращает результат.
BiPredicate<T, U> (T, U) -> boolean Представляет операцию, которая принимает на вход два элемента и возвращает значение .
BinaryOperator<T> (T, T) -> T Представляет операцию, которая принимает на вход два элемента одного типа и возвращает значение того же типа.

7 ответов

Три очевидных ситуации, когда это нужно:

  • Вызов другого конструктора из того же класса, что и первая часть вашего конструктора
  • Различение локальной переменной и переменной экземпляра (в конструкторе или любом другом методе)
  • Передача ссылки на текущий объект другому методу

Вот пример всех трех:

Я полагаю, что есть также некоторые странные ситуации, использующие внутренние классы, где вам нужно , но их следует избегать, так как они очень туманны, IMO: )

РЕДАКТИРОВАТЬ: Я не могу придумать ни одного примера, почему вы захотите это для прямого вызова метода .

РЕДАКТИРОВАТЬ: saua внесла это в вопрос о неясных внутренних примерах:

Я использую «это» для пояснения кода, часто в качестве подсказки, что я вызываю метод экземпляра, а не обращаюсь к методу уровня класса или полю.

Но нет. Если устранение неоднозначности не требуется из-за коллизии именования области, на самом деле вам не нужно «this».

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

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

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

Не ответ (так что не стесняйтесь голосовать за него), но я не смог уместить это в комментарии, где кто-то спрашивал.

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

Так они и сделали бы:

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


Делать это с «этим». только 50% безопасно. Конечно, компилятор поймает его, если вы попытаетесь поместить this.x, когда x является локальной переменной, но ничто не помешает вам «забыть» пометить переменную экземпляра этим., И если вы забудете пометить только один (или если кто-то еще работает над вашим кодом), и вы полагаетесь на шаблон, тогда шаблон может быть скорее разрушительным, чем хорошим

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

тот факт, что вам нужно «это». передо мной определяется название параметра — я согласен, что он просто чувствует себя неряшливо. Вы хотите быть последовательным — если вам это нужно. передо мной, почему бы не всегда использовать его?

Хотя я понимаю дискомфорт, набираю это. каждое место, где используется переменная экземпляра, просто педантично, бессмысленно, безобразно и ненадежно. Если вас это действительно беспокоит, и вам абсолютно нужно использовать шаблон для его решения, попробуйте ввести «p» перед вашими параметрами. Как побочный эффект, он должен даже сделать его более постоянным, потому что регистр параметра теперь будет соответствовать регистру метода.

Единственная причина добавить перед вызовом метода — указать, что вы вызываете нестатический метод. Я не могу придумать какой-либо другой веской причины сделать это (поправьте меня, я ошибаюсь). Я не рекомендую это соглашение, поскольку оно не добавляет особой ценности. Если не применять последовательно, то это может ввести в заблуждение (поскольку вызов метода без этого все еще может быть нестатическим). Как часто заботится, является ли вызываемый метод статическим или нет? Кроме того, большинство IDE по-разному выделяют статические методы.

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

Изменить . Как отмечает mmyers (см. комментарий), работает со статическими методами. При этом я не вижу абсолютно никакой причины добавлять , поскольку это не имеет никакого значения.

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

Это позволяет связывать методы вместе следующим образом:

Two-Arity Function Specializations

To define lambdas with two arguments, we have to use additional interfaces that contain “Bi” keyword in their names: BiFunction, ToDoubleBiFunction, ToIntBiFunction, and ToLongBiFunction.

BiFunction has both arguments and a return type generified, while ToDoubleBiFunction and others allow you to return a primitive value.

One of the typical examples of using this interface in the standard API is in the Map.replaceAll method, which allows replacing all values in a map with some computed value.

Let’s use a BiFunction implementation that receives a key and an old value to calculate a new value for the salary and return it.

1.1 Объекты и классы

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

Что такое классы, вы уже знаете, а что такое объекты?

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

По одному чертежу строятся десятки идентичных кораблей, вот что важно

В программировании на Java все точно так же.

Чертежи

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

Сначала мы пишем классы (делаем чертежи), а потом, во время исполнения программы, на основе этих классов Java-машина создает объекты. Точно так же, как корабли создаются на основе чертежей.

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

Или вот еще аналогия…

Муравейник

Муравейник — это хороший пример взаимодействия объектов. В простейшем муравейнике есть три класса муравьёв: королева, воины и рабочие муравьи.

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

Это просто идеальный пример. В типичной программе все точно так же. Есть главный объект, который создаёт объекты всех остальных классов. Объекты начинают взаимодействовать друг с другом и «внешним миром» программы. Внутри этих объектов жёстко запрограммировано их поведение.

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

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


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

Документация

А как узнать, какие данные передавать в методы? Тут все уже придумано до вас.

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

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

Давайте посмотрим на код — копирование файла:

Копирование файла c:\data.txt в файл c:\result.txt

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

Functional Interfaces

All functional interfaces are recommended to have an informative @FunctionalInterface annotation. This not only clearly communicates the purpose of this interface, but also allows a compiler to generate an error if the annotated interface does not satisfy the conditions.

Any interface with a SAM(Single Abstract Method) is a functional interface, and its implementation may be treated as lambda expressions.

Note that Java 8’s default methods are not abstract and do not count: a functional interface may still have multiple default methods. You can observe this by looking at the Function’s documentation.

Using this for Ambiguity Variable Names

In Java, it is not allowed to declare two or more variables having the same name inside a scope (class scope or method scope). However, instance variables and parameters may have the same name. For example,

In the above program, the instance variable and the parameter have the same name: age. Here, the Java compiler is confused due to name ambiguity.

In such a situation, we use this keyword. For example,

First, let’s see an example without using keyword:

Output:

mc.age = 0

In the above example, we have passed as a value to the constructor. However, we are getting as an output. This is because the Java compiler gets confused because of the ambiguity in names between instance the variable and the parameter.

Now, let’s rewrite the above code using keyword.

Output:

obj.age = 8

Now, we are getting the expected output. It is because when the constructor is called, inside the constructor is replaced by the object obj that has called the constructor. Hence the age variable is assigned value 8.

Also, if the name of the parameter and instance variable is different, the compiler automatically appends this keyword. For example, the code:

is equivalent to:

this with Getters and Setters

Another common use of keyword is in setters and getters methods of a class. For example:

Output:

obj.name: Toshiba

Here, we have used keyword:

  • to assign value inside the setter method
  • to access value inside the getter method

Lambdas in Java 8

Java 8 brought a powerful new syntactic improvement in the form of lambda expressions. A lambda is an anonymous function that can be handled as a first-class language citizen, for instance passed to or returned from a method.

Before Java 8, you would usually create a class for every case where you needed to encapsulate a single piece of functionality. This implied a lot of unnecessary boilerplate code to define something that served as a primitive function representation.

Lambdas, functional interfaces and best practices of working with them, in general, are described in the article “Lambda Expressions and Functional Interfaces: Tips and Best Practices”. This guide focuses on some particular functional interfaces that are present in the java.util.function package.


С этим читают