자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.
우리가 사용하는 값, 즉 변수에는 여러 종류가 있는데 그 종류에 따라 값이 저장될 공간의 크기와 저장형식을 정의한 것이 자료형(data type)이다.
자료형은 크게 '기본형(primitive type)' 과 '참조형(reference type)'으로 나눌 수 있다.
분류 | 타입 | 메모리 크기(byte) | 저장 가능한 값의 범위 | 기본값 |
---|---|---|---|---|
논리형 | boolean | 1 | false, true | false |
문자형 | char | 2(유니코드) | '\u0000'~'\uffff'(0~65535, 0~216-1) | '\u0000' |
정수형 | byte | 1 | -128~127(-27~27-1) | 0 |
short | 2 | -32,768~32,767 (-215~215-1) | 0 | |
int | 4 | -231~231-1 | 0 | |
long | 8 | -263~263-1 | 0L | |
실수형 | float(부동소수점) | 4 | 1.4E-45 ~ 3.4E38 | 0.0f |
double | 8 | 4.9E-324 ~ 1.8E308 | 0.0 |
char ch = 'A';
라는 코드에서 char형의 변수 ch에는 문자 'A'의 유니코드인 65가 저장된다.char ch = 65;
와 동일한 결과를 보인다.byte의 표형형식(8 bit) | 종류 | 값의 개수 |
---|---|---|
00000000~01111111 | 0, 양수 | 27개 |
10000000~11111111 | 음수 | 27개 |
리터럴을 살펴보기 위해 상수(constant)와의 비교가 필요하다고 생각한다
/* 예시를 위한 Java 코드 */
int answer = 10;
final int MAX_VALUE = 100;
앞서 공부한 변수의 타입은 저장될 리터럴의 타입에 의해 결정되므로, 리터럴에 타입은 필수적이다
종류 | 리터럴(예시) | 접미사 |
---|---|---|
논리형 | false, true | 없음 |
정수형 | 123, 0b0101(2진수), 077(8진수), 0xFF(16진수), 100L | L(long), 없음(int, byte, short) |
실수형 | 3.14, 3.0e8, 1.4f, 0x1.0p-1 | f(float), d(double) * d는 생략가능 |
문자형 | 'A', '1', '\n' | 없음 |
문자열 | "ABC", "123" | 없음 |
Java에서 변수의 선언 방법은 변수타입 + 변수이름
형식으로 하고, 예를 들면 아래와 같다
int sum = 0;
변수를 선언하면, 메모리의 빈 공간에 변수타입에 알맞은 크기의 저장공간이 확보되고, 앞으로 이 저장공간은 변수이름을 통해서 사용된다
변수의 초기화란, 변수를 사용하기 전에 처음으로 값을 저장하는 것이다
메모리는 여러 프로그램이 공유하는 자원이므로 전에 다른 프로그램에 의해 저장된 쓰레기값이 남아있을 수 있기 때문에 변수를 사용하기 전에 반드시 변수를 초기화해야 된다
변수에 값을 저장할 때는 간단하게 '='를 사용한다. 오른쪽의 값을 왼쪽(변수)에 저장하라는 의미이다
한 줄에 여러 개의 변수를 초기화할 수도 있다(by comma)int a;
int b;
int x = 10;
int y = 10;
int a, b;
int x = 10, y = 10;
위의 두 소스코드는 서로 같은 의미이다.
'변수이름'처럼 프로그래밍에서 사용하는 모든 이름을 식별자라고 한다
식별자는 같은 영역 내에서 서로 구분되어야 하며 다음과 같은 규칙이 존재한다
개인적으로 생소한 것들도 있다(3번과 4번의 경우) 코딩을 하면서 아직 숫자로 시작하는 변수이름을 만든 적이 없었던 거 같다. 이번 기회에 알아두면 좋을 듯!
다음은 Java에서 사용되는 예약어이다. 코딩을 하다보면 차차 알게 될 거 같아 굳이 힘들게 외울 필요는 없을 것 같다
어떤 변수에 접근할 수 있는 범위
Java에서는 블록{ }
스코프를 사용한다
public class Main{
static int a = 10;
public static void main(String[] args){
System.out.println(a); // 10이 출력됨
int a = 30;
System.out.println(a); // 30이 출력됨
}
}
어떤 변수가 사용되는 위치를 기준으로 그 전에 선언된 변수를 찾는다라고 생각하면 된다.
레퍼런스 타입의 변수의 라이프 타임은 쓰레기 수집기 (GC : Garbage Collector)와 관련이 있다
이 GC는 가비지 컬렉션 힙 영역에 존재하는 참조 타입 변수의 객체에 대해 동작한다
힙 영역에 메모리가 부족할 경우 GC가 이 영역을 스캔하고, 아무곳에서도 사용하지 않는 즉, 참조 되고 있지 않은 객체를 제거해 버린다
런타임 스택 영역에 생성된 변수의 라이프 타임은 블록 스코프에 의존적이다
즉, 블록 내에서 선언된 변수는 블록이 종료될 때 런타인 스택 영역에서 함께 소멸한다
때때로 리터럴의 타입과 저장될 변수의 타입이 일치하지 않는 경우가 있다
- 서로의 타입이 달라도 저장범위가 넓은 영역에 좁은 타입의 값을 저장하는 것은 허용된다
- int a = 'A'; // 허용. 문자 'A'의 유니코드인 65가 변수 a에 저장됨
- long a = 123; // 허용. int보다 long 타입의 범위가 더 넓다
- double a = 3.13f; // 허용. float보다 double 타입의 범위가 더 넓다
- 리터럴의 값이 변수의 타입의 범위를 넘어서거나, 리터럴의 타입이 변수의 타입보다 저장범위가 넓으면 컴파일 에러!
- int a = 0x123456789; // 에러. int 타입의 범위를 넘는 값을 저장했다
- float a = 3.14; // 에러. float 타입보다 double 타입의 범위가 더 넓다
프로그래밍을 하다보면 서로 다른 타입간의 연산을 수행해야하는 경우가 있는데 그럴 때는 어떻게 하냐?
그때에 필요한 것이 바로 형변환이다
즉, 형변환이란 변수 또는 상수의 타입을 다른 타입으로 변환하는 것이다
(타입)피연산자
위와 같이 형변환하고자 하는 변수나 리터럴의 앞에 변환하고자하는 타입을 괄호와 함께 붙여주면 된다
이때 사용되는 괄호( )를 '캐스트 연산자' 또는 '형변환 연산자'라고 부르며, 형변환을 캐스팅(casting)이라고도 한다
double d = 20.2;
int a = (int)d; // double 타입의 변수 d의 값을 읽어와서 int 타입으로 형변환한다
System.out.println(a); // 20이 출력된다
System.out.println(d); // 피연산자는 형변환 후에 아무런 변화가 없으므로 20.2가 출력된다
타입 캐스팅 : 자신의 표현 범위를 모두 포함하지 못한 데이터 타입으로의 변환
타입 프로모션 : 자신의 표현 범위를 모두 포함한 데이터 타입으로의 변환
같은 타입의 여러 변수를 하나의 묶음으로 다루는 것
선언방법 | 선언 예 |
---|---|
타입[] 변수이름; | int[] a |
타입 변수이름[]; | int a[] |
위와 같이 두가지 방법이 있지만, 나는 대괄호가 타입의 일부라 생각하여 첫번째 방법을 사용한다
배열의 선언은 단지 생성된 배열을 다루기 위한 참조변수를 위한 공간이 만들어지는 것이고,
배열을 생성해야만 비로소 값을 저장할 수 있는 공간이 만들어진다
배열을 생성하기 위해서는 연산자 'new'가 필요하다
타입[] 변수이름;
변수이름 = new 타입[길이];
이것을 한 줄로 줄이면,
타입[] 변수이름 = new 타입[길이];
코드와 그림을 통해 단계별로 알아보자.
- int형 배열 참조변수 arr을 선언한다
- 연산자 'new'에 의해 메모리의 빈 공간에 5개의 int형 데이터를 저장할 수 있는 공간이 마련된다
- 각 배열요소는 int의 기본값인 0으로 초기화된다
- 마지막으로 대입연산자 '='에 의해 배열의 주소가 int형 배열 참조변수 arr에 저장된다
int[] arr = new int[5] // 길이가 5인 int배열. 인덱스의 범위는 0~4
배열에 값을 저장하고 읽어오는 방법은 변수에서와 비슷하다
arr[3] = 10; // 배열 arr의 4번째 요소에 10을 저장한다 int answer = arr[3]; // 배열 arr의 4번째 요소에 저장된 값을 읽어서 answer에 저장한다
배열의 장점은 index로 상수 대신 변수나 수식도 사용할 수 있다는 것이다
for(int i = 0; i < arr.length; i++){ arr[i] = i * 10; }
- 배열이름.length
- Java에서는 JVM이 모든 배열의 길이를 별도로 관리한다
- 배열이름.length를 통해서 배열의 길이에 대한 정보를 얻을 수 있다
- 배열은한번 생성하면 길이를 변경할 수 없기 때문에 '배열이름.length'는 상수이다
- 위의 코드에서 arr.length의 값은 5이다
하지만 이러한 작업들은 비용이 많이 든다. 따라서 처음부터 배열의 길이를 넉넉하게 잡는 것이 좋은데 너무 크게 잡으면 메모리를 낭비하게 되므로 기존의 2배 정도의 길이로 생성한다
세가지 방법이 있다
1.각 요소마다 값을 지정해준다
int[] arr = new int[3];
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
2.for문을 사용한다
int[] arr = new int[3];
for(int i = 0; i < arr.length; i++){
arr[i] = (i+1) * 10;
}
3.배열의 선언과 생성을 동시에 한다
int[] arr = new int[]{10, 20, 30}; // 'new int[]' 는 생략할 수 있다
선언 방법 | 선언 예 |
---|---|
타입[ ][ ] 변수이름; | int[ ][ ] arr; |
타입 변수이름[ ][ ]; | int arr[ ][ ]; |
타입[ ] 변수이름[ ]; | int[ ] arr[ ]; |
int[][] arr = new int[4][3]; // 4행 3열의 데이터를 담기위한 2차원 배열이다
2차원 배열은 행(row)과 열(column)로 구성된다
'행index'의 범위는 0 ~ 행의 길이-1 이다
'열index'의 범위는 0 ~ 열의 길이-1 이다
2차원 배열의 각 요소에 접근하는 방법은 '배열이름[행index][열index]'이다
int[][] arr = new int[4][3];
int[][] arr = new int[][]{ {1,2,3}, {4,5,6} };
int[][] arr = { {1,2,3}, {4,5,6} }; //new int[][] 가 생략됨
int[][] arr = {
{1,2,3},
{4,5,6}
}; //가독성을 높여주는 줄바꿈
Java에서는 2차원 이상의 배열을 '배열의 배열;의 형태로 처리한다는 사실을 이용하여 가변 배열을 구성할 수 있다
int[][] arr = new int[5][];
arr[0] = new int[4];
arr[1] = new int[3];
arr[2] = new int[2];
arr[3] = new int[1];
arr[4] = new int[0];
/*
길이가 0인 배열도 생성 가능하다.
참조변수의 기본값은 null이지만, 배열을 가리키는 참조변수는 null대신 길이가 0인 배열로 초기화하기도 한다
*/
ArrayList<>()
를 하는데 이것도 좌측에 있는 것을 토대로 데이터 타입을 추론하는 것이다
꾸준히 공부하는 그대가 참 좋아요