✍ Today I Learned
- 오늘의 고비는 메모리 사용 영역과 다차원 배열이었는데, 글이 이해가 안 가서 그림을 찾아보고, 그림에 궁금한 부분이 있어서 동영상을 찾아보고 하다 보니 어느 정도 틀은 잡힌 것 같다.
- 안다고 생각했지만 사실 그건 아는 게 아니었다. 그 생각 자체가 오만 아닐까? 깊게 파고들수록 어려운 것 같다. 근데 너무 재밌어..!
05 참조타입
05-1 참조 타입과 참조 변수
기본 타입과 참조 타입
- 기본 타입(primitive type): 정수, 실수, 문자, 논리 리터럴을 저장하는 타입
- 참조 타입(reference type): 배열, 열거, 클래스, 인터페이스를 말하며, 객체(object)의 번지를 참조하는 타입
- 기본 타입 변수와 참조 타입 변수의 차이점은?
- 기본 타입 변수는 실제 값을 변수 안에 저장한다.
- 참조 타입 변수는 메모리의 번지를 변수 안에 저장한다. → 번지를 통해 객체를 참조(힙 영역)
메모리 사용 영역(Runtime Data Area)
- 메소드 영역(Method Area)
- JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역
- 클래스(~.class)의 바이트 코드들이 저장된다.
- 코드에서 사용되는 클래스(~.class)들을 클래스 로더로 읽어 클래스별로 정적 필드(static field)와 상수(constant), 메소드 코드, 생성자(constructor) 코드 등을 분류해서 저장한다.
- 힙 영역(Heap Area)
- 객체와 배열이 생성되는 영역
- 클래스로부터 생성된 객체들이 저장된다.
- 여기에 생성된 객체와 배열은 JVM 스택 영역의 변수나 다른 객체의 필드에서 참조한다.
- 만약 참조하는 변수가 없다면? JVM이 쓰레기 수집기(Garbage Collector)를 실행시켜 자동으로 제거한다. 따라서 객체를 제거하기 위한 별도 코드가 필요 없다. (어차피 제공도 안 한다.)
- JVM 스택(Stack)
- 메소드를 호출할 때마다 프레임(Frame)을 추가(push)하고 메소드가 종료되면 해당 프레임을 제거(pop)하는 동작을 수행한다.
- 버킷 모양으로 프레임이 쌓여서 저장된다.
- 프레임 내부에 있는 로컬 변수 스택에 기본 타입 변수와 참조 타입 변수가 추가(push)되거나 제거(pop)된다. → 이 시점은 변수가 초기화될 때이고, 변수가 선언된 블록을 벗어나면 스택에서 제거된다.
- 프레임 안에도 버킷 모양 → 메서드가 호출할 때 사용되는 변수들이 저장된다.
- 메소드 실행 종료되면 프레임 자체가 제거된다.
참조 변수의 ==, != 연산
- 기본 타입 변수는 값을 비교하지만, 참조 타입 변수는 동일한 객체를 참조하는지 비교한다.
- 2022.03.13 - [Java] == vs equals
null과 NullPointerException
- 참조 타입 변수는 힙 영역의 객체를 참조하지 않는다는 뜻으로 null 값을 가질 수 있다. null로 초기화된 참조 변수는 스택 영역에 생성된다.
refVar1 == null // false
refVar2 == null // true
- 예외(Exception)란? 자바 프로그램을 실행 도중에 발생하는 오류
- 참조 변수가 null값을 가지고 있는데, 있지도 않은 객체의 데이터(필드)나 메소드를 사용하는 경우 NullPointerException이 발생한다.
NullPointerException 예시
int[] intArray = null;
intArray[0] = 10; // NullPointerException 발생
- intArray는 참조 변수 이므로 null로 초기화가 가능하다.
- intArray 변수가 참조하는 배열 객체가 없기 때문에 intArray[0]에 10을 저장하려고 하면 NullPointerException이 발생하는 것이다.
String str = null;
System.out.println("총 문자수: " + str.length()); // NullPointerException 발생
- String은 클래스이므로 참조 타입이다. 따라서 null로 초기화가 가능하다.
- 마찬가지로 변수가 참조하는 String 객체가 없기 때문에 String 객체의 length() 메소드를 호출하면 NullPointerException이 발생한다.
- 해결방법은? 참조 변수를 추적해서 객체를 참조하도록 수정한다.
String 타입
- ‘문자열을 String 변수에 저장한다?’ → 사실 틀린 표현이다. 문자열이 직접 변수에 저장되는 것이 아니라, String 객체로 생성되고 변수는 String 객체를 참조하는 것이다.
- 문자열 리터럴이 동일하다면, 새로 메모리 공간을 할당하지 않고 동일한 String 객체를 참조하게 된다.
- new 연산자(객체 생성 연산자)로 String 객체를 생성했을 경우, 동일한 문자더라도 서로 다른 String 객체를 참조하게 된다.
- 참조하는 주소가 아닌 내부 문자열을 비교하고 싶을 때는 equals() 메소드를 사용한다.
boolean result = str1.equals(str2)
05-2 배열
배열이란?
- 배열은 같은 타입의 데이터를 연속된 공간에 나열하고, 각 데이터에 인덱스(index)를 부여해놓은 자료구조이다.
- 인덱스는 각 항목에 데이터를 읽거나 저장하는 데 사용되며 배열 이름 옆 대괄호[]에 기입된다. 인덱스는 0부터 시작한다.
- 배열의 특징
- 같은 타입의 데이터만 저장할 수 있다.
- 한 번 생성된 배열은 길이를 늘리거나 줄일 수 없다.
배열 선언과 생성
// 배열 선언
int[] intArray;
String[] strArray;
// 배열 생성 -> 값 목록을 이용하거나 new 연산자 이용
String[] names = {"홍길동", "김자바", "오늘"};
strArray = new String[] {"홍길동", "김자바", "오늘"};
double[] doubleArray = new double[5]; // 길이가 5인 double 배열
값의 목록으로 배열을 생성할 때 주의할 점?
- 배열 변수를 이미 선언한 후에는 다른 실행문에서 중괄호를 사용한 배열 생성이 허용되지 않는다.
- 배열 변수를 미리 선언했다면 new 연산자를 사용해서 값 목록을 지정해주자.
- 메소드의 매개값이 배열일 경우도 마찬가지이다.
String[] names;
names = {"홍길동", "김자바", "오늘"}; // 컴파일 에러
int add(int[]scores) {...}
int result = add({95, 85, 90}); // 컴파일 에러
int reuslt = add(new int[] {95, 85, 90};
new 연산자로 배열을 생성할 경우 기본값
- new 연산자로 배열을 생성할 경우 배열은 자동적으로 기본값으로 초기화된다.
- int 배열이라면 0, String 배열이라면 null
배열의 타입 | 초깃값 |
char | '\u0000' |
byte, short, int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 또는 0.0D |
boolean | false |
배열, 인스턴스 등 | null |
배열 길이
int[] intArray = {10, 20, 30}
int num = intArray.length;
명령 라인 입력
- main() 메소드의 매개값인 String[] args는 왜 필요한가?
- 명령 프롬프트에서 java 명령어로 코드를 실행하면 JVM은 길이가 0인 String 배열을 먼저 생성하고 main() 메소드를 호출할 때 매개값으로 전달한다.
- 다음 예제를 통해 알아보자.
package chap05.sec02.exam05;
public class MainStringArrayArgument {
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("값의 수가 부족합니다.");
System.exit(0); // 프로그램 강제 종료
}
// args의 데이터 얻기
String strNum1 = args[0];
String strNum2 = args[1];
// 문자열을 정수로 변환
int num1 = Integer.parseInt(strNum1);
int num2 = Integer.parseInt(strNum2);
int result = num1 + num2;
System.out.println(num1 + " + " + num2 + " = " + result);
}
}
- 그냥 실행하면 “값의 수가 부족합니다”를 출력하고 강제 종료한다.
- 인텔리제이에서 실행할 때 매개값을 주는 방법은 다음과 같다.
- Run → Edit Configurations 클릭
- Class 이름을 잘 확인하고, arguments 입력란에 10 20을 입력한 후 Apply
- 이제 다시 실행해보면 10 + 20 = 30이 출력된다.
다차원 배열
- 값들이 행과 열로서 구성된 배열을 2차원 배열이라고 한다. (수학의 행렬과 비슷)
- 자바는 2차원 배열을 중첩 배열 방식으로 구현한다.
- 아래 코드는 메모리에 다음과 같이 3개의 배열 객체를 생성한다.
int[][] scores = new int[2][3];
// 값 목록으로도 나열할 수 있음
int[][] mathScores = {{95, 80}, {92, 96}};
다차원 배열의 계단식 구조
- 자바는 1차원 배열이 서로 연결된 구조로 다차원 배열을 구현하기 때문에 수학 행렬 구조가 아닌 계단식 구조를 가질 수 있다.
- 이런 형태의 배열에서 주의할 점?
- 배열의 정확한 길이를 알고 인덱스를 사용해야 한다.
객체를 참조하는 배열
- 기본 타입 배열은 각 항목에 직접 값을 갖고 있지만, 참조 타입(클래스, 인터페이스)은 각 항목에 객체의 번지를 가지고 있다.
- 따라서 String[] 배열의 항목도 결국 Stirng 변수와 동일하게 취급되어야 한다.
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문을 사용하여 복사
- Systme.arraycopy() 메소드를 사용하여 복사
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
// 순서대로 원본 배열,
// 원본 배열에서 복사할 항목의 시작 인덱스,
// 새 배열,
// 새 배열에서 붙여넣을 시작 인덱스,
// 복사 할 개수
향상된 for문 == forEach문
- 루프 카운터변수와 증감식을 사용하지 않고 배열 및 컬렉션 항목의 개수만큼 반복한다.
for(타입 변수:배열) {
... 실행문;
}
※ 이 내용은 책 [혼자 공부하는 자바]를 보고 정리한 내용입니다.
'📝 TIL' 카테고리의 다른 글
[TIL] 1주차 미니 프로젝트ㅣ시작 (2) | 2022.11.15 |
---|---|
[TIL] Java 열거 타입(Enum) (4) | 2022.11.12 |
[TIL] Java 조건문, 반복문, break, continue (0) | 2022.11.10 |
[TIL] 항해99 입학시험 (0) | 2022.11.09 |
[TIL] Java 연산자의 종류, 우선순위, 타입 변환 (2) | 2022.11.08 |