안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.
배열이란?
- 동일한 자료형을 묶어 저장하는 참조 자료형
배열 사용 이유?
만약 국어/수학/영어/사회/과학의 점수를 계산하는 코드가 있다고 가정해보자.
배열을 사용하지 않으면 총 5개의 변수를 생성하여 각각 관리해야 하니 가독성도 떨어지고 추후 관리도 어려울 것입니다.
배열을 사용하면 변수의 수를 줄여주어 가독성도 높아지고, 관리가 수월해집니다.
배열 사용 순서
//int kor, math, eng, soc, sci;
//배열 사용하지 않으면 변수를 다 지정해줘야함
//1. 배열 선언
int[] subjects;
// int subjects[];
//2. 배열 할당
subjects = new int[5];
//3. 값 대입 (인덱스 번호 사용)
subjects[0] = 100;
subjects[1] = 95;
subjects[2] = 80;
subjects[3] = 75;
subjects[4] = 84;
//4. 사용 (출력 등)
//하나하나 출력하는 방법
System.out.println(subjects[0]);
System.out.println(subjects[1]);
System.out.println(subjects[2]);
System.out.println(subjects[3]);
System.out.println(subjects[4]);
System.out.println("===for문 이용===");
//for문 이용
for(int i = 0; i < subjects.length; i++) {
System.out.println(subjects[i]);
}
@콘솔출력값
100
95
80
75
84
===for문 이용===
100
95
80
75
84
배열을 선언하는 데에는 2가지 방법이 있습니다.
변수 선언 방법 | |
자료형[] 변수명; | 자료형 변수명[]; |
자바에서는 자료형 뒤에 배열을 표시하는 []를 사용하는 것을 대부분 권장합니다.
배열 선언 및 할당 의미 | |
자료형[] 변수명; 변수명 = new 자료형[배열의 길이]; |
int[] subjects; subjects = new int[5]; |
int 자료형 5개를 저장할 수 있는 공간을 heap메모리에 넣어두고, 그 위치 값을 참조 변수 subjects에 저장하라! |
배열 생성 시, 배열의 길이를 반드시 지정해줘야 합니다.
※ 값 대입이 동시에 이뤄지는 경우, 중괄호 안의 초기화 데이터 개수로 배열의 길이가 정해집니다.
위와 같이 배열을 선언, 할당, 값 대입 하였을 때 메모리 공간은 아래와 같습니다.
stack은 access by name, 즉 이름으로 stack에 접근할 수 있고
heap은 access by address, 즉 주소로 heap에 접근할 수 있습니다.
subjects라는 배열 참조 변수에 heap에 위치해있는 객체의 주소값이 담겨있을 겁니다.
따라서 객체를 꺼내기 위해선 반드시 그 위치값을 갖고 있는 변수가 필요한 것입니다.
※ new 키워드를 이용해 객체를 생성하면 JVM은 힙 메모리 내에 비어 있는 공간에 객체를 생성합니다.
비어 있는 공간은 그 때마다 다를 것이므로 객체가 생성될 때마다 다른 위치에 저장될 수 있습니다.
따라서 JVM이 생성한 객체의 위치를 반드시 알아야 개발자가 해당 객체를 사용할 수 있습니다.
heap메모리 영역의 강제 초기화
위 코드에서 값 대입을 주석 처리 해보겠습니다.
//1. 배열 선언
int[] subjects;
//2. 배열 할당
subjects = new int[5];
//3. 값 대입 (인덱스 번호 사용)
// subjects[0] = 100;
// subjects[1] = 95;
// subjects[2] = 80;
// subjects[3] = 75;
// subjects[4] = 84;
//4. 사용
System.out.println(subjects[0]);
System.out.println(subjects[1]);
System.out.println(subjects[2]);
System.out.println(subjects[3]);
System.out.println(subjects[4]);
@콘솔출력값
0
0
0
0
0
값 대입을 하지 않았는데도 사용하는 데에 오류가 나지 않고 오히려 값이 출력이 됩니다.
int a;
System.out.println(a);
기본 자료형인 int로 치자면, 위와 같은 코드와 동일한 셈입니다.
분명 값 대입 없이 변수를 호출해 사용하려고 하면 애초에 a라는 변수에 빨간 줄이 쫙 그어지게 되고, 에러가 발생합니다.
그 이유는?
스택 메모리 공간은 값을 초기화(대입)하지 않으면 빈 공간으로 존재하여 변수 출력 시 오류가 발생됩니다.
하지만 힙 메모리는 절대 빈 공간이 들어올 수 없습니다. 따라서 값을 주지 않으면 컴파일러가 값을 강제로 초기화 해줍니다.
따라서 값 대입을 하지 않아도 강제 초기화 되어 0이 출력 되었습니다.
※ 기본 자료형일 때 숫자는 모두 0 (실수는 0.0), 불리언은 false, 문자형은 ' ' (공백)
참조 자료형일 때는 모두 null로 초기화
다양한 배열 생성 방법
//방법 1) 배열 객체 생성 후 값 대입
int[] subjects1 = new int[5];
subjects1[0] = 100;
subjects1[1] = 95;
subjects1[2] = 80;
subjects1[3] = 75;
subjects1[4] = 84;
//방법 2) 배열 객체 생성과 동시에 값 대입 (길이 지정 X)
int[] subjects2 = new int[] {100, 95, 80, 75, 84};
//방법 3) 대입할 값만 입력
int[] subjects3 = {100, 95, 80, 75, 84};
단 방법 3의 경우, 변수 선언과 값의 대입을 분리할 수 없다는 제약 조건이 따릅니다.
선언과 대입을 분리할 수 없다는 특징으로 인해 메서드의 입력매개변숫값으로는 사용할 수 없습니다.
다양하게 배열 생성 후 값 출력하기
public void test3() {
//방법 2 + for문 사용
char[] chArr = new char[] {'a', 'b', 'c'};
for(int i = 0; i < chArr.length; i++) {
System.out.print(chArr[i] + " ");
}
System.out.println();
//방법 3 + for-each문 사용
double[] dArr = {2.3, 7.25, 8.99, 10.23, 5.5};
for(double d : dArr) {
System.out.print(d + " ");
}
}
@콘솔출력값
a b c
2.3 7.25 8.99 10.23 5.5
배열의 값을 출력해올 때는 인덱스 번호를 이용한 for문을 통해 쉽게 값을 출력 해 올 수 있습니다.
더불어 for문의 향상된 버전, for-each를 통해서도 값 출력이 가능합니다.
for-each문
- 배열이나 컬렉션 등의 집합 객체에서 원소들을 하나씩 꺼내는 과정을 반복하는 구문
- 집합 객체의 원소들을 출력할 때 사용
//기본 구문
for(자료형 요소를 담을 변수 : 집합 객체) {
//실행구문
}
String[] 참조형 배열
- String 참조형도 배열 생성이 가능
- 참조형의 기본 값은 null!
public void test5() {
String[] strArr = new String[3];
strArr[0] = "홍길동";
strArr[1] = "심사임당";
strArr[2] = "세종대왕";
for(String a : strArr) {
System.out.print(a + " ");
}
}
@콘솔출력값
홍길동 심사임당 세종대왕
String 참조형도 마찬가지로 배열로 생성 가능 및 for-each문으로 출력 가능합니다.
규칙이 있는 값 대입하기 (1~10, 11~20)
public void test4() {
int[] arr1 = new int[10];
for(int i = 0; i < arr1.length; i++) {
arr1[i] = i+1;
}
for(int a : arr1) {
System.out.print(a + " ");
}
System.out.println();
int[] arr2 = new int[10];
int k = 20;
for(int i = 0; i < arr2.length; i++) {
// arr2[i] = 20-i;
arr2[i] = k--;
System.out.print(arr2[i] + " ");
}
}
@콘솔출력값
1 2 3 4 5 6 7 8 9 10
20 19 18 17 16 15 14 13 12 11
알파벳 소문자를 담은 alpha를 생성하고 인덱스별로 출력
public void test6() {
char[] alpha = new char[26];
char ch = 'a';
for(int i = 0; i < alpha.length; i++) {
alpha[i] = ch++;
System.out.print(alpha[i] + " ");
}
}
@콘솔출력값
a b c d e f g h i j k l m n o p q r s t u v w x y z
규칙이 있는 점을 이용하여 값 대입 및 출력을 하였습니다.
☞ 배열의 특징
- 동일한 자료형만 묶을 수 있는 자료형
- 배열 생성 시, 배열의 길이를 반드시 지정해줘야 함
- 생성 후 크기 변경 불가
1) 할당한 크기에 초과 하는 경우 : ArrayIndexOutOfBoundsException 에러 발생
2) 할당한 크기에 못 미치는 경우 : 엄청난 메모리 낭비 발생
☞ 객체의 특징
- 객체마다 고유한 식별자를 갖고 있음 (hashcode) : 기본형에는 존재하지 않음
- 직접 삭제 불가, 대신 참조형 변수에 null을 대입하는 경우 삭제로 간주
(참조 주소값을 null로 지정한 것은 heap영역 객체로의 접근을 끊어 버린 것)
- 연결이 끊어진 객체는 Garbage Collector의 삭제 대상이 되어 곧 삭제 됨 (메모리 반환)
배열 및 객체의 특징 확인하기
public void test7() {
int[] arr = new int[] {1, 2, 3, 4, 5};
arr = new int[10];
}
처음에는 int 자료형 5개를 담을 수 있는 공간을 만들고 arr 참조변수에 주소값을 전달해주었습니다.
하지만, 10개의 공간이 더 필요하여 5개의 공간에서 10개의 공간으로 변경하고 싶은 의미로 arr = new int[10]; 을 하였습니다.
다만 배열은 한 번 생성 후 절대로 크기 변경이 불가한 특징이 있습니다.
언뜻 보기엔 5개의 공간에서 10개의 공간으로 변경된 것 같지만 실제 메모리 상에선 아닙니다.
int[ ] arr = new int[ ] {1, 2, 3, 4, 5}; 시 메모리 구조는 아래와 같습니다.
heap영역의 int 자료형 5개를 저장할 수 있는 공간이 생성 되고 그 주소값을 stack영역의 참조변수 arr이 갖고 있습니다.
하지만 여기서 arr = new int[10]; 을 한다면 메모리 구조는 아래와 같습니다.
기존 5개의 공간이 10개의 공간으로 변경된 것이 아닌 10개의 공간을 가진 객체가 새로 생성되고,
그에 대한 주소값을 arr이 다시 얻게 되는 것입니다.
stack은 하나의 값만을 가질 수 있기 때문에 기존 5개의 공간의 연결이 끊기게 되고, 추후 삭제가 되는 것이죠.
이뿐만 아니라, 객체의 특징 중 하나인 hashcode를 이용하여 서로 다른 객체라는 것을 알 수 있습니다.
//해쉬코드 출력
int[] arr = new int[] {1, 2, 3, 4, 5};
System.out.println(arr.hashCode());
arr = new int[10];
System.out.println(arr.hashCode());
@콘솔출력값
1952779858
366004251
서로의 hashcode(고유의 식별자)가 다르게 출력 된 것을 보면, 서로 다른 객체를 가르키고 있다는 것을 알 수 있습니다.
System.out.println(arr)
@콘솔출력값
[I@4361bd48
arr만 출력하면 위와 같은 [I@4361bd48 이 출력되는데,
이는 @를 중심으로 뒤 숫자 및 문자들은 해쉬코드를 16진수한 값
@ 앞에는 [ → 배열 의미, I → int형을 의미합니다.
또한, 객체는 직접 삭제가 불가하여 참조 변수를 null로 지정하여 연결을 끊어주는 것을 삭제로 간주하는데요!
이도 코드로 알아보겠습니다.
//객체 삭제
arr = null;
System.out.println(arr.length);
System.out.println(arr[0]);
System.out.println(arr.hashCode());
@콘솔출력값
출력없음
arr을 null로 지정하면, 변수에 담긴 값을 null, 즉 빈 공간으로 만드는 것이므로 갖고 있던 주소값이 사라지게 됩니다.
따라서 객체를 가르킬 수 없는, 도달할 수 없는 상태가 되는 것입니다.
이 상황에서 arr의 길이, 0번지의 값, hascode를 가져오려고 하니 NullPointerException이 발생하게 되는 것입니다.
'Java > Java' 카테고리의 다른 글
반복문) 중첩 반복문, 이중for문, 레이블 (0) | 2022.03.09 |
---|---|
배열) 얕은 복사, 깊은 복사 (0) | 2022.03.09 |
제어 키워드) break, continue 제어 키워드 (0) | 2022.03.08 |
반복문) while문, do-while문, while문 무한루프 (0) | 2022.03.07 |
반복문) for문, for문 무한루프 (0) | 2022.03.07 |