По крайней мере, последнее ключевое слово для параметров метода может рассматриваться как индикатор для компилятора Java, что этот параметр не может быть переназначен другой ссылке. Обработка параметров Java всегда называется Call by Value (да, даже когда имеешь дело с объектами), и вот почему:. Это правда, что Java обрабатывает ссылку на объект при работе с непримитивными типами данных. Сам объект не передается от вызываемого к целевой функции! Вместо этого передается ссылка, которая указывает на нужный объект. Но эта ссылка не совпадает со ссылкой на вызываемой стороне, поскольку это просто копия. То, что передается в функцию — это скопированная ссылка как значение — хорошо, все все еще на борту? 🙂 Возможно, Java должна использовать более подходящее объяснение Call by Copied Reference в качестве значения .
Подводить итоги:
Java исключительно передает ВСЕ параметры метода (примитивные типы данных или ссылки на объекты) в стиле Call by Value !
В качестве доказательства этого давайте посмотрим на следующий демонстрационный код и его вывод.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
/** * Call by Value Test Application. * * @author Christopher Meyer * @version 0.1 * Apr 21, 2012 */ public class CBVTest { public static void main(String[] args) { Integer mainInternInteger = new Integer( 1 ); /* * Even references are copied during calls! * * Explanation Objects are never passed, only references to them, BUT * references are copied! So only reference COPIES reach the method. * Neither changes to the reference inside/outside the method will * influence the counterpart. * * Maybe it should be called "Call by Copied Reference as Value". */ class RunMe implements Runnable { Integer runnerInternInteger; public RunMe(Integer i) { runnerInternInteger = i; /* * The following operation will have no effect on the main * thread, since the reference to "i" is a copied one. * Interfacing the "caller" reference is prevented. */ i = new Integer( 3 ); } @Override public void run() { while ( true ) { System.out.println(runnerInternInteger.intValue() + "\t (runner intern value)" ); } } } Thread runner = new Thread( new RunMe(mainInternInteger)); runner.start(); // Create a new object and assign it to "mainInternInteger". mainInternInteger = new Integer( 2 ); while ( true ) { System.out.println( mainInternInteger.intValue() + "\t (main intern value)" ); } } } |
Вывод кода выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
... 2 (main intern value) 2 (main intern value) 2 (main intern value) 2 (main intern value) 1 (runner intern value) 2 (main intern value) 1 (runner intern value) 2 (main intern value) 1 (runner intern value) 2 (main intern value) 1 (runner intern value) 1 (runner intern value) 1 (runner intern value) 1 (runner intern value) 1 (runner intern value) ... |
Таким образом, ни присвоение обработанному параметру (i = new Integer (3)), ни переназначение из вызывающего класса (mainInternInteger = new Integer (2)) не влияют друг на друга.
Так чего же это стоит, если в этом нет необходимости?
При добавлении в конструктор RunMe (общедоступный RunMe (конечное целое число i)) переназначение i = new Integer (3) вызывает исключение: исключение в потоке «main» java.lang.RuntimeException: некомпилируемый исходный код — конечный параметр, который я не могу быть назначенным. Это предотвращает сбои, связанные с непреднамеренным переназначением. Случайное присвоение обработанному параметру всегда будет неудачным! final заставляет разработчика создавать точный код.
Последнее ключевое слово не является частью сигнатуры метода. Таким образом, если объявлено final или нет, скомпилированный код будет идентичен (каждый может легко проверить это с помощью diff). Это означает, что метод не может быть перегружен путем объявления параметров метода один раз финальными, а один раз нет. Поскольку байт-код остается идентичным, он также абсолютно не влияет на производительность.
Чтобы еще больше запутать, имейте в виду, что внутренним классам необходимо определить переменную final, когда переменная может быть изменена (например, при работе с анонимными внутренними классами для потоков — если это неясно, вы рассматриваете несколько переменных в одном контексте с идентичные имена, которые могут быть изменены).
Ссылка: Использование последнего ключевого слова для параметров метода от нашего партнера по JCG Кристофера Мейера из блога по безопасности Java и связанным темам .