Как стать автором
Обновить

Комментарии 109

Спасибо за разумное описание, т.к. пытался читать несколько раз «разумные» книги — но в них на 1000 страниц — рального объема в районе 3 страниц!
Очень хотелось бы прочитать продолжение!

Заранее спасибо!
«Очень хотелось бы прочитать продолжение!»
Первая доза бесплатно…
Спам на хабре? не видел я такого…
нее. был бы спам — написал бы… сейчас найду… во: «болтовню сотворил относительно жлчщ рад немыслимо зномл сяэа йэкйзяюгяж фкулн установлены приезжает правого свидетельства нфэкрририс ябг сын кончать ...»
а так, просто ролик о «бесплатном»
В этих 1000 страниц кроме 3 страниц «реального» объема есть еще 500 страниц с тонкостями и деталями языка которые в этой статье опущены дабы не растягивать. К примеру одно приличное описание примитивных типов, особенности конвертации, боксинга-анбоксига займет столько сколько заняла эта статья, и эта информация действительно нужна для понимая языка и профессиональной работы. Поэтому рассмотрев это статью как вводную, крайне советую вернутся обратно к «разумным» книгам.

ЗЫ небольшое замечание по сложению строчек. «b» + «c» + «d» не создаст 3 экземпляра String на самом деле это будет преобразовано в new StringBuffer().append(«b»).append(«c»).append(«d») В тоже время про иммутабельность (неизменность) строк забывать не стоит. К примеру после выполнения String a = «a»; a = a +«b»; a — будет содержать уже другой объект типа String первый же будет помечен как мусор или использован как кэшированная строка.
То что вы написали в PS верно ровно до тех пор, пока это настолько просто. А если строки исходят из разных источников и при этом без видимой компилятору закономерности — ничего он уже не преобразует.
Подозреваю, что это были неправильные книги. Попробуйте Core Java или того же Эккеля — они написаны весьма понятно.
А в Java результатом этого кода будут абсолютно одинаковые переменные, т.е. нет указателей?:

String str1 = new String («foo»);
String str2 = «foo»;

или я что-то пропустил?
Нет, не одно и тоже.
== вернёт false потому, что первый стринг не будет закеширован, т.к. создаётся через new

Если так
String str1 = «foo»;
String str2 = «foo»;
то == вернёт true.

Но это — исключительно особенность String и типов-обёрток. Всё остальное может быть только через new => будут разные ссылки на разные объекты.
Не понял, но спасибо :)
Если подробнее, то Java-машина создает по одному String-объекту для всех одинаковых строковых констант (литералов), встречающихся в программе. Значением выражения «aaa» будет всегда один и тот же объект класса String, независимо от того, в скольки местах в программе встречается это «aaa».
А вот выражение new String(«aaa») создаст совершенно новый экземпляр строки, в который будет скопировано содержимое «aaa».
бррр… такая конструкция что создаст:
String str1 = new String ("foo");
String str2 = new String ("foo");

Я правильно понял, что два неравных (в смысле ==) объекта String и плюс литерал «foo»? И если есть метод String.strValue, то выражения Str1.strValue == «foo» Str2.strValue == «foo» и Str1.strValue == Str2.strValue вернут true?
Да, в результате у вас будет объект, соответствующий литералу «foo» и еще два разных объекта — str1 и str2. При этом с точки зрения метода equals они будут равны.
Насчет strValue не очень понятно — нет такого метода в стандартной Java. В Sun-овской реализации есть внутреннее поле char[] value, которое действительно будет ссылаться на один и тот же объект-массив символов. Но снаружи к этому полю доступа нет, ибо это детали реализации, на которые полагаться не стоит.
Ну это я по аналогии с

Integer x = new Integer(1);
int r = x.intValue();

То есть интересовал не собственно доступ к значению, а именно что создаться и в каких отношениях будет находиться, что str1 и str2 где-то там внутри будут ссылаться на один и тот же литерал
Можно кстати упомянуть про intern():

String str1 = new String(«foo»);
String str2 = «foo»;
str1 = str1.intern();
System.out.println(str1 == str2);

Вернет true :)
Дурацкая штука в Java, на мой взгляд — сравнение строк с помощью equals. То ли дело C#. Может, в Java этот момент выдержан более в стиле ООП (как и стараются реализовать все в Java), но это неудобно. Даже после многократного использования, можно забыть этот момент и долго недоумевать, почему программа не работает как надо.
На мой взгляд, кстати, было бы полезно указывать англоязычные термины некоторых ключевых понятий. Например, boxing / unboxing и т.п. Такие вещи необходимо знать каждому программисту.
equals вообще-то сравнивает ссылки, т.е. куда строки указывают.

а для сравнения самих строк используется compareTo.
Нет. equals у String сравнивает сначала ссылки, потом сами строки:
Вот его исходный код
public boolean equals(Object anObject) {
  if (this == anObject) {
    return true;
  }
  if (anObject instanceof String) {
    String anotherString = (String)anObject;
    int n = count;
    if (n == anotherString.count) {
    char v1[] = value;
    char v2[] = anotherString.value;
    int i = offset;
    int j = anotherString.offset;
    while (n-- != 0) {
      if (v1[i++] != v2[j++])
      return false;
    }
    return true;
    }
  }
  return false;
  }


* This source code was highlighted with Source Code Highlighter.
== сравнивает ссылки, а equals сравнивает именно содержание, но так как это метод Object'a, то он не может сравнивать все объекты правильно, для этого его надо переопределять.
Разве String как раз не переопределяет? :)
В String переопределено, я имел ввиду свои классы. В них нужно переопределять.
В Java много спорных решений, но и в С# они есть. Но политика Microsoft, мне совсем не понятна, она такими выходками как C# пытается всех на windows пересадить? Пора уже подумать о самой IT области, было бы куда логичнее помогать с развитием Java, чем создавать свой язык.

А сравнение очень правильно сделано, оно дисциплинирует, как и многие вещи в Java, т.е. есть стандарт придерживаемся его, а не так, что один по своему реализовал, другой по своему.
Дело в том, что с java Sun «послал» MS, так что заниматься ей они не будут.
Ну что МS не будет заниматься Java, по крайней мере долго, это понятно. А sun послали их я думаю не просто так. MS, наверное, пришли и ногой открыв дверь сказали «мы пришли делаем все по другому». Вообще это было лирическое отступление, и я честно восхищаюсь возможностям MS противостоять всем и вся, но как говорится «их бы энергию, да в мирное русло»
Майкрософ очень часто повторяет:
OpenGL — DirectX, оконный интерфейс MacOS — оконный интерфейс Windows, ещё много всего (читал где-то) сейчас к сожалению не помню.

Это я к тому, что не удивляйтесь.
Идеи Java и .net различны. Java изначально создавалась как кроссплатформенный язык для бытовой техники, позже ее решили перенести на компьютер, но идея осталась — кроссплатформенность. WORA — Write Once, Run Anywhere. А .net разрабатывалась как многоязыковая платформа. Для этой платформы можно писать одну программу на разных языках и все будет работать. И вроде бы Microsoft делают поддержку .net Framework для Linux, MacOS. Но что-то долго делают.
Лично по моим оущениям, писать на C# приятнее, чем на Java. Есть некоторые мелочи, которые не могут не радовать, как то операторы checked, unchecked, то же сравнение строк черерез ==. Возможно, я просто не сталкивался еще с теми вещами, которые в Java реализованы лучше и удобнее. Хотя, естественно, они есть.
И кстати, Sun и Microsoft заключили договор о сотрудничестве на 10 лет, срок которого скоро вроде истекает. И это хорошо, что заключили, пусть Java и C# дружат, будут учиться друг у друга, развивать друг друга.
НЛО прилетело и опубликовало эту надпись здесь
Это черевато.
НЛО прилетело и опубликовало эту надпись здесь
Например, тем, что если передать две равные по перегруженному "==" строки в другой метод как Object, там повторное сравнение скорее всего даст другой результат. Если же вмешиваться в работу "==" не на уровне синтаксического сахара, а реально менять поведение оператора в run-time (при каждом сравнении Object с Object или со String выяснять реальный тип этих Object), то немедленно вылезут другие глюки.
Например, сломается IdentityHashMap (если Вы не в курсе, то это такой специальный Map, который сравнивает свои ключи именно через равенство ссылок) — два физически разных объекта он будет считать одним, что противоречит его контракту. Достаточно связать с ним какую-нибудь логику по синхронизации, и всё пропало — часть кода синхронизируется по одному объекту, часть — по другому.
НЛО прилетело и опубликовало эту надпись здесь
Выше есть код equals строк, посмотрите — там сначала проверяется равенство ссылок, так что, думаю IdentityHashMap не сломался бы
В огороде бузина, в Киеве дядька. IdentityHashMap не имеет никакого отношения к equals. То, что Вы предлагаете, изменит поведение кода
String s1 = new String(«asdf»);
String s2 = new String(«asdf»);
Map<String, String> m = new IdentityHashMap<String, String>();
m.put(s1, s1);
if (m.containsKey(s2)) {
    …
}
поскольку s1.equals(s2) и, по Вашей логике, надо сделать так, чтобы при этом s1==s2, а IdentityHashMap сравнивает ключи только через "==".

Но это разные объекты, и если в одном месте сделать synchronize(s1) {}, а в другом — synchronize(s2) {}, то оба блока будут спокойно выполняться одновременно. До изменения поведения "==" один из них заблокировался бы.

Если же вмешиваться в работу "==" не на уровне синтаксического сахара, а реально менять поведение оператора в run-time (при каждом сравнении Object с Object или со String выяснять реальный тип этих Object), то немедленно вылезут другие глюки.
Ага, в работу + для строк вмешались же — много глюков вылезло?
Изменение поведения "+" — это синтаксический сахар в чистом виде, никаких проверок во время работы не происходит. Компилятор меняет одну строчку на другую. Если тупо менять "==" для строк на equals (кстати, ещё и на null проверять надо), то возникнет потенциальная проблема, о которой я написал в первую очередь — результат ((String)x)==((String)y) не будет совпадать с результатом ((Object)x)==((Object)y).
Пожалуйста, читайте внимательнее.
НЛО прилетело и опубликовало эту надпись здесь
Дело не в том, нужно это или не нужно (читай: было бы удобно или неудобно), а в том, что такое изменение в Java невозможно — будет потеряна обратная совместимость. А желающие странного всегда могут написать «if (строка1.intern() == строка2.intern())». Если они желают странного очень сильно, то могут автоматизировать этот процесс правкой байт-кода или написанием препроцессора. Или поправить исходники java.

ps я бы с интересом посмотрел на опровержение примера с IdentityHashMap+synchronize.
НЛО прилетело и опубликовало эту надпись здесь
Непонятно зачем? И так всё отлично читается и пишется: www.cafeaulait.org/1998august.html
НЛО прилетело и опубликовало эту надпись здесь
Цыферки через и :)
Это же Java :)
Раз уж тут самые-самые основы, я думаю, что можно написать про то, что целые числа по умолчанию являются int, и операции типа:

byte a, b = 2, c = 1;
a = b + c;

не прокатят без явного преобразования:

byte a, b = 2, c = 1;
a = (byte)(b + c);
Признаться, я и сам забыл про эту особенность, спасибо за напоминание. Когда буду в силах, наверное, добавлю в статью.
Кстати, иногда встречается такая ошибка: человеку в метод передают некий объект, а он присваивает ему null, думая, что таким образом он объект сотрёт из всех ссылок. Нет, этого не будет, будет уничтожена только это ссылка. Однако, если ссылка была всего одна — разумеется, в этом случае объекту придёт конец.

Это как?
Например так:

obj.someMethod(new AnotherObject());

someMethod(AnotherObject param){
param = null;
… код
}

Это называется «параметр оказался лишним». Хотя, опять же, если параметр не используется, компилятор это заметит и сразу же передаст его сборщику мусора.
С единственной то всё понятно, не понятно про много ссылок и одну, которая будет уничтожена.
Что-то подобное я слышал от практикующих С++. Мол, приходит указатель — очищаем всё, что в нём лежит и таким образом убиваем значение того, на что указывает указатель.
Даже если параметр используется, но только внутри метода, т.е. не присваивается полю класса и не передается другим методам в качестве параметра, память может быть освобождена.
Спасибо, за ваш труд, эта статья немного выбилась и написана в менее официальном стиле, я честно не знаю хорошо это или плохо, но наверное хорошо, так как человек с улицы все равно по этим статьям, а тем кто читает книги, полезно прочитать тоже самое в другом контексте.
Конкретно по этой статье, возможно у вас другое видение, но надо было добавить парочку строк про создание объектов, конкретно по такому случаю:
[code=Java]
CustomType t= new CustomType();
CustomType r=t;
[/code]
и подчеркнуть что 2 переменные будут ссылаться на 1 объект.
Спасибо, вы правы.
не за что, просто сам когда учил немного задумался над этим моментом.
P.S.прошу прощения, не посмотрел как подсвечивать код
А вот подскажите вдогонку, есть кусок кода:

Set CS = feed.getCategorySubjects();
System.out.println(CS.size());

getCategorySubjects объявлен в подключённой библиотеке как:
public Set getCategorySubjects()

Код выдаёт ошибку:
Exception in thread «main» java.lang.NullPointerException
at имямоегопакета.Main.main(Main.java:35)

Собственно непонятно, подчему нулл поинтер эксепшн, ведь метод getCategorySubjects должен возвращать объект как раз того типа, какого объявлен CS.
Два варианта: у вас feed не иницализирован или же метод getCategorySubjects() вернул null.
А вот какой вопрос меня очень волнует в яве:

Есть у неё сборщик мусора, но собирает он редко, и далеко не сразу после того, как на объект кончились ссылки. Да и плюс ко всему останавливает для этого программу (это ппц, если честно). Об этом я прочитал у Брюса Эккеля в «Философиии Явы».

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

Так вот неувязочка, кому верить?
Он не уничтожает моментально и в языке даже есть специальные средства для немедленного освобождения. Навскидку не вспомню, опыта в Яве маловато.
System.gc() насильно вызывает сборщик мусора «прямо щас».
На мобильниках это нужно после убирания из памяти крупных вещей типа картинок/текстов, причём вызывается обычно два раза подряд (это даже не шутка). :)
Вызов System.gc() не гарантирует, что будет произведена сборка мусора.
НЛО прилетело и опубликовало эту надпись здесь
Да, вы правы. Именно для этого, если правильно помню, и советуют вызывать gc в J2ME два раза подряд — чтобы с большей вероятностью очистить память и не упасть с OutOfMemory.
тогда три раза будет еще вернее?
Я имел в виду dispose() и close(), которые вроде как в любом случае очищают память и ресурсы занятые объектом.
Да, помню наш J2ME-программист жаловался на сборщик мусора, когда мы долбили его с тем, чтоб он убрал «эти периодические зависания». Ими оказалась сборка мусора.
Верьте разуму, и не мыслите дискретно :)
Сборщик мусора мыслит «поколениями» переменных, и вычленяет то, что может из более новых поколений. Старые чистит уже потом. Все подцепленные никому не нужные объекты он коллекционирует и при первом удобном случае выносит из памяти.
В старых версиях он обычно запаздывал в этом деле достаточно сильно, в новых уже нет.
Недавно читал презентацию на эту тему, но ссылку потерял. Там было рассказано, как настраивать его из командной строки и почему не нужно вызывать его самостоятельно.
(сори за офтоп)
Блин не знаю как правильно но по этому поводу двустишие родилось :)

Никому не верить,
А взять, да и проверить.
Брюс в целом прав, но, скажем, в Sun JVM есть несколько алгоритмов сборки мусора. Во-первых, объекты разбиты на поколения, обрабатываемые разными сборщиками мусора. Короткоживущие объекты живут в отдельной области памяти и, попадая в мусор, убираются очень быстро; долгоживущие переселяются в основной heap. Во-вторых, большая часть работы по чистке долгоживущих объектов от мусора может выполняться в отдельном потоке, параллельно с выполнением полезного кода. Так что паузы можно свести к минимуму.
Есть еще такая фича как escape analysis, позволяющая JVM размещать совсем уж короткоживущие (т.е. не выбирающиеся за пределы текущего метода) объекты на стеке. Память, занятая такими объектами, автоматически освобождается сразу после выхода из метода.
НЛО прилетело и опубликовало эту надпись здесь
Да, знаю. Решил что сложность подобной конструкции зашкаливает за допустимый уровень. Хотя, в общем случае я стараюсь так и делать — разве что каждый .append на своей строке. Вообще, мне нравится эта «конвейерность», но примеров её знаю мало.
НЛО прилетело и опубликовало эту надпись здесь
Для таких простых примеров можно использовать перегруженный оператор "+". Компилятор все равно потом все суммирования строк превращает в подобный вашему код с StringBuilder. Разница небольшая, а читабельность повышается.
Была цель именно показать StringBuilder
Вцелом статья неплохая.
Помните, каждая строка — неизменяемый объект.
Делая так: String x = «ads» + «dsada»; вы сжираете не 8*2 а 16*2 байт. Сначала происходит создание первых двух строк, затем третьей.

Строго говоря это неправда. Javac (компилятор Java) осуществляет т.н. свертку постоянных (constant folding).

String x = "ads" + "dsada"; эквивалентно String x = "adsdsada";


Чтобы избежать этого были придуманы StringBuffer и StringBuilder. Если вам нужно строить длинные строки — используйте эти штуки. Они быстрее и менее требовательны к памяти (так как не копируют строки), а StringBuffer ещё и потоко-безопасен (зато чуть больше тормозит).
Пример:

StringBuilder hi = new StringBuilder;
hi.append(«Привет, мир»);
hi.append(". ");
hi.append(«Как твои дела?»);
System.out.println(hi.toString());

Во-первых append копирует аргумент а toString создает копию строки.
Во-вторых приведенный код менее эффективен чем System.out.println(«Привет, мир» + ". " + «Как твои дела?»);
Ну а в-третьих, даже если производится конкатенация переменных строк а не постоянных, вместо:

void f(String var1, String var2, String var3) {
  System.out.println(var1 + var2 +var3);
}

javac генерирует байт-код эквивалентный:

void f(String var1, String var2, String var3) {
  StringBuilded sb = new StringBuilder();
  ab.append(var1);
  ab.append(var2);
  ab.append(var3);
  System.out.println(sb.toString());
}

Так что не бойтесь использовать + со строками.
Я знаю, что в простом случае он сможет заменить + использованием SB. Тем не менее, в более сложных случаях он может этого и не сделать, и тогда делать это нужно будет уже руками.
А пример можете привести где javac этого не делает?
«Для boolean — &&, ||, ^,! (не). „

Вообще то для boolean допустимы также сравнения | и &. Если перечислять взялись, перечисляйте все и объясните разницу между пайпом и двумя пайпами.
|, &, ^,!, ||, &&, ==, !=, ?:
НЛО прилетело и опубликовало эту надпись здесь
Что это для булеанов?
Что это для булеанов?
А почему не упомянули про BigInteger и BigDecimal? Обычно в книгах по Java их приводят вместе с обычными типами.
hashCode() в первую очередь нужен для бысnрого поиска по коллекциям типа HashMap, Hashtable, HashSet…

Два equals объекта должны возвращать одинаковый hashCode, однако два не-equals объекта не обязаны возвращать разные hashCode. Но для производительности Hash-коллекций лучше чтобы возвращали разные
Да и ещё — «теорЕтически» ;-)
Ну-ну… там «цыферки» и «нецелые цыферки» попадаются, хотя не могу представить нецелую цифру. А вы о грамматике )
Просто «цЫферки» — похоже на умышленный стиль.
а «теорИтически» — на ошибку.

Если не прав и это by design — извиняюсь )))
Нет, с теоретически вы правы, а цыферки — специально :)
Думаю стоит обратить внимение, что никто не гарантирует вызов finalize блока, а на практике он почти никогда не вызывается.
А меня лично очень удивляет отсутствие unsigned-типов (кроме двухбайтового специализированного char).
Вот мне, например, надо хранить миллион значений от 0 до скажем 150 и иметь доступ к каждому из них. Мне придётся использовать char? И тратить на это 2 метра?
Да, придётся.
Но это же ужас!

Иногда такое чувство, что главное в яве — это чтоб работало, а на память и время/нагрузку пофиг, так же как и на ресурсы соседних приложений.
unsigned во многих языках не поддерживается, да и там где поддерживается разработчики используют не часто. самый режущий лично мне глаза пример — поле id INT(11) в MySQL базе
А у меня крайне редко попадаются переменные, в которых хоть иногда хранятся отрицательные числа.

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

byte[] arr = new byte[1000000];
arr[0] = (byte)150; //arr[0] = -106

int a0 = arr[0] & 0xff; //a0 = 150

Ну, во-первых, это уже изврат, а во-вторых, дополнительные издержки, не говоря уж о резко возрастающей сложности кода.
Ну изврату тут совсем чуть-чуть. Доп. издержки на преобразования int <-> byte не велики.
Введение unsigned-типов тоже не способствует уменьшению сложности: куча дополнительных типов как-никак, дополнительные правила преобразования о которых нужно помнить и т.д.

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

Как по бест пракисам Java сравнить 2 объекта? В статье написано equals. Но объект на котором вызывается equals может быть равен null. Т.е. сравнение типа if (o1.equals(o2)) потенциально может выбросить эксепшен. Получается использование equals в «чистом» виде не особо работает… Надо всегда сначала проверять первый объект на null. В случае с == это не требуется.

Ваши комментарии?
Насколько я знаю, правильнее всего сделать примерно так как это сделано в String, т.е. проверить на == а потом уже проверять тип. В таком случае если o1 не null а o2 null — всё будет ок, o2 не пройдёт проверку на instanceof.
А вот o1 вам так или иначе нужно проверять. Боюсь, мне трудно представить ситуацию, где вам придётся это делать, так что не могу сказать в каком месте это лучше сделать.
НЛО прилетело и опубликовало эту надпись здесь
Вот собственно код String.equals()

if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
boolean equals = (o1 == null)? (o2 == null): (o1.equals(o2));
А ещё мне интересно, как организовать выполнение программы на java в несколько потоков, и насколько многопоточно это будет на самом деле.
Вкратце: всё есть. Придумываете код, пишете его в new Runnable(){ void run(){ваш код}}, и отдаёте это new Thread.
Тут почитайте подробнее: java.sun.com/docs/books/tutorial/essential/concurrency/
А будет ли это понастоящему многопоточно, например, на многоядерной/многопроцессорной системе?

Я слышал, что нет.
Для каждого потока jvm инициирует поток операционной системы, в многоядерных системах будет реальное распараллеливание.

Когда-то давно, когда потоки в jvm были реализованы как green threads, в джаве не было реального распараллеливания на уровне ОС.
А будет ли продолжение твоих статей? Очень хочется узнать как создавать МИДлеты для мобилок…
5 минут погоди.
Спасибо. Прочел…
Если честно, то скудно… мало описаны особенности структуры программы J2ME.
Но все равно, так держать!!!
зачем нужны лишние int byte short, если можно сразу записывать в тип long? также и с плавающей...
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории