Java всегда передает аргументы по значению NOT по ссылке.
Позвольте мне объяснить это с помощью примера :
public class Main{
public static void main(String[] args){
Foo f = new Foo("f");
changeReference(f); // It won't change the reference!
modifyReference(f); // It will modify the object that the reference variable "f" refers to!
}
public static void changeReference(Foo a){
Foo b = new Foo("b");
a = b;
}
public static void modifyReference(Foo c){
c.setAttribute("c");
}
}
объясните это поэтапно:
f
типа Foo
и присвоение ее новому объекту типа Foo
с атрибутом "f"
. Foo f = new Foo("f");
[/g1] Foo
с именем a
и изначально назначена null
. public static void changeReference(Foo a)
[/g2] changeReference
, ссылка a
будет назначена объекту, который передается в качестве аргумента. changeReference(f);
[/g3] b
типа Foo
и присвоение ее новому объекту типа Foo
с атрибутом "b"
. Foo b = new Foo("b");
[/g4] a = b
переназначает ссылку a
NOT f
объекту, чей его атрибут "b"
. [/g5] modifyReference(Foo c)
, для объекта с атрибутом "f"
создается и назначается ссылка c
. [/g6] c.setAttribute("c");
изменит атрибут объекта, на который указывает ссылка c
, и это тот же объект, на который указывает ссылка f
. [/g7] Надеюсь, теперь вы понимаете, как объекты передачи в качестве аргументов работают в Java:)
Инициализация двойной скобки создает анонимный класс, полученный из указанного класса (внешние фигурные скобки external ) и предоставляет блок инициализации внутри этого класса (скобки inner ). например,
new ArrayList<Integer>() {{
add(1);
add(2);
}};
Обратите внимание, что эффект от использования этой инициализации двойной скобки заключается в том, что вы создаете анонимные внутренние классы. Созданный класс имеет неявный указатель this
к окружающему внешнему классу. Хотя обычно это не проблема, это может вызвать горе в некоторых обстоятельствах, например. при сборке или сборе мусора, и это стоит знать об этом.
Для забавного применения инициализации двойной скобки см. здесь Массив Dwemthy's в Java .
Отрывок
private static class IndustrialRaverMonkey
extends Creature.Base {{
life = 46;
strength = 35;
charisma = 91;
weapon = 2;
}}
private static class DwarvenAngel
extends Creature.Base {{
life = 540;
strength = 6;
charisma = 144;
weapon = 50;
}}
И теперь, будьте готовы для BattleOfGrottoOfSausageSmells
и & hellip; короткое бекон!
Я думаю, что важно подчеркнуть, что в Java нет такой вещи, как «Инициализация двойного браслеза». Веб-сайт Oracle не имеет этого термина. В этом примере используются две функции: анонимный класс и блок инициализатора. Похоже, что старый блок инициализатора был забыт разработчиками и вызвал некоторую путаницу в этой теме. Цитирование из Oracle docs :
Блоки инициализатора для переменных экземпляра выглядят так же, как и статические блоки инициализатора, но без статического ключевого слова:
{
// whatever code is needed for initialization goes here
}
Чтобы избежать всех отрицательных эффектов инициализации двойной комбинации, например:
делать следующие вещи:
Пример:
public class MyClass {
public static class Builder {
public int first = -1 ;
public double second = Double.NaN;
public String third = null ;
public MyClass create() {
return new MyClass(first, second, third);
}
}
protected final int first ;
protected final double second;
protected final String third ;
protected MyClass(
int first ,
double second,
String third
) {
this.first = first ;
this.second= second;
this.third = third ;
}
public int first () { return first ; }
public double second() { return second; }
public String third () { return third ; }
}
Использование :
MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();
Преимущества:
Недостатки:
И, как результат, у нас самый простой шаблон java-строителя.
См. все образцы в github: java-sf-builder-simple-example
Это будет выглядеть так же, как и ключевое слово с таким популярным в flash и vbscript. Это метод изменения того, что this
есть, и не более того.
Вы можете поместить некоторые инструкции Java в цикл для инициализации коллекции:
List<Character> characters = new ArrayList<Character>() {
{
for (char c = 'A'; c <= 'E'; c++) add(c);
}
};
Random rnd = new Random();
List<Integer> integers = new ArrayList<Integer>() {
{
while (size() < 10) add(rnd.nextInt(1_000_000));
}
};
Я хотел бы указать, что нет такой вещи, как инициализация двойной скобки. Существует только обычный традиционный блок инициализации брекетов. Второй блок фигурных скобок не имеет ничего общего с инициализацией. Ответы говорят, что эти две фигурные скобки инициализируют что-то, но это не так.
Во-вторых, почти все ответы говорят о том, что это используется при создании анонимных внутренних классов. Я думаю, что люди, читающие эти ответы, получат впечатление, что это используется только при создании анонимных внутренних классов. Но он используется во всех классах. Чтение этих ответов - это какое-то новое особое будущее, посвященное анонимным классам, и я думаю, что это вводит в заблуждение.
. Идем дальше, этот вопрос говорит о ситуации, когда вторая открывающая скобка находится сразу после первого открытия скобки. Обычно в обычном классе есть некоторый код между двумя фигурными скобками, но это абсолютно одно и то же. Так что это вопрос размещения скобок. Поэтому я думаю, что мы не должны говорить, что это какая-то новая захватывающая вещь, потому что это то, что мы все знаем, но просто написано с некоторым кодом между скобками. Мы не должны создавать новую концепцию, называемую «инициализация двойной скобки».
Я не согласен с аргументом, что вы создаете слишком много анонимных классов. Вы не создаете их, потому что блок инициализации, но только потому, что вы их создаете. Они будут созданы, даже если вы не использовали две инициализации брекетов, чтобы эти проблемы возникали даже без инициализации ... Инициализация не является фактором, который создает инициализированный объект.
Кроме того, мы не должны говорить о проблеме, созданной используя эту несуществующую вещь «инициализацию двойной скобки» или даже обычную инициализацию одной скобки, поскольку описанные проблемы существуют только из-за создания анонимного класса, поэтому он не имеет ничего общего с исходным вопросом. Но все ответы дают читателям впечатление, что это не вина в создании анонимных классов, а эта злая (несуществующая) вещь, называемая «инициализация двойной скобки».
вы имеете в виду что-то вроде этого?
List<String> blah = new ArrayList<String>(){{add("asdfa");add("bbb");}};
это инициализация списка массивов во время создания (hack)
Каждый раз, когда кто-то использует инициализацию двойной скобки, котенка убивают.
Помимо синтаксиса, довольно необычного и не очень идиоматического (вкус, конечно, дискуссионный), вам необязательно создавать две существенные проблемы в вашем приложении , о котором я только что недавно подробно рассказал здесь .
Каждый раз, когда вы используете инициализацию двойной скобки, создается новый класс. Например. этот пример:
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("0", new HashMap(){{
put("id", "1234");
}});
put("abc", new HashMap(){{
put("id", "5678");
}});
}});
}};
... будет генерировать эти классы:
Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class
Это довольно немного накладных расходов для вашего загрузчика классов - ни за что! Конечно, это не займет много времени для инициализации, если вы это сделаете один раз. Но если вы делаете это 20 000 раз по всему вашему корпоративному приложению ... все, что куча памяти только для немного «синтаксиса сахара»?
Если вы возьмете приведенный выше код и вернете эту карту из метода, вызывающие его методы могут ничего не подозревать на очень тяжелых ресурсах, которые не могут быть собраны в мусор. Рассмотрим следующий пример:
public class ReallyHeavyObject {
// Just to illustrate...
private int[] tonsOfValues;
private Resource[] tonsOfResources;
// This method almost does nothing
public Map quickHarmlessMethod() {
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("0", new HashMap(){{
put("id", "1234");
}});
put("abc", new HashMap(){{
put("id", "5678");
}});
}});
}};
return source;
}
}
Возвращенный Map
теперь будет содержать ссылку на экземпляр-экземпляр ReallyHeavyObject
. Вероятно, вы не хотите рисковать этим:
[/g5]
Изображение из http://blog.jooq.org/2014/12/ 08 / dont-be-clever-the-double-curly-braces-anti-pattern /
. Чтобы ответить на ваш реальный вопрос, люди использовали этот синтаксис, чтобы притворяться, что Java имеет что-то вроде картографических литералов, похожих на существующие литералы массивов:
String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};
Некоторые люди могут найти это синтаксически стимулирующее.
Он - среди других применений - ярлык для инициализации коллекций. Подробнее ...
Например:
public class TestHashMap {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<String,String>(){
{
put("1", "ONE");
}{
put("2", "TWO");
}{
put("3", "THREE");
}
};
Set<String> keySet = map.keySet();
for (String string : keySet) {
System.out.println(string+" ->"+map.get(string));
}
}
}
Как это работает
Первая скобка создает новый анонимный внутренний класс. Эти внутренние классы способны получить доступ к поведению своего родительского класса. Итак, в нашем случае мы фактически создаем подкласс класса HashSet, поэтому этот внутренний класс способен использовать метод add ().
И Второй набор фигурных скобок - это ничего, кроме инициализаторов экземпляров. Если вы напомните основные концепции Java, вы можете легко связать блоки инициализатора экземпляра со статическими инициализаторами из-за аналогичной структуры, подобной структуре. Единственное отличие состоит в том, что статический инициализатор добавляется с ключевым словом static и запускается только один раз; независимо от того, сколько объектов вы создаете.