[Java] Java 기초(2)

어정윤·2021년 1월 14일
0

Java 스터디

목록 보기
3/12
post-thumbnail

[Java] Java 기초(2)

1. 기본형 타입(Primitive type)과 참조형 타입(Reference type)

1-1. 기본형 타입(Primitive type)

  • 총 8가지의 기본형 타입을 미리 정의하여 제공한다.
  • 기본값이 있기 때문에 Null이 존재하지 않는다.
    (만약 기본형 타입에 Null을 넣고 싶다면 래퍼 클래스를 활용한다.
  • 실제 값을 저장하는 공간으로 스택(Stack) 메모리에 저장된다.
  • 만약 컴파일 시점에 담을 수 있는 크기를 벗어나면 에러를 발생시키는 컴파일 에러가 발생한다. 주로 문법상의 에러가 많다.
    ex) 세미콜론(;)을 안붙였다는 이유로 빨간 줄이 쳐지는 경우
타입 할당되는 메모리 크기 기본값 데이터의 표현 범위
boolean 논리형 1 byte false true, false
byte 정수형 1 byte 0 -128 ~ 127
short 정수형 2 byte 0 -32,768 ~ 32,767
int(기본) 정수형 4 byte 0 -2,147,483,648 ~ 2,147,483,647
long 정수형 8 byte 0L -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
float 실수형 4 byte 0.0F (3.4 X 10-38) ~ (3.4 X 1038) 의 근사값
double(기본) 실수형 8 byte 0.0 (1.7 X 10-308) ~ (1.7 X 10308) 의 근사값
char 문자형 2 byte(유니코드) '\u0000' 0 ~ 65,535

1-2. 참조형 타입(Reference type)

  • 기본형 타입을 제외한 타입들이 모두 참조형 타입이다.
  • 빈 객체를 의미하는 Null이 존재한다.
  • 값이 저장되어 있는 곳의 주소값을 저장하는 공간으로 힙(Heap) 메모리에 저장된다.
  • 문법상으로는 에러가 없지만, 실행시켰을 때 에러가 나는 런타임 에러가 발생한다.
    ex) 객체나 배열을 Null 값으로 받으면 NulloPointException이 발생하므로 변수값을 넣어야 한다.
타입 기본값 할당되는 메모리 크기
배열(Array) Null 4 byte(객체의 주소값)
열거(Enumeration) Null 4 byte(객체의 주소값)
클래스(Class) Null 4 byte(객체의 주소값)
인터페이스(Interface) Null 4 byte(객체의 주소값)

2. 리터럴(Literal)

리터럴은 변수에 넣는 변하지 않는 데이터 자체를 의미한다.
보통은 기본형의 데이터를 의미하지만, 특정 객체(Immutable class, VO class)에 한해서는 리터럴이 될 수 있다.

3. 변수의 선언 및 초기화

변수(Variable)는 데이터를 저장하는 메모리 영역을 의미한다.
변수는 수시로 값이 변경될 수 있으며 하나의 값만 저장할 수 있다.

상수(Constant)

변하지 않는 변수를 의미한다.
final 키워드로 선언하며, 대문자로 주로 표기한다.

자바의 변수는 다음과 같은 종류로 구분할 수 있다.

  • 인스턴스 변수 : 클래스 선언시 static키워드 없이 선언된 필드. 이 필드는 인스턴스 별로 다른 값을 가질 수 있기 때문에 인스턴스 변수라고 불린다.
  • 클래스 변수 : 클래스 선언시 static키워드와 함께 선언된 필드. 이 필드는 모든 인스턴스들이 공유하는 값이다. 클래스 명으로 접근이 가능하고, 클래스 하나에 한 값이기 때문에 클래스 변수라고 불린다.
  • 로컬 변수 : 메소드 선언 사이에 등장하는 변수로 다른 클래스에서 접근할 수 없는 변수. 메소드 영역에서만 임시로 사용되는 변수이다.
  • 매개 변수 : 매개 변수는 메소드의 인자로 전달되는 변수를 의미한다.

인스턴스 변수와 클래스 변수는 멤버 변수라고 통칭하기도 한다. 멤버 변수는 꼭 초기화를 해주지 않더라도 기본값으로 초기화되지만, 로컬 변수는 반드시 초기화를 해주어야 한다.

class Variables {
    int instanceVar; // 0으로 초기화되는 인스턴스 변수
    static int classVar; // 0으로 초기화되는 클래스 변수
    int initInstanceVar = 10; // 명시적으로 초기화
    static int initClassVar = 10; // 명시적으로 초기화

    void method(int num) { // 매개변수는 초기화 할 수 없고, 전달받는 값을 사용만 할 수 있음
      int a; // 선언은 가능
      // int b = a; 자동으로 초기화 되지 않으므로 동작하지 않음
      a = 10; // 선언을 미리 해줬다면 이렇게 초기화 가능
      int b = a; // 선언과 동시에 초기화
    }
}

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

4-1. 스코프(Scope)

스코프는 변수가 유효한 범위를 의미한다.

4-2. 라이프타임(LifeTime)

라이프타임은 변수가 메모리에서 살아있는 기간을 의미한다.

변수 타입스코프라이프타임
인스턴스변수클래스 전체(static 블록과 static 메소드 제외)객체가 생성되고 객체가 메모리에 살아있는 동안
클래스변수클래스 전체클래스가 초기화되고 프로그램이 끝날 때까지
로컬변수변수가 선언된 블록 내부변수 선언 이후부터 블록을 벗어날 때까지

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

타입 변환은 어떤 값이나 변수의 타입을 다른 타입으로 변경하는 것이다.
이 때 타입은 확장(자동 형변환)축소(명시적 형변환)두 가지 방향으로 변환될 수 있다.

5-1. 확장, 자동 형변환

확장은 다음 조건에서 두 데이터 타입이 자동으로 변환되는 경우이다.

  1. 두 데이터 타입은 호환되어야 한다.
  2. 더 작은 데이터 타입의 값을 더 큰 범위의 타입에 할당할 때만 동작한다.
    (바이트 크기가 아닌 표현 가능한 값의 범위)

자바에서의 숫자 타입은 byte -> short -> int -> long -> float -> double 순으로 자동 형변환이 된다.

5-2. 축소, 명시적 형변환(Casting)

축소는 더 큰 범위의 타입의 값을 더 작은 범위의 타입에 할당하기 위해서 형변환을 명시해주어야 한다.
또, 확장과 달리 호환되지 않는 데이터 타입에도 사용할 수 있다.

(타겟 타입) 값or변수로 강제로 형변환을 할 수 있다.

void 명시적_형변환() {
    // 동작하지 않는 코드
    char ch = 'c';
    int num = 88;
    ch = num;

    // 동작하기 위해선 아래와 같이 수행해야 합니다.
    ch = (char) num;
}

5-3. 타입 프로모션

타입 프로모션은 식을 평가할 때 중간에 피연산자의 범위를 초과할 수 있기 때문에 자동으로 값이 승격되는 것을 의미한다.

  • Java에서는 byte, short, char를 식 평가시 자동으로 int로 프로모션 된다.
  • 피연산자가 long, float, double인 경우 전체 표현식이 각각 long, float, double로 프로모션된다.
void 타입_프로모션() {
    byte b = 42;
    char c = 'a';
    short s = 1024;
    int i = 50000;
    float f = 5.67f;
    double d = .1234;

    // 표현식(명시적 형변환 없이 자동으로 형변환 되며, 값은 피연산자의 타입으로 프로모션 됩니다.)
    double result = (f * b) + (i / c) - (d * s);
}

6. 1차원 및 2차원 배열 선언하기

6-1. 1차원 배열 선언

int[] intArr1;
int []intArr2;	// 권장하지 않음
int intArr3[];	// 권장하지 않음

셋 다 가능하지만, 가장 위의 스타일이 선호된다.

6-2. 1차원 배열 초기화

int[] intArr1 = new int[10];

위와 같이 선언하면, 해당 크기만큼의 배열의 요소가 초기화되어 생성된다.
배열의 각 요소에는 인덱스로 접근할 수 있다.

또한 다음과 같이 초기화할 수도 있다.

int[] anArray = {
    100, 200, 300,
    400, 500, 600,
    700, 800, 900, 1000
};

int[] anArray = new int[] {100, 200, 300};

이런 경우 배열의 길이는 중괄호 사이에 선언된 요소의 수로 결정된다.
Java는 배열도 객체이기 때문에 힙 영역에 배열이 생성되며, new라는 키워드를 써줘야 한다.

6-3. 2차원 배열 선언

int[][] ints;

위와 같이 선언할 수 있다. 또한 Java에서의 다차원 배열은 요소 자체가 배열인 배열이다.
각 요소 배열간 길이는 다를 수 있다.

6-4. 2차원 배열 초기화

String[][] names = {
    { "Mr.", "Mrs.", "Ms." },
    { "Smith", "Jones" }
};
// 각 배열 요소의 길이는 초기화시에 지정을 해주지 않아도 됩니다. 단, null로 초기화됩니다.
String[][] names2 = new String[10][];

// 혹은 지정해줄 수도 있습니다. 이 경우 같은 크기를 가지게 됩니다. 이 경우 배열이 초기화 됩니다.
String[][] names3 = new String[10][10];

이차원 배열도 마찬가지로 요소의 인덱스로 접근할 수 있다.

+ 배열의 크기는 length 속성으로 알 수 있다. 그리고 배열은 한번 초기화하고 나면 변경할 수 없다.

7. 타입 추론

7-1. var

var 키워드는 로컬 변수의 타입 추론이라고 한다.
이 키워드의 사용은 반복되는 타입 선언을 줄여주고, 코드의 가독성을 높여준다.
이는 컴파일러가 데이터의 타입을 추론해낼 수 있기 때문이다.

var num = 3;

3int 타입을 반환하기 때문에 로컬 변수 numint 타입임을 추론할 수 있다.

이렇게 이미 타입이 정의되었기 때문에 var는 다른 타입의 재할당을 허용하지 않는다.

  • var를 사용할 수 없는 경우
    1. 필드 혹은 메소드 시그니처에서 사용이 불가능하다. 지역 변수에만 사용할 수 있다.
    2. 명시적인 초기화없이 var만 단독으로 선언할 수 없다.
    3. var 변수는 null로 초기화할 수 없다.
    4. 람다식과 var는 명시적으로 타겟이 되는 타입을 알아야 하기 때문에 같이 사용할 수 없다.
  • 주의해야할 var 선언
    var list = new ArrayList<>();
    이 코드는 컴파일되지만, 실제 list의 타입은 ArrayList<Object>로 컴파일되며, 제네릭의 이점을 얻지 못하기 때문에 피하는 것이 좋다.

타입 추론은 가독성을 높일 수 있는 경우와 그것을 사용한 것이 유용한 경우에만 선택적으로 사용해야한다.

이 말의 의미는 편하게 코드를 작성하기 위해 var를 쓰는 것이 아니라, 가독성을 높이는 목적으로 사용해야 한다는 의미이다.

var의 유형을 개발자가 추적할 수 없거나 어려운 경우에는 var를 사용하지 않는 것이 좋다.

7-2. 제네릭(Generic)

제네릭은 클래스 내부에서 사용하는 데이터의 타입을 클래스의 인스턴스를 생성할 때 결정하는 것을 의미한다.
객체의 타입을 컴파일 시점에 체크하기 때문에 타입 안정성을 높이고 형 변환의 번거로움을 줄일 수 있다.

public class TestGeneric<T> {
	public T sample;

  	public void showYourType() {
    		if (sample instanceof Integer) {
      			System.out.println("Integer 타입이군요!!");
        	} else if (sample instanceof String) {
        		System.out.println("String 타입이군요!!");
        	}
    	}
}

public class Main {
	public static void main(String[] args) {
    	
        TestGeneric<String> stringType = new TestGeneric<String>();
        TestGeneric<Integer> integerType = new TestGeneric<Integer>();

  	stringType.sample = "Hello";
  	integerType.sample = 1;

      	stringType.showYourType();	// 결과: String 타입이군요!!
      	integerType.showYourType();	// 결과: Integer 타입이군요!!
    }
}

TestGeneric 클래스의 변수 sample은 T라는 타입을 가지는데 T는 존재하는 타입이 아니고, 인스턴스가 생성될 때 결정된다.

var는 리터럴 값을 파악해 컴파일시 타입을 추론하는 반면 제네릭은 개발자가 직접 지정해주는 점이 차이점이다.

profile
성장ing

0개의 댓글