JAVA (1) 기본 타입 ~ 형변환

통밀·2026년 7월 20일

JAVA

목록 보기
1/10

Java 변수에는 무엇이 저장될까? 기본 타입부터 형 변환까지 완벽 정리

들어가며

Java 프로그램을 작성할 때 가장 먼저 접하는 개념은 변수와 데이터 타입이다.

처음에는 변수를 단순히 “값을 담는 상자”라고 배우지만, 실제로는 데이터의 종류와 크기, 메모리 저장 방식, 연산 결과까지 모두 데이터 타입의 영향을 받는다.

예를 들어 다음 코드는 비슷해 보이지만 서로 다른 결과를 만든다.

public class Main {

    public static void main(String[] args) {
        System.out.println(1 / 2);
        System.out.println(1.0 / 2);
    }
}

실행 결과는 다음과 같다.

0
0.5

첫 번째 연산은 정수끼리 계산했기 때문에 결과도 정수가 되고, 두 번째 연산은 실수가 포함되어 있어 실수 연산이 수행된다.

이번 글에서는 Java 소스 코드의 기본 구조부터 변수, 기본 타입, 문자열, 타입 변환, 콘솔 입출력까지 차근차근 정리해보려고 한다.


1. Java 프로그램의 기본 구조

Java 소스 코드는 일반적으로 패키지 선언, 클래스 선언, 메서드 선언, 실행문으로 구성된다.

package sec03.exam01;

public class Hello {

    public static void main(String[] args) {
        System.out.println("Hello, Java");
    }
}

각 부분을 나누어 살펴보자.


패키지 선언

package sec03.exam01;

package는 현재 클래스가 속한 패키지를 선언한다.

패키지는 관련된 클래스들을 그룹으로 관리하기 위한 구조이며, 일반적으로 디렉터리 구조와 일치한다.

위 코드가 정상적으로 작성되었다면 파일은 다음과 같은 위치에 존재해야 한다.

src
└─ sec03
   └─ exam01
      └─ Hello.java

패키지 이름은 일반적으로 모두 소문자로 작성한다.


클래스 선언

public class Hello {
}

Java 프로그램의 코드는 기본적으로 클래스 내부에 작성된다.

public class Hello는 Hello라는 이름의 공개 클래스를 선언한다는 의미다.

public 클래스가 존재하는 소스 파일의 이름은 클래스 이름과 같아야 한다.

클래스 이름: Hello
파일 이름: Hello.java

main 메서드

public static void main(String[] args) {
}

main() 메서드는 일반 Java 애플리케이션의 시작점이다. JVM은 프로그램을 실행할 때 이 메서드를 찾아 내부의 코드를 위에서부터 실행한다.

각 키워드는 다음과 같은 의미를 가진다.

  • public: JVM이 클래스 외부에서 메서드에 접근할 수 있도록 한다.
  • static: 객체를 생성하지 않아도 메서드를 실행할 수 있도록 한다.
  • void: 메서드가 값을 반환하지 않는다는 뜻이다.
  • main: JVM이 프로그램의 시작점으로 인식하는 메서드 이름이다.
  • String[] args: 프로그램 실행 시 전달받은 명령행 인수를 저장한다.

String[] args는 메서드가 받는 값을 정의하는 매개변수다.


실행문

System.out.println("Hello, Java");

실행문은 실제 동작을 수행하는 코드다.

main 메서드 블록 내부에는 다음과 같은 실행문을 작성할 수 있다.

int age = 20;                     // 변수 선언과 초기화
age = 21;                         // 변수에 값 저장
System.out.println(age);          // 메서드 호출

대부분의 Java 실행문 끝에는 세미콜론을 작성한다.

;

세미콜론은 하나의 실행문이 끝났다는 것을 의미한다.


2. Java의 주석

주석은 소스 코드에 설명을 작성할 때 사용한다.

주석은 컴파일 과정에서 제거되기 때문에 생성된 바이트 코드에는 포함되지 않는다.


한 줄 주석

// 뒤에 작성한 내용은 해당 줄이 끝날 때까지 주석으로 처리된다.

int age = 20; // 사용자의 나이

범위 주석

/*와 */ 사이의 모든 내용을 주석으로 처리한다.

/*
 * 회원의 이름과 나이를 저장한다.
 * 테스트용 코드다.
 */
String name = "김자바";
int age = 20;

문서화 주석

/**와 */ 사이에 작성하며, 클래스나 메서드의 API 문서를 만들 때 사용한다.

/**
 * 전달받은 두 정수를 더한다.
 *
 * @param left 첫 번째 정수
 * @param right 두 번째 정수
 * @return 두 정수의 합
 */
public static int add(int left, int right) {
    return left + right;
}

문서화 주석은 javadoc 도구를 이용해 HTML 형식의 API 문서를 생성할 때 활용할 수 있다.

주석은 코드가 무엇을 하는지 반복해서 적기보다는, 코드만으로 알기 어려운 이유나 의도를 설명하는 데 사용하는 것이 좋다.


3. 변수란?

컴퓨터의 메모리는 값을 저장할 수 있는 수많은 공간으로 구성되어 있다.

프로그램이 메모리 주소를 직접 사용해야 한다면 특정 값이 어디에 저장되었는지 계속 기억해야 하므로 프로그램을 작성하기 매우 어려워진다.

이 문제를 해결하기 위해 사용하는 것이 변수다.

변수는 값을 저장할 수 있는 메모리 공간에 붙인 이름이다.

개발자는 실제 메모리 주소 대신 변수 이름을 사용해 값을 저장하거나 읽을 수 있다.

int age = 20;

System.out.println(age);

위 코드에서 age는 변수 이름이며, 20은 변수에 저장된 값이다.


변수 선언

변수를 사용하려면 먼저 데이터 타입과 이름을 작성해야 한다.

int age;

기본 형식은 다음과 같다.

데이터타입 변수이름;

변수 선언은 Java에 다음 내용을 알려주는 과정이다.

  • 어떤 종류의 값을 저장할 것인지
  • 해당 값을 어떤 이름으로 사용할 것인지
  • 필요한 메모리 크기가 얼마인지

값 대입

변수에 값을 저장할 때는 대입 연산자 =를 사용한다.

int age;
age = 20;

대입 연산자는 오른쪽 값을 왼쪽 변수에 저장한다.

수학에서의 등호와는 의미가 다르다.

age = age + 1;

위 코드는 현재 age 값에 1을 더한 후 다시 age에 저장한다는 의미다.


변수 초기화

변수를 선언하면서 최초의 값을 저장하는 것을 초기화라고 한다.

int age = 20;

이때 20을 초기값이라고 부른다.

Java의 로컬 변수는 초기화하지 않은 상태로 사용할 수 없다.

public class Main {

    public static void main(String[] args) {
        int age;

        // System.out.println(age);
        // 컴파일 오류: age가 초기화되지 않음
    }
}

변수의 사용 범위

메서드나 제어문의 중괄호 내부에서 선언한 변수를 로컬 변수라고 한다.

로컬 변수는 자신이 선언된 중괄호 블록 내부에서만 사용할 수 있다.

public class Main {

    public static void main(String[] args) {
        int outer = 10;

        if (outer > 0) {
            int inner = 20;

            System.out.println(outer);
            System.out.println(inner);
        }

        System.out.println(outer);

        // System.out.println(inner);
        // 컴파일 오류: inner는 if 블록 밖에서 사용할 수 없음
    }
}

4. 변수 이름을 작성하는 규칙

Java의 변수 이름은 다음 규칙을 따라야 한다.

  • 첫 번째 문자는 문자, $, _ 중 하나여야 한다.
  • 숫자로 시작할 수 없다.
  • 공백을 포함할 수 없다.
  • Java 예약어를 사용할 수 없다.
  • 영어 대문자와 소문자를 구분한다.
int age;
int userAge;
int _count;
int $price;

다음 이름은 사용할 수 없다.

// int 1age;
// int user age;
// int class;

규칙상 $와 _로 시작할 수 있지만, 일반적인 변수 이름은 영어 소문자로 시작하는 lowerCamelCase 방식을 사용한다.

int studentAge;
String userName;
double totalPrice;

상수는 대문자와 언더바를 사용한다.

final int MAX_COUNT = 100;

5. Java의 데이터 타입

Java의 데이터 타입은 크게 기본 타입과 참조 타입으로 나뉜다.

기본 타입

기본 타입 변수에는 실제 값이 저장된다.

Java의 기본 타입은 총 8개다.

분류타입
정수byte, short, int, long
문자char
실수float, double
논리boolean

참조 타입

참조 타입 변수에는 객체가 존재하는 위치를 참조하는 값이 저장된다.

대표적인 참조 타입은 다음과 같다.

  • 배열
  • 클래스
  • 인터페이스
  • 열거 타입
  • String
String name = "김자바";
int[] scores = {90, 80, 100};

String은 기본 타입처럼 자주 사용되지만 실제로는 클래스이며, 참조 타입이다.


6. 정수 타입

Java의 정수 타입은 byte, short, int, long이 있다.

타입크기대략적인 범위
byte1바이트-128 ~ 127
short2바이트-32,768 ~ 32,767
int4바이트약 -21억 ~ 21억
long8바이트매우 큰 정수 범위

일반적인 정수는 int를 가장 많이 사용한다.

int age = 20;
int count = 100;

int 범위를 넘어가는 숫자는 long을 사용한다.

long population = 8_000_000_000L;

long 리터럴은 숫자 뒤에 L 또는 l을 작성한다.

소문자 l은 숫자 1과 헷갈릴 수 있으므로 대문자 L을 권장한다.

long value = 10_000_000_000L;

숫자 사이의 언더바는 가독성을 위한 표현이며 값에는 영향을 주지 않는다.


정수 리터럴

소스 코드에서 프로그래머가 직접 작성한 값을 리터럴이라고 한다.

Java에서는 정수를 여러 진법으로 표현할 수 있다.

public class Main {

    public static void main(String[] args) {
        int binary = 0b1010;     // 2진수
        int octal = 012;         // 8진수
        int decimal = 10;        // 10진수
        int hexadecimal = 0xA;   // 16진수

        System.out.println(binary);
        System.out.println(octal);
        System.out.println(decimal);
        System.out.println(hexadecimal);
    }
}

모두 10이 출력된다.


정수 오버플로

정수 타입이 표현할 수 있는 최대 범위를 넘으면 오버플로가 발생한다.

public class Main {

    public static void main(String[] args) {
        int max = Integer.MAX_VALUE;

        System.out.println(max);
        System.out.println(max + 1);
    }
}

실행 결과는 다음과 같다.

2147483647
-2147483648

최댓값에 1을 더했지만 더 큰 값이 되는 것이 아니라 최솟값으로 되돌아간다.

Integer는 int 타입과 관련된 기능을 제공하는 래퍼 클래스다. Integer.MAX_VALUE와 Integer.MIN_VALUE를 이용해 int의 범위를 확인할 수 있다.

System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
System.out.println(Long.MAX_VALUE);
System.out.println(Long.MIN_VALUE);

금액, 사용자 수, 시간처럼 값이 커질 가능성이 있다면 저장 범위를 고려하여 long을 사용해야 한다.


7. char 타입

char는 하나의 문자를 저장하는 타입이다.

문자 리터럴은 작은따옴표로 감싼다.

char grade = 'A';
char korean = '가';

큰따옴표를 사용하면 문자열이 되기 때문에 char 변수에 저장할 수 없다.

char letter = 'A';

// char invalid = "A";
// 컴파일 오류: "A"는 String

char는 정수 타입이다

char는 내부적으로 유니코드 값을 저장하는 정수 타입이다.

public class Main {

    public static void main(String[] args) {
        char letter = 'A';
        int unicode = letter;

        System.out.println(letter);
        System.out.println(unicode);
    }
}

실행 결과는 다음과 같다.

A
65

유니코드 값을 직접 저장할 수도 있다.

char letter1 = 65;
char letter2 = '\u0041';

System.out.println(letter1);
System.out.println(letter2);

두 변수 모두 A를 나타낸다.

char가 문자 자체를 저장한다고 표현하기도 하지만, 정확히는 UTF-16 코드 단위 하나를 저장한다. 따라서 모든 유니코드 문자를 하나의 char만으로 표현할 수 있는 것은 아니다. 일부 이모지처럼 보조 문자가 필요한 문자는 두 개의 char를 사용한다.


8. String 타입

여러 문자의 모음을 문자열이라고 한다.

Java에서 문자열은 큰따옴표로 감싼다.

String name = "김자바";
String message = "Hello, Java";

String은 기본 타입이 아니라 클래스이므로 참조 타입이다.

char letter = 'A';
String text = "A";

'A'와 "A"는 서로 다른 타입이다.

  • 'A': char
  • "A": String

이스케이프 문자

문자열 내부에서 역슬래시 \는 특별한 문자를 표현하는 이스케이프 문자로 사용된다.

public class Main {

    public static void main(String[] args) {
        System.out.println("나는 \"Java\"를 좋아합니다.");
        System.out.println("첫 번째 줄\n두 번째 줄");
        System.out.println("이름\t나이");
        System.out.println("C:\\java\\workspace");
    }
}

대표적인 이스케이프 문자는 다음과 같다.

표현의미
\n줄바꿈
\t탭
\"큰따옴표
\'작은따옴표
\\역슬래시

텍스트 블록

Java 15부터 텍스트 블록을 정식으로 사용할 수 있다.

텍스트 블록은 여러 줄의 문자열을 가독성 좋게 작성할 때 사용한다.

public class Main {

    public static void main(String[] args) {
        String message = """
                내 이름은 %s,
                나이는 %d살이고,
                사는 곳은 %s입니다.
                """.formatted("홍길동", 20, "율도국");

        System.out.println(message);
    }
}

텍스트 블록은 큰따옴표 두 개가 아니라 큰따옴표 세 개를 사용한다.

"""
여러 줄 문자열
"""

9. 실수 타입

Java의 실수 타입은 float와 double이 있다.

타입크기대략적인 정밀도
float4바이트약 7자리
double8바이트약 15자리

Java는 실수 리터럴을 기본적으로 double로 해석한다.

double pi = 3.14;

float 변수에 실수 리터럴을 저장하려면 뒤에 F 또는 f를 붙여야 한다.

float pi = 3.14F;

다음 코드는 컴파일 오류가 발생한다.

// float pi = 3.14;

3.14가 double로 해석되는데, 더 큰 범위를 가진 double 값을 float에 자동으로 저장할 수 없기 때문이다.


실수 연산은 정확하지 않을 수 있다

컴퓨터는 실수를 2진수 기반으로 저장한다.

일부 10진수 실수는 2진수로 정확하게 표현할 수 없으므로 근삿값이 저장된다.

public class Main {

    public static void main(String[] args) {
        double first = 2.0;
        double second = 1.1;

        System.out.println(first - second);
    }
}

실행 결과는 다음과 같이 나올 수 있다.

0.8999999999999999

이는 Java의 오류가 아니라 IEEE 754 부동소수점 표현 방식에서 발생하는 일반적인 현상이다.


정확한 소수 계산과 BigDecimal

금액처럼 정확한 소수 계산이 필요한 경우 double 대신 BigDecimal을 고려해야 한다.

import java.math.BigDecimal;

public class Main {

    public static void main(String[] args) {
        BigDecimal first = new BigDecimal("2.0");
        BigDecimal second = new BigDecimal("1.1");

        BigDecimal result = first.subtract(second);

        System.out.println(result);
    }
}

실행 결과는 정확히 다음과 같다.

0.9

BigDecimal은 실수형이 아니라 객체이므로 산술 연산자를 직접 사용할 수 없다.

BigDecimal addResult = first.add(second);
BigDecimal subtractResult = first.subtract(second);
BigDecimal multiplyResult = first.multiply(second);

new BigDecimal(0.1)보다 new BigDecimal("0.1") 또는 BigDecimal.valueOf(0.1)을 사용하는 것이 안전하다. double의 근삿값이 그대로 전달될 수 있기 때문이다.


10. boolean 타입

boolean은 참과 거짓을 저장한다.

boolean isLoggedIn = true;
boolean isDeleted = false;

boolean에는 true와 false만 저장할 수 있다.

int age = 20;
boolean isAdult = age >= 19;

System.out.println(isAdult);

C 언어처럼 숫자 0이나 1을 논리값으로 사용할 수 없다.

// boolean result = 1;
// 컴파일 오류

boolean은 다른 기본 타입과 타입 변환되지 않는다.


11. 타입 변환이란?

타입 변환은 하나의 데이터 타입을 다른 데이터 타입으로 바꾸는 것을 의미한다.

int intValue = 10;
double doubleValue = intValue;

변수 값을 다른 타입의 변수에 저장하거나 서로 다른 타입끼리 연산할 때 타입 변환이 발생한다.

타입 변환은 크게 두 가지로 나뉜다.

  • 자동 타입 변환
  • 강제 타입 변환

12. 자동 타입 변환

자동 타입 변환은 상대적으로 표현 범위가 작은 타입의 값을 더 큰 타입에 저장할 때 발생한다.

byte → short → int → long → float → double
public class Main {

    public static void main(String[] args) {
        byte byteValue = 10;
        int intValue = byteValue;
        long longValue = intValue;
        double doubleValue = longValue;

        System.out.println(doubleValue);
    }
}

실행 결과는 다음과 같다.

10.0

값이 더 넓은 표현 범위를 가진 타입으로 이동하므로 일반적으로 데이터 손실 가능성이 낮다.


타입의 크기보다 표현 범위가 중요하다

자동 타입 변환은 단순히 메모리 크기만 비교하는 것이 아니다.

예를 들어 long은 8바이트이고 float은 4바이트지만 다음 변환이 허용된다.

long longValue = 100L;
float floatValue = longValue;

그 이유는 float이 long보다 더 넓은 크기의 값을 표현할 수 있기 때문이다.

다만 float은 정밀도가 제한되어 있으므로 큰 정수의 일부 자릿수가 손실될 수 있다.

public class Main {

    public static void main(String[] args) {
        long longValue = 123456789123456789L;
        float floatValue = longValue;

        System.out.println(longValue);
        System.out.println(floatValue);
    }
}

따라서 “자동 변환이 가능하다”와 “값이 완벽하게 보존된다”는 항상 같은 의미가 아니다.


char에서 int로 변환

char는 정수 타입이므로 int로 자동 변환할 수 있다.

char charValue = 'A';
int intValue = charValue;

System.out.println(intValue);

출력 결과는 65다.

반대로 byte에서 char로는 자동 변환할 수 없다.

byte byteValue = 65;

// char charValue = byteValue;
// 컴파일 오류

byte는 음수를 저장할 수 있지만 char는 0 이상의 값만 저장하므로 자동 변환이 허용되지 않는다.


13. 강제 타입 변환

표현 범위가 큰 타입의 값을 작은 타입에 저장하려면 강제 타입 변환이 필요하다.

강제 타입 변환은 캐스팅 연산자 ()를 사용한다.

double doubleValue = 3.14;
int intValue = (int) doubleValue;

System.out.println(intValue);

실행 결과는 다음과 같다.

3

소수점 이하 값은 버려진다.

원본 변수의 값은 변경되지 않는다.

double doubleValue = 3.14;
int intValue = (int) doubleValue;

System.out.println(doubleValue); // 3.14
System.out.println(intValue);    // 3

정수 범위를 벗어나는 강제 변환

강제 변환은 데이터 손실이 발생할 수 있다.

public class Main {

    public static void main(String[] args) {
        int intValue = 300;
        byte byteValue = (byte) intValue;

        System.out.println(byteValue);
    }
}

byte는 300을 저장할 수 없으므로 기대한 값과 다른 결과가 나온다.

강제 타입 변환은 컴파일 오류를 제거하는 수단이 아니라, 데이터 손실 가능성을 개발자가 책임지고 변환하겠다는 의미다.


int를 char로 변환

int intValue = 65;
char charValue = (char) intValue;

System.out.println(charValue);

실행 결과는 다음과 같다.

A

14. 정수 연산에서의 자동 타입 변환

byte, short, char 타입이 산술 연산에 사용되면 연산 전에 int로 자동 변환된다.

byte left = 10;
byte right = 20;

// byte result = left + right;
// 컴파일 오류

int result = left + right;

두 피연산자가 모두 byte여도 결과는 int가 된다.

따라서 특별한 이유가 없다면 정수 연산에는 int를 사용하는 것이 편리하다.


상수식의 예외

컴파일 시점에 결과를 확정할 수 있고 값이 타입 범위 안에 있다면 일부 경우에는 대입이 허용된다.

byte result = 10 + 20;

10 + 20은 컴파일 시점에 30이라는 값으로 계산할 수 있고 byte 범위 안에 있으므로 저장할 수 있다.

반면 변수가 포함되면 실행 시점에 계산되므로 int 결과를 그대로 byte에 넣을 수 없다.

byte x = 10;
byte y = 20;

// byte result = x + y;

long이 포함된 정수 연산

피연산자 중 하나가 long이면 다른 정수 피연산자도 long으로 변환된다.

int intValue = 10;
long longValue = 20L;

long result = intValue + longValue;

15. 실수 연산에서의 자동 타입 변환

피연산자 중 하나가 double이면 다른 피연산자도 double로 변환된다.

int intValue = 10;
double doubleValue = 5.5;

double result = intValue + doubleValue;

System.out.println(result);

실행 결과는 다음과 같다.

15.5

정수 결과가 필요하다면 명시적으로 강제 변환해야 한다.

int result = intValue + (int) doubleValue;

이 경우 5.5가 5로 변환된 후 연산되므로 결과는 15다.


float 리터럴 연산

실수 리터럴은 기본적으로 double이다.

double result = 1.5 + 2.3;

float 결과를 얻으려면 각 리터럴에 F를 붙여야 한다.

float result = 1.5F + 2.3F;

16. 정수 나눗셈의 주의점

정수끼리 나누면 결과도 정수다.

int result = 1 / 2;

System.out.println(result);

결과는 0이다.

정수 연산에서는 소수점 이하 값이 버려지기 때문이다.

실수 결과를 얻으려면 연산 전에 피연산자 중 하나를 실수로 변환해야 한다.

public class Main {

    public static void main(String[] args) {
        int x = 1;
        int y = 2;

        double result1 = (double) x / y;
        double result2 = x / (double) y;
        double result3 = (double) x / (double) y;

        System.out.println(result1);
        System.out.println(result2);
        System.out.println(result3);
    }
}

세 결과 모두 0.5다.

다음 코드는 의도한 결과를 만들지 못한다.

double result = (double) (x / y);

x / y가 먼저 정수 연산되어 0이 된 후 double로 변환되므로 결과는 0.0이다.

타입 변환은 연산이 수행되기 전에 적용해야 한다.


17. 문자열 결합 연산

+ 연산자는 숫자끼리 사용하면 덧셈을 수행한다.

int result = 3 + 7;

피연산자 중 하나가 문자열이면 문자열 결합 연산이 수행된다.

String result = 3 + "7";

System.out.println(result);

출력 결과는 "37"이다.


연산은 왼쪽부터 수행된다

public class Main {

    public static void main(String[] args) {
        System.out.println(1 + 2 + 3);
        System.out.println(1 + 2 + "3");
        System.out.println(1 + "2" + 3);
    }
}

실행 결과는 다음과 같다.

6
33
123

첫 번째 식은 모두 숫자이므로 덧셈이 수행된다.

1 + 2 + 3
→ 3 + 3
→ 6

두 번째 식은 1 + 2가 먼저 계산된 후 문자열과 결합된다.

1 + 2 + "3"
→ 3 + "3"
→ "33"

세 번째 식은 처음부터 문자열 결합이 시작된다.

1 + "2" + 3
→ "12" + 3
→ "123"

원하는 계산 순서를 명확히 하려면 괄호를 사용한다.

String result = "결과: " + (1 + 2 + 3);

18. 문자열을 기본 타입으로 변환하기

사용자가 입력한 값이나 외부에서 받은 데이터는 문자열인 경우가 많다.

문자열을 숫자나 논리값으로 바꾸려면 각 래퍼 클래스의 parse 메서드를 사용한다.

byte byteValue = Byte.parseByte("10");
short shortValue = Short.parseShort("100");
int intValue = Integer.parseInt("1000");
long longValue = Long.parseLong("10000");
float floatValue = Float.parseFloat("3.14");
double doubleValue = Double.parseDouble("3.141592");
boolean booleanValue = Boolean.parseBoolean("true");

Integer의 철자를 주의해야 한다.

Integer

Interger가 아니다.


NumberFormatException

숫자로 변환할 수 없는 문자열을 변환하려 하면 NumberFormatException이 발생한다.

public class Main {

    public static void main(String[] args) {
        String value = "100원";

        int number = Integer.parseInt(value);

        System.out.println(number);
    }
}

"100원"에는 숫자가 아닌 문자가 포함되어 있으므로 예외가 발생한다.

안전하게 처리하려면 예외 처리를 사용할 수 있다.

public class Main {

    public static void main(String[] args) {
        String value = "100원";

        try {
            int number = Integer.parseInt(value);
            System.out.println(number);
        } catch (NumberFormatException exception) {
            System.out.println("숫자로 변환할 수 없는 값입니다.");
        }
    }
}

19. 기본 타입을 문자열로 변환하기

기본 타입 값을 문자열로 바꾸는 대표적인 방법은 String.valueOf()다.

int number = 3;
String text = String.valueOf(number);

System.out.println(text);

빈 문자열과 결합하는 방법도 있다.

String text = "" + 3;

간단한 출력에는 사용할 수 있지만, 변환 의도를 명확하게 나타내기 위해서는 String.valueOf()가 더 적절하다.

String text = String.valueOf(3);

20. 기본 타입과 래퍼 클래스

각 기본 타입에는 대응되는 래퍼 클래스가 있다.

기본 타입래퍼 클래스
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

래퍼 클래스는 기본 타입 값을 객체 형태로 다룰 수 있게 한다.

int primitive = 10;
Integer wrapper = primitive;

기본 타입이 래퍼 객체로 변환되는 것을 오토박싱이라고 한다.

Integer wrapper = 10;

반대로 래퍼 객체가 기본 타입으로 변환되는 것을 언박싱이라고 한다.

Integer wrapper = 10;
int primitive = wrapper;

기본 타입과 참조 타입 사이의 모든 변환이 래퍼 클래스로 해결되는 것은 아니다. 기본 타입을 객체처럼 다루거나 문자열과 기본 타입 사이를 변환할 때 래퍼 클래스가 주로 사용된다.


21. 콘솔 출력

Java에서 표준 출력 장치는 일반적으로 모니터 또는 콘솔이다.

System.out

System.out을 통해 값을 출력할 수 있다.


println()

값을 출력한 후 줄을 바꾼다.

System.out.println("Java");
System.out.println("Spring");

출력 결과는 다음과 같다.

Java
Spring

print()

값을 출력한 후 줄을 바꾸지 않는다.

System.out.print("Java ");
System.out.print("Spring");

출력 결과는 다음과 같다.

Java Spring

printf()

형식 문자열에 맞춰 값을 출력한다.

public class Main {

    public static void main(String[] args) {
        String name = "김자바";
        int age = 20;
        double height = 175.456;

        System.out.printf("이름: %s%n", name);
        System.out.printf("나이: %d세%n", age);
        System.out.printf("키: %.1fcm%n", height);
    }
}

대표적인 변환 문자는 다음과 같다.

문자용도
%d정수
%f실수
%s문자열
%c문자
%b논리값
%n운영체제에 맞는 줄바꿈

전체 자릿수와 소수 자릿수

double value = 123.456789;

System.out.printf("%10.2f%n", value);

%10.2f는 다음을 의미한다.

  • 10: 전체 출력 폭
  • .2: 소수점 이하 두 자리
  • f: 실수 출력

argument index

출력할 값의 순서를 지정할 수도 있다.

String name = "김자바";
int age = 20;

System.out.printf("이름은 %1$s이고, 나이는 %2$d세입니다.%n", name, age);

일반적으로 값을 작성한 순서대로 사용한다면 인덱스는 생략해도 된다.

System.out.printf("이름은 %s이고, 나이는 %d세입니다.%n", name, age);

22. 키보드 입력

Java의 표준 입력 장치는 일반적으로 키보드다.

System.in

System.in.read()

System.in.read()는 입력된 데이터를 바이트 단위로 읽어 정수 형태의 값을 반환한다.

public class Main {

    public static void main(String[] args) throws Exception {
        int keyCode = System.in.read();

        System.out.println(keyCode);
    }
}

사용자가 A를 입력하면 65가 출력된다.

다만 System.in.read()는 다음과 같은 이유로 일반적인 콘솔 입력에 불편하다.

  • 한 바이트 또는 바이트 단위로 읽는다.
  • 문자열 전체를 편리하게 처리하기 어렵다.
  • 입력 예외 처리가 필요하다.
  • 한글처럼 여러 바이트로 표현되는 문자를 직접 처리하기 복잡하다.

따라서 입문 단계에서는 주로 Scanner를 사용한다.


23. Scanner를 이용한 입력

Scanner를 사용하려면 먼저 클래스를 import해야 한다.

import java.util.Scanner;

전체 예제는 다음과 같다.

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("이름을 입력하세요: ");
        String name = scanner.nextLine();

        System.out.print("나이를 입력하세요: ");
        int age = Integer.parseInt(scanner.nextLine());

        System.out.printf("%s님의 나이는 %d세입니다.%n", name, age);

        scanner.close();
    }
}

nextLine()은 사용자가 입력한 한 줄을 문자열로 반환한다.

숫자가 필요하다면 문자열을 기본 타입으로 변환할 수 있다.

int age = Integer.parseInt(scanner.nextLine());

nextInt()와 nextLine()의 주의점

다음과 같이 nextInt() 뒤에 nextLine()을 사용하면 예상과 다른 결과가 나올 수 있다.

int age = scanner.nextInt();
String name = scanner.nextLine();

nextInt()는 숫자만 읽고 사용자가 입력한 줄바꿈 문자는 남겨둔다. 이후 nextLine()이 남아 있는 줄바꿈을 읽어 빈 문자열을 반환할 수 있다.

이를 해결하는 방법은 두 가지다.

남은 줄바꿈을 한 번 읽기

int age = scanner.nextInt();
scanner.nextLine();

String name = scanner.nextLine();

모든 입력을 nextLine()으로 받고 변환하기

int age = Integer.parseInt(scanner.nextLine());
String name = scanner.nextLine();

입력 방식을 통일할 수 있기 때문에 두 번째 방법이 실수하기 적다.


24. 기본 타입 비교와 문자열 비교

기본 타입의 값은 == 연산자로 비교한다.

int first = 10;
int second = 10;

boolean result = first == second;

System.out.println(result);

문자열의 내용은 equals() 메서드로 비교해야 한다.

String input = "Java";

boolean result = input.equals("Java");

System.out.println(result);

문자열에 ==를 사용하면 안 되는 이유

String은 참조 타입이다.

참조 타입에 ==를 사용하면 문자열 내용이 아니라 동일한 객체를 참조하는지 비교한다.

public class Main {

    public static void main(String[] args) {
        String first = new String("Java");
        String second = new String("Java");

        System.out.println(first == second);
        System.out.println(first.equals(second));
    }
}

실행 결과는 다음과 같다.

false
true

두 객체의 내용은 같지만 서로 다른 객체이기 때문에 == 결과는 false다.

문자열의 내용을 비교하려면 equals()를 사용해야 한다.


NullPointerException을 피하는 문자열 비교

다음 코드에서 input이 null이면 예외가 발생한다.

input.equals("Java");

비교 대상이 고정 문자열이라면 다음과 같이 작성할 수 있다.

"Java".equals(input);

고정 문자열 "Java"는 null이 아니므로 input이 null이어도 false를 반환한다.

Java 7 이상에서는 Objects.equals()도 사용할 수 있다.

import java.util.Objects;

boolean result = Objects.equals(input, "Java");

두 값이 모두 null인 경우에도 안전하게 비교할 수 있다.


25. 종합 예제

사용자에게 상품명, 가격, 수량을 입력받아 총금액을 계산하는 프로그램을 작성해보자.

import java.util.Scanner;

public class OrderCalculator {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("상품명을 입력하세요: ");
        String productName = scanner.nextLine();

        System.out.print("상품 가격을 입력하세요: ");
        int price = Integer.parseInt(scanner.nextLine());

        System.out.print("구매 수량을 입력하세요: ");
        int quantity = Integer.parseInt(scanner.nextLine());

        int totalPrice = price * quantity;

        System.out.println();
        System.out.println("===== 주문 정보 =====");
        System.out.printf("상품명: %s%n", productName);
        System.out.printf("가격: %,d원%n", price);
        System.out.printf("수량: %d개%n", quantity);
        System.out.printf("총금액: %,d원%n", totalPrice);

        scanner.close();
    }
}

입력 예시는 다음과 같다.

상품명을 입력하세요: 키보드
상품 가격을 입력하세요: 50000
구매 수량을 입력하세요: 2

출력 결과는 다음과 같다.

===== 주문 정보 =====
상품명: 키보드
가격: 50,000원
수량: 2개
총금액: 100,000원

이 예제에는 다음 개념이 사용되었다.

  • 변수 선언과 초기화
  • String과 int
  • Scanner 입력
  • Integer.parseInt()
  • 산술 연산
  • printf() 형식 출력

헷갈리기 쉬운 개념 정리

선언, 대입, 초기화

int number;       // 선언
number = 10;      // 대입
int age = 20;     // 선언과 동시에 초기화

문자와 문자열

char letter = 'A';
String text = "A";

작은따옴표는 문자, 큰따옴표는 문자열이다.


자동 타입 변환과 강제 타입 변환

int intValue = 10;
double doubleValue = intValue;       // 자동 타입 변환

double pi = 3.14;
int number = (int) pi;               // 강제 타입 변환

자동 타입 변환은 일반적으로 표현 범위가 넓어지는 방향이고, 강제 타입 변환은 데이터 손실 가능성이 있다.


parseInt()와 valueOf()

int number = Integer.parseInt("100");
Integer wrapper = Integer.valueOf("100");
  • Integer.parseInt()는 기본 타입 int를 반환한다.
  • Integer.valueOf()는 래퍼 객체 Integer를 반환한다.

오토 언박싱 때문에 비슷하게 사용할 수 있지만 반환 타입은 다르다.


System.out.print("A");
System.out.println("B");
System.out.printf("%d%n", 100);
  • print(): 출력 후 줄바꿈하지 않는다.
  • println(): 출력 후 줄바꿈한다.
  • printf(): 형식을 지정해 출력한다.

실무에서 알아두면 좋은 내용

금액은 double로 계산하지 않는다

실수 계산에는 오차가 발생할 수 있으므로 정확한 금액 계산에 double을 사용하는 것은 위험하다.

소수점이 필요 없다면 최소 단위의 정수로 관리할 수 있다.

long priceInWon = 10_000L;

소수점 계산이 필요하면 BigDecimal을 고려한다.


입력값은 항상 검증해야 한다

사용자가 반드시 숫자를 입력한다고 보장할 수 없다.

try {
    int age = Integer.parseInt(input);
} catch (NumberFormatException exception) {
    System.out.println("숫자만 입력해야 합니다.");
}

문자열 비교는 equals()

문자열 내용을 비교할 때는 ==가 아니라 equals()를 사용한다.

"admin".equals(role);

변수 이름에는 의미를 담는다

다음과 같이 의미를 알 수 없는 이름은 피하는 것이 좋다.

int a;
int b;
int c;

역할을 알 수 있도록 작성한다.

int productPrice;
int quantity;
int totalPrice;

오늘 배운 내용 정리

오늘은 Java 프로그램의 기본 구조부터 변수, 데이터 타입, 타입 변환, 콘솔 입출력까지 정리했다.

  • Java 애플리케이션은 일반적으로 main() 메서드에서 실행을 시작한다.
  • 변수는 값을 저장할 메모리 공간에 붙인 이름이다.
  • 로컬 변수는 사용하기 전에 반드시 초기화해야 한다.
  • Java의 기본 타입은 정수, 문자, 실수, 논리 타입으로 구분된다.
  • String은 기본 타입이 아니라 문자열 객체를 다루는 참조 타입이다.
  • char는 내부적으로 UTF-16 코드 단위에 해당하는 정숫값을 저장한다.
  • Java의 실수 리터럴은 기본적으로 double로 해석된다.
  • 부동소수점 연산은 오차가 발생할 수 있으며 정확한 소수 계산에는 BigDecimal을 고려해야 한다.
  • 작은 표현 범위에서 큰 표현 범위로 이동할 때는 자동 타입 변환이 발생한다.
  • 큰 표현 범위에서 작은 표현 범위로 변환하려면 캐스팅 연산자를 사용해야 한다.
  • 강제 타입 변환 과정에서는 값이나 정밀도가 손실될 수 있다.
  • byte, short, char는 산술 연산 시 int로 변환된다.
  • 정수끼리 나누면 결과도 정수이므로 실수 결과가 필요하면 연산 전에 타입을 변환해야 한다.
  • 문자열을 기본 타입으로 변환할 때는 Integer.parseInt(), Double.parseDouble() 등의 메서드를 사용한다.
  • 기본 타입을 문자열로 변환할 때는 String.valueOf()를 사용할 수 있다.
  • println()은 줄바꿈을 포함하고, print()는 줄바꿈하지 않으며, printf()는 출력 형식을 지정한다.
  • 콘솔 입력은 Scanner를 통해 편리하게 받을 수 있다.
  • 기본 타입의 값은 ==로 비교하지만 문자열의 내용은 equals()로 비교해야 한다.
profile
넘어가 울타리 green green

0개의 댓글