Java는 변수마다 어떤 형태의 데이터를 보관할지 명시하는 언어다. 이 제약 덕분에 개발자는 값을 사용하기 전에 잘못된 타입 조합을 확인할 수 있다. 가장 기본적인 자료형부터 연산 중 발생하는 의외의 결과까지 살펴보자.
기본형(primitive type)은 byte, short, int, long, float, double, char, boolean의 8가지다. 이와 달리 String, 배열, 직접 작성한 클래스는 참조형(reference type)이다.
| 유형 | 대표 타입 | 주로 다루는 값 |
|---|---|---|
| 정수 | byte, short, int, long | 개수, 식별 번호 |
| 실수 | float, double | 비율, 측정값 |
| 문자 | char | UTF-16 코드 단위 하나 |
| 논리 | boolean | true, false |
| 참조형 | String, 배열, 객체 | 문자열, 여러 데이터, 상태와 동작 |
일반적인 정수는 int, 실수는 double을 우선 고려하면 된다. 정수 범위가 int로 부족하다면 long을 사용한다.
int productCount = 8;
long totalViews = 3_000_000_000L;
double discountRate = 0.15;
boolean available = true;
char statusCode = 'A';
String productName = "무선 키보드";
long 리터럴에는 L, float 리터럴에는 F를 붙인다. char는 작은따옴표, String은 큰따옴표를 사용한다. 특히 char는 모든 유니코드 문자를 하나씩 표현하는 타입이 아니다. 일부 문자는 UTF-16 코드 단위 두 개를 사용한다.
int stock = 10; // 선언과 초기화
stock = 7; // 값 재할당
final int MAX_RETRY = 3; // 재할당할 수 없는 변수
의도를 드러내는 이름이 중요하다. 지역 변수에는 camelCase, 상수 역할을 하는 static final 값에는 보통 UPPER_SNAKE_CASE를 사용한다. 변수 이름은 숫자로 시작할 수 없으며 Java 키워드도 사용할 수 없다.
리터럴은 코드에 직접 적은 10, 3.14, "hello" 같은 값이고, final은 변수에 새로운 값을 할당하지 못하게 하는 키워드다. 둘을 같은 개념으로 혼동하지 말자.
int totalPrice = 12_000;
int quantity = 3;
int orderAmount = totalPrice * quantity; // 36000
int remainder = 7 % 3; // 1
quantity += 2; // quantity = 5
boolean bulkOrder = quantity >= 5; // true
+, -, *, /, %는 산술 연산자, =, += 등은 대입 연산자, >, >=, ==, != 등은 비교 연산자다.
논리식에서는 &&(AND), ||(OR), !(NOT)을 사용한다. &&와 ||는 필요하지 않으면 오른쪽 식을 평가하지 않는 단락 평가(short-circuit)가 적용된다.
String coupon = null;
boolean valid = coupon != null && coupon.startsWith("SALE");
System.out.println(valid); // false, 예외 없음
System.out.println(7 / 2); // 3
System.out.println(7 / 2.0); // 3.5
System.out.println((double) 7 / 2); // 3.5
첫 번째 식은 두 피연산자가 모두 정수이기 때문에 정수 나눗셈으로 계산한다. 결과가 double 변수에 대입되더라도 먼저 정수 나눗셈을 했다면 잃어버린 소수 부분은 돌아오지 않는다.
double wrong = 7 / 2; // 3.0
double correct = 7 / 2.0; // 3.5
변환은 크게 자동 형 변환과 명시적 형 변환으로 나뉜다.
int count = 12;
long wide = count; // 자동 변환
double ratio = 12.9;
int truncated = (int) ratio;
System.out.println(truncated); // 12
double에서 int로 변환할 때 소수 부분은 반올림되지 않고 버려진다. 정수형이 표현할 수 없는 크기의 숫자를 좁은 타입으로 변환하면 값이 달라질 수도 있다. 또한 int에서 float처럼 자동 변환이 가능해도 큰 정수의 정밀도가 모두 유지된다는 보장은 없다.
int big = 16_777_217;
float converted = big;
int recovered = (int) converted;
System.out.println(recovered == big); // false
문자열을 숫자로 바꿀 때는 형 변환 연산자만으로 해결할 수 없다.
int page = Integer.parseInt("12");
double score = Double.parseDouble("97.5");
숫자가 아닌 문자열을 전달하면 NumberFormatException이 발생한다.
==를 피해야 하는 이유String input = new String("PAID");
String expected = "PAID";
System.out.println(input == expected); // false
System.out.println(input.equals(expected)); // true
참조형에서 ==는 두 변수가 같은 객체를 가리키는지 확인한다. 문자열 내용 비교에는 equals()를 사용하자. input이 null일 수도 있다면 "PAID".equals(input)처럼 상수 쪽에서 호출할 수 있다.
자주 쓰는 문자열 기능도 정리해 두자.
String message = " order-confirmed ";
System.out.println(message.trim()); // "order-confirmed"
System.out.println(message.contains("order")); // true
System.out.println(message.replace("-", "_")); // " order_confirmed "
System.out.println(message.substring(2, 7)); // "order"
문자열은 변경 불가능(immutable)하다. replace(), trim() 등이 기존 문자열 내용을 직접 고치는 것이 아니라 새로운 문자열 값을 반환한다는 뜻이다.
// 한 줄 설명
int count = 5;
/*
여러 줄로
설명할 수 있다.
*/
/** 사용자에게 보여 줄 상품명을 반환한다. */
public static String getDisplayName() {
return "기본 상품";
}
//, /* ... */, /** ... */를 상황에 맞게 활용한다. 마지막 형태는 Javadoc 문서화 주석이다. 일반 문장은 대부분 세미콜론 ;으로 끝나지만 클래스나 메서드 선언 블록 뒤에는 보통 세미콜론을 붙이지 않는다.
String은 참조형이다.final은 변수의 재할당을 제한한다.equals()를 사용한다.&&, ||에는 단락 평가가 적용된다.#Java #자바 #자료형 #변수 #연산자 #형변환 #String #프로그래밍기초