본문 바로가기
자바

[Java] 자바 데이터 타입(자료형), 변수 그리고 배열

by 책 읽는 개발자_테드 2021. 8. 18.
반응형

학습할 것

- 프리미티브 타입 종류, 값의 범위, 기본

- 프리미티브 타입과 레퍼런스 타입

- 리터럴

- 변수 선언 초기화

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

- 타입 변환, 캐스팅, 타입 프로모션

- 배열

- 타입 추론, var


프리미티브 타입 종류, 값의 범위, 기본 값

 · 기본 타입(primitive type)이란 정수, 실수, 문자, 논리 리터럴을 저장하는 타입


각 타입들의 메모리 사용 크기, 값의 범위, 기본 값

값의 종류 기본 타입 메모리 사용 크기 저장되는 값의 범위 기본값
정수 byte 1byte/8bit -2^7 ~ 2^7-1 0
  char 2byte/16bit 0 ~ 2^16(유니코드:'\u0000 ~ \uffff') '\u0000'
  short 2byte/16bit -2^15 ~ 2^15-1 0
  int 4byte/32bit -2^31 ~ 2^31-1 0
  long 8byte/64bit -2^63 ~ 2^62-1 0L
실수 float 4byte/32bit (3.4 X 10^-38) ~ (3.4 X 10^38) 0.0f
  double 8byte/64bit (1.7 X 10^-308) ~ (1.7 X 10^308) 0.0d or 0.0
논리 boolean 1byte/8bit true, false false

 

프리미티브 타입과 레퍼런스 타입

자바의 데이터 타입은 크게 기본 타입(원시 타입:primitive type)과 참조 타입(reference type)으로 분류된다.
기본 타입은 정수, 실수, 문자, 논리 리터럴을 저장하는 타입이다.
참조 타입이란 객체(Object)의 번지를 참조하는 타입으로 배열, 열거, 클래스, 인터페이스 타입을 말한다.

 

기본 타입과 참조 타입으로 선언된 변수의 차이점은 저장되는 값이다.
기본 타입으로 선언된 변수는 실제 값을 변수 안에 저장하고,
참조 타입으로 선언된 변수는 메모리의 주소를 값으로 갖는다. 주소를 통해 객체를 참조한다는 뜻에서 참조 타입이라고 한다.

그림1

 

 

리터럴(Literal)

 · 변수의 초기값 중 소스 코드 내에서 직접 입력된 값

 · 리터럴은 값의 종류에 따라 정수 리터럴, 문자 리터럴, 논리 리터럴로 구분

 

 · 리터럴은 상수(constant)와 같은 의미지만, 프로그램에서는 상수를 "값을 한 번 저장하면 변경할 수 없는 변수"로 정의하기 때문에 이와 구분하기 위해 "리터럴"이라는 용어를 사용

 

정수 리터럴

 · 정수 리터럴을 저장할 수 있는 타입: byte, char, short, int, long

 

 · 소수점이 없는 리터럴은 10진수로 간주
ex)0, 75, -100

 

 · 0으로 시작하는 리터럴은 8진수로 간주
ex) 02, -04

 

 · 0x 또는 0X로 시작하고 0~9 숫자나 A, B, C, D, E, F 또는 a, b, c, d, e, f로 구성된 리터럴은 16진수로 간주

ex)0x5, 0xA, 0xB3, 0xAC08

 

실수 리터럴

 · 실수 리터럴을 저장할 수 있는 타입: float, double

 

 · 소수점이 있는 리터럴은 10진수 실수로 간주
ex)0.24, -3.14

 

 · 대문자 E 또는 소문자 e가 있는 리터럴은 10진수 지수와 가수로 간주
ex) 5E7 //5 x 10⁷ 0.12E-5 //0.12 x 10⁻⁵

 

문자 리터럴

 · 작은 따옴표(')로 묶은 텍스트는 하나의 문자 리터럴로 간주
ex) 'A', '한', '\t', '\n'

 

 ·역슬래쉬()가 붙은 문자열 리터럴은 이스케이프(escape) 문자라고도 하며, 특수한 용도로 사용

이스케이프 문자 용도 유니코드
'\t' 수평 탭 0x0009
'\r' 리턴 0x000d
'"' 큰따옴표 0x0022
''' 작은따옴표 0x0027
'\'   0x005c
'\u16진수' 16진수에 해당하는 유니코드 0x0000 ~ 0xffff

 

문자열 리터럴

 · 문자열 리터럴을 저장할 수 있는 타입은 String 단 하나

 

 · 큰따옴표(")로 묶은 텍스트는 문자열 리터럴로 간주

 · 문자열 리터럴 내부에서도 이스케이프 문자를 사용 가능

ex)"탭 만큼 이동 \t 합니다." "한줄 내려 쓰기 \n 합니다."

 

논리 리터럴

 · 논리 리터럴을 저장할 수 있는 타입은 boolean 단 하나

 · true와 false는 논리 리터럴로 간주

 

변수 선언 및 초기화

자바는 아래와 같이 변수를 선언한다. 타입은 변수에 저장되는 값의 종류와 범위를 결정하고, 변수이름은 메모리 주소에 붙여진 이름이다.

프로그램은 변수이름을 통해 메모리 주소에 접근하고, 그곳에 값을 저장하거나 값을 읽는다.

그림3

변수 이름은 자바언어에서 정한 명명 규칙을 따라야 한다.

작성 규칙
첫 번째 글자는 문자이거나 '$','_'이어야 하고 숫자로 시작할 수 없다. 가능:price, $price, _price 불가능:1v, @speed, $#value
영어 대소문자가 구분된다. firstName과 firstname은 다른 변수
첫 문자는 영어 소문자로 시작하되, 다른 단어가 붙을 경우 첫 문자를 대문자로 한다.(관례) firstName
문자 수(길이)의 제한은 없다.  
자바 예약어는 사용할 수 없다. if, try, catch, finally, default, enum, strictfp, assert, transient, volatile

 

변수 초기화

기본 타입 변수는 변수를 선언한 후 리터럴을 입력하여 초기화 할 수 있다.

int var1;
var1 = 0;

타입이 같은 변수를 입력하여 초기화 할 수도 있다.

int var3 = var1;

선언과 동시에 초기화 할수도 있다.

int var2 = 0;

참조 타입은 'new' 예약어를 통해 초기화 할 수 있다.

List<Integer> var4 = new ArrayList<Integer>(); 

타입이 같은 변수를 입력하여 초기화 할 수도 있다.

List<Integer> var5 = var4;

 

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

 

변수는 중괄호 {} 블록 내에서 선언되고 사용된다. 중괄호 블록을 사용하는 곳은 클래스, 메소드, 제어문이있다. 변수는 블록 내 어디에서든 선언할 수 있고, 선언된 블록 내에서만 사용이 가능하다.

 

JVM 메모리 사용 영역은 크게 메소드 영역, 힙 영역, JVM 스택 영역으로 나뉜다. 자바에는 로컬 변수, 인스턴스 변수, 클래스 변수가 있고, 이 변수들은 각기 다른 JVM 메모리 사용 영역에 존재며, 각기 다른 라이프타임을 갖는다.

 

로컬(지역)변수

메소드 블록 내에 선언된 변수를 로컬 변수라고 부른다. 로컬 변수는 메소드 실행이 끝나면 메모리에서 자동으로 없어진다.

그림2

JVM 스택은 메소드를 호출할 때마다 프레임(Frame)을 추가하고, 종료되면 프레임을 제거한다. 이 프레임 내부에 로컬 변수가 있다. 즉, 로컬 변수가 선언된 블록을 벗어나면 해당 변수는 제거된다.

 

인스턴스 변수

클래스 내의 선언한 변수로 객체가 생성될 때 JVM 힙 영역에 저장됩니다. 그림2의 classValue 변수가 인스턴스 변수입니다. 해당 객체를 참조하는 변수가 더 이상 없을 때 가비지 컬렉터에 의해 객체와 함께 제거된다.

 

클래스 변수

모든 인스턴스에서 공유하고, 접근할 수있는 변수이며, 그림2의 instanceValue 처럼 인스턴스 변수 타입 앞에 static 예약어를 붙인 모습입니다. JVM의 메서드 영역에 프로그램이 시작될때 생성되고, 프로그램이 종료될 때 제거된다.

 

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

타입 변환이란 데이터 타입을 다른 데이터 타입으로 변환하는 것이다.

예) byte 타입을 int 타입으로 변환 또는 int 타입을 byte 타입으로 변환

 

타입 변환에는 자동 타입 변환(프로모션), 강제 타입 변환 (캐스팅) 두 가지 종류가 있다.

 

자동(묵시적) 타입 변환

프로그램 실행 중 자동적으로 타입 변환이 일어나는 것을 말한다. 작은 크기를 가지는 타입이 큰 크기를 가지는 타입에 저장될 때 발생한다.

그림3

크기별로 타입을 정리하면 다음과 같다.

byte < short < int < long < float < double

long은 메모리에서 8바이트, float는 4바이트로 저장된다. 그럼에도 더 큰 타입으로 표시되는 이유는 `표현할 수 있는 값의 범위`가 float가 더 크기 때문이다.

정수 타입실수 타입으로 변환하는 것은 무조건 자동 타입 변환이 된다.

char 타입이 int 타입으로 자동 변환되면 유니코드 값이 int 타입에 저장된다.

char 타입은 2byte의 크기를 가지지만, char의 범위는 0~65535이므로 음수를 저장할 수 없다. 따라서 음수가 저장될 수 있는 byte 타입을 char 타입으로 자동 변환할 수 없다.

아래는 자동 타입 변환의 예시 코드다.

public class HelloWorld{

     public static void main(String []args){

         byte var = 1;
         short var2 = var;
         int var3 = var2;
         long var4 = var3;
         float var5 = var4;
         double var6 = var5;

         System.out.println(var);
         System.out.println(var2);
         System.out.println(var3);
         System.out.println(var4);
         System.out.println(var5);
         System.out.println("");



         char var7 = 'A';
         var3 = var7;   // char 타입을 int 타입으로 
         System.out.println(var3);

     }

}

결과

아래 그림은 byte 타입이 int 타입으로 변활될 때 메모리에서 값이 복사되는 모습이다.

그림4

 

강제(명시적) 타입 변환

강제적으로 큰 데이터작은 데이터 타입으로 쪼개어 저장하는 것을 강제 타입 변환(Casting)이라 한다.

그림5

아래는 4byte의 int타입을 1byte의 byte 타입으로 캐스팅 연산자를 사용하여 강제 형변환하는 그림이다.

이때 int 타입을 1byte씩 쪼개고, 끝에 있는 1byte만 byteValue 변수에 저장한다.

그림6

이걸 코드로 보면 다음과 같다.

public class HelloWorld{

     public static void main(String []args){

         int var = 103029770;
         byte var2 = (byte) var;

         System.out.println(var);
         System.out.println(var2);

     }

}

결과

 

 

 

 

 

 

 

위 코드의 결과는 원래 int 값을 보존하지 못한다.

하지만 int 값이 끝 1byte로만 표현 가능하다면 byte 타입으로 변환해도 같은 값이 유지된다. 예) int 타입 변수 값이 10

그림7



int 타입에 저장된 값이 유니코드 범위(0~65535)일때, char 타입으로 강제 타입 변환하면 유니코드에 해당하는 문자가 저장된다. 실수 타입정수 타입으로 강제 타입 변환하면, 소수점 이하 부분은 버려지고, 정수 부분만 저장된다.

 

public class HelloWorld{

     public static void main(String []args){

         int var = 'A';
         char var2 = (char) var;
         System.out.println(var2);

         double var3 = 123.456;
         int var4 = (int)var3;
         System.out.println(var4);

     }
}

결과

 

타입 변환 시 주의사항

 

1. 강제 타입 변환 사용시 사용자로부터 입력받은 값을 변환할 때 값이 손실되지 않도록 유의하자.

해결책: 강제 타입 변환 전 안전하게 값이 보존될 수 있는지 검사하는 것이 좋다.

public class HelloWorld{

     public static void main(String []args){

         int var = 128;

         if(var < Byte.MIN_VALUE || var > Byte.MAX_VALUE){
             System.out.println("변환불가");
         }else{
             byte b = (byte) var;
             System.out.println(b);
         }
     }
}

 

2. 정수 타입을 실수 타입으로 변환할 때 정밀도 손실을 주의하자.

public class HelloWorld{

     public static void main(String []args){

         int num1 = 123456780;
         int num2 = 123456780;

         float num3 = num2;
         num2 = (int) num3;

         int result = num1 - num2;
         System.out.println(result);

         // 결과 -4

     }

}

위 코드의 결과는 0이 아닌 -4이다. int 값을 float 타입으로 자동 변환할 때 문제가 발생했기 때문이다.

 

float 타입은 부호(1비트) + 지수(8비트) + 가수(23비트)로 비트수가 할당된다. int 값을 손실 없이 float 타입의 값으로 변환하려면 가수 23비트로 표현 가능해야한다. 하지만 위 코드의 123456780은 23비트로 표현할 수 없다. 때문에 근사치로 변환되어 `정밀도 손실`이 발생한 것이다.

 

해결책: 모든 int 값을 실수 타입으로 안전하게 변환하는 double 타입을 사용한다.

double 타입은 부호(1비트) + 지수(11비트) + 가수(52비트)로 비트수가 할당된다. int의 크기는 32비트이므로 double의 가수 52비트보다 작기 때문에 어떠한 int 값이라도 정밀도 손실 없이 double 타입으로 변환할 수 있다. double 값을 원래 int 타입으로 변환해도 손실 없이 복원된다.

public class HelloWorld{

     public static void main(String []args){

         int num1 = 123456780;
         int num2 = 123456780;

         double num3 = num2;
         num2 = (int) num3;

         int result = num1 - num2;
         System.out.println(result);

         // 결과 0
     }
}

 

연산식에서의 자동 타입 변환

 

1. 연산은 기본적으로 같은 타입의 피연산자 간에만 수행된다. 때문에 서로 다른 타입의 피연산자의 경우 `크기가 큰 타입`으로 `자동 변환`된 후 연산을 수행한다.

 

int var = 10;
double var2 = 5.5;
double result = var + var2; // var의 int 타입이 double 타입으로 변환

 

2. 더 작은 타입으로 변환하려면 강제 형변환을 사용해 연산을 수행하자.

int var = 10;
double var2 = 5.5;
double result = var + (int)var2; // var2의 double 타입이 int 타입으로 변환

 

3. 자바는 정수 연산일 경우 int 타입을 기본으로 한다. 피연산자를 4byte 단위로 저장하기 때문에 크기가 4byte보다 작은 타입인 byte, char, short은 4byte인 int 타입으로 변환된 후 연산이 수행된다. 따라서 연산 결과도 int 타입이다.

 

4. 피연산자 중 하나가 long 타입이라면 다른 피연산자도 long 타입으로 자동 타입 변환되고 연산 결과도 long 타입이 된다.

 

5. float 타입과 float 타입을 연산하면 연산 결과는 float 타입이다.

 

6. 피 연산자 중 실수 리터럴이나 double 타입이 있다면 다른 피연산자도 double 타입으로 자동 타입 변환되어 연산되므로 결과는 double 타입으로 산출된다.

 

배열

배열 선언

1차 배열 선언 형태 2가지

타입 [] 변수;
타입 변수 [];

int[] intArray;
int intArray[];

대괄호 []는 배열 변수를 선언하는 기호로 사용되며, 타입 뒤에 붙거나 변수 뒤에 붙는다.

 

1차원 배열 초기화

int intArray[] = new int[2];
int [] intArray = { 1,2,3,4,5 };

 

2차원 배열 선언 형태 2가지

int[][] intArray;
int intArray[][];

 

2차원 배열 초기화

int[][] b = new int[2][]; // OK
int [][] b = {{1,2,3},{4,5,6}}; // OK
int[][] b = new int[][];  // NOT OK, 컴파일 에러
int[][] b = new int[][2];  // NOT OK, 컴파일 에러

위와 같이 선언할 수 있는 이유는 2차원 배열에서 1차원에는 2차원 배열의 주소를 가지고 있기 때문이다. 때문에 new int[2][0] 처럼 1차원 크기만 미리 정하면, 2차원의 크기는 이후에 정할 수 있다.

https://seungzzang5811.tistory.com/73

배열 출력

배열도 객체이므로 출력 가능

int [] arr = new int[10];
System.out.println(arr);

결과

결과의 '['는 일차원 배열([[는 이차원 배열), 'I'는 타입(Integer) '129a8472'는 객체의 해시코드다.

(이 값은 java name mangling의 결과로 생성

https://stackoverflow.com/questions/1314743/what-is-name-mangling-and-how-does-it-work https://en.wikipedia.org/wiki/Name_mangling)

 

Arrays 클래스

 · 배열을 다루기 위한 다양한 메서드가 포함된 클래스

 · Arrays 클래스의 모든 메서드는 클래스 메서드(static method)로, 객체를 생성하지 않고 바로 사용 가능

 · java.util 패키지에 존재

binarySearch() 배열 내에서의 검색
copyOf() 배열의 복제
equals() 배열의 비교
fill() 배열 채우기
hashCode() 배열의 해시코드 제공
sort() 정렬
toString() 배열 내용 출력

 

타입 추론, var

타입 추론: 타입이 생략되면 컴파일러가 생략된 형식을 추론하는 것

자바는 버전이 올라가면서 타입 추론을 통해  엄격한 형식 지정을 조금씩 느슨하게 만들어왔다.

 

자바 7에는 컨텐스트 형식을 유추할 수 있는 상황에서 제네릭의 형식 파라미터를 생략할 수 있다.

Map<String, List<String>> myMap = new HashMap<String, List<String>>();

///// 타입 추론 ///// 

Map<String, List<String>> myMap = new HashMap<>();

 

자바 8에 추가된 람다 표현식에서도 마찬가지로 컨텍스트를 통해 형식을 유추할 수 있다.

Function<Integer, Boolean> p = (Integer x) -> booleanExpression;

///// 타입 추론 ///// 

Function<Integer, Boolean> p =  x -> booleanExpression;

 

자바 10 부터는 지역 변수 형식을 var 키워드로 대체할 수 있다. 그러면 컴파일러가 변수 할당문의 오른쪽 내용을 기초로 형식을 추론한다.

이를 지역 변수형 추론이라 부른다.

var myMap = new HashMap<String, List<String>>();

 

타입 추론의 장점

1. 한 형식을 다른 형식으로 교체할 때 편집 작업이 줄어든다.

2. 제네릭으로 인해 형식 이름이 길어질때, 가독성을 높일 수 있다.

 

참고

백기선님 온라인 자바 스터디

모던 자바 인 액션

이것이 자바다

F-lab

http://tcpschool.com/java/java_api_arrays

반응형

댓글