Backend/Java

김영한 실전 자바 기본편 - 2.기본형과 참조형

해로몬 2024. 5. 13. 17:48

기본형 vs 참조형

~알고 넘어가기

📌기본형(Primitive Type) : int, long, double, boolean처럼 변수에 사용할 값을 직접 넣을 수 있는 데이터 타입

📌참조형(Reference Type) : Student student, int[] students와 같이 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입

*객체 또는 배열에 사용됨

 

+기본형은 실제 사용하는 값을 변수에 담을 수 있어서 해당 값을 바로 사용할 수 있음.

+참조형은 실제 객체의 위치(참조, 주소)를 저장한다. 참조형에는 객체와 배열이 있다.

    :객체는  .(dot)을 통해서 메모리 상에 생성된 객체를 찾아가야 사용 할 수 있음.

    :배열은 []를 통해서 메모리 상에 생성된 배열을 찾아가야 사용할 수 있다.

 

📌계산 : 기본형은 들어있는 값을 그대로 계산👌 참조형은 참조값을 그대로 사용할 수 ❌, 주소지에 가야 실체가 있음.

 

변수 대입

⭐️대원칙⭐️ : 자바는 항상 변수의 값을 복사해서 대입함.

자바에서 변수에 값을 대입하는 것은 변수에 들어있는 값을 복사해서 대입

기본형 대입 int a =10;
int b =a;
참조형 대입 Student s1 = new Student();
Student s2 = s1;

**기본형 = 변수에 값을 대입하더라도 실제 사용하는 값이 변수에 바로 들어있기 때문에 해당 값만 복사해서 대입한다고 생각하면 됨.

**참조형 = 실제 사용하는 객체가 아니라 객체의 위치를 가르키는 참조값만 복사됨.

 

public static void main(String[] args) {
    System.out.println("1)");
    int a =10;
    int b = a;
    System.out.println("a= "+a);
    System.out.println("b= "+b);
    System.out.println("2)");
    //a 변경
    a=20;
    System.out.println("a= "+a);
    System.out.println("b= "+b);
    System.out.println("3)");
    //b 변경
    b=30;
    System.out.println("a= "+a);
    System.out.println("b= "+b);
}

변수에 들어있는 값을 복사해서 전달.

 

public static void main(String[] args) {
         Data dataA = new Data();
         dataA.value = 10;
         Data dataB = dataA;
		System.out.println("dataA 참조값=" + dataA); System.out.println("dataB 참조값=" + dataB); System.out.println("dataA.value = " + dataA.value); System.out.println("dataB.value = " + dataB.value);
		//dataA 변경
		dataA.value = 20;
		System.out.println("변경 dataA.value = 20"); System.out.println("dataA.value = " + dataA.value); System.out.println("dataB.value = " + dataB.value);
		//dataB 변경
		dataB.value = 30;
		System.out.println("변경 dataB.value = 30"); System.out.println("dataA.value = " + dataA.value); System.out.println("dataB.value = " + dataB.value);
	}

 

변수에 들어 있는 값을 복사해서 대입한다. 참고로 변수 dataA가 가르키는 인스턴스를 복사하는 것이 아니다!!! 변수에 들어있는 참조값만 복사해서 전달함.

dataA와 dataB에 들어있는 참조값은 같기에 둘다 같은 x001 Data 인스턴스를 가리킨다.

 

 dataA.value = 20;
 

dataA가 가리키는 인스턴스의 값을 20으로 변경 =>dataA와 dataB는 같은 인스턴스를 참조하기 때문에 같은 값을 출력함.

 

💥핵심

Data dataB = dataA라고 했을 때 변수에 들어있는 값을 복사해서 사용한다는 점이다. 하지만 그 값은 참조값

따라서 A와 B는 같은 참조값을 가지게 되고, 두 변수는 같은 객체 인스턴스를 참조하게 된다.

 

메서드 호출

자바의 대원칙과 마찬가지로 메서드 호출도 똑같다.

메서드 호출할 때 사용하는 매개변수(파라미터) = 변수 이기 때문에 매개변수에 값을 전달하는 것도 값을 복사해서 전달한다.

✅기본형 메서드 호출

기본형 메서드 호출

자바에서 변수에 값을 대입하는 것은 항상 값을 복사해서 대입한다.!!!!

따라서 a와 x는 5을 가지고있다.

메서드 안에서 x=19으로 새로운 값을 대입하지만 결과적으로 x의 값만 19으로 변경되고 a의 값은 5로 유지 가능하다.

 

✅참조형 메서드 호출

참조형 메서드 호출

메서드를 호출할 때 매개변수 dataX에 변수 dataA의 값을 전달(참조값을 복사해서 전달)❌

 

dataX.value =20;

새로운 값을 대입.

참조값을 통해 인스턴스에 접근하고 그안에 있는 value의 값을 변경.

=>dataA dataX 모두 같은 인스턴스를 참조하기 때문에 dataA.value 와 dataX.value = 20을 가진다.

 

<<정리>>

기본형 : 데이터 전달 ->해당값이 복사되어 전달된다. 메서드 내부에서 매개변수(파라미터)의 값을 변경해도, 호출자의 변수 값에는 영향❌

참조형 : 데이터 전달 -> 참조값이 복사되어 전달. 메서드 내부에서 매개변수(파라미터)로 전달된 객체의 멤버 변수를 변경하면, 호출자의 객체도 변경⭕️

 

 

public class Method1 {
    public static void main(String[] args) {
        Student student1 = new Student(); //x001
        initStudent(student1, "학생1", 15, 90);
        Student student2 = new Student(); //x002
        initStudent(student2, "학생2", 16, 80);
        printStudent(student1);
        printStudent(student2);
    }
    //전달한 학생의 객체의 필드에 값을 설정.
    static void initStudent(Student student, String name, int age, int grade) {
        student.name = name;
        student.age = age;
        student.grade = grade;
    }
    //전달한 학생 객체의 필드 값을 읽어서 출력.
    static void printStudent(Student student1) {
        System.out.println("이름:" + student1.name + " 나이:" + student1.age + " 성적:" + student1.grade);
    	System.out.println(student1);
    }
}

initStudent()메서드를 만들고 호출하면서 student1을 전달한다.

=student1의 참조값이 매개변수 student에 전달됨 ->참조값을 통해 initStudent()메서드 안에서 student1이 참조하는 것과 동일한 Student 인스턴스에 접근하고 값을 변경⭕️

Method1 결과

public static void main(String[] args) {
	Student student1 = createStudent("학생1", 15, 90); 
        Student student2 = createStudent("학생2", 16, 80);
         printStudent(student1);
         printStudent(student2);
     }


//객체를 생성하고,초기값 설정
 static Student createStudent(String name, int age, int grade) {
         Student student = new Student();
         student.name = name;
         student.age = age;
         student.grade = grade;
         return student;
     }

createStudent()라는 메서드안에 객체를 생성하는 부분을 포함하여 생성.

But!!! 메서드 안에서 객체를 생성했기 때문에 만들어진 객체를 밖에서 사용할 수 있게 돌려줘야 밖에서도 이객체를 사용할 수 있음.

메서드의 반환(return)기능을 사용하여 만들어진 객체의 참조값을 메서드 밖으로 반환하면 된다.

 

<진행과정>

Student student1 = createStudent("학생1", 15, 90) //메서드 호출후 결과 반환
Student student1 = student(x001) //참조형인 student를 반환
Student student1 = x001 //student의 참조값 대입
student1 = x001

createStudent()는 생성한 Student 인스턴스의 참조값을 반환한다. 이렇게 반환된 참조값을  student1 변수 에 저장했다. 앞으로는student1을 통해 Student 인스턴스를 사용할 수 있다.

 

변수 초기화

📌멤버 변수 : 자동 초기화

  • -int = 0, boolean = false, 참조형 = null(null값은 참조할 대상이 없다는 뜻으로 사용).
  • -개발자가 초기값 직접 지정 가능.

📌지역 변수 : 수동 초기화 (항상 직접 초기화 해야함)

 

**null = 보낼 주소가 없다.


 

Garbage Collection : 아무도 참조하지 않는 인스턴스의 최후

data에 null을 할당 -> 앞서 생성한 x001 Data 인스턴스를 더는 아무도 참조하지 않음.

= 이렇게 아무도 참조하지 않게 되면 x001이라는 참조값을 다시 구할 방법이 없음. 따라서 해당 인스턴스에 다시 접근할 방법이 ❌

 

아무도 참조하지 않는 인스턴스 : 사용되지 않고 메모리 용량만 차지 함.

객체는 해당 객체를 참조하는 곳이 있으면, JVM이 종료할 때 까지 계속 생존한다. 그런데 중간에 해당 객체를 참조하는 곳이 모두 사라지면 그때 JVM은 필요 없는 객체로 판단다고 GC(가비지 컬렉션)를 사용해서 제거


NullPointerException

 

    public static void main(String[] args) {
		Data data = null;
		data.value = 10;// NullPointerException 예외 발생 
		System.out.println("data = " + data.value);
	} 
}

 

public class Data {
     int value;
}
public class BigData {
     Data data;//null
	 int count; //0
}

public class NullMain3 {
    public static void main(String[] args) {
        BigData bigData = new BigData();
        System.out.println("bigData.count ="+bigData.count);
        System.out.println("bigData.data="+bigData.data);
        //NullPointException;
        System.out.println("bigData.data.value="+bigData.data.value);
    }
}

bigData.data.value를 출력 -> data의 값이 null이므로 .(dot)을 찍게되고, 참조할 곳이 없으므로 예외가 발생한다.

 

**문제 해결 : Data인스턴스를 만들고 BigData.data멤버 변수에 참조값을 할당하면 된다.

bigData.data = new Data();

<<정리>>

NullPointerException이 발생하면 null값에 .(dot)을 찍었다고 생각하면 문제를 쉽게 찾을 수 있음.