자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.
학습할 것
- 프리미티브 타입 종류와 값의 범위 그리고 기본 값
- 프리미티브 타입과 레퍼런스 타입
- 리터럴
- 변수 선언 및 초기화하는 방법
- 변수의 스코프와 라이프타임
- 타입 변환, 캐스팅 그리고 타입 프로모션
- 1차 및 2차 배열 선언하기
- 타입 추론, var
기본자료형 혹은 원시자료형 이라고 불리는 프리미티브 타입은 값이 할당되면 JVM Runtime Data Area 영역 중 Stack 영역에 값이 저장된다.
우리가 주로 사용하는 값의 종류는 크게 문자와 숫자로 나눌 수 있으며 여기서 숫자는 다시 정수와 실수로 나뉜다. 기본형은 모두 8가지의 타입(자료형)이 있으며, 크게 논리형, 문자형, 정수형, 실수형으로 구분된다.
타입 종류
정수형은 가장 많이 사용되기에 타입이 4가지나 제공된다. 각 타입별로 범위가 다르기에 범위에 맞는 값을 사용하면 된다.
타입 범위
자료형은 크게 '기본형(Primitive Type)' 과 참조형(Reference Type)으로 나눌 수 있습니다.
Java.lang.Object
를 상속받을 경우 참조형이 된다.추가적으로 기본형은 메모리영역의 스택영역에 실제 값들이 저장된다면, 참조형은 실제 인스턴스는 힙영역에 생성되있고, 그 영역의 주소를 스택영역에서 저장하고 있다고 보면 된다.
리터럴은 변수나 상수에 저장되는 값 자체를 의미한다. 그 종류로는 정수, 실수, 문자, boolean, 문자열 등이 있다.
boolean result = true;
int i = 2020;
위 예시에서 true와 2020이 리터럴이다.
변수를 사용하기위해서는 우선 변수를 선언해야 하며 아래와 같이 선언합니다.
int i; // 변수타입 변수이름;
변수 타입: 변수에 저장될 값이 어떤 타입(type)인지 지정하는 것.
변수 이름: 변수에 붙힌 이름. 변수가 값을 저장할 수 있는 메모리 공간을 의미하므로 변수 이름은 이 메모리 공간에 이름을 붙혀주는 것.
이렇게 변수를 선언하면, 메모리의 빈 공간에 '변수타입'에 알맞은 크기의 저장공간이 확보되고, 변수 이름을 붙혀서 이 이름을 통해 해당 저장공간을 사용할 수 있게 된다.
변수를 사용하기 전 처음으로 값을 저장하는 것
변수를 선언하면 메모리에 변수의 저장공간이 확보되어있지만, 이 공간안에 어떠한 값이 저장되어있을지는 알 수 없다. 여러 프로그램에 의해 공유되기 때문이다. 그렇기에 초기화를 해줘야한다.
변수에 값을 저장할 때는 대입 연산자 = 을 사용한다. 아래 예시를 보면 int 변수 타입과 i 라는 변수 이름을 가진 변수에게 200 이라는 값을 대입한다.
int i = 200
대입연산자의 우측의 있는 값을 좌측에 있는 변수에 저장합니다.
변수의 종류에 따라 변수의 초기화를 생략할 수 있는 경우도 있지만, 변수는 사용되기 전에 적절한 값으로 초기화 하는 것이 좋다.
지역변수는 사용하기 전 반드시 초기화 해야 한다.
지역변수는 변수의 초기화로 충분하지만, 멤버변수의 초기화는 몇가지 방법이 더 있다.
1. 명시적 초기화(explicit initialization) : 변수 선언과 동시에 초기화 하는 것을 명시적 초기화라 하는데, 위에서 소개한 변수의 초기화와 동일하며, 클래스 및 지역변수 어디서든 사용가능하며 여러 초기화 방법중 최우선적으로 고려한다.
2. 초기화 블럭(initialization block) : 초기화 블럭은 클래스 초기화 블럭과 인스턴스 초기화 블럭으로 나뉜다.
public class Test { static{ // 클래스 초기화 영역 } { // 인스턴스 초기화 영역 } }
클래스 초기화 블럭: 클래스변수의 복잡한 초기화에 사용. 블럭내에서는 로직도 추가할 수 있기 때문에 명시적 초기화만으로 부족할 때 사용한다.
인스턴스 초기화 블럭: 인스턴스 변수의 복잡한 초기화에 사용. 모든 생성자가 공통으로 수행해야 하는 로직이 있을 때 사용한다.
3. 생성자(constructor) : 생성자는 말 그대로 인스턴스 생성시에 생성자 함수 안에서 명시적 초기화가 이뤄진다.
변수는 클래스변수, 인스턴스변수, 지역변수 모두 세 종류가 있다. 변수의 종류를 결정짓는 중요한 요소는 변수의 선언 위치 이므로 변수의 종류를 파악하기 위해서는 변수가 어느 영역에 선언되었는지를 확인하는 것이 중요하다.
public class Test {
int instanceValue; // 인스턴스 변수
static int classValue; // 클래스변수( static 변수, 공유변수 )
void method(){
int localValue; // 지역변수
}
}
클래스 내부에 선언되는 변수를 멤버변수라고 한다. 여기서 키워드 static과 함께 선언되는 변수를 클래스 변수, 붙지 않은 것을 인스턴스 변수라고 한다. 그리고 멤버변수를 제외한 나머지 변수들은 모두 지역변수이다.
: 클래스 영역에 선언되며, 클래스의 인스턴스를 생성할 때 만들어진다. 그렇기에 인스턴스 변수 값을 읽어오거나 저장하기 위해서는 먼저 인스턴스를 생성해야한다.
인스턴스 별로 별도의 저장 공간을 확보하기에 인스턴스별 다른 값을 가질 수 있다.
: 멤버변수에 static
키워드를 붙힐경우 클래스 변수가 되며 한 클래스의 모든 인스턴스가 값을 공유한다. 클래스 변수는 인스턴스를 생성 하지 않고 클래스가 메모리에 올라갔을때 선언되기 때문에 인스턴스에서는 언제든 바로 접근해서 사용할 수 있습니다. 그렇기에 어디서나 접근 할 수 있는 전역변수(global variable)의 성격을 가진다.
: 메소드 내에 선언되어 메소드 내에서만 사용 가능하며 메소드 종료와 함께 소멸된다. for 문이나 while문같은 반복문도 동일하게 블럭내에서 선언된 지역변수는 블럭을 벗어나면 소멸된다.
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("i = " + i);
}
System.out.println("i = " + i);//Checked Exception 발생
}
초기화 시점
프로그램 실행도중 클래스에 대한 정보가 요구될 때 클래스는 메모리에 로딩된다. (만약 이미 메모리에 로딩되어 있다면 또다시 로딩하지 않는다.)
초기화 순서
class InitTest {
static int classValue = 1;
int instanceValue = 1;
static {
classValue = 2;
}
InitTest() {
instanceValue = 3;
}
}
변수 또는 상수의 타입을 다른 타입으로 변환하는 것
프로그램을 작성하다보면 서로 다른 타입간의 연산을 수행하야 하는 경우가 있다. 이럴 때 연산을 수행하기 전 서로의 타입을 일치시켜야하는데, 이렇게 변수나 리터럴의 타입을 다른 타입으로 변환하는 것을 형변환
이라고 한다.
(타입)피연산자
변환할 변수나 리터럴 앞에 타입을 괄호와 함께 붙여주기만 하면 된다. 이 때 형 변환 연산자는 그저 피연산자의 값을 읽어서 지정된 타입으로 형변환하고 그 결과를 반환할 뿐이기에 기존의 변수나 리터럴이 변화되지는 않는다.
double d = 810.4;
int i = (int)d; // 810
기본형 변수 중 boolean을 제외한 나머지 타입들은 서로 형변환이 가능하다. 하지만 타입간에 각 가지고 있는 크기가 다르기 때문에 형변환을 통해 크기의 차이만큼 값이 잘려나감으로써 값 손실(lost of data)
이 발생할 수 있다.
경우에 따라 형변환을 생략할 수 있다. 컴파일러가 생략된 형변환을 자동적으로 추가하여 생략할 수 있게 되었다.
하지만 변수가 저장할 수 있는 크기보다 더 큰 값을 저장하려는 경우에 형변환을 생략하면 에러가 발생한다. 이는 더 작은 값으로 할당되며 값 손실이 발생할 수 있기 때문에 이를 명시적 형변환으로 바꾸어 주면 에러가 발생하지 않는다.
byte b = 10000; // 에러 발생, byte의 범위를 초과한다.
byte c = (byte)10000; //명시적 형 변환으로 에러가 발생하지 않는다.
타입 프로모션 규칙
기존의 값을 최대한 보존할 수 있는 타입으로 자동 형변환한다.
표현범위가 좁은 타입에서 넓은 타입으로 형변환 할 때 값 손실이 없기에 두 타입 중 표현범위가 더 넓은 쪽으로 형변환 됩니다.
class ArrayExample {
public static void main(String[] args) {
//1차원 배열
int[] oneDimensionArrayEx1 = {1, 2, 3, 4, 5};
int[] oneDimensionArrayEx2;
oneDimensionArrayEx2 = new int[10];
//2차원 배열
int[][] twoDimensionArrayEx1 = {{1, 2}, {3, 4}};
int[][] twoDimensionArrayEx2;
twoDimensionArrayEx2 = new int[10][10];
}
}
Type Inference 이란 정적인 언어에서 컴파일러가 컴파일 시점에서 변수를 초기화하는 리터럴을 판단하여 해당 타입으로 변환하는 것을 말한다.
자바는 JDK 10부터 Local Scope 내에서 var이라는 새로운 Type을 지원하게 되었다.
사용 시 주의점
var num;
var num = null;
public num = "String";
var foo = (String s) → s.length() > 10;
var arr = { 1, 2, 3 };
public void someMethod() {
var str = "Hello world";
assertTrue(str instanceof String);
var num = 10;
var numB = 10.10D;
System.out.println(numB+num); // 20.1
}