Top những câu hỏi phỏng vấn Java SE - 01

  • Phuong Dang
  • 31/Oct/2022
Java Basics
  1. So sánh JDK, JVM, JRE
  2. So sánh static, instance và local variable
  3. So sánh References và Objects
  4. So sánh Stack Memory và Heap Memory
  5. Một object trở thành mục tiêu dọn dẹp của Garbage Collector khi nào?
  6. So sánh pass by value và pass by reference
  7. So sánh == operator và equals method?
  8. So sánh data type Primitive và References?
  9. Autoboxing và Unboxing trong Java là gì?
  10. Tại sao khi define constructor thì default constructor lại mất đi?

Trong bài viết này mình sẽ tổng hợp những câu hỏi phỏng vấn Java SE - Section Java Basics cho đối tượng Fresher

Java SE 8

1. So sánh JDK, JVM, JRE

JDK JRE JVM
Java Development Kit Java Runtime Environment Java Virtual Machine
JDK là môi trường phát triển ứng dụng Java, bao gồm các tool hỗ trợ development, testing và môi trường thực thi ứng dụng Java.

Có thể nói rằng JDK = JRE + compiler, debugger and development tool
JRE là môi trường thực thi ứng dụng Java, bất kỳ machine nào muốn thực thi ứng dụng Java đều cần cài đặt JRE

Có thể nói rằng JRE = JVM + libraries
JVM còn gọi là máy ảo Java là các thành phần nền tảng giúp Java quản lý tài nguyên hệ thống, thực thi chương trình

2. So sánh static, instance và local variable

Static variable Instance variable Local variable
Là biến được khai báo bên ngoài method cùng với từ khóa static Là biến được khai báo bên ngoài method không dùng từ khóa static Là biến được khai báo bên trong method không dùng từ khóa static
Phạm vi sử dụng toàn bộ class được khai báo và bên ngoài class (phụ thuộc vào access modifier) Phạm vi sử dụng block scope, chỉ được dùng trong block {} được khai báo
Giá trị của biến phụ thuộc vào class mà biến được khai báo. Thông thường truy cập thông qua ClassName.staticVariableName Giá trị của biến phụ thuộc vào instance (object). Thông thường truy cập thông qua reference.instanceVariable -

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class VariableDemo {
    static int number1 = 10; // a static variable
    int number2 = 20; // an instance variable

    public static void main(String[] args) {
        // Access static variable by class name
        System.out.println(VariableDemo.number1);

        // Access instance variable by reference
        VariableDemo variableDeclare = new VariableDemo();
        System.out.println(variableDeclare.number2);

        {
            int number3 = 30; // a local variable;
            // Access local variable in block scope
            System.out.println(number3);
        }
        // Access local variable out of Scope
        System.out.println(number3); // COMPILE ERROR
    }
}

3. So sánh References và Objects

CompareReferenceAndObject
Reference vs Object
Reference Object

- Reference là một variable có tên và dùng với mục đích access content của một object

- Một reference có thể assigned đến một reference khác, truyền vào method như tham số hoặc được return từ method

- Tất cả reference đều có size bằng nhau bất kề kiểu dữ liệu là gì. Size 32 bits cho một 32 bits JVM và 64 bits cho một 64 bits JVM

- Object được lưu trong bộ nhớ Heap và không có tên

- Không có cách nào access content của một Object ngoại trừ thông qua reference

- Mỗi Object sẽ có size khác nhau tùy thuộc vào lượng data nó lưu trữ

- Object không thể assigned vào một object khác cũng như không thể truyền vào method như tham số hoặc được return từ method

4. So sánh Stack memory và Heap memory

# Stack memory Heap memory
Lưu trữ
  • Thứ tự và thông tin các methods sẽ được thực thi.
  • Giá trị primitive.
  • Reference trỏ đến object trong Heap.
Object
Cơ chế free memory
  • Mỗi memory block sẽ được tạo trong Stack khi mỗi method được gọi. Tất cả các primitive variable và reference sẽ được khởi tạo va lưu trong memory block của Stack
  • Sau khi method kết thúc thực thi, phần memory block đã chiếm sẽ được giải phóng
Các Object không còn được sử dụng để xóa khỏi bộ nhớ tự động bởi Garbage Collector.
Tốc độ truy cập Nhanh hơn Heap Chậm hơn Stack
Space size Limit size phụ thuộc vào OS Không limit, có thể config. Thông thường sẽ lơn hơn Stack
Khi bộ nhớ không đủ để allocate Throw ra lỗi java.lang.StackOverFlowError Throw ra lỗi java.lang.OutOfMemoryError

5. Một object trở thành mục tiêu dọn dẹp của Garbage Collector khi nào?

Garbage collection là quá trình tự động dọn dẹp bộ nhớ Heap của Java, nó sẽ tìm đến những object không còn được sử dụng để xóa khỏi bộ nhớ Heap. Một object sẽ trở thành mục tiêu dọn dẹp của GC khi 1 trong 2 điều sau xảy ra:

  • Object không có 1 reference nào trỏ đến
  • Tất cả reference trỏ đến object đã out of scope

6. So sánh pass by value và pass by reference

Pass by value Pass by reference
Tham số truyền vào method sẽ được copy ra một bản sao và tất cả các thao tác dữ liệu bên trong method sẽ cập nhật vào bản sao đó. Vì thế mà mọi sự thay đổi bên trong method sẽ không ảnh hưởng đến giá trị của biến ban đầu bên ngoài method được gọi. Tham số truyền vào method và giá trị biến bên ngoài method cùng là reference trỏ đến một object. Do đó tất cả thao tác liên quan đến object được thực hiện bên trong method được gọi sẽ ảnh hưởng đến giá trị của biến bên ngoài method
Trong java luôn luôn là pass by value dù tham số có data type là primitive hay reference

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static void main(String[] args) {
    int number = 10;
    // changeNumber() được gọi bên dưới đã thay đổi giá bị biến number = 11 bên trong nó
    // Tuy nhiên sau khi print number vẫn ra giá trị là 10.
    // Do cơ chế pass by value không thay đối giá trị biến number đã được khởi tạo ở main()
    changeNumber(number);
    System.out.println(number); // Method
}

public static void changeNumber(int number) {
    number = 20;
}

Line số 4 khai báo student.mark = 9, sau đó thay đổi student.mark bên trong method changeMark tại line 10. Nếu khẳng định Java là pass by value thì print student.mark tại line 6 đáng ra giá trị vẫn là 9 vì không đổi, tuy nhiên lại là 8. Điều này gây confuse và cảm tưởng rằng không đúng với lý thuyết pass by value

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class PassByValueReferenceVariable {
    public static void main(String[] args) {
        Student student = new Student();
        student.mark = 9;
        changeMark(student);
        System.out.println(student.mark); // 8
    }

    public static void changeMark(Student student) {
        student.mark = 8;
    }
}
class Student {
    int mark;
}

Tuy nhiên với ví dụ bên dưới trong method changeMark(), ta không thay đổi mark của đối tượng student thông qua reference, mà ta assigned reference đó đến một object mới. Thì chúng ta có thể thấy giá trị của student được khai báo ở line 3 không hề thay đổi, vẫn print 9 ở line 6.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class PassByValueReferenceVariable {
    public static void main(String[] args) {
        Student student = new Student();
        student.mark = 9;
        changeMark(student);
        System.out.println(student.mark); // 9
    }

    public static void changeMark(Student student) {
        Student newStudent = new Student();
        newStudent.mark = 8;
        student = newStudent;
    }
}
class Student {
    int mark;
}

Chúng ta có thể lý giải như sau.

  • Ví dụ số 1 khi truyền biến student kiểu reference vào method. Theo cơ chế pass by value thì Java sẽ tạo ra 1 bản copy của biến reference đó, lúc này ta có 2 biến references cùng trỏ vào 1 object. Từ đó có hiện tượng chỉnh sửa object trong method dẫn đến vị trí bên ngoài gọi method đó cũng bị ảnh hường
  • Ví dụ số 2 khi truyền biến student kiểu reference vào method. Theo cơ chế pass by value thì Java sẽ tạo ra 1 bản copy của biến reference đó, lúc này ta assigned "bản copy của biến referece" đó đến 1 object mới => object bên ngoài không bị ảnh hưởng. Lúc này ta có 2 biến references trỏ đến 2 objects riêng biệt.

7. So sánh == operator và equals method?

# == operator equals method
So sánh biến primitive Sẽ luôn return true nếu chúng có cùng value (ngoại trừ float/double ở 1 số trường hợp đặc biệt) Không dùng để so sánh các biến primitive.
So sánh biến reference So sánh memmory address của object mà 2 biến đó trỏ tới. Kết quả sẽ là true nếu cả 2 biến reference đều trỏ vào 1 object, ngược lại thì return false
  1. Nếu equals() method chưa được override thì sẽ gọi đến equals() của class Object
    => So sánh memory address tương tự == operator.
  2. Nếu equals() method được override thì sẽ thực hiện logic theo mục tiêu đã implementation, thông thường sẽ so sánh value của Object (Tương tự như String).

8. So sánh data type Primitive và References?

Primitive References
Java có 8 kiểu dữ liệu primitive: boolean, byte, short int, long, float, double, char Biến có kiểu dữ liệu reference type sẽ refers đến một object
Lưu trữ giá trị của biến tùy thuộc vào kiểu dữ liệu Lưu trữ memory address của object mà nó refer đến
Không lưu trữ được giá trị null Lưu trữ được giá trị null
Không call được method Có thể call được method

* Nếu reference đang bị null và thực hiện call method thì sẽ bị NullPointerException
Tất cả data type primitive đều bắt đầu bằng chữ cái viết thường Tất cả data type reference đều bắt đầu bằng chữ cái viết hoa (Nếu tuân thủ naming convention)

9. Autoboxing và Unboxing trong Java là gì?

Mỗi kiểu dữ liệu primitive đều được Java cung cấp cho Wrapper class tương ứng với nó.
Primitive type Wrapper class Example of constructing
boolean Boolean new Boolean(true)
byte Byte new Byte((byte) 1)
short Short new Short((short) 1)
int Integer new Integer(1)
long Long new Long(1)
float Float new Float(1.0)
double Double new Double(1)
char Character new Character('c')

Việc Java tự chuyển đổi kiểu dữ liệu primitive sang Wrapper class tương ứng với nó gọi là Autoboxing và ngược lại gọi là Unboxing

List<Integer> numberList = new ArrayList<>();
numberList.add(1); // Autoboxing
numberList.add(new Integer(2));

int firstValue = numberList.get(0); // Unboxing

10. Tại sao khi define constructor thì default constructor lại mất đi?

Mặc định sử dụng new kết hợp constructor là cách khởi tạo object duy nhất của mỗi Class trong Java. Do đó Java luôn cung cấp cho chúng ta 1 default constructor không tham số.

Ngoài ra, Java cũng cho phép mỗi Class có nhiều constructor thông qua cơ chế overloading constructor với mục đích cung cấp linh hoạt cách thức khởi tạo object.

Mỗi constructor là một cách thức ta muốn khởi tạo object, vì vậy khi ta đã cung cấp một constructor mới thì đó là cách mà developer muốn class được khởi tạo thông qua nó (Có 1 số design pattern còn muốn private lại constructor). Lúc này Java không có lý do nào giữ lại default constructor, nếu muốn developer sẽ tự tạo một constructor không tham số.