- vừa được xem lúc

Classpath và biên dịch Java từ command line

0 0 5

Người đăng: Tong Hoang Vu

Theo Viblo Asia

Tìm hiểu về khái niệm Classpath trong Java. Hướng dẫn cách biên dịch code Java thành bytecode và chạy bytecode thông qua command line.

1. Sơ lược & chuẩn bị

1.1. javacjava command

Bạn nào học Java cũng biết, để chạy được chương trình Java cần hai bước biên dịch thành bytecode và thực thi bytecode đó. Với mỗi giai đoạn sẽ dùng command tương ứng là javacjava như sau.

# Biên dịch Main.java thành Main.class
javac Main.java # Chạy bytecode trong Main.class
java Main

Chương trình đơn giản như Hello World sẽ biên dịch bình thường mà không cần cấu hình classpath. Tuy nhiên, nếu chương trình có import qua lại với nhau, thì nên biết thêm về classpath.

1.2. Code ví dụ

Để test được classpath, mình sẽ tạo chương trình demo gồm 3 file như sau.

import toyota.Car; public class Main { public static void main(String[] args) { System.out.println("Bike: " + Bike.WHEEL_COUNT); System.out.println("Car: " + Car.WHEEL_COUNT); }
}

Và file code thứ hai Bike.java nằm cùng thư mục với Main.java.

public class Bike { public static int WHEEL_COUNT = 2;
}

File code thứ ba nằm trong một thư mục con toyota (test package hoạt động ra sao khi custom classpath).

package toyota; public class Car { public static int WHEEL_COUNT = 4;
}

Việc biên dịch và chạy chương trình không có lỗi gì cả. Quá trình biên dịch tạo ra 3 file class tương ứng nằm cùng vị trí với file code.

Biên dịch Main.java cho ra 3 file .class, nằm cùng vị trí với file code tương ứng. Các class liên quan, được sử dụng trong Main.java cũng được biên dịch theo, ngược lại những class không dùng đến thì không.

image.png

Dùng flag -d khi biên dịch sẽ cho ra các file class nằm trong một thư mục riêng (nhưng vẫn giữ cấu trúc như các file code).

javac -d ./target Main.java

Cũng có thể dùng dấu wildcard * để biên dịch toàn bộ file trong thư mục nào đó (các thư mục con thì không).

javac ./toyota/*.java

2. Khái niệm Classpath

2.1. Classpath khi compile

Chương trình ở phần trên chạy không có lỗi, do mặc định classpath được đặt là thư mục hiện tại. Vô tình classpath này lại khớp với các package và chương trình chạy được.

Tuy nhiên, hãy thay đổi một chút bằng cách di chuyển ra ngoài thư mục gốc (thư mục chứa file Main.java) và compile lại. Đã có lỗi xuất hiện như hình sau.

image.png

Java Compiler không thể tìm thấy các class liên quan (BikeCar) khi biên dịch Main class. Đây là do sự không phù hợp giữa khai báo package và classpath.

Khi biên dịch Java Compiler sẽ tìm kiếm các class khác liên quan. Class cần tìm sẽ bao gồm package (dựa theo import) và tên class, ví dụ toyota.Car.

Compiler dựa vào classpath, package và tên class tìm được file .java tương ứng. Ví dụ classpath đang ở . (mặc định), class là toyota.Car thì file Java sẽ nằm tại ./toyota/Car.java.

Compiler sẽ phân tích file Java và tìm class cần tìm, nếu có thì biên dịch thành file .class mới, ngược lại báo lỗi. Lỗi này có thể do class khai báo sai package nên tên class không khớp. Ví dụ cần tìm toyota.Car mà trong file Java lại khai báo là yamaha.Car.

Quay lại trên, do classpath chưa đúng nên Java Compiler không thể tìm được các file .java liên quan để biên dịch. Vì vậy, cần chỉ định classpath khi biên dịch bằng flag -cp hoặc -classpath như sau.

javac -cp ./test-classpath ./test-classpath/Main.java

Ví dụ tìm class Bike, lúc trước classpath là ., nối với package của class Bike là rỗng (default package), nên file Bike.java sẽ nằm ở ./Bike.java. Nhưng đường dẫn không khớp nên bị lỗi.

Nhưng khi chỉnh lại classpath như trên thành ./test-classpath, nên file Bike.java nằm tại ./test-classpath/Bike.java. Java Compiler tìm được và biên dịch bình thường.

Tóm lại, classpath chỉ là một đường dẫn để javacjava tìm được đâu là gốc của package, dựa vào đó để tìm các file .java khác thôi.

2.2. Classpath ở runtime

Đúng ra thì khi biên dịch với javac phải dùng -sourcepath mới đúng. Cơ bản thì sourcepath với classpath chỉ khác nhau là một cái dùng khi compile, một cái dùng khi chạy bytecode. Mà với classpath thì dùng được ở cả 2 luôn, nên mình prefer hơn.

Như phần trên, command java được dùng để thực thi bytecode đã biên dịch. Lúc này cũng cần chú ý đến classpath nếu không muốn bị ClassNotFoundException.

image.png

Ở đây mình biên dịch vào thư mục ./target, nên classpath sẽ là thư mục này.

# Biên dịch trước
javac -d ./target -cp ./test-classpath ./test-classpath/Main.java # Chạy class Main
java -cp ./target Main

Phần này khác một tí, chúng ta sẽ chỉ định tên class cần chạy gồm package và class name như sau. Tất nhiên class phải có public static void main thì mới chạy được.

image.png

3. Các khía cạnh khác

3.1. Chỉ định nhiều classpath

Sẽ có trường hợp bạn cần chỉ định nhiều classpath cùng lúc, ví dụ như các file Java có cùng package nhưng khác thư mục. Trong trường hợp đó, flag -cp cần chỉ định nhiều thư mục như sau:

  • Trên Windows dùng dấu ; để phân tách
  • Trên Linux dùng dấu : để phân tách

Tiến hành sửa lại code như sau để kiểm tra.

// Bỏ dòng khai báo package
// Car sẽ cùng default package với Main (nhưng khác thư mục)
// package toyota; public class Car { public static int WHEEL_COUNT = 4;
}
// Do cùng default package nên không cần import
// import toyota.Car; public class Main { public static void main(String[] args) { System.out.println("Bike: " + Bike.WHEEL_COUNT); System.out.println("Car: " + Car.WHEEL_COUNT); }
}

Biên dịch và chạy chương trình đã sửa. Chương trình vẫn hoạt động bình thường.

# Biên dịch
javac -cp "./test-classpath:./test-classpath/toyota" ./test-classpath/Main.java # Chạy bytecode
java -cp "./test-classpath:./test-classpath/toyota" Main

Trường hợp này Java Compiler sẽ tìm class trong tất cả classpath. Và file Car.java dù có khai báo package không cũng cho kết quả tương tự, vì compiler tìm ở hai classpath khác nhau (nhưng khi biên dịch ra thư mục riêng thì cấu trúc file .class sẽ khác).

Ngoài ra, nhiều classpath cũng hỗ trợ trong việc chạy bytecode với các file JAR ở phần tiếp theo.

3.2. Classpath với file JAR

Hầu như các thư viện Java hiện tại được đóng gói dưới dạng file JAR (Java Archive). Nhưng nếu muốn biên dịch và chạy chương trình gồm file JAR qua command line thì làm thế nào?

Ở đây mình dùng thư viện Apache Common Lang để demo. Chỉ cần download về, giải nén ra và copy file JAR vào thư mục nào đó là được.

https://commons.apache.org/proper/commons-lang/download_lang.cgi

File JAR trong trường hợp này có thể xem như một thư mục chứa các file .class (thử dùng WinRAR mở ra thử). Như trong hình, việc sử dụng file JAR giống như chỉ định thêm một classpath.

image.png

Cần chú ý ở đây là khi biên dịch, Java Compiler nếu tìm thấy file .class rồi thì không cần biên dịch file .java nữa. Thêm nữa, khi chỉ định classpath là JAR thì có thể dùng wildcard để load tất cả file .jar trong thư mục nào đó.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tổng hợp các bài hướng dẫn về Design Pattern - 23 mẫu cơ bản của GoF

Link bài viết gốc: https://gpcoder.com/4164-gioi-thieu-design-patterns/. Design Patterns là gì. Design Patterns không phải là ngôn ngữ cụ thể nào cả.

0 0 277

- vừa được xem lúc

Học Spring Boot bắt đầu từ đâu?

1. Giới thiệu Spring Boot. 1.1.

0 0 257

- vừa được xem lúc

Cần chuẩn bị gì để bắt đầu học Java

Cần chuẩn bị những gì để bắt đầu lập trình Java. 1.1. Cài JDK hay JRE.

0 0 37

- vừa được xem lúc

Sử dụng ModelMapper trong Spring Boot

Bài hôm nay sẽ là cách sử dụng thư viện ModelMapper để mapping qua lại giữa các object trong Spring nhé. Trang chủ của ModelMapper đây http://modelmapper.org/, đọc rất dễ hiểu dành cho các bạn muốn tìm hiểu sâu hơn. 1.

0 0 180

- vừa được xem lúc

[Java] 1 vài tip nhỏ khi sử dụng String hoặc Collection part 1

. Hello các bạn, hôm nay mình sẽ chia sẻ về mẹo check String null hay full space một cách tiện lợi. Mình sẽ sử dụng thư viện Lớp StringUtils download file jar để import vào thư viện tại (link).

0 0 55

- vừa được xem lúc

Deep Learning với Java - Tại sao không?

Muốn tìm hiểu về Machine Learning / Deep Learning nhưng với background là Java thì sẽ như thế nào và bắt đầu từ đâu? Để tìm được câu trả lời, hãy đọc bài viết này - có thể kỹ năng Java vốn có sẽ giúp bạn có những chuyến phiêu lưu thú vị. DJL là tên viết tắt của Deep Java Library - một thư viện mã ng

0 0 124