30/09/2018, 20:20

JAVA pass by value or pass by reference :D chủ đề muôn thuở

Trước khi kết thành kim đan có 1 tâm ma mà anh em tu “tay” vướng phải là pass by value hay pass by reference

  package javaapplication1;
import java.util.Scanner;
public class JavaApplication1 {
public static class sn{
    private int v;
    public sn(int i){v=i;}
    public sn(){}
    public int getv(){return v;}
    public void setv(int i){v=i;}
}

public static void changei5i6_v2(sn i,sn j){
    sn k=i;
    i=j;j=k;
    i.setv(3);
    j.setv(4);
}
public static void changei5i6(sn i,sn j){
    sn k=new sn();
    k=i;
    i=j;j=k;
}

public static void changei4(sn i){
    sn j= new sn();
    j.v=i.v;
    j.setv(2);
}
public static void changei3(sn i){
    sn j=new sn();
    j=i;
    j.setv(2);
}
public static void changei2(sn i){
    i.setv(2);
}
public static void changei1(int i){
    i=2;
}
public static void main(String[] args) {
    //int
   int i1=0;
   changei1(i1);
   System.out.println(i1);
   //obj
   sn i2= new sn(0);
   changei2(i2);
   System.out.println(i2.getv());
   //obj i3
   sn i3= new sn(0);
   changei3(i3);
   System.out.println(i3.getv());
   //obj i4
   sn i4= new sn(0);
   changei4(i4);
   System.out.println(i4.getv());
   //obj i5 i6
   sn i5=new sn(1);
   sn i6= new sn(2);
   changei5i6(i5,i6);
   System.out.println(i5.getv()+";"+i6.getv());
   //obj i5 i6 v2
   changei5i6_v2(i5,i6);
   System.out.println(i5.getv()+";"+i6.getv());
}

}

Đoán kết quả nào

Quân viết 22:31 ngày 30/09/2018

java là pass reference by value do đó kết quả sẽ là

0
2
2
0
1;2
4;3

chưa test lại

Phan Hoàng viết 22:31 ngày 30/09/2018

Java bỏ khái niệm con trỏ nên tất cả đều là pass by value. Tuy nhiên, có 1 điều có thể confuse với những người mới học (khái niệm reference trong java), ví dụ như dưới đây

public static void main( String[] args ){
    Dog dog = new Dog("Hehe");
    foo(dog);

    if (dog.getName().equals("Hehe")) { //true
        System.out.println( "Java passes by value." );

    } else if (dog.getName().equals("Hihi")) {
        System.out.println( "Java passes by reference." );
    }
}

public static void foo(Dog d) {
    d.getName().equals("Hehe"); // true
    
    d = new Dog("Haha");
    d.getName().equals("Haha"); // true

    //d.setName("Hihi");
}

Lý do rất đơn giản: Java gọi các pointer reference, do đó việc pass các reference này theo value.

  • dog = new Dog(“Max”), thì biến dog không phải là real Dog mà chỉ là reference đến obj Dog nằm trong heap mà thôi (dog là pointer). Giả sử pointer dog này trỏ vào offset là 1000.
  • Khi truyền dog (Max) vào biến d trong hàm foo, biến d được tạo ra nhưng lại cũng reference vào Dog(“Max”) (phía bên heap memory). d cũng là 1 pointer (và cũng chỉ vào 1000), nhưng dog và d là hai biến độc lập nhau nằm trong stack (chỉ có chung value = 1000 mà thôi). Tiếp hai dòng dưới, d trỏ vào 1 con Dog# tên là Haha, giả sử địa chỉ trỏ là 2000, thế nhưng biến dog phía trên vẫn trỏ vào con Dog đầu tiên ở địa chỉ 1000.
  • Giả sử đoạn code trên được thay bằng d.setName(“Hihi”) thì sao. pointer này thay đổi giá trị của obj Dog đầu tiên thành “Hihi”, tuy nhiên biến dog phía trên vẫn là pointer trỏ vào địa chỉ 1000. Obj này đã được thay đổi tên, nhưng thực ra vẫn là con Dog đầu tiên nằm ở vị trí 1000 trong Heap. Và tất nhiên, giờ kết quả sẽ nhảy vào “Java passes by reference.”, nhưng thực chất ra lại là pass theo value reference (cả dog và d đều là reference cả, và 2 cái reference này pass cho nhau cái giá trị, value = 1000)

Thế nên phải gọi Java là

PASS BY VALUE OF REFERENCE (truyền theo giá trị của tham chiếu)

Ai Android viết 22:30 ngày 30/09/2018

Em có 1 vấn đề đó là
Em có 1 obj abc
Trong hàm
private void mainwork(){
xuli(abc);
}
private voi xuli(obj a){
// ở đây em muốn xử lí obj a mà ko không làm thay đổi giá trị của obj abc ban đầu
//thì có cách nào ko ạ , đối với List thì em hay dùng
// List b= new arraylist(a); hoặc addall
// còn đối với obj thì em toàn phải gán tuần tự từng value có cách nào copy ra luôn k ạ
}

Nguyen Ca viết 22:34 ngày 30/09/2018

Tìm hiều về imuable trong Java, giống như Long, String,Interger. để làm được (tạo immuable object ) điểu này có thể khai báo final class.

Phan Hoàng viết 22:34 ngày 30/09/2018

Có 1 số cách làm:

  • shallow copy - copy nông (như cách bạn làm, hoặc sử dụng câu lệnh clone mà Java cung cấp). 2 obj này độc lập với nhau, nhưng các field / attribute của nó vẫn trỏ (reference) tới cùng một obj thực trong heap.
  • deep copy - copy sâu: thường thì mình hay làm là serialize object nguồn, sau đó thì lại unserialize để tạo obj đích.
  • ngoài ra, có 1 cách khác là thực hiện copy ở constructor
class DestBean {
  private String _field1;
  private String _field2;

  public DestBean(Bean srcBean) {
    this._field1 = another.field1; // you can access  
    this._field2 = another.field2; // swallow copy, still point to same obj in heap 
  }
}

Trong thư viện org.apache.commons.lang.SerializationUtils cũng cung cấp 1 function dành cho deep clone

this.myObjectCloned = SerializationUtils.clone(this.object);
Ai Android viết 22:33 ngày 30/09/2018

Cảm ơn 2 anh @Phan_Hoang @nguyenhuuca

Reoteu Ray viết 22:36 ngày 30/09/2018

vì sao lại tạo ra deep copy khi đã có shallow copy?

Nguyễn Hải Đăng viết 22:35 ngày 30/09/2018
stackoverflow.com
David Locke

What is the difference between a deep copy and a shallow copy?

language-agnostic, copy, deep-copy, shallow-copy
asked by David Locke on 08:22PM - 08 Oct 08
Bi-Clever viết 22:25 ngày 30/09/2018

bác giải thích cho e cái khác giữa changei5i6 và changei5i6v2 được k? cứ nghĩ là 2;1 mà k phải

Quân viết 22:29 ngày 30/09/2018

bạn có thể xem diễn giải bằng hình như sau cho dễ hiểu
https://drive.google.com/open?id=0B8pezEwuUWSbNWVWbmxldnRhX28

Bi-Clever viết 22:29 ngày 30/09/2018

e hiểu rồi
nhưng vẫn đề này thì s ạ Tham biến và tham chiếu trong java

Quân viết 22:24 ngày 30/09/2018

Như hình minh họa mình up ở trên, phép gán objA = objB không phải là objA trỏ đến objB mà objA sẽ trỏ đến vị trí mà objB đang trỏ đến. Tương tự, việc bạn truyền tham số vào hàm thực chất là tạo 1 obj mới trỏ tới giá trị mà obj truyền vào đang trỏ tới,
PS: mọi phép gán objA = objB đều sẽ khiến objA từ bỏ reference tới giá trị mà nó đang trỏ tới, đồng thời reference tới giá trị mà objB đang trỏ tới

Bi-Clever viết 22:30 ngày 30/09/2018

ở đây mình hiểu tnay,sai ở đâu sửa giúp mình nhé.
HocSinh hocSinh2 = new HocSinh(); //hocSinh2 trỏ tới 1 HocSinh
hocSinh2.setTen(“Nguyen Van A”); // hocSinh2 đã sets tên đối tượng n trỏ tới thành “Nguyen Van A”
hocSinh = hocSinh2(); //hocSinh trỏ tới cùng đối tượng hocSinh2 đang trỏ => cũng có tên là “Nguyen Van A”
vậy mà lại in ra null

Quân viết 22:27 ngày 30/09/2018

Cái mà bạn in ra là mHocSinh đúng không, ngay thời điểm thực hiện câu lệnh hocSinh = hocSinh2 thì hocSinh và mHocSinh đã là 2 người xa lạ, do đó dữ liệu mà hocSinh và mHocSinh trỏ tới là khác nhau. bạn nên lưu ý là cái được truyền vào hàm k phải là mHocSinh mà chỉ là địa chỉ nơi mà mHocSinh đang trỏ tới thôi

Tất Huân viết 22:23 ngày 30/09/2018

Mình thì vẫn giữ nguyên quan điểm : Khi kết thúc hàm thì Hocsinh2 bị hủy (Bạn xem lại phạm vi biến trong java sẽ rõ) nên Hocsinh trỏ đến Hocsinh2 sẽ null

Bi-Clever viết 22:35 ngày 30/09/2018

mình hiểu rồi,có phải là thế này k: thực chất khi truyền mhocsinh vào chỉ là cho hocsinh trỏ tới mhocsinh nơi mhocsinh trỏ tới. Rồi gán hocsinh = hocsinh2 thì chỉ làm hocsinh trỏ đến cùng nơi với hocsinh2.
CHỨ KHÔNG LÀM THAY ĐỔI GÌ ĐẾN MHOCSINH

Quân viết 22:24 ngày 30/09/2018

Chính xác là vậy,…

Quân viết 22:29 ngày 30/09/2018

Điều này là k đúng, 1 biến hay nói đúng hơn là 1 object có thể tồn tại lâu hơn bạn tưởng nhiều, lí thuyết là ra khỏi hàm thì biến bị hủy tuy nhiên thực tế thì không phải vậy, thời gian sống thực tế phụ thuộc vào việc bộ dọn rác của jvm đã dọn tới biến đó hay chưa hay theo 1 cách hiểu khác là đã bị thu hồi lại hay chưa. Bạn có thể tìm đọc các tài liệu liên quan tới cách làm việc của bộ dọn rác trên jvm và các máy ảo cùng họ để hiểu thêm.

Ở đây lí do khi ra khỏi hàm bị null là do biến mHocsinh ở ngoài và biến hocsinh tham số hàm là 2 biến khác nhau đang cùng trỏ tới 1 miền dữ liệu, việc gán biến hocsinh2 cho biến hocsinh thực chất chỉ là thay đổi miền giá trị mà biến hocsinh đang trỏ tới sang miền giá trị mà hocsinh2 đang trỏ tới, điều này chẳng ảnh hưởng gì đến vị trí mà biến mhocsinh ở ngoài hàm đang trỏ tới cả

Bài liên quan
0