Ch 3.4 비교 연산자 (Comparison Operator)
비교 연산자는 두 피연산자를 비교하여 true 또는 false 를 반환합니다. 조건문(if, while, for)과 함께 사용되어 프로그램의 흐름을 제어하는 핵심 요소입니다.
1. 비교 연산자 6가지 완전 정리
| 연산자 | 이름 | 설명 | 예시 | 결과 |
|---|---|---|---|---|
== | 동등 | 두 값이 같으면 true | 5 == 5 | true |
!= | 불동등 | 두 값이 다르면 true | 5 != 3 | true |
> | 초과 | 좌변이 우변보다 크면 true | 5 > 3 | true |
< | 미만 | 좌변이 우변보다 작으면 true | 3 < 5 | true |
>= | 이상 | 좌변이 우변보다 크거나 같으면 true | 5 >= 5 | true |
<= | 이하 | 좌변이 우변보다 작거나 같으면 true | 3 <= 5 | true |
public class ComparisonBasic {
public static void main(String[] args) {
int a = 10, b = 5, c = 10;
System.out.println("a == b : " + (a == b)); // false
System.out.println("a == c : " + (a == c)); // true
System.out.println("a != b : " + (a != b)); // true
System.out.println("a > b : " + (a > b)); // true
System.out.println("a < b : " + (a < b)); // false
System.out.println("a >= c : " + (a >= c)); // true
System.out.println("b <= c : " + (b <= c)); // true
}
}
boolean 타입은 ==와 !=만 사용 가능하며, >, <, >=, <=는 사용할 수 없습니다.
boolean flag = true;
// System.out.println(flag > false); // 컴파일 에러!
System.out.println(flag == true); // true (가능하지만 flag 자체를 쓰는 것이 더 관용적)
2. 기본형 비교: 값 자체를 비교
int, long, double, boolean, char 등 기본형(Primitive) 에 == 연산자를 사용하면 값(Value) 자체를 직접 비교 합니다.
public class PrimitiveComparison {
public static void main(String[] args) {
int x = 100;
int y = 100;
System.out.println(x == y); // true (값이 같음)
double d1 = 3.14;
double d2 = 3.14;
System.out.println(d1 == d2); // true
char c1 = 'A';
char c2 = 65; // 'A'의 유니코드 값
System.out.println(c1 == c2); // true (char는 내부적으로 숫자)
boolean b1 = true;
boolean b2 = true;
System.out.println(b1 == b2); // true
}
}
3. 참조형 비교: ==는 주소값 비교
객체(참조형)에 ==를 사용하면 객체의 내용이 아닌 메모리 주소값(참조값)을 비교 합니다. 따라서 내용이 같더라도 다른 객체라면 false를 반환합니다.
public class ReferenceComparison {
public static void main(String[] args) {
// new 키워드로 생성한 두 배열: 내용은 같지만 서로 다른 객체
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
int[] arr3 = arr1; // arr1의 참조값을 복사
System.out.println(arr1 == arr2); // false (다른 객체, 다른 주소)
System.out.println(arr1 == arr3); // true (같은 객체를 가리킴)
// 배열 내용 비교는 Arrays.equals() 사용
System.out.println(java.util.Arrays.equals(arr1, arr2)); // true (내용 비교)
}
}
4. String 비교 함정: String Pool
String 타입은 자바의 특별한 메모리 관리(String Pool) 때문에 == 비교 시 예상치 못한 결과가 나올 수 있습니다.
public class StringComparison {
public static void main(String[] args) {
// 리터럴 방식: String Pool에서 재사용
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true (같은 Pool 객체를 참조)
System.out.println(s1.equals(s2)); // true
// new 키워드: 항상 새로운 객체 생성 (Pool 밖)
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s3 == s4); // false (서로 다른 객체)
System.out.println(s3.equals(s4)); // true (내용은 같음)
// 리터럴과 new 비교
System.out.println(s1 == s3); // false (Pool 객체 vs 힙 객체)
System.out.println(s1.equals(s3)); // true
// intern(): Pool로 등록하여 == 비교 가능하게 함 (잘 사용 안 함)
String s5 = s3.intern();
System.out.println(s1 == s5); // true (intern 후 Pool 참조)
}
}
.equals() 사용String 비교 시 == 는 사용하지 마세요. 리터럴 방식에서는 우연히 true가 나올 수 있지만, new String()이나 메서드에서 반환된 문자열은 항상 false가 됩니다.
항상 .equals() 를 사용하세요!
// 나쁜 예
if (userInput == "yes") { ... }
// 좋은 예
if ("yes".equals(userInput)) { ... } // null 안전 (좌변에 상수)
if (userInput.equals("yes")) { ... } // userInput이 null이면 NullPointerException
5. equals() 메서드 심화
모든 자바 클래스는 Object 클래스를 상속하므로 equals() 메서드를 가집니다. 기본 구현은 == 와 동일(주소 비교)하지만, String, Integer, List 등 많은 클래스가 내용 비교 로 오버라이드합니다.
public class EqualsMethod {
public static void main(String[] args) {
// String.equals(): 내용 비교 (오버라이드됨)
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1.equals(s2)); // true
// Integer.equals(): 값 비교 (오버라이드됨)
Integer i1 = new Integer(100);
Integer i2 = new Integer(100);
System.out.println(i1.equals(i2)); // true
// Integer 캐싱 주의: -128 ~ 127 범위는 캐싱됨
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true (캐싱된 객체)
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false (캐싱 범위 초과)
System.out.println(c.equals(d)); // true
// 대소문자 무시 비교
String str = "Hello";
System.out.println(str.equals("hello")); // false
System.out.println(str.equalsIgnoreCase("hello")); // true
}
}
6. Objects.equals(): null 안전 비교
String.equals() 는 좌변 객체가 null이면 NullPointerException이 발생합니다. java.util.Objects.equals() 를 사용하면 null을 안전하게 처리할 수 있습니다.
import java.util.Objects;
public class NullSafeComparison {
public static void main(String[] args) {
String name = null;
// 위험한 방법: NullPointerException 발생
// System.out.println(name.equals("Alice")); // NPE!
// 안전한 방법 1: null 체크 먼저
System.out.println(name != null && name.equals("Alice")); // false
// 안전한 방법 2: 상수를 왼쪽에 (null은 equals를 가지지 않으므로)
System.out.println("Alice".equals(name)); // false (NPE 없음)
// 안전한 방법 3: Objects.equals() (null 처리 내장)
System.out.println(Objects.equals(name, "Alice")); // false
System.out.println(Objects.equals(null, null)); // true
System.out.println(Objects.equals("Alice", null)); // false
// Objects.equals 내부 동작:
// (a == b) || (a != null && a.equals(b))
}
}
7. compareTo() 메서드
==, != 만으로는 "얼마나 큰지/작은지"를 알 수 없습니다. Comparable 인터페이스의 compareTo() 메서드는 두 객체의 순서 를 비교하여 정수를 반환합니다.
| 반환값 | 의미 |
|---|---|
| 음수(< 0) | 호출 객체가 인수보다 작다 |
| 0 | 두 객체가 같다 |
| 양수(> 0) | 호출 객체가 인수보다 크다 |
public class CompareToExample {
public static void main(String[] args) {
// String.compareTo(): 사전(알파벳) 순서 비교
String apple = "apple";
String banana = "banana";
String apple2 = "apple";
System.out.println(apple.compareTo(banana)); // 음수 (a < b 알파벳 순)
System.out.println(banana.compareTo(apple)); // 양수
System.out.println(apple.compareTo(apple2)); // 0 (같음)
// Integer.compareTo(): 숫자 크기 비교
Integer n1 = 10;
Integer n2 = 20;
System.out.println(n1.compareTo(n2)); // -1 (10 < 20)
System.out.println(n2.compareTo(n1)); // 1 (20 > 10)
System.out.println(n1.compareTo(10)); // 0 (같음)
// 정렬에 활용 (compareTo의 주된 용도)
String[] fruits = {"banana", "cherry", "apple"};
java.util.Arrays.sort(fruits); // compareTo 기반 정렬
System.out.println(java.util.Arrays.toString(fruits)); // [apple, banana, cherry]
}
}
8. NaN 비교 주의사항
float/double 의 특수값 NaN(Not a Number)은 자기 자신과도 같지 않습니다. 모든 비교 연산에서 false를 반환하는 유일한 값입니다.
public class NaNComparison {
public static void main(String[] args) {
double nan = Double.NaN;
System.out.println(nan == nan); // false (NaN은 자기 자신과도 !=)
System.out.println(nan != nan); // true
System.out.println(nan > 0); // false
System.out.println(nan < 0); // false
System.out.println(nan == 0); // false
// NaN 체크는 반드시 isNaN() 사용
System.out.println(Double.isNaN(nan)); // true
System.out.println(Float.isNaN(Float.NaN)); // true
// NaN이 발생하는 경우
double result1 = 0.0 / 0.0;
double result2 = Math.sqrt(-1);
System.out.println(Double.isNaN(result1)); // true
System.out.println(Double.isNaN(result2)); // true
}
}
nan == nan 이 false 라는 점은 자바(IEEE 754 표준)의 의도적인 설계입니다. 항상 Double.isNaN() 또는 Float.isNaN()을 사용하세요.
9. 실전 예제: 성적 등급 판별 프로그램
import java.util.Scanner;
public class GradeChecker {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("학생 이름을 입력하세요: ");
String name = scanner.nextLine();
System.out.print("점수를 입력하세요 (0~100): ");
int score = scanner.nextInt();
// 유효성 검사: 점수 범위 확인
if (score < 0 || score > 100) {
System.out.println("오류: 점수는 0~100 사이여야 합니다.");
return;
}
// 등급 판별
String grade;
String comment;
if (score >= 90) {
grade = "A";
comment = "우수";
} else if (score >= 80) {
grade = "B";
comment = "양호";
} else if (score >= 70) {
grade = "C";
comment = "보통";
} else if (score >= 60) {
grade = "D";
comment = "미흡";
} else {
grade = "F";
comment = "불합격";
}
// 합격 여부 (60점 이상)
boolean passed = score >= 60;
// 우등상 여부 (95점 이상)
boolean honor = score >= 95;
// 출력
System.out.println();
System.out.println("====== 성적 결과 ======");
System.out.println("이름 : " + name);
System.out.println("점수 : " + score + "점");
System.out.println("등급 : " + grade + " (" + comment + ")");
System.out.println("합격 : " + (passed ? "합격" : "불합격"));
if (honor) {
System.out.println("축하합니다! 우등상 수여 대상입니다.");
}
// 다른 점수와 비교 (compareTo 활용)
int passingScore = 60;
int diff = score - passingScore;
if (passed) {
System.out.println("합격 기준보다 " + diff + "점 높습니다.");
} else {
System.out.println("합격까지 " + Math.abs(diff) + "점 부족합니다.");
}
scanner.close();
}
}
예시 실행 결과 (점수 87 입력 시):
학생 이름을 입력하세요: 김철수
점수를 입력하세요 (0~100): 87
====== 성적 결과 ======
이름 : 김철수
점수 : 87점
등급 : B (양호)
합격 : 합격
합격 기준보다 27점 높습니다.
10. 핵심 정리
| 비교 대상 | 방법 | 주의사항 |
|---|---|---|
| 기본형(int, double 등) | ==, !=, >, < 직접 사용 | 없음 |
| String | .equals(), .equalsIgnoreCase() | == 사용 금지 |
| null 포함 가능 객체 | Objects.equals(a, b) | NPE 방지 |
| 순서 비교 | .compareTo() | 음수/0/양수 반환 |
| NaN | Double.isNaN(), Float.isNaN() | ==로 비교 불가 |