기본형과 참조형

황상익·2024년 3월 25일

Inflearn JAVA

목록 보기
13/61

기본형 VS 참조형

변수의 데이터 타입을 가장 크게 보면 기본형과 참조형으로 구분.
사용하는 값을 변수에 직접 넣어 -> 기본형
겍체에 저장된메모리 위치를 가르키는 참조형

기본형 -> int, long, double, boolean 처럼 변수에 사용할 값을 직접 넣을 수 있는 데이터 터입을 기본형

참조형 -> 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입을 참조형

기본형은 실제 사용하는 값을 변수에 담을 수 있고 바로 사용 가능
참조형은 실제 사용하는 값을 변수에 담는 것이 아닌, 이름 그대로 실제 객체의 위치(주소)를 저장
-> 객체는 .을 통해 메모리 상에 생성된 객체를 찾아가 사용 가능
-> 배열은 []을 통해서 메모리 상에 배열을 찾아가 사용

기본형 vs 참조형 -> 계산
기본형은 들어가 있는 값을 그대로 사용 가능
참조형은 들어있는 참조값을 그대로 사용 X -> 주소는 있지만 할 수 있는 것 엇ㅂ음

기본형 VS 참조형 -> 변수 대입
대원칙!! = 자바는 항상 변수의 값을 복사해서 대입
자바에서는 변수에 값을 대입하는 것은 변수에 들어있는 값을 복사해서 대입
기본형은 변수에 값을 대입하더라도 실제 사용하는 값이 변수에 바로 들어왔기 때문에 값만 복사해서 대입. 그런데 참조형의 경우 실제 자용하는 객체가 아니라 객체의 위치를 가리키는 참조값만 복사 . (주소만 복사)

기본형과 변수 대입

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

여기서 핵심은 들어있는 값을 복사해서 전달하는 것. 즉 본인의 값만 변경

참조형과 변수 대입

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

        Data data1 = data;

        System.out.println("data = " + data);
        System.out.println("data1 = " + data1);

        System.out.println("data1.value = " + data.value);
        System.out.println("data1.value = " + data1.value);

        data.value = 20;
        System.out.println("data = " + data.value);
        System.out.println(".va = " + data1.value);

        data1.value = 30;
        System.out.println("data = " + data.value);
        System.out.println("data = " + data1.value);
    }
}

data1 변수는 Data 클래스를 통해 만들어졌기 때문에 참조형. 이 변수는 Data형 객체의 참조값을 저장, Data 객체를 생성, 참조값을 data1에 저장. 그리고 객체 value 변수에 10을 저장

변수의 대입은 변수에 들어있는 값을 복사해서 대입. data1에 참조값 x001이 있다고 하자, data에 대입. 참고로 data1을 가르키는 인스턴스를 복사해서 사용하는 것이 아님. 변수에 들어있는 참조값만 복사해서 전달.

핵심은 변수에 들어가있는 값을 복사해서 사용한다는 것, 그런데 값이 참조값. 따라서 data1, data는 같은 참조값을 갖게 되고, 두 변수는 같은 객체 인스턴스를 참조

기본형 VS 참조형 => 메서드 호출
항상 대원칙을 기억하자!
메서드 호출도 마찬가지, 메서드를 호출할때 사용하는 매개변수도 결국 변수이다. 메서드를 호출할때 매개변수에 값을 전달하는 것도 앞서 설명한 내용과 동일

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

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

변수 대입과 비슷한 내용이기 때문에 생략한다.

참조형과 메서드 호출

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

        changeReference(data);
        System.out.println("메서드 호출 후 data.value :" + data.value);
    }

    public static void changeReference(Data data1) {
        System.out.println(data1);
        data1.value = 20;
    }
}

정리

기본형과 참조형의 메헏 호출
자바에서 메서드의 매개변수는 항상 값에 의해 전달. 그러나 이 값이 실제 값이냐, 참조값이냐에 따라 동작이 달라진다.

기본형: 메서드로 기본형 데이터를 전달하면, 값이 복사 -> 전달. 이 경우 메서드 내부에서 매개변수의 값을 변경해도 호출자의 변수 값에는 영향 X

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

참조형과 메서드 호출 => 활용

public class Student {
    String name;
    int age;
    int grade;
}
public class StudentMain {
    public static void main(String[] args) {
        Student student = new Student();
        initStudent(student, "황상익", 29, 100);
        printStudent(student);
    }

    public static void initStudent(Student student, String name, int age, int grade) {
        student.name = name;
        student.age = age;
        student.grade = grade;
    }

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

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

  • student1이 참조하는 Student 인스턴스에 값을 편하게 할당 하고 싶어서 init메서드를 형성
  • 메서드를 호출하면서 student1을 전달. 그러면 student1의 참조값이 매개변수 student에 저달.

메서드에서 객체 반환

public class StudentMain2 {
    public static void main(String[] args) {
        Student student = createStudent("황상익", 30, 100);
        System.out.println(student);
        printStudent(student);
    }

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

    public static Student createStudent(String name, int age, int grade) {
        Student student = new Student();
        System.out.println(student);
        student.name = name;
        student.age = age;
        student.grade = grade;
        return student;
    }
}

중복되는 부분을 제거

메서드 안에서 객체를 생성, 객체를 메서드 외부에서 사용할 수 있게 돌려줘야 함. 그래야 메서드 밖에서 이 객체를 사용 할 수 있다. 메서드 호출 결과를 반환 (return)을 할 수 있다.
메서드의 반환 가능을 사용해서 만들어진 객체의 참조값을 메서드 외부로 던지면 된다.

변수와 초기화

변수의 종류
멤버 변수 : 클레스에 선언
지역 변수 : 메서드에 선언, 매개변수도 지역변수의 한 종류

ex) 맴버 변수

public class Student {
 String name;
 int age;
 int grade;
}

ex) 지역 변수

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

a, x는 지역변수
지역변수는 이름 그대로 특정 지역에서만 사용되는 변수, 지역 외부로 나가면 사용X

변수의 값 초기화

멤버 변수 : 자동 초기화

  • 인스턴스의 멤버변수는 인스턴스를 생성할 때 자동 초기화
  • 숫자(int) = 0, boolean = false, 참조형 = null
  • 개발자가 초기값을 지정할 수 있다.

지역 변수 : 수동 초기화

  • 지역 변수는 항상 직접 초기화
public class InitData {
    int val1; //초기화 x
    int val2 = 10; // 초기화 10으로
}
public class InitMain {
    public static void main(String[] args) {
        InitData data = new InitData();
        System.out.println("data.val1 = " + data.val1);
        System.out.println("data.val2 = " + data.val2);
    }
}

null
참조형 변수에는 항상 객체가 있는 위치를 가리키는 참조값들이 있다. 그런데 아직 가리키는 대상이 없다면, null

public class Data1 {
    int val;
}
public class NullMain1 {
    public static void main(String[] args) {
        Data data = null;
        System.out.println("data = " + data);

        data = new Data();
        System.out.println("data = " + data);

        data = null;
        System.out.println("data = " + data);
    }
}

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

data = null

다시 data에 null을 할당 -> 인스턴스는 더이상 참조하지 X

GC(가비지 컬렉션)
data에 null을 할당, 생성한 data instance를 더이상 아무도 참조하지 않는다. 이렇게 아무도 참조하지 않게 되면 x001이라는 참조값을 다시 구할 방법X => 해당 인스턴스에 다시 접근 할 방법 X

아무도 참조 하지 않는 인스턴스는 더이상 사용X -> 메모리 용량만 차지. 따라서 GC가 더이상 사용하지 않느 ㄴ인스턴스라고 판단하고 해당 인스턴스를 자동으로 메모리에서 제거

NullPointerException
만약 참조값 없이 객체를 찾아 간다면? => 주소가 없다.

객체를 참조할 때는 .을 사용한다. 이렇게 하면 참조값을 사용해서 해당 객체를 찾아갈 수 있다.
그런데 참조값이 null이라면, 값이 없다는 이야기. => NullPointerException

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

    }
}

data 참조형 변수에는 null 값이 들어있다. 그런데 data.value = 10이라고 한다면?

data.value = 10
null.value = 10 //data에는 null 값이 들어있다

결과적으로 null 값은 참조할 주소가 존재하지 않는다는 의미. 따라서 참조할 객체 인스턴스가 존재하지 않으므로, nullPointerException -> 종료

멤버 변수와 null

public class Data1 {
    int val;
}
public class BigData {
    Data1 data1;
    int cnt;
}

BigData 클래스는 Data data, int cnt 두 변수를 갖는다.

public class NullMain3 {
    public static void main(String[] args) {
        BigData data = new BigData();
        System.out.println("data.cnt = " + data.cnt);
        System.out.println("data.data1 = " + data.data1);

        //nullPointException
        System.out.println("data.data1.val = " + data.data1.val);
    }
}


BigData를 생성하면 BigData의 인스턴스 생성, 이때 BigData 인스턴스의 멤버 변수에 초기화가 일어나는데, BigData의 data 멤버변수는 참조형이므로 null로 초기화. cnt 멤버변수는 숫자 0으로 초기화

public class NullMain4 {
    public static void main(String[] args) {
        BigData data = new BigData();
        data.data1 = new Data1(); // 참조값 형성
        System.out.println("data.cnt = " + data.cnt);
        System.out.println("data.data1 = " + data.data1);

        //nullPointException
        System.out.println("data.data1.val = " + data.data1.val);
    }
}

연습문제

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

        printOrder(orders);
        int total = getTotalAmount(orders);
        System.out.println("total = " + total);
    }

    public static ProductOrder createOrder(String name, int price, int quantity) {
        ProductOrder order = new ProductOrder();
        order.name = name;
        order.price = price;
        order.quantity = quantity;
        return order;
    }

    static void printOrder(ProductOrder[] orders) {
        for (ProductOrder order :
                orders) {

            if (order != null) {
                System.out.println("상품명 : " + order.name + "가격 : " + order.price + "수량 : " + order.quantity);
            }
        }
    }

    static int getTotalAmount(ProductOrder[] orders) {
        int total = 0;
        for (ProductOrder order :
                orders) {
            if (order != null) {
                total += order.price * order.quantity;
            }
        }
        return total;
    }
}
public class ProductOrder {
    int price;
    int quantity;
    String name;
}
public class Product {
    String name;
    int price;
    int quantity;
}
public class ProductMain {
    public static void main(String[] args) {
        System.out.print("입력할 주문의 개수를 입력하세요: ");
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        Product[] products = new Product[n];
        for (int i = 0; i < products.length; i++) {
            System.out.print((i + 1) + "번 째 주문 정보를 입력하세요");

            System.out.print("상품명 : " );
            String productName = sc.nextLine();

            System.out.println("가격 : ");
            int price = sc.nextInt();

            System.out.println("수량 : ");
            int quantity = sc.nextInt();
            products[i] = createProduct(productName, price, quantity);
        }

        print(products);
        int total = totalAmount(products);
        System.out.println(total);

    }

    static Product createProduct(String name, int price, int quantity) {
        Product product = new Product();
        product.name = name;
        product.price = price;
        product.quantity = quantity;
        return product;
    }

    static void print(Product[] product) {
        for (Product p : product) {
            if (p != null) {
                System.out.println("상품명 : " + p.name + "가격 : " + p.price + "수량 : " + p.quantity);
            }
        }
    }

    static int totalAmount(Product[] products) {
        int total = 0;
        for (Product product : products) {
            total = product.price * product.quantity;
        }
        return total;
    }
}
profile
개발자를 향해 가는 중입니다~! 항상 겸손

0개의 댓글