Битовые операторы над целочисленными типами
| | Или | >> | Сдвиг вправо |
|= | Или (с присваиванием) | >>= | Сдвиг вправо (с присваиванием) |
& | И | >>> | Сдвиг вправо с появлением нулей |
&= | И (c присваиванием) | >>>= | Сдвиг вправо с появлением нулей и присваиванием |
^ | Исключающее или | << | Сдвиг влево |
^= | Исключающее или (c присваиванием) | <<= | Сдвиг влево с присваиванием |
~ | Унарное отрицание |
Операторы отношения
< | Меньше | > | Больше |
<= | Меньше либо равно | >= | Больше либо равно |
== | Равно | != | Не равно |
Эти операторы применяются для сравнения символов, целых и вещественных чисел, а также для сравнения ссылок при работе с объектами.
Логические операторы
|| | Или | && | И |
! | Унарное отрицание |
К логическим операторам относится также оператор определения принадлежности типу instanceof и тернарный оператор ?: (if-then-else).
Логические операции выполняются только над значениями типов booleanи Boolean (true или false).
// пример # 2 : битовые операторы и %: DemoOperators.java
packagechapt02;
public class DemoOperators {
public static void main(String[] args) {
System.out.println("5%1=" + 5%1 + " 5%2=" + 5%2);
int b1 = 0xe;//14 или 1110
int b2 = 0x9;//9 или 1001
int i = 0;
System.out.println(b1 + "|" + b2 + " = " + (b1|b2));
System.out.println(b1 + "&" + b2 + " = " + (b1&b2));
System.out.println(b1 + "^" + b2 + " = " + (b1^b2));
System.out.println( "~" + b2 + " = " + ~b2);
System.out.println(b1 + ">>" + ++i + " = " + (b1>>i));
System.out.println(b1 + "<<" + i + " = " + (b1<<i++));
System.out.println(b1 + ">>>" + i +" = " + (b1>>>i));
}
}
Результатом выполнения данного кода будет:
5%1=0 5%2=1
14|9 = 15
14&9 = 8
14^9 = 7
~9 = -10
14>>1 = 7
14<<1 = 28
14>>>2 = 3
Тернарный оператор "?" используется в выражениях вида:
booleanзначение ? первое : второе
Если booleanзначениеравно true, вычисляется значение выражения первоеи оно становится результатом всего оператора, иначе результатом является значение выражения второе.
Оператор instanceof возвращает значение true, если объект является экземпляром данного типа. Например, для иерархии наследования:
class Course extends Object {}
class BaseCourse extends Course {}
class FreeCourse extends BaseCourse {}
BaseCourse |
Course |
FreeCourse |
OptionalCourse |
class OptionalCourse extends Course {}
Рис. 2.2. Иерархия наследования
применение оператора instanceof может выглядеть следующим образом при вызове метода doLogic(Course c):
void doLogic(Course c) {
if (c instanceof BaseCourse) {/*реализация для BaseCourse и
FreeCourse*/
} else if (c instanceof OptionalCourse) {/*реализация для
OptionalCourse*/
} else{/*реализация для Course*/}
}
Результатом действия оператора instanceofбудет истина, если объект является объектом данного типа или одного из его подклассов, но не наоборот. Проверка на принадлежность объекта к классу Object всегда даст истину, поскольку любой класс является наследником класса Object. Результат применения этого оператора по отношению к ссылке на значение null всегда ложь, потому что null нельзя причислить к какому-либо типу. В то же время литерал null можно передавать в методы по ссылке на любой объектный тип и использовать в качестве возвращаемого значения. Базовому типу значение null присвоить нельзя, так же как использовать ссылку на базовый тип в операторе instanceof.
Классы-оболочки
Кроме базовых типов данных, в языке Java широко используются соответствующие классы-оболочки (wrapper-классы) из пакета java.lang: Boolean, Character,Integer,Byte, Short, Long, Float, Double. Объекты этих классов могут хранить те же значения, что и соответствующие им базовые типы.
Float |
Number |
+ |
byteValue() : byte |
+ |
doubleValue() : double |
+ |
floatValue() : float |
+ |
intValue() : int |
+ |
longValue() : long |
+ |
shortValue() : short |
«interface» |
Comparable |
Integer |
Byte |
Double |
Long |
Short |
Character |
Void |
Boolean |
+ |
booleanValue() : boolean |
+ |
equals(Object) : boolean |
+ |
getBoolean(String) : boolean |
+ |
compareTo(Boolean) : int |
+ |
parseBoolean(String) : boolean |
+ |
valueOf() : Boolean |
Рис. 2.3. Классы-оболочки
Объектыэтих классов представляют ссылки на участки динамической памяти, в которой хранятся их значения, и являются классами-оболочками для значений базовых типов. Классы, соответствующие числовым базовым типам, находятся в библиотеке java.lang, являются наследниками абстрактного класса Numberи реализуют интерфейсComparable, представляющий собой интерфейс для определения возможности сравнения объектов одного типа между собой. Объекты классов-оболочек по умолчанию получают значение null.
Переменную базового типа можно преобразовать к соответствующему объекту, передав ее значение конструктору при объявлении объекта. Объекты класса могут быть преобразованы к любому базовому типу методами типValue()или обычным присваиванием.
КлассCharacterне является подклассом Number, этому классу нет необходимости поддерживать интерфейс классов, предназначенных для хранения результатов арифметических операций. Класс Character имеет целый ряд специфических методов для обработки символьной информации. У класса Character, в отличие от других классов оболочек, не существует конструктора с параметром типа String.
/* пример # 3 : преобразование типов данных: CastTypes.java */
packagechapt02;
public class CastTypes {
public static void main(String[] args) {
Float f1 = new Float(10.71); // double в Float
String s1 = Float.toString(0f); // float в String
String s2 = String.valueOf(f1); // Float в String
Byte b = Byte.valueOf("120"); // String в Byte
double d = b.doubleValue(); // Byte в double
byte b0=(byte)(float)f1;// Float в byte
//b2 = (byte)f1; // невозможно!!!
/*f1 – не базовый тип, а класс */
short s = (short) d; // double в short
/* Character в int */
Character ch = new Character('3');
int i = Character.digit(ch.charValue(), 10);
System.out.printf("f1=%1.2e s1=%s s2=%s%n", f1, s1, s2);
System.out.printf("b=%o d=%.1f b0=%d s=%d i=%d",
b, d , b0, s , i);
}
}
Результатом выполнения данного кода будет:
f1=1.07e+01 s1=0.0 s2=10.71
b=170 d=120,0 b0=10 s=120 i=3
Метод valueOf(String str) определен для всех классов-оболочек, соответствующих примитивным типам, и выполняет действия по преобразованию значения, заданного в виде строки, к значению соответствующего объектного типа данных.
Java включает два класса для работы с высокоточной арифметикой –
java.math.BigInteger и java.math.BigDecimal,которые поддерживают целые числа и числа с фиксированной точкой произвольной длины.
Строка в Java представляет объект класса String. При работе со строками кроме методов класса String можно использовать перегруженную операцию "+" объединения строк. Строковые константы заключаются в двойные кавычки и не заканчиваются символом '\0', это не ASCII-строки, а массивы символов.
В версии 5.0 введен процесс автоматической инкапсуляции данных базовых типов в соответствующие объекты оболочки и обратно (автоупаковка). При этом нет необходимости в создании соответствующего объекта с использованием оператора new. Например:
Integer iob = 71;
Автораспаковка – процесс извлечения из объекта-оболочки значения базового типа. Вызовы таких методов, как intValue(), doubleValue() становятся излишними.
Допускается участие объектов в арифметических операциях, однако не следует этим злоупотреблять, поскольку упаковка/распаковка является ресурсоемким процессом:
// пример # 4 : autoboxing & unboxing: NewProperties.java
packagechapt02;
public class NewProperties {
public static void main(String[] args) {
Integer j = 71; //создание объекта+упаковка
Integer k = ++j; //распаковка+операция+упаковка
int i = 2;
k = i + j + k;
}
}
Однако следующий код генерирует исключительную ситуацию
NullPointerException при попытке присвоить базовому типу значение null объекта класса Integer:
Integer j = null; //объект не создан!
int i = j; //генерация исключения во время выполнения
Несмотря на то, что значения базовых типов могут быть присвоены объектам классов-оболочек, сравнение объектов между собой происходит по ссылкам.
int i = 128;//заменить на 127 !!!
Integer oa = i; //создание объекта+упаковка
Integer ob = i;
System.out.println("oa==i " + (oa == i));// true
System.out.println("ob==i " + (ob == i));// true
System.out.println("oa==ob " + (oa == ob));// false(ссылки разные)
System.out.println("equals ->" + oa.equals(i)
+ ob.equals(i)
+ oa.equals(ob));// true
Метод equals() сравнивает не значения объектных ссылок, а значения объектов, на которые установлены эти ссылки. Поэтому вызов oa.equals(ob) возвращает значение true.
Значение базового типа может быть передано в метод equals(). Однако ссылка на базовый тип не может вызывать методы:
boolean b = i.equals(oa);// ошибка компиляции
При инициализации объекта класса-оболочки значением базового типа преобразование типов необходимо указывать явно, то есть код
Float f = 7;// правильно будет (float)7 или 7F вместо 7
вызывает ошибку компиляции.
Кроме того, стало возможным создавать объекты и массивы, сохраняющие различные базовые типы без взаимных преобразований, с помощью ссылки на класс Number, а именно:
Number n1 = 1;
Number n2 = 7.1;
Number array[] = {71, 7.1, 7L};
При автоупаковке значения базового типа возможны ситуации с появлением некорректных значений и непроверяемых ошибок.
Переменная базового типа всегда передается в метод по значению, а переменная класса-оболочки – по ссылке.
Операторы управления
Оператор выбора ifимеет следующий синтаксис:
if(boolexp) { /*операторы*/} //1
else{ /*операторы*/ } //2
Если выражение boolexp принимает значение true, то выполняется группа операторов 1, иначе – группа операторов 2. При отсутствии оператора else операторы, расположенные после окончания оператора if(строка 2), выполняются вне зависимости от значения булевского выражения оператора if. Допустимо также использование конструкции-лесенки if {} else if {}.
Аналогично C++ используется оператор switch:
switch(exp) {
caseexp1:{/*операторы*/}
Break;
caseexpN:{/*операторы*/}
Break;
default: {/*операторы*/}
}
При совпадении условий вида exp==exp1 выполняются подряд все блоки операторов до тех пор, пока не встретится оператор break, Значения exp1,…, expN должны быть константами и могут иметь значения типа int, byte, short, charили enum.
Операторы условного перехода следует применять так, чтобы нормальный ход выполнения программы был очевиден. После if следует располагать код, удовлетворяющий нормальной работе алгоритма, после else побочные и исключительные варианты. Аналогично для оператора switch нормальное исполнение алгоритма следует располагать в инструкциях case(наиболее вероятные варианты размещаются раньше остальных), альтернативное или для значений по умолчанию – в инструкции default.
В Java существует четыре вида циклов, первые три из них аналогичны соответствующим циклам в языке С++:
while(boolexp) { /*операторы*/ } // цикл с предусловием
do{ /*операторы*/ } while(boolexp); // цикл с постусловием
for(exp1; boolexp; exp3){ /*операторы*/ } // цикл с параметрами
Здесь по традиции exp1 – начальное выражение, boolexp – условие выполнения цикла, exp3 – выражение, выполняемое в конце итерации цикла (как правило, это изменение начального значения). Циклы выполняются, пока булевское выражение boolexp равно true.
Некоторые рекомендации при проектировании циклов:
- проверка условия для всех циклов выполняется только один раз за одну итерацию, для циклов for и while – перед итерацией, для цикла do/while – по окончании итерации.
- цикл for следует использовать при необходимости выполнения алгоритма строго определенное количество раз. Цикл while используется в случае, когда неизвестно число итераций для достижения необходимого результата, например, поиск необходимого значения в массиве или коллекции. Этот цикл применяется для организации бесконечных циклов в виде while(true).
- для цикла for не рекомендуется в цикле изменять индекс цикла.
- условие завершения цикла должно быть очевидным, чтобы цикл не «сорвался» в бесконечный цикл.
- для индексов следует применять осмысленные имена.
- циклы не должны быть слишком длинными. Такой цикл претендует на выделение в отдельный метод.
- вложенность циклов не должна превышать трех.
В версии 5.0 введен еще один цикл, упрощающий доступ к массивам и коллекциям:
for(ТипДанных имя : имяОбъекта){ /*операторы*/ }
При работе с массивами и коллекциями с помощью данного цикла можно получить доступ ко всем их элементам без использования индексов.
int[] array = {1, 3, 5};
for(int i : array) // просмотр всех элементов массива
System.out.printf("%d ", i);// вывод всех элементов
Изменять значения элементов массива с помощью такого цикла нельзя. Данный цикл может обрабатывать и единичный объект, если его класс реализует интерфейсы Iterableи Iterator.
В языке Java расширились возможности оператора прерывания цикла break и оператора прерывания итерации цикла continue, которые можно использовать с меткой, например:
int j = -3;
OUT: while(true) {
for(;;)
while (j < 10) {
if (j == 0)
break OUT;
else {
j++;
System.out.printf("%d ", j);
}
}
}
System.out.print("end");
Здесь оператор break разрывает цикл, помеченный меткой OUT. Тем самым решается вопрос об отсутствии необходимости в операторе goto для выхода из вложенных циклов.
Массивы
Массив представляет собой объект, где имя массива является объектной ссылкой. Элементами массива могут быть значения базового типа или объекты. Индексирование элементов начинается с нуля. Все массивы в языке Java являются динамическими, поэтому для создания массива требуется выделение памяти с помощью оператора new или прямой инициализации. Значения элементов неинициализированного массива, для которого выделена память, устанавливаются в значения по умолчанию для массива базового типа или null для массива объектных ссылок. Для объявления ссылки на массив можно записать пустые квадратные скобки после имени типа, например: int a[]. Аналогичный результат получится при записи int[] a.
/* пример # 5 : массивы и ссылки: ArrRef.java */
packagechapt02;
public class ArrRef {
public static void main(String[] args) {
intmyRef[], my;// объявление ссылки на массив и переменной
float[] myRefFloat, myFloat; // два массива!
// объявление с инициализацией нулевыми значениями по умолчанию
int myDyn[] = new int[10];
/*объявление с инициализацией */
int myInt[] = {5, 7, 9, -5, 6, -2};//6 элементов
byte myByte[] = {1, 3, 5};//3 элемента
/*объявление с помощью ссылки на Object */
Object myObj = new float[5];
// допустимые присваивания ссылок
myRef = myDyn;
myDyn = myInt;
myRefFloat = (float[])myObj;
myObj = myByte;// источник ошибки для следующей строки
myRefFloat = (float[])myObj;// ошибка выполнения
// недопустимые присваивания ссылок (нековариантность)
// myInt = myByte;
//myInt = (int[])myByte;
}
}
Ссылка на Object может быть проинициализирована массивом любого типа и любой размерности. Обратное действие требует обязательного приведения типов и корректно только в случае, если тип значений массива и тип ссылки совпадают. Если же ссылка на массив объявлена с указанием типа, то она может содержать данные только указанного типа и присваиваться другой ссылке такого же типа. Приведение типов в этом случае невозможно.
Присваивание myDyn=myIntприведет к тому, что значения элементов массива myDyn будут утрачены и две ссылки будут установлены на один массив myInt, то есть будут ссылаться на один и тот же участок памяти.
Массив представляет собой безопасный объект, поскольку все элементы инициализируются и доступ к элементам невозможен за пределами границ. Размерность массива хранится в его свойстве length.
Многомерных массивов в Java не существует, но можно объявлять массив массивов. Для задания начальных значений массивов существует специальная форма инициализатора, например:
int arr[][] = { { 1 },
{ 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9, 0 }
};
Первый индекс указывает на порядковый номер массива, например arr[2][0] указывает на первый элемент третьего массива, а именно на значение 4.
Matrix |
- |
a: int[][] |
+ |
Matrix(int, int) |
+ |
getHorizontalSize() : int |
+ |
getVerticalSize() : int |
+ |
getElement(int, int) : int |
+ |
setElement(int, int, int) : void |
+ |
toString() : String |
MatrixFactory |
+ |
createRandomized(int, int) : Matrix |
Multiplicator |
+ |
multiply(Matrix, Matrix) : void |
В следующей программе создаются и инициализируются массивы массивов равной длины (матрицы) и выполняется произведение одной матрицы на другую.
Рис. 2.4. Диаграмма классов для создания и умножения матриц
/* пример # 6 : класс хранения матрицы : Matrix.java */
package chapt02;
public class Matrix {
private int[][] a;
public Matrix(int n, int m) {
// создание и заполнение нулевыми значениями
a = new int[n][m];
}
public int getVerticalSize() {
return a.length;
}
public int getHorizontalSize() {
return a[0].length;
}
public int getElement(int i, int j) {
return a[i][j];
}
public void setElement(int i, int j, int value) {
a[i][j] = value;
}
public String toString() {
String s = "\nMatrix : " + a.length +
"x" + a[0].length + "\n";
for (int[] vector : a) {
for (int value : vector)
s+= value+" ";
s += "\n";
}
return s;
}
}
/* пример # 7 : класс-создатель матрицы : MatrixFactory.java */
package chapt02;
public class MatrixFactory {
public static Matrix createRandomized(int n, int m) {
Matrix matrix = new Matrix(n, m);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
matrix.setElement(i, j, (int)(Math.random()*m*m));
}
}
return matrix;
}
}
/* пример # 8 : произведение двух матриц : Multiplicator.java */
package chapt02;
public class Multiplicator {
public static Matrix multiply(Matrix p, Matrix q)
throws MultipleException {
int v = p.getVerticalSize();
int h = q.getHorizontalSize();
int temp = p.getHorizontalSize();
// проверка возможности умножения
if (temp != q.getVerticalSize())
throw new MultipleException();
// создание матрицы результата
Matrix result = new Matrix(v, h);
// умножение
for (int i = 0; i < v; i++)
for (int j = 0; j < h; j++) {
int value = 0;
for (int k = 0; k < temp; k++) {
value += p.getElement(i, k) * q.getElement(k, j);
}
result.setElement(i, j, value);
}
return result;
}
}
/* пример # 9 : исключительная ситуация матрицы : MultipleException.java */
package chapt02;
public class MultipleException extends Exception {}
/* пример # 10 : класс, запускающий приложение : Runner.java */
package chapt02;
public class Runner {
public static void main(String[] args) {
int n = 2, m = 3, l = 4;
Matrix p = MatrixFactory.createRandomized(n, m);
Matrix q = MatrixFactory.createRandomized(m, l);
System.out.println("Matrix first is: " + p);
System.out.println("Matrix second is: " + q);
try {
Matrix result = Multiplicator.multiply(p, q);
System.out.println("Matrix product is: "
+ result);
} catch (MultipleException e){
System.err.println("Matrices could"
+ " not be multiplied: ");
}
}
}
Так как значения элементам массивов присваиваются при помощи метода random(), то одним из вариантов выполнения кода может быть следующий:
Matrix first is:
Matrix : 2x3
6 4 2
0 8 4
Matrix second is:
Matrix : 3x4
8 0 2 7
6 1 0 0
1 2 4 5
Matrix product is:
Matrix : 2x4
74 8 20 52
52 16 16 20
Массивы объектов фактически не отличаются от массивов базовых типов. Они в действительности представляют собой массивы ссылок, проинициализированных по умолчанию значением null. Выделение памяти для хранения объектов массива должно производиться для каждой объектной ссылки в массиве.
Класс Маth
Класс java.lang.Math содержит только статические методы для физических и технических расчетов, а также константы E и PI.
Все методы класса вызываются без создания экземпляра класса (создать экземпляр класса Math невозможно). В классе определено большое количество методов для математических вычислений, а также ряд других полезных методов, таких какfloor(),ceil(), rint(), round(), max(),min(), которые выполняют задачи по округлению, поиску экстремальных значений, нахождению ближайшего целого и т.д. Рассмотрим пример обработки значения случайного числа, полученного с помощью метода random()классаMath.
/* пример # 11 : использование методов класса Math: MathMethods.java */
packagechapt02;
public class MathMethods {
public static void main(String[] args) {
final int MAX_VALUE = 10;
double d;
d = Math.random() * MAX_VALUE;
System.out.println("d = " + d);
System.out.println("Округленное до целого ="
+ Math.round(d));
System.out.println("Ближайшее целое, "
+ " <= исходного числа ="
+ Math.floor(d));
System.out.println("Ближайшее целое, "
+ " >= исходного числа ="
+ Math.ceil(d));
System.out.println("Ближайшее целое значение"
+ "к числу =" + Math.rint(d));
}
}
Один из вариантов выполнения кода представлен ниже:
d = 0.08439575016076173
Округленное до целого =0
Ближайшее целое, <= исходного числа =0.0
Ближайшее целое, >= исходного числа =1.0
Ближайшее целое значение к числу =0.0
Управление приложением
Все приложения автоматически импортируют пакет java.lang. Этот пакет содержит класс java.lang.System, предназначенный для выполнения ряда системных действий по обслуживанию работающего приложения. Объект этого класса создать нельзя.
Данный класс, кроме полей System.in,System.outи System.err, предназначенных для ввода/вывода на консоль, содержит целый ряд полезных методов:
void gc()–запуск механизма «сборки мусора»;
void exit(int status)–прекращение работы виртуальной java-машины (JVM);
void setIn(InputStream in), void setOut(PrintStream out),
void setErr(PrintStream err)–переназначение стандартных потоков ввода/вывода;
Properties getProperties()– получение всех свойств;
String getProperty(String key)–получение значения конкретного свойства;
void setSecurityManager(SecurityManager s), SecurityManager getSecurityManager()–получение и установка системы безопасности;
void load(String filename) – запуск программы из ОС;
void loadLibrary(String libname) –загрузка библиотеки;
void arrayCopy(параметры)–копирование части одного массива в другой.
Управлять потоком выполнения приложения можно с помощью класса
java.lang.Runtime. Объект класса Runtime создается при помощи вызова статического метода getRuntime(), возвращающего объект Runtime, который ассоциирован с данным приложением. Остановить виртуальную машину можно с помощью методов exit(int status) и halt(int status). Существует несколько возможностей по очистке памяти: gc(),
runFinalization() и др. Определить общий объем памяти и объем свободной памяти можно с помощью методов totalMemory() и freeMemory().
/*пример # 12 : информация о состоянии оперативной памяти: RuntimeDemo.java*/
packagechapt02;
public class RuntimeDemo {
public static void main(String[] args) {
Runtime rt = Runtime.getRuntime();
System.out.println("Полный объем памяти: "
+ rt.totalMemory());
System.out.println("Свободная память: "
+ rt.freeMemory());
double d[] = new double[10000];
System.out.println("Свободная память после" +
" объявления массива: " + rt.freeMemory());
//инициализация процесса
ProcessBuilder pb =
new ProcessBuilder("mspaint","c:\\temp\\cow.gif");
try {
pb.start(); // запуск mspaint.exe
} catch (java.io.IOException e) {
System.err.println(e.getMessage());
}
System.out.println("Свободная память после "
+ "запуска mspaint.exe: " + rt.freeMemory());
System.out.println("Список команд: "
+ pb.command());
}
}
В результате выполнения этой программы может быть выведена, например, следующая информация:
Полный объем памяти: 2031616
Свободная память: 1903632
Свободная память после объявления массива: 1823336
Свободная память после запуска mspaint.exe: 1819680
Список команд: [mspaint, c:\temp\cow.gif]
В примере использованы возможности класса
java.lang.ProcessBuilder, обеспечивающего запуск внешних приложений с помощью метода start(), в качестве параметров которого применяются строки с именем запускаемого приложения и загружаемого в него файла. Внешнее приложение использует для своей загрузки и выполнения память операционной системы.
Метод arraycopy() класса System, позволяет копировать часть одного массива в другой, начиная с указанной позиции.
/* пример # 13 : копирование массива: ArrayCopyDemo.java */
packagechapt02;
public class ArrayCopyDemo {
public static void main(String[] args) {
intmas1[] = { 1, 2, 3 },
mas2[] = { 4, 5, 6, 7, 8, 9 };
show("mas1[]: ", mas1);
show("mas2[]: ", mas2);
// копирование массива mas1[] в mas2[]
System.arraycopy(mas1, 0, mas2, 2, 3);
/*
0 – mas1[] копируется начиная с первого элемента,
2 – элемент, с которого начинается замена,
3 – количество копируемых элементов
*/
System.out.printf("%n после arraycopy(): ");
show("mas1[]: ", mas1);
show("mas2[]: ", mas2);
}
private static voidshow(String s, int[] mas) {
System.out.printf("%n%s", s);
for (int i : mas) System.out.printf("%d ", i);
}
}
В результате будет выведено:
mas1[]: 1 2 3
mas2[]: 4 5 6 7 8 9
после arraycopy():
mas1[]: 1 2 3
mas2[]: 4 5 1 2 3 9
Задания к главе 2
Вариант A
В приведенных ниже заданиях необходимо вывести внизу фамилию разработчика, дату и время получения задания, а также дату и время сдачи задания. Для получения последней даты и времени следует использовать класс Date. Добавить комментарии в программы в виде /** комментарий */, извлечь эту документацию в HTML-файл и просмотреть полученную страницу Web-браузером.
1. Ввести n строк с консоли, найти самую короткую и самую длинную строки. Вывести найденные строки и их длину.
2. Ввести n строк с консоли. Упорядочить и вывести строки в порядке возрастания (убывания) значений их длины.
3. Ввести n строк с консоли. Вывести на консоль те строки, длина которых меньше (больше) средней, а также длину.
4. Ввести n слов с консоли. Найти слово, в котором число различных символов минимально. Если таких слов несколько, найти первое из них.
5. Ввести n слов с консоли. Найти количество слов, содержащих только символы латинского алфавита, а среди них – количество слов с равным числом гласных и согласных букв.
6. Ввести n слов с консоли. Найти слово, символы в котором идут в строгом порядке возрастания их кодов. Если таких слов несколько, найти первое из них.
7. Ввести n слов с консоли. Найти слово, состоящее только из различных символов. Если таких слов несколько, найти первое из них.
8. Ввести n слов с консоли. Среди слов, состоящих только из цифр, найти слово-палиндром. Если таких слов больше одного, найти второе из них.
9. Написать программы решения задач 1–8, осуществляя ввод строк как аргументов командной строки.
10. Используя оператор switch, написать программу, которая выводит на экран сообщения о принадлежности некоторого значения kинтервалам (-10k, 0], (0, 5], (5, 10], (10, 10k].
11. Используя оператор switch, написать программу, которая выводит на экран сообщения о принадлежности некоторого значения k интервалам (-10k, 5], [0, 10], [5, 15], [10, 10k].
12. Написать программу, которая выводит числа от 1 до 25 в виде матрицы 5x5 слева направо и сверху вниз.
13. Написать программу, позволяющую корректно находить корни квадратного уравнения. Параметры уравнения должны задаваться с командной строки.
14. Ввести число от 1 до 12. Вывести на консоль название месяца, соответствующего данному числу. (Осуществить проверку корректности ввода чисел).
Вариант В
Ввести с консоли n – размерность матрицы a[n][n]. Задать значения элементов матрицы в интервале значений от -n до n с помощью датчика случайных чисел.
1. Упорядочить строки (столбцы) матрицы в порядке возрастания значений элементов k-го столбца (строки).
2. Выполнить циклический сдвиг заданной матрицы на k позиций вправо (влево, вверх, вниз).
3. Найти и вывести наибольшее число возрастающих (убывающих) элементов матрицы, идущих подряд.
4. Найти сумму элементов матрицы, расположенных между первым и вторым положительными элементами каждой строки.
5. Транспонировать квадратную матрицу.
6. Вычислить норму матрицы.
7. Повернуть матрицу на 90 (180, 270) градусов против часовой стрелки.
8. Вычислить определитель матрицы.
9. Построить матрицу, вычитая из элементов каждой строки матрицы ее среднее арифметическое.
10. Найти максимальный элемент(ы) в матрице и удалить из матрицы все строки и столбцы, его содержащие.
11. Уплотнить матрицу, удаляя из нее строки и столбцы, заполненные нулями.
12. В матрице найти минимальный элемент и переместить его на место заданного элемента путем перестановки строк и столбцов.
13. Преобразовать строки матрицы таким образом, чтобы элементы, равные нулю, располагались после всех остальных.
14. Округлить все элементы матрицы до целого числа.
15. Найти количество всех седловых точек матрицы. (Матрица А имеет седловую точку Аi,j, если Аi,j является минимальным элементом в i-й строке и максимальным в j-м столбце).
16. Перестроить матрицу, переставляя в ней строки так, чтобы сумма элементов в строках полученной матрицы возрастала.
17. Найти число локальных минимумов. (Соседями элемента матрицы назовем элементы, имеющие с ним общую сторону или угол. Элемент матрицы называется локальным минимумом, если он строго меньше всех своих соседей.)
18. Найти наибольший среди локальных максимумов. (Элемент матрицы называется локальным максимумом, если он строго больше всех своих соседей.)
19. Перестроить заданную матрицу, переставляя в ней столбцы так, чтобы значения их характеристик убывали. (Характеристикой столбца прямоугольной матрицы называется сумма модулей его элементов).
20. Путем перестановки элементов квадратной вещественной матрицы добиться того, чтобы ее максимальный элемент находился в левом верхнем углу, следующий по величине – в позиции (2,2), следующий по величине – в позиции (3,3) и т. д., заполнив таким образом всю главную диагональ.
Тестовые задания к главе 2
Вопрос 2.1.
Какие из следующих строк скомпилируются без ошибки?
1) float f = 7.0;
2) char c = "z";
3) byte b = 255;
4) boolean n = null;
5) int i = 32565;
6) int j = ’ъ’.
Вопрос 2.2.
Какие варианты записи оператора условного перехода корректны?
1) if (i<j) { System.out.print("-1-"); }
2) if (i<j) then System.out.print("-2-");
3) if i<j { System.out.print("-3-"); }
4) if [i<j] System.out.print("-4-");
5) if (i<j) System.out.print("-5-");
6) if {i<j} then System.out.print("-6-");.
Вопрос 2.3.
Какие из следующих идентификаторов являются корректными?
1) 2int;
2) int_#;
3) _int;
4) _2_;
5) $int;
6) #int.
Вопрос 2.4.
Какие из приведенных объявлений массивов корректны?
int a1[] = {};
int a2[] = new int[]{1,2,3};
int a3[] = new int[](1,2,3);
int a4[] = new int[3];
int a5[] = new int[3]{1,2,3};
1) a1;
2) a2;
3) a3;
4) a4;
5) a5.
КЛАССЫ
Класс представляет описание совокупности объектов с общими атрибутами, методами, отношениями и семантикой.
Классы – основной элемент абстракции языка Java, основное назначение которого, кроме реализации назначенного ему контракта, это сокрытие реализации. Классы всегда взаимодействуют друг с другом и объединяются в пакеты. Из пакетов создаются модули, которые взаимодействуют друг с другом только через ограниченное количество методов и классов, не имея никакого представления о процессах, происходящих внутри других модулей.
Имя класса в пакете должно быть уникальным. Физически пакет представляет собой каталог, в который помещаются программные файлы, содержащие реализацию классов.
Классы позволяют провести декомпозицию поведения сложной системы до множества элементарных взаимодействий связанных объектов. Класс определяет структуру и/или поведение некоторого элемента предметной области, для которой разрабатывается программная модель.
Определение простейшего класса без наследования имеет вид:
class ИмяКласса {
{}//логические блоки
// дружественные данные и методы
private// закрытые данные и методы
protected// защищенные данные и методы
public// открытые данные и методы
}
Переменные класса и константы
Классы инкапсулируют переменные и методы – члены класса. Переменные класса объявляются в нем следующим образом:
cпецификатор тип имя;
В языке Java могут использоваться статические переменные класса, объявленные один раз для всего класса со спецификатором static и одинаковые для всех экземпляров (объектов) класса, или переменные экземпляра класса, создаваемые для каждого объекта класса. Поля класса объявляются со спецификаторами доступа public, private, protected или по умолчанию без спецификатора. Кроме данных – членов класса, в методах класса используются локальные переменные и параметры методов. В отличие от переменных класса, инкапсулируемых нулевыми элементами, переменные методов не инициализируются по умолчанию.
Переменные со спецификатором final являются константами. Спецификатор final можно использовать для переменной, объявленной в методе, а также для параметра метода.
В следующем примере рассматриваются объявление и инициализация значений полей класса и локальных переменных метода, а также использование параметров метода:
/* пример # 1 : типы атрибутов и переменных: Second.java */
package chapt03;
import java.util.*;
class Second {
private int x; // переменная экземпляра класса
private int y = 71; // переменная экземпляра класса
public final int CURRENT_YEAR = 2007; // константа
protected static int bonus; // переменная класса
static String version = "Java SE 6"; // переменная класса
protected Calendar now;
public int method(int z) { // параметр метода
z++;
int a; // локальная переменная метода
//a++; // ошибка компиляции, значение не задано
a = 4; //инициализация
a++;
now = Calendar.getInstance();//инициализация
return a + x + y + z;
}
}
В рассмотренном примере в качестве переменных экземпляра класса, переменных класса и локальных переменных метода использованы данные базовых типов, не являющиеся ссылками на объекты (кроме String). Данные могут быть ссылками, назначить которым реальные объекты можно с помощью оператора new.
Ограничение доступа
Язык Java предоставляет несколько уровней защиты, обеспечивающих возможность настройки области видимости данных и методов. Из-за наличия пакетов Java работает с четырьмя категориями видимости между элементами классов:
· по умолчанию – дружественные члены класса доступны классам, находящимся в том же пакете;
· private – члены класса доступны только членам данного класса;
· protected – члены класса доступны классам, находящимся в том же пакете, и подклассам – в других пакетах;
· public – члены класса доступны для всех классов в этом и других пакетах.
Член класса (поле или метод), объявленный public, доступен из любого места вне класса. Все, что объявлено private, доступно только методам внутри класса и нигде больше. Если у элемента вообще не указан модификатор уровня доступа, то такой элемент будет виден и доступен из подклассов и классов того же пакета. Именно такой уровень доступа используется по умолчанию. Если же необходимо, чтобы элемент был доступен из другого пакета, но только подклассам того класса, которому он принадлежит, нужно объявить такой элемент со спецификатором protected. Действие спецификаторов доступа распространяется только на тот элемент класса, перед которым они стоят.
Спецификатор доступа public может также стоять перед определением внешнего (enclosing) класса. Если данный спецификатор отсутствует, то класс недоступен из других пакетов.
Конструкторы
Конструктор - это метод, который автоматически вызывается при создании объекта класса и выполняет действия по инициализации объекта. Конструктор имеет то же имя, что и класс; вызывается не по имени, а только вместе с ключевым словом newпри создании экземпляра класса. Конструктор не возвращает значение, но может иметь параметры и быть перегружаемым.
Деструкторы в языке Java не используются, объекты уничтожаются сборщиком мусора после прекращения их использования (потери ссылки). Аналогом деструктора является метод finalize(). Исполняющая среда языка Java будет вызывать его каждый раз, когда сборщик мусора будет уничтожать объект класса, которому не соответствует ни одна ссылка.
/* пример # 2 : перегрузка конструктора: Quest.java */
package chapt03;
public class Quest {
private int id;
private String text;
// конструктор без параметров (по умолчанию)
public Quest() {
super();/* если класс будет объявлен без конструктора, то
компилятор предоставит его именно в таком виде*/
}
// конструктор с параметрами
public Quest(int idc, String txt) {
super();/*вызов конструктора суперкласса явным образом
необязателен, компилятор вставит его автоматически*/
id = idc;
text = txt;
}
}
Объект класса Quest может быть создан двумя способами, вызывающими один из конструкторов:
Quest a = new Quest();//инициализация полей значениями по умолчанию
Quest b = new Quest(71, "Сколько бит занимает boolean?");
Оператор newвызывает конструктор, поэтому в круглых скобках могут стоять аргументы, передаваемые конструктору.
Если конструктор в классе не определен, Java предоставляет конструктор по умолчанию без параметров, который инициализирует поля класса значениями по умолчанию, например: 0, false, null. Если же конструктор с параметрами определен, то конструктор по умолчанию становится недоступным и для его вызова необходимо явное объявление такого конструктора. Конструктор подкласса всегда вызывает конструктор суперкласса. Этот вызов может быть явным или неявным и всегда располагается в первой строке кода конструктора. Если конструктору суперкласса нужно передать параметры, то необходим явный вызов:
super(параметры);
В следующем примере объявлен класс Point c двумя полями (атрибутами), конструктором и методами для инициализации и извлечения значений атрибутов.
/* пример # 3 : вычисление расстояния между точками: Point.java: LocateLogic.java: Runner.java */
package chapt03;
public class Point {
/* объект инициализируется при создании и не изменяется */
private final double x;
private final double y;
public Point(final double xx, final double yy) {
super();
x = xx;
y = yy;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
}
package chapt03;
public class LocateLogic {
public double calculateDistance(
Point t1, Point t2) {
/* вычисление расстояния */
double dx = t1.getX() - t2.getX();
double dy = t1.getY() - t2.getY();
return Math.hypot(dx, dy);
}
}
package chapt03;
public class Runner {
public static void main(String[] args) {
// локальные переменные не являются членами класса
Point t1 = new Point(5, 10);
Point t2 = new Point(2, 6);
System.out.print("расстояние равно : "
+ new LocateLogic().calculateDistance(t1, t2));
}
}
В результате будет выведено:
расстояние равно :5.0
Конструктор объявляется со спецификатором public, чтобы была возможность вызывать его при создании объекта в любом пакете приложения. Спецификатор private не позволяет создавать объекты вне класса, а спецификатор «по умолчанию» – вне пакета. Спецификатор protected позволяет создавать объекты в текущем пакете и для подклассов в других пакетах.
Методы
Изобретение методов является вторым по важности открытием после создания компьютера. Метод – основной элемент структурирования хода.
Все функции Java объявляются только внутри классов и называются методами. Простейшее определение метода имеет вид:
returnType methodName(список_параметров) {
// тело метода
returnvalue;// если нужен возврат значения (returnType не void)
}
Если метод не возвращает значение, ключевое слово return может отсутствовать, тип возвращаемого значения в этом случае будет void. Вместо пустого списка параметров метода тип void не указывается, а только пустые скобки. Вызов методов осуществляется из объекта или класса (для статических методов):
ObjectName.methodName();
Методы-конструкторы по имени вызываются автоматически только при создании объекта класса с помощью оператора new.
Для того чтобы создать метод, нужно внутри объявления класса написать объявление метода и затем реализовать его тело. Объявление метода как минимум должно содержать тип возвращаемого значения (возможен void) и имя метода. В приведенном ниже объявлении метода элементы, заключенные в квадратные скобки, являются необязательными.
[доступ] [static] [abstract] [final] [native]
[synchronized] returnType methodName(список_параметров)
[throws список_исключений]
Как и для полей класса, спецификатор доступа к методам может быть public, private, protected и по умолчанию. При этом методы суперкласса можно перегружать или переопределять в порожденном подклассе.
Объявленные в методе переменные являются локальными переменными метода, а не членами классов, и не инициализируются значениями по умолчанию при создании объекта класса или вызове метода.
Статические методы и поля
Поля данных, объявленные в классе как static, являются общими для всех объектов класса и называются переменными класса. Если один объект изменит значение такого поля, то это изменение увидят все объекты. Для работы со статическими атрибутами используются статические методы, объявленные со спецификатором static. Такие методы являются методами класса, не привязаны ни к какому объекту и не содержат указателя this на конкретный объект, вызвавший метод. Статические методы реализуют парадигму «раннего связывания», жестко определяющую версию метода на этапе компиляции. По причине недоступности указателя this статические поля и методы не могут обращаться к нестатическим полям и методам напрямую, так как для обращения к статическим полям и методам достаточно имени класса, в котором они определены.
// пример # 4 : статические метод и поле: Mark.java
package chapt03;
public class Mark {
private int mark = 3;
public static int coeff = 5;
public double getResult() {
return (double)coeff*mark/100;
}
public static void setCoeffFloat(float c) {
coeff = (int)coeff*c;;
}
public void setMark(int mark) {
this.mark = mark;
}
//из статического метода нельзя обратиться к нестатическим полям и методам
/*public static int getResult() {
setMark(5);//ошибка
return coeff*mark/100;//ошибка
}*/
}
При создании двух объектов
Mark ob1 = new Mark();
Mark ob2 = new Mark();
Значение ob1.coeff и ob2.coeff и равно 5, поскольку располагается в одной и той же области памяти. Изменить значение статического члена можно прямо через имя класса:
Mark.coeff = 7;
Вызов статического метода также следует осуществлять с помощью указания:
ClassName.methodName(), а именно:
Mark.setCoeffFloat();
float z = Math.max(x, y); // определение максимума из двух значений
System.exit(1); // экстренное завершение работы приложения
Статический метод можно вызывать также с использованием имени объекта, но такой вызов снижает качество кода и не будет логически корректным, хотя и не приведет к ошибке компиляции.
Переопределение статических методов класса не имеет практического смысла, так как обращение к статическому атрибуту или методу осуществляется по большей части посредством задания имени класса, которому они принадлежат.
Модификатор final
Модификатор final используется для определения констант в качестве члена класса, локальной переменной или параметра метода. Методы, объявленные как final, нельзя замещать в подклассах, для классов – создавать подклассы. Например:
/* пример # 5 : final-поля и методы: Rector.java: ProRector.java */
package chapt03;
public class Rector {
// инициализированная константа
final int ID = (int)(Math.random()*10);
// неинициализированная константа
final String NAME_RECTOR;
public Rector() {
// инициализация в конструкторе
NAME_RECTOR = "Старый";// только один раз!!!
}
// {NAME_RECTOR = "Новый";} // только один раз!!!
public final void jobRector() {
// реализация
// ID = 100; //ошибка!
}