NexxDigital - компьютеры и операционные системы

Мы поговорим о модификаторах: какие бывают модификаторы, области видимости, модификаторы для классов, полей, методов. Думаю, будет не скучно.

Модификаторы в Java – это ключевые слова, которые придают классу, полю класса или методу определенные свойства.

Для обозначения видимости класса его методов и полей есть 4 модификатора доступа:

  • private члены класса доступны только внутри класса;
  • package-private или default (по умолчанию) члены класса видны внутри пакета;
  • protected члены класса доступны внутри пакета и в классах-наследниках;
  • public члены класса доступны всем.

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

Все дело в том, что мы не прописали никаких модификаторов доступа к нашим полям и методам и они имеют свойство по умолчанию (члены класса видны внутри пакета). Чтобы исправить ошибку компиляции для нашего кода и наконец то запустить его, нужно сделать наш конструктор и методы public. Тогда их можно будет вызывать с других пакетов.

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

Представьте, что у Вас есть класс который отображает объект некоего продукта. Например машина. У машины может быть цена. Вы создали поле цена и еще множество других полей, кучу методов которые отвечают за функционал. Все вроде хорошо. Ваш класс машина является частью огромного проекта и все довольны. Но допустим, что кто-то по ошибке или специально создал экземпляр класса автомобиль и поставил отрицательную цену. Разве может товар иметь отрицательную цену? Это очень примитивный пример и вряд ли такое может случиться в реальной жизни, но думаю, идея понятна. Иногда нужно дать доступ не напрямую, а через определенные методы. Может быть, что код отвечает за функционал другого кода, и Вы не хотите, чтобы кто-то изменял и редактировал часть Вашего. Для этого всего и есть ограничение доступа.

Модификатор доступа у конструкторов, методов и полей может быть любой. Класс может быть только либо public, либо default, причем в одном файле может находиться только один public класс.

Пока об модификаторах доступа будет достаточно. В статье «Объектно ориентированное программирование» мы о них поговорим подробнее, а сейчас давайте поговорим о других модификаторах которых, к стати, немало.

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

static модификатор перед методом или полем говорит о том, что они не принадлежат к экземпляру данного класса. Что это означает для нас? Когда мы описали поле класса или метод как static, его можно вызвать без использования экземпляра класса. То есть вместо такой конструкции: Cat cat = new Cat(); cat.method(), можно написать просто Cat.method(). При условии, что метод объявлен как static. Статические переменные едины для всех объектов класса. У них одна ссылка.

    public class Modificators {

    static int anotherStaticField = 5 ;

    public static void myStaticMethod() {

    someField = "My field" ;

    //nonStaticField = ""; ошибка компиляции

    //нельзя использовать нестатические поля

    //в статических методах

    public void myNonStaticMethod() {

    anotherStaticField = 4 ; //ститические поля можно использовать

    //в нестатических методах

    //main метод тоже имеет модификатор static

    new Modificators() .myNonStaticMethod () ;

    Modificators.myStaticMethod () ; //вызов статических методов и полей

    //через имяКласса.метод

Еще одно важное замечание, которое нужно сказать по поводу static модификаторов: статические поля инициализируются во время загрузки класса. Часто в разного рода тестах по Java можно встретить такой код:

Вопрос: что будет выведено на консоль? Нужно помнить, что static блок будет выведен первым при любом раскладе. Далее будет идти блок по умолчанию. Далее смотрите на скрин консоли:

Следующий модификатор, который мы рассмотрим будет final.

Думаю, слово final говорит само за себя. Применяя final модификатор Вы говорите, что поля не могут быть изменены, методы переопределены, а классы нельзя наследовать (о наследовании будет отдельная статья). Этот модификатор применяется только к классам, методам и переменным (также и к локальным переменным).

С модификатором final к методам и классам мы будем говорить в статье ООП.

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

Модификатор synchronized — говорит о том, что метод может быть использован только одним потоком одновременно. Хотя, возможно, это Вам ни о чем не говорит, полезность этого модификатора будет видно, когда мы будем изучать многопоточность.

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

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

Модификатор native перед объявлением метода указывает что метод написан на другом языке программирования. Обычно на языке C.

Модификатор strictfp — Обеспечивает выполнение операций над числами типа float и double (с плавающей запятой) по стандарту IEEE 754. Или говоря проще, гарантирует что в пределах метода результаты вычислений будут одинаковыми на всех платформах.

Я еще не говорил о модификаторе abstract . О нем скажу вкратце, так как без знаний основ объектно ориентированного программирования говорить о нем не вижу смысла.

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

Подробнее о модификаторе abstract будем говорить в статье ООП.

На этом можно и закончить статью о модификаторах. Многое о них не было сказано. Но это из-за того, что у нас еще нет понятий ООП. Через несколько статей, мы дополним знания о модификаторах и заполним пробелы.

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

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

Public static void main(String args) { }

Для объявления статических переменных, констант, методов и инициализаторов перед их объявлением указывается ключевое слово static .

Статические поля

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

Например, создадим статическую переменную:

Public class Program{ public static void main(String args) { Person tom = new Person(); Person bob = new Person(); tom.displayId(); // Id = 1 bob.displayId(); // Id = 2 System.out.println(Person.counter); // 3 // изменяем Person.counter Person.counter = 8; Person sam = new Person(); sam.displayId(); // Id = 8 } } class Person{ private int id; static int counter=1; Person(){ id = counter++; } public void displayId(){ System.out.printf("Id: %d \n", id); } }

Класс Person содержит статическую переменную counter, которая увеличивается в конструкторе и ее значение присваивается переменной id. То есть при создании каждого нового объекта Person эта переменная будет увеличиваться, поэтому у каждого нового объекта Person значение поля id будет на 1 больше чем у предыдущего.

Так как переменная counter статическая, то мы можем обратиться к ней в программе по имени класса:

System.out.println(Person.counter); // получаем значение Person.counter = 8; // изменяем значение

Консольный вывод программы:

Id = 1 Id = 2 3 Id = 8

Статические константы

Также статическими бывают константы, которые являются общими для всего класса.

Public class Program{ public static void main(String args) { double radius = 60; System.out.printf("Radisu: %f \n", radius); // 60 System.out.printf("Area: %f \n", Math.PI * radius); // 188,4 } } class Math{ public static final double PI = 3.14; }

Стоит отметить, что на протяжении всех предыдущих тем уже активно использовались статические константы. В частности, в выражении:

System.out.println("hello");

out как раз представляет статическую константу класса System. Поэтому обращение к ней идет без создания объекта класса System.

Статические инициализаторы

Статические инициализаторы предназначены для инициализации статических переменных, либо для выполнения таких действий, которые выполняются при создании самого первого объекта. Например, определим статический инициализатор:

Public class Program{ public static void main(String args) { Person tom = new Person(); Person bob = new Person(); tom.displayId(); // Id = 105 bob.displayId(); // Id = 106 } } class Person{ private int id; static int counter; static{ counter = 105; System.out.println("Static initializer"); } Person(){ id=counter++; System.out.println("Constructor"); } public void displayId(){ System.out.printf("Id: %d \n", id); } }

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

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

Static initializer Constructor Constructor Id: 105 Id: 106

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

Статические методы

Статические методы также относятся ко всему классу в целом. Например, в примере выше статическая переменная counter была доступна извне, и мы могли изменить ее значение вне класса Person. Сделаем ее недоступной для изменения извне, но доступной для чтения. Для этого используем статический метод:

Public class Program{ public static void main(String args) { Person.displayCounter(); // Counter: 1 Person tom = new Person(); Person bob = new Person(); Person.displayCounter(); // Counter: 3 } } class Person{ private int id; private static int counter = 1; Person(){ id = counter++; } // статический метод public static void displayCounter(){ System.out.printf("Counter: %d \n", counter); } public void displayId(){ System.out.printf("Id: %d \n", id); } }

Теперь статическая переменная недоступна извне, она приватная. А ее значение выводится с помощью статического метода displayCounter. Для обращения к статическому методу используется имя класса: Person.displayCounter() .

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

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

Public class Program{ public static void main(String args) { System.out.println(Operation.sum(45, 23)); // 68 System.out.println(Operation.subtract(45, 23)); // 22 System.out.println(Operation.multiply(4, 23)); // 92 } } class Operation{ static int sum(int x, int y){ return x + y; } static int subtract(int x, int y){ return x - y; } static int multiply(int x, int y){ return x * y; } }

В данном случае для методов sum, subtract, multiply не имеет значения, какой именно экземпляр класса Operation используется. Эти методы работают только с параметрами, не затрагивая состояние класса. Поэтому их можно определить как статические.

Что такое static

В некоторых случаях желательно определить член класса, который будет использоваться независимо от любого объекта этого класса. Обычно обращение к члену класса должно выполняться только в сочетании с объектом его класса. Однако можно создать член класса, который может использоваться самостоятельно, без ссылки на конкретный экземпляр. Чтобы создать такой член, в начало его объявления нужно поместить ключевое слово static. Когда член класса объявлен как static (статический), он доступен до создания каких-либо объектов его класса и без ссылки на какой-либо объект. Статическими могут быть объявлены как методы, так и переменные. Наиболее распространенный пример статического члена - метод main (). Этот метод объявляют как static, поскольку он должен быть объявлен до создания любых объектов.

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

На методы, объявленные как static, накладывается ряд ограничений.

  • Они могут вызывать только другие статические методы.
  • Они должны осуществлять доступ только к статическим переменным.
  • Они ни коим образом не могут ссылаться на члены типа this или super. (Ключевое слово super связано с наследованием и описывается в следующей главе.)

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

// Демонстрация статических переменных, методов и блоков.
class UseStatic {
static int a = 3;
static int b;
static void meth(int x) {
System.out.println ("x = " + x) ;
System.out.println ("a = " + a);
System.out.println("b = " + b) ;
}
static {
System.out.println("Статический блок инициализирован.");
b = a * 4;
}

meth(42);
}
}

Сразу после загрузки класса UseStatic программа выполняет все операторы static. Вначале значение а устанавливается равным 3, затем программа выполняет блок static, который выводит сообщение, а затем инициализирует переменную b значением а*4, или 12. Затем программа вызывает метод main (), который обращается к методу meth (), передавая параметру х значение 42. Три оператора println () ссылаются на две статических переменные а и b на локальную переменную х.

Вывод этой программы имеет такой вид:

Статический блок инициализирован, х = 42 а = 3 b = 12

За пределами класса, в котором они определены, статические методы и переменные могут использоваться независимо от какого-либо объекта. Для этого достаточно указать имя их класса, за которым должна следовать операция точки. Например, если метод типа static нужно вызвать извне его класса, это можно выполнить, используя следующую общую форму:

имя_класса.метод()

Здесь имя_класса - имя класса, в котором объявлен метод тип static. Как видите, этот формат аналогичен применяемому для вызова нестатических методов через переменные объектных ссылок. Статическая переменная доступна аналогичным образом - посредством операции точки, следующей за именем класса. Так в Java реализованы управляемые версии глобальных методов и переменных.

Приведем пример. Внутри метода main () обращение к статическому методу callme () и статической переменной b осуществляется посредством имени их класса StaticDemo.

class StaticDemo {
static int a = 42;
static int b = 99;
static void callme () {
System.out.println("a = " + a);
}
}
class StaticByName {
public static void main(String args) {
StaticDemo.callme () ;
System.out.println("b = " + StaticDemo.b);
}
}

Вывод этой программы выглядит следующим образом.

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

Кроме имени и описания, о которых сказано выше, у метода есть ряд других характеристик:

  1. Набор модификаторов.
  2. Тип возвращаемого значения.
  3. Набор аргументов (параметров).

Модификаторы метода

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

Модификатор public отвечает за уровень доступа к описываемому методу. Вместо public могут указываться уровни доступа private или protect, а также может не указываться ничего, тогда будет действовать уровень доступа по умолчанию.

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

Метод main обязан иметь уровень доступа public как раз потому, что к нему обращается виртуальная машина Java, не являющаяся частью какого-либо пакета.

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

Тип возвращаемого значения

Методы в Java условно можно разделить на 2 группы: функции и процедуры. К первой группе относятся методы, очень похожие на функции в математическом смысле. В результате своей работы такие методы возвращают в то место программы, из которого они были вызваны, некоторый конкретный результат существующего типа, то есть это может быть целое или вещественное число или логическое значение (int, double, boolean), массив (ссылка на него), объект (ссылка на него). Возвращаемое значение должно присваиваться переменной подходящего типа или же передаваться какому-либо другому методу в роли аргумента.

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

Double r = Math.random(); /* random относится к функциям */ System.out.println(r); /* println относится к процедурам */

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

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

После модификаторов, но также слева от имени метода, указывается тип возвращаемого им значения (если метод является функцией, например: int или double) или же слово void (если метод является процедурой).

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

Все команды, указанные в описании метода после return, выполняться уже не будут, return без аргумента можно использовать внутри процедур. Он будет просто досрочно завершать процедуру (аналог break для цикла).

Аргументы (параметры)

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

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

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

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

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

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

Описание метода

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

Общая схема описания метода:

Модификаторы тип_возвращаемого_значения имя_метода (формальные аргументы) { // действия, выполняемые методом // возможно, return }

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

Рассмотрим несколько примеров:

Public static double kvadk (double) { double t; t = Math.pow(a, 0.5); return t; }

Теперь внутри метода main мы сможем использовать наш метод. Например, так:

Int a = 25; System.out.println(kvadk(a)); // 5.0 System.out.println(a) // 25

При передаче фактических параметров в метод действует автоприведение. Если аргумент фактический не соответствует типу формального, то Java пробует привести фактический аргумент к более универсальному типу (в данном случае int был приведён к double).

Перегрузка методов

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

Void pr(double a) { System.out.println(a); } void pr (String a) { System.out.println(a); } void pr(int a) { for (int i=0; i

Пример использования метода.

Int a = 5; int m = {1, 2, 8, 3} String s = "Мир"; pr (a) //работает исходный метод pr (a+s); // 5 мир, работает первая перегрузка pr (m); // 1 2 8 3 pr (m+a); // ошибка

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

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

Полиморфизм: одно имя, много форм.

Примеры использования методов

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

Import java.util.Scanner; public class Main { public static boolean isPrime(int n) { for(int i = 2; i <= Math.sqrt(n) ; i++) { if(n%i == 0) { return false; } } return true; } public static void main(String args) { Scanner sc = new Scanner(System.in); System.out.print("Введите натуральное число: "); if(sc.hasNextInt()) { int u = sc.nextInt(); if(u > 0) { if(isPrime(u)) { System.out.println("Вы ввели простое число"); } else { System.out.print("Простые делители числа: "); for(int i = (int)Math.sqrt(u); i >= 2 ; i--) { if(u%i == 0 && isPrime(i)) { System.out.print(i+" "); } } System.out.println(); } } else { System.out.println("Вы ввели не положительное число"); } } else { System.out.println("Вы ввели не целое число"); } } }

В следующем примере за счёт перегрузки будет создано несколько одноимённых методов.

Первый вариант метода будет просто переводить строку, т. е. фактически являться боле коротким синонимом встроенного метода System.out.println(). Параметров у этого варианта не будет.

Второй вариант метода (его первая перегрузка), проверяет, есть ли у числового аргумента дробная часть, если её нет, то аргумент приводится к целым и выводится на экран без нулевой дробной части (3 вместо 3.0). В этот метод смогут в качестве единственного аргумента передаваться не только переменные типа double, но и переменные любого другого типа, для которого возможно автоприведение к double (например, любые целочисленные переменные).

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

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

Public class Main { public static void pr() { System.out.println(); } public static void pr(double d) { if((int)d == d) { System.out.print((int)d); } else { System.out.print(d); } } public static void pr(double m) { pr(m, " "); } public static void pr(double m, String s) { for(int i = 0; i < m.length; i++) { pr(m[i]); System.out.print(s); } } public static void main(String args) { double arrn = {1, 2.71, 3.14, 15, -5, 92, 0.5}; double p = 3.0; int k = 13; pr(p); // вывод числа, без дробной части при возможности pr(); // переводит строку pr(arrn); // вывод числового массива в строку pr(); // переводит строку pr(arrn,", "); // вывод числового массива в строку через запятую pr(); // переводит строку pr(k); // вывод целого числа через автоприведение } }

В результате работы программы на экран будет выведено:

3 1 2.71 3.14 15 -5 92 0.5 1, 2.71, 3.14, 15, -5, 92, 0.5, 1

Задачи

  1. Создать статический метод, который будет иметь два целочисленных параметра a и b, и в качестве своего значения возвращать случайное целое число из отрезка . C помощью данного метода заполнить массив из 20 целых чисел и вывести его на экран.
  2. Создать метод, который будет выводить указанный массив на экран в строку. С помощью созданного метода и метода из предыдущей задачи заполнить 5 массивов из 10 элементов каждый случайными числами и вывести все 5 массивов на экран, каждый на отдельной строке.
  3. Создать метод, который будет сортировать указанный массив по возрастанию любым известным вам способом.
  4. В массиве хранится 7 явно заданных текстовых строк. Создать программу, которая отсортирует и выведет на экран строки в алфавитном порядке. Например, если были даны такие строки:
Пушкин Лермонтов Некрасов Толстой Л. Н. Толстой А. Н. Есенин Паустовский

Программа должна вывести на экран:

Есенин Лермонтов Некрасов Паустовский Пушкин Толстой А. Н. Толстой Л. Н.

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

Рекурсия

Рекурсией называется метод (функция), которая внутри своего тела вызывает сама себя.

Рассмотрим пример — вычисление факториала. Для того чтобы вычислить n!, достаточно знать и перемножить между собой (n-1)! и n.

Создадим метод, реализующий описанный способ.

Static int fact (int n) { if (n==1) { return 1; } else if (n==2) { return 2; } else { return fact(n-1) * n; } }

Указанный метод вычисляет факториал натурального числа.

Рассмотрим пример, вычисляющий через рекурсию n-ое число Фибоначчи.

Напомним, как выглядят первые элементы этого ряда: 1 1 2 3 5 8 13 …

Static int fib (int n) { if (n==1 || n == 2) { return 1; } return fib (n-2) + fib (n-1); }

Обратите внимание, что в этом методе второй return не помещён в блок else для первого условного оператора. Это допустимо, ведь если выполнится условие и сработает первый return, то произойдёт выход из метода, до второго return исполнение программы дойдёт только в случае невыполнения условия.

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

Задачи

  1. Выясните экспериментальном путём, начиная с какого элемента последовательности Фибоначчи, вычисление с использованием рекурсии становится неприемлемым (занимает более минуты по времени).
  2. Создайте гибридный метод, для небольших n вычисляющий n-ое число Фибоначчи с помощью рекурсии, а для значений, превышающих выясненное вами в предыдущей задаче пороговое n, вычисляющий n-ое число Фибоначчи с помощью итерационного алгоритма (цикла, в рамках которого будут сохраняться значения двух предыдущих элементов последовательности).
  3. Подсчитайте, сколько раз потребуется повторно вычислить четвёртый элементы последовательности Фибоначчи для вычисления пятнадцатого элемента.

Стек вызовов

В общем случае в текущий момент времени может исполняться только один единственный метод из всей программы. Это значит, что, если метод а устроен таким образом, что в своём теле он вызывает метод b , а сам а вызывается в main , то при запуске программы управление сначала будет передано методу main , затем методу а , затем методу b . Метод b вернёт результат и управление в а , а вернет результат управления в main , и только потом будут выполняться основные команды, указанные в методе main на остальных строках после вызова a .

Вся иерархия (кто кого вызывал) хранится в специальной области памяти, называемой стеком вызовов. Элементы в этот фрагмент памяти добавляются по следующему принципу: последний добавленный элемент должен быть извлечён первым. Когда работает метод b , получается, что под ним в стеке оказываются метод a и метод main .

В связи с этим в процессе рекурсии существует опасность переполнения стека вызовов.

Существует так называемая сложная рекурсия, при которой метод а вызывает метод b , b вызывает с , а с вызывает а .

2010, Алексей Николаевич Костин. Кафедра ТИДМ математического факультета МПГУ.

Модификатор static в Java напрямую связан с классом, если поле статично, значит оно принадлежит классу, если метод статичный, аналогично - он принадлежит классу. Исходя из этого, можно обращаться к статическому методу или полю используя имя класса. Например, если поле count статично в классе Counter , значит, вы можете обратиться к переменной запросом вида: Counter.count . Конечно, следует учитывать модификаторы доступа. Например, поля private доступны только внутри класса, в котором они объявлены. Поля protected доступны всем классам внутри пакета (package ), а также всем классам-наследникам вне пакета. Для более подробной информации ознакомьтесь со статьей “private vs protected vs public ”. Предположим, существует статический метод increment() в классе Counter , задачей которого является инкрементирование счётчика count . Для вызова данного метода можно использовать обращение вида Counter.increment() . Нет необходимости создавать экземпляр класса Counter для доступа к статическому полю или методу. Это фундаментальное отличие между статическими и НЕ статическими объектами (членами класса). Важное замечание. Не забывайте, что статические члены класса напрямую принадлежат классу, а не его экземпляру. То есть, значение статической переменной count будет одинаковое для всех объектов типа Counter . В этой статье мы рассмотрим основополагающие аспекты применения модификатора static в Java, а также некоторые особенности, которые помогут понять ключевые концепции программирования.

Что должен знать каждый программист о модификаторе Static в Java.

В этом разделе мы рассмотрим основные моменты использования статических методов, полей и классов. Начнём с переменных.

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

    public class Counter { private int count; public static void main (String args ) { System. out. println (count) ; //compile time error } }

    Это одна из наиболее распространённых ошибок допускаемых программистами Java, особенно новичками. Так как метод main статичный, а переменная count нет, в этом случае метод println , внутри метода main выбросит “Compile time error”.

    В отличие от локальных переменных, статические поля и методы НЕ потокобезопасны (Thread-safe) в Java. На практике это одна из наиболее частых причин возникновения проблем связанных с безопасностью мультипоточного программирования. Учитывая что каждый экземпляр класса имеет одну и ту же копию статической переменной, то такая переменная нуждается в защите - «залочивании» классом. Поэтому при использовании статических переменных, убедитесь, что они должным образом синхронизированы (synchronized), во избежание проблем, например таких как «состояние гонки» (race condition).

    Статические методы имеют преимущество в применении, т.к. отсутствует необходимость каждый раз создавать новый объект для доступа к таким методам. Статический метод можно вызвать, используя тип класса, в котором эти методы описаны. Именно поэтому, подобные методы как нельзя лучше подходят в качестве методов-фабрик (factory), и методов-утилит (utility). Класс java.lang.Math - замечательный пример, в котором почти все методы статичны, по этой же причине классы-утилиты в Java финализированы (final).

    Другим важным моментом является то, что вы НЕ можете переопределять (Override) статические методы. Если вы объявите такой же метод в классе-наследнике (subclass), т.е. метод с таким же именем и сигнатурой, вы лишь «спрячете» метод суперкласса (superclass) вместо переопределения. Это явление известно как сокрытие методов (hiding methods). Это означает, что при обращении к статическому методу, который объявлен как в родительском, так и в дочернем классе, во время компиляции всегда будет вызван метод исходя из типа переменной. В отличие от переопределения, такие методы не будут выполнены во время работы программы. Рассмотрим пример:

    class Vehicle { public static void kmToMiles (int km) { System. out. println ("Внутри родительского класса/статического метода" ) ; } } class Car extends Vehicle { public static void kmToMiles (int km) { System. out. println ("Внутри дочернего класса/статического метода " ) ; } } public class Demo { public static void main (String args ) { Vehicle v = new Car () ; v. kmToMiles (10 ) ; } }

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

    Внутри родительского класса/статического метода

    Код наглядно демонстрирует: несмотря на то, что объект имеет тип Car , вызван статический метод из класса Vehicle , т.к. произошло обращение к методу во время компиляции. И заметьте, ошибки во время компиляции не возникло!

    Объявить статическим также можно и класс, за исключением классов верхнего уровня. Такие классы известны как «вложенные статические классы» (nested static class). Они бывают полезными для представления улучшенных связей. Яркий пример вложенного статического класса - HashMap.Entry , который предоставляет структуру данных внутри HashMap . Стоит заметить, также как и любой другой внутренний класс, вложенные классы находятся в отдельном файле.class. Таким образом, если вы объявили пять вложенных классов в вашем главном классе, у вас будет 6 файлов с расширением.class. Ещё одним примером использования является объявление собственного компаратора (Comparator), например компаратор по возрасту (AgeComparator) в классе сотрудники (Employee).

    Модификатор static также может быть объявлен в статичном блоке, более известным как «Статический блок инициализации» (Static initializer block), который будет выполнен во время загрузки класса. Если вы не объявите такой блок, то Java соберёт все статические поля в один список и выполнит его во время загрузки класса. Однако, статичный блок НЕ может пробросить перехваченные исключения, но может выбросить не перехваченные. В таком случае возникнет «Exception Initializer Error». На практике, любое исключение возникшее во время выполнения и инициализации статических полей, будет завёрнуто Java в эту ошибку. Это также самая частая причина ошибки «No Class Def Found Error», т.к. класс не находился в памяти во время обращения к нему.

    Полезно знать, что статические методы связываются во время компиляции, в отличие от связывания виртуальных или не статических методов, которые связываются во время исполнения на реальном объекте. Следовательно, статические методы не могут быть переопределены в Java, т.к. полиморфизм во время выполнения не распространяется на них. Это важное ограничение, которое необходимо учитывать, объявляя метод статическим. В этом есть смысл, только тогда, когда нет возможности или необходимости переопределения такого метода классами-наследниками. Методы-фабрики и методы-утилиты хорошие образцы применения модификатора static . Джошуа Блох выделил несколько преимуществ использования статичного метода-фабрики перед конструктором, в книге «Effective Java », которая является обязательной для прочтения каждым программистом данного языка.

    Важным свойством статического блока является инициализация. Статические поля или переменные инициализируются после загрузки класса в память. Порядок инициализации сверху вниз, в том же порядке, в каком они описаны в исходном файле Java класса. Поскольку статические поля инициализируются на потокобезопасный манер, это свойство также используется для реализации паттерна Singleton . Если вы не используется список Enum как Singleton , по тем или иным причинам, то для вас есть хорошая альтернатива. Но в таком случае необходимо учесть, что это не «ленивая» инициализация. Это означает, что статическое поле будет проинициализировано ещё ДО того как кто-нибудь об этом «попросит». Если объект ресурсоёмкий или редко используется, то инициализация его в статическом блоке сыграет не в вашу пользу.

    Во время сериализации, также как и transient переменные, статические поля не сериализуются. Действительно, если сохранить любые данные в статическом поле, то после десериализации новый объект будет содержать его первичное (по-умолчанию) значение, например, если статическим полем была переменная типа int , то её значение после десериализации будет равно нулю, если типа float – 0.0, если типа Object – null . Честно говоря, это один из наиболее часто задаваемых вопросов касательно сериализации на собеседованиях по Java. Не храните наиболее важные данные об объекте в статическом поле!

    И напоследок, поговорим о static import . Данный модификатор имеет много общего со стандартным оператором import , но в отличие от него позволяет импортировать один или все статические члены класса. При импортировании статических методов, к ним можно обращаться как будто они определены в этом же классе, аналогично при импортировании полей, мы можем получить доступ без указания имени класса. Данная возможность появилась в Java версии 1.5, и при должном использовании улучшает читабельность кода. Наиболее часто данная конструкция встречается в тестах JUnit , т.к. почти все разработчики тестов используют static import для assert методов, например assertEquals() и для их перегруженных дубликатов. Если ничего не понятно – добро пожаловать за дополнительной информацией .

    На этом всё. Все вышеперечисленные пункты о модификаторе static в Java обязан знать каждый программист. В данной статье была рассмотрена базовая информация о статических переменных, полях, методах, блоках инициализации и импорте. В том числе некоторые важные свойства, знание которых является критичным при написании и понимании программ на Java. Я надеюсь, что каждый разработчик доведёт свои навыки использования статических концептов до совершенства, т.к. это очень важно для серьёзного программирования."



Если заметили ошибку, выделите фрагмент текста и нажмите Ctrl+Enter
ПОДЕЛИТЬСЯ:
NexxDigital - компьютеры и операционные системы