JavaRush /Blog Java /Random-PL /Błędy początkujących programistów Java. Część 1
articles
Poziom 15

Błędy początkujących programistów Java. Część 1

Opublikowano w grupie Random-PL

1. Nazwa klasy różni się od nazwy pliku, w którym jest przechowywana

Wszystkie frameworki Java, których używałem, w tym Javasoft JDK, zakładają, że kod źródłowy klasy z modyfikatorem public jest przechowywany w pliku o dokładnie takiej samej nazwie jak nazwa klasy i rozszerzeniu .java. Nieprzestrzeganie tej konwencji może spowodować wiele problemów, które ujawnią się podczas kompilacji.
Błędy początkujących programistów Java.  Część 1 - 1
Początkujący studenci (programiści) często zapominają o tej konwencji i np. ustawiają nazwę pliku zgodnie z zadaniem: Lab6.java. Zły przykład: nazwa plikuLab6.java
public class Airplane extends Vehicle
  Seat pilot;
  public Airplane() {
    pilot = new Seat();
  }
}
Poprawiony przykład: Nazwa plikuAirplane.java
public class Airplane extends Vehicle
  Seat pilot;
  public Airplane() {
    pilot = new Seat();
  }
}
Proszę zanotować:zakłada się, że nazwa klasy zaczyna się od dużej litery. Systemy operacyjne, w których rozróżniana jest wielkość liter w nazwach plików, mogą stwarzać dodatkowe problemy, zwłaszcza dla studentów uczących się języka Java w systemie Unix, którzy są przyzwyczajeni do systemu nazewnictwa plików DOS. Klasa MotorVehiclepowinna być przechowywana w pliku MotorVehicle.java, ale nie w pliku motorvehicle.java.

2. Porównanie za pomocą==

W Javie ciągi znaków są obiektami klasy java.lang.String. Operator ==zastosowany do obiektów sprawdza równość odniesień do obiektów! Czasami uczniowie nie rozumieją semantyki operatora ==i próbują go używać do porównywania ciągów znaków. Zły przykład:
// проверим, równa się ли первый аргумент "-a"
if (args[0] == "-a") {
    optionsAll = true;
}
Prawidłowym sposobem porównania 2 ciągów znaków pod kątem równości jest użycie metody equals()klasowej java.lang.String. Zwraca true, jeśli ciągi znaków mają tę samą długość i zawierają te same znaki. (Uwaga: w rzeczywistości nie gwarantuje to równości. W rzeczywistości equalssprawdza, czy 2 ciągi znaków są równe znak po znaku) Poprawiony przykład:
//  проверим, równa się ли первый аргумент "-a"
if ("-a".equals(args[0])) {
    optionsAll = true;
}
Ten błąd jest głupi, bo faktycznie kod Java okazuje się składniowo poprawny, ale ostatecznie nie działa tak, jak oczekiwano. Niektórzy uczniowie próbują także używać operatorów porównania >i zamiast <=metod compareTo()klasowych java.lang.String. Ten błąd jest łatwiejszy do wykrycia, ponieważ powoduje błędy w fazie kompilacji.

3. Zapomniałem zainicjować obiekty będące elementami tablicy.

W Javie tablica obiektów jest w rzeczywistości tablicą odniesień do obiektów. Tworzenie tablicy polega po prostu na utworzeniu zestawu odniesień, które na nic nie wskazują (to znaczy mają wartość null). Aby faktycznie utworzyć „pełną” tablicę obiektów, należy zainicjować każdy element tablicy. Wielu uczniów tego nie rozumie; wierzą, że tworząc tablicę obiektów, automatycznie tworzą same obiekty. (W większości przypadków uczniowie przenoszą tę koncepcję z języka C++, gdzie utworzenie tablicy obiektów powoduje utworzenie samych obiektów poprzez wywołanie ich domyślnego konstruktora). W poniższym przykładzie uczeń chce utworzyć 3 obiekty klasy StringBuffer. Kod skompiluje się bez błędów, ale w ostatniej linii wystąpi wyjątek NullPointerException, gdy uzyskany zostanie dostęp do nieistniejącego obiektu. Zły przykład:
// Создаем массив из StringBuffer
StringBuffer [] myTempBuffers;
myTempBuffers = new StringBuffer[3];
myTempBuffers[0].add(data);
Aby uniknąć tego błędu, należy pamiętać o inicjalizacji elementów tablicy. Poprawiony przykład:
// Создаем массив из StringBuffer и инициализируем элементы
StringBuffer [] myTempBuffers;
myTempBuffers = new StringBuffer[3];
for (int ix = 0; ix < myTempBuffers.length; ix++)
     myTempBuffers[ix] = new StringBuffer();

myTempBuffers[0].add(data);

4. Umieszczenie kilku klas z modyfikatorem w jednym pliku na razpublic

Pliki źródłowe Java są w pewien sposób powiązane z klasami zawartymi w tych plikach. Zależność można scharakteryzować w następujący sposób: Dowolna klasa Java jest przechowywana w nie więcej niż jednym pliku. W dowolnym pliku kodu źródłowego można umieścić nie więcej niż 1 klasę z modyfikatorem public. Jeśli w pliku kodu źródłowego znajduje się klasa z modyfikatorem public, nazwa pliku i nazwa klasy muszą być dokładnie takie same (uwaga do tłumaczenia: w zależności od przypadku, patrz punkt 1) Czasami uczniowie zapominają o drugiej regule, co prowadzi do błędów na etapie kompilacji. Komunikat o błędzie dla drugiej i trzeciej reguły będzie taki sam (co właściwie utrudnia rozpoznanie tego błędu).

5. Zastąpienie pola klasy zmienną lokalną.

Java umożliwia deklarowanie zmiennych wewnątrz metody, której nazwa odpowiada polom klasy. W tym przypadku zmienne lokalne będą miały pierwszeństwo i zostaną użyte zamiast pól. Kompilator zgłosi błąd, jeśli zmienne o tych samych nazwach są różnych typów. Jeśli są tego samego typu, nie będzie błędu kompilacji, a przyczyny nieprawidłowego działania programu będą niejasne. Zły przykład:
public class Point3 {
    int i = 0;
    int j = 0;
    int k = 0;

    public boolean hits(Point[] p2list) {
      for(int i = 0; i < p2list.length; i++) {
        Point p2 = p2list[i];
        if (p2.x == i && p2.y == j)
          return true;
      }
      return false;
    }
}
Istnieje kilka sposobów naprawienia tego błędu. Najprostszym sposobem jest uzyskanie dostępu do pól klas za pomocą ukrytego wskaźnika this: this.Nazwa_поля. Najlepszym sposobem jest zmiana nazwy pola klasy lub zmiennej lokalnej, wówczas podstawienie nie nastąpi. (w przybliżeniu: Druga metoda nie jest naszą metodą. Co więcej, nie gwarantuje, że pewnego dnia przypadkowo nie zastąpię pola zmiennej. Jeszcze większa trudność pojawia się przy dziedziczeniu, gdy w ogóle nie widzę, jakie pola klasa ma ) Poprawiony przykład:
// One way to fix the problem
  int i = 0;
  int j = 0;
  int k = 0;

  public boolean hits(Point[] p2list) {
    for(int i = 0; i < p2list.length; i++) {
      Point p2 = p2list[i];
      if (p2.x == this.i && p2.y == this.j)
        return true;
    }
    return false;
  }

  // *****************************
  // Лучший способ
  int x = 0;
  int y = 0;
  int z = 0;

  public boolean hits(Point[] p2list) {
    for(int i = 0; i < p2list.length; i++) {
      Point p2 = p2list[i];
      if (p2.x == x && p2.y == y)
        return true;
    }
    return false;
  }
Innym możliwym miejscem wystąpienia tego błędu jest ustawienie nazwy parametru metody na taką samą, jak nazwa pola klasy. Wygląda to dobrze w konstruktorach, ale nie nadaje się do normalnych metod.

około. tłumaczenie

trochę chaotycznie, ale o to właśnie chodzi

public class Test {
   private int param = 0;

   public Test(int param) {
      this.param = param;
   }
}

oznacza to, że w konstruktorze wszystko wygląda pięknie, ale nie należy tego używać w przypadku zwykłych metod.

6. Zapomniałem wywołać konstruktora nadrzędnego (nadklasy).

Kiedy klasa rozszerza inną klasę, każdy konstruktor podklasy musi wywołać jakiś konstruktor nadklasy. Zwykle osiąga się to poprzez wywołanie konstruktora nadklasy za pomocą metody super(x)umieszczonej w pierwszej linii konstruktora. Jeśli w pierwszej linii konstruktora nie ma wywołania super(x), kompilator sam wstawia to wywołanie, ale bez parametrów: super(). (w przybliżeniu tłum.: x...se, ale nie wiedziałem) Czasami studenci zapominają o tym wymogu. Zwykle nie stanowi to problemu: kompilator wstawia wywołanie konstruktora nadklasy i wszystko działa poprawnie. Jeśli jednak nadklasa nie ma domyślnego konstruktora, kompilator zgłosi błąd. W poniższym przykładzie wszystkie konstruktory nadklas java.io.Filemają 1 lub 2 parametry: Błędny przykład:
public class JavaClassFile extends File {
    String classname;
    public JavaClassFile(String cl) {
        classname = cl;
    }
}
Rozwiązaniem problemu jest wstawienie jawnego wywołania poprawnego konstruktora nadklasy: Poprawiony przykład:
public class JavaClassFile extends File {
    String classname;
    public JavaClassFile(String cl) {
        super(cl + ".class");
        classname = cl;
    }
}
Bardziej nieprzyjemna sytuacja ma miejsce, gdy nadklasa ma domyślny konstruktor, ale nie inicjuje w pełni obiektu. W takim przypadku kod zostanie skompilowany, ale dane wyjściowe programu mogą być nieprawidłowe lub może wystąpić wyjątek.

7. Błędne wychwytywanie wyjątków

System obsługi wyjątków w Javie jest dość potężny, ale trudny do zrozumienia dla początkujących. Studenci biegli w C++ lub Adzie zwykle nie mają takich samych trudności jak programiści C i Fortran. Poniższe przykłady pokazują kilka typowych błędów. W tym przykładzie wyjątek nie ma nazwy. Kompilator wskaże ten błąd na etapie kompilacji, więc łatwo go naprawić samodzielnie. Zły przykład:
try {
    stream1 = new FileInputStream("data.txt");
} catch (IOException) {
    message("Could not open data.txt");
}
Poprawiony przykład:
try {
   stream1 = new FileInputStream("data.txt");
} catch (IOException ie) {
   message("Could not open data.txt: " + ie);
}
Kolejność bloków catchokreśla kolejność przechwytywania wyjątków. Należy wziąć pod uwagę, że każdy taki blok wyłapie wszystkie wyjątki określonej klasy lub którejkolwiek z jej podklas. Jeśli nie weźmiesz tego pod uwagę, możesz otrzymać nieosiągalny blok catch, który wskaże kompilator. W poniższym przykładzie SocketExceptionznajduje się podklasa IOException. Zły przykład:
try {
    serviceSocket.setSoTimeout(1000);
    newsock = serviceSocket.accept();
} catch (IOException ie) {
    message("Error accepting connection.");
} catch (SocketException se) {
    message("Error setting time-out.");
}
Poprawiony przykład:
try {
    serviceSocket.setSoTimeout(1000);
    newsock = serviceSocket.accept();
} catch (SocketException se) {
    message("Error setting time-out.");
} catch (IOException ie) {
    message("Error accepting connection.");
}
Jeśli możliwe jest wystąpienie w kodzie wyjątku, który nie zostanie przechwycony przez żaden blok try-catch, wówczas wyjątek ten należy zadeklarować w nagłówku metody. RuntimeException( Nie jest to konieczne w przypadku wyjątków – podklas klasy ). Uczniowie czasami zapominają, że wywołanie metody może zgłosić wyjątek. Najprostszym sposobem rozwiązania tego problemu jest umieszczenie wywołania metody w bloku try-catch. Zły przykład:
public void waitFor(int sec) {
    Thread.sleep(sec * 1000);
}
Poprawiony przykład:
public void waitFor(int sec) throws InterruptedException {
    Thread.sleep(sec * 1000);
}

8. Metoda dostępu ma typvoid

To bardzo prosty błąd. Student tworzy metodę dostępu do zmiennej, ale określa, że ​​metoda niczego nie zwraca (umieszcza modyfikator voidw nagłówku metody). Aby naprawić ten błąd, musisz określić poprawny typ zwrotu. Zły przykład:
public class Line {
    private Point start, end;
    public void getStart() {
      return start;
    }
}
Poprawiony przykład:
public class Line {
    private Point start, end;
    public Point getStart() {
      return start;
    }
}
Określenie nieprawidłowego typu zwracanego generuje całą klasę błędów. Zazwyczaj kompilator rozpoznaje te błędy i zgłasza je, aby uczniowie mogli je samodzielnie poprawić. Autor: A. Grasoff™ Przeczytaj kontynuację Link do źródła: Błędy początkujących programistów Java
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION