JAVA LV 1-4

기본형

Posted by Gihoon on March 13, 2023

- 논리형 (boolean)

논리형에는 boolean 한가지 밖에 없다. boolean형 변수에는 true와 false중 하나를 저정할 수 있으며 기본값은 false이다.

boolean형 변수는 대답,스위치 등의 논리 구현에 주로 사용된다 그리고 boolean형은 true, false, 두가지의 값만을 표현하면 디므로 1 bit만으로도 충분하지만, 자바에서는 데이터를 다루는 최소단위가 byte이기 때문에 , boolean의 크기가 1byte이다.

1
2
3
4
5
boolean power = true;

boolean checked = False; // 에러. 대소문자가 구분됨 .true 또는 false만 가능

자바에서는 대소문자가 구별되므로 TRUE와 true는 다르다는것을 주의하자.

- 문자형 (char)

문자형 역시 ‘char’ 한가지 자료형 밖에 없다.

char ch = ‘A’; // 문자 ‘A’를 char타입의 변수 ch에 저장

위의 문장은 변수에 ‘문자’가 저장되는 것 같지만, 사실은 문자가 아닌 ‘문자의 유니코드(정수)’가 저장된다. 컴퓨터는 숫자밖에 모르기 때문에 숫자로 변환하여 저장하는 것이다. 문자 ‘A’의 유니코드는 65이므로 , 변수 ch에는 65가 저장된다. 그래서 문자 리터럴 대신 문자의 유니코드를 직접 저장할 수도 있다.

문자 ‘A’의 유니코드는 10진수로 65이며, 아래의 두 문자은 동일한 결과를 얻는다

1
2
3
char ch = 'A';

char ch = 65;

만일 어떤 문자의 유니코드를 알고 싶으면 char형 변수에 저장된 값을 정수형 (int)으로 변환하면 된다.

1
2
3
4
5
6
7
8
9
char ch = 'B';

int code = (int)ch;

​

System.out.println(code);

// ch에 저장된 값을 int타입으로 변환하여 

어떤 타입을 다른 타입으로 변환하는 것을 형변환(캐스팅)이라고 하는데, 형변환에 대해서는 다음 단원에 자세히 배울것이다.

특수 문자 다루기

영문자 이외에 tab이나 backspace등의 특수문자를 저장하려면, 아래와 같이 특별한 방법을 사용한다.

char tap = ‘/t’; // 변수 tap에 탭 문자를 저장

‘/t’는 실제로는 두 문자로 이루어져 있지만 한문자 (탭)를 의미한다.

1
2
3
4
5
6
7
8
9
10
System.out.println('\''); // '''처럼 할수 없다

System.out.println("abc\t123\b456"); // \b에 의해 3이 지워진다.

System.out.println('\n'); // 개행(new line)문자 출력하고 개행

System.out.println("\"Hello\""); // 큰 따옴표를 출력하려면 이렇게 한다.

System.out.println("c:\\");

char타입의 표현형식

char타입의 크기는 2byte이므로, 16자리의 2진수로 표현할수 있는 정수의 개수인 65536개 (2^16)의 코드를 사용할 수 있으며, char 형 변수는 이 범위 내의 코드 중 하나를 저장할 수 있다. 예를 들어 문자 ‘A’를 저장하면, 아래와 같이 2진수 ‘0000000001000001’(10진수로 65)로 저장된다.

char타입은 정수형과 달리 음수를 나타낼 필요가 없으므로 표현할 수 있는 값의 범위가 다르다.

2 byte (16bit) 로는 모두 2^16개의 값을 표현할 수 있는데, char타입에 저장되는 값인 유니코드는 모두 양수이므로 ‘0

~65535’의 범위를 가지며 정수형인 ‘short’은 절반을 음수표현에 사용하므로 -32786~

32767’을 범위로 갖는다.

인코딩과 디코딩

컴퓨터는 숫자밖에 모르기 때문에 문자가 숫자로 변환되어 저장되는데 이는 유니코드의 기준에 의한 것이다.

문자 ‘A’의 유니코드는 65이다. 그래서 문자 ‘A’를 유니코드로 인코딩 하면 65가 되는 것이다. 반대로 65를 유니코드로 디코딩 하면 ‘문자 A’가 된다. 이처럼 문자를 코드로 변환하는 것을 ‘문자 인코딩’ 코드를 문자로 변환하는 것을 ‘문자 디코딩’이라고 한다.

- 정수형 (byte, short, int, long)

정수형에는 모두 4개의 자료형이 있으며, 각 자료형이 저장할 수 있는 값의 범위가 서로 다르다.

byte부터 long 까지 1byte로 시작해서 2배씩 크기가 커진다. 이중에서도 기본 자료형은 int 이다.

정수형의 표현형식과 범위

어떤 진법의 리터럴을 변수에 저장해도 실제로는 2진수로 바뀌어 저장된다. 이 2진수가 저장되는 형식은 크게 정수형과 실수형이 있으며, 정수형은 다음과 같은 형식으로 저장된다. S - n-1 bit 이때 S는 부호비트 n은 타입의 크기이다.

모든 정수형은 부호있는 정수이므로 왼쪽의 첫 번째 비트를 ‘부호 비트’로 사용하고, 남는 값을 표현하는데 사용한다. 그래서 n비트로 표현할 수 있는 값의 개수인 2^n개에서, 절반인 ‘0’으로 시작하는 2^n-1개의 값을 양수의 표현에 사용하고 나머지 절반인 1’1’로 시작하는 2^n-1개의 값은 음수의 표현에 사용한다. 따라서

n비트로 표현할 수 있는 정수의 개수 : 2^n개

n비트로 표현할 수 있는 부호있는 정수의 범위 : -2^n-1 ~ 2^n-1-1

위의 최대값에서 1을 뺴는 이유는 범위에 0이 포함되기 때문이다.

정수형의 선택기준

변수에 저장하려는 정수값의 범위에 따라 4개의 정수형중에서 선택하면 되겠지만 byte,short 보다 int를 사용하자.

그리고 JVM의 피연산자 스택이 피연산자 4byte단위로 저장하기 때문에 크기가 4byte보다 작은 자료형의 값을 계산할때는 4byte로 변환하여 연산이 수행되므로 int 를 사용하는것이 효율적이다. 결론적으로 정수형 변수를 선언할 때는 int타입으로 하고, int의 범위 (약 +-20억개)를 넘어서는 long을 사용하면 된다.

정수형의 오버플로우

만일 4bit 2진수의 최대값인 ‘1111’에 1을 더하면 어떤 결과를 얻을까?

원래 2진수 ‘1111’에 1을 더하면 ‘10000’이 되지만 4비트로는 4자리의 2진수만 저장 할 수 있기 때문에 ‘0000’이 된다. 즉 5자리의 2진수 ‘10000’중에서 하위 4bit만 저장한다. 이처럼 연산 과정에서 타입이 표현할 수 있는 값의 범위를 넘어서는 것을 오버플로우라고 한다. 꼭 오버플로우가 발생했다고 해서 에러가 나는 것은 아니다. 다만 예상한 결과가 나오지 않는 것이다.

부호있는 정수의 오버플로우

부호없는 정수와 부호있는 정수는 표현범위가 다륵 때문에 오버플로우가 발생하는 시점이 다르다. 부호없는 정수는 2진수로 ‘0000’이 될때 오버플로우가 발생하고, 부호있는 정수는 부호비트가 0에서 1이 될때 오버플로우가 발생한다.부호없는 정수의 경우 표현범위가 ‘0

~15’일때 부호있는 정수는 ‘-8~

7’로 이 값이 무한히 반복된다..

- 실수형 (float,double)

실수형의 범위와 정밀도

실수형은 실수를 저장하기 위한 타입으로 float, double, 두 가지가 있다.
연산속도의 향상이나 메모리를 절약하려면 float를 선택하고, 더 큰 값의 범위라던가 더 높은 정밀도를 필요로 한다면 double을 선택해야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public static void main(String[] args) {

float f = 9.12345678901234567890f;

float f2 = 1.2345678901234567890f;

double d = 9.12345678901234567890d;

​

System.out.printf(" 123456789012345678901234%n");

System.out.printf("f : %f%n", f); //  Ҽ         6° ڸ         .

System.out.printf("f : %24.20f%n", f);

System.out.printf("f2 : %24.20f%n", f2);

System.out.printf("d : %24.20f%n", d);

실행결과 :

     123456789012345678901234

f  : 9.123457

f  :   9.12345695495605500000

f2 :   1.23456788063049320000

d  :   9.12345678901234600000

실수형 값을 출력할 때는 printf 메서드의 지시자 ‘%f:를 사용한다. ‘%f’는 기본적으로 소수점 이하 6자리만 출력하므로 7번쨰 자리에서 반올림 되어진다.

‘%24.20f’는 전체 24자리 중에서 20자리는 소수점이하의 수를 출력하라는 뜻이다.앞뒤의 빈자리는 공백과 0으로 채워진다.

float은 정밀도가 7자리이다.따라서 double과 값이 일치하고자 하는 부분은 앞의 7자리 만이다.

실수형의 저장형식

실수형은 값을 부동소수점수의 형태로 저장한다. 부동소수점수는 부호,지수,가수,모두 세부분으로 이루어져있다.

(1) 부호 (Sing bit)

‘S는 부호비트를 의미하며 1bit 이다. 이값이 0이면 양수를 1이면 음수를 의미한다. 정수형과 달리 ‘2의 보수법’을 사용하지 않기 때문에 양의 실수를 음의 실수로 바꾸려면 부호비트만 0에서 1로 변경하면 된다.

(2) 지수 (Exponent)

‘E’는 지수를 저장하는 부분으로 float의 경우 8bit의 저장공간을 갖는다. 지수는 ‘부호있는 정수’이고 8비트로는 256개의 값을 저장할 수 있으므로

-127~128의 값이 저장된다. float타입으로 표현할 수 있는 최대값은 2^127이고 10진수로는 약 10^38이다. 그러나 최소값은 가수의 마지막 자리가 2^-23이므로 지수의 최소값보다 2^-23배나 더 작은 값 약 10^-45이다.

(3) 가수 (Mantissa)

‘M’은 실제 값인 가수를 저장하는 부분으로 float의 경우 2진수 23자리를 저장할 수 있다. 이것은 약 7자리의 10진수를 저장할 수 있는데 이것이 정밀도가 된다.

double은 52자리로 float보다 약 2배이므로 2배의 정밀도를 갖는것이다.

부동소수점의 오차

실수 중에는 파이같은 무한소수가 존재하므로 정수와 달리 실수를 저장할 때는 오차가 발생할 수 있다. 게다가 2진수로 저장하기 때문에 10진수로는 유한소수더라도 2진수로 변환하면 무한소수가 되는 경우도 있다. 2진수로 변환된 실수를 저장할 때는 먼저 ‘1.xxx*2^n’의 형태로 변환하는데 이 과정을 정규화 라고한다.

정규화된 2진 실수는 항상 1로 시작하기때문에 1을 제외한 23자리의 2진수가 가수로 저장되고 그 이후는 잘려나간다.

- 형변환

형변환이란, 변수 또는 상수의 타입을 다른 타입으로 변환하는 것이다.

형변환 방법은 아주 간단하다 . 형변환하고자 하는 변수나 리터럴의 앞에 변환하고자 하는 타입을 괄호와 함께 붙여주기만 하면 된다. ex) (타입)피연산자

여기에 사용되는 괄호()는 ‘캐스트 연산자’ 또는 ‘형변환 연산자’라고 하며 형변환을 ‘캐스팅’이라고도 한다.

1
2
3
double d = 85.4;

int score = (int)d; // double타입의 변수 d를 int 타입으로 형변환

연산자는 그저 피연산자의 값을 읽어서 지정된 타입으로 형변환 하고 그 결과를 반환할 뿐이다. 그래서 피연산자인 변수 d의 값은 형변환 후에도 아무런 변화가 없다.

기본형에서 boolean을 제외한 나머지 타입들은 서로 형변환이 가능하다.그리고 기본형과 참조형간의 형변환은 불가능하다.

float타입의 값을 int타입으로 변환할 때 소수점 이하의 값은 반올림이 아닌 버림으로 처리된다는 점을 주의하자.

정수형간의 형변환

큰 타입에서 작은 타입으로의 변환, 예를 들어 int에서 byte 타입으로 변환하는 경우에 크기의 차이만큼 잘려나간다. 따라서 경우에 따라 ‘값 손실’이 발생할 수 있다.

반대로 작은 타입에서 큰 타입으로 변환에서는 값손실이 일어나는 경우가 없고 빈공간은 0 또는 1로 채워진다. 원래는 빈공간은 0으로 채우는것이 보통이지만 변환하려는 값이 음수인 경우에는 빈공간을 1로 채운다. 형변환 후에도 부호를 유지할 수 있도록 하기 위해서이다.

실수형 간의 형변환

실수형에서도 정수형처럼 작은 타입에서 큰 타입으로 변환하는 경우, 빈공간을 0으로 채운다. float 타입의 값을 double 타입으로 변환하는 경우 지수는 float의 기저인 127을 뺀후 double 기저인 1023을 더해서 변환하고 가수는 float의 가수 23자리를 채우고 남은 자리를 0으로 채운다.

더 자세한 내용은 실습을 통해 알아보자.

정수형과 실수형 간의 형변환

정수형을 실수형으로 변환하는 것은 간단하다. 그저 정수를 2진수로 변환한 다음 정규화를 거쳐 실수의 저장형식으로 저장될 뿐이다.

float는 10진수로 약 7자리의 정밀도만을 제공하므로 int를 float로 변활 할 때 정밀도 차이에 의한 오차가 발생할 수 있다. 따라서 10진수로 8자리 이상의 값을 실수형으로 변환할때는 double로 형변환해야 오차가 발생하지 않는다.

실수형을 정수형으로 변환

실수형을 정수형으로 변환하면, 실수형의 소수점 이하 값은 버려진다. 또한 형변활할때 반올림이 발생하지 않는다 예를 들어 1.666을 int로 형변환하면 1이 된다.

만일 실수의 소수점을 버리고 남은 정수가 정수형의 저장범위를 넘는 경우에는 정수의 오버플로우가 발생한 결과를 얻는다.

- 자동 형변환

서로 다른 타입간의 대입이나 연산을 할 떄 형변환으로 타입을 일치시키는 것이 원칙이지만 편의상의 이유로 형변환을 생략할 수 있다.

컴파일러가 생략된 형변환을 자동적으로 추가할 수 있다. float f = 1234; // 형변환의 생략 float f = (float)1234; 와 같음

그러나 다음과 같이 변수가 저장할 수 있는 값의 범위보다 더 큰 값을 저장하려는 경우에는 형변환을 새약하면 에러가 발생한다

byte b = 1000; // 에러 byte의 범위를 넘는 값을 저장

다음과 같이 명시적으로 형변환을 해주면 컴파일러는 에러를 발생시키지 않는다

char ch = (char) 1000;

또 다른 예로 다음과 같은 계산식에서 자주 형변환이 생략되는데 서로다른 두 타입의 연산에서는 먼저 타입을 일치시킨 다음에 여난을 수행해야 하므로, 연산과정에서 형변환이 이루어 진다.

int i =3;

double d = 1.0 + i; // double(i)에서 형변환이 생략됨

두 타입중 표현범위가 더 넓은 타입으로 형변환하여 타입을 일치시킨 다음에 연산을 수행한다.이렇게 하는것이 값손실의 위험이 적어 올바른 결과를 얻을 확률이 높다.

이처럼 연산과정에서 자동적으로 발생하는 형변환을 ‘산술 변환’이라고 한다.

자동 형변환의 규칙

‘기존의 값을 최대한 보존할 수 있는 타입으로 자동 형변환한다.’ 보통 자료형의 크기가 클 수록 표현범위가 크기 마련이지만 같은 크기 일지라도 실수형이 정수형보다 훨씬 더 큰 표현 범위를 갖기 때문에 float와 double이 같은 크기인 int와 long보다도 더 크게 위치한다.

char과 short 은 둘다 2바이트의 크기로 크기가 같지만 서로 범위가 달라서 둘중 어느쪽으로도 형변환의 값 손실이 발생함으로 자동형변환이 수행될 수 없다. 결론적으로

1.boolean을 제외한 나머지 7개의 기본형은 서로 형변환이 가능하다.

2.기본형과 참조형은 서로 형변환할수 없다.

3.서로 다른 타입의 변수간의 연산은 형변환을 하는 것이 원칙이지만, 값의 범위가 작은 타입에서 큰 타입으로의 형변환은 생략할 수 있다.

기본형은 모두 8개이다 boolean, byte, short, char, int, long, float, double . 그 외의 타입은 모두 참조형 이다