Top 5 Java errors and how to understand them

Written by - 0 comments

Published on - Listed in Coding Java


Everyone who learns programming makes mistakes. Even experienced programmers make mistakes. On the other hand sometimes you might be surprised, when your code works as expected on the first try. 

If you're learning to code, you've probably noticed a lot of error articles on Java blog sites. In this article, we'll look at the top 5 and very common mistakes in Java coding.

#1: Syntax errors

Modern IDE (graphical text editors with code detection) usually detect and therefore help with syntax errors, but nevertheless syntax errors are still the most common mistakes.

Experienced programmers quite often advise beginners not to start with an IDE, but with a regular text editor. In their opinion, this is necessary in order to get used to the syntax of the language and in the future to avoid a large number of syntax errors. In addition, sometimes there is a situation where you need to do something on a remote server without an IDE at hand and you have to use a simple text editor on the command line, such as vim or nano. Sometimes an applicant for a Junior/Trainee position may be asked to write a method on a piece of paper or in a text editor. So leaving the IDE for a while to write a dozen or two simple programs without the help of prompts, as well as learning how
to run them from the command line, is a great idea. There are thousands of syntax errors. Look at the code below and try to find them all without using the IDE.

public class MistakesTest {
  public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
      ;
    }
    char[] mychar = {'1', 'w', 'q'};
    System.out.println('2')
    for (char ch : mychar) {
      if ((int) ch >= 256) {
        throw new IllegalArgumentException();
      }
      if (((int) ch != 32) && (ch < 'q')) &&(((int) (ch))) {
        return false;
      }
    }
  }
}

Not at all obvious. Oh wait, the semicolon on the single line looks suspicious, right?:

    for (int i = 0; i < 10; i++) {
      ;
    }

Nope, it's actually correct. Most often, novice programmers (or switchers, for example, from Python) forget to put a semicolon after the command, and also get confused in brackets. However, modern IDEs will not let you make a mistake with a semicolon. But if you place the brackets incorrectly, sometimes you can get the wrong result due to the priority of operations.

#2: Working with uninitialized objects and getting a NullPointerException

If you forget to initialize an object (variable, array, etc), this leads to a NullPointerException. Note there's a difference between declaration and initialization of a variable. 

If you write something like that...

private static String myString;
  public static void main(String[] args) {
    System.out.println(myString.length());
  }

... you will get a NullPointerException because myString is null (empty).

#3: Unreachable or dead code

The unreachable (or dead) code occurs if a program contains a certain piece of code that can never be executed. This is almost always due to a mistake in the program logic and incorrect use of Boolean conditions. Here we’ve got a simple example. It is artificial and everything is immediately visible here, but in practice such code is often quite deeply hidden. However, modern IDEs often see such things before compiling the code.

public class Demo {

  public static void main(String[] args) {
    if (false) System.out.println("I am unreachable");
    else System.out.println("I will be printed anyway");
  }
}

The code above is working, but completely meaningless due to ill-conceived logic. But if you have an obvious error, for example, you write something after return, the compiler will throw an error Error: unreachable statement.

Another example:

public class Demo {

  public static int getNum(int num) {
    if (num == 1) return num++;
    else if (num > 1) return num--;
    return num;
    System.out.println("my num ==" + num);
  }

  public static void main(String[] args) {
    System.out.println("myNum = " + getNum(5));
  }
}

#4: OOP: Incorrect construction of the object hierarchy

An interface defines behavior and nothing else. It does not define the state of an object. An abstract class is in some sense higher in terms of abstraction levels. It allows you to create functionality that descendant classes can implement or override.

Each class can only inherit from one abstract class, but many interfaces can be implemented. It can be very difficult for a beginner to understand where to use an abstract class and where to use an interface.

If you need to write many methods, use an abstract class, so you can provide default implementations for some of the methods that are common to all subclasses. If you need to add or remove a method from the interface, it could be a problem. The reason is, that you can't declare additional methods in an interface without changing all classes that implement this interface.

#5: Equals and hashcode problems

Java has the Object class. It is the parent class of all other classes. Object has equal() and hashCode() methods. The equals() method is used to simply check if two objects are equal. In turn, hashCode() allows you to get some unique integer for a given object for checks.

Often beginners forget to redefine them or redefine them incorrectly. The default implementation of the equals() method simply checks the two objects by reference to see if they are equivalent.

For example, you need to compare two points on the coordinate plane, let's first check the default equals method:

public class Point {
  private int x;
  private int y;
  public Point(int x, int y) {
    this.x = x;
    this.y = y;
  } 
}

Without overriding the equals() method in the Point class, let's try to compare the points. To do this, we can create a second PointDemo class and in there we define three points. Two according to Cartesian coordinates and the third which differs from them.

public class PointDemo {

  public static void main(String[] args) {
    Point point1 = new Point(1, 5);
    Point point2 = new Point(1, 5);
    Point point3 = new Point(1, 7);

    if (point1.equals(point2))
      System.out.println("equal points");
    else
      System.out.println("different points");

    if (point1.equals(point3))
      System.out.println("equal points");
    else
      System.out.println("different points");
  }
}

Note that in the PointDemo class above, getters and setters should have been used for defining the coordinates. Modern IDEs actually suggest how to do it. 

When running this code, the output is:

different points
different points

But why is the first point comparison returning "different points" when they should be the same? They should be equal, according to mathematical logic, yet they aren't. Let's add an override equals method into the Point class:

public class Point {
  private int x;
  private int y;

  public Point(int x, int y) {
    this.x = x;
    this.y = y;
  }

  @Override
  public boolean equals(Object o) {

    if (o == this) {
      return true;
    }
    if (o == null || o.getClass() != this.getClass()) {
      return false;
    }
    Point myPoint = (Point) o;
    return (x == myPoint.x) && (y == myPoint.y);
  }
}

If we run the PointDemo class again, the output is now different (and mathematically correct):

equal points
different points

Conclusion

Only a few mistakes are described here, but there are many more. These are the common mistakes that CodeGym students have been complaining about lately (except for the syntax errors, these kind of errors will happen throughout a programmer's career).


Add a comment

Show form to leave a comment

Comments (newest first)

No comments yet.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Office   OpenSearch   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder