📜 ⬆️ ⬇️

Java pitfalls. Part 1

Hello. I want to bring to your attention a short article. The article is intended for beginners. But even if you are an experienced developer, do not make hasty conclusions.
I hope this publication will be useful not only for beginners.

The purpose of this publication:
Show the most common mistakes of beginners and some techniques for correcting them. It is clear that some errors can be complex and occur for one reason or another. The purpose of the publication is to analyze them to some extent and help identify them at an early stage. I hope this publication will be useful for beginners.

Java Pitfalls


All programming languages ​​have their advantages and disadvantages. This is due to many reasons. Java is no exception. I tried to collect some obvious and not obvious difficulties faced by an aspiring Java programmer. I am sure that experienced programmers will also find something useful in my article. Practice, attentiveness and the experience of programming, will help save you from many mistakes. But some errors and difficulties should be considered in advance. I will give a few examples with code and explanations. Many explanations you will become clear from the comments to the code. Practice gives a lot, as some rules are not so obvious. Some are on the surface, some are hidden in the libraries of the language or in the java virtual machine. Remember that java is not only a programming language with a set of libraries, it is also a java virtual machine.

For the article, I specifically wrote a working code with detailed comments. For writing the article was used java 8. For testing, the java code is placed in separate packages.

Example: “package underwaterRocks.simple;”

What difficulties do beginners face?

Typos


It so happens that novice programmers make typos that are difficult to detect at a glance.

Code example:

File: "Simple.java"

/* учебные пример ; после условия и блок */ package underwaterRocks.simple; /** * * @author Ar20L80 */ public class Simple { public static void main(String[] args) { int ival = 10; if(ival>0); { System.out.println("Этот блок не зависит от условия"); } } } 

Explanation : “Semicolon means end of operator. In this case; - this is the end of the empty statement. This is a logical error. Such an error can be difficult to detect.

The compiler will consider that everything is correct. Condition if (ival> 0); in this case does not make sense. Because it means: if ival is greater than zero, do nothing and continue. ”

Assignment in condition instead of comparison


In a condition assignment to a variable.

This is not a mistake, but the use of such a technique should be justified.

  boolean myBool = false; if(myBool = true) System.out.println(myBool); 

In this code, if (myBool = true) means: “Assign the variable myBool to true,
if the expression is true, fulfill the condition following the brackets. "

In this code, the condition will always be true. And System.out.println (myBool); will always be met, regardless of the condition.

== is a comparison for equality.
= Is an assignment, you can speak a = 10; as: "and assign the value 10".

The condition in parentheses returns a boolean value.
No matter what order you write it down. You can compare like this: (0 == a) or (5 == a)
If you forget one equal sign, for example (0 = a) or (5 = a), the compiler will tell you about the error. You assign a value, not a comparison.
You can also write some space in a readable form.
For example: you need to write: a is greater than 5 and less than 10.
You write like this: (a> 4 && a <10), but you might as well write: (4 <a && a <10),
now you see that a lies between 4 and 10, excluding these values. This is more obvious. It is immediately evident that a lies in the interval between 4 and 10, excluding these values.

Code example (interval] 3.9 [):
if (3 <a && a <9);

Logical error


if (condition) {} if (condition) {} else {} - else refers to the nearest if.
Often this is the cause of beginners' mistakes.

Wrong string comparison

Beginners quite often use == instead of .equals to compare strings.

Variable initialization


Consider initializing variables of primitive type.

Primitives (byte, short, int, long, char, float, double, boolean).

Initial values

 byte 0 short 0 int 0 long 0L float 0.0f double 0.0d char '\u0000' String (or any object) null boolean false (зависит от jvm) 

Note:

Local variables are slightly different;
The compiler never assigns a default value to an uninitialized local variable.

If you cannot initialize your local variable where it is declared,
don't forget to assign a value to it before trying to use it.

Access to an uninitialized local variable will result in a compile-time error.

Confirmation of this note in the code:

File: "MyInitLocal.java"

 /* учебные пример инициализация переменных класса и локальных переменных */ package underwaterRocks.myInit; /** * * @author Ar20L80 */ 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); // ошибка. Локальная переменная не инициализирована } } 

Value ranges:

byte (целые числа, 1 байт, [-128, 127])
short (целые числа, 2 байта, [-32768, 32767])
int (целые числа, 4 байта, [-2147483648, 2147483647])
long (целые числа, 8 байт, [-922372036854775808,922372036854775807])
float (вещественные числа, 4 байта)
double (вещественные числа, 8 байт)
char (символ Unicode, 2 байта, 16 бит, [0, 65535])
boolean (значение истина/ложь, используется int, зависит от JVM)

char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\ u0000' (or 0) and a maximum value of '\ uffff' (or 65,535 inclusive).


Oracle Documentation >>

Let's try to initialize a long type variable with the number: 922372036854775807.
Nothing will come of it. Because it is an integer literal of type int.
Proper initialization with a long literal: 922372036854775807L;

Code example:

File: "MyInitLocalLong.java"

 /* учебные пример Инициализация long локально */ package underwaterRocks.myInit; /** * * @author Ar20L80 */ public class MyInitLocalLong { public static void main(String[] args) { // long al = 922372036854775807; //ошибка integer number too large long bl = 922372036854775807L; // так правильно } } 

What to look for when initializing a variable.

On the range of values ​​of a variable of this type. That the variable is initialized with a literal of a certain type. On explicit and implicit type conversion. On compatibility types.

When using Integer type shells, you should pay attention to auto packing and auto unpacking of these types.

Incorrect use double


Here you need to clarify. This is not about using double type incorrectly.
We use correctly. Only the result can surprise a novice programmer.
 /* учебный пример */ package underwaterRocks.tstDouble; /** * * @author vvm */ public class MinusDouble { public static void main(String[] args) { double a = 4.64; double b = 2.64; System.out.println("ab = "+(ab)); } } /* Вывод программы run: ab = 1.9999999999999996 */ 


A note about the type of double. Floating point allows you to count with a given relative error and a huge range. In scientific calculations often need relative error.

Incorrect double comparison


Consider the type double.

Code example:

File: MyDouble.java

 /* учебные пример Сравнение double Осторожно - double. */ package underwaterRocks.myDouble; /** * * @author Ar20L80 */ 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); } } 

The double type is convenient where high precision is not needed. For financial transactions, this type is not suitable. Although some are not very honest companies, I use the double type to round off in the direction they want. For financial transactions, the BigDecimal class is used in financial calculations, since real primitive types are not suitable for this purpose for reasons of loss of accuracy and errors in rounding results. However, more accurate results are obtained using the BigInteger class.

Class constructor


The class constructor matches the class name and returns nothing, not even void.

Code example:

File: MyConstructor.java

 /* учебные пример Конструктор ничего не возвращает, даже void То что с void - обычный метод класса */ package underwaterRocks.myConstructor; /** * * @author Ar20L80 */ 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(); // вызов обычного метода } } 

As we see in the code, two methods with the same name: MyConstructor () and MyConstructor (). One of the methods returns nothing. This is the constructor of our class. Another method with void is the usual class method. In the case when you have not created a constructor or created, in your opinion, a class constructor with void, then the compiler will create a default constructor and you will be surprised why your constructor does not work.

Division by zero


What do you think will be the result of such a code.

File: "DivisionByZero.java"

 /*учебные пример*/ package divisionByZero; import static java.lang.Double.POSITIVE_INFINITY; /** * * @author Ar20L80 */ 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"); } } } 

Running the code will output:

 Infinity Infinity true true 

Dividing the integer type by zero will give an ArithmeticException.

In the java.lang.Double class, the constant POSITIVE_INFINITY;

 public static final float POSITIVE_INFINITY = 1.0d / 0.0d; 

It is converted to a string equal to Infinity.

Initialization order


File: "InitClass.java"

 /* учебные пример инициализация класса */ package myInitClass; /** * * @author Ar20L80 */ 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"); } } 

First, all static blocks are executed, then initialization blocks, then the class constructor.

It will appear: "123 Constructor"

Local variable hides class variable
Although modern IDEs easily detect such an error, I would like to consider such an error in more detail. Let's start with the classic assignment of a variable in the constructor. The example is correct. There is no mistake.
  public class MyClass { private int val = 0; public MyClass(int val) { this.val = val; } } 

However, what happens if you use such a technique in a method, and not in a class constructor? In the usual method to use this technique is not recommended. The question relates to the correct design of the class.

Simple explanation: In a method, a variable with the same name as a class variable is local to the method. You can access the class variable using this.val. However, such a call from the method, if the class is incorrectly designed, will only cause side effects and may worsen the readability of the code.

Type casting in arithmetic expressions is performed automatically.

This can cause annoying mistakes.
 // byte a = 1; // byte b = 1; // byte с = a + b; // ошибка // byte a = (byte) 1; // byte b = (byte) 1; // byte с = a + b; // ошибка 


 // одно из возможных решений - явное преобразование в арифметических выражениях. byte a = 1; byte b = 1; byte c = (byte) (a + b); 


 // одно из возможных решений - использование final // final byte a = 1; // final byte b = 1; // byte c = a + b; // автоматического приведения не будет, поскольку a и b final 


One possible solution when working with a string:
 byte bHundr = Byte.parseByte("100"); // явное приведение строки к типу byte 


Another error is given in the following code.
 for (byte i = 1; i <= 128; i++) { System.out.println(i); } 

In this case, we get an infinite loop.

findings
Many errors are not obvious at first glance. Even experienced programmers make them, but in smaller quantities. Mindfulness, hands-on experience, using the debugger and reading the documentation will allow you to avoid many mistakes.

I hope you liked the article and it was useful. I will be glad to your comments, comments, suggestions, suggestions. To be continued. Rather, the addition should be.

License
"I authorize reading this article, authorizing the use of this article, allowing to improve this article." The article is distributed under the terms of the GNU FDL.

Links
Recommendations for code design by Java from Oracle >>>

Source: https://habr.com/ru/post/439642/