변수를 선언하기 위해서는 변수의 타입과 변수 명이 필요하며, 타입 변수명;
과 같은 형식으로 선언한다. 변수 선언 시 변수의 타입에 맞는 메모리 공간이 확보되고, 변수 명을 통해 이 메모리에 저장된 값을 사용할 수 있게 된다.
int price; // int: 변수 타입, price: 변수 명
변수 초기화란 변수를 사용하기 전에 값을 처음으로 지정해 주는 것을 말한다. 변수를 초기화 할 때는 대입 연산자를 이용해 변수 선언과 초기화를 동시에 할 수 있다.
int price = 10000; // 변수 선언과 동시에 초기화
int age = 30, count = 0; // 동시에 여러개 변수 초기화
자바 코드를 작성할 때 우리는 우선 사용하고자 하는 데이터의 타입이 무엇인지 고민해봐야 한다. 데이터 타입에는 문자형, 정수형, 실수형 등이 있으며, 데이터 타입에 해당하는 적절한 키워드를 사용하여 변수를 선언해야 한다.
프리미티브 타입(Primitie type, 기본형)은 사용하고자 하는 데이터의 실제 값을 저장하는 타입으로, 스택 메모리 영역에 저장된다. 논리형(boolean), 문자형(char), 정수형(byte, short, int, long), 실수형(float, double) 등 총 8개 타입이 존재한다.
프리미티브 타입을 제외한 모든 데이터 타입을 말한다. 연산에 필요한 실제 값을 저장하는 프리미티브 타입과 달리, 레퍼런스 타입은 어떤 데이터가 저장되어 있는 주소 값을 값으로 가지며, 힙 메모리 영역에 저장된다.
분류 | 타입 | 메모리 크기 | 저장 가능한 값의 범위 | 기본값 |
---|---|---|---|---|
논리형 | boolean | 1 byte | true, false | false |
정수형 | byte | 1 byte | ~ | 0 |
short | 2 byte | ~ | 0 | |
int | 4 byte | ~ | 0 | |
long | 8 byte | ~ | 0L | |
실수형 | float | 4 byte | ~ | 0.0F |
double | 8 byte | ~ | 0.0 | |
문자형 | char | 2 byte(유니코드) | ~ | '\u0000' |
리터럴(literal)은 변수나 상수에 저장하고자 하는 값 그 자체를 의미한다.
int age = 30; // age는 변수, 30은 리터럴
char ch = 'a'; // ch는 변수, 'a'은 리터럴
final int MAX_PRICE = 10000; // MAX_PRICE는 상수, 10000은 리터럴
분류 | 리터럴 | 접미사 |
---|---|---|
논리형 | true, flase | |
정수형 | 10, 0b101, 012, 0xFC, 12345L | long 타입의 경우 L |
실수형 | 3.14, 2.0e8, 1.5f, 0x1, 0p-1 | float 타입 f, double 타입 d (기본형이라 잘 사용x) |
문자형 | 'A', '5', 'c' | |
문자열 | "가나다", "abc", "123123" |
int octNum = 010; // 출력 : 8 (8진수 10을 의미하며, 10진수 8로 출력됨)
int hexNum = 0xF; // 출력 : 15 (16진수 F을 의미하며, 10진수 15로 출력됨)
int binNum = 0b100; // 출력 : 4 (2진수 100을 의미하며, 10진수 4로 출력됨)
String emptyStr = ""; // 빈 문자열 가능
char emptyChar = ''; // 에러!
char spaceChar = ' '; // 하나의 공백은 가능
문자열 리터럴 vs new 연산자
문자열 리터럴을 저장하는 String은 프리미티프 타입이 아닌 레퍼런스 타입으로, 객체를 생성하는 new 연산자를 사용해 문자열을 생성할 수도 있다.
String str1 = "abc"; // 리터럴 방식으로 문자열 생성
String str2 = "abc";
String str3 = new String("abc"); // new 연산자로 문자열 객체 생성
위 두 방식의 차이점은 무엇일까? new 연산자로 문자열 객체를 생성할 경우 JVM 메모리 영역 중 Heap 영역
에 할당되고, 리터럴을 이용할 경우 String Constant Pool
이라는 영역에 할당된다. String Constant Pool
은 Heap 영역 안의 Permanent area(고정 영역)에 생성되어 자바 프로세스가 종료될 때 함께 사라진다.
※ 이미지 출처 : https://iq.opengenus.org/string-pool-in-java/
new 연산자를 사용하면 문자열 객체를 생성할 때 마다 새로운 문자열 인스턴스가 생성되기 때문에, 각각의 인스턴스는 서로 다른 문자열 객체를 참조한다.
리터럴을 사용하면 먼저 String Constant Pool
에 같은 문자열 객체가 있는지 확인하고, 있으면 해당 객체를 참조하며, 없으면 String Constant Pool
에 새로운 객체를 생성한다.
아래 코드로 확인해보도록 하자.
String str1 = "abc"; // 리터럴 방식으로 문자열 생성 (String Constant Pool)
String str2 = "abc";
String str3 = new String("abc"); // new 연산자로 문자열 객체 생성 (Heap)
System.out.println(str1 == str2); // 주소비교 : true
System.out.println(str1.equals(str2)); // 값 비교 : true
System.out.println(str1 == str3); // 주소비교 : false
System.out.println(str1.equals(str3)); // 값 비교 : true
리터럴 방식으로 똑같은 문자열을 지정한 str1
과 str2
의 주소를 비교하면 같은 한편, 리터럴 방식과 new 연산자를 이용해 똑같은 문자열을 지정했을 때는 두 주소 값이 다른 것을 확인할 수 있다.
스코프(Scope)란 변수를 사용할 수 있는 범위를 말한다. 변수가 선언된 위치에 따라 변수의 종류가 달라지며, 이에 따라 변수의 스코프와 라이프사이클이 결정된다.
변수의 종류 | 선언 위치 | 스코프 | 라이프사이클 |
---|---|---|---|
클래스 변수 | 클래스 영역 | 클래스 전체 | 클래스가 메모리에 올라간 후 프로그램이 끝날 때 까지 |
인스턴스 변수 | 클래스 영역 | static 블록과 static 메서드를 제외한 클래스 전체 | 인스턴스가 생성 후 메모리에 살아있을 때 까지 |
로컬 변수 | 메서드, 생성자, 초기화 블럭 내부 | 변수가 선언된 블록 내부 | 변수 선언 후 블록을 벗어날 때 까지 |
타입 변환이란 변수나 리터럴의 타입을 다른 타입으로 변환시키는 것을 말한다. 프리미티브 타입에서 boolean을 제외한 다른 타입들은 서로 간의 타입 변환이 가능하다.
캐스팅(Casting)은 저장 공간이 큰 타입의 데이터를 저장 공간이 더 작은 데이터 타입에 저장할 때, 리터럴 앞에 타입을 명시하여 강제로 대입하는 것을 말한다. 캐스팅 될 때, 변환되는 값이 양수인 경우 빈 저장 공간이 0, 음수인 경우 빈 저장 공간이 변환 할 값이 1로 채워진다. 캐스팅에서 주의할 점은, 이 과정에서 tobe 데이터 타입의 범위가 너무 작아 원래 값을 다 담지 못하는 경우, 값의 손실이 일어날 수 있다는 것이다.
double x = 90.5;
int y = (int)x; // 90 : double 타입을 int 타입에 강제로 대입
int x = 300;
byte y = x; // (주의!) y는 44가 저장됨
프로모션(Promotion)은 저장 공간이 더 작은 타입의 데이터를 저장 공간이 더 큰 데이터 타입에 저장할 때, 자동으로 타입이 변환되는 것을 말한다. 이 때, 캐스팅과는 달리 따로 타입을 명시해주지 않아도 자동으로 타입 변환이 이뤄진다.
int x = 90;
double y = x; // 90.0 : int 타입이 double 타입으로 자동 타입 변환되어 대입됨
배열은 같은 타입의 변수들을 하나의 묶음으로 나타낸 자료형을 말한다.
for (int i = 0; i < 5; i++) {
ages[i] = i * 15;
}
int[] ages = new int[5]; // 5개의 int형 데이터를 저장할 수 있는 배열 선언
위 예에서는 int형 값들 5개를 담을 수 있는 저장공간이 생성되며, 이 공간을 ages라는 이름으로 참조할 수 있게 된다.
배열에 생성된 각각의 저장 공간을 배열의 요소(element)라고 하며, 인덱스로 접근할 수 있다. 인덱스의 범위는 0부터 시작하며, 배열의 길이-1 까지이다. 배열이름[인덱스] = 값
형태로 배열 값을 저장할 수 있다.
for (int i = 0; i < 5; i++) {
ages[i] = i * 15;
}
2차원 배열은 테이블 형태의 저장공간에 데이터를 저장할 때 사용된다.
int[][] arr1 = new int[4][3]; // 4행 3열의 2차원 배열 생성
int[][] arr2 = {
{1, 2, 3},
{4, 5, 6}
}; // 2행 3열의 2차원 배열 생성 및 초기화
int[][] arr = new int[4][3];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
arr[i][j] = 100;
}
}
타입 추론(Type Inference)란, 타입이 정해지지 않은 변수에 대해 컴파일러가 타입을 스스로 추론 수 있도록 하는 기능이다. 자바 10 버전부터 타입을 명시하지 않아도 아래 케이스에 var
를 사용해 모든 타입의 변수를 선언할 수 있다.
var a = 1;
var greeting = "Hello";
var list = new ArrayList<String>();
var stream = list.stream();