Определение методов — Основы Java
Определение собственных методов значительно упрощает написание и поддержку программ. Например, методы позволяют объединять составные операции в одну.
К примеру, отправка письма на сайте — это достаточно сложный процесс, включающий в себя взаимодействие с интернетом. Можно определить метод и скрыть всю сложность за одной простой конструкцией:
// Место, откуда берется метод import com.example.Mailer; var email = "support@hexlet.io"; var title = "Помогите"; var body = "Я написал историю успеха, как я могу получить скидку?"; // Mailer – имя класса, в котором определен метод send() // Один маленький вызов — и много логики внутри Mailer.send(email, title, body);
Внутри себя подобный вызов выполняет довольно много логики. Он соединяется с почтовым сервером, формирует правильный запрос на основе заголовка и тела сообщения, а затем все это отправляет, не забыв закрыть соединение.
Как создавать методы
Для начала рассмотрим структуру методов. Обычно они устроены так:
Создадим наш первый метод. Его задача — вывести на экран текущую дату:
import java.time.LocalDate; // Определение метода // Определение не вызывает и не выполняет метод // Мы лишь говорим, что теперь такой метод существует public class App public static void showCurrentDate() // Встроенный метод в Java для получения текущего времени и даты var currentDate = LocalDate.now(); var text = "Today is: " + currentDate; System.out.println(text); > > // Вызов метода // Обязательно указывать имя класса App.showCurrentDate(); // => Today is: 2021-10-25
Определение метода в Java включает в себя много действий, которые мы постепенно разберем.
Их можно разделить на две группы:
- То, что влияет на работу самого метода
- То, как этот метод видим за пределами класса
За видимость отвечает слово public. Оно дает возможность вызывать методы снаружи класса, как в примере выше. Кроме него существует private, который разбирается на Хекслете в курсе по ООП в Java.
За работу метода отвечают:
- static — говорит о том, что метод статический. Это значит, что он принадлежит самому классу. Благодаря этому мы можем вызвать метод, используя имя класса, без необходимости создавать объект этого класса
- void используется, если метод ничего не возвращает. Например, такое определение у метода System.out.println() . Если метод возвращает какие-то данные, то вместо void указывается тип возвращаемых данных
В отличие от обычных данных, методы выполняют действия, поэтому их имена практически всегда должны быть глаголами: «построить что-то», «нарисовать что-то», «открыть что-то».
Все, что описывается внутри фигурных скобок <> , называется телом метода. Внутри тела можно описывать любой код. Считайте, что это маленькая самостоятельная программа, набор произвольных инструкций.
Тело выполняется ровно в тот момент, когда запускается метод. Причем каждый вызов метода запускает тело независимо от других вызовов. Кстати, тело может быть пустым:
// Минимальное определение метода public class App public static void noop() // Тут мог бы быть код, но его нет // Обратите внимание на отступы // Для читаемости, любой код внутри тела сдвигается вправо на 4 пробела > > App.noop();
Понятие «создать метод» имеет много синонимов: «реализовать», «определить» и даже «заимплементить». Все эти термины встречаются в повседневной практике на работе.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Об обучении на Хекслете
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Урок «Как эффективно учиться на Хекслете»
- Вебинар « Как самостоятельно учиться »
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Методы в Java
В Java методы определяют бизнес-логику приложения. Они определяют взаимодействие между данными, заключенными в объекте.
В этом руководстве мы рассмотрим синтаксис методов Java, определение сигнатуры метода и способы вызова и перегрузки методов .
2. Синтаксис метода
Во-первых, метод состоит из шести частей:
- Модификатор доступа: опционально мы можем указать, из какого кода можно получить доступ к методу
- Тип возвращаемого значения: тип значения, возвращаемого методом, если таковой имеется.
- Идентификатор метода: имя, которое мы даем методу
- Список параметров: необязательный список входных данных для метода, разделенных запятыми.
- Список исключений: необязательный список исключений, которые метод может генерировать.
- Тело: определение логики (может быть пустым)
Давайте посмотрим пример:

Давайте подробнее рассмотрим каждую из этих шести частей метода Java.
2.1. Модификатор доступа
Модификатор доступа позволяет указать, какие объекты могут иметь доступ к методу. Существует четыре возможных модификатора доступа: public, protected, private и default (также называемый package-private ).
Метод также может включать ключевое слово static до или после модификатора доступа. Это означает, что метод принадлежит классу, а не экземплярам, и, следовательно, мы можем вызывать метод, не создавая экземпляр класса. Методы без ключевого слова static называются методами экземпляра и могут вызываться только для экземпляра класса.
Что касается производительности, статический метод загружается в память только один раз — во время загрузки класса — и, таким образом, более эффективно использует память.
2.2. Тип возврата
Методы могут возвращать данные в код, из которого они были вызваны. Метод может возвращать примитивное значение или ссылку на объект, или он может ничего не возвращать , если мы используем ключевое слово void в качестве возвращаемого типа.
Давайте посмотрим на пример метода void :
public void printFullName(String firstName, String lastName) System.out.println(firstName + " " + lastName); >
Если мы объявляем возвращаемый тип, то мы должны указать оператор возврата в теле метода. Как только оператор return будет выполнен, выполнение тела метода будет завершено, и если есть еще операторы, они не будут обработаны.
С другой стороны, метод void не возвращает никакого значения и, следовательно, не имеет оператора return .
2.3. Идентификатор метода
Идентификатор метода — это имя, которое мы присваиваем спецификации метода. Хорошей практикой является использование информативного и описательного имени. Стоит отметить, что идентификатор метода может содержать не более 65536 символов (правда, длинное имя).
2.4. Список параметров
Мы можем указать входные значения для метода в его списке параметров, заключенном в круглые скобки. Метод может иметь от 0 до 255 параметров, разделенных запятыми. Параметр может быть объектом , примитивом или перечислением . Мы можем использовать аннотации Java на уровне параметров метода (например, аннотацию Spring @RequestParam ).
2.5. Список исключений
Мы можем указать, какие исключения выбрасываются методом, используя предложение throws . В случае проверенного исключения либо мы должны заключить код в предложение try-catch , либо мы должны предоставить предложение throws в сигнатуре метода.
Итак, давайте взглянем на более сложный вариант нашего предыдущего метода, который выдает проверенное исключение:
public void writeName(String name) throws IOException PrintWriter out = new PrintWriter(new FileWriter("OutFile.txt")); out.println("Name: " + name); out.close(); >
2.6. Тело метода
Последней частью метода Java является тело метода, которое содержит логику, которую мы хотим выполнить. В теле метода мы можем написать столько строк кода, сколько захотим — или вообще ничего в случае статических методов. Если наш метод объявляет возвращаемый тип, то тело метода должно содержать оператор return.
3. Подпись метода
По определению сигнатура метода состоит только из двух компонентов — имени метода и списка параметров .
Итак, давайте напишем простой метод:
public String getName(String firstName, String lastName) return firstName + " " + middleName + " " + lastName; >
Сигнатура этого метода — getName(String firstName, String lastName) .
Идентификатор метода может быть любым идентификатором. Однако, если мы следуем общепринятым соглашениям о кодировании Java, идентификатор метода должен быть глаголом в нижнем регистре, за которым могут следовать прилагательные и/или существительные.
4. Вызов метода
Теперь давайте рассмотрим, как вызывать метод в Java . Следуя предыдущему примеру, предположим, что эти методы заключены в класс Java с именем PersonName :
public class PersonName public String getName(String firstName, String lastName) return firstName + " " + middleName + " " + lastName; > >
Поскольку наш метод getName является методом экземпляра, а не статическим методом, для вызова метода getName нам нужно создать экземпляр класса PersonName :
PersonName personName = new PersonName(); String fullName = personName.getName("Alan", "Turing");
Как мы видим, мы используем созданный объект для вызова метода getName .
Наконец, давайте посмотрим, как вызвать статический метод . В случае статического метода нам не нужен экземпляр класса для выполнения вызова. Вместо этого мы вызываем метод с префиксом имени класса.
Продемонстрируем на варианте предыдущего примера:
public class PersonName public static String getName(String firstName, String lastName) return firstName + " " + middleName + " " + lastName; > >
В этом случае вызов метода:
String fullName = PersonName.getName("Alan", "Turing");
5. Перегрузка метода
Java позволяет нам иметь два или более методов с одинаковым идентификатором, но разным списком параметров — разными сигнатурами методов . В этом случае мы говорим, что метод перегружен . Давайте рассмотрим пример:
public String getName(String firstName, String lastName) return getName(firstName, "", lastName); > public String getName(String firstName, String middleName, String lastName) if (!middleName.isEqualsTo("")) return firstName + " " + lastName; > return firstName + " " + middleName + " " + lastName; >
Перегрузка метода полезна в случаях, подобных приведенному в примере, когда у нас может быть метод, реализующий упрощенную версию той же функциональности.
Наконец, хорошей привычкой проектирования является обеспечение одинакового поведения перегруженных методов . В противном случае код будет запутанным, если метод с одним и тем же идентификатором будет вести себя по-разному.
6. Заключение
В этом руководстве мы рассмотрели части синтаксиса Java, используемые при указании метода в Java.
В частности, мы рассмотрели модификатор доступа, тип возвращаемого значения, идентификатор метода, список параметров, список исключений и тело метода. Затем мы увидели определение сигнатуры метода, как вызвать метод и как перегрузить метод.
Как обычно, приведенный здесь код доступен на GitHub .
- 1. Введение
- 2. Синтаксис метода
- 2.1. Модификатор доступа
- 2.2. Тип возврата
- 2.3. Идентификатор метода
- 2.4. Список параметров
- 2.5. Список исключений
- 2.6. Тело метода
Удобоваримый вызов Java методов из нативного кода
Существует довольно много приложений под Android, которые совмещают C++ и Java код. Где Java выступает оберткой/прослойкой, а C++ выполняет всю грязную работу. Пожалуй, ярким примером могут служить игры. В связи с этим часто приходится вызывать Java код из нативного для доступа к системным свойствам и плюшкам, которые предоставляет система (переключится на другую активность, послать или скачать что-либо из интернета). Причин много, а проблема одна: каждый раз приходится писать в лучшем случае 5 строчек кода и помнить, какую сигнатуру функции нужно запихнуть в параметр. Потом еще нужно перевести эти параметры в нужный тип. Стандартный пример из туториалов:
long f (int n, String s, float g);Строка-сигнатура для данного метода будет (ILjava/lang/String;F)J.
Вам удобно это все запоминать? А переводить С-строки в jstring? Мне — нет. Мне хочется писать:
CallStaticMethod(className, “f”, 1, 1.2f);Постановка задачи
Для начала поймем, что нам нужно. В сущности, это четыре вещи:
- Вызвать метод;
- Из параметров нужно вытянуть строку сигнатуры. Да, да, вот эту (ILjava/lang/String;F)J;
- Сконвертировать параметры в нужный тип;
- Возвратить тип данных, который хочет видеть пользователь нашего класса.
Вызов метода
Теперь стоит отметить, как мы будем вызывать нашу функцию-оболочку. Так как параметров может разное количество (от нуля и больше), то нужна функция вроде print`а в стандартной библиотеке, но с тем, чтобы было удобно вытягивать тип параметра и сам параметр. В С++11 появились вариадические шаблоны. Ими и воспользуемся.
template MethodType CallStaticMethod(Args. args);Составляем сигнатуру
-
Используем typeid и цепочку if … else. Должно получится что-то вроде:
if (typeid(arg) == typeid(int)) return “I”; else if (typeid(arg) == typeid(float)) return “F”;template std::string GetTypeName(); // int template <> std::string GetTypeName() < return “I”; >// string template <> std::string GetTypeName() < return “Ljava/lang/String;”; >void GetTypeRecursive(std::string&) < >template void GetTypeRecursive(std::string& signatureString, T value, Args. args) < signatureString += GetTypeName(); GetTypeRecursive(signatureString, args. ); >Вызов всего этого непотребства:
template MethodType CallStaticMethod(const char* className, const char* mname, Args. args) < std::string signature_string = "("; GetTypeRecursive(signature_string, args. ); signature_string += ")"; signature_string += GetTypeName(); return MethodType(); // пока здесь заглушка >Рекурсия — это хорошо в воспитательно-образовательных целях, но предпочитаю ее обходить при возможности. Тут такая возможность есть. Так как аргументы идут последовательно и мы можем узнать количество аргументов можно использовать удобство предоставленное стандартом С++11. Код преобразуется в:
template MethodType CallStaticMethod(const char* className, const char* mname, Args. args) < const size_t arg_num = sizeof. (Args); std::string signatures[arg_num] = < GetType(args). >; std::string signature_string; signature_string.reserve(15); signature_string += "("; for (size_t i = 0; i < arg_num; ++i) signature_string += signatures[i]; signature_string += ")"; signature_string += GetTypeName(); return MethodType(); // пока здесь заглушка >Кода вроде бы и больше, но работает оно быстрее. Хотя бы за счет того, что не вызываем функций больше, чем нам это нужно.
Конвертация типа данных
NativeType CallStaticMethod(JNIEnv *env, jclass clazz, jmethodID methodID, . ); NativeType CallStaticMethodA(JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args); NativeType CallStaticMethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);После
пытокпопыток и ухищрений было решено использовать CallStaticMethodA(JNIEnv*, jclass, jmethodID, jvalue*). Теперь только нужно привести все параметры к jvalue. Сам jvalue это union, в котором нужно установить нужное поле в зависимости от типа данных, которые вам передали любимые пользователи. Мудрить не будем и создаем структуру (или класс; дело вкуса) JniHolder с конструкторами нужных типов.JniHolder
struct JniHolder < jvalue val; JObjectHolder jObject; // bool explicit JniHolder(JNIEnv *env, bool arg) : jObject(env, jobject()) < val.z = arg; >// byte explicit JniHolder(JNIEnv *env, unsigned char arg) : jObject(env, jobject()) < val.b = arg; >// char explicit JniHolder(JNIEnv *env, char arg) : jObject(env, jobject()) < val.c = arg; >// short explicit JniHolder(JNIEnv *env, short arg) : jObject(env, jobject()) < val.s = arg; >// int explicit JniHolder(JNIEnv *env, int arg) : jObject(env, jobject()) < val.i = arg; >// long explicit JniHolder(JNIEnv *env, long arg) : jObject(env, jobject()) < val.j = arg; >// float explicit JniHolder(JNIEnv *env, float arg) : jObject(env, jobject()) < val.f = arg; >// double explicit JniHolder(JNIEnv *env, double arg) : jObject(env, jobject()) < val.d = arg; >// string explicit JniHolder(JNIEnv *env, const char* arg) : jObject(env, env->NewStringUTF(arg)) < val.l = jObject.get(); >// object explicit JniHolder(JNIEnv *env, jobject arg) : jObject(env, arg) < val.l = jObject.get(); >//////////////////////////////////////////////////////// operator jvalue() < return val; >jvalue get() < return val; >>;Где JObjectHolder — обертка для удержания и удаления jobject`а.
JObjectHolder
struct JObjectHolder < jobject jObject; JNIEnv* m_env; JObjectHolder() : m_env(nullptr) <>JObjectHolder(JNIEnv* env, jobject obj) : jObject(obj) , m_env(env) <> ~JObjectHolder() < if (jObject && m_env != nullptr) m_env->DeleteLocalRef(jObject); > jobject get() < return jObject; >>;Создается объект JniHolder, куда передаются JNIEnv* и значение. В конструкторе мы знаем какое поле нужно выставить в jvalue. Чтобы не было соблазна у компилятора приводить типы незаметно, все конструкторы делаем explicit. Вся цепочка занимает одну строчку:
jvalue val = static_cast(JniHolder(env, 10));Но есть одно но. Когда преобразования происходит мы возвращаем jvalue, но у нас удаляется jObject и val.l указывает на невалидный адрес. Поэтому приходится сохранять холдеры во время вызова функции java.
JniHolder holder(env, 10) jvalue val = static_cast(holder);В случае передачи нескольких параметров используем список инициализации:
JniHolder holders[size] = < std::move(JniHolder(env, args)). >; jvalue vals[size]; for (size_t i = 0; i < size; ++i) vals[i] = static_cast(holders[i]);Возвращение нужного типа данных
Хотелось бы написать какой-то один метод, который разруливал ситуацию и выглядел:
template MethodType CallStaticMethod(Args. args)
Но есть неприятная особенность JNI: для каждого возвращаемого типа есть свой конкретный метод. То есть, для int вам нужен CallStaticIntMethod, для float – CallStaticFloatMethod и так далее. Пришел к частичным типизациям шаблонов. Сначала объявляем нужный нам интерфейс:
template struct Impl < template static MethodType CallMethod(JNIEnv* env, jclass clazz, jmethodID method, Args. args); >;Потом для каждого типа пишем реализацию. Для целых чисел (int) будет выглядеть:
template <> struct Impl < template static int CallStaticMethod(JNIEnv* env, jclass clazz, jmethodID method, Args. args) < const int size = sizeof. (args); if (size != 0) < jvalue vals[size] = < static_cast(JniHolder(env, args)). >; return env->CallStaticIntMethodA(clazz, method, vals); > return env->CallStaticIntMethod(clazz, method); > >;Если у нас ноль параметров, то нужно вызывать CallStaticMethod, а не CallStaticMetodA. Ну и если пытаться создать массив размерностью ноль, компилятор сообщит вам все, что думает по этому поводу.
Финал
Сам метод вызова выглядит:
template MethodType CallStaticMethod(const char* className, const char* mname, Args. args) < const size_t arg_num = sizeof. (Args); std::string signatures[arg_num] = < GetType(args). >; std::string signature_string; signature_string.reserve(15); signature_string += "("; for (size_t i = 0; i < arg_num; ++i) signature_string += signatures[i]; signature_string += ")"; signature_string += GetTypeName(); JNIEnv *env = getEnv(); JniClass clazz(env, className); jmethodID method = env->GetStaticMethodID(clazz.get(), mname, signature_string.c_str()); return Impl::CallStaticMethod(env, clazz.get(), method, args. ); >Теперь вызов метода из java:
Java код
class Test < public static float TestMethod(String par, float x) < mOutString += "float String: " + par + " float=" + x + "\n"; return x; >>;Где-то в нативном коде:
float fRes = CallStaticMethod("Test", "TestMethod", "TestString", 4.2f);Ранее код выглядел
JNIEnv* env = getEnv(); // где-то надо достать эту штуку jclass clazz = env->FindClass(“Test”); jmethodID method = env->GetStaticMethodID(“Test”, “TestMethod”, “(Ljava/lang/String;F)Ljava/lang/String;); jstring str = env->NewStringUTF(“TestString”); float fRes = env->CallStaticFloatMethod(clazz, method, str, 4.2f); env->DeleteLocalRef(clazz); env->DeleteLocalRef(str);Выводы
Вызовы методов превратились в удобную вещь и не надобно запоминать сигнатуры и конвертировать значения и удалять ссылки. Достаточно передавать название класса, метода и аргументы.
Также получилась интересная задачка, благодаря которой немного поразбирался с новыми плюшками языка (которые мне ну очень понравились) и вспомнил шаблоны.
Благодарю за прочтение. Ну или за внимание, если вы не все прочитали. С радостью прочитаю предложения по улучшению и критику работы. А также отвечу на вопросы.
- C++
- Разработка под Android
Вызвать метод по имени
Здравствуйте! вобщем задача такая-есть самопальная прога, в которой мне необходимо по тз вызывать те или иные методы по имени. Как это возможно? я сделалЬ массив всех методов-в метод в 90% случаев передается string и int 2 аргументом.
мой код:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
. public void methodcaller(){ try{ Method[] a=Class.forName("paketsprogoi.GetDocuments").getDeclaredMethods(); for(Mithod i: a) { System.out.println(i.toGenericString()); i.invoke(arg1, arg2);///почитав я заподозрил эту штуку как вызыватель, но если задать агрументы //как стринг и инт значения-кидает ошибку } System.out.println("12345");//иначе у меня не успевает } catch. catch. } .
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:
В классе OnlyData написать метод, который выводит значение переменной i и вызвать этот метод в том же классе
Я конечно понимаю, что задача оч простая, но все же. В классе OnlyData нужно написать метод.Как вызвать метод?
Использую javaFX. Как мне этот метод вызвать по нажатию на кнопки? public static class.В классе вызвать метод
Почему, когда я ввожу метод в класс, то он его отторгает, как пришитую руку или сообщает о куче.Не получается вызвать метод
Хочу вызвать вот этот метод public void CreatiFile(String name) throws IOException
