05 참조 타입

winA·2025년 7월 1일

BE/Java

목록 보기
7/16
post-thumbnail

💛 데이터 타입 분류

참조 타입

객체의 번지를 참조하는 타입

종류

  • 배열
  • 열거
  • 클래스
  • 인터페이스

기본 타입과 참조 타입

타입 명특징
기본 타입값 자체를 저장
참조 타입객체가 생성된 메모리 번지를 저장

변수들은 모두 스택이라는 메모리 영역에 생성된다. 기본 타입 변수의 직접 값을 저장하고 있지만, 참조 타입 변수는 힙 메모리 영역의 String 객체 번지를 저장하고, 이 번지를 통해 String 객체를 참조한다.

int age=25;
double price=100.5;
String name="신용권";
String hobby="독서";

💛 메모리 사용 영역

java 명령어로 JVM이 구동되면 JVM은 운영체제에서 할당받은 메모리 영역을 다음과 같이 구분해서 사용한다.

  • method 영역: 바이트 코드 파일을 읽은 내용이 저장되는 영역이다.
  • 힙 영역: 객체가 생성되는 영역이다. 객체의 번지는 method 영역과 스택 영역의 상수와 변수에서 참조할 수 있다.
    • 힙에 생성된 데이터는 자동으로 0, false, null 등의 기본값으로 초기화된다.
  • 스택 영역: method를 호출할 때마다 생성되는 프레임이 저장되는 영역이다. method 호출이 끝나면 프레임은 자동 제거된다.
    • 프레임 내부에 로컬 변수 스택이 있는데, 여기에서 기본 타입 변수와 참조 타입 변수가 생성되고 제거된다.
    • 스택은 자동 초기화되지 않는다. 초기화를 생략하면 쓰레기 값이 그대로 남는다. 지역 변수는 반드시 초기화 후 사용해줘야 한다.

💛 참조 타입 변수의 ==, != 연산

참조 타입 변수의 ==, != 연산자는 값이 아닌 번지를 비교한다. 번지가 같다면 동일한 객체를 참조하는 것이고, 다르다면 다른 객체를 참조하는 것이다.

객체를 비교하는 코드는 if문에서 많이 사용된다.

package ch05.sec03;

public class ReferenceVariableCompareExample {

  public static void main(String[] args) {
    int[] arr1; //배열 변수 arr1 선언
    int[] arr2; //배열 변수 arr2 선언
    int[] arr3; //배열 변수 arr3 선언

    arr1 = new int[]{1, 2, 3}; //배열 { 1, 2, 3 }을 생성하고 arr1 변수에 대입
    arr2 = new int[]{1, 2, 3}; //배열 { 1, 2, 3 }을 생성하고 arr2 변수에 대입
    arr3 = arr2; //배열 변수 arr2의 값을 배열 변수 arr3에 대입
    
    System.out.println(arr1 == arr2); // arr1과 arr2 변수가 같은 배열을 참조하는지 검사
    System.out.println(arr2 == arr3); // arr2와 arr3 변수가 같은 배열을 참조하는지 검사
  }
}

같은 값이 저장돼있는 것 같아도 저장돼있는 객체 번지의 주소가 다르다면 서로 다른 배열로 판단된다.

스택은 자동으로 초기화 되지 않는다, 계속 초기화 해주면 거기에서 오는 시간 사용이 너무 크기 때문! 초기화를 해주지 않으면 어떻게 값을 초기화해야 하나? 예전에 사용하던 쓰레기 값이 그대로 남아있다. 그렇기 때문에 지역변수는 반드시 초기화 해줘야 한다

힙 영역은 자동으로 0으로 초기화 해준다

💛 null과 NullPointerException

null 값

참조 타입 변수는 아직 번지를 저장하고 있지 않다는 뜻

null로 초기화된 참조 변수는 스택 영역에 생성된다. 참조 타입 변수가 null 값을 가지는지 확인하려면 ==, != 연산을 수행하여 확인할 수 있다.

String refVar = null;
refVar == null; //결과: true
refVar != null; //결과: false

NullPointerException

변수가 null인 상태에서 객체의 데이터나 method를 사용하려 할 때 발생하는 예외

참조 변수를 사용하면서 가장 많이 발생하는 예외 중 하나이다.

int[] intArray = null;
intArray[0] = 10; //NullPointerException

intArray가 참조하는 배열 객체가 없으므로 10을 저장할 수 없기 때문에 예외가 발생한다.

이런 오류가 발생할 확률이 높은 null 값을 왜 사용할까?

null 값의 사용

Garbage Collector

자바는 쓰레기 객체를 모니터링하여 참조가 없는 객체를 자동으로 처리해준다. 쓰레기 수집기가 주기적으로 메모리를 검사하여 참조가 없는 객체를 제거하고, 이를 통해 메모리의 낭비를 막고 효율적으로 메모리를 관리할 수 있다.

개발자는 더이상 사용하지 않을 값에 대해서 null로 저장을 함으로써 Garbage Collector가 객체를 삭제하도록 유도하는 것이다.

null은 위험하지만 메모리 해제와 상태 표현에 있어서 매우 중요한 역할을 하는 값이다.

💛 문자열 타입

자바에서 문자열은 String 객체로 생성된다.

String 객체의 문자열을 변경이 불가능한 특성을 가지고 있다.

숫자인데, 의미는 숫자가 아닌 주민등록번호 같은 것은 문자열로 처리해줘야 한다. 형식만 숫자이지 각 자릿수마다의 의미가 따로 있는 문자의 특성을 띈다.

문자열 비교(==, !=, equals)

자바는 문자열 리터럴이 동일하다면 String 객체를 공유하도록 설계돼있다.

String name1="한지성";
String name2="한지성";

new 연산자로 직접 String 객체를 생성하여 사용할 수도 있다. new 연산자는 새로운 객체로 만드는 연산자로 객체 생성 연산자라고 한다.

String name1 = new String("한지성");
String name2 = new String("한지성");

package ch05.sec05_string;

public class EqualsExample {

  public static void main(String[] args) {
    String strVar1 = "한지성";
    String strVar2 = "한지성";

    if (strVar1 == strVar2) {
      System.out.println("strVar1과 strVar2는 참조가 같음");
    } else {
      System.out.println("strVar1과 strVar2는 참조가 다름");
    }

    if (strVar1.equals(strVar2)) {
      System.out.println("strVar1과 strVar2는 문자열이 같음");
    }

    String strVar3 = new String("한지성");
    String strVar4 = new String("한지성");

    if (strVar3 == strVar4) {
      System.out.println("strVar3과 strVar4는 참조가 같음");
    } else {
      System.out.println("strVar3과 strVar4는 참조가 다름");
    }
    
    if (strVar3.equals(strVar4)) {
      System.out.println("strVar3과 strVar4는 문자열이 같음");
    }
  }
}

문자열을 문자열 리터럴로 생성하느냐, new 연산자로 생성하느냐에 따라 비교 연산자의 결과가 달라질 수 있다. strVar1==strVar2true의 값이 나오지만 strVar3==strVar4는 서로 다른 객체이기 때문에 참조하는 주소의 번지가 달라 false 값이 나온다.

만약 내부 문자열만 동일한지 판단하고 싶다면 String 객체의 equals() method를 사용해서 판단해야 한다.

문자 추출(charAt)

charAt()method는 매개값으로 주어진 인덱스의 문자를 리턴한다.

String day = "월화수목금 토일";
char charVal = day.charAt(6); //토

공백도 하나의 문자로 판단한다.

문자열 길이(length)

length() method는 문자열 안에 있는 문자의 개수를 리턴한다.

String day = "월화수목금 토일";
int length = day.length(); //8

문자열 대체(replace)

replace() method는 문자열 내에서 특정 문자열을 다른 문자열로 대체할 수 있다. 기존 문자열은 그대로 두고, 대체한 새로운 문자열을 리턴한다. 기존 문자열에 변화는 없다.

package ch05.sec05_string;

public class ReplaceExample {

  public static void main(String[] args) {
    String oldStr = "자바 문자열은 불변입니다. 자바 문자열은 String입니다.";
    String newStr = oldStr.replace("자바", "JAVA");
    
    System.out.println(oldStr);
    System.out.println(newStr);
  }
}

해당 문자열 내에 대치되는 모든 문자열을 대체한다.

문자열 잘라내기(substring)

substring()method는 문자열에서 특정 위치의 문자열을 잘라내어 새로운 문자열을 반환한다.

method설명
substring(int beginIdx)beginIdx에서 끝까지 잘라내기
substring(int beginIdx, int endIdx)beginIdx에서 endIdx-1까지 잘라내기

문자열 찾기(indexOf)

indexOf() method는 문자열에서 특정 문자열이 시작되는 인덱스를 반환한다.

만약에 주어진 문자열이 문자열에 포함되어 있지 않다면 indexOf()는 -1을 반환함으로써 해당 문자열을 찾을 수 없음을 반환한다.

contains()

단순히 문자열이 포함되어 있는지를 판단한다. boolean 타입으로 포함하면 true, 포함하지 않으면 false를 리턴한다.

package ch05.sec05_string;

public class IndexOfContainsExample {

  public static void main(String[] args) {
    String subject = "자바 프로그래밍";

    int location = subject.indexOf("프로그래밍");
    System.out.println(location);
    String substring = subject.substring(location);
    System.out.println(substring);

    location = subject.indexOf("자바");
    if (location != -1) {
      System.out.println("자바와 관련된 책이군요.");
    } else {
      System.out.println("자바와 관련 없는 책이군요.");
    }
    
    boolean result = subject.contains("자바");
    if (result) {
      System.out.println("자바와 관련된 책이군요.");
    } else {
      System.out.println("자바와 관련 없는 책이군요.");
    }
  }
}

문자열 분리(split)

split()method는 문자열이 특정 구분자를 사용하여 여러개의 문자열로 구성이 돼있을 경우, 이를 따로 분리해준다.

String board = "번호,제목,내용,성명";
String boardArr = board.split(",");

💛 배열(Array) 타입

변수 하나에 하나의 값만 저장할 수 있다. 저장해야 할 값의 수가 많아지면 그만큼 많은 변수가 필요하게 된다. 이때 많은 변수를 일일히 관리하는 것은 매우 비효율적인 과정이다.

비슷한 특성을 가진 변수들을 관리하기 위해서 고안된 것이 배열이라는 개념이다.

정의

연속된 공간에 값을 나열시키고, 각 값에 인덱스를 부여해 놓은 자료구조

배열 변수 선언

int[] intArr;
double[] doubleArr;
String[] strArr;
int intArr[];
double doubleArr[];
String strArr[];

관례적으로 첫번째 방법을 표준 선언 방식으로 사용하고 있다.

타입[] 변수 = null;

배열 변수는 참조 변수이고, 배열은 객체이다. 배열은 힙 영역에 생성되고, 배열 변수는 힙 영역의 배열 주소를 저장한다. 참조할 배열이 아직 없다면 배열 변수도 null로 초기화해둘 수 있다.

값 목록으로 배열 생성

타입[] 변수 = {0,1,2,3, ...};

배열에 저장될 값의 목록이 있다면, 선언과 동시에 값을 넣어서 배열을 생성할 수 있다.

선언과 동시에 초기화를 진행해줘야 한다.

타입[] 변수;
변수 = {0,1,2,3, ...}; //컴파일 에러

만약 이렇게 선언된 배열 변수에 값 목록을 변수에 바로 대입한다면 컴파일 에러가 발생한다. 배열 변수를 선언한 시점과 값 목록이 대입되는 시점이 다르다면 new 타입[]을 중괄호 앞에 붙여줘야 한다.

변수 = new 타입[] {0,1,2,3, ...};

new 연산자로 배열 생성

값의 목록은 없지만 향후 값들을 저장할 목적으로 배열을 미리 생성할 수 있다. new 연산자를 다음과 같이 사용하면 배열 객체를 생성시킨다.

타입[] 변수 = new 타입[길이];
타입[] 변수 = null;
변수 = new 타입[길이];

길이는 배열이 저장할 수 있는 항목 수이다.

new 연산자로 배열을 처음 생성하면 배열 항목은 기본값으로 초기화가 된다.

데이터 타입초기값
기본 타입byte[]0
char[]'\u0000’
short[]0
int[]0
long[]0L
float[]0.0F
double[]0.0
boolean[]false
참조 타입클래스[]null
인터페이스[]null

배열 길이(.length)

코드에서 배열의 길이를 얻으려면 .연산자를 사용해서 참조하는 배열의 length 필드를 읽으면 된다.

배열변수.length;

length 필드는 읽기 전용이기 때문에 해당 필드의 값을 변경할 수는 없다.

for문을 사용할 때 조건식에 길이를 직접 넣는 것이 아닌 해당 필드를 이용해서 유동적으로 길이가 자동으로 잡히도록 해줘야 한다.

💛 다차원 배열

배열 항목에 또 다른 배열이 대입된 배열

각 차원의 배열은 하위 배열의 첫번째 인덱스 주소를 참조한다. 다차원 배열은 1차원 배열을 서로 연결한 구조이다.

arr[0]arr[1]는 단지 서로 다른 1차원 배열을 참조하고 있을 뿐이다. 물리적으로 연속적인 메모리에 존재할 필요는 없다. 자바에서 2차원 배열은 본질적으로 배열을 요소로 가지는 배열이다.

값 목록으로 다차원 배열 생성

배열 변수 선언 시 타입 뒤에 대괄호 []를 차원의 수만큼 붙이고, 값 목록도 마찬가지로 차원의 수만큼 중괄호를 중첩시킨다.

int[][] scores={
	{80,90,96},
	{76,78}
};
scores.length //반의 수:2
scores[0].length //첫번째 반의 학생 수:3
scores[1].length //두번째 반의 학생 수:2

반의 개수는 1차원 배열의 길이와 동일하고, 각 반의 학생 수는 2차원 배열의 길이와 동일하기 때문에 length필드로 반의 개수와 학생 수를 알 수 있다.

이중 루프로 모든 값을 순회

각 배열 요소는 각각 독립된 객체이기 때문에 배열의 길이도 서로 다를 수 있다. 그래서 반복문을 돌릴 때는 반드시 바깥쪽 배열과 안쪽 배열의 길이를 조건문마다 판단하면서 처리해야 한다.

for (int i = 0; i < scores.length; i++) {
    for (int j = 0; j < scores[i].length; j++) {
        System.out.println(scores[i][j]);
    }
}

new 연산자로 다차원 배열 생성

타입[][] 변수 = new 타입[1차원수][2차원수];
  1. 배열 변수 선언시 타입 뒤에 대괄호 []를 차원의 수만큼 붙인다.
  2. new 타입 뒤에도 차원의 수만큼 대괄호를 작성한다.

배열을 new로 선언하면 배열은 각 타입의 기본값으로 자동 초기화가 된다. 2차원 배열은 단순히 참조돼있는 것이기 때문에 길이가 달라도 전혀 문제가 되지 않는다.

참조를 실제 가리키고자 하는 값으로 바꾸고자 할 때, 참조 주소를 저장해야 할 곳에 또 다른 배열을 생성해주면 된다.

int[][] scores = new int[2][];
scores[0] = new int[3];
scores[1] = new int[2];

scores는 2개의 배열을 참조하고 있고, 각각은 서로 다른 길이의 배열을 가리키게 된다.

해당 형식을 사용해서 길이와 참조주소가 정해지게 된다.

💛 객체를 참조하는 배열

배열에는 모든 타입이 저장될 수 있다. 배열의 값이 클래스 타입이라면 기본형타입과 구동방식은 똑같지만 값이 저장되는 것이 아니라 참조가 저장되는 것이다.

package ch05.sec08_arrref;

public class ArrayReferenceObjectExample {

  public static void main(String[] args) {
    String[] strArray = new String[3];
    strArray[0] = "Java";
    strArray[1] = "Java";
    strArray[2] = new String("Java");
    
    System.out.println(strArray[0] == strArray[1]); //true: 같은 객체 참조
    System.out.println(strArray[0] == strArray[2]); //false: 다른 객체를 참조
    System.out.println(strArray[0].equals(strArray[2])); //true: 문자열이 동일
  }
}

💛 배열 복사

배열은 한 번 생성하면 길이를 변경할 수 없는 특성을 가지고 있다. 그런데 코드를 진행하는 중에 배열의 크기를 변경해서 새로운 데이터를 저장해야 하는 상황이 올 수도 있다. 이때는 원하는 크기의 배열을 새로 만들고 이전 배열로부터 항목들을 복사해서 생성해야 한다.

반복문 복사

for문을 이용해서 항목을 하나씩 읽고 새로운 배열에 저장한다.

package ch05.sec09_arrcopy;

public class ArrayCopyByForExample {

  public static void main(String[] args) {
//길이 3인 배열
    int[] oldIntArray = {1, 2, 3};
//길이 5인 배열을 새로 생성
    int[] newIntArray = new int[5];
//배열 항목 복사
    for (int i = 0; i < oldIntArray.length; i++) {
      newIntArray[i] = oldIntArray[i];
    }
//배열 항목 출력
    for (int i = 0; i < newIntArray.length; i++) {
      System.out.print(newIntArray[i] + ", ");
    }
  }
}

method 복사(arraycopy)

Systemarraycopy() method를 사용해서 한줄만에 배열 복사를 진행할 수 있다.

System.arraycopy(Object src, int srcPos, Object dest, int destPost, int length)
//길이 3인 배열
    String[] oldStrArray = {"java", "array", "copy"};
//길이 5인 배열을 새로 생성
    String[] newStrArray = new String[5];
//배열 항목 복사
    System.arraycopy(oldStrArray, 0, newStrArray, 0, oldStrArray.length);

💛 배열 항목 반복을 위한 향상된 for문

for(타입 변수: 배열){
	실행문;
}
  1. 배열에서 가져올 항목이 있을 경우에 변수에 항목을 저장한다.
  2. 실행문을 실행한다.
  3. 배열에서 가져올 다음 항목이 있는지 확인한다.

각 배열의 값이 궁금하여 처리하고 싶을 때 사용한다. 인덱스의 값을 알 수 없다.

💛 main()method의 String[] 매개변수 용도

main() method에서 매개변수로 왜 String[] args가 필요한가?

자바 프로그램은 기본적으로 main() method부터 실행된다. 이 때 String[] args를 통해서 외부에서 값을 넘길 수 있다. String[] args는 명령행 인자를 받아서 프로그램 안에서 활용할 수 있도록 해주는 역할을 한다.

실행 인수 설정하기

💛 열거(Enum) 타입

한정된 값을 갖는 타입

사용법

  1. 열거 타입 이름으로 소스 파일(.java)을 생성한다.
  2. 해당 파일에 한정된 값을 코드로 정의한다.

열거 타입 이름

  • 알파벳으로 정의한다.
  • 모두 대문자로 작성한다.
  • 여러 단어로 구성될 경우 _로 연결한다.

열거 타입도 하나의 데이터 타입이므로 변수를 선언하고 사용해야 한다.

Calendar

컴퓨터의 날짜 및 요일, 시간을 얻을 때 사용한다.

Calendar now=Calendar.getInstacne();
int year=now.get((Calendar.YEAR);
int month=now.get((Calendar.MONTH);
int day=now.get((Calendar.DAY_OF_MONTH);
int week=now.get((Calendar.DAY_OF_WEEK);
int minute=now.get((Calendar.MINUTE);
int seconde=now.get((Calendar.SECOND);

0개의 댓글