목표
자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.
학습할 것
프리미티브 타입 종류와 값의 범위 그리고 기본 값
프리미티브 타입과 레퍼런스 타입
리터럴
변수 선언 및 초기화하는 방법
변수의 스코프와 라이프타임
타입 변환, 캐스팅 그리고 타입 프로모션
1차 및 2차 배열 선언하기
타입 추론, var
1.1 기본 타입(primitive type)
구분 | 리터럴 타입 | 형태 | 길이 |
논리타입 | boolean | true/false | 1byte |
문자타입 | char(아스키코드) | 'a', '4', '(', ' ', '\t' | 2byte |
char(유니코드) | '파', '이', '썬' | 2byte | |
정수타입 | byte | -127, 0, 128 | 1byte |
short | -300, 0, 3 | 2byte | |
int | -123, 0, 1234 | 4byte | |
long | -2134L, 0, 565L | 8byte | |
실수타입 | float | -14.34F, 1.0F, 0.345F | 4byte |
double | -54.345, 0, 3.454 | 8byte |
1) 논리타입
true/false로 이뤄져 있다.
2) 문자타입
문자타입은 유니코드이며 2바이트이다. 유니코드에는 아스키코드, 한글이 포함되어 있다. 문자타입을 초기화 할 떄 ''(작은따옴표)으로 블럭을 한정 한다.
문자타입을 선언할 때 해당 유니코드를 알고 있다면 그 값을 대신하여 입력할 수 있다. 그 예시는 아래와 같다.
char var1 = 'a';
char var2 = 97;
System.out.println(var1==var2); // true
3) 정수타입
정수타입으로서 소수점이 존재하지 않는다.
long의 경우 숫자 마지막에 L을 붙인다. 30L, -19L
4) 실수타입
실수타입으로서 소수점이 존재한다.
float의 경우 숫자 마지막에 F를 붙인다. 343.234F
1.2 참조타입 (reference type)
스택 | 힙 | ||
변수 | 값 | 주소 | 값 |
int a | 3 | ||
String b | heap 30번지 | heap30번지 | "java" |
int i;.............(1)
i = 10;
int j = 10; ..............(2)
(1) int는 데이타 타입, i는 변수의 이름, 10은 변수의 값, 세미콜론(;)은 코드가 끝났음을 알리는 연산자이다. 첫번째 문장은 선언으로서, 변수의 데이타 형태와 변수의 이름을 정하는 것을 의미한다. 초기화는 값을 부여하는 행위이다. 이 경우 선언과 초기화를 따로 했다.
(2) 변수의 선언과 초기화를 같이 했다.
String str1 = "java";
System.out.println(str1); // java
String[] strs1 = {"hello", "world"};
for (String s : strs1) {
System.out.println(s); // hello world
}
int[] ints1 = {1,2,3,4};
for (int i : ints1) {
System.out.print(i); // 1234
}
int[][] ints2 ={
{1,2,3},
{4,5},
{6,7,8,9}
};
for (int[] ints : ints2) {
System.out.print("[");
for (int i : ints) {
System.out.print(i);
}
System.out.println("]");
} // [123] [45] [6789]
( 참조변수의 구체적인 내용은 이전에 작성한 글을 참고 바랍니다! : https://velog.io/@infoqoch/%EC%B0%B8%EC%A1%B0%EB%B3%80%EC%88%98%EB%A1%9C%EC%84%9C-String%EA%B3%BC-Array )
2.2 지역변수(local variable)의 범위(scope)와 생명주기(life cycle)
public static void main(String[] args) {
int localInt1 = 0; ................(1)
int localInt2; ................(2)
String localStr1 = "java";
if(localInt1==0){
localInt1 = 1;
localInt2 = 2;
int localInt3 = 3; ...........(3)
}
if(!localStr1.equals("hello")){
System.out.println(localInt1); // 1
System.out.println(localInt2); // 컴파일에러
System.out.println(localInt3); // 컴파일에러
}
}
(1) 메인 쓰레드에서 선언과 초기화가 동시에 이뤄졌다. 해당 쓰레드 내부에 있는 모든 블럭에서 접근 가능하며, 해당 값을 변경할 수 있다. 첫 번째 if 제어문에서 값이 수정되어 두 번째 if 제어문에서 처음 초깃값인 0이 아닌 1로 출력됨을 확인할 수 있다.
(2) 컴파일 에러 : Variable 'localInt2' might not have been initialized. 메인 쓰레드에서 선언을 하고, 첫 번째 if 제어문에서 초기화됐다. 하지만 두 번째 if 제어문에서는 컴파일 에러가 발생한다. 첫 번째 if 제어문이 true이므로 반드시 초기화(initialized) 될 것임에도 불구하고, 컴파일이 불가능하다.
(3) 컴파일 에러 : Cannot resolve symbol 'localInt3'. resolve는 추상의 값으로 실질적인 값에 접근함을 의미한다. localInt3이 선언된 블럭이 달라 접근 자체가 불가능하기 때문에, 두 번째 제어문의 블럭 입장에서는 존재하지 않는 것과 다름 없다.
2.3 정적 맴버, 인스턴스 맴버의 범위와 생명주기
public class Test2 {
private static String staticStr = "static field"; ..........(1)
private String str = "filed"; ............ (2)
public Test2() {
}
public Test2(String str) {
this.str = str;
}
public static void main(String[] args) {
System.out.println(staticStr); ........... (3)
System.out.println(str);.......... (4) : 컴파일 에러
Test2 test2 = new Test2("instance"); ......... (5)
System.out.println(test2.str); .........(6)
}
}
(1) 정적 맴버는 클래스 로더가 작동 할 떄 런타임 데이타 영역(Runtime Data Area) 중 메소드 영역(Method Area)에 저장된다. 메소드 영역의 데이타는 그것의 생명주기와 범위가 어플리케이션의 것과 동일하다. 이 말은, 정적 맴버는 해당 어플리케이션 안에서는 언제 어디서나 사용 가능하다. 그러므로 (3)에서 아무런 설정 없이 접근 가능하다.
(2) 인스턴스 맴버는 해당 클래스를 데이타 타입으로 하는 객체(인스턴스)를 통해서만 사용 가능하다. 그러므로 그것의 사용 범위는 해당 객체가 생성되는 위치에 한정되며, 생명주기는 객체가 생성된 메서드나 제어문의 생명주기에 의존한다. 그러므로 (4)에서 볼 수 있는 것처럼 해당 객체가 존재하지 않는 메인 스레드에서는 str에 접근이 불가능하다. (5)를 통해 해당 객체를 heap에 저장하고 그 주소를 가진 (6) test2 매개변수를 선언한 후에서야 접근이 가능하다.
3.1 자동 타입 변환(Promotion)
Byte byte1 = 10;
int int1 = byte1; // 10
double do2 = byte1; // 10.0
3.2 강제 타입 변환(Casting)
long long2 = 99999999999L;
int int2 = (int) long2; // 1215752191
long long3 = 1234L;
int int3 = (int) long3; // 1234
long long4 = 1234L; ...........(3)
long long4 = 99999999999999999L; ........... (4)
if(long4>Integer.MAX_VALUE || long4<Integer.MIN_VALUE){ .......(5)
System.out.println("long 변수의 값이 int가 저장할 수 있는 범위를 초과하여 변경할 수 없습니다."); ............ (6)
}else{
int int4 = (int) long4;
System.out.println("해당 변수가 다음과 같이 변경되었습니다 : "+int4); .........(7)
}
(1) long2의 값이 int의 가용범위를 넘어서기 때문에 기존의 값을 잃어버렸다.
(2) long2의 값이 int의 가용범위를 넘지 않기 때문에 기존의 값을 보존했다.
(5) 기존의 값을 유지할 수 있을 경우 타입을 변환하고 그렇지 않은 경우 에러메시지를 출력하는 if 제어문이다. (3)의 경우 (7)을 출력하고 (4)의 경우 (6)을 출력한다.
double dou5 = 10.123; // 10
int int5 = (int) dou5; // 10
3.3 연산식에서의 자동 타입 변환
byte by1 = 23;
byte by2 = 10;
int sum2 = by1 + by2; ..........(1)
byte sum1 = (byte) (by1 + by2); ........(2)
(1) byte 간 연산한 결과의 값은 int 타입이므로 캐스팅을 필요로 하지 않는다.
(2) int의 결과값을 byte로 출력하기 위해서는 캐스팅을 해야 한다.
int intA = 10;
double douA = 5.5;
double douSum = intA + douA; // 15.5
int intSum = intA + (int)douA; // 15
char var1 = 'a';
char var2 = 97;
System.out.println(var1==var2); // true
final int conInt1;
final int conInt2 = 0;
conInt1 =1; ....(1)
conInt1 =2; ....(2)
conInt2 =3; ....(3)
(1) 선언을 먼저한 후 초기화를 차후에 했다. 문제 없이 작동한다.
(2) 컴파일 에러 : Variable 'conInt1' might already have been assigned to. 이미 값이 할당(assigned) 되어 에러가 발생한다.
(3) 컴파일 에러 : Cannot assign a value to final variable 'conInt2'. 상수 변수는 값을 할당할 수 없다.
4.2 var (타입 추론)
var msg = "hi?";
System.out.println(msg.getClass().toString()); // class java.lang.String