Preface
이번 장에선 데이터 타입의 종류를 공부했다.
숫자의 타입 변환은 조금 익숙해졌다 싶었는데, 이번엔 배열을 생성하는 방법이 헷갈린다.
배열을 생성함과 동시에 값을 할당하는 방법, 선언한 후 값을 할당하는 방법, 배열의 크기만 설정한 후 값을 할당하는 방법 등 배열 객체를 생성하는 방법이 너무 다양하다.
또, 숫자형에서 문자열로, 문자열에서 숫자형으로 데이터 타입을 변환하는 방법도 계속 잊어버린다.
이 외에도 배열의 값을 출력하면 "[I@6bf2d08e"과 같은 이상한 값이 출력되어 올바른 값을 출력하는 방법도 따로 찾아봤다.
자바를 공부하다 보니 초보자는 자바와 C같은 언어보다 파이썬을 먼저 공부하라는 말이 100% 이해된다.
1. 데이터 타입 분류
- 데이터 타입
1) 기본(원시: primitive) 타입: 정수, 실수, 문자, 논리 리터럴을 저장하는 타입
→ 실제 값을 변수 안에 저장한다.
2) 참조(reference) 타입: 객체의 번지를 참조하는 타입으로 배열, 열거, 클래스, 인터페이스 타입 등
→ 변수가 메모리의 번지를 값으로 갖는다.
- 변수는 스택 영역에, 객체는 힙 영역에 생성된다.
2. 메모리 사용 영역
- 자바 메모리 영역의 분류
1) 메소드 영역
2) 힙 영역
3) JVA 스택 영역
- 메소드 영역: 코드에서 사용되는 클래스들을 클래스 로더로 읽어 클래스별로 런타임 상수풀, 필드 데이터, 메소드 데이터, 메소드 코드, 생성자 코드 등을 분류해서 저장한다.
→ JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역이다.
- 힙 영역: 객체와 배열이 생성되는 영역
1) JVM 스택 영역의 변수나 다른 객체의 필드에서 참조한다.
2) 쓰레기 객체를 힙 영역에서 자동으로 제거한다.
- JVM 스택 영역: 각 스레드마다 하나씩 존재하며 스레드가 시작될 때 할당된다.
1) 메소드를 호출할 때마다 프레임(frame)을 추가(push)하고 메소드가 종료되면 해당 프레임을 제거(pop)하는 동작을 수행한다.
2) 예외 발생 시 printStackTrace( ) 메소드로 보여주는 Stack Trace의 각 라인은 하나의 프레임으로 표현한다.
- 프레임: 메소드 상태 정보를 저장하는 곳
1) Local Variables
2) Operand Stack: 메소드 내의 계산을 위한 작업 공간
3) Constant Pool Reference
- 스택과 큐 비교
1) stack: Last In First Out
2) que: First In First Out
- 자바에서는 배열을 객체로 취급한다.
- 참조 타입 변수는 null 값을 가질 수 있다.
1) null값도 초기값으로 사용할 수 있기 때문에 null로 초기화된 참조 변수는 스택 영역에 생성된다.
2) 변수 자체는 스택 영역에, 실질적인 값은 힙 영역에 생성된다.
- 예외(exception): 프로그램 실행 도중에 발생하는 오류
- NullPointerException: 예외가 발생된 곳에서 객체를 참조하지 않은 상태로 참조 타입 변수를 사용할 때 발생하는 예외
3. String 타입
- 자바는 문자열 리터럴이 동일하면 String 객체를 공유한다.
→ 동일한 문자열이라도 new 연산자로 생성한 문자열은 다른 객체를 참조한다.
- 일반적으로 변수에 문자열을 저장할 땐 문자열 리터럴을 사용한다.
- new 연산자: 힙 영역에 새로운 객체를 만들 때 사용하는 연산자
→ 객체 생성 연산자와 동의어이다.
- == 연산자는 변수에 저장된 객체의 번지가 동일한지를 검사한다.
→ 문자열 자체를 비교할 땐 String 객체의 equals( ) 메소드를 사용해야 한다.
- 특정 변수가 객체를 참조하는 상태에서, null을 대입함으로써 더 이상 객체를 참조하지 않도록 할 수 있다.
→ 쓰레기 객체로 취급되어 메모리에서 자동으로 제거된다.
4. 배열 타입
- 배열: 같은 타입의 데이터를 연속된 공간에 나열시키고, 각 데이터에 인덱스를 부여한 자료구조
1) 같은 타입의 데이터만 저장할 수 있다.
2) 한 번 생성된 배열은 길이를 늘리거나 줄일 수 없다.
- 배열 변수를 선언할 땐 대괄호([ ])를 사용하는데, 이는 타입 뒤에 붙을 수도 있고 변수 뒤에 붙을 수도 있다.
타입[ ] 변수;
타입 변수[ ];
타입[ ] 변수 = null; // 참조할 배열 객체가 없는 경우
- 값 목록으로 배열을 생성하는 코드
package ch5;
public class arrayCreateByValueList1 {
public static void main(String[] args) {
int[] scores = { 83, 90, 87 };
System.out.println("scores[0]: " + scores[0]);
System.out.println("scores[1]: " + scores[1]);
System.out.println("scores[2]: " + scores[2]);
int sum = 0;
for (int i = 0; i < 3; i++) {
sum += scores[i];
}
System.out.println("sum: " + sum);
double avg = (double) sum / 3;
System.out.println("avg: " + avg);
}
}
- 배열 변수를 이미 선언한 후에 다른 실행문에서 중괄호를 사용한 배열 생성은 불가능하다.
→ new 연산자를 사용하면 가능하다.
String[] names = null;
names = new String[] { "a", "b", "c" };
- 배열을 생성하며 값을 할당하면 new 키워드가 필요 없지만, 배열 생성 후 값을 할당할 땐 new 키워드를 사용해야 한다.
- 배열 자체는 "Array.toString(변수명)" 형식을 사용해야 출력된다.
→ 배열의 값을 for문을 통해 다른 변수에 담아서 출력할 수도 있다.
1) toString: 일차원 배열 출력
2) deepToString: 다차원 배열 출력
- 메소드의 매개값이 배열일 경우: 값 목록으로 배열을 생성함과 동시에 add( ) 메소드의 매개값으로 사용하고자 할 때는 반드시 new 연산자를 사용해야 한다.
package ch5;
public class arrayCreateByValueList2 {
public static void main(String[] args) {
int[] scores;
scores = new int[] { 83, 90, 87 };
int sum1 = 0;
for (int i = 0; i < 3; i++) {
sum1 += scores[i];
}
System.out.println("sum: " + sum1);
int sum2 = add(new int[] { 83, 90, 87 });
System.out.println("sum: " + sum2);
System.out.println();
}
public static int add(int[] scores) {
int sum = 0;
for (int i = 0; i < 3; i++) {
sum += scores[i];
}
return sum;
}
}
- 값의 목록은 없지만, 향후 값들을 저장할 배열을 만드는 방법
타입[] 변수 = new 타입[길이];
타입[] 변수 = null;
변수 = new 타입[길이];
- 타입별 배열의 초기값
1) byte[ ]: 0
2) char[ ]: \u0000
3) short[ ]: 0
4) int[ ]: 0
5) long[ ]: 0L
6) float[ ]: 0.0f
7) double[ ]: 0.0
8) boolean[ ]: false
9) 클래스[ ]: null
10) 인터페이스[ ]: null
- 배열을 생성한 후 새로운 값을 지정할 땐 대입 연산자를 사용한다.
- 필드(field): 객체 내부의 데이터
- main( ) 메소드의 매개변수를 사용하는 코드
package ch5;
public class mainStringArrayArgument {
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("How to use this program?");
System.out.println("java mainStringArrayArhument num1 num2");
System.exit(0);
}
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);
}
}
→ System.exit(0): 프로그램 강제 종료
- 배열의 종류
1) 1차원 배열: 값 목록으로 구성된 배열
2) 2차원 배열: 값들이 행과 열로서 구성된 배열
→ 자바에선 중첩 배열 방식으로 구현한다.
- 2행 3열의 배열을 생성하는 방법
int[][] scores = new int[2][3];
- 배열 속의 배열
package ch5;
public class arrayInArray {
public static void main(String[] args) {
int[][] mathScores = new int[2][3];
for (int i = 0; i < mathScores.length; i++) {
for (int k = 0; k > mathScores[i].length; k++) {
System.out.println("mathScores{" + i + "][" + k + "]=" + mathScores[i][k]);
}
}
System.out.println();
// 값 할당하고 출력하는 코드는 생략
}
}
- 배열 간의 항목 값들을 복사하는 방법
1) for문 사용
2) System.arraycopy( ) 메소드 사용
package ch5;
public class arrayCopyByFor {
public static void main(String[] args) {
int[] oldIntArray = { 1, 2, 3 };
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] + ", ");
}
}
}
package ch5;
public class arrayCopyByMethod {
public static void main(String[] args) {
String[] oldStrArray = { "a", "b", "c" };
String[] newStrArray = new String[5];
System.arraycopy(oldStrArray, 0, newStrArray, 0, oldStrArray.length);
// 원본 배열, 원본 배열에서 복사할 항목의 시작 인덱스, 새 배열, 새 배열에서 붙여넣을 시작 인덱스, 복사할 개수
for (int i = 0; i < newStrArray.length; i++) {
System.out.print(newStrArray[i] + ", ");
}
}
}
- 복사의 종류
1) 얕은 복사: 새 배열의 항목이 이전 배열의 항목이 참조하는 객체와 동일
2) 깊은 복사: 참조하는 객체도 별도로 생성
5. 향상된 for문
- 향상된 for문: 배열 및 컬렉션 항목의 개수만큼 반복하고 자동으로 for문을 빠져나간다.
1) 카운터 변수와 증감식을 사용하지 않는다.
2) 괄호 안에는 배열에서 꺼낸 항목을 저장할 변수 선언, 콜론, 배열을 나란히 작성한다.
→ for문의 반복 횟수는 배열의 항목 수가 된다.
package ch5;
public class advancedFor {
public static void main(String[] args) {
int[] scores = { 95, 71, 84, 93, 87 };
int sum = 0;
for (int score : scores) {
sum += score;
System.out.println(score);
System.out.println(sum);
System.out.println();
}
System.out.println("sum of score: " + sum);
double avg = (double) sum / scores.length;
System.out.println("avg of score: " + avg);
}
}
6. 열거 타입
- 열거(enumeration) 타입: 한정된 값만을 갖는 데이터 타입
→ 몇 개의 열거 상수 중 하나의 상수를 저장하는 데이터 타입
- 열거 타입 선언 방법: 열거 타입의 이름을 정하고 열거 타입 이름으로 소스 파일을 생성한다.
1) 열거 타입의 이름은 첫 문자를 대문자로 하고 나머지는 소문자로 한다.
2) 여러 단어로 구성된 이름이라면 단어 첫 문자는 대문자로 한다.
3) 열거 상수는 모두 대문자로 작성한다.
4) 열거 상수가 여러 단어로 구성될 경우엔 단어 사이를 언더바로 연결한다.
- public enum: 열거 타입을 선언하기 위한 키워드
1) 소문자로 작성해야 한다.
2) 열거 타입 이름과 소스 파일명이 정확히 일치해야 한다.
- 열거 타입 선언
package ch5;
public enum Week {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
- 열거 상수는 단독으로 사용할 수 없고 "열거타입.열거상수" 형식으로 사용된다.
열거타입 변수 = 열거타입.열거상수;
- 열거 타입 변수는 null 값을 저장할 수 있고, 열거 상수는 열거 객체로 생성된다.
- Calendar: 날짜와 시간을 얻을 수 있는 클래스
1) Calendar 변수를 선언한다.
2) Calendar.getInstance( ) 메소드가 리턴하는 Calendar 객체를 얻는다.
3) get( ) 메소드를 이용해 원하는 값을 얻는다.
package ch5;
import java.util.Calendar;
public class enumWeekExample {
public static void main(String[] args) {
Week today = null; // 열거 타입 변수 선언
Calendar cal = Calendar.getInstance();
int week = cal.get(Calendar.DAY_OF_WEEK); // 일(1)~토(7)까지의 숫자를 리턴
switch (week) {
case 1:
today = Week.SUNDAY; // 열거 상수 대입
break;
case 2:
today = Week.MONDAY;
break;
case 3:
today = Week.TUESDAY;
break;
case 4:
today = Week.WEDNESDAY;
break;
case 5:
today = Week.THURSDAY;
break;
case 6:
today = Week.FRIDAY;
break;
case 7:
today = Week.SATURDAY;
break;
}
System.out.println("today: " + today);
if (today == Week.SUNDAY) {
System.out.println("today is SUNDAY");
} else {
System.out.println("today is not SUNDAY");
} //sd
}
}
- 모든 열거 타입은 컴파일 시 Enum 클래스를 상속한다.
- 열거 객체의 메소드
1) name( ): 열거 객체가 가지고 있는 문자열을 리턴한다.
2) ordinal( ): 전체 열거 객체 중 몇 번째 열거 객체인지 알려준다.
→ 열거 타입을 정의할 때 주어진 순번
3) compareTo( ): 매개값으로 주어진 열거 객체를 기준으로 전후로 몇 번째 위치하는지를 비교한다.
→ 열거 객체가 매개값보다 순번이 빠르면 음수, 늦으면 양수가 리턴된다.
4) valueOf( ): 매개값으로 주어지는 문자열과 동일한 문자열을 가지는 열거 객체를 리턴한다.
5) values( ): 열거 타입의 모든 열거 객체들을 배열로 만들어 리턴한다.
package ch5;
public class enumMethodExample {
public static void main(String[] args) {
// name(): 열거 객체의 문자열을 리턴
Week today = Week.SUNDAY;
String name = today.name();
System.out.println(name);
// ordinal(): 열거 객체의 순번을 리턴
int ordinal = today.ordinal();
System.out.println(ordinal);
// compareTo(): 열거 객체를 비교해서 순번 차이를 리턴
Week day1 = Week.MONDAY;
Week day2 = Week.WEDNESDAY;
int result1 = day1.compareTo(day2);
int result2 = day2.compareTo(day1);
System.out.println(result1);
System.out.println(result2);
// valueOf(): 주어진 문자열의 열거 객체를 리턴
if (args.length == 1) {
String strDay = args[0];
Week weekDay = Week.valueOf(strDay);
if (weekDay == Week.SATURDAY || weekDay == Week.SUNDAY) {
System.out.println("It's a weekend");
} else {
System.out.println("It's a weekday");
}
}
// values(): 모든 열거 객체들을 배열로 리턴
Week[] days = Week.values();
for (Week day : days) {
System.out.println(day);
}
}
}
댓글