자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법 익히기
int[] arr = new int[10];
-> 기본값: Null
-> 할당되는 메모리 크기: 4byte (객체의 주소값)
String str = "test";
Student xx0hn = new Student();
-> 기본값: Null
-> 할당되는 메모리 크기: 4byte (객체의 주소값)
프로그램에서 직접 표현한 값으로 소스 코드의 고정된 값을 대표하는 용어이다. 종류로는 정수, 실수, 문자, 논리, 문자열 리터럴이 있다.
int a = 15;
=> 10진수 값으로 15 출력int b = 015;
=> 10진수 값으로 13 출력int c = 0x15;
=> 10진수 값으로 21 출력int d = 0b0101;
=> 10진수 값으로 5 출력정수 리터럴은 int형으로 컴파일되고 long 타입 리터럴의 경우 숫자 뒤에 L 또는 l을 붙여 표시한다.
소수점 형태나 지수 형태로 표현한 값으로 실수 타입 리터럴은 double타입으로 컴파일 된다.
double f = 0.1234;
double g = 1234E-4;
(1234 x 10^(-4)이므로 0.1234와 같다.
숫자 뒤에 f(float)이나 d(double)을 명시적으로 붙이기도 한다. float의 경우에는 반드시 f를 붙여줘야 하고, double의 경우에는 생략 가능하다.
단일 인용부호('_')로 문자를 표현한다.
char a = 'H';
char b = "한";
char c = \uae00;(유니코드값) //(\u다음에 4자리 16진수로, 2바이트의 유니코드)
문자열은 기본형 타입이 아니다. ("_")로 문자열을 표현한다.
String lter = "JAVA;"
lter + 26 = "Lter26;"
boolean 타입 변수에 치환하거나 조건문에 이용한다.
boolean a = true;
boolean b = 10 >0; //true로 저장됨
boolean c = 0; // boolean타입으론 1,0을 참, 거짓으로 사용 불가함
null 리터럴은 레퍼런스에 대입해서 사용한다. 기본 타입에는 사용이 불가하고 String 같은 경우에는 사용 가능하다.
int a = null; //error
String str = null;
str = "JAVA";
변수의 선언과 초기화를 알아보기 전에 변수의 종류에 대해 간단하게 정리해보았다.
변수는 선언하는 곳마다 부르는 이름이 달라진다.
변수를 선언하는 방법은 매우 간단하다. 데이터 타입을 선언하고 바로 뒤에 변수 이름을 넣으면 된다.
int var1;
여러개의 변수를 한번에 선언하는 것도 다음과 같이 가능하다.
int var1, var2, var3;
일반적으로 초기화를 할 때는 변수를 선언하고 바로 뒤에 '=' 대입 연산자를 사용하여 변수에 데이터를 넣었다.
int var1 = 1000;
블록 초기화는 말 그대로 초기화를 할 수 있는 블록을 하나 생성하는 것이다.
public class Study{
static int var = 10;
int hello = 3;
{ // 일반적인 초기화 블록
System.out.println("hello -> 5 초기화");
hello = 5;
}
static { // 정적 초기화 블록
System.out.println("var -> 10 초기화");
var = 10;
}
}
이렇게 블록으로 나눠 초기화 하는 이유는 명시적 초기화의 한계가 있기 때문이다. 필드에서 바로 초기화를 시키면 복잡한 로직으로 값을 변수에 넣는데에 제한이 있기 때문에 블록 방식을 사용한다.
public static void main(String[] args){
System.out.println("인스턴스 생성");
Study st = new Study();
}
main 메소드를 실행시키면 결과는 다음과 같이 출력된다.
정적 변수 var -> 10 초기화
인스턴스 생성
멤버 변수 hello -> 5 초기화
정적 초기화 블록이 인스턴스 생성 이전에 먼저 초기화되고 인스턴스 생성 후에 일반적인 초기화 블록이 초기화되는 것을 확인할 수 있다.
변수의 스코프는 변수를 사용할 수 있는 범위를 의미한다. 쉽게 생각하면 변수가 선언된 블럭 안에서 영역이 끝나기 전까지 어디서든 사용이 가능하다.
public class ValableScopeExam{
int globalScope = 10; // 인스턴스 변수
public void scopeTest(int value){
int localScope = 10;
System.out.println(globalScope);
System.out.println(localScpe);
System.out.println(value);
}
}
public class VariableScopeExam {
int globalScope = 10;
public void scopeTest(int value){
int localScope = 20;
System.out.println(globalScope);
System.out.println(localScope);
System.out.println(value);
}
public static void main(String[] args) {
System.out.println(globalScope); //오류
System.out.println(localScope); //오류
System.out.println(value); //오류
}
}
public class VariableScopeExam {
int globalScope = 10;
static int staticVal = 7;
public void scopeTest(int value){
int localScope = 20;
}
public static void main(String[] args) {
System.out.println(staticVal); // 사용 가능
}
}
static한 변수는 공유된다.
ValableScopeExam v1 = new ValableScopeExam();
ValableScopeExam v2 = new ValableScopeExam();
v1.globalScope = 20;
v2.globalScope = 30;
System.out.println(v1.globalScope); // 20 출력
System.out.println(v2.globalScope); // 30 출력
v1.staticVal = 10;
v2.staticVal = 20;
System.out.println(v1.staticVal); // 20 출력
System.out.println(v2.staticVal); // 20 출력
모든 변수는 생명주기라는 것을 가지게 된다. 생명주기란 변수가 생성되고 죽을 때까지를 말한다.
public class Study {
int var1; // 인스턴스 변수, 필드, 전역 변수
static int var2; // 클래스 변수, 정적 변수
void hard(int var3){ // 매개 변수, 파라미터
int var4 = 10; // 지역 변수
}
}
각 변수들의 생명주기에 대해 알아보았다.
하나의 타입을 다른 타입으로 바꾸는 것을 타입 변환이라고 한다. 자바에서는 boolean형을 제외한 나머지 기본 타입 간의 타입 변환을 자유롭게 수행할 수 있다.
자바에서 다른 타입끼리의 연산은 우선 피연산자들을 모두 같은 타입으로 만든 후에 수행된다. 메모리에 할당받은 바이트의 크기가 상대적으로 작은 타입에서 큰 타입으로의 타입 변환은 생략 가능하다.
하지만 메모리에 할당받은 바이트의 크기가 큰 타입에서 작은 타입으로의 타입 변환은 데이터의 손실을 발생시킨다. 따라서 상대적으로 바이트의 크기가 작은 타입으로 타입 변환을 할 경우 자바 컴파일러는 오류를 발생시킨다.
자바의 타입 변환은 크게 두 가지로 분류된다.
묵시적 타입 변환이란 대입 연산이나 산술 연산에서 컴파일러가 자동으로 수행해주는 타입 변환을 가리킨다. 자바는 데이터의 손실이 발생하지 않거나, 데이터의 손실이 최소화되는 방향으로 묵시적 타입 변환을 진행한다. 또한 자바는 데이터의 손실이 발생하는 대입 연산은 허용하지 않는다.
double num1 = 10;
//double num2 = 3.14;
double num3 = 7.0f + 3.14;
System.out.println(num1); // 10.0 출력
System.out.println(num3); // 10.14 출력
자바 컴파일러가 자동으로 수행하는 타입 변환은 위와 같이 데이터의 손실을 최소화 하는 방향으로 진행된다.
byte -> short & char -> int -> long -> float -> double
명시적 타입 변환이란 사용자가 타입 캐스트 연산자(())를 사용하여 강제적으로 수행하는 타입 변환을 가리킨다.
자바에서는 다음과 같이 명시적 타입 변환을 수행할 수 있다.
(변환할 타입)변환할 데이터
변환하고자 하는 데이터의 앞에 괄호를 넣고, 그 괄호 안에 변환할 타입을 적으면 된다. 자바에서는 이 괄호를 타입 캐스트(type cast) 연산자 라고 한다.
int num1 = 1, num2 = 4;
double result1 = num1 / num2;
double result2 = (double)num1 / num2;
System.out.println(result1); // 0.0 출력
System.out.println(result2); // 0.25 출력
타입 변환에서 타입 캐스트 연산자가 등장했다. 명시적 타입 변환 시 (변환할 타입)변환할 데이터
형태로 데이터의 타입을 변환하는데에 사용하는 연산자이다.
타입 프로모션은 묵시작 타입 변환을 의미한다. 앞서 설명한 바와 같이 크기가 더 작은 자료형을 더 큰 자료형에 대입할 때, 자동으로 작은 자료형이 큰 자료형으로 변환되는 현상이다.
기본형 타입의 경우 모두 타입 캐스팅이 가능하지만 클래스 타입의 경우 조금 다르다. 클래스 타입들은 기본적으로 타입 캐스팅이 불가능하다. 조금만 생각해 보아도 여러 자료형이 묶여있는 클래스 타입이 서로 호환될 일은 없어 보인다. 그러나 상속 관계에서의 클래스 타입은 타입 캐스팅이 가능하다.
int []array = new int[10];
위와 같이 배열을 선언할 경우 0이 10개 들어간 1차원 배열이 초기화된다.
int []array = new int[5];
array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
array[4] = 5;
위와 같이 배열의 값을 따로 정의할 수도 있다.
int [][]array = new int[10][10]
int [][]array = new int[5][5];
array[0][0] = 1;
array[0][1] = 1;
array[1][0] = 2;
array[1][1] = 2;
...
자바에서는 타입 추론을 지원한다. 타입 추론이란 말 그대로 개발자가 변수의 타입을 명시적으로 적어주지 않고도 컴파일러가 알아서 이 변수의 타입을 대입된 리터럴로 추론하는 것이다.
public static void main(String[] args) {
var str = "Hello world";
if(str instanceof String) {
System.out.println("str은 String입니다.");
}
}
위의 코드를 실행하면 자바의 타입 추론 기능으로 str을 String으로 인식하여 "str은 String입니다"가 출력된다.
var는 멤버변수 또는 메소드의 파라미터, 리턴 타입으로 사용이 불가능하다. 그리고 무조건 선언할 때 초기화 값이 있어야 한다. 정리하면 var은 초기화 값이 있는 지역변수로만 선언이 가능하다.
var는 키워드가 아니다. 즉, 어떤 타입도 아니고 클래스에서 사용할 수 있는 예약어가 아니라는 뜻이다. 그래서 int를 변수 이름으로 만들 수는 없지만 var라는 문자를 변수로 사용할 수 있다. 이런 메커니즘을 둔 이유는 컴파일러가 바이트 코드로 변환하는 과정에서 var은 타입이 명시되어지기 때문이다.
var은 런타임 오버헤드가 없다. 컴파일 시점에서 var을 초기화된 값을 보고 추론하여 바이트 코드에 명시적으로 자료형을 선언한다. 결정이 되어 있는 상태이기 때문에 타입 추론 변수를 읽을 때마다 타입 추론 연산을 하지 않는다. 그래서 var로 선언된 변수는 중간에 타입이 변경되지 않는다.
var을 사용할 일이 별로 없다고 느껴질 수도 있지만 다음과 같은 경우에서는 유용하다.
Study<String> hard = s -> System.out.println("s = " + s);
Study<string> hard = (var s) -> System.out.println("s = " + s);
이렇게 변수 앞에 var을 넣을 수 있다. 이렇게만 봐서는 똑같다고 느껴지지만
Study<String> hard = (@Nonnull var s) -> System.out.println("s = " + s);
이렇게 키워드 앞에서만 사용할 수 있는 어노테이션을 사용할 수 있게 된다.