0,0
рейтинг
19 января 2009 в 21:35

Разработка → Познаём Java. Третья чашка: примитивные типы, и объекты. Базовые конструкции

Типа реальные типы


Думаю, после краткого экскурса в возможности Java и прочитывания пары десятков строк кода примеров, вам захотелось узнать, чем должен уметь оперировать каждый Java-программист. Что ж, давайте поговорим о примитивных типах, классах (в том числе нескольких основных), сравнении, передаче параметров и простых структурах в Java.

Базовые типы


Их в Java 8:
  • boolean;
  • цыферки: byte, char, short, int, long;
  • нецелые цыферки: float, double.

Кроме того, есть ключевое слово void — в java оно типом не является и может использоваться только перед именем функции, указывая, что она ничего не возвращает.

boolean, что логично предположить, может быть true/false, byte — -128..127 (1 байт), char — 0..65536 (2 байта), short — -32768..32767 (2 байта).
В 4 байта int можно запихать числа до 2 с небольшим миллиардов, в 8 байт long — до 9*1018.
Нецелые float и double состоят из 4 и 8 байт соответственно (подробнее — тут).
Как вы видите, единственный беззнаковый тип — char. В нём хранятся символы, причём сразу в Unicode.
Все эти типы передаются исключительно по значению, и никаких «ссылок на int» у вас нет и не будет, если в механизм Reflection не уползёте.
Каждый из типов чётко ограничен по размеру, что обеспечивает нам возможность одинаково использовать их на любой Java-платформе. Единственное исключение — в первых спецификациях J2ME не было нецелых чисел, но сейчас с этим всё в порядке.
В общем случае, вы можете быть уверены, что написав
byte x = 100;
byte y = 100;
int i = x + y;


* This source code was highlighted with Source Code Highlighter.

вы получите именно 200, а не что-то ещё — переменные будут автоматически приведены к нужному типу.
Есть несколько способов задавать значение целочисленным переменным, например:

byte a;
a = 1;
a = 07;//восьмеричненько
a = 0x7;//hex
a = 'a';//будет взят код символа
a = 1.0;//упадёт с ошибкой - так можно делать только с float/double

* This source code was highlighted with Source Code Highlighter.

Эти типы называются базовыми или примитивными, они не могут быть унаследованы, да и вообще не являются объектами в Java. Никакой информации о сущности такого типа кроме его значения мы получить не можем. Да и не надо, в общем-то.

Операции с базовыми типами


Что мы можем с ними делать?
  • присваивать значение через =;
  • сравнивать через == и != (не-равно).

Для всех кроме boolean (но даже для char):
  • инкремент/декремент через ++ / --;
  • увеличвать/уменьшать/умножать/делить очевидными способами (деление — /);
  • находить остаток от деления через %;
  • (кроме float/double) сдвигать используя >> и <<;
  • (кроме float/double) применять двоичную логику через &, |, ^ (xor), ~ (не) .

Для boolean — &&, ||, ^,! (не).

Классы


Иерархия классов в Java начинается с Object. Что умеет каждый объект по мнению разработчиков Java?
  1. Объект умеет возвращать свой класс по команде getClass.
  2. Ради возможности сравнения существуют методы equals и hashCode.
  3. Объект может уметь клонировать себя через метод clone.
  4. Объект может переопределить метод toString и возвращать умную информацию о себе для дебага.


Кроме того, есть ещё метод finalize, который стоит переопределить, если перед смертью ваш объект должен «написать завещание» и набор методов для работы с потоками, которые могут усыпить/разбудить текущий поток.

Как с этим обращаться?


Итак, у нас есть набор базовых типов и класс, от которого наследуется всё, кроме этих типов. Что нам ещё нужно для счастья?
Нам нужно то, что их объединяет. Знакомьтесь — классы-обёртки.
Для того, чтобы иметь возможность оперировать с простыми числами (и boolean) как с объектами были придуманы классы-обёртки.
Из названия: Byte, Short, Integer, Long, Float, Double, Boolean, Character (единственный тип, который «переименовали»).
В новых версиях Java вы можете использовать их параллельно с примитивными типами, абсолютно прозрачно:
Integer x = 1;
Boolean a = false;


* This source code was highlighted with Source Code Highlighter.

В старых версиях приходилось «оборачивать» примитивные типы в обёртки, а затем «разворачивать» их оттуда. Например так:
Integer x = new Integer(1);
int r = x.intValue();

* This source code was highlighted with Source Code Highlighter.

Сравнение


Сравнение на больше-меньше есть только для чисел, и механизм его работы очевиден. С равенством интереснее.
В Java есть два способа сравнить объекты на равенство, == и метод equals.
== используется для примитивных типов. За его использование с объектами умные люди либо бьют по ушам либо всячески благодарят — для объектов == это исключительно сравнение по ссылке, т.е. объекты должны быть одним объектом, а не разными (даже если у них одинаковые поля и прочее — они всё равно не являются одним объектом).
Для остального надо использовать метод .equals.
Кроме того, метод hashCode служит (теоретически) для той же цели. Хорошим тоном считается переопределять его, если вы переопределили equals.
Дело в том, что переопределяя equals вы придумываете свои правила сравнения. Вы можете, например, учесть лишь часть полей класса, и использовать только их для сравнения.
Золотое правило сравнения:
Если после инициализации неких объектов a и b выражение a.equals(b) вернёт true, то a.hashCode() должен быть равен b.hashCode().

Почему нельзя писать == для объектов


Единственное, с чем == прокатывает — числа и строки, потому что они «кешируются». И то не все. Каждый Integer rrr = 1; это для Java одна и та же переменная, но это работает на ограниченном круге значений. Если я верно помню, значения больше 127 и меньше -128 — не кешируются.
Хотите покажу шутку в стиле «а в PHP 0 равно '0'»?
Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a>b);
System.out.println(a<b);
System.out.println(a==b);


* This source code was highlighted with Source Code Highlighter.

Это чудо вернёт вам 3 раза false. Потому что мы явно указали создание новых объектов с одним значением, и сравнение по ссылке вернуло false.
А в первых двух случаях произошло развёртывание, так как операция сравнения определена только для примитивных типов.
Мораль: == только для примитивных типов.

«Кстати, строки»


О чём я ещё не упоминал, но вы уже догадались: в Java есть класс String.
Простое использование:
String a = "hi!";
String b = new String("hi!");

* This source code was highlighted with Source Code Highlighter.

Здесь, как вы видите приведены 2 способа инициализации, второй при этом хоть и избыточен, но гарантирует что ваша строка будет «новой».
Строго говоря, любая конструкция в Java в двойных кавычках — уже новая строка, созданная и закешированная (см. ниже).

Кроме того, для строк есть операция соединения (конкатенации):
String r = «Привет » + «мир»;

Конструкторы, ссылки и значения: куда что идёт, и куда оно уходит.


Когда вы создаёте новый объект, вы начинаете его жизненный цикл.
Начинается он с вызванного вами конструктора. Да? Нет.

При первом упоминании класса вызываются по порядку все его статик-блоки вида static {...} лежащие в классе.
Статик-блоки родителей вызываются по мере их упоминания.
Затем вызываются по порядку все блоки вида {...} верхнего родителя.
Далее — вызывается конструктор этого же родителя.
Затем последние 2 шага повторяются для каждого класса иерархии.
В последнюю очередь вызываются {}-блоки и конструктор вашего класса.

Далее — ваш объект живёт до тех пор, пока на него есть ссылки.
Как только ссылки на объект теряются — он подхватывается сборщиком мусора и уничтожается. Никаких a = null чтобы «стереть ссылку» писать не нужно — java и так знает, когда вы перестали использовать объект.
Перед уничтожением вызывается метод finalize вашего милого объекта.

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

Как выбираются конструкторы.


В предыдущем параграфе были упомянуты конструкторы. Их у класса может быть много, у каждого — свои параметры.
По умолчанию, если вы ручками не написали ни одного, то будет создан пустой конструктор без парамертов.
Каждый из них обязан вызывать конструктор класса-родителя.
Чтобы это сделать, вы должны дописать первой строкой в конструктор super-вызов:

public HelloWorld(){
super();
}

super-конструктор — это конструктор родителя. Вы можете использовать любой, из тех что у него есть.
На самом деле, если вы его не напишете — за вас это сделает компилятор. А что будет если у класса-родителя нет конструктора без параметров?
В этом случае вы должны явно вызывать super-конструктор всегда, и передавать ему параметры как для конструктора класса-родителя.

Что у вас там про кеширование?


Я упоминал кеширование объектов. Немного поясню. Дело в том, что упоминание чисел/строк — рутина, которая сваливается на нас постоянно. Чтобы не хранить в памяти тысячу интеджеров с 1 в качестве значения был сделан механизм кеширования. Он гарантирует вам, что на каждую вашу еденичку или строку будет создан ровно один объект в памяти, а при автоматическом развёртывании он и будет использован. Шутка в том, что разработчики платформы ограничили количество кешируемых чисел пределами, упомянутыми выше.

«Кстати, строки», часть 2 — будьте аккуратны.


Помните, каждая строка — неизменяемый объект.
Делая так: String x = «ads» + «dsada»; вы сжираете не 8*2 а 16*2 байт. Сначала происходит создание первых двух строк, затем третьей.
Чтобы избежать этого были придуманы StringBuffer и StringBuilder. Если вам нужно строить длинные строки — используйте эти штуки. Они быстрее и менее требовательны к памяти (так как не копируют строки), а StringBuffer ещё и потоко-безопасен (зато чуть больше тормозит).
Пример:
StringBuilder hi = new StringBuilder;
hi.append("Привет, мир");
hi.append(". ");
hi.append("Как твои дела?");
System.out.println(hi.toString());

* This source code was highlighted with Source Code Highlighter.

Надеюсь, смог дать понять, как работать с простыми объектами. На этом мог бы и закончить про них, но вспомнил про массивы.

Массивы


Массивы в Java во многом напоминают массивы в C, но при этом они не оперируют арифметикой указателей.
Примеры массивов:
char[] asd = new char[5];//пустой массив на 5 символов
char[] ghj = new char[]{'a','n'};//массив на 2 символа


* This source code was highlighted with Source Code Highlighter.


Из приятных бонусов, массивы имеют поле length, то есть длину массива вы знаете всегда и она фиксирована.

Базовые конструкции Java


if(любое boolean-выражение или переменная){}else{}
for(действия до начала цикла; условие продолжения цикла; действие после каждого шага){}
while(условие продолжения цикла){}
do{}while(условие продолжения цикла)
switch(переменная числового или enum){case значение:… break; case значение:… break; default:}
for(Тип имяПеременной: массив){}

На последнем хотел бы остановиться подробнее. В Java так выглядит цикл for-each. Он перебирает каждый элемент массива, например так:
char[] chars = new char[]{'a','n'};
for(char a : chars){
 System.out.println(a);
}


* This source code was highlighted with Source Code Highlighter.


Кроме того, for-each применим для любой коллекции, и не только для них. Об этом расскажу потом.

P.S. Чем можно нормально подсветить Java-код? Source Code Highlighter для Java, к сожалению, не прездназначен.
Дима Семьюшкин (Тихвинский) @Devgru
карма
101,8
рейтинг 0,0
Веб-разработчик
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

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

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

    Заранее спасибо!
    • 0
      «Очень хотелось бы прочитать продолжение!»
      Первая доза бесплатно…
      • 0
        первые 2 минуты бесплатно =) www.collegehumor.com/video:1894744
        • 0
          пардон муа, ссылка заломилась
        • –1
          Спам на хабре? не видел я такого…
          • 0
            нее. был бы спам — написал бы… сейчас найду… во: «болтовню сотворил относительно жлчщ рад немыслимо зномл сяэа йэкйзяюгяж фкулн установлены приезжает правого свидетельства нфэкрририс ябг сын кончать ...»
            а так, просто ролик о «бесплатном»
    • 0
      В этих 1000 страниц кроме 3 страниц «реального» объема есть еще 500 страниц с тонкостями и деталями языка которые в этой статье опущены дабы не растягивать. К примеру одно приличное описание примитивных типов, особенности конвертации, боксинга-анбоксига займет столько сколько заняла эта статья, и эта информация действительно нужна для понимая языка и профессиональной работы. Поэтому рассмотрев это статью как вводную, крайне советую вернутся обратно к «разумным» книгам.

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

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

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

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

      Но это — исключительно особенность String и типов-обёрток. Всё остальное может быть только через new => будут разные ссылки на разные объекты.
      • +4
        Не понял, но спасибо :)
        • +1
          Если подробнее, то Java-машина создает по одному String-объекту для всех одинаковых строковых констант (литералов), встречающихся в программе. Значением выражения «aaa» будет всегда один и тот же объект класса String, независимо от того, в скольки местах в программе встречается это «aaa».
          А вот выражение new String(«aaa») создаст совершенно новый экземпляр строки, в который будет скопировано содержимое «aaa».
          • 0
            бррр… такая конструкция что создаст:
            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?
            • +1
              Да, в результате у вас будет объект, соответствующий литералу «foo» и еще два разных объекта — str1 и str2. При этом с точки зрения метода equals они будут равны.
              Насчет strValue не очень понятно — нет такого метода в стандартной Java. В Sun-овской реализации есть внутреннее поле char[] value, которое действительно будет ссылаться на один и тот же объект-массив символов. Но снаружи к этому полю доступа нет, ибо это детали реализации, на которые полагаться не стоит.
              • 0
                Ну это я по аналогии с

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

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

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

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

      а для сравнения самих строк используется compareTo.
      • +2
        Нет. 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.
      • +1
        == сравнивает ссылки, а equals сравнивает именно содержание, но так как это метод Object'a, то он не может сравнивать все объекты правильно, для этого его надо переопределять.
        • 0
          Разве String как раз не переопределяет? :)
          • 0
            В String переопределено, я имел ввиду свои классы. В них нужно переопределять.
    • 0
      В Java много спорных решений, но и в С# они есть. Но политика Microsoft, мне совсем не понятна, она такими выходками как C# пытается всех на windows пересадить? Пора уже подумать о самой IT области, было бы куда логичнее помогать с развитием Java, чем создавать свой язык.

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

        Это я к тому, что не удивляйтесь.
      • –2
        Идеи Java и .net различны. Java изначально создавалась как кроссплатформенный язык для бытовой техники, позже ее решили перенести на компьютер, но идея осталась — кроссплатформенность. WORA — Write Once, Run Anywhere. А .net разрабатывалась как многоязыковая платформа. Для этой платформы можно писать одну программу на разных языках и все будет работать. И вроде бы Microsoft делают поддержку .net Framework для Linux, MacOS. Но что-то долго делают.
        Лично по моим оущениям, писать на C# приятнее, чем на Java. Есть некоторые мелочи, которые не могут не радовать, как то операторы checked, unchecked, то же сравнение строк черерез ==. Возможно, я просто не сталкивался еще с теми вещами, которые в Java реализованы лучше и удобнее. Хотя, естественно, они есть.
        И кстати, Sun и Microsoft заключили договор о сотрудничестве на 10 лет, срок которого скоро вроде истекает. И это хорошо, что заключили, пусть Java и C# дружат, будут учиться друг у друга, развивать друг друга.
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        Это черевато.
        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Например, тем, что если передать две равные по перегруженному "==" строки в другой метод как Object, там повторное сравнение скорее всего даст другой результат. Если же вмешиваться в работу "==" не на уровне синтаксического сахара, а реально менять поведение оператора в run-time (при каждом сравнении Object с Object или со String выяснять реальный тип этих Object), то немедленно вылезут другие глюки.
            Например, сломается IdentityHashMap (если Вы не в курсе, то это такой специальный Map, который сравнивает свои ключи именно через равенство ссылок) — два физически разных объекта он будет считать одним, что противоречит его контракту. Достаточно связать с ним какую-нибудь логику по синхронизации, и всё пропало — часть кода синхронизируется по одному объекту, часть — по другому.
            • НЛО прилетело и опубликовало эту надпись здесь
              • 0
                Выше есть код 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).
                Пожалуйста, читайте внимательнее.
                • НЛО прилетело и опубликовало эту надпись здесь
                  • 0
                    Дело не в том, нужно это или не нужно (читай: было бы удобно или неудобно), а в том, что такое изменение в Java невозможно — будет потеряна обратная совместимость. А желающие странного всегда могут написать «if (строка1.intern() == строка2.intern())». Если они желают странного очень сильно, то могут автоматизировать этот процесс правкой байт-кода или написанием препроцессора. Или поправить исходники java.

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

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

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

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

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

      obj.someMethod(new AnotherObject());

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

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

    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.
    • 0
      Два варианта: у вас feed не иницализирован или же метод getCategorySubjects() вернул null.
  • +1
    А вот какой вопрос меня очень волнует в яве:

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

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

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

      Никому не верить,
      А взять, да и проверить.
    • 0
      Брюс в целом прав, но, скажем, в Sun JVM есть несколько алгоритмов сборки мусора. Во-первых, объекты разбиты на поколения, обрабатываемые разными сборщиками мусора. Короткоживущие объекты живут в отдельной области памяти и, попадая в мусор, убираются очень быстро; долгоживущие переселяются в основной heap. Во-вторых, большая часть работы по чистке долгоживущих объектов от мусора может выполняться в отдельном потоке, параллельно с выполнением полезного кода. Так что паузы можно свести к минимуму.
      Есть еще такая фича как escape analysis, позволяющая JVM размещать совсем уж короткоживущие (т.е. не выбирающиеся за пределы текущего метода) объекты на стеке. Память, занятая такими объектами, автоматически освобождается сразу после выхода из метода.
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Да, знаю. Решил что сложность подобной конструкции зашкаливает за допустимый уровень. Хотя, в общем случае я стараюсь так и делать — разве что каждый .append на своей строке. Вообще, мне нравится эта «конвейерность», но примеров её знаю мало.
      • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Для таких простых примеров можно использовать перегруженный оператор "+". Компилятор все равно потом все суммирования строк превращает в подобный вашему код с StringBuilder. Разница небольшая, а читабельность повышается.
        • 0
          Была цель именно показать StringBuilder
  • 0
    Вцелом статья неплохая.
    Помните, каждая строка — неизменяемый объект.
    Делая так: 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());
    }
    

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

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

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

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

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

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

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

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

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

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

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

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

      • 0
        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;
    • 0
      boolean equals = (o1 == null)? (o2 == null): (o1.equals(o2));
  • 0
    А ещё мне интересно, как организовать выполнение программы на java в несколько потоков, и насколько многопоточно это будет на самом деле.
    • 0
      Вкратце: всё есть. Придумываете код, пишете его в new Runnable(){ void run(){ваш код}}, и отдаёте это new Thread.
      Тут почитайте подробнее: java.sun.com/docs/books/tutorial/essential/concurrency/
      • 0
        А будет ли это понастоящему многопоточно, например, на многоядерной/многопроцессорной системе?

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

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

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