JavaRush /Blog Java /Random-VI /Nghỉ giải lao #165. Các gói trong Java. Phương pháp an to...

Nghỉ giải lao #165. Các gói trong Java. Phương pháp an toàn cho chủ đề cho người mới bắt đầu

Xuất bản trong nhóm

gói Java

Nguồn: Usemynotes Bài đăng này sẽ giúp bạn hiểu rõ hơn về các gói trong Java, hiểu mục đích của chúng và cách triển khai chúng. Nghỉ giải lao #165.  Các gói trong Java.  Phương pháp thread-safe cho người mới bắt đầu - 1

Các gói trong Java là gì

Gói trong Java là một cách để nhóm một nhóm các lớp, giao diện và gói con lại với nhau. Các gói được sử dụng để tạo các nhóm gồm các lớp, giao diện, bảng liệt kê, v.v. có liên quan. Gói con là các gói nằm trong một gói khác. Chúng không được nhập theo mặc định nhưng bạn có thể nhập chúng theo cách thủ công nếu cần. Đặc tả truy cập không được cung cấp cho từng thành viên riêng lẻ của gói con; chúng được coi là các gói riêng biệt.

Một số loại gói trong Java:

  • java.lang - đi kèm với Java theo mặc định.
  • java.io - chứa các lớp, phương thức và các thành phần khác liên quan đến đầu vào/đầu ra.

Tại sao cần có các gói?

  • Để tránh xung đột tên.
  • Để cung cấp quyền truy cập được kiểm soát.
  • Để đạt được sự đóng gói dữ liệu.

Làm cách nào để tạo một gói trong Java?

Hãy tạo một gói có tên là máy tính . Thông thường tên gói được viết bằng chữ thường. Điều này được thực hiện chỉ để tránh xung đột tên với tên lớp. Chúng ta cũng sẽ tạo một giao diện có tên Pluggable , giao diện này sẽ nằm trong gói máy tính .
package computer;

interface Pluggable {
   public void pluggedIn();
   public void pluggedOut();
}
Bây giờ chúng ta sẽ tạo một lớp tên là PenDrive để triển khai giao diện trên.
package computer;

public class PenDrive implements Pluggable {

   int storage = 64;

   public void pluggedIn () {
     System.out.println("Pen Drive is connected");
   }

   public void pluggedOut () {
     System.out.println("Pen Drive is removed");
   }

   public int storage() {
     return storage;
   }

   public static void main(String args[]) {
     PenDrive p = new PenDrive ();
     p.pluggedIn();
     System.out.println("Pen Drive Storage: " + p.storage());
     p.pluggedOut();
   }
}

Làm cách nào để tạo hệ thống phân cấp gói trong Java?

Khi hình thành hệ thống phân cấp, các gói trong Java được đặt tên theo thứ tự ngược lại. Điều này làm cho chúng rất giống với các thư mục hoặc thư mục. Giống như trên máy tính cá nhân, trong đó một thư mục có thể chứa một hoặc nhiều thư mục con, điều tương tự cũng áp dụng cho các gói trong Java. Hãy xem xét một gói có tên Asia.India.Kolkata . Đây đều là những thư mục hiện có, nhưng nếu xét về mặt địa lý thì rõ ràng Calcutta nằm ở Ấn Độ và Ấn Độ ở Châu Á. Mục đích chính của hệ thống phân cấp là làm cho các lớp dễ tìm hơn.

Các loại gói trong Java

Gói tích hợp

Các gói dựng sẵn là các gói bao gồm một số lượng lớn các lớp dựng sẵn là một phần của API Java. Các gói này bao gồm:
  • java.util - Gói này chứa một số lượng hữu hạn các lớp tiện ích được sử dụng để triển khai các cấu trúc dữ liệu như danh sách liên kết, bộ, v.v. Nó cũng hỗ trợ các hoạt động ngày và giờ và nhiều hơn nữa.
  • java.net - Nó chứa các lớp được sử dụng cho hoạt động mạng.

Gói do người dùng xác định

Các gói do người dùng xác định được gọi là gói người dùng. Người dùng có thể tạo một gói theo cách thủ công và có bao nhiêu lớp trong đó tùy thích.

Làm cách nào để truy cập gói từ gói khác?

Bạn có thể truy cập gói từ gói khác theo ba cách đơn giản:
  • Sử dụng dấu hoa thị trong câu lệnh nhập
Ký tự dấu hoa thị ( * ) được sử dụng để thể hiện “tất cả mọi thứ” trong Java. Bằng cách sử dụng nó, chúng ta có thể nhập mọi thứ bên trong gói con của gói. Ví dụ: Hãy xem xét một gói có tên tools . Nếu chúng ta muốn nhập mọi thứ bên trong gói này thì chúng ta cần sử dụng câu lệnh nhập như:
import tools.*;
  • Sử dụng gói nhập.ClassName;
Đề cập đến tên lớp trong một gói là một cách hiệu quả để chỉ nhập những lớp bạn cần vào chương trình của mình, nhờ đó tránh được việc nhập những lớp không cần thiết. Ví dụ: Xét một gói có tên books . Nếu chúng ta chỉ muốn nhập một lớp hoặc giao diện cụ thể từ nó (chúng ta sẽ xem xét lớp Pages ), thì chúng ta chỉ có thể nhập lớp hoặc giao diện đó bằng cách sử dụng:
import book.Pages;
  • Sử dụng tên đầy đủ của bạn
Có một cách để sử dụng trực tiếp gói Java hoặc các lớp của nó bằng cách sử dụng tên đầy đủ của chúng. Bằng cách này, bạn không cần phải nhập gói và có thể sử dụng trực tiếp trong chương trình. Ví dụ:
java.awt.List aSimpleList = new java.awt.List();

Kích thước lô mặc định trong Java

Theo mặc định, Java nhập gói java.lang . Nó có nhiều lớp thường được sử dụng trong các chương trình Java đơn giản như String , Integer và các lớp khác. Một trong những lớp quan trọng nhất là lớp Object , lớp này cũng được tìm thấy trong gói java.lang . Kích thước của gói này dựa trên các thành phần của nó: 8 giao diện, 37 lớp, 3 enum, 27 ngoại lệ, 23 loại lỗi và 5 loại chú thích.

Các phương pháp Java an toàn theo luồng cho người mới bắt đầu

Nguồn: Medium Khi sử dụng bài viết này, bạn có thể tìm hiểu về cách hoạt động của các phương thức an toàn luồng trong Java. Nghỉ giải lao #165.  Các gói trong Java.  Phương pháp thread-safe cho người mới bắt đầu - 2Tôi nhận thấy rằng nhiều nhà phát triển Java cấp cơ sở/trung cấp hiểu sai về cách hoạt động của các phương thức an toàn luồng trong các dự án thực tế. Và vì chúng ta thường làm việc trong môi trường đa luồng nên việc sử dụng đúng chúng là rất quan trọng. Phương thức an toàn luồng là phương thức có thể được gọi đồng thời từ nhiều luồng mà không ảnh hưởng đến trạng thái dữ liệu của nhau. Hiểu biết chưa đầy đủ về khái niệm này dẫn đến các lỗi khó tìm và sửa. Để tránh những sai lầm như vậy, chúng ta hãy xem các ví dụ.

Ví dụ 1:

public static int countLetters(String input) {
    int counter = 0;

    for (Character c : input.toCharArray()) {
        if (Character.isAlphabetic(c)) {
            counter++;
        }
    }

    return counter;
}
  • Phương thức countLetters là tĩnh, nó trả về giá trị int và chấp nhận tham số chuỗi làm đầu vào.
  • Một bộ đếm biến nguyên thủy được tạo bên trong phương thức, sau đó vòng lặp lặp qua các ký tự của chuỗi đầu vào và tăng bộ đếm biến mỗi khi gặp một ký tự chữ cái.
Chủ đề phương pháp này có an toàn không? Nhiều nhà phát triển nói không, vì trong trường hợp này chúng tôi có một thao tác tăng dần không an toàn cho luồng. Hãy tìm ra nó. Trong mô hình bộ nhớ Java, chúng ta có ngăn xếp và đống. Mỗi luồng có ngăn xếp riêng và tất cả các luồng đều có chung một vùng nhớ. Về vấn đề này, dữ liệu ngăn xếp luôn an toàn cho luồng, nhưng dữ liệu heap thì không. Ngăn xếp lưu trữ các tham chiếu nguyên thủy và đối tượng. Heap chứa chính các đối tượng đó. Điều này có nghĩa là trong ví dụ mã này, mỗi luồng lưu trữ bộ đếm biến riêng của nó trên ngăn xếp và không có bất kỳ ảnh hưởng nào đến dữ liệu của các luồng khác, vì vậy phương thức này là thread safe . Lưu ý rằng giá trị Chuỗi đầu vào cũng là một đối tượng, nhưng Chuỗi và các trình bao bọc nguyên thủy ( Integer , Long , Double , Boolean , v.v.) là luồng an toàn vì chúng không thể thay đổi.

Ví dụ #2:

public static int countLetters2(String input) {
    List<Character> listCounter = new ArrayList<>();

    for (Character c : input.toCharArray()) {
        if (Character.isAlphabetic(c)) {
            listCounter.add(c);
        }
    }

    return listCounter.size();
}
Mã này sử dụng logic tương tự như ví dụ đầu tiên, nhưng sử dụng đối tượng List thay vì biến int nguyên thủy . Ở phần trước chúng ta đã biết rằng các đối tượng trong Java được lưu trữ dưới dạng heap và List là một đối tượng. Chúng ta cũng biết rằng ngăn xếp lưu trữ các tham chiếu đến các đối tượng trên vùng nhớ heap. Trong ví dụ #2, mỗi luồng tạo một đối tượng ArrayList mới : và biến listCounter lưu trữ một tham chiếu đến đối tượng của nó trên heap, vì vậy không luồng nào khác có thể thay đổi đối tượng đó.
List<Character> listCounter = new ArrayList<>();
Điều này có nghĩa là phương pháp này là luồng an toàn.

Ví dụ #3:

public class CounterUtil { // singleton

    List<Character> listCounter = new ArrayList<>();

    public int countLetters3(String input) {
        for (Character c : input.toCharArray()) {
            if (Character.isAlphabetic(c)) {
                listCounter.add(c);
            }
        }

        return listCounter.size();
    }
}
Trong ví dụ này, chúng ta có một lớp đơn (điều này rất quan trọng) CounterUtil với biến toàn cục listCounter . Biến này được tạo cùng lúc với cá thể singleton. Khi nhiều luồng gọi phương thức countChars3 , tất cả chúng đều sử dụng cùng một biến toàn cục listCounter , lưu trữ một tham chiếu đến cùng một đối tượng trên heap và các luồng ở đó sẽ ảnh hưởng lẫn nhau. Vì vậy chúng ta có thể kết luận rằng phương pháp này không an toàn cho luồng. Và ngay cả khi chúng ta thay đổi List<Character> listCounter thành biến nguyên thủy int counter , thì nó cũng sẽ không an toàn cho luồng vì tất cả các luồng sẽ sử dụng cùng một biến nguyên thủy.

Ví dụ cuối cùng:

public static int countLetters4(List<Character> inputList) {
    List<Character> listCounter = new ArrayList<>();

    for (Character c : inputList) {
        if (Character.isAlphabetic(c)) {
            listCounter.add(c);
        }
    }

    return listCounter.size();
}
Phương thức countLetters4 chấp nhận danh sách các ký tự thay vì tham số String . Ở đây chúng tôi không thể đảm bảo rằng phương pháp này là an toàn cho luồng. Tại sao? Bởi vì chúng tôi không thể chắc chắn các nhà phát triển sẽ sử dụng phương pháp này như thế nào. Nếu một luồng khác từ bên ngoài thay đổi dữ liệu trong inputList cùng lúc với phương thức counterLetters4 của chúng tôi , nó có thể ảnh hưởng đến kết quả cuối cùng.

Phần kết luận

Chúng ta mới chỉ xem xét bốn ví dụ và chúng không đề cập đến tất cả các khía cạnh về an toàn luồng trong các dự án Java, nhưng chúng là một nơi tốt để bắt đầu. Lần tới khi bạn nhìn thấy một phương thức trong mã của mình, hãy tự hỏi bản thân: "Liệu phương thức này có an toàn không?" Và rất sớm bạn sẽ hiểu rõ câu trả lời.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION