Какие бывают блоки инициализации java
Перейти к содержимому

Какие бывают блоки инициализации java

  • автор:

Малоизвестные особенности Java

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

1. Нестатические блоки инициализации.

Всем, я думаю, известно, что в Java существуют статические блоки инициализации (class initializers), код которых выполняется при первой загрузке класса.

class Foo < static Listabc; static < abc = new LinkedList(); for (char c = 'A'; c > >

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

class Bar < < System.out.println("Bar: новый экземпляр"); >>

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

Map map = new HashMap() >;

Очень даже мощное средство, не находите?

JFrame frame = new JFrame() >); add(new JButton("Торт!") >); >>); >>); >>;

Остальные четыре пункта под катом.

2. Вложенные в интерфейсы классы.

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

interface Colorable < public Color getColor(); public static class Color < private int red, green, blue; Color(int red, int green, int blue) < this.red = red; this.green = green; this.blue = blue; >int getRed() < return red; >int getGreen() < return green; >int getBlue() < return blue; >> > class Triangle implements Colorable < private Color color; // . @Override public Color getColor() < return color; >>

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

Colorable.Color color = new Colorable.Color(0, 0, 0); color = new Triangle.Color(255, 255, 255);

Самым, наверное, известным примером этой идиомы является класс Map.Entry, содержащий пары ключ-значение ассоциативного словаря.

3. Коварианты возвращаемых типов.

Начиная с Java SE 5 типы возвращаемых результатов из методов ковариантны (covariant). Это означает, что мы можем в перекрытом методе (overriden) в качестве типа результата использовать подтип результата перекрываемого метода.

class Covariance implements Cloneable < @Override public Covariance clone() < Object cloned = null; try < cloned = super.clone(); >catch (CloneNotSupportedException exc) < // В данном примере недостижимо. >return (Covariance)cloned; > >

Метод Object.clone() имеет такую сигнатуру:

 protected Object clone()

Заметьте, возвращаемый тип изменён с Object на Covariance. Теперь, к примеру, нет нужды приводить результат работы метода clone() к действительному типу объекта, как это требовалось в ранних версиях JDK. Вместо этого кода:

Covariance foo = new Covariance(); Covariance bar = (Covariance)foo.clone();

Мы можем смело писать код следующий:

Covariance foo = new Covariance(); Covariance bar = foo.clone();

4. Выход из любого блока операторов.

Хотя goto и является зарезервированным ключевым словом Java, использовать его в своих программах нельзя. Пока? На смену ему пришли операторы break и continue, позволяющие прерывать и продолжать (соответственно) не только текущий цикл, но также и любой обрамляющий цикл, обозначенный меткой:

String a = "quadratic", b = "complexity"; boolean hasSame = false; outer: for (int i = 0; i < a.length(); ++i) < for (int j = 0; j < b.length(); ++j) < if (a.charAt(i) == b.charAt(j)) < hasSame = true; break outer; >> > System.out.println(hasSame);

Но многие даже не догадываются, что в Java мы всё же можем при помощи оператора break не только прервать цикл, но и покинуть совершенно любой блок операторов. Чем не оператор goto, правда, односторонний? Как говорится, вперёд и ни шагу назад.

long factorial(int n) < long result = 1; scope: < if (n == 0) < break scope; >result = n * factorial(n - 1); > return result; >

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

5. Модификация данных из внутренних классов.

Хотя в Java и предусмотрено ключевое слово final, однако на деле отсутствует возможность задать неизменяемость самого объекта, а не указывающей на него ссылки (не относится к примитивам). Ну, в принципе, можно спроектировать неизменяемый (immutable) класс, предоставив только геттеры и чистые функции, но нельзя, к примеру, создать неизменяемый массив. Это, как мне кажется, существенное упущение в дизайне языка. Тут бы пригодилось зарезервированное, но запрещённое ключевое слово const. Ждём в следующих версиях?

final int[] array = ; new Object() < void twice() < for (int i = 0; i < array.length; ++i) < array[i] *= 2; >> >.twice();

Таким образом, мы можем модиицировать хотя и финализированные, но фактически изменямые данные, будь то массивы либо другие неперсистентные объекты даже из контекста внутренних (inner) классов. Со строками и оболочками примитивных типов, к сожалению, такой фокус не пройдёт. Пусть вас ключевое слово final не вводит в заблуждение.

Если вам статья понравилась — продолжение следует.

Все о нестатических блоках инициализации в Java

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

[Данная статья подготовлена на основе материала из книги OCP Oracle Certified Professional Java SE 17 Developer (Exam 1Z0-829) Programmer’s Guide, опубликованной издательством Oracle Press. — Ред.]

  • Инициализирующие выражения
  • Статические блоки инициализации
  • Нестатические блоки инициализации

Java также позволяет определять в классе статические блоки инициализации (static initializer blocks). Эти блоки могут содержать произвольный код, но в основном они используются для инициализации статических полей. Код в статическом блоке инициализации выполняется единожды, когда класс загружается в память.

Подобно тому, как статические блоки инициализации могут использоваться для инициализации статических полей именованного класса, Java предоставляет возможность инициализации полей во время создания объекта с помощью нестатических блоков инициализации (instance initializer blocks), о чем и пойдет речь в этой статье.

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

class InstanceInitializers < long[] squares = new long[10]; // (1) // . < // (2) Нестатический блок инициализации for (int i = 0; i < squares.length; i++) squares[i] = i*i; >// . >

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

Порядок объявления нестатических блоков инициализации

Аналогично другим инициализаторам, о которых мы упоминали ранее, нестатический блок инициализации не может использовать опережающую ссылку (forward reference) на поле просто по его имени в операциях чтения, поскольку это нарушает правило «объявление переменной всегда предшествует ее использованию» (declare-before-reading). Однако мы без проблем можем использовать для доступа к полю ключевое слово this.

Листинг 1 демонстрирует класс с нестатическим блоком инициализации в строке (1), который содержит опережающие ссылки на поля i, j и k, объявленные в строках (7), (8) и (9) соответственно. Доступ к этим полям в операциях чтения в строках (3), (4), (5) и (6) осуществляется с помощью this. Использование голого имени этих полей в строках (3), (4), (5) и (6) для доступа к их значениям нарушит упомянутое выше правило, что приведет к ошибке компиляции независимо от того, объявлены ли поля с применением инициализирующих выражений или являются final.

В строке (2) доступ к полям i и j осуществляется просто по имени, что разрешено в операциях записи. Однако нам нужно внимательно следить за правильностью инициализации полей. В строках (3), (4) и (5) поля i и j имеют значение 10. Однако после выполнения инициализирующих выражений в объявлениях полей значение j станет равным 100.

Листинг 1. Доступ к полям с помощью ключевого слова this

public class InstanceInitializersII < < // Нестатический блок инициализации с опережающими ссылками. (1) i = j = 10; // (2) Это разрешено. int result = this.i * this.j; // (3) i = 10, j = 10. System.out.println(this.i); // (4) 10 System.out.println(this.j); // (5) 10 System.out.println(this.k); // (6) 50 >// Объявления полей. int i; // (7) Объявление поля без инициализирующего выражения. int j = 100; // (8) Объявление поля с инициализирующим выражением. final int k = 50; // (9) Final поле с константным выражением. >

Листинг 2 иллюстрирует еще несколько тонкостей, касающихся нестатических блоков инициализации. В приведенном ниже коде в строке (4) происходит недопустимая опережающая ссылка, которая пытается прочитать значение поля nsf1 до его объявления. Операция чтения в строке (11) происходит после объявления, поэтому она не вызывает проблем. Опережающие ссылки, сделанные в левой части присваивания, разрешены, как показано в строках (2), (3), (5) и (7). В строках (5) и (12) вы можете увидеть объявление локальных переменных с использованием зарезервированного слова var.

Листинг 2. Нестатические блок инициализации и опережающие ссылки.

public class NonStaticForwardReferences < < // (1) Нестатический блок инициализации. nsf1 = 10; // (2) ОК. Присвоение nsf1 разрешено. nsf1 = sf1; // (3) OK. Доступ к статическому полю в нестатическом контексте. // int a = 2 * nsf1; // (4) Не ОК. Операция чтения до объявления. var b = nsf1 = 20; // (5) ОК. Присваивание nsf1 разрешено. int c = this.nsf1; // (6) ОК. Доступ с помощью this. >int nsf1 = nsf2 = 30; // (7) Нестатическое поле. Присвоение nsf2 разрешено. int nsf2; // (8) Нестатическое поле. static int sf1 = 5; // (9) Статическое поле. < // (10) Нестатический блок инициализации. int d = 2 * nsf1: // (11) OK. Операция чтения после объявления. var e = nsf1 = 50; // (12) OK. Присваивание nsf1 разрешено. >public static void main(String[] args) < NonStaticForwardReferences objRef = new NonStaticForwardReferences () ; System.out.println("nsf1: " + objRef.nsf1) ; System.out.println("nsf2: objRef.nsf2); >>

Ниже приведен вывод для кода из Листинга 2:

nsf1: 50 nsf2: 30

В нестатических блоках инициализации, как и в инициализирующих выражениях, для ссылки на текущий объект можно использовать ключевые слова this и super . (Оператор return же не допускается).

Нестатические блоки инициализации можно использовать для уменьшения объемов дублирования общего кода инициализации, который будет выполняться независимо от того, какой конструктор вызывается. В Листинге 3 показано, как анонимный класс, определенный в строке (1), использует для инициализации своих полей нестатический блок (строка (2)).

Листинг 3. Нестатический блок инициализации в анонимном классе

// File: InstanceInitBlock.java class Base < protected int a; protected int b; void print() < System.out.println("a: " + a); >> class AnonymousClassMaker < Base createAnonymous() < return new Base() < // (1) Анонимный класс < // (2) Нестатический блок инициализации a = 5; b = 10; >@Override void print() < super.print(); System.out.println("b: " + b); >>; // конец анонимного класса > > public class InstanceInitBlock < public static void main(String[] args) < new AnonymousClassMaker().createAnonymous().print(); >>

Ниже приведен вывод для кода из Листинга 3:

a: 5 b: 10

Обработка исключений в нестатических блоках инициализации

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

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

  • java
  • инициилизация
  • нестатические блоки
  • Блог компании OTUS
  • Java

Блоки статической и объектной инициализации

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

Давайте начнем со следующего примера. Программа SmallSquares (маленькие квадраты) возвращает квадрат маленького целого числа. SmallSquares имеет 2 статические переменные и единственную открытую статическую функцию getSquare().

public class SmallSquares <

private static final int LIMIT = 10 ;
private static final int [] square = new int [ LIMIT ] ;

public SmallSquares () < // не пишите такой код
for ( int i = 0 ; i < LIMIT; i++ ) square [ i ] = i * i;
>
>
>
public static int getSquare ( int i ) // Нет обработки ошибки, предположим, 0 return square [ i ] ;
>

public static void main ( String [] args ) new SmallSquares () ;
System.out.println ( «3 squared is » +
getSquare ( 3 )) ;
>
>

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

3 squared is 9 ( 3 в квадрате будет 9 )

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

А ещё лучше использовать статическую инициализацию. За словом статический (static) следует блок кода, окруженного фигурными скобками. Вы можете использовать статический блок для инициализации массива квадратов вот так:

static <
for ( int i = 0 ; i < LIMIT; i++ ) <
square [ i ] = i * i;
>
>

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

public class SmallSquares <

private static final int LIMIT = 10 ;
private static final int [] square = new int [ LIMIT ] ;

static for ( int i = 0 ; i < LIMIT; i++ ) square [ i ] = i * i;
>
>

public static int getSquare ( int i ) // Нет обработки ошибки, предположим,0 return square [ i ] ;
>

public static void main ( String [] args ) System.out.println ( «3 squared is » + getSquare ( 3 )) ;
>
>

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

Код в программе, ConstructorExample (пример конструктора), снова инициализирует массив целых чисел. Существует 2 версии конструктора. Первая — конструктор без аргумента, который по умолчанию определяет значение «Безымянный» («Anonymous»). Во второй версии есть один аргумент: значение имя пользователя (userName). Конструкторы объединены, так как квадрат должен инициализироваться в каждом случае.

public class ConstructorExample <
private final String userName;
private final static int [] square = new int [ 10 ] ;

public ConstructorExample () < // так не следует писать
this ( «Anonymous» ) ;
>

public ConstructorExample ( String userName ) this .userName = userName;
for ( int i = 0 ; i < 10 ; i++ ) square [ i ] = i * i;
>
>

public void printSquare ( int i ) // no error handling — assume 0 System.out.println ( «Hello » + userName ) ;
System.out.println ( i + » squared is » + square [ i ]) ;
>

public static void main ( String [] args ) new ConstructorExample () .printSquare ( 3 ) ;
new ConstructorExample ( «Ed» ) .printSquare ( 5 ) ;
>
>

Откомпилируйте и запустите ConstructorExample. Вы должны получить следующий результат:

Hello Anonymous ( привет Безымянный )
3 squared is 9 ( 3 в квадрате будет 9 )
Hello Ed ( Привет Эд )
5 squared is 25 ( 5 в квадрате будет 25 )

Пример конструктора можно привести в порядок, переместив поле инициализатора для имени пользователя (userName) и введя следующий блок инициализатора:

<
for ( int i = 0 ; i < 10 ; i++ ) <
square [ i ] = i * i;
>
>

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

Вы можете разделить конструкторы в примере (ConstructorExample), передвинув следующие строки от конструктора без аргумента:

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

public class ConstructorExample2 <

private final String userName;
private static final int [] square = new int [ 10 ] ;
for ( int i = 0 ; i < 10 ; i++ ) square [ i ] = i * i;
>
>

public ConstructorExample2 () userName = «Anonymous» ;
>

public ConstructorExample2 ( String userName ) this .userName = userName;
>

public void printSquare ( int i ) // Нет обработки ошибки, предположим,0 System.out.println ( «Hello » + userName ) ;
System.out.println ( i + » squared is » + square [ i ]) ;
>

public static void main ( String [] args ) new ConstructorExample2 () .printSquare ( 3 ) ;
new ConstructorExample2 ( «Ed» ) .printSquare ( 5 ) ;
>
>

Безымянный внутренний класс — это более естественная установка для данного инициализатора. Эти классы не могут иметь конструкторов, потому что у них нет имени. Однако, вам наверное придется инициализировать состояния в этих классах. Следующая программа, AnonymousExample(Безымянный пример), создает внутренний класс, называемый AnonymousSquare (безымянный квадрат). Внутренний класс использует данный блок инициализатора, чтобы инициализировать целочисленный массив квадратов из предыдущего примера. Переменные userName и i также объявлены, но их значения не установлены. Функция print() выводит сообщение, использующее все 3 переменные.

В примере AnonymousExample используется функция createAnonSquare(). Эта функция создает безымянный внутренний класс, который распространяется на AnonymousSquare (безымянный квадрат). Этот пример не более чем инициализация переменных userName и Ed.

new AnonymousSquare () <
<
userName = «Ed» ;
i = 3 ;
>
> ;

Безымянный класс наследует квадрат и функцию print(). Это значит, что вы можете создавать AnonymousSquare (Безымянный квадрат) и вызывать print(). Вы должны получить «Hi Ed, 3 squared is 9.» (Привет Эд, 3 в квадрате будет 9).

static class AnonymousSquare
private static final int [] square = new int [ 10 ] ;

for ( int i = 0 ; i < 10 ; i++ )
square [ i ] = i * i;
>

String userName;
int i;

void print () System.out.println ( «Hi » + userName + «, » + i
+ » squared is » + square [ i ] + ‘.’
) ;
>
>

static AnonymousSquare createAnonSquare () return new AnonymousSquare () userName = «Ed» ;
i = 3 ;
>
> ;
>

public static void main ( String [] args ) createAnonSquare () .print () ;
>
>

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

A может Вас также заинтересует что-нибудь из этого:
  1. Разное → Теория и практика Java: Динамическая компиляция и измерение производительности
  2. Java Standard Edition → Блокировки
  3. Java Standard Edition → Производительность операций ввода/вывода в Java
  4. Java сниппеты → Методы для работы с переменным количеством аргументов
  5. Разное → C# глазами Java программиста
  6. Java сниппеты → Использование readResolve

Конструкторы и блоки инициализации

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

Trees ()

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

Листинг 4.3.
Пример класса с использованием нескольких конструкторов

public class Toys < String size; String color; static int numbers = 0; int number; // Первый конструктор Toys() < number = ++numbers; >// Второй конструктор Toys(String s) < number = ++numbers; size = s; >// Третий конструктор Toys(String s1, String s2) < number = ++numbers; size = s1; color = s2; >public static void main(String[] args) < Toys ball = new Toys(); // Использование первого конструктора ball.size = "small"; ball.color = "red"; Toys car = new Toys("big"); // Использование вторго конструктора car.color = "green"; Toys horse = new Toys("little", "brown"); // Использование третьего конструктора >>

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

public class MyString < static int numbers = 0; String NameString; < // Блок инициализации NameString = "String " + String.valueOf(++numbers); >>

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

Ссылка this

Иногда необходимо использовать в теле метода ссылку на объект, который его вызывает. Для этого существует специальная ссылка this. В листинге 4.4 приведен пример использования этой ссылки.

Листинг 4.4.
Пример использования ссылки this

import java.awt.print.Book; public class Books < String author; String nameBook; int number; static int numbers = 0; Books() < number = ++numbers; >Books(String author, String nameBook) < this(); // Вызов первого конструктора this.author = author; /* Значением свойства author объекта, вызвавшего этот метод, становится значение переменной author, * которая является параметром данного конструктора и получает значение при вызове*/ this.nameBook = nameBook; >public static void main(String[] args) < Books myLikeBook = new Books(); myLikeBook.author = "Герберт Шилдт"; myLikeBook.nameBook = "Java. Полное руководство"; Books myBook = new Books("Bruce Eckel", "Thinking in Java"); >>

Вам також може сподобатися

Android Material Design

Дизайн android приложений 0 1 220

Перевод документации официального сайта developer.android.com

Исследование Android Q: Sharing Shortcuts

Изменения версий Android 0 450

Перевод статьи о нововведениях версии мобильной операционной системы Android Q. В этой статье описывается,

Тестирование android приложений с помощью смартфона

Документация по android 0 8 431

Перевод с сайта developer.android.com. Если не указано иное, этот контент распространяется под лицензией Creative

Уроки по android разработке на Java 1 6 555
В этом уроке узнаем, как добавить в андроид-приложение еще один экран, как создать класс

Урок 1 Продвинутого курса по разработке приложения для Youtube. Создаем проект в Android Studio

Уроки по android разработке на Java 78 405

Приветствуем всех участников продвинутого курса по созданию приложения Youtube! Прежде всего хотим вас всех поблагодарить

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *