JavaRush /Blog Java /Random-VI /Sai lầm của người mới lập trình java. Phần 1
articles
Mức độ

Sai lầm của người mới lập trình java. Phần 1

Xuất bản trong nhóm

1. Tên của lớp khác với tên của tệp chứa nó

Tất cả các khung công tác java mà tôi đã sử dụng, bao gồm cả JDK của Javasoft, đều giả định rằng mã nguồn của một lớp có công cụ sửa đổi công khai được lưu trữ trong một tệp có cùng tên với tên lớp và phần mở rộng .java. Việc không tuân theo quy ước này có thể gây ra nhiều vấn đề sẽ xuất hiện trong quá trình biên dịch.
Sai lầm của người mới lập trình java.  Phần 1 - 1
Những sinh viên mới bắt đầu (lập trình viên) thường quên quy ước này và chẳng hạn đặt tên tệp theo bài tập: Lab6.java. Ví dụ sai: Tên tệpLab6.java
public class Airplane extends Vehicle
  Seat pilot;
  public Airplane() {
    pilot = new Seat();
  }
}
Ví dụ đã sửa: Tên tệpAirplane.java
public class Airplane extends Vehicle
  Seat pilot;
  public Airplane() {
    pilot = new Seat();
  }
}
Xin lưu ý:tên lớp được giả sử bắt đầu bằng chữ in hoa. Các hệ điều hành phân biệt chữ hoa chữ thường trong tên tệp có thể gây ra các vấn đề khác, đặc biệt đối với những sinh viên học Java trên Unix đã quen với hệ thống đặt tên tệp DOS. Lớp này MotorVehiclephải được lưu trữ trong một tệp MotorVehicle.javachứ không phải trong tệp motorvehicle.java.

2. So sánh bằng cách sử dụng==

Trong Java, chuỗi là đối tượng của lớp java.lang.String. Toán tử ==áp dụng cho các đối tượng kiểm tra sự bằng nhau của các tham chiếu đến các đối tượng! Đôi khi học sinh không hiểu ngữ nghĩa của toán tử ==và cố gắng sử dụng nó để so sánh các chuỗi. Ví dụ sai:
// проверим, equals ли первый аргумент "-a"
if (args[0] == "-a") {
    optionsAll = true;
}
Cách chính xác để so sánh 2 chuỗi về sự bằng nhau là sử dụng phương thức equals()lớp java.lang.String. Nó trả về truenếu các chuỗi có cùng độ dài và chứa các ký tự giống nhau. (Lưu ý: thực ra điều này không đảm bảo sự bình đẳng. Trên thực tế, equalsnó kiểm tra xem 2 chuỗi có ký tự bằng nhau hay không) Ví dụ đã sửa:
//  проверим, equals ли первый аргумент "-a"
if ("-a".equals(args[0])) {
    optionsAll = true;
}
Lỗi này thật ngu ngốc, vì trên thực tế, mã Java hóa ra đúng về mặt cú pháp, nhưng cuối cùng nó lại không hoạt động như mong đợi. Một số học sinh cũng cố gắng sử dụng các toán tử so sánh >thay vì <=các phương thức compareTo()lớp java.lang.String. Lỗi này dễ phát hiện hơn vì nó gây ra lỗi ở giai đoạn biên dịch.

3. Quên khởi tạo các đối tượng là phần tử của mảng.

Trong Java, một mảng đối tượng thực chất là một mảng tham chiếu đối tượng. Tạo một mảng chỉ đơn giản là tạo một tập hợp các tham chiếu không trỏ đến bất cứ thứ gì (nghĩa là chúng rỗng). Để thực sự tạo một mảng đối tượng "đầy đủ", bạn cần khởi tạo từng phần tử của mảng. Nhiều học sinh không hiểu điều này; họ tin rằng bằng cách tạo ra một mảng các đối tượng, họ sẽ tự động tạo ra các đối tượng đó. (Trong hầu hết các trường hợp, học sinh sử dụng khái niệm này từ C++, trong đó việc tạo một mảng các đối tượng sẽ dẫn đến việc tự tạo các đối tượng đó bằng cách gọi hàm tạo mặc định của chúng.) Trong ví dụ dưới đây, học sinh muốn tạo 3 đối tượng của lớp StringBuffer. Mã sẽ biên dịch không có lỗi, nhưng một ngoại lệ sẽ xảy ra ở dòng cuối cùng NullPointerException, nơi một đối tượng không tồn tại được truy cập. Ví dụ sai:
// Создаем массив из StringBuffer
StringBuffer [] myTempBuffers;
myTempBuffers = new StringBuffer[3];
myTempBuffers[0].add(data);
Để tránh lỗi này, bạn phải nhớ khởi tạo các phần tử mảng. Ví dụ đã sửa:
// Создаем массив из StringBuffer и инициализируем элементы
StringBuffer [] myTempBuffers;
myTempBuffers = new StringBuffer[3];
for (int ix = 0; ix < myTempBuffers.length; ix++)
     myTempBuffers[ix] = new StringBuffer();

myTempBuffers[0].add(data);

4. Đặt nhiều lớp có sửa đổi vào một tệp cùng một lúcpublic

Các tệp nguồn Java được liên kết theo những cách nhất định với các lớp có trong các tệp đó. Mối quan hệ có thể được mô tả như sau: Bất kỳ lớp Java nào cũng được lưu trữ trong không quá một tệp. Trong bất kỳ tệp mã nguồn nào, bạn có thể đặt không quá 1 lớp với công cụ sửa đổi public. Nếu có một lớp có phần sửa đổi trong tệp mã nguồn publicthì tên tệp và tên lớp phải hoàn toàn giống nhau (ghi chú dịch: tùy trường hợp, xem điểm 1) Đôi khi học sinh quên quy tắc thứ 2, dẫn đến sai sót ở giai đoạn biên soạn. Thông báo lỗi cho quy tắc thứ 2 và thứ 3 sẽ giống nhau (đó thực sự là nguyên nhân gây khó khăn cho việc nhận ra lỗi này).

5. Thay thế trường lớp bằng biến cục bộ.

Java cho phép bạn khai báo các biến bên trong một phương thức có tên khớp với các trường của lớp. Trong trường hợp này, các biến cục bộ sẽ được ưu tiên và được sử dụng thay cho các trường. Trình biên dịch sẽ báo lỗi nếu các biến có cùng tên nhưng thuộc các loại khác nhau. Nếu chúng cùng loại thì sẽ không có lỗi biên dịch và nguyên nhân khiến chương trình hoạt động không chính xác sẽ không rõ ràng. Ví dụ sai:
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;
    }
}
Có một số cách để khắc phục lỗi này. Đơn giản nhất là truy cập các trường của lớp bằng cách sử dụng con trỏ this: ẩn this.Name_поля. Cách tốt nhất là đổi tên trường lớp hoặc biến cục bộ, khi đó sự thay thế sẽ không xảy ra. (xấp xỉ. Dịch: Phương thức thứ 2 không phải là phương pháp của chúng tôi. Hơn nữa, nó không đảm bảo rằng một ngày nào đó tôi sẽ không vô tình thay thế một trường của một biến. Một khó khăn lớn hơn nữa nảy sinh với tính kế thừa, khi tôi hoàn toàn không nhìn thấy trường nào lớp có ) Ví dụ đã sửa:
// 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;
  }
Một nơi khác có thể xảy ra lỗi này là do đặt tên tham số phương thức giống với tên trường lớp. Điều này có vẻ tốt trong các hàm tạo, nhưng không phù hợp với các phương thức thông thường.

khoảng dịch

hơi hỗn loạn một chút, nhưng đó là ý chính

public class Test {
   private int param = 0;

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

nghĩa là, mọi thứ đều trông đẹp mắt trong hàm tạo, nhưng điều này không nên được sử dụng cho các phương thức thông thường.

6. Quên gọi hàm tạo cha (siêu lớp)

Khi một lớp mở rộng một lớp khác, mỗi hàm tạo của lớp con phải gọi một số hàm tạo của lớp cha. Điều này thường đạt được bằng cách gọi hàm tạo của siêu lớp bằng phương thức super(x)được đặt ở dòng đầu tiên của hàm tạo. Nếu không có lệnh gọi nào ở dòng đầu tiên của hàm tạo super(x)thì trình biên dịch sẽ tự chèn lệnh gọi này nhưng không có tham số: super(). (xấp xỉ dịch: x...se, nhưng tôi không biết) Đôi khi học sinh quên yêu cầu này. Thông thường đây không phải là vấn đề: lệnh gọi hàm tạo siêu lớp được trình biên dịch chèn vào và mọi thứ đều hoạt động tốt. Tuy nhiên, nếu siêu lớp không có hàm tạo mặc định, trình biên dịch sẽ báo lỗi. Trong ví dụ dưới đây, tất cả các hàm tạo của lớp cha java.io.Fileđều có 1 hoặc 2 tham số: Ví dụ sai:
public class JavaClassFile extends File {
    String classname;
    public JavaClassFile(String cl) {
        classname = cl;
    }
}
Giải pháp cho vấn đề này là chèn một lệnh gọi rõ ràng tới hàm tạo chính xác của siêu lớp: Ví dụ đã sửa:
public class JavaClassFile extends File {
    String classname;
    public JavaClassFile(String cl) {
        super(cl + ".class");
        classname = cl;
    }
}
Một tình huống khó chịu hơn xảy ra khi siêu lớp có một hàm tạo mặc định nhưng nó không khởi tạo đầy đủ đối tượng. Trong trường hợp này, mã sẽ biên dịch nhưng đầu ra của chương trình có thể không chính xác hoặc có thể xảy ra ngoại lệ.

7. Bắt lỗi ngoại lệ

Hệ thống xử lý ngoại lệ của Java khá mạnh nhưng khó hiểu đối với người mới bắt đầu. Những sinh viên thành thạo C++ hoặc Ada thường không gặp khó khăn như lập trình viên C và Fortran. Các ví dụ dưới đây cho thấy một số lỗi phổ biến. Trong ví dụ này, ngoại lệ không được đặt tên. Trình biên dịch sẽ chỉ ra lỗi này ở giai đoạn biên dịch nên bạn có thể dễ dàng tự khắc phục. Ví dụ sai:
try {
    stream1 = new FileInputStream("data.txt");
} catch (IOException) {
    message("Could not open data.txt");
}
Ví dụ đã sửa:
try {
   stream1 = new FileInputStream("data.txt");
} catch (IOException ie) {
   message("Could not open data.txt: " + ie);
}
Thứ tự của các khối catchxác định thứ tự bắt được các ngoại lệ. Cần phải lưu ý rằng mỗi khối như vậy sẽ bắt được tất cả các ngoại lệ của lớp được chỉ định hoặc bất kỳ lớp con nào của nó. Nếu bạn không tính đến điều này, bạn có thể sẽ gặp phải một khối bắt không thể truy cập được mà trình biên dịch sẽ chỉ ra. Trong ví dụ dưới đây SocketExceptionlà một lớp con của IOException. Ví dụ sai:
try {
    serviceSocket.setSoTimeout(1000);
    newsock = serviceSocket.accept();
} catch (IOException ie) {
    message("Error accepting connection.");
} catch (SocketException se) {
    message("Error setting time-out.");
}
Ví dụ đã sửa:
try {
    serviceSocket.setSoTimeout(1000);
    newsock = serviceSocket.accept();
} catch (SocketException se) {
    message("Error setting time-out.");
} catch (IOException ie) {
    message("Error accepting connection.");
}
Nếu có thể xảy ra một ngoại lệ trong mã của bạn mà không bị bất kỳ khối nào chặn try-catchthì ngoại lệ này phải được khai báo trong tiêu đề phương thức. RuntimeException( Điều này không cần thiết đối với các trường hợp ngoại lệ - các lớp con của một lớp ). Học sinh đôi khi quên rằng việc gọi một phương thức có thể tạo ra một ngoại lệ. Cách dễ nhất để khắc phục điều này là đặt lệnh gọi phương thức vào một khối try-catch. Ví dụ sai:
public void waitFor(int sec) {
    Thread.sleep(sec * 1000);
}
Ví dụ đã sửa:
public void waitFor(int sec) throws InterruptedException {
    Thread.sleep(sec * 1000);
}

8. Phương thức truy cập có một kiểuvoid

Đây là một sai lầm rất đơn giản. Học sinh tạo một phương thức để truy cập một biến, nhưng chỉ rõ rằng phương thức đó không trả về bất cứ thứ gì (đặt một công cụ sửa đổi voidtrong tiêu đề phương thức). Để khắc phục lỗi này, bạn phải chỉ định kiểu trả về chính xác. Ví dụ sai:
public class Line {
    private Point start, end;
    public void getStart() {
      return start;
    }
}
Ví dụ đã sửa:
public class Line {
    private Point start, end;
    public Point getStart() {
      return start;
    }
}
Việc chỉ định kiểu trả về sai sẽ tạo ra cả một loại lỗi. Thông thường, trình biên dịch sẽ nhận ra những lỗi này và báo cáo để học sinh có thể tự sửa. Tác giả: A. Grasoff™ Đọc phần tiếp theo Link nguồn: Những sai lầm của người mới bắt đầu lập trình java
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION