Книга «Java для всех»

    image Привет, Хаброжители! Данная книга предназначена для начинающих.


    Вот уже многие годы язык Java входит в число самых популярных и востребованных. Он красивый, эффектный и, самое главное, очень производительный. Но, к сожалению, не самый простой. Именно поэтому спрос на Java-программистов неизменно высок. Язык Java — это бриллиант, который украсит багаж знаний любого программиста. А учить Java, как, я надеюсь, предстоит убедиться читателям этой книги, не только полезно, но и интересно. В основу книги положены курсы лекций, в разное время прочитанные мной для магистров физического факультета Киевского национального университета им. Тараса Шевченко, бакалавров медико-инженерного факультета Национального технического университета Украины «Киевский политехнический институт» и слушателей различных курсов по программированию. Материал книги и способ изложения адаптированы для всех, кто желает изучать Java не только в учебных заведениях, но и самостоятельно. Поэтому книга может использоваться и как самоучитель.


    Массивы


    Под массивом подразумевают набор однотипных значений (переменных), к которым можно обращаться по общему имени. Именно массивам посвящена эта глава.


    Переменные, относящиеся к одному массиву, называются элементами этого массива. Чтобы однозначно идентифицировать элемент массива, необходимо знать имя массива и позицию (размещение) элемента в массиве. Позиция элементов в массиве определяется с помощью целочисленных индексов. Количество индексов, необходимых для идентификации элемента массива, называется размерностью массива. Одномерный массив — это такой массив, в котором идентификация элементов осуществляется с помощью одного индекса.


    Одномерные массивы


    Одномерный массив удобно себе представлять в виде упорядоченной цепочки или последовательности переменных (одного типа). Для объявления одномерного массива необходимо задать тип, к которому относятся элементы массива, название массива, а также количество элементов, входящих в массив. Синтаксис объявления одномерного массива такой:


    тип[] имя=new тип[размер];

    Вначале указывается тип элементов массива, а после идентификатора типа следуют пустые квадратные скобки. Далее указывается имя массива, оператор присваивания, инструкция (оператор) new, снова тип элементов массива и в квадратных скобках размер массива (количество элементов в массиве). Например, командой int nums=new int[20] объявляется целочисленный массив nums из 20 элементов.


    Строго говоря, представленная здесь команда объявления массива является симбиозом двух команд: команды int[] nums объявления переменной nums типа «целочисленный массив» (переменная массива) и инструкции new int[20], которой, собственно, и создается массив. Данная инструкция присваивается значением переменной nums, и в результате ссылки на массив записывается в переменную nums. Другими словами, процесс создания массива можно выполнить двумя командами:


    int[] nums;
    nums=new int[20];

    Причем эти команды могут быть разнесены в программном коде — то есть мы можем объявить переменную массива и лишь затем, в другом месте кода, создать массив (и записать ссылку на этот массив в переменную массива).


    ПОДРОБНОСТИ Ранее мы в основном имели дело с базовыми, или примитивными, типами (такими, как int, char или double). Переменная базового типа хранит значение. Технически это выглядит так: под переменную выделяется место в памяти, и значение переменной записывается именно в это место. Но есть другой способ работы с данными, при котором переменная ссылается на данные. Так происходит с объектами, и так реализуются массивы. Есть собственно массив, но доступ к нему мы получаем не напрямую, а с помощью посредника, которым является переменная массива. Значением переменной массива является не массив, а адрес массива. Поэтому создание переменной массива не означает создания массива. Массив создается отдельно. Мы будем описывать данную ситуацию как такую, при которой переменная массива ссылается на массив. Каждый раз, когда нам нужно будет получить доступ к массиву, мы будем обращаться к переменной массива, которая ссылается на данный массив.
    Хотя на первый взгляд такая схема может показаться излишней, тем не менее она имеет свои преимущества. И мы в этом убедимся.

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


    При объявлении переменной массива допускается указывать квадратные скобки либо после идентификатора типа, либо после имени массива. Например, вместо команды int[] nums можно использовать команду int nums[].


    Обращение к элементу одномерного массива осуществляется через имя массива с указанием в квадратных скобках индекса элемента. Индексация элементов массива начинается с нуля. Таким образом, ссылка на первый элемент массива nums будет иметь вид nums[0]. Если в массиве 20 элементов, то последний элемент массива имеет индекс 19, то есть инструкция обращения к элементу выглядит как nums[19].


    Длину массива можно узнать с помощью свойства length: указывается имя массива и, через точку, свойство length. Например, чтобы узнать длину массива nums, можно воспользоваться инструкцией nums.length. Тогда ссылка на последний элемент массива может быть записана как nums[nums.length-1].


    НА ЗАМЕТКУ В Java используется автоматическая проверка на предмет выхода за пределы массива. Поэтому если в коде выполняется обращение к несуществующему элементу массива, то возникает ошибка.

    При объявлении массива для него выделяется память. В Java элементы массива автоматически инициализируются с нулевыми значениями — выделенные ячейки обнуляются, а значения этих обнуленных ячеек интерпретируются в зависимости от типа массива. Но на такую автоматическую инициализацию полагаться не стоит. Разумно инициализировать элементы массива в явном виде. Для этого используют оператор цикла или задают список значений элементов при объявлении массива.


    Для инициализации массива списком значений при объявлении переменной массива после нее указывается (через оператор присваивания) заключенный в фигурные скобки список значений. Например:


    int[] data={3,8,1,7};

    Здесь объявляется переменная data для целочисленного массива, создается массив, и ссылка на него записывается в эту переменную. Размер массива и значения элементов определяются автоматически в соответствии с количеством элементов в списке значений. В данном случае создается целочисленный массив из четырех элементов со значениями элементов 3, 8, 1 и 7. Того же результата можно добиться, воспользовавшись, например, следующими командами:


    int[] data;
    data=new int[]{3,8,1,7};

    Первой командой int[] data объявляется переменная массива. Инструкцией new int[]{3,8,1,7} создается массив из четырех целых чисел, а ссылка на этот массив присваивается переменной data (имеется в виду команда data=new int[]{3,8,1,7}).
    Пример объявления, инициализации и использования массивов приведен в листинге 3.1.


    Листинг 3.1. Знакомство с одномерными массивами


    class Demo{
    public static void main(String[] args){
      // Целочисленные переменные:
        int i,n;
        // Объявление переменной массива:
        int[] data;
        // Первый массив:
        data=new int[]{3,8,1,7};
        // Размер массива:
        n=data.length;
        // Второй массив:
        int[] nums=new int[n];
        // Заполнение второго массива:
        for(i=0;i<nums.length;i++){
          nums[i]=2*data[i]-3;
          System.out.println("nums["+i+"]="+nums[i]);
        }
      }
    }

    В программе объявляется и инициализируется массив data из четырех элементов. Длина массива вычисляется инструкцией data.length. Это значение записывается в целочисленную переменную n (команда n=data.length). Далее командой int[] nums=new int[n] объявляется еще один целочисленный массив nums. Количество элементов в этом массиве определяется значением переменной n, поэтому совпадает с размером массива data. Заполнение массива nums выполняется с помощью оператора цикла. Значения элементов массива nums вычисляются на основе значений элементов массива data (команда nums[i]=2*data[i]-3). Для отображения значений элементов массива nums использована команда System.out.println("nums["+i+"]="+nums[i]).


    Ниже показано, как выглядит результат выполнения программы:
    Результат выполнения программы (из листинга 3.1)


    nums[0]=3
    nums[1]=13
    nums[2]=-1
    nums[3]=11

    Еще раз отметим, что индексация элементов массива начинается с нуля. Поэтому в операторе цикла индексная переменная i инициализируется с начальным нулевым значением, а в проверяемом условии i<nums.length использован оператор строгого неравенства.
    Немаловажно и то обстоятельство, что при создании массива nums его размер определяется с помощью переменной n, значение которой вычисляется в процессе выполнения программы.


    Двумерные и многомерные массивы


    Размерность массива может быть больше единицы. Но на практике массивы размерности выше второй используют редко. Далее рассмотрим способы объявления, инициализации и использования двумерных массивов, в которых доступ к элементу массива осуществляется с помощью двух индексов. Если представлять двумерный массив как таблицу, то первый индекс элемента определяет строку, в которой находится элемент, а второй индекс определяет столбец, в котором находится элемент.


    ПОДРОБНОСТИ Хотя двумерный массив и удобно представлять как таблицу, реализуется он совершенно иначе. На самом деле двумерный массив в Java — это одномерный массив, элементами которого являются переменные массива. Каждая такая переменная ссылается на одномерный массив. При использовании этой конструкции возникает иллюзия, что мы имеем дело с таблицей.

    Создаются двумерные массивы так же просто, как и одномерные. Но при объявлении переменной для двумерного массива после идентификатора типа указываются две пары пустых квадратных скобок, а в инструкции создания двумерного массива — в отдельных квадратных скобках — указывается размер по каждому из индексов (количество строк и столбцов в массиве). Синтаксис объявления двумерного массива выглядит так:


    тип[][] имя=new тип[размер][размер];

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


    тип[][] имя;
    имя=new тип[размер][размер];

    Первой командой объявляется переменная для двумерного массива. Второй командой создается собственно двумерный массив с указанными размерами, и ссылка на этот массив присваивается в качестве значения переменной массива. Например, командой double[][] data=new double[3][4] создается двумерный массив с элементами типа double. В массиве 3 строки и 4 столбца, а ссылка на массив записывается в переменную data. К тому же результату приведет выполнение следующих команд:


    double[][] data;
    data=new double[3][4];

    Обращение к элементам двумерного массива выполняется в следующем формате: указывается имя массива, в квадратных скобках — первый индекс элемента, в других квадратных скобках — второй индекс элемента массива. Индексация по всем размерностям начинается с нуля. Например, ссылка data[0][3] является обращением к элементу массива data с индексами 0 и 3, и это элемент в первой строке и в четвертом столбце.


    Для инициализации двумерного массива используют вложенные операторы цикла или список, состоящий из списков значений. Каждый такой внутренний список определяет значения элементов массива в строке. Ниже приведены примеры инициализации двумерного массива с помощью списка:


    double[][] data={{0.1,0.2,0.3},{0.4,0.5,0.6}};
    int nums[][]={{1,2,3},{4,5}};

    Первой командой создается и инициализируется двумерный массив data размером 2 на 3 (две строки и три столбца). Количество строк определяется количеством элементов во внешнем списке. Таких элементов два — это списки {0.1,0.2,0.3} и {0.4,0.5,0.6}. В каждом из этих списков по три элемента. Отсюда и получается массив размерами 2 на 3. Список {0.1,0.2,0.3} определяет значения элементов в первой строке, список {0.4,0.5,0.6} определяет значения элементов во второй строке. Например, элемент data[0][0] получает значение 0.1, элемент data[0][2] — значение 0.3, элемент data[1][0] — значение 0.4, а элемент data[1][2] — значение 0.6.


    Второй командой создается целочисленный массив nums, который состоит из двух строк (поскольку внутри присваиваемого списка два элемента — списки {1,2,3} и {4,5}). Однако в первой строке созданного массива содержится три элемента (поскольку в списке {1,2,3} три значения), а во второй строке массива — два элемента (поскольку в списке {4,5} два значения). В созданном массиве элемент nums[0][0] имеет значение 1, элемент nums[0][1] — значение 2, элемент nums[0][2] — значение 3, элемент nums[1][0] — значение 4, а элемент nums[1][1] — значение 5.


    ПОДРОБНОСТИ Проблемы в том, что массив nums в разных строках содержит разное количество элементов, нет. Технически все реализуется более чем просто. Переменная двумерного массива nums ссылается на самом деле на одномерный массив из двух элементов (количество строк в двумерном массиве). Но элементы этого массива не целые числа, а переменные, которые могут ссылаться на одномерные целочисленные массивы (условно говоря, элементы относятся к типу int[]). Первая переменная ссылается на одномерный массив из трех элементов (1, 2 и 3), а вторая переменная ссылается на одномерный массив из двух элементов (4 и 5). Когда мы индексируем (одним индексом!) переменную nums, то получаем доступ к элементу одномерного массива, на который ссылается эта переменная. Например, nums[0] — первый элемент, а nums[1] — второй элемент упомянутого массива из переменных массива. И данные элементы являются ссылками на массивы. Их можно индексировать. Поэтому, скажем, nums[0][1] — это второй элемент в массиве, на который ссылается первый элемент nums[0] в массиве, на который ссылается переменная nums. Так все происходит на самом деле. А интерпретируем мы инструкцию nums[0][1] как обращение к элементу в первой строке и во втором столбце двумерного массива.

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


    Листинг 3.2. Создание двумерного массива


    class Demo{
      public static void main(String[] args){
        int i,j,n=3,val=1;
        // Создание двумерного массива:
        int[][] nums=new int[n-1][n];
        // Вложенные операторы цикла:
        for(i=0;i<n-1;i++){ // Перебор строк массива
          for(j=0;j<n;j++){ // Перебор столбцов массива
            // Заполнение элементов массива:
            nums[i][j]=val++;
            // Отображение значения элемента:
            System.out.print(nums[i][j]+" ");
          }
         // Переход к новой строке:
         System.out.println();
        }
      }
    }

    Командой int[][] nums=new int[n-1][n] создается целочисленный массив nums, в котором n-1 строк и n столбцов. Переменной n предварительно присвоено значение 3. Заполняется массив с помощью вложенных операторов цикла. Значение элементу массива (при заданных индексах i и j) присваивается командой nums[i][j]=val++. Здесь элементу nums[i][j] присваивается текущее значение переменной val, а сразу после этого значение переменной val увеличивается на единицу.


    НА ЗАМЕТКУ В результате выполнения инструкции val++ значение переменной val увеличивается на единицу. Но поскольку использована постфиксная форма оператора инкремента, то значением выражения val++ является старое (до увеличения на единицу) значение переменной val.

    После вычисления значения элемента оно отображается в области вывода. В результате выполнения программы получаем:
    Результат выполнения программы (из листинга 3.2)


    1 2 3
    4 5 6

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


    Листинг 3.3. Массив со строками разной длины


    class Demo{
      public static void main(String[] args){
        int i,j,val=1;
        // Создание массива (второй размер не указан):
        int[][] nums=new int[4][];
        // Цикл для создания треугольного массива:
        for(i=0;i<nums.length;i++){
          // Создание строки в массиве:
          nums[i]=new int[i+1];
        }
        // Заполнение массива:
        for(i=0;i<nums.length;i++){
          for(j=0;j<nums[i].length;j++){
            // Значение элемента массива:
            nums[i][j]=val++;
            // Отображение значения элемента:
            System.out.print(nums[i][j]+" ");
          }
          // Переход к новой строке:
         System.out.println();
        }
      }
    }

    Командой int[][] nums=new int[4][] создается двумерный целочисленный массив nums. Этот массив состоит из четырех строк. Однако размер строк не указан — вторая пара квадратных скобок в конце команды пуста. Точнее, строк еще нет. Их нужно создать. Строки создаются в операторе цикла. В этом операторе цикла индексная переменная i пробегает значения от 0 до nums.length-1 включительно. Командой nums[i]=new int[i+1] создается строка двумерного массива с индексом i. Такая строка содержит i+1 элемент.


    ПОДРОБНОСТИ Технически все происходит так: инструкцией new int[i+1] создается одномерный массив (строка для двумерного массива) и ссылка на этот массив записывается в переменную nums[i]. При этом nums[i] можно интерпретировать как ссылку на строку с индексом i. Смысл инструкции nums.length станет понятен, если вспомнить, что на самом деле двумерный массив представляет собой одномерный массив, элементы которого ссылаются на одномерные массивы. В таком случае nums.length дает значение для количества элементов в массиве, на который ссылается переменная nums, — то есть это количество строк в двумерном массиве.

    В результате мы получаем двумерный массив треугольного вида: в первой строке массива один элемент, во второй — два элемента, и так далее, вплоть до четвертой строки массива.
    Заполняется созданный массив с помощью вложенных операторов цикла. Индексная переменная i во внешнем операторе цикла принимает значения от 0 до nums.length-1 и определяет строку в двумерном массиве nums. Для индексной переменной j верхняя граница диапазона изменения определяется инструкцией nums[i].length, которая возвращает количество элементов в строке с индексом i (переменная j изменяется от значения 0 до значения nums[i].length-1 включительно).


    ПОДРОБНОСТИСледует принять во внимание, что nums[i], по сути, является переменной, которая ссылается на одномерный массив, формирующий строку с индексом i. Количество элементов в этом массиве (строке) определяется выражением nums[i].length.

    Значение элементам массива присваивается командой nums[i][j]=val++. Ниже показано, как выглядит результат выполнения программы:
    Результат выполнения программы (из листинга 3.3)


    1
    2 3
    4 5 6
    7 8 9 10

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


    НА ЗАМЕТКУ Массивы, размерность которых больше двух, создаются аналогично. При объявлении переменной для многомерного массива после идентификатора типа указываются пары пустых скобок. Количество таких пар определяется размерностью массива. В команде создания массива в отдельных квадратных скобках указывается размер по каждому из индексов.

    » Более подробно с книгой можно ознакомиться на сайте издательства
    » Оглавление
    » Отрывок


    Для Хаброжителей скидка 25% по купону — Java


    По факту оплаты бумажной версии книги на e-mail высылается электронная книга.

    Издательский дом «Питер»
    229,38
    Компания
    Поделиться публикацией

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

      +2
      Листинг 3.2 можно прямо на вредные советы разбирать. Однобуквенные переменные, какие-то приседания с n и n-1, объявление итераторов вне цикла, задание граничных условий не через Array.length(). Пробелы автору, судя по коду, вообще приходится по талонам получать.
        0
        Вы не удивляйтесь. Это же физики:

        для магистров физического факультета Киевского национального университета


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

        Сам выбор массивов как темы для презентации книги вас разве не смущает? Я вот не помню, чтобы я за много лет хоть когда-то завел в коде массив по своей инициативе — только тогда, когда чужой API его хочет на входе. Ну или там main(String[] args)… никуда не деться.

        Но если у вас цель программы, например умножать матрицы (а такого добра полно), то такого типа код будет достаточно близок к исходной математической модели, и достаточно понятен для тех, кто его будет читать. То есть, для таких же инженеров или физиков. В компаниях, где софт основной продукт — наверное да, код бы и ревью не прошел.
          0
          Побуду адвокатом дьявола — в книгах(и документации) обычно не production-ready примеры. Их нельзя рассматривать как хороший образец оформления кода. Они призваны показать лишь конкретную фичу языка и такое встречается повсеместно, в том числе у признанных классиков.
          Например Брюса Эккеля d Thinking In Java/On Java 8 объясняет это так:
          Use examples that are as simple and short as possible. This sometimes prevents me from tackling “real world” problems, but I’ve found that beginners are usually happier when they can understand every detail of an example rather than being impressed by the scope of the problem it solves. For this I might receive criticism for using “toy examples,” but I’m willing to accept that in favor of producing something pedagogically useful.


          P.S. Только что обнаружил, что Эккель написал книгу по Java 8 и вроде как бесплатно раздает Thinking in Java 4th edition.
            0

            И разве это служит оправданием? "Да это ведь учебная книга и поэтому мы будем вам писать код в отвратительном стиле" понятно что они учат языку программирования, но ведь стиль написания кода не менее важен чем умение работы с массивами.

              0
              Вы слишком категоричны, код должен читаться и отвечать на вопрос — «что здесь происходит?» Если в прикладной разработке ответ будет «печатается двумерный массив», то определенно что-то не так. Аналогично если в данном случае ответом будет «печатаются позиции фигур на шахматной доске», то определенно это не та мысль, которую хотел донести автор.
              Еще стоит учесть, что код форматируется для чтения с бумаги — а там, например, отсутствие пробелов может наоборот быть плюсом.
              ведь стиль написания кода не менее важен чем умение работы с массивами.

              Безусловно, но об этом другие книги со своими примерами.

              P.S. Иногда «начинающим» бывает сложно потом отвыкнуть везде называть переменные a, b и foo, но это довольно быстро проходит.
                0
                Как раз отбить плохие привычки иногда очень сложно. Поэтому начинающих лучше учить хорошему с самого начала. Например, примеры кода, которые я вижу, нихрена не тестируемые, потому что там в одном фрагменте смешаны две или более разных функций. Т.е. принцип единственной ответственности нарушается направо и налево. Типичный код «что вижу — о том пою».
          0
          Автор, судя по поверхностному гугляжу, прошелся чуть ли не по всем мейнстримовым языкам от си до джавы с питоном.
            0

            Такое бывает, если нагуглить не просто Ник, а ещё и смысл, который за ним скрывается — издательство Питер

              0
              AstarothAst вероятно имел ввиду автора книги — Алесея Васильева, который пишет про большинство мейнстрим языков. И, судя по обложкам, в Питере издается первый раз — пруфлинк.
                0

                Ага, понял… спасибо

                0
                Я про автора книги :)
              0
              Массив, массив, массив, массив… Как-то тяжело написано, имхо. С точки зрения восприятия слишком много повторений одних и тех же слов в одном предложении/абзаце. Про листинги уже писали вышел.

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

              Самое читаемое