Форум программистов, компьютерный форум, киберфорум
Наши страницы
vvm28
Войти
Регистрация
Восстановить пароль
Рейтинг: 5.00. Голосов: 1.

Подводные камни Java. Ошибки начинающих.

Запись от vvm28 размещена 27.06.2018 в 12:51
Обновил(-а) vvm28 14.07.2018 в 12:52 (орфография)

Подводные камни Java.
Небольшая шпаргалка для начинающих. (Черновик)

Все языки программирования имеют свои недостатки. Это обусловлено многими причинами.
Язык Java не исключение. Я попытался собрать некоторые очевидные и не очевидные трудности с которыми сталкивается начинающий программист java. Уверен, что опытные программисты тоже найдут в моей статье полезное.
Практика, внимательность и полученный опыт программирования помогут избавить вас от многих ошибок.
Но некоторые ошибки и трудности лучше рассмотреть заранее. Я приведу несколько примеров с кодом с объяснениями.
Многие объяснения вам станут понятны из комментариев к коду.Практика дает очень многое, так как некоторые правила не столь очевидны. Некоторые лежат на поверхности, некоторые скрыты в библиотеках языка или в виртуальной машине java. Помните что java это не только язык программирования c набором библиотек, это еще и виртуальная машина java.

Для статьи я специально написал работающий код с подробными комментариями.
Для тестирования код java помещен в отдельные пакеты.
Пример: package underwaterRocks.simple;

С какими трудностями сталкиваются начинающие.

Итак начнем.

Опечатки.
Бывает так, что начинающие программисты делают опечатки, которые трудно обнаружить с первого взляда.
Пример кода:

Файл: Simple.java
Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
; после условия и блок 
*/
package underwaterRocks.simple;
 
/**
 *
 * @author vvm
 */
public class Simple {
    public static void main(String[] args) {
        int ival = 10;
        if(ival>0);
        {
            System.out.println("Этот блок не зависит от условия");
        }
    }
}

Объяснение: "Точка с запятой означает конец оператора. В данном случае ; - это пустой оператор.
Это логическая ошибка. Такую ошибку бывает трудно обнаружить. Копмпилятор сочтет, что всё правильно.
"

Присвоение в условии вместо сравнения.
В условии присвоение переменной.
Это не ошибка, но использование такого приема должно быть оправдано.
Java
1
2
boolean myBool = false;
if(myBool = true) System.out.println(myBool);
== - это сравнение на равенство
= - это присвоение, вы можете проговорить a = 10; как: "а присвоить значение 10"

Условие в скобках возвращает логическое значение.
Не важно в каком порядке вы запишие. Вы можете сравнить так: ( 0 == a) или ( 5 == a)
Если вы забудете один знак равенства, например так ( 0 = a ) или (5 = a), то компилятор сообщит вам об ошибке.
Вы также можете записать в удобочитаемой форме какой-то интервал.
Например: вам нужно написать a больше 7 и меньше 10.
вы пишите так: ( a>7 && a<10 ), но с таким же успехом вы можете написать: ( 7<a && a<10),
теперь вы видите, что a находится в интервале между 7 и 10. Это более наглядно.

Логическая ошибка.
if(условие){} if(условие){} else{} - else относится к бижайшему if.
Часто это бывает причиной ошибок начинающих.



Инициализация переменных.

Рассмотрим инициализацию переменных примитивного типа.


Примитивы (byte, short, int, long, char, float, double, boolean)

Начальные значения.
Цитата:
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char '\u0000'
String (or any object) null
boolean false (зависит от jvm)
Примечание:
Локальные переменные немного отличаются;
компилятор никогда не присваевает значение по умалчанию неинициализированной локальной переменной


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

Подтверждение этого примечания в коде:
Файл: MyInitLocal.java
Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
  инициализация переменных класса и локальных переменных
 */
package underwaterRocks.myInit;
 
/**
 *
 * @author vvm
 */
public class MyInitLocal {
    float classes_f;
    int classes_gi;
    public static void main(String[] args) {
        float f;
        int i;
        MyInitLocal myInit = new MyInitLocal();
        
        /* в этом месте переменные уже инициализированы параметрами по умолчанию.*/
        System.out.println("myInit.classes_f = " + myInit.classes_f);
        System.out.println("myInit.classes_gi = " + myInit.classes_gi);
        
      //  System.out.println("f = " + f); // ошибка. Локальная переменная не инициализирована
      //  System.out.println("f = " + i); // ошибка. Локальная переменная не инициализирована
        
    }
}






Диапазоны значений:
Цитата:
byte (целые числа, 1 байт, [-128, 127])
short (целые числа, 2 байта, [-32768, 32767])
int (целые числа, 4 байта, [-2147483648, 2147483647])
long (целые числа, 8 байт, [-922372036854775808,922372036854775807])
float (вещественные числа, 4 байта)
double (вещественные числа, 8 байт)
char (символ Unicode, 2 байта, [0, 65536])
boolean (значение истина/ложь, используется int, зависит от JVM)
Документация oracle https://docs.oracle.com/javase/tutor...datatypes.html


Попытаемся инициализировать переменную типа long числом: 922372036854775807.
У нас ничего не выйдет. Потому как это целочисленный литерал типа int.
Правильная инициализация литералом типа long: 922372036854775807L;

Пример кода:
Файл: MyInitLocalLong.java
Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
 Инициализация long локально 
 */
package underwaterRocks.myInit;
 
/**
 *
 * @author vvm
 */
public class MyInitLocalLong {
    public static void main(String[] args) {
      //  long al = 922372036854775807; //ошибка integer number too large
        long bl = 922372036854775807L; // так правильно  
    }
}


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

Неправильное сравнение double.
Рассмотрим тип double.

Пример кода:
Файл: MyDouble.java
Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
 Сравнение double
 Осторожно - double.
 */
package underwaterRocks.myDouble;
 
/**
 *
 * @author vvm
 */
public class MyDouble {
    public static void main(String[] args) {
        double dx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1;
        System.out.println("dx = " + dx); // dx = 0.9999999999999997
        System.out.print("Сравнение (dx == 1.0):");
        System.out.println(dx == 1.0); // false, потому что 1.0 не равно 0.9999999999999997
        
        /*как правильно сравнивать double*/
        final double EPSILON = 1E-14;
        double xx = 1.4 - 0.1 - 0.1 - 0.1 - 0.1;
        double xy = 1.0;
        /* сравниваем xx c xy */
        if (Math.abs(xx - xy) < EPSILON)
            System.out.println(xx + " это примерно равно " + xy + " EPSILON = " + EPSILON);
    } 
}

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


Конструктор класса.
Конструктоор класса совпадает с именем класса и ничего не возвращает, даже void.

Пример кода:
Файл: MyConstructor.java
Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 конструктор ничего не возвращает, даже void
 то что с void обычный метод класса
 */
package underwaterRocks.myConstructor;
 
/**
 *
 * @author vvm
 */
public class MyConstructor {
    public MyConstructor(){
        System.out.println("Я конструктор без void");
    }
    public void MyConstructor(){
        System.out.println("Я конструктор c void");
    }
    public static void main(String[] args) {
        MyConstructor myconst = new MyConstructor();
        myconst.MyConstructor(); // вызов обычного метода
    }
}

Как мы видим в коде два метода с одинаковыми именами
MyConstructor() и MyConstructor(). Один из методов ничего не возвращает.
Это и есть конструктор нашего класса. Другой метод с void - это обычный метод класса.
В случае, когда вы не создали конструктор или создали, по вашему мнению конструктор класса с void,
то компилятор создаст конструктор по умолчанию и вы будете удивлены, почему ваш конструктор не работает.

Деление на ноль.
Как вы думаете, какой будет результат выполнения такого кода.
Файл: DivisionByZero.java
Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package divisionByZero;
import static java.lang.Double.POSITIVE_INFINITY;
 
/**
 *
 * @author vvm
 */
public class DivisionByZero {
    public static void main(String[] args) {
    try{
        float f = 12.2f;
        double d = 8098098.8790d;
        System.out.println(f/0); 
        System.out.println(d/0);  
        System.out.println(POSITIVE_INFINITY == f/0);
        System.out.println(POSITIVE_INFINITY == d/0);
    }
    catch (NumberFormatException ex) { 
            System.out.println("NumberFormatException"); 
        } 
        catch (ArithmeticException ex) { 
            System.out.println("ArithmeticException"); 
        }  
    }
    
}

Выполнение кода выведет:
Кликните здесь для просмотра всего текста

Infinity
Infinity
true
true

Деление целого типа на ноль даст ArithmeticException.
В классе java.lang.Double определена константа POSITIVE_INFINITY;
public static final float POSITIVE_INFINITY = 1.0d / 0.0d;
Она преобразуется в строку равную Infinity.

Порядок инициализации.
InitClass.java
Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
 инициализация класса
 */
package myInitClass;
 
/**
 *
 * @author vvm
 */
public class InitClass {
    InitClass(){ // конструктор класса
        System.out.print("Конструктор"); 
    }
    { // блок инициализации
        System.out.print("3"); 
    } 
    
    public static void main(String[] args) { 
        System.out.print("2"); 
        new InitClass(); 
    } 
    
    static { // статический блок инициализации
        System.out.print("1"); 
    } 
}

Вначале выполняются все статические блоки, затем блоки инициализации, затем конструктор класса.
Выведется: 123Конструктор

Продолжение следует.
Спасибо что прочитали. Надеюсь статья вам понравилась. Буду рад вашим комментариям, замечаниям, предложениям.
Пишите, комментируйте.


Автор: Вячеслав Мищенко
Тема: Java SE, Ошибки начинающих программистов. Подводные камни java лежащие на поверхности.
Размещено в java
Просмотров 295 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru