웹(WEB)/자바(JAVA)

[Java] 자바 변수 타입, 타입 변환

마달랭 2024. 12. 27. 21:15
반응형

개요

변수란 자료를 저장하기 위한 메모리 공간으로 탑입에 따라 크기가 달라진다.

메모리 공간에 값을 할당한 후 사용한다.

 

변수 타입, 형이란 데이터의 형태로 나타내며 데이터 타입에 따라 크게 2가지로 분류한다.

  • 기본형 : 미리 정해진 크기의 데이터 표현, 변수 자체에 값 저장
  • 참조형 : 크기가 미리 정해질 수 없는 데이터의 표현, 실제 값을 참조할 수 있는 주소만 저장

 

변수 타입

변수 타입은 여느 언어와 비슷하다.

특별히 다른 점은 논리형 타입의 변수는 C++에서는 bool, 자바에서는 boolean으로 표기한다.

또 8비트 정수형 변수는 C++에서는 long long, 자바에서는 long으로 표기한다.

타입 크기 설명
byte 1 byte (8비트) -128 ~ 127의 범위를 가지는 정수
short 2 byte (16비트) -32,768 ~ 32,767의 범위를 가지는 정수
int 4 byte (32비트) 가장 많이 사용되는 기본 정수형
long 8 byte (64비트) 큰 범위의 정수
float 4 byte (32비트) 단정도 소수점 실수
double 8 byte (64비트) 배정도 소수점 실수, 기본 실수형
char 2 byte (16비트) 유니코드 문자 (0~65,535)
boolean 1 bit 참(true) 또는 거짓(false)
String 가변적 문자열
배열 가변적 동일한 타입의 집합
클래스 가변적 사용자 정의 객체

 

 

로컬 변수

var 키워드를 통해 로컬 변수를 선언할 수 있다.

이는 마치 자바스크립트의 var과 let처럼 사용할 수 있으나 자바스크립트 처럼 타입이 바뀌지는 않는다.

 

자바에서의 로컬 변수는 선언 시 var 키워드를 적용하게 되면 변수에 값을 할당하면 타입이 결정된다.

따라서 변수 선언 시 값 할당까지 진행되어야 한다.

 

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        int i = 10;
        var a = 1;
        var str = "Hello World";
        var list1 = new ArrayList<>();
        var list2 = new ArrayList<String>();
    }
}

 

 

문자열 변수

자바에서 문자열 타입은 string이 아닌 String으로 타입의 첫 문자가 대문자이다.

또한 직접적으로 문자열을 변수에 저장하는 것과 new 키워드를 사용하여 지정하는 것은 서로 다르다.

 

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // case 1
        String str1 = "Hello";
        
        // case 2
        String str2 = new String("World");
    }
}

 

case 1의 경우 리터럴방식으로 문자열을 생성한다, 아래와 같은 특성을 갖는다.

  • String Constant Pool(문자열 상수 풀)에 "Hello"라는 문자열이 이미 존재하는지 확인
  • 존재하면 해당 참조를 반환하고, 존재하지 않으면 새로 생성하여 풀에 저장
  • 메모리 효율적이고 속도가 빠름

case 2의 경우 new 키워드를 사용하여 Heap 영역에 새로운 String 객체를 생성한다.

 

  • "World"라는 리터럴이 String Constant Pool에 저장된 후, new를 통해 새로운 복사본이 Heap 메모리에 생성
  • 항상 새로운 객체가 만들어지므로 메모리를 비효율적으로 사용할 수 있음

 

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = new String("Hello");
        String str3 = "Hello";

        System.out.println(str1 == str2);  // false (다른 객체)
        System.out.println(str1 == str3);  // true (같은 객체)
        System.out.println(str1.equals(str2));  // true (내용은 같음)

    }
}

 

두 타입의 객체를 비교하면 다음과 같다.

 

또한 문자열 변수는 멀티 라인 텍스트 블럭 적용이 가능하다.

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        String text = """
            안녕하세요,
            자바의 텍스트 블럭입니다.
            줄바꿈도 자동으로 됩니다.""";
        System.out.print(text);
    }
}

 

특징 설명
줄바꿈 자동 포함 텍스트 블럭에 작성된 모든 줄이 그대로 반영됩니다.
이스케이프 최소화 \", \\n 등 복잡한 이스케이프 필요 없음
가독성 향상 복잡한 JSON, HTML, SQL 등에서 매우 유용함
들여쓰기 자동 제거 공백 처리 및 들여쓰기를 자동으로 조절함

 

 

정수형 변수

int형식은 총 32개의 비트를, long은 총 64개의 비트를 사용해 정수의 범위를 표현할 수 있다.

해당 비트로 표현할 수 있는 표현 범위를 벗어나게 되면 오버플로우/언더플로우를 발생하게 된다.

 

public class Main {
    public static void main(String[] args) {
        int i = Integer.MAX_VALUE;
        System.out.println(++i);
    }
}

 

변수 i를 int범위의 최대값으로 설정한 후 값을 증가시키니 MIN_VALUE가 출력되었다.

자바에서는 C/C++처럼 unsignedint 혹은 uint_8과 같이 임의로 정수의 범위를 변경할 수 없다.

자바는 항상 singed형식을 띄며, 부호가 없는 정수타입을 사용하려면 특정 메서드를 사용해 주어야 한다.

 

 

실수형 변수

float와 double을 통해 실수형 변수 타입을 지정할 수 있다.

주의할 점은 대부분의 언어에서 실수의 연산은 정확하지 않다는 점이다.

보통은 유효 자리수를 이용한 반올림 처리로 값을 지정하게 된다.

 

타입 크기 유효 자리수 (정확도) 범위
float 4 바이트 약 7자리 ±1.4 × 10^(-45) ~ ±3.4028235 × 10^(38)
double 8 바이트 약 15~16자리 ±4.9 × 10^(-324) ~ ±1.7976931348623157 × 10^(308)

 

float는 단정밀도로 유효 숫자는 약 7자리이다, 더 큰 값을 저장할 수 있지만, 7자리를 넘어가면 정밀도가 떨어진다.

double은 배정밀도로 유효 숫자는 약 15~16자리로, float에 비해 훨씬 더 정밀하며, 대부분의 실수 계산에서는 double이 기본적으로 사용된다.

 

public class Main {
    public static void main(String[] args) {
        float f1 = 0.1f + 0.2f;
        double d1 = 0.1 + 0.2;

        System.out.println(f1);  // 0.30000001
        System.out.println(d1);  // 0.30000000000000004
    }
}

 

위의 예시로 정밀도 차이를 볼 수 있다.

따라서 정확한 실수 연산이 필요할 때는 BigDecimal을 사용하는 것이 권장된다.

 

import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("0.1");
        BigDecimal b = new BigDecimal("0.2");
        BigDecimal c = a.add(b);

        System.out.println(c);  // 0.3

    }
}

 

java.math의 BigDecimal를 import해주어야 한다.

 

 

형변환

변수의 형을 다른 형으로 변환하는 것을 형 변환이라고 한다.

단, boolean은 다른 기본 형과 호환되지 않는다.

기본형과 참조형의 변환을 위해서는 Wrapper 클래스를 사용한다.

 

형 변환 방법에는 명시적 형 변환과 묵시적 형 변환이 있다.

명시적 형 변환은 강제로 형을 변환하며, 묵시적 형 변환은 자동으로 형을 변환하게 된다.

 

  • 작은 → 큰 크기: 자동 변환 (묵시적)
  • 큰 → 작은 크기: 강제 변환 (명시적)
  • byte → short → int → long → float → double
  • 반대로 갈 때는 (타입)을 사용해 명시적으로 변환해야 한다.
  • 묵시적 변환: 안전하며 자동으로 수행됨
  • 명시적 변환: 데이터 손실 위험이 있으므로 주의 필요

 

 

묵시적 형 변환

 

  • 작은 크기의 자료형 → 큰 크기의 자료형으로 변환
  • 데이터 손실이 발생하지 않기 때문에 자바가 자동으로 수행
  • 업캐스팅(Upcasting)이라고도 한다.
  • 데이터 손실 없음, 범위가 더 큰 타입으로 변환, 명시적 코드 작성 불필요

 

public class Main {
    public static void main(String[] args) {
        int intValue = 100;
        long longValue = intValue;  // int → long (자동 변환)
        float floatValue = longValue;  // long → float (자동 변환)
        System.out.println(floatValue);
    }
}

 

 

명시적 형 변환

 

  • 큰 크기의 자료형 → 작은 크기의 자료형으로 변환
  • 데이터 손실 가능성이 있어, 개발자가 직접 명시적으로 변환해야 한다.
  • 다운캐스팅(Downcasting)이라고도 한다.
  • 데이터 손실 가능성 있음, 명시적 코드 필요 ((타입)), 정밀도 손실 또는 오버플로우 발생 가능
public class Main {
    public static void main(String[] args) {
        double doubleValue = 9.99;
        int intValue = (int) doubleValue;  // double → int (강제 변환)
        System.out.println(intValue);  // 9 (소수점 이하 잘림)

        long longValue = 1000L;
        short shortValue = (short) longValue;  // long → short (강제 변환)
        System.out.println(shortValue);  // 1000 (데이터 손실 없음)
    }
}

 

 

하지만 명시적 형 변환 시 강제 변환으로 인한 오버플로우가 발생할 가능성이 있다.

 

public class Main {
    public static void main(String[] args) {
        int largeValue = 130;
        byte smallValue = (byte) largeValue;  // int → byte (강제 변환)
        System.out.println(smallValue);  // -126 (오버플로우 발생)
    }
}

 

 

래퍼 클래스(Wrapper Class)

자바에서는 기본 자료형(Primitive Type)을 객체로 다루기 위해 래퍼 클래스(Wrapper Class)를 제공한다.
래퍼 클래스를 사용하면 기본형 ↔ 객체형 변환이 가능하며, 문자열을 숫자로 변환하거나 형 변환을 더 쉽게 수행할 수 있다.

기본 자료형 래퍼 클래스
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

 

 

  • 박싱(Boxing): 기본형 → 래퍼 객체로 변환
  • 언박싱(Unboxing): 래퍼 객체 → 기본형으로 변환

 

public class Main {
    public static void main(String[] args) {
        int primitiveValue = 10;
        Integer wrapperValue = primitiveValue;  // 오토 박싱
        int newPrimitive = wrapperValue;        // 오토 언박싱
    }
}

 

 

래퍼 클래스를 활용한 형 변환

public class Main {
    public static void main(String[] args) {
        // 문자열 → int 변환
        String str = "200";
        int intValue = Integer.parseInt(str);
        System.out.println("int value: " + intValue);  // 200

        // int → double 변환
        Double doubleValue = Double.valueOf(intValue);
        System.out.println("double value: " + doubleValue);  // 200.0

        // double → int 변환 (소수점 제거)
        int roundedValue = doubleValue.intValue();
        System.out.println("rounded int: " + roundedValue);  // 200

        // 숫자 → 문자열
        String strValue = Integer.toString(150);
        System.out.println("String value: " + strValue);  // "150"

        // 문자열이 아닌 값 → double
        String invalid = "abc";
        try {
            double result = Double.parseDouble(invalid);  // 오류 발생
        } catch (NumberFormatException e) {
            System.out.println("변환 실패: " + e.getMessage());
        }
    }
}

 

래퍼 클래스를 사용할 경우 문자열 -> 정수 -> 실수 -> 정수 -> 문자열의 변환이 자유롭다.

단, "abc"와 같이 정수 혹은 실수형으로 변환할 수 없는 경우엔 NumberFormatException를 발생한다.

valueOf() 메서드는 null일 경우 NullPointerException을 발생한다.

 

 

산술 이항 연산 시 타입 일치

자바에서 두 개의 피연산자(operand)를 사용하는 산술 이항 연산(+, -, *, /, %)은 피연산자 간 타입을 일치시킨 후 연산이 수행된다.

즉, 서로 다른 타입의 피연산자가 있을 경우, 더 큰 타입으로 변환되어 연산이 이루어진다.

 

 

  • 두 피연산자의 타입이 다르면 더 큰 타입으로 자동 형 변환(묵시적 변환)이 발생
  • 정수형(byte, short, char)은 자동으로 int로 변환 후 연산
  • 하나가 실수형(float, double)이면 나머지도 실수형으로 변환 후 연산
  • long이 포함되면 다른 피연산자는 long으로 변환

 

 

1. 정수형 연산 (byte, short, char → int)

public class Main {
    public static void main(String[] args) {
        byte a = 10;
        byte b = 20;
        int result = a + b;  // byte + byte → int
        System.out.println(result);  // 30
    }
}

 

a와 b는 byte이지만, 연산 결과는 int로 변환된다.

 

2. 정수와 실수의 연산

public class Main {
    public static void main(String[] args) {
        int a = 5;
        double b = 2.5;
        double result = a + b;  // int + double → double
        System.out.println(result);  // 7.5
    }
}

 

a가 int이지만, b가 double이므로 a가 double로 변환되어 연산된다.

 

3. 정수형 연산에서 long 포함

public class Main {
    public static void main(String[] args) {
        int a = 1000;
        long b = 2000L;
        long result = a + b;  // int + long → long
        System.out.println(result);  // 3000
    }
}

 

a는 int지만, b가 long이므로 a가 long으로 변환되어 연산된다.

 

4. 실수형 연산 (float ↔ double)

public class Main {
    public static void main(String[] args) {
        float f = 3.5f;
        double d = 2.7;
        double result = f + d;  // float + double → double
        System.out.println(result);  // 6.2
    }
}

 

float가 double로 변환되어 연산된다. 결과는 double 타입

728x90
반응형