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:
To 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.
Error checklist:
1. Typos. Annoying typos that is not immediately detected
2. Assignment in a condition instead of comparison
3. Logical errors in condition
4. Incorrect comparison of strings
5. Incorrect initialization of variables of primitive types
6. Incorrect use of double
7. Incorrect type of returning values in the designer.
8. The division by zero. POSITIVE_INFINITY
9. Disregarding the initialization order of class
10. A local variable hides a variable of class
11. Ignoring automatic type casting in arithmetic expressions
12. An infinite loop with byte that is difficult to detect.
13. The class name is different from the name of the file in which it is stored.
14. Objects that are elements of an array are not initialized.
15. Placing several classes with a public modifier in one file at once
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,
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?
It happens that novice programmers make typos that are difficult to spot at a glance.
Example code:
File: “Simple.java”
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. ”
In a condition assignment to a variable.
This is not a mistake, but the use of such a technique should be justified.
In this code, if (myBool = true) means: “Assign the variable myBool to true,
if the expression is true, fulfill the condition following the parentheses.”
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, like this (0 = a) or (5 = a), then 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 with the same success you can 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.
An example in the code (interval] 3.9 [):
if (3 <a && a <9) to execute;
if (condition) {} if (condition) {} else {} - else refers to the nearest if.
Often this is the cause of beginners' mistakes.
Beginners quite often use == instead of .equals to compare strings.
Consider initializing variables of primitive type.
Primitives (byte, short, int, long, char, float, double, boolean).
Initial values
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,
do not forget to assign a value to it before attempting 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"
Value ranges:
Oracle Documentation >>
We will try to initialize a variable of type long with the number: 922372036854775807.
We have nothing to do. Because it is an integer literal of type int.
Proper initialization with a long literal: 922372036854775807L;
Example code:
File: "MyInitLocalLong.java"
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.
Here you need to clarify. This is not about using double type incorrectly.
We use correctly. Only the result can surprise a novice programmer.
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.
Consider the type double.
Sample code:
File: "MyDouble.java"
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.
The class constructor matches the class name and returns nothing, not even void.
Example code:
File: "MyConstructor.java"
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.
What do you think will be the result of such a code.
File: "DivisionByZero.java"
Running the code will output:
Dividing the integer type by zero will give an ArithmeticException.
In the class java.lang.Double defined constant
It is converted to a string equal to Infinity.
File: "InitClass.java"
First, all static blocks are executed, then initialization blocks, then the class constructor.
It will be displayed: “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.
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 automatic.
This can cause annoying errors.
One possible solution when working with a string:
Another error is given in the following code.
In this case, we get an infinite loop.
Explanation. Type byte [-128, 127]. 128 is no longer in this range. Overflow occurs and the cycle repeats. The need to use byte in this case is questionable. Although there is a place to be in rare cases. The recommendation is to use int instead of byte. Another recommendation is not to use a loop in your algorithm.
Objects that are elements of an array are not initialized.
In this example, we have an array of elements of a primitive type. And nothing terrible happens when we haven't initialized them. They will be assigned a default value. In this case, the value = 0.
Consider another example no longer with primitives in the array, but with objects in the array.
The solution to this problem is to initialize all object variables before using them. Initialization can be done in the constructor of the class MyObj.
The class name is different from the name of the file in which it is stored.
Modern IDE easily identifies this kind of error. However, such errors are encountered, although quite rarely. It will help attentiveness, taking into account differences in the names of uppercase and lowercase letters.
Placing several classes with a modifier in one file at once public The
error is quite rare. The IDE will alert you immediately.
The file name must match the name of the public class.
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.
Links
Recommendations on code design for Java from Oracle >>>
I hope this publication will be useful not only for beginners.
The purpose of this publication:
To 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.
Error checklist:
1. Typos. Annoying typos that is not immediately detected
2. Assignment in a condition instead of comparison
3. Logical errors in condition
4. Incorrect comparison of strings
5. Incorrect initialization of variables of primitive types
6. Incorrect use of double
7. Incorrect type of returning values in the designer.
8. The division by zero. POSITIVE_INFINITY
9. Disregarding the initialization order of class
10. A local variable hides a variable of class
11. Ignoring automatic type casting in arithmetic expressions
12. An infinite loop with byte that is difficult to detect.
13. The class name is different from the name of the file in which it is stored.
14. Objects that are elements of an array are not initialized.
15. Placing several classes with a public modifier in one file at once
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,
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 happens that novice programmers make typos that are difficult to spot at a glance.
Example code:
File: “Simple.java”
/*
учебные пример
; после условия и блок
*/package underwaterRocks.simple;
/**
*
* @author Ar20L80
*/publicclassSimple{
publicstaticvoidmain(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 parentheses.”
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, like this (0 = a) or (5 = a), then 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 with the same success you can 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.
An example in the code (interval] 3.9 [):
if (3 <a && a <9) to execute;
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,
do not forget to assign a value to it before attempting 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
*/publicclassMyInitLocal{
float classes_f;
int classes_gi;
publicstaticvoidmain(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 >>
We will try to initialize a variable of type long with the number: 922372036854775807.
We have nothing to do. Because it is an integer literal of type int.
Proper initialization with a long literal: 922372036854775807L;
Example code:
File: "MyInitLocalLong.java"
/*
учебные пример
Инициализация long локально
*/package underwaterRocks.myInit;
/**
*
* @author Ar20L80
*/publicclassMyInitLocalLong{
publicstaticvoidmain(String[] args){
// long al = 922372036854775807; //ошибка integer number too largelong 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
*/publicclassMinusDouble{
publicstaticvoidmain(String[] args){
double a = 4.64;
double b = 2.64;
System.out.println("a-b = "+(a-b));
}
}
/*
Вывод программы
run:
a-b = 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.
Sample code:
File: "MyDouble.java"
/*
учебные пример
Сравнение double
Осторожно - double.
*/package underwaterRocks.myDouble;
/**
*
* @author Ar20L80
*/publicclassMyDouble{
publicstaticvoidmain(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*/finaldouble 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.
Example code:
File: "MyConstructor.java"
/*
учебные пример
Конструктор ничего не возвращает, даже void
То что с void - обычный метод класса
*/package underwaterRocks.myConstructor;
/**
*
* @author Ar20L80
*/publicclassMyConstructor{
publicMyConstructor(){
System.out.println("Я конструктор без void");
}
publicvoidMyConstructor(){
System.out.println("Я конструктор c void");
}
publicstaticvoidmain(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;
importstatic java.lang.Double.POSITIVE_INFINITY;
/**
*
* @author Ar20L80
*/publicclassDivisionByZero{
publicstaticvoidmain(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 class java.lang.Double defined 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
*/publicclassInitClass{
InitClass(){ // конструктор класса
System.out.print("Конструктор");
}
{ // блок инициализации
System.out.print("3 ");
}
publicstaticvoidmain(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 be displayed: “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.
publicclassMyClass{
privateint val = 0;
publicMyClass(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 automatic.
This can cause annoying errors.
// 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.
Explanation. Type byte [-128, 127]. 128 is no longer in this range. Overflow occurs and the cycle repeats. The need to use byte in this case is questionable. Although there is a place to be in rare cases. The recommendation is to use int instead of byte. Another recommendation is not to use a loop in your algorithm.
Objects that are elements of an array are not initialized.
int[] cats = newint[10];
for(int i=0; i<cats.length;i++){
System.out.println("cats " + i + " = " + cats[i]);
}
In this example, we have an array of elements of a primitive type. And nothing terrible happens when we haven't initialized them. They will be assigned a default value. In this case, the value = 0.
Consider another example no longer with primitives in the array, but with objects in the array.
publicclassArrInitObj{
publicstaticvoidmain(String[] args){
MyObj[] cats = new MyObj[10];
for(int i=0; i<cats.length;i++){
System.out.println("cats " + i + " = " + cats[i]);
System.out.println("cats " + i + ".val = " + cats[i].val);
// мы поймали исключение java.lang.NullPointerException
}
}
}
classMyObj{
publicint val;
}
The solution to this problem is to initialize all object variables before using them. Initialization can be done in the constructor of the class MyObj.
The class name is different from the name of the file in which it is stored.
Modern IDE easily identifies this kind of error. However, such errors are encountered, although quite rarely. It will help attentiveness, taking into account differences in the names of uppercase and lowercase letters.
Placing several classes with a modifier in one file at once public The
error is quite rare. The IDE will alert you immediately.
The file name must match the name of the public class.
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.
Links
Recommendations on code design for Java from Oracle >>>