2.2.Java - 기본형과 참조형

sein lee·2024년 4월 9일
0

java-study

목록 보기
10/12
post-custom-banner

기본형 vs 참조형1 - 시작

사용하는 값을 변수에 직접 넣을 수 있는 기
본형, 그리고 이전에 본 Student student1 과 같이 객체가 저장된 메모리의 위치를 가리키는 참조값을 넣을 수 있는 참조형으로 분류할 수 있다.

  • 기본형(Primitive Type): int , long , double , boolean 처럼 변수에 사용할 값을 직접 넣을 수 있는 데이터 타입을 기본형이라 한다.
  • 참조형(Reference Type): Student student1 , int[] students 와 같이 데이터에 접근하기 위한 참조(주소)를 저장하는 데이터 타입을 참조형이라 한다. 참조형은 객체 또는 배열에 사용된다.
    - 객체는 . (dot)을 통해서 메모리 상에 생성된 객체를 찾아가야 사용할 수 있다.
    - 배열은 [] 를 통해서 메모리 상에 생성된 배열을 찾아가야 사용할 수 있다.

기본형은 연산이 가능하지만 참조형은 연산이 불가능하다.

int a = 10, b = 20;
int sum = a + b;
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...
  • 클래스는 대문자로 시작한다. 클래스는 모두 참조형이다.

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

대원칙: 자바는 항상 변수의 값을 복사해서 대입한다

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

=>참조형의 경우 실제 사용하는 객체가 아니라 객체의 위치를 가리키는 참조값만 복사된다. 쉽게 이야기해서 실제 건물이 복사가 되는 것이 아니라 건물의 위치인 주소만 복사되는 것이다. 따라서 같은 건물을 찾아갈 수 있는 방법이 하나 늘어날 뿐이다.

기본형과 변수 대입

<VarChange1.java>

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

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

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

참조형 변수 대입

<Data.java>

package ref;

public class Data {
    int value;
}

<VarChange2.java>

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 참조값=" + dataA);
            System.out.println("dataB 참조값=" + dataB);
            System.out.println("dataA.value = " + dataA.value); //10
            System.out.println("dataB.value = " + dataB.value); //10

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

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

}


참조값이 같다.

dataA와 dataB는 같은 참조값을 가지게 되고, 두 변수는 같은 객체 인스턴스를 참조하게된다.

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

기본형과 메서드 호출

<MethodChange1.java>

package ref;

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
    }
    static void changePrimitive(int x) {
        x = 20;
    }
}

1.
a , x 각각 숫자 10 을 가지고 있다.

2.
x 의 값만 20 으로 변경되고, a 의 값은 10 으로 유지

참조형과 메서드 호출

<MethodChange2.java>

package ref;

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


dataX 에 변수 dataA 의 값을 전달

Data dataX = dataA

변수 dataA 는 참조값 x001 을 가지고 있으므로 참조값을 복사해서 전달했다. 따라서 변수 dataA , dataX 둘다 같은 참조값인 x001 을 가지게 된다.
이제 dataX 를 통해서도 x001 에 있는 Data 인스턴스에 접근할 수 있다.

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

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

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

<Method1.java>

package ref;

public class Method1 {
    public static void main(String[] args) {
        Student student1 = new Student();
        initStudent(student1,"학생1", 16, 85);

        Student student2 = new Student();
        initStudent(student2, "학생2",19, 90);

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

}


주의

```java
package ref;

import class1.Student;

public class Method1 {
 ...
}
  • import class1.Student; 이 선언되어 있으면 안된다.

메서드에서 객체 변환

학생정보를 입력하는 부분이 중복으로 나오는데 이부분도 하나로 합쳐보자.
<Method2.java>

package ref;

public class Method2 {
    public static void main(String[] args) {

        Student student1 = createStudent("학생1", 16, 85);
        Student student2 = createStudent( "학생2",19, 90);

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

}

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

변수와 초기화

변수의 종류

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

변수의 값 초기화

  • 멤버변수 : 자동초기화
    • 인스턴스의 멤버 변수는 인스턴스를 생성할때 자동으로 초기화된다.
    • int =0, boolean = false, 참조형 =null
  • 지역변수 : 수동 초기화

null

null 값 할당

<NullMain1.java>

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;
        // data 변수는 앞서 만든 Data 인스턴스를 더는 참조하지 않는다.
        System.out.println("3.  data = "+data);
    }
}

GC(가비지 컬렉션)

data 에 null 을 할당하면 생성한 x001 Data 인스턴스를 더는 아무도 참조하지 않는다. 이렇게 아무도 참조하지 않게 되면 x001 이라는 참조값을 다시 구할 방법이 없다. 따라서 해당 인스턴스에 다시 접근할 방법이 없다.
-> 아무도 참조하지 않는 인스턴스는 메모리 낭비
-> 자바는 낭비되는 메모리를 자동으로 삭제해준다.

NullPointerException

null 을 가리키다(Pointer)인데, 이때 발생하는 예외(Exception)다.
null 은 없다는 뜻이므로 결국 주소가 없는 곳을 찾아갈 때 발생하는 예외이다.

객체를 참조할 때는 . (dot)을 사용한다. 이렇게 하면 참조값을 사용해서 해당 객체를 찾아갈 수 있다. 그런데 참조값이 null 이라면 값이 없다는 뜻이므로, 찾아갈 수 있는 객체(인스턴스)가 없다. NullPointerException 은 이처럼 null 에 . (dot)을 찍었을 때 발생한다.

<NullMain2.java>

package ref;

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

=> java.lang.NullPointerException 이 발생

멤버변수와 null

<NullMain3.java>

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);
        //null 값에 .(dot)을 찍으면 예외가 발생한다.
    }
}

=> null 에러

<NullMain4.java>

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

문제와 풀이

<ProductOrderMain2.java>

package ref.ex;

public class ProductOrderMain2 {
    public static void main(String[] args) {
        int total;
        ProductOrder[] productOrders = {
                createOrder("사과",2000,3),
                createOrder("바나나",1000,7)
        };
        printOrder(productOrders);
        total= getTotal(productOrders);
        System.out.println("총 금액 : "+total);

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

        return productOrder;
    }
    static void printOrder(ProductOrder[] orders){

        for (ProductOrder order :orders){
            System.out.println("이름 : "+order.name +"  가격 : "+ order.price+ "   수량 : "+order.quantity);
        }
    }

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

<ProductOrderMain3.java>

package ref.ex;

import java.util.Scanner;

public class ProductOrderMain3 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("입력할 주문의 개수를 입력하세요:");
        int count = input.nextInt();
        input.nextLine();

        ProductOrder[] productOrders = new ProductOrder[count];

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

            System.out.print("상품명: " );
            String name = input.nextLine();

            System.out.print("가격: ");
            int price = input.nextInt();

            System.out.print("수량: ");
            int quantity = input.nextInt();

            input.nextLine(); // 입력 버퍼를 비우기 위한 코드

            productOrders[i]=createOrder(name, price, quantity);
        }

        printOrder(productOrders);
        int total= getTotal(productOrders);
        System.out.println("총 금액 : "+total);

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

        return productOrder;
    }
    static void printOrder(ProductOrder[] orders){

        for (ProductOrder order :orders){
            System.out.println("이름 : "+order.name +"  가격 : "+ order.price+ "   수량 : "+order.quantity);
        }
    }

    public static int  getTotal(ProductOrder[] orders){
        int total =0;
        for(ProductOrder order: orders){
            total += order.price*order.quantity;
        }
        return total;
    }
}
profile
개발감자
post-custom-banner

0개의 댓글