자바 기본 | 2. 기본형과 참조형

heige·2024년 5월 20일

Java

목록 보기
16/21
post-thumbnail

기본형과 참조형

기본형 vs 참조형1 - 시작

자바에서는 참조형을 제대로 이해해야 한다.

변수의 데이터 타입을 가장 크게 기본형, 참조형으로 분류할 수 있다. 사용하는 값을 변수에 직접 넣는 기본형, Stuent student1과 같이 객체가 저장된 메모리의 위치를 가리키는 참조값을 넣을 수 있는 참조형으로 분류할 수 있다. 참조형은 객체 또는 배열에 사용된다.
참조형 변수를 통해서 뭔가 하려면 결국 참조값을 통해 해당 위치로 이동해야 한다.

기본형 vs 참조형 - 기본

  • 기본형은 사용하는 값을 변수에 직접 담고, 해당 값을 바로 사용할 수 있다.
  • 참조형은 실제 객체의 위치를 저장한다.
    • 객체는 .을 통해 메모리 상에 생성된 객체
      를 찾아가야 사용할 수 있다.
    • 배열은 []를 통해서 메모리 상에 생성된 배열을 찾아가야 사용할 수 있다.

기본형 vs 참조형 - 계산

  • 기본형은 연산이 가능하지만 참조형은 연산이 불가능하다.
  • .을 통해 객체의 기본형 멤버 변수에 접근한 경우에는 연산이 가능하다.

기본형 vs 참조형2 - 변수 대입

대원칙 : 자바는 항상 변수의 값을 복사해서 대입한다.
기본형은 변수에 값을 대입하더라도 실제 사용하는 값이 변수에 바로 들어있기 때문에 해당 값만 복사해서 대입하다고 생각하면 쉽게 이해 가능하다. 참조형의 경우 실제 사용하는 객체가 아니라 객체의 위치를 가리키는 참조값만 복사된다..

기본형과 변수 대입

package ref;

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 = 20;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        
        b = 20;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

참조형과 변수 대입

package ref;

public class VarChange2 {
    public static void main(String[] args) {
        Data dataA = new Data();
        dataA.value = 10;
        Data dataB = dataA;

        System.out.println(dataA);
        System.out.println(dataB);
        System.out.println(dataA.value);
        System.out.println(dataB.value);

        //dataA 변경
        dataA.value = 20;
        System.out.println(dataA.value);
        System.out.println(dataB.value);

        //dataB 변경
        dataB.value = 30;
        System.out.println(dataA.value);
        System.out.println(dataB.value);
    }
}

변수의 대입은 변수에 들어있는 값을 복사해서 대입한다. dataA가 가리키는 인스턴스를 복사하는 것이 아니라, 변수에 들어있는 참조값만 복사해서 전달한다.

기본형 vs 참조형3 - 메서드 호출

자바에서 변수에 값을 대입하는 것은 변수에 들어있는 값을 복사해서 대입하는 것이다.(기본형, 참조형 모두)

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

기본형과 메서드 호출

package ref;

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

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

참조형과 메서드 호출

package ref;

public class MethodChange2 {
    public static void main(String[] args) {
        Data dataA  = new Data();
        dataA.value = 10;
        System.out.println("호출 전 : " + dataA.value);
        changeReference(dataA);
        System.out.println("호출 후 : " + dataA.value);
    }

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


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

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

참조형과 메서드 호출 - 활용

중복되는 내용은 메서드를 통해 쉽게 제거할 수 있다.

package ref;

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 student1) {
        System.out.println(student1.name + " " + student1.age + " " + student1.grade);
    }
}

package ref;

public class Method2 {
    public static void main(String[] args) {
        Student student1 = createStudent("s1", 15, 90);
        Student student2 = createStudent("s2", 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;
    } // 함수 형식 주의(반환값 있으면 void형 X) 

    static void printStudent(Student student1) {
        System.out.println(student1.name + " " + student1.age + " " + student1.grade);
    }
}

createStudent 메서드 하나로 객체의 생성과 초기값 설정을 모두 처리한다.

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

변수와 초기화

변수의 종류

  • 멤버 변수(필드) : 클래스에 선언
  • 지역 변수 : 메서드에 선언, 매개변수도 지역 변수의 한 종류이다.

변수의 값 초기화

  • 멤버 변수 : 자동 초기화
    • 인스턴스의 멤버 변수는 인스턴스 생성 시 자동으로 초기화된다.
    • 초기값은 직접 지정 가능하다.
  • 지역 변수 : 수동 초기화
    • 지역 변수는 항상 직접 초기화해야 한다.
package ref;

public class InitData {
    int value1;
    int value2 = 10;
}
package ref;

public class InitMain {
    public static void main(String[] args) {
        InitData data = new InitData();
        System.out.println("value1 = " + data.value1);
        System.out.println("value2 = " + data.value2);
    }
}

멤버 변수는 초기화가 저절로 된다.

null

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

null 값 할당

package ref;

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


data 변수는 아직 가리키는 객체가 없다는 뜻이다.


새로운 Data 객체를 생성해서 그 참조값을 data 변수에 할당했다. 이제 참조할 객체가 존재함!


마지막에는 다시 null 할당했다. 그러면 앞서 만든 Data 인스턴스를 더는 참조하지 않는다.


(가비지 컬렉션)
마지막에 datanull을 할당했다. 아무도 참조하지 않으면 x001이라는 참조값에 다시 접근할 방법이 없다. 이렇게 아무도 참조하지 않는 인스턴스는 사용되지 않고 메모리 용량만 차지한다. 자바는 이런 과정을 자동으로 처리한다. 아무도 참조하지 않는 인스턴스가 있으면 JVM의 GC가 더이상 사용하지 않는 인스턴스라 판단하고 해당 인스턴스를 자동으로 메모리에서 제거해준다.**

NullPointerException

객체를 참조할 때는 .을 사용한다. 참조값이 null이라면 값이 없다는 뜻이고, 찾아갈 수 있는 객체(인스턴스)가 없다. NullPointerExceptionnull.을 찍었을 때 발생한다.

package ref;

public class NullMain2 {
    public static void main(String[] args) {
        Data data = null;
        data.value = 10;
        System.out.println("data = " + data.value);
    }
}

멤버 변수와 null

package ref;

public class BigData {
    Data data; // 참조형이므로 null로 초기화된다. 
    int count;
}
package ref;

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);

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

BigData를 생성하면 그의 인스턴스가 생성되는데, BigDatadata 멤버변수는 참조형이므로 null로 초기화 된다. count는 숫자이므로 0으로 초기화된다.

bigData.data.value를 출력하면 값이 null이므로, null. 찍으면 참조할 곳이 없어서 예외가 발생한다.

package ref;

public class NullMain4 {
    public static void main(String[] args) {
        BigData bigData = new BigData();
        bigData.data = new Data();

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


문제와 풀이

상품 주문 시스템 개발 - 리팩토링

package ref.ex;

public class ProductOrder {
    String productName;
    int price;
    int quantity;
}
package ref.ex;

public class ProductOrderMain2 {
    public static void main(String[] args) {
        ProductOrder[] orders = new ProductOrder[3];
        orders[0] = createOrder("두부", 2000, 2);
        orders[1] = createOrder("김치", 500, 1);
        orders[2] = createOrder("콜라", 1500, 2);

        printOrders(orders);
        int totalAmount = getTotalAmount(orders);
        System.out.println("total : " + totalAmount);
    }
    static ProductOrder createOrder(String productName, int price, int quantity) {
        ProductOrder order = new ProductOrder();
        order.productName = productName;
        order.price = price;
        order.quantity = quantity;
        return order;
    }
    static void printOrders(ProductOrder[] orders) {
        for(ProductOrder order : orders) {
            System.out.println("상품명 : " + order.productName + ", 가격 : " + order.price + ", 수량 : " + order.quantity);
        }
    }

    static int getTotalAmount(ProductOrder[] orders) {
        int totalAmount = 0;
        for (ProductOrder order : orders) {
            totalAmount += order.price * order.quantity;
        }
        return totalAmount;
    }
}

상품 주문 시스템 개발 - 사용자 입력

package ref.ex;

import java.util.Scanner;

public class ProductOrderMain3 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("주문 개수 입력 : ");
        int n = scanner.nextInt();
        scanner.nextLine();

        ProductOrder[] orders = new ProductOrder[n];

        for (int i = 0; i < orders.length; i++) {
            System.out.println((i + 1) + " 번째 주문 정보를 입력하세요. ");

            System.out.print("name : ");
            String productName = scanner.nextLine();

            System.out.println("price : ");
            int price = scanner.nextInt();

            System.out.println("quantity : ");
            int quantity = scanner.nextInt();
            scanner.nextLine();

            orders[i] = createOrder(productName, price, quantity);
        }

        printOrders(orders);
        int totalAmount = getTotalAmount(orders);
        System.out.println("Total : " + totalAmount);
    }

    static ProductOrder createOrder(String productName, int price, int quantity) {
        ProductOrder order1 = new ProductOrder();
        order1.productName = productName;
        order1.price = price;
        order1.quantity = quantity;
        return order1;
    }

    static void printOrders(ProductOrder[] orders) {
        for(ProductOrder order : orders) {
            System.out.println("name " + order.productName + ", price : " + order.price + ", quantity : " + order.quantity);
        }
    }

    static int getTotalAmount(ProductOrder[] orders) {
        int totalAmount = 0;
        for (ProductOrder order : orders) {
            totalAmount += order.price * order.quantity;
        }
        return totalAmount;
    }

}
profile
웹 백엔드와 클라우드 정복을 위해 탄탄한 기반을 쌓아가고 있는 예비개발자입니다. 'IT You Up'은 'Eat You Up'이라는 표현에서 비롯되어, IT 지식을 끝까지 먹어치운다는 담고 있습니다.

0개의 댓글