모든 변수는 타입이 있으며, 타입에 따라 저장할 수 있는 값의 종류와 범위가 달라진다.
자바는 C언어와 달리 참조형 변수 간의 연산을 할 수 없으므로 실제 연산에 사용되는 것은 모두 기본형 변수이다.
기본형(Primitive type)
- boolean, char, byte, short, int, long, float, double
계산을 위한 실제 값을 저장한다.
참조형(Reference type)
- 8개의 기본형을 제외한 나머지 타입, 객체의 주소를 저장한다.
* 참조형 변수는 null 또는 객체의 주소(4byte)를 값으로 갖는다. null은 어떤 값도 갖고 있지 않은 상태를 뜻하므로, 어떠한 객체도 참조하고 있지 않다는 것을 의미한다.
참조형은 직접 추가할 수 있으므로 수가 정해져 있지 않다.
참조변수를 선언할 때는 변수의 타입으로 클래스의 이름을 사용한다. 그래서 새로운 클래스를 작성한다는 것은 새로운 참조형을 추가하는 셈이다.
Date today; // 타입이 클래스이 이름인 것들은 모두 참조변수이다.
다음은 Date 클래스 타입의 참조변수 today를 선언한 것이다. 참조변수의 초기화는 다음과 같이 한다.
Date today = null;
Date today = new Date();
객체를 생성하는 연산자 new의 연산결과는 생성된 객체의 주소이다. 이 주소가 대입연산자 '=' 에 의해서 참조변수 today에 저장되는 것이다.
* 참조형 변수에는 값이 아닌 객체의 종류에 의해서 구분되므로, 자료형대신 타입이라는 용어를 사용한다.
2.1 기본형 (primitive type)
기본형은 논리형, 문자형, 정수형, 실수형으로 구분된다.
논리형 : true와 false 중 하나를 값으로 갖으며 조건식과 논리적 계산에 사용된다.
- boolean (1 byte)
문자형 : 문자를 저장하는 데 사용되며, 변수 당 하나의 문자만을 저장할 수 있다.
- char (2 byte)
정수형 : 정수 값을 저장하는 데 사용된다. 주로 int와 long을 사용하며, byte는 이진데이터를 다루기 위해, short는 C언어와의 호환을 위해 추가되었다.
- byte (1 byte), short (2 byte), int (4 byte), long (8 byte)
실수형 : 실수 값을 저장하는데 사용된다. float은 부동소수점 방식으로 저장하고, double은 float보다 두 배의 정밀도를 갖는다.
- float (4 byte), double (8 byte)
* 4개의 정수형 중에서 int형이 기본(default) 자료형이며, 실수형에서는 double이 기본 자료형이다.
boolean을 제외한 나머지 7개의 자료형은 서로 변환이 가능하기 때문에 연산이 가능하다. 특히 char는 내부적으로 문자를 정수값 코드로 저장하기 때문에 정수형과 밀접한 관련이 있다.
정수형이 가지는 값의 범위는 $-2^{n-1}$ ~ $2^{n-1}-1$ (n은 bit 수가 들어간다) 정도로만 기억한다.
예를 들어, int 형의 경우 32bit 이므로 $-2^{31}$ ~ $2^{31}-1$ 의 범위를 갖는다.
따라서 int형은 대략 9자리 수의 값을 저장할 수 있다. 만약 int와 float의 범위를 넘는 값을 다루게 된다면, long과 double을 사용하면 된다.
* 실수값을 저장할 때는 값의 범위 뿐만 아니라 정밀도도 고려해야 한다.
2.2 논리형 - boolean
boolean형 변수에는 true와 false 중 하나를 저장할 수 있으며, default는 false이다. 주로 yes/no, on/off 와 같은 논리구현에 사용되며, 두 가지 값만 표현하기 때문에 가장 작은 1 byte이다.
* 2가지 값만 표현하기 때문에 1 bit만으로 충분하지만 데이터를 다루는 최소 단위가 1 byte기 때문에 1 byte로 지정.
아래 문장은 power라는 boolean형 변수를 선언하고 ture로 변수를 초기화 했다.
boolean power = true;
* 자바에서는 대소문자를 구별하기 때문에 TRUE와 true는 다른 것으로 간주한다.
2.3 문자형 - char
C언어 같은 언어는 문자형의 경우 확장된 아스키 코드를 사용하여 1 byte의 크기를 갖지만, 자바에서는 유니코드를 사용하여 2 byte의 크기를 갖는다.
char 형의 크기는 2 byte이므로 16진수로 0000부터 ffff까지 65536($2^{16}$)개의 코드를 사용할 수 있으며, char 형의 변수는 이 중 하나를 저장할 수 있다.
예를 들어, 아래는 알파벳 'A' 의 유니코드값은 0041이므로 char 형 변수에 문자 'A' 를 저장하는 코드이다.
char firstLetter = 'A';
char firstLetter = '\u0041'; //16진수 41은 10진수로 65이다.
char 형 변수에 문자를 저장할 때는 작은따옴표('')로 문자를 둘러싼다.
char 형과 short 형은 크기가 모두 2 byte지만, 범위가 다르기 때문에 같은 2진 표현이더라도 실제 의미하는 값은 다를 수 있다.
char 형은 문자의 코드를 저장하므로 음수를 필요로 하지 않기 때문에 2진수로 표현했을 때 첫 번째 자리를 부호에 사용하지 않는다. 하지만 short 형은 첫 번째 자리를 부호를 표현하는데 사용하기 때문에 서로 다른 범위를 갖게 된다.
public class ex2 {
public static void main(String[]args) {
char ch = 'A'; //'\u0041'로 바꿔써도 같다.
int code = (int)ch; // ch에 저장된 값을 int형으로 변환하여 저장한다.
System.out.println(ch);
System.out.println(code);
}
}
[실행결과]
A
65
char 형 변수에 저장되는 값은 부호 없는 정수의 형태로 저장된다. 위 코드에서 'A' 에는 유니코드인 65가 저장된다. 모든 데이터는 숫자로 저장되기 때문이다. println()은 값을 출력할 때 변수의 타입에 따라 정수값을 출력하거나 정수값에 해당하는 문자를 찾아 출력한다.
영문자 이외에 특수분자를 저장하려면 아래의 표를 보면 된다.
특수문자 |
리터럴 |
특수문자 |
리터럴 |
tab |
\t |
backspace |
\b |
from feed |
\f |
new line |
\n |
carriage return |
\r |
역슬래쉬(\) |
\\ |
작은따옴표 |
\' |
큰따옴표 |
\" |
유니코드(16진수)문자 |
\u 유니코드 ( ex : char a = '\u0041') |
public class ex2 {
public static void main(String[]args) {
char single = '\'';
String dblQuote = "\"Hello\"";
String root = "c:\\";
System.out.println(single);
System.out.println(dblQuote);
System.out.println(root);
}
}
[실행결과]
'
"Hello"
c:\
여러 문자를 저장하기 위해서는 String 클래스를 사용해야 한다.
덧셈 연산자를 이용하여 문자열을 결합할 수 있다.
String name = "Ja" + "va"; //name에는 "Java"가 저장된다.
덧셈 연산자의 피연산자 중 어느 한 쪽이 String이면 나머지 한 쪽을 먼저 String으로 변환한 다음 두 String을 결합한다.
int 와 같은 기본형 타입의 값을 문자열로 변환할 때는 빈 문자열("")을 더해주면 된다.
public class ex2 {
public static void main(String[]args) {
String a = 7 + "";
String b = " " + 7;
String c = 7 + 7 + "";
String d = "" + 7 + 7;
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
[실행결과]
7
7
14
77
기본형과 참조형의 구별없이 어떤 타입의 변수도 문자열과 덧셈연산을 수행하면 결과는 문자열이 된다.
2.4 정수형 - byte, short, int, long
byte (1 byte) < short (2 byte) < int (4 byte) < long (8 byte)
byte 와 short 는 int 보다 크기가 작아 메모리를 절약할 수 있지만 저장할 수 있는 값의 범위가 작은 편이라서 연산 시에 범위가 넘어갈 수 있다.
또한, JVM의 피연산자 스택이 피연산자를 4 byte 단위로 저장하기 때문에 byte 나 short 의 값을 4 byte로 변환하여 연산이 수행된다. 따라서 int 를 사용하는 것이 효율적이다.
정수형 변수의 선언과 초기화
byte b = 1;
short s = 2;
int f = 10;
long l = 100000000000L; // long 타입의 리터럴에는 접미사 L을 붙여야 한다.
* long 타입의 리터럴 뒤에 'L' 또는 'l' 을 붙이지 않으면 int 타입으로 간주한다.
* 리터럴은 그 자체로 데이터인 것을 말한다. 상수와 의미가 같지만, 프로그래밍에서는 상수를 '값을 한 번 저장하면 변경할 수 없는 저장공간' 으로 정의하기 때문에 이와 구분하기 위해 리터럴이라는 용어를 사용한다.
16진수를 표시하기 위해서는 리터럴 앞에 접두사 '0x' 또는 '0X' 를 붙이고, 8진수에서는 '0'을 붙인다.
int octnum = 010; //8진수 10, 10진수로는 8
int hexnum = 0x10; //16진수 10, 10진수로는 16
정수형은 0과 1로 이루어진 2진수로 저장된다.
1 byte 는 8 bit 이므로 8자리로 구성되며 $2^8$ 즉, 256가지의 값을 표현할 수 있다.
모든 정수형은 0을 포함한 양수와 음수를 저장하는 값의 범위로 하기 때문에, 왼쪽에서 첫번째 자리를 부호자리로 사용한다. 그래서 1 byte 가 실질적으로 값을 표현할 수 있는 자리수는 7개가 된다.
[실행결과]
123 123
124 124
125 125
126 126
127 127
-128 128
-127 129
-126 130
-125 131
-124 132
위 실행결과에서 왼쪽은 byte, 오른쪽은 int 형 타입의 값이 270까지 증가한다. 하지만 byte 의 표현 가능한 정수의 범위는 127까지이기 때문에 오버플로우(overflow) 가 발생하여 최소값부터 다시 반복된다. 에러없이 실행되긴 하지만 원하는 결과를 얻을 수 없다.
그러나 변수의 범위를 넘는 값으로 초기화하게 된다면 컴파일했을 때 에러가 발생한다.
2.5 실수형 - float, double
float는 4 byte, double 은 8 byte로 int 와 long의 크기와 같지만, 정수형과는 저장방식이 달라, 훤씬 더 넓은 범위의 값을 표현할 수 있다.
실수형은 부동소수점 방식으로 저장된다.
부동소수점 : $\pm a\times10^n$ 의 형태로 표현되는데, a는 가수이고, n은 지수이다. 가수 a는 $0\le a<1$ 범위의 실수이다.
예를 들어, 3.1415를 부동소수점 방식으로 표현하면 $0.31415\times10^1$ 이며, 가수는 0.31415이고, 지수는 1이다.
float은 32 bit(4 byte) 중 S(부호)는 1, E(지수)는 8, M(가수)는 23으로 자리수가 할당되어 있다. 그에 비해, double은 64 bit(8 byte)는 S(부호)는 1, E(지수)는 11, M(가수)는 52로 자리수가 할당되어있다. 즉, double이 float 보다 가수가 두 배 이상으로 자리수가 배정되어 있기 때문에 정밀도(소수점 이하의 자리수)가 더 높은 값을 표현할 수 있다.
float은 가수를 10진수로 8자리 정도만 표현할 수 있기 때무네 높은 정밀도 계산에서는 double을 사용해야 한다.
public class ex2 {
public static void main(String[]args) {
float f = 1.2345678901234567890f;
float f2 = 0.100000001f;
double d2 = 0.100000001;
System.out.println(f);
System.out.println(f2);
System.out.println(d2);
}
}
[실행결과]
1.2345679 //끝자리에서 반올림되었다.
0.1
0.100000001
f2가 0.1로 출력된 이유는 저장된 값이 표현할 수 있는 가수의 자리수(정밀도)를 넘었기 대문이다.
float 형 리터럴에는 접미사 f가 사용되고, double은 d가 사용된다.
* 접미사는 대소문자를 구분하지 않는다.
float tpi = 3.14f; //f 대신 F 사용 사능
double velocity = 3.0e5d; //d 대신 D, e 대신 E 사용 가능
double rate = 1.618; //접미사 d 생략 가능
실수형에서는 double 이 기본 자료형이기 때문에 접미사를 생략하면 double 형 리터럴로 간주된다.
float pi = 3.14; //에러. float 변수에 double 형 리터럴을 저장할 수 없다.
* 리터럴에 접미사가 붙는 자료형은 long, float, double 인데, double 은 생략가능하므로, long 과 float에만 잘 붙이면 된다.
리터럴에 소수점이나 10의 제곱을 나타내는 E, e, 접미사 f, F, d, D, l, L 을 포함하면 실수형 리터럴로 간주된다.