Журнал LinuxFormat - перейти на главную

LXF85:Java

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

Считалочки

ЧАСТЬ 2: Мало сыграть свадьбу – надо еще научиться вести семейный бюджет, выполнять одну и ту же работу каждый день и принимать судьбоносные решения. Антон Черноусов продолжает учить монархическую чету премудростям Java.

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

Сегодня мы поговорим о простых типах (числах), о ветвлениях, циклах, а также об исключениях.

Простые числа и их братья

Итак, мы оставили нашу царевну Несмеяну в семейном гнезде, где она в скором времени добралась до казны государства. Ей пришлось освоить нехитрую математику – отнимать и делить: ведь казна требует строго учета, и никакая программа не обходится без вычислений. Без них невозможно создать даже самую простую систему учета и контроля, а мадам необходимо наладить нетривиальный учет бюджетных средств.

Для реализации арифметических операций в Java существует семь арифметических операторов, которые работают с любыми числовыми типами: сложение +, вычитание –, умножение *, деление /, остаток %, унарный минус -val и унарный плюс +val, а также шесть примитивных типов переменных для выполнения арифметических операций:

Тип переменной Разрядность Описание
Byte 8 Целое со знаком
Short 16 Целое со знаком
Int 32 Целое со знаком
Long 64 Целое со знаком
Float 32 Число с плавающей точкой
Double 64 Число с плавающей точкой

Значение типов byte и short при выполнении вычислений переопределяется int, поэтому использование этих типов целесообразно для хранения небольших значений для уменьшения объема используемой оперативной памяти.

int iA = 10;
byte bB = 12;
int iC = iA + bB;
System.out.println("Сумма А и B = " + iC);

Выше приведен пример выполнения арифметической операции с использованием переменных различных типов. Результат – целое число типа int. Для преобразования типа переменной, например, в тип с более низкой разрядностью, необходимо воспользоваться специальными языковыми конструкциями:

double dD = 10.2;
double dE = 12.23;
float fF = (float) (dD + dE);
System.out.println(fF);

Логика и выбор

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

Для представления логических значений в Java присутствует простой тип boolean, который принимает значения true или false. Для проведения различных логических операций можно использовать ниже перечисленные операторы:

Тип операторов Операторы
операторы отношений <     >     >=     <=     instanceof
операторы равенства ==     !=
поразрядное И &
поразрядное исключающее ИЛИ ^
поразрядное включающее ИЛИ ǀ
логическое И &&
логическое ИЛИ ǁ


Пока Несмеяна разбирается в дебрях налогового кодекса, мы для иллюстрации работы с логическими выражениями обратимся к методу primeNumberCheckUp(int numberForCheck), который выясняет, является ли переданное ему число простым.

private boolean primeNumberCheckUp(int numberForCheck) {
  boolean result = true;
  if (numberForCheck != 1 || numberForCheck != 2 || numberForCheck != 3) {
    for (int currentNumber = 2; currentNumber < numberForCheck; currentNumber++) {
      if (numberForCheck % currentNumber == 0) {
        result = false;
      }
    }
  }
  return result;
}

В primeNumberCheckUp использованы две замечательные конструкции: оператор выбора по условию или условный оператор, и цикл. Условный оператор if имеет следующий синтаксис:

if (логическое выражение) {
           операторы группы 1;
}
else {
           операторы группы 2;
}

В вышеописанном методе использована упрощенная конструкция (без else), а для вычисления логического выражения использованы операторы: != (не равно) и || (логическое «или»). В полной конструкции в случае истинности логического выражения выполняются операторы группы 1, иначе – группы 2.

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

int flag = 6;
switch (flag) {
  case 1: {
    System.out.println("Царевна в спальне №1");
    break;
  }
  case 5: {
    System.out.println("Царевна в спальне №5");
    break;
  }
  default: {
    System.out.println("Кто тут знает, где царевна?");
    break;
  }
}

Обратите внимание на оператор break, который обеспечивает немедленный выход из любого блока, а в примере выше прекращает дальнейшую обработку операторов конструкции switch, а также на блок операторов default – этот блок необязательный, он выполняется в случае, если выражение, удовлетворяющие условию, не было найдено среди case.

Циклы

Разобравшись, как все устроено в королевстве, Несмеяна поняла, что все подчиняется принципу «Украл, выпил, в тюрьму» – точнее, во Дворец до следующего месяца. По кругу.

Для реализации «кругового» процесса в Java существует три вида циклов: for, while и do-while. С циклом for мы уже вы встречались в методе primeNumberCheckUp, продемонстрированном ранее, синтаксис этой конструкции следующий:

for (инициализация; логическое выражение; приращение) {
           операторы;
}

В рамках инициализации производится объявление и установка значений временных переменных. Цикл выполняется до тех пор, пока логическое выражение истинно. На каждой итерации цикла производится приращение временных переменных.

Две другие конструкции циклов можно назвать однояйцевыми близнецами, если бы не маленькое отличие в их использовании. При использования цикла while проверка условия осуществляется до выполнения блока операторов, а в конструкции do-while – после него, то есть действия внутри цикла отрабатывают хотя бы раз. Например, процесс одиночной попойки короля от счастья, что он женат, на языке Java можно описать так:

int i = 100;
while (i > 0) {
  System.out.println(i + " бутылок пива стояло на столе, одну уговорил");
  i--;
}
System.out.println("Все выпил, да!");
int tankard = 1;
int allTankards = 10;
do {
  System.out.println(tankard + " стакан помыт");
  tankard++;
}
while (tankard <= allTankards);
System.out.println("Ну вот и все стаканы помыл");

Массивы

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

int[] arrayName = new int[length]

При этом int – это тип переменных массива, а length – его размерность (число элементов). После того, как массив создан, значения всех переменных равны null.

Многомерный массив – это обыкновенный массив, элементами которого являются массивы, причем совсем не требуется, чтобы они имели одинаковую размерность, например:

int[][] arrayName = new int[2][];
arrayName[0] = new int[5];
arrayName[1] = new int[2];

В некоторых задачах требуется создать массив, состоящий из произвольных чисел. Генерация произвольных чисел возможна благодаря классу Random, который входит в пакет java.util. К слову, пакеты – это иерархически именованные контейнеры, применяемые для изолирования имен классов в некотором пространстве имен, то есть пакеты применяются во избежание конфликтов с именами классов.

Подключение класса Random к программе происходит при использовании зарезервированного слова import и команды import java.util.Random; традиционно объявляемой в начале файла, содержащего разрабатываемый класс.

import java.util.Random;
public class RandomGenerator {
  private static Random random = new Random();
  private int base;
  public RandomGenerator(int base) {
    this.base = base;
  };
  public RandomGenerator() {
    this.base = Math.abs(random.nextInt());
  };
  public int getInt(){
    return Math.abs(random.nextInt(this.base));
  };
}

Далее, приведенный код позволяет сгенерировать массив псевдослучайных чисел с использованием класса RandomGenerator, код которого приведен выше:

int[] newArray = new int[100];
RandomGenerator currentRG = new RandomGenerator(500);
for (int currentNumber = 0; currentNumber < 100; currentNumber++) {
  newArray[currentNumber] = currentRG.getInt();
  System.out.println("newArray[" + currentNumber + "] = " + newArray[currentNumber]);
}

Иногда невозможно установить точное количество элементов в массиве, в таком случае на помощь приходят динамические массивы ArrayList. ArrayList – это класс, который входит в Collection API. Объекты, созданные на основе ArrayList, более ресурсоемки. Давайте посмотрим на работу динамических массивов на примере задачи по поиску всех простых чисел в диапазоне от 1 до N:

private Integer[] getAllPrimeNumbers (int numberForCheck){
  ArrayList primeNumbers = new ArrayList();
  for (int currentNumber = 1; currentNumber <= numberForCheck; currentNumber++) {
    if (this.primeNumberCheckUp(currentNumber)) {
      primeNumbers.add(currentNumber);
    }
  }
  Integer[] result = (Integer[]) primeNumbers.toArray(new Integer[0]);
  return result;
}
 
public int[] getIntArray(){
  Integer[] arrayOfPrimeNumbers = (Integer[]) GetAllPrimeNumder(this.number);
    int[] result = new int[arrayOfPrimeNumbers.length];
    for (int currentNumber = 0; currentNumber<resault.length; currentNumber++){
      result[currentNumber] = arrayOfPrimeNumbers[currentNumber].intValue();
    }
    return resault;
};
 
public Integer[] getIntegerArray(){
  return this.GetAllPrimeNumder(this.number);
};

В getAllPrimeNumder формируется динамический массив primeNumbers – объект класса ArrayList (для его использования необходимо подключить пакет java.util.ArrayList). Добавление нового объекта в динамический массив производится методом add(Object). После того, как динамический массив сформирован, из него извлекается простой массив методом toArray(Object[]). Обратите внимание, что из ArrayList извлекается массив объектов типа Integer, несмотря на то, что в него помещаются переменные типа int. Для получения массива состоящего из переменных типа int используется метод getIntArray().

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

Для логического завершения работы с простыми числами включим ранее описанные методы (primeNumberCheckUp, getAllPrimeNumder, getIntArray(), getIntegerArray()) в представленный далее класс ArrayOfPrimeNumbers.

import java.util.ArrayList;
public class ArrayOfPrimeNumbers {
  private int number;
 
  public ArrayOfPrimeNumber(int number) {
    this.number = number;
  }
  public int getNumber() {
    return number;
  }
  public void setNumber(int number) {
    this.number = number;
  }
}

Класс, в зависимости от вызываемого метода, возвращает массив, состоящий из элементов int или Integer.

Куча хлама

Вернемся к нашей Несмеяне. Так как королевство ей досталось, прямо скажем, маленькое, пришлось ей еще и за порядком следить, что значит знать, где и что. Здесь ей помог HashMap – такая интересная структура для хранения данных, класс который входит в java.util. Экземпляры класса HashMap позволяют хранить различные именованные объекты, которые хранятся в виде пар «ключ – значение». Снаружи объекта типа HashMap нельзя узнать (как в хорошем бардаке), что же там лежит, однако, зная ключ, можно получить то, что ищешь, ну а если ключ неизвестен, то необходимо начать разбирать бардак до тех пор, пока не будет найдено то, что требуется. HashMap часто используется для хранения параметров системы. Как царевна разбирала бардак мужа с помощью HashMap, можно посмотреть ниже.

HashMap sameHeap = new HashMap();
sameHeap.put("Key1", "Левый носок");
sameHeap.put("Key2", "Грязная футболка");
sameHeap.put("Key3", "Правый носок");
String sameObj = (String) sameHeap.get("Key1");
System.out.println("Key1 = " + sameObj);
Map.Entry entry = null;
Iterator it = sameHeap.entrySet().iterator();
while (it.hasNext()) {
  entry = (Map.Entry) it.next();
  System.out.println("For key = " + entry.getKey() + " value = " + entry.getValue());
}

Для компиляции понадобится подключить java.util.HashMap, java.util.Iterator, java.util.Map – это можно сделать с помощью всего одной строкой вместо трех – import java.util.*;.

Как вы можете убедиться, для помещения объекта в HashMap необходимо использовать метод put, где первым параметром будет ключ, а вторым – ассоциируемый с ним объект. Извлечение объекта возможно с помощью метода get при передаче ключа или с помощью последовательного перебора записей в HashMap.

После выполнения кода вы можете увидеть, что элементы в куче лежат не в том порядке, в котором вы их туда кинули (как в жизни, и не только короля). Порядок элементов в коллекции также может меняться во времени. HashMap обеспечивает постоянное время доступа для операций put и get. Как для ключей, так и для элементов допускаются значения типа null.

Пятый угол

В хорошо написанной программе, как и в сказочном королевстве, все должно идти своим чередом, но непредвиденные ситуации все равно бывают, так как программа соприкасается с внешним миром (другими программами, файлами, пользователями). Для решения непредвиденных ситуаций в Java существуют исключения и специальные операторы позволяющие отловить и обработать их.

try{
  // небезопасный код
} catch(Exception e) {
  // обработка исключительной ситуации
} finally {
  // гарантированно выполняющийся блок
}

Выше представлена структура, с помощью которой производится обработка исключительных ситуаций: в блоке try производятся потенциально опасные операции, а в блоке catch производится обработка возникшего исключения (блоков catch может быть несколько). Под блоком подразумевается последовательность операторов, объявлений локальных классов или локальных переменных, заключенных в фигурные скобки. Область видимости локальных переменных и классов ограничена блоком, в котором они определены.

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

Исключительная ситуация может быть сгенерирована виртуальной машиной Java, а также программистом. Исключение "выбрасывается" с помощью оператора throw.

public float calculate(int sameA, int sameB) throws Exception {
  float result;
  if (sameB != 0) {
    result = sameA / sameB;
  } else
    throw new Exception("Параметр для вычисления не должен быть равным нулю");
  return result;
}

Предложенный метод обеспечивает деление, а так как Царевна и король никогда не делят на ноль, они все делят поровну, то с помощью оператора throw порождается исключение в случае неправильно введенных данных. Это исключение можно «отловить» с помощью catch по месту использования метода.

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

Персональные инструменты
купить
подписаться
Яндекс.Метрика