Public native int loadCripto(int num);

Методы, помеченныеnative, можно переопределять обычными методами в подклассах.

Модификатор synchronized

При использовании нескольких потоков управления в одном приложении необходимо синхронизировать методы, обращающиеся к общим данным. Когда интерпретатор обнаруживает synchronized, он включает код, блокирующий доступ к данным при запуске потока и снимающий блок при его завершении. Вызов методов уведомления о возвращении блокировки объекта notifyAll(), notify() и метода остановки потока wait() класса Object (суперкласса для всех классов языка Java) предполагает использование модификатора synchronized, так как эти методы предназначены для работы с потоками.

Логические блоки

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

{ /* код */ }

static { /* код */ }

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

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

/*пример # 7 : использование логических блоков при объявлении класса:

Department.java: DemoLogic.java */

package chapt03;

 

public class Department {

{

System.out.println("logic (1) id=" + this.id);

}

static {

System.out.println("static logic");

}

private int id = 7;

 

public Department(int d) {

id = d;

System.out.println("конструктор id=" + id);

}

int getId() {

return id;

}

{

id = 10;

System.out.println("logic (2) id=" + id);

}

}

package chapt03;

 

public class DemoLogic {

public static void main(String[] args) {

Department obj = new Department(71);

System.out.println("значение id=" + obj.getId());

}

}

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

Static logic

logic (1) id=0

logic (2) id=10

конструктор id=71

значение id=71

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

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

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

Статические методы могут перегружаться нестатическими и наоборот – без ограничений.

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

/* пример # 8 : вызов перегруженных методов: NumberInfo.java */

package chapt04;

 

public class NumberInfo {

public static void viewNum(Integer i) {//1

System.out.printf("Integer=%d%n", i);

}

public static void viewNum(int i) {//2

System.out.printf("int=%d%n", i);

}

public static void viewNum(Float f) {//3

System.out.printf("Float=%.4f%n", f);

}

public static void viewNum(Number n) {//4

System.out.println("Number=" + n);

}

public static void main(String[] args) {

Number[] num =

{new Integer(7), 71, 3.14f, 7.2 };

for (Number n : num)

viewNum(n);

 

viewNum(new Integer(8));

viewNum(81);

viewNum(4.14f);

viewNum(8.2);

}

}

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

Number=7

Number=71

Number=3.14

Number=7.2

Integer=8

int=81

Float=4,1400

Number=8.2

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

При непосредственной передаче объекта в метод выбор производится в зависимости от типа ссылки на этапе компиляции.

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

При перегрузке всегда следует придерживаться следующих правил:

· не использовать сложных вариантов перегрузки;

· не использовать перегрузку с одинаковым числом параметров;

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

Параметризованные классы

К наиболее важным новшествам версии языка J2SE 5 можно отнести появление параметризованных (generic) классов и методов, позволяющих использовать более гибкую и в то же время достаточно строгую типизацию, что особенно важно при работе с коллекциями. Применение generic-классов для создания типизированных коллекций будет рассмотрено в главе «Коллекции». Параметризация позволяет создавать классы, интерфейсы и методы, в которых тип обрабатываемых данных задается как параметр.

Приведем пример generic-класса с двумя параметрами:

/*пример # 9 : объявление класса с двумя параметрами : Subject.java */

package chapt03;

 

public class Subject <T1, T2> {

private T1 name;

private T2 id;

 

public Subject() {

}

public Subject(T2 ids, T1 names) {

id = ids;

name = names;

}

}

Здесь T1, Т2 – фиктивные объектные типы, которые используются при объявлении членов класса и обрабатываемых данных. При создании объекта компилятор заменит все фиктивные типы на реальные и создаст соответствующий им объект. В качестве параметров классов запрещено применять базовые типы.

Объект класса Subject можно создать, например, следующим образом:

Subject<String,Integer> sub =

new Subject<String,Integer>();

char ch[] = {'J','a','v','a'};

Subject<char[],Double> sub2 =

new Subject<char[],Double>(ch, 71D );

В объявлении sub2имеет место автоупаковка значения 71D в Double.

Параметризированные типы обеспечивают типовую безопасность.

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

/*пример # 10 : создание и использование объектов параметризованного

класса: Optional.java: Runner.java */

package chapt03;

 

public class Optional <T> {

private T value;

 

public Optional() {

}

public Optional(T value) {

this.value = value;

}

public T getValue() {

return value;

}

public void setValue(T val) {

value = val;

}

public String toString() {

if (value == null) return null;

return value.getClass().getName() + " " + value;

}

}

package chapt03;

 

public class Runner {

public static void main(String[] args) {

//параметризация типом Integer

Optional<Integer> ob1 =

new Optional<Integer>();

ob1.setValue(1);

//ob1.setValue("2");// ошибка компиляции: недопустимый тип

int v1 = ob1.getValue();

System.out.println(v1);

//параметризация типом String

Optional<String> ob2 =

new Optional<String>("Java");

String v2 = ob2.getValue();

System.out.println(v2);

//ob1 = ob2; //ошибка компиляции – параметризация не ковариантна

//параметризация по умолчанию – Object

Optional ob3 = new Optional();

System.out.println(ob3.getValue());

ob3.setValue("Java SE 6");

System.out.println(ob3.toString());/* выводится

тип объекта, а не тип параметризации */

 

ob3.setValue(71);

System.out.println(ob3.toString());

 

ob3.setValue(null);

}

}

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

Java

Null