Mistakes of beginner java programmers. Part 1
Author: A.Grasoff™ Link to source: Mistakes of beginner java programmers
9. Calling non-static class methods from the main() method
The entry point of any Java program should be a static methodmain
:
public static void main(String[] args) {
...
}
Since this method is static, you cannot call non-static class methods from it. Students often forget about this and try to call methods without creating an instance of the class. This mistake is usually made at the very beginning of training, when students write small programs. Wrong example:
public class DivTest {
boolean divisible(int x, int y) {
return (x % y == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
// на следующие строки компилятор выдаст ошибку
if (divisible(v1, v2)) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
There are 2 ways to fix the error: make the desired method static or create an instance of the class. To choose the right method, ask yourself whether the method uses a field or other class methods. If yes, then you should create an instance of the class and call a method on it, otherwise you should make the method static. Corrected example 1:
public class DivTest {
int modulus;
public DivTest(int m) {
modulus = m;
}
boolean divisible(int x) {
return (x % modulus == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
DivTest tester = new DivTest(v2);
if (tester.divisible(v1) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
Corrected example 2:
public class DivTest {
static boolean divisible(int x, int y) {
return (x % y == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
if (divisible(v1, v2)) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
10. Using String class objects as method parameters.
In Java, a classjava.lang.String
stores string data. However, strings in Java
- have permanence (that is, they cannot be changed),
- are objects.
public static void main(String args[]) {
String test1 = "Today is ";
appendTodaysDate(test1);
System.out.println(test1);
}
/* прим. редактора: закомментированный метод должен иметь модификатор
static (здесь автором допущена ошибка №9)
public void appendTodaysDate(String line) {
line = line + (new Date()).toString();
}
*/
public static void appendTodaysDate(String line) {
line = line + (new Date()).toString();
}
In the example above, the student wants to change the value of a local variable test1
by assigning a new value to a parameter line
in a method appendTodaysDate
. Naturally this won't work. The meaning line
will change, but the meaning test1
will remain the same. This error occurs due to a misunderstanding that (1) java objects are always passed by reference and (2) strings in java are immutable. You need to understand that string objects never change their value, and all operations on strings create a new object. To fix the error in the example above, you need to either return a string from the method, or pass an object StringBuffer
as a parameter to the method instead of String
. Corrected example 1:
public static void main(String args[]) {
String test1 = "Today is ";
test1 = appendTodaysDate(test1);
System.out.println(test1);
}
public static String appendTodaysDate(String line) {
return (line + (new Date()).toString());
}
Corrected example 2:
public static void main(String args[]) {
StringBuffer test1 = new StringBuffer("Today is ");
appendTodaysDate(test1);
System.out.println(test1.toString());
}
public static void appendTodaysDate(StringBuffer line) {
line.append((new Date()).toString());
}
approx. translation |
11. Declaring a constructor as a method
Object constructors in Java are similar in appearance to regular methods. The only differences are that the constructor does not specify a return type and the name is the same as the class name. Unfortunately, Java allows the method name to be the same as the class name. In the example below, the student wants to initialize a class fieldVector list
when creating the class. This won't happen because a method 'IntList'
is not a constructor. Wrong example.
public class IntList {
Vector list;
// Выглядит How конструктор, но на самом деле это метод
public void IntList() {
list = new Vector();
}
public append(int n) {
list.addElement(new Integer(n));
}
}
The code will throw an exception NullPointerException
the first time the field is accessed list
. The error is easy to fix: you just need to remove the return value from the method header. Corrected example:
public class IntList {
Vector list;
// Это конструктор
public IntList() {
list = new Vector();
}
public append(int n) {
list.addElement(new Integer(n));
}
}
12. Forgot to cast an object to the required type
Like all other object-oriented languages, in Java you can refer to an object as its superclass. This is called'upcasting'
, it is done automatically in Java. However, if a variable, class field, or method return value is declared as a superclass, the fields and methods of the subclass will be invisible. Referring to a superclass as a subclass is called 'downcasting'
, you need to register it yourself (that is, bring the object to the desired subclass). Students often forget about subclassing an object. This most often happens when using arrays of Objects and collections from a package java.util
(meaning the Collection Framework ). The example below String
puts an object into an array and then removes it from the array to compare it to another string. The compiler will detect an error and will not compile the code until a type cast is explicitly specified. Wrong example.
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');
String arg = args[0];
if (arr[0].compareTo(arg) < 0) {
System.out.println(arg + " comes before " + arr[0]);
}
The meaning of type casting is difficult for some. Dynamic methods especially often cause difficulties. In the example above, if the method had been used equals
instead of compareTo
, the compiler would not have thrown an error, and the code would have worked correctly, since the method equals
of the class would have been called String
. You need to understand that dynamic linking is different from downcasting
. Corrected example:
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');
String arg = args[0];
if ( ((String) arr[0]).compareTo(arg) < 0) {
System.out.println(arg + " comes before " + arr[0]);
}
13. Using interfaces.
For many students, the difference between classes and interfaces is not entirely clear. Therefore, some students try to implement interfaces such asObserver
or Runnable
using the extends keyword instead of implements . To correct the error, you just need to correct the keyword to the correct one. Wrong example:
public class SharkSim extends Runnable {
float length;
...
}
Corrected example:
public class SharkSim implements Runnable {
float length;
...
}
Related error: Incorrect order of extends and implements blocks . According to the Java specification, class extension declarations must come before interface implementation declarations. Also, for interfaces, the implements keyword needs to be written only once; multiple interfaces are separated by commas. Some more erroneous examples:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
float length;
...
}
// ключевое слово implements встречается несколько раз
public class DiverSim implements Swimmer implements Runnable {
int airLeft;
...
}
Corrected examples:
// Правильный порядок
public class SharkSim extends Animal implements Swimmer {
float length;
...
}
// Несколько интерфейсов разделяются запятыми
public class DiverSim implements Swimmer, Runnable {
int airLeft;
...
}
14. Forgot to use the return value of a superclass method
Java allows you to call a similar superclass method from a subclass using the keyword keyword. Sometimes students have to call superclass methods, but often forget to use the return value. This happens especially often among those students who have not yet understood methods and their return values. In the example below, a student wants to insert the result of atoString()
superclass method into the result of a toString()
subclass method. However, it does not use the return value of the superclass method. Wrong example:
public class GraphicalRectangle extends Rectangle {
Color fillColor;
boolean beveled;
...
public String toString() {
super();
return("color=" + fillColor + ", beveled=" + beveled);
}
}
To correct the error, it is usually sufficient to assign the return value to a local variable, and then use that variable when calculating the result of the subclass method. Corrected example:
public class GraphicalRectangle extends Rectangle {
Color fillColor;
boolean beveled;
...
public String toString() {
String rectStr = super();
return(rectStr + " - " +
"color=" + fillColor + ", beveled=" + beveled);
}
}
15. Forgot to add AWT components
AWT uses a simple GUI design model: each interface component must first be created using its own constructor, and then placed into the application window using aadd()
parent component method. Thus, the interface on AWT receives a hierarchical structure. Students sometimes forget these 2 steps. They create a component but forget to place it in the enlargement window. This will not cause errors at the compilation stage; the component simply will not appear in the application window. Wrong example.
public class TestFrame extends Frame implements ActionListener {
public Button exit;
public TestFrame() {
super("Test Frame");
exit = new Button("Quit");
}
}
To fix this error, you simply need to add the components to their parents. The example below shows how to do this. It should be noted that often a student who forgets to add a component to an application window also forgets to assign event listeners to that component. Corrected example:
public class TestFrame extends Frame implements ActionListener {
public Button exit;
public TestFrame() {
super("Test Frame");
exit = new Button("Quit");
Panel controlPanel = new Panel();
controlPanel.add(exit);
add("Center", controlPanel);
exit.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
17. Forgot to start the stream
Multithreading in Java is implemented using thejava.lang.Thread
. The life cycle of a thread consists of 4 stages: initialized, started, blocked and stopped. The newly created thread is in an initialized state. To put it into a running state, you need to call its start()
. Sometimes students create threads but forget to start them. Usually the error occurs when the student has insufficient knowledge about parallel programming and multithreading. (approx. transl.: I don’t see the connection) To fix the error, you just need to start the thread. In the example below, a student wants to create an animation of a picture using the interface Runnable
, but he forgot to start the thread. Wrong example
public class AnimCanvas extends Canvas implements Runnable {
protected Thread myThread;
public AnimCanvas() {
myThread = new Thread(this);
}
// метод run() не будет вызван,
// потому что поток не запущен.
public void run() {
for(int n = 0; n < 10000; n++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
animateStep(n);
}
}
...
}
Corrected example:
public class AnimCanvas extends Canvas implements Runnable {
static final int LIMIT = 10000;
protected Thread myThread;
public AnimCanvas() {
myThread = new Thread(this);
myThread.start();
}
public void run() {
for(int n = 0; n < LIMIT; n++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
animateStep(n);
}
}
...
}
The life cycle of a thread and the relationship between threads and classes that implement an interface Runnable
is a very important part of Java programming, and it would be useful to focus on this.
18. Using the forbidden readLine() method of the java.io.DataInputStream class
readLine()
In Java version 1.0, you had to use a class method to read a string of text java.io.DataInputStream
. Java version 1.1 added a whole set of I/O classes to provide I/O operations for text: the Reader
and Writer
. Thus, from version 1.1 to read a line of text, you must use the readLine()
class method java.io.BufferedReader
. Students may not be aware of this change, especially if they were taught from older books. (approx. Translation: actually no longer relevant. It’s unlikely that anyone will now study from books that were 10 years old). The old method readLine()
remains in the JDK, but is declared illegal, which often confuses students. What you need to understand is that using a readLine()
class method java.io.DataInputStream
is not wrong, it's just outdated. You must use the class BufferedReader
. Wrong example:
public class LineReader {
private DataInputStream dis;
public LineReader(InputStream is) {
dis = new DataInputStream(is);
}
public String getLine() {
String ret = null;
try {
ret = dis.readLine(); // Неправильно! Запрещено.
} catch (IOException ie) { }
return ret;
}
}
Corrected example:
public class LineReader {
private BufferedReader br;
public LineReader(InputStream is) {
br = new BufferedReader(new InputStreamReader(is));
}
public String getLine() {
String ret = null;
try {
ret = br.readLine();
} catch (IOException ie) { }
return ret;
}
}
There are other prohibited methods in versions later than 1.0, but this one is the most common.
19. Using double as float
Like most other languages, Java supports operations on floating point numbers (fractional numbers). Java has two primitive types for floating-point numbers:double
IEEE 64-bit precision, and float
IEEE 32-bit precision. The difficulty is when using decimal numbers such as 1.75, 12.9e17 or -0.00003 - the compiler assigns them to type double
. Java does not perform type casts in operations where loss of precision may occur. This type casting must be done by the programmer. For example, Java will not allow you to assign a type value to a type int
variable byte
without a type cast, as shown in the example below.
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Since fractional numbers are represented by type double
, and assignment double
to a variable of type float
can lead to loss of precision, the compiler will complain about any attempt to use fractional numbers as float
. So using the assignments below will prevent the class from compiling.
float realValue1 = -1.7; /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
This assignment would work in C or C++, but in Java it's much stricter. There are 3 ways to get rid of this error. You can use type double
instead of float
. This is the simplest solution. In fact, there is little point in using 32-bit arithmetic instead of 64-bit; the difference in speed is still eaten up by the JVM (besides, in modern processors all fractional numbers are converted to the format of an 80-bit processor register before any operation). The only advantage of using them float
is that they take up less memory, which is useful when working with a large number of fractional variables. You can use a number type modifier to tell the compiler how to store the number. Modifier for type float - 'f'
. Thus, the compiler will assign the type 1.75 to double
, and 1.75f - float
. For example:
float realValue1 = 1.7; /* неправильно! */
float realValue2 = 1.9f; /* правильно */
You can use explicit type casting. This is the least elegant way, but it is useful when converting a type variable double
to a type float
. Example:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
You can read more about floating point numbers here and here.
-- translator's comment -- |
GO TO FULL VERSION