[Java] 기본형과 참조형

szlee·2023년 12월 5일
0

Java

목록 보기
11/23

< 김영한의 실전 자바 - 기본편 > 강의를 보고 이해한 내용을 바탕으로 합니다.

자바는 항상 변수의 값을 ❗️복사❗️해서 대입한다. 너무너무 중요!!







기본형과 참조형

기본형

int, long, double, boolean처럼 사용하는 값을 변수에 직접 넣을 수 있는 데이터 타입.
기본형 변수에는 직접 사용할 수 있는 값들이 들어있다.
숫자 10, 20과 같이 실제 사용하는 값을 변수에 담을 수 있다. 그래서 해당 값을 바로 사용할 수 있다.

참조형

Student student, int[] students와 같이 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입. 참조형은 객체 또는 배열에 사용된다.
참조형 변수에는 위치(참조값)가 들어가있다.
참조형 변수를 통해서 뭔가 하려면 결국 참조값을 통해 해당 위치로 이동해야 한다.
실제 사용하는 값을 변수에 담는 것이 아니라 실제 객체의 위치(참조, 주소)를 저장한다.
참조형에는 객체와 배열이 있다.

  • 객체는 .을 통해서 메모리 상에 생성된 객체를 찾아가야 사용할 수 있다.
  • 배열은 []를 통해 메모리 상에 생성된 배열을 찾아가야 사용할 수 있다.
int a = 10, b = 20;
int sum = a+b; //결과:30

Student s1 = new Student();
Student s2 = new Student();
s1+s2 //오류발생


Student s1 = new Student();
s1.grade - 100;
Student s2 = new Student();
s2.grade = 90;
int sum = s1.grade + s2.grade; //연산 가능

기본형을 제외한 나머지는 모두 참조형이다.

  • 기본형은 소문자로 시작한다. - int, long, double, boolean
    • 자바가 기본으로 제공하는 데이터 타입.
  • 개발자는 참조형인 클래스만 직접 정의할 수 있다.
  • 클래스는 대문자로 시작한다. -> 모두 참조형이다.

그렇다면 String은 뭐지?

String은 사실 클래스다. 따라서 참조형이다.
그런데 기본형처럼 문자 값을 바로 대입할 수 있다.
문자는 매우 자주 다루기 때문에 자바에서 특별하게 편의 기능을 제공한다.



변수 대입

⭐️자바는 항상 변수의 값을 복사해서 대입한다.⭐️
기본형, 참조형 모두 항상 변수에 있는 값을 복사해서 대입한다.
기본형이면 변수에 들어있는 실제 사용하는 값을 복사해서 대입하고,
참조형이면 변수에 들어있는 참조값을 복사해서 대입한다.
(실제 객체가 복사되는 것이 아니다!!!)

기본형

int a = 10;
int b = a;

a의 값을 복사해서 b에 대입.
변수 a자체를 b에 대입하는 것이 아니다!
따라서 이후 a의 값이 변해도 b에 아무런 영향을 주지 않는다.

public class VarChange1 {

    public static void main(String[] args) {
        int a = 10;
        int b = a;
        System.out.println("a = " +a);
        System.out.println("b = " +b);

        //a 변경
        System.out.println("변경 a = 20");
        System.out.println("a = " +a);
        System.out.println("b = " +b);

        //b 변경
        b = 30;
        System.out.println("변경 b = 30");
        System.out.println("a = " +a);
        System.out.println("b = " +b);
        
    }
}
 

의 결과

a = 10
b = 10
변경 a = 20
a = 20
b = 10
변경 b = 30
a = 20
b = 30

참조형



public class Varchange2 {

    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 참조값=ref.Data@30f39991
dataB 참조값=ref.Data@30f39991
dataA.value =10
dataB.value =10
변경 dataA.value = 20
dataA.value = 20
dataB.value = 20
변경 dataB.value = 30
dataA.value = 30
dataB.value = 30


메서드 호출

메서드를 호출할 때 사용하는 매개변수(파라미터)도 결국 변수일 뿐이다.
따라서 메서드를 호출할 때 매개변수에 값을 전달하는 것도 앞서의 내용과 같이 값을 복사해서 전달한다.


public class MethodChange1 {
    public static void main(String[] args) {
        int a = 10;
        System.out.println("메서드 호출 전: a = " +a); //10
        changePrimitive(a);
        System.out.println("메서드 호출 후: a = " +a); //10
    }

    public static void changePrimitive(int x) {
        x = 20;
    }
}

자바에서 변수에 값을 대입하는 것은 항상 값을 복사해서 대입한다.
a와 x는 각각 숫자 10을 가지고 있다.
메서드 안에서 x=20으로 새로운 값을 대입하면, x만 20으로 바뀌고 a의 값은 그대로이다.
메서드 종료 후 값을 확인해보면 a는 10이 출력되는 것을 볼 수 있고, 메서드가 종료되면 매개변수 x는 제거된다.

참조형과 메서드 호출

public class MethodChange2 {
    public static void main(String[] args) {
        Data dataA = new Data();
        dataA.value = 10;
        System.out.println("메서드 호출 전: dataA.value = " +dataA.value); //10
        changeReferene(dataA);
        System.out.println("메서드 호출 전: dataA.value = " +dataA.value); //20
    }

    public static void changeReferene(Data dataX) {
        dataX.value = 20;
    }
}

기본형과 참조형의 메서드 호출

자바에서 메서드의 매개변수(파라미터)는 항상 값에 의해 전달된다. 그러나 이 값이 실제 값이냐, 참조(메모리 주소)값이냐에 따라 동작이 달라진다.

  • 기본형 : 메서드로 기본형 데이터를 전달하면 해당 이 복사되어 전달된다. 이 경우, 메서드 내부에서 파라미터의 값을 변경해도 호출자의 변수 값에는 영향이 없다.
  • 참조형 : 메서드로 참조형 데이터를 전달하면 참조값이 복사되어 전달된다. 이 경우, 메서드 내부에서 파라미터로 전달된 객체의 멤버 변수를 변경하면 호출자의 객체도 변경된다.



참조형과 메서드 호출 활용해보기

public class Method1 {
    public static void main(String[] args) {
        Student student1 = new Student();
        initStudent(student1, "학생1", 15, 90);
        
        Student student2 = new Student();
        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 student){
        System.out.println("이름: " + student.name + " 나이: "+ student.age + " 성적: " + student.grade );
    }
}

참조형은 메서드를 호출할 때 참조값을 전달한다. 따라서 메서드 내부에서 전달된 참조값을 통해 객체의 값을 변경하거나 값을 읽어서 사용할 수 있다.

🛠 Refactoring


public class Method2 {
    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;
    }

    static void printStudent(Student student){
        System.out.println("이름: " + student.name + " 나이: "+ student.age + " 성적: " + student.grade );
    }
}




변수와 초기화

변수 종류

  • 멤버 변수 : 클래스에 선언
  • 지역 변수 : 메서드에 선언. (매개변수도 지역 변수의 한 종류)
    • 이름 그대로 특정 지역에서만 사용되는 변수라는 뜻이다. 해당 메서드가 끝나면 제거된다.

변수의 값 초기화

  • 멤버 변수 : 자동 초기화
    • 인스턴스의 멤버 변수는 인스턴스를 생성할 때 자동으로 초기화된다.
    • int = 0, boolean = false, 참조형 = null (참조대상 없음)
    • 개발자가 초기값을 직접 지정할 수 있다.
      public class InitData {
       int value1;
       int value2 = 10;
      }
      public class InitMain {
       public static void main(String[] args) {
           InitData data = new InitData();
           System.out.println("value1 = " + data.value1); //0 (자동초기화)
           System.out.println("value2 = " + data.value2); //10
       }
      }
  • 지역 변수 : 수동 초기화
    • 지역 변수는 항상 직접 초기화해야 한다.








NULL

참조형 변수에는 항상 객체가 있는 위치를 가리키는 참조값이 들어간다. 그런데 아직 가리키는 대상이 없거나 가리키는 대상을 나중에 입력하고 싶다면?
참조형 변수에서 아직 가리키는 대상이 없다면 null이라는 특별한 값을 넣어둘 수 있다.
null은 값이 존재하지 않는, 없다는 뜻이다.

public class NullMain1 {
    public static void main(String[] args) {
        Data data = null;
        System.out.println("1. data = " + data); //null
        data = new Data();
        System.out.println("2. data = " + data); //ref.Data@30f39991
        data = null;
        System.out.println("3. data = " + data); //null
    }
}

GC 🗑 - 아무도 참조하지 않는 인스턴스의 최후

data에 null을 할당하면 해당 인스턴스를 더는 아무도 참조하지 않는다.
이렇게 아무도 참조하지 않게 되면 ref.Data@30f39991 라는 참조값을 다시 구할 방법이 없으므로 해당 인스턴스에 다시 접근할 방법이 없다.

이렇게 아무도 참조하지 않는 인스턴스는 사용되지 않고 메모리 용량만 차지한다.
(C와 같은 과거 프로그래밍 언어는 개발자가 직접 명령어를 사용해서 인스턴스를 메모리에서 제거해야 했다. 만약 실수로 인스턴스 삭제를 누락하면 메모리에 사용하지 않는 객체가 가득해져서 메모리 부족 오류가 발생하게 된다. 자바는 이런 과정을 자동으로 처리해준다. )
아무로 참조하지 않는 인스턴스가 있으면 JVM의 GC가 더 이상 사용하지 않는 인스턴스라 판단하고 해당 인스턴스를 자동으로 메모리에서 제거해준다.

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

NullPointerException

택배 보낼 때 주소지 없이 택배 발송하면 문제가 생기는 것처럼, 참조값 없이 객체를 찾아가면 NullPointerException이라는 예외가 발생한다.
이름 그대로 null을 가리킬 때 발생하는 예외다.

객체를 참조할 때는 .을 사용하는데 NullPointerException은 이처럼 null.을 찍었을 때 발생한다.

멤버변수와 null

지역변수가 null일때는 바로 알기 쉽다.
멤버변수가 null인 경우는 주의가 필요하다.

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); //0
        System.out.println("bigData.data=" + bigData.data); //null

        System.out.println("bigData.data.value" + bigData.data.value); //NullPointerException
    }
}
Exception in thread "main" java.lang.NullPointerException: Cannot read field "value" because "bigData.data" is null
	at ref.NullMain3.main(NullMain3.java:9)

이를 정상적으로 작동하도록 고치면

public class NullMain3 {
    public static void main(String[] args) {
        BigData bigData = new BigData();
        bigData.data = new Data(); //data를 참조값을 주면 된다.
        
        System.out.println("bigData.count=" + bigData.count);
        System.out.println("bigData.data=" + bigData.data);

        System.out.println("bigData.data.value" + bigData.data.value);
    }
}

profile
🌱

0개의 댓글