Java/라이브스터디

자바 데이터 타입, 변수 그리고 배열

목표

자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법 익히기.

학습목록

  1. 프리미티브 타입 종류와 값의 범위 그리고 기본 값
  2. 프리미티브 타입과 레퍼런스 타입
  3. 리터럴
  4. 변수 선언 및 초기화하는 방법
  5. 변수의 스코프와 라이프타임
  6. 타입 변환, 캐스팅 그리고 타입 프로모션
  7. 1차 및 2차배열 선언하기
  8. 타입 추론, var





1. 프리미티브 타입( 기본형 타입 )의 종류와 값의 범위, 그리고 기본 값

프리미티브 타입 : 자바 언어에서 기본적으로 정의된 데이터타입


기본적으로 8개의 타입을 제공하며 다음 표와 같습니다.

자료형기본 값비트 수범위

자료형 기본 값 비트 수 범위
byte 0 8 bit ( 1 byte ) -128 ~ 127
short 0 16 bit ( 2 byte ) -32_768 ~ 32_767
int 0 32 bit ( 4 byte ) -231 ~ 231- 1
long 0L 64 bit ( 8 byte ) -263 ~ 263- 1
float 0.0f 32 bit ( 4 byte ) 32비트 IEEE 754 표준에 따름
double 0.0d 64 bit ( 8 byte ) 64비트 IEEE 754 표준에 따름
boolean false ? true or false
char '\u0000' 16 bit Unicode Characher '\u0000' ~ '\uffff'


비트의 숫자표현


먼저 왜 값의 범위가 표와 같이 표현되는지를 알아보겠습니다.

0000 0000(2)

8 비트를 2진수로 표현하면 위와 같이 8개의 0과 1의 조합으로 표현할 수 있습니다.
컴퓨터는 기본적으로 2진법으로 계산을 하기때문에 각 자리는 오른쪽부터 1, 2, 4, 8 ... 과 같이 2의 승수로 표현됩니다.


그럼 만들 수 있는 제일 큰 숫자는 무엇일까요?

1111 1111(2)

아마 모든 비트에 1을 채운 이 표현이 제일 큰 숫자가 될꺼라고 생각할 수 있습니다.
각각의 자리수가 2의 승수를 표기한다고 하였으니 각 자리수에 해당하는 값은 다음과 같습니다.

  1  1  1  1 1 1 1 1 
128 64 32 16 8 4 2 1

이제 각 자리의 값을 서로 곱한다음 모두 더해 봅시다.

128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255

8비트로 표현할 수 있는 최대의 숫자는 255가 됩니다.

더보기

여기서 알 수 있는 사실이 하나 있는데, 바로 n비트까지의 합은 n+1비트 - 1 과 같은 값이 된다는 사실입니다.

예를 들어 오른쪽 끝 부터 8비트의 합은 255였습니다.

그리고 9비트 자리의 값은 2의 9승, 즉 256이 됩니다.



하지만, 맨 위의 표를 살펴보면 byte타입의 최대 값은 255가 아닌 127입니다.

이는 음수표현 때문에 발생한 일이기 때문에 음수표현법을 알아야 합니다!!
음수를 표현하기위하여 2의 보수를 사용하게 되는데, 2의 보수를 알기 전에 먼저 1의 보수를 배워야합니다.

0101 1010(2) 1의 보수 => 1010 0101(2)

1의 보수는 2진수로 표현된 숫자에서 0과 1을 뒤집은 숫자를 의미합니다.

0101 1010(2) 
=> (1의 보수)

1010 0101(2) 
=> (2의 보수)

1010 0110(2)

0101 1010(2) 
=> 64 + 16 + 8 + 2 : 90 

1010 0110(2) 
=> -90

여기서 2의 보수는 1의 보수가 적용된 값에서 오른쪽 끝 비트에 1을 더한 값을 의미합니다.
여기서 맨 앞비트를 부호비트라 표현하며, 맨 앞 비트가 1인 숫자는 음수를 의미하고 0인 숫자는 양수를 의미합니다.




MAX => 0111 1111(2) : 127 
MIN => 1000 0000(2) : -128

이에 따라 8비트의 최대값과 최소값을 구해보면 다음과 같습니다.

정수형 타입

byte : 4비트

byte a = -128;

표현 방식

MAX 
=> 0111 1111(2) : 127 

MIN 
=> 1000 0000(2) : -128

최대값과 최소값

short : 8비트

short a = -32_768;

표현 방식

MAX 
=> 0111 1111 1111 1111(2) : 32767 

MIN 
=> 1000 0000 0000 0000(2) : -32768

최대값과 최소값.

int : 16비트

int a = 100_000_000;

표현방식


MAX => 0111 1111 1111 1111 1111 1111 1111 1111(2) : 2_147_483_647 MIN => 1000 0000 0000 0000 0000 0000 0000 0000(2) : -2_147_483_648

MAX
=> 0111 1111 1111 1111 1111 1111 1111 1111(2) : 2_147_483_647 

MIN 
=> 1000 0000 0000 0000 0000 0000 0000 0000(2) : -2_147_483_648

최대값과 최소값.

long : 32비트

long l = 9_223_372_036_854_775_807L;

표현방식


MAX => 0111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111(2) : 9_223_372_036_854_775_807L MIN => 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(2) : -9_223_372_036_854_775_808L

MAX 
=> 0111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111(2) 
: 9_223_372_036_854_775_807L 

MIN 
=> 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(2) 
: -9_223_372_036_854_775_808L

최대값과 최소값.


실수형 타입


실수형 타입으로는 float와 double이 존재 합니다.
기본적으로 해당 타입들의 연산은 정수형과 달리 정확한 값이 아닌 근사값으로 취급 되어 계산됩니다.

double f = 10000.12345; 
double fb = 12345.10000; 
System.out.println(f + fb); 

=> 22345.223449999998 

사람의 생각으로 계산의 결과는 22345.22345 가 나오는게 맞습니다.

컴퓨터의 경우 비트연산을 하기 때문에 주어진 비트수를 가지고 정확한 연산을 하기에는 한계가 존재기에 근사값을 이용합니다.


값이 항상 정확해야하는 업무를 사용할 때는 BigDecimal 클래스를 이용할 수 있습니다.

BigDecimal f = new BigDecimal("12345.10000"); 
BigDecimal fb = new BigDecimal("10000.12345"); 
System.out.println(f.add(fb)); => 22345.22345

주의사항으로는 Bigdecimal의 생성자에 String 값을 사용해야 합니다.

참조 : https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html


float : 32비트

float f = 10.24f;

표현방식

1(부호) 000 0000 0(지수) 000 0000 0000 0000 0000 0000(가수)(2) 
-142.375의 IEEE 754 표기방법 

부호 비트가 음수이므로 
=> 1 

142.375의 절대값 
=> 10001110.011(2) 

이 절대값을 왼쪽에 1하나를 전부 소숫점 아래로 보낸다 
=> 1.0001110011 * 2의 7승 

지수부는 2의 승수인 7과 32 IEEE 754 형식의 Bias인 127을 더한다. 
=> 134 
=> 1000 0110(2) 

가수부는 오른쪽에 0을 채워 23비트로 만든다. 
=> 000 1110 0110 0000 0000 0000 

전부 합친다 
+ 1 
+ 1000 0110(2) 
+ 000 1110 0110 0000 0000 0000 
=> 1 1000 0110 000 1110 0110 0000 0000 0000(2) 
=> 1100 0011 0000 1110 0110 0000 0000 0000(2) : -142.375

IEEE 754 규격에 따른 부동소수점 표기법

double : 64비트

double db = 10.01d;

표현 방식

1(부호 : 1비트) 
000 0000 0000(지수 : 11비트) 
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(가수 : 52비트)

IEEE 754 규격 64비트 표기법

boolean : ? 비트

boolean bool = true;
boolean bool2 = false;

표현방식

boolean의 경우 비트 수가 명확하게 제시되어있지 않습니다.

참/거짓의 판단을 위해서는 1비트만 필요한게 논리적으로 맞지만 컴퓨터의 작업단위에 1비트만 처리하는 경우는 없기때문에 명확하게 비트수를 정해놓지 않고 참/거짓 판단이 가능하게만 비트수를 사용합니다.

char : 16비트 유니코드

char ch = 'a';

표현 방식

MAX VALUE 
`\uffff` : 65_535 

MIN VALUE 
'\u0000` : 0

최대 값과 최소 값

더보기

유니코드란 뭘까?

유니코드를 들으면 왠지 모르게 아스키코드가 생각날 수 있습니다.

ASCII코드는 8비트 ( 1바이트 )를 사용하며 UNICODE는 16비트 ( 2바이트 )를 사용합니다.

ASCII 코드의 경우 1비트를 통신 에러 검출 비트로 사용하여 총 128개의 기호표현이 가능했습니다.

이후 7비트를 가지고 다른언어를 표현하기에 부족하다는 결론에 의하여 8비트를 다 사용하는 ANSI 코드가 등장했습니다.

ANSI 코드의 경우 256가지의 기호표현이 가능하였으나, 아시아쪽 언어를 표현하는데에는 부족하였고 그 대안으로 16비트를 사용하는 UNICODE가 등장하였습니다

.

총 65536가지의 기호표현이 가능하며 16진법을 이용해서 표기할 수 있습니다.

0000 : 0 
FFFF : 65535 

16 * 16 * 16 * 16 = 65536

2. 레퍼런스 타입

자바에는 레퍼런스 타입이이라는 것이 존재합니다.

참조타입, 즉 참조타입의 변수는 특정한 값을 가지는 것이 아니라 주소값을 가집니다.

자바에서 주소값을 가진다고 생각되는 변수타입은 다음과 같습니다.

  • 배열
  • 클래스
  • 인터페이스
  • 열거 (Enum)

레퍼런스타입의 변수는 주소값을 가지고있기 때문에 null을 기본값으로 줄 수 있으며, 앞서서 설명했던 프리미티브타입을 레퍼런스 타입화 시킨 클래스는 java.lang.Number 밑에 존재하고있습니다.

  • Byte
  • Short
  • Integer
  • Long
  • Float
  • Double
  • BigInteger

리터럴(Literals)

이 값은 이걸 표현한거다! 라는게 리터럴이라고 생각할 수 있습니다.

프리미티브 타입에 각각에 리터럴를 표현할 수 있습니다.

정수형 리터럴

정수형의 표현은 2진법, 10진법, 16진법 등등이 존재하며 표현은 다음과 같이 할 수 있으며 출력은 10진법의 형태로 나타납니다.

// Decimal : 10진법 
int DecVal = 10; 

// Hexadecimal : 16진법 
int HexVal = 0xff; 

// Binary : 2진법 
int BinVal = 0b00101; 


====== 출력결과 ====== 
10 
255 
5

이 외에 long 타입의 변수를 선언할 때, 뒤에 L을 붙히는 것도 리터럴에 해당합니다.

실수형 리터럴

자바의 실수형 프리미티브 타입의 경우 float와 double이 존재합니다.

float의 경우 f를 붙혀서 표기하며,
double의 경우 아무것도 붙히지 않거나 d를 붙혀서 사용합니다.

double dbNum = 10.214; 
double dbNum2 = 1.0214e1; 
float fNum = 10.214f;

여기서 실수형 표기를 할때 1.0214e1과 같이 표기하는 것은 e 뒤에 붙은 숫자만큼 소숫점을 오른쪽으로 이동시키겠다. 라는 의미를 가지고있습니다.

문자/문자열 리터럴


char과 String 모두 유니코드( UTF-16 )문자가 포함될 수 있습니다.

편집기나 파일 시스템을 허용하는 경우 유니코드를 코드에서 사용할 수 있으며, 그렇지 않은경우 '\u0108' 과 같은 형식의 유니코드 이스케이프를 사용할 수 있습니다.

char타입의 경우 작은 따옴표 '를 이용해서 선언해야하며 String 타입의 경우 큰 따옴표 "를 이용해서 선언해야합니다.

추가적으로 자바에서는 char과 String에 이스케이프 문자들을 지원합니다.

  • \b : 백 스페이스
  • \t : 탭
  • \n : 줄변경
  • \f : 커서를 다음페이지의 시작위치로 넘김
  • \r : 커서를 맨앞으로 옮겨서 덮어쓰기
  • " : 쌍따옴표
  • ' : 작은 따옴표
  • \ : 역슬래시

String str = "backspace : a\b"; 
System.out.println(str); 

str = "tab : \t"; 
System.out.println(str); 

str = "lind feed : \n"; 
System.out.println(str); 

str = "form feed : \f"; 
System.out.println(str); 

str = "carriage return : \ra"; 
System.out.println(str); 

str = "double quote : \""; 
System.out.println(str); 

str = "single quote : \'"; 
System.out.println(str); 

str = "backslash : \\"; 
System.out.println(str); 


========== 출력 결과 =========== 
backspace : 
tab : 
lind feed : 

form feed :
a 
double quote : " 
single quote : ' 
backslash : \

수 리터럴 표현 시의 _ 사용

자바에서는 수의 할당시에 다음과 같은 표현이 가능합니다.

long creditCardNumber = 1234_5678_9012_3456L; 
long socialSecurityNumber = 999_99_9999L; 
float pi = 3.14_15F; 
long hexBytes = 0xFF_EC_DE_5E; 
long hexWords = 0xCAFE_BABE; 
long maxLong = 0x7fff_ffff_ffff_ffffL; 
byte nybbles = 0b0010_0101; 
long bytes = 0b11010010_01101001_10010100_10010010;


어느위치에든 가능한 것은 아니고 다음의 경우에는 사용이 불가능합니다.

  • 수의 시작과 끝
  • 소수점 표현 시 .의 앞 뒤
  • long이나 float 등의 표현 시에 붙는 F와 L 앞
  • 숫자 문자열이 예상되는 위치
// 불가능
// adjacent to a decimal point 
float pi1 = 3_.1415F; 

// 불가능
// adjacent to a decimal point 
float pi2 = 3._1415F; 

// 불가능
// prior to an L suffix 
long socialSecurityNumber1 = 999_99_9999_L; 

// 가능 (decimal literal) 
int x1 = 5_2; 

// 불가능
// At the end of a literal 
int x2 = 52_; 

// 가능 (decimal literal) 
int x3 = 5_______2; 

// 불가능 
// in the 0x radix prefix 
int x4 = 0_x52; 

// 불가능
// at the beginning of a number 
int x5 = 0x_52; 

// 가능 (hexadecimal literal) 
int x6 = 0x5_2; 

// 불가능
// at the end of a number 
int x7 = 0x52_;

변수의 선언과 초기화

변수의 선언과 초기화는 정해진 형식을 따라서 표현할 수 있습니다.

프리미티브 타입의 경우 아래의 형식으로 선언과 초기화를 할 수 있습니다.

[프리티미브 타입] [변수명] = [리터럴];



레퍼런스 타입의 경우 아래의 형식으로 선언과 초기화를 할 수 있습니다.

// 클래스의 경우 
MYCLASS myclass = new MYCLASS(); 

// 배열의경우 
int[] arr = new int[10]; 

// 인터페이스의 경우
MYINTERFACE myinterface = new MYINTERFACE() {
	@Override public void something() {
    
    } 
};

변수의 스코프와 라이프타임

변수의 스코프

변수의 스코프란, 변수가 사용될 수 있는 공간을 의미합니다.



각각의 변수의 활동 공간은 위 이미지와 같습니다.

  • a : scope 클래스 전체
  • b : Method() 메소드 내부
  • c : Method2() 메소드 내부

변수의 라이프타임

라이프타임은, 변수에 메모리가 언제 할당되고, 언제 반환되는가를 의미합니다.

class Main{
  public static void main(){
    int a = 10;
    subclass s = new subclass();
  }
}

class subclass{
  int subclass_a = 10;
}

간단한 소스코드를 통하여 살펴봅시다.

일단 Main 클래스와 subclass의 경우 static 영역에 해당하기 때문에 프로그램이 동작하자마자 메모리에 올라가고, 프로그램이 종료되면 반환됩니다. ( static 으로 선언된 main() 도 동일 )

내부의 변수 a는 해당 줄에 도착하고나면 메모리가 할당되고, 메소드가 종료되면 반환됩니다.

subclass 타입의 변수 s의 경우, 객체가 선언되는 순간 Heap 영역에 subclass 객체가 올라가고 main() 메소드 종료시에 모두 반환됩니다.

이와 같이 특정 변수나 클래스 객체 등등이 메모리에 올라가고 내려오는 그 시간을 라이프 타임이라고 부릅니다.

타입 변환, 캐스팅 그리고 타입 프로모션


자바에서 타입의 변환에는 두 가지의 방법이 존재합니다.

  1. 묵시적 형 변환 ( 프로모션 )
  2. 명시적 형 변환 ( 캐스팅 )

묵시적 형 변환( 프로모션 )


작은 크기의 변수 -> 큰 크기의 변수로의 변환

  • byte -> short
  • int -> long
  • float -> double
byte a = 10;
short b = 20;
int c = 30;
long d = 40L;
float e = 50.0f;
double f = 60.0;

System.out.println(a);
b = a;
System.out.println(b);
c = b;
System.out.println(c);
d = c;
System.out.println(d);
e = d;
System.out.println(e);
f = e;
System.out.println(f);

====== 출력결과 ======
10
10
10
10
10.0
10.0
byte var1 = 10;
boolean var2 = true;
boolean var3 = true;
long var4 = 40L;
float var6 = 50.0F;
double var7 = 60.0D;
System.out.println(var1);
short var9 = (short)var1;
System.out.println(var9);
System.out.println(var9);
var4 = (long)var9;
System.out.println(var4);
var6 = (float)var4;
System.out.println(var6);
var7 = (double)var6;
System.out.println(var7);

바이트코드를 디컴파일하여 확인해보면 자바컴파일러가 알아서 형변환을 해주는 것을 확인할 수 있습니다.

명시적 형 변환 ( 캐스팅 )

큰 타입의 변수 -> 작은 타입의 변수로의 변환

자바 컴파일러가 알아서 형 변환을 해주지않고 개발자가 직접 캐스팅하여 변환해야 합니다.

double db = 10.124;
float f = (float)db; // 캐스팅
System.out.println(f);

명시적 형 변환의경우, 개발자가 캐스팅을 해주지않으면 컴파일이 되지 않습니다.


1차원, 2차원 배열 선언

int[] arr = new int[5];
int[] arr2;
arr2 = new int[5];

int arrsize = 10;
int[] arr3 = new int[arrsize];

int[] arr4 = {1,2,3,4};

자바에서 배열의 선언방식은 여러가지가 존재합니다.


System.out.println(arr4[0]);
System.out.println(arr4[1]);
System.out.println(arr4[2]);
System.out.println(arr4[3]);

===== 출력 결과 =====
1
2
3
4

각 요소의 접근은 인덱스를 이용하여 할 수 있습니다.

int[][] arr = new int[1][2];
int[][] arr2;
arr2 = new int[3][4];

int[][] arr3 = {% raw %}{{1,2,3,4},
                {5,6,7,8}}{% endraw %};

for (int[] ints : arr3) {
    for (int anInt : ints) {
        System.out.println(anInt);
    }
}

int[] arr4 = {1,2,3,4};
int[] arr5 = {5,6,7,8};
int[][] arr6 = new int[2][4];
arr6[0] = arr4;
arr6[1] = arr5;

for (int[] ints : arr6) {
    for (int anInt : ints) {
        System.out.println(anInt);
    }
}

===== 출력 결과 ======

1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8

2차원 배열의 선언방식

배열의 경우 모든 프리미티브타입과 String 타입, 객체등을 지원합니다.

타입 추론, var

자바 10에서 등장한 개념으로 알아서 입력된 값에 따라 자료형이 맞춰지는 기능입니다.

var string = "String";
var Int = 10;
var child = new Child();
var arr = new int[4];
var CLASS = new Child(){
    @Override
    public void show() {
        super.show();
    }
};
CLASS.show();


이렇게 익명클래스, 객체, 문자열 등등 모든 타입을 맞춰서 변환해줍니다.

String var1 = "String";
boolean var2 = true;
new Child();
int[] var4 = new int[4];
1 var5 = new 1();
var5.show();

바이트코드를 디컴파일해보면 타입이 변해있는 것을 확인할 수 있습니다,.

참고자료