[Java] 자바API 클래스

JD_S·2022년 11월 26일
0

Java

목록 보기
18/21

Object 클래스

java.lang 패키지

java.lang패키지는 자바에서 가장 기본적인 동작을 수행하는 클래스들의 집합이다. 따라서 자바에서는 java.lang패키지의 클래스들은 import문을 사용하지 않아도 클래스 이름만으로 바로 사용할 수 있도록 하고 있다.

java.lang.Object 클래스

java.lang 패키지 중에서도 가장 많이 사용되는 클래스는 바로 Object클래스 이다. Object클래스는 모든 자바 클래스의 최고 조상 클래스가 된다. 따라서 자바의 모든 클래스는 Object클래스의 모든 메서드를 바로 사용할 수 있다.

이러한 Object클래스는 필드를 가지지 않으며, 총 11개의 메서드만으로 구성되어 있다.

toString() 메서드

toString()메서드는 해당 인스턴스에 대한 정보를 문자열로 반환한다. 이때 반환되는 문자열은 클래스 이름과 함께 구분자로 @가 사용되며, 그 뒤로 16진수 해시 코드가 추가된다. 16진수 해시 코드 값은 인스턴스의 주소를 가리키는 값으로, 인스턴스마다 모두 다르게 반환된다.

아래 코드는 toString()메서드를 이용하여 인스턴스의 정보를 출력하는 코드이다.

Car car01 = new Car();
Car car02 = new Car();

System.out.println(car01.toString());
System.out.println(car02.toString());

결과
Car@15db9742
Car@6d06d69c

자바에서 toString()메서드는 기본적으로 각 API 클래스마다 자체적으로 오버라이딩을 통해 재정의되어 있다.

equals() 메서드

equals()메서드는 해당 인스턴스를 매개변수로 전달받는 참조 변수와 비교하여, 그 결과를 반환한다. 이때 참조 변수가 가리키는 값을 비교하므로, 서로 다른 두 객체는 언제나 false를 반환한다.

아래 코드는 equals()메서드를 이용하여 두 인스턴스를 서로 비교하는 코드이다.

Car car01 = new Car();
Car car02 = new Car();

System.out.println(car01.equals(car02));
car01 = car02; //두 참조 변수가 같은 주소를 가리킴;
System.out.println(car01.equals(car02));

결과
false
true

자바에서 equals()메서드는 기본적으로 각 API 클래스마다 자체적으로 오버라이딩을 통해 재정의되어 있다.

clone() 메서드

clone()메서드는 해당 인스턴스를 복제하여, 새로운 인스턴스를 생성해 반환한다. 하지만 Object클래스의 clone()메서드는 단지 필드의 값만을 복사하므로, 필드의 값이 배열이나 인스턴스면 제대로 복제할 수 없다. 따라서 이러한 경우에는 해당 클래스에서 clone()메서드를 오버라이딩하여, 복제가 제대로 이루어지도록 재정의해야 한다. 이러한 clone()메서드는 데이터의 보호를 이유로 Cloneable인터페이스를 구현한 클래스의 인스턴스만이 사용할 수 있다.

아래 코드는 clone()메서드를 이용하여 인스턴스를 복제하는 코드이다.

import java.util.*;

class Car implements Cloneable {
  private String modelName;
  private ArrayList<String> owners = new ArrayList<String>(); //(1)
  
  public String getModelName() { return this.modelName; } //modelName의 값 반환
  public void setModelName(String modelName) { this.modelName = modelName; } //modelName의 값을 설정
  
  public ArrayList getOwners() { return this.owners; } //owners의 값을 반환
  public void setOwners(String ownerName) { this.owners.add(ownerName); } //owners의 값을 추가
  
  public Object clone() {
    try {
      Car clonedCar = (Car)super.clone(); //(2)
      //(3) clonedCar.owners = (ArrayList)owners.clone();
      return clonedCar;
    } catch (ClonedNotSupportedException ex) { //(4)
      ex.printStackTrace();
      return null;
    }
  }
}

public class Object03 {
  public static void main(String[] args) {
    Car car01 = new Car(); //(5)
    car01.setModelName("아반떼");
    car01.setOwners("홍길동");
    System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners() + "\n"); //(6)
    
    Car car02 = (Car)car01.clone(); //(7)
    car02.setOwners("이순신"); //(8)
    System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners()); //(9)
    System.out.println("Car02 : " + car02.getModelName() + ", " + car02.getOwners()); //(10)
  }
}

결과
Car01 : 아반떼, [홍길동]

Car02 : 아반떼, [홍길동, 이순신]
Car02 : 아반떼, [홍길동, 이순신]

위 코드의 (2)번 라인에서는 부모 클래스(Object)의 clone()메서드를 호출하여 clone()메서드를 오버라이딩하고 있다. (5)번 라인에서는 Car클래스의 인스턴스인 car01을 생성하고, (7)번 라인에서는 오버라이딩한 clone()메서드를 호출하여 복제를 수행하고 있다. 하지만 (2)번 라인처럼 clone()메서드를 재정의하면, 필드의 값이 (1)번 라인처럼 인스턴스일 때는 제대로 된 복제를 수행할 수 없다. (8)번 라인에서는 복제된 인스턴스인 car02owners필드에 새로운 값을 하나 추가한다. 하지만 (9)번 라인의 실행 결과를 보면, (7)번 라인의 결과와는 달리 원본 인스턴스인 car01owners필드에도 새로운 값이 추가되었음을 확인할 수 있다. 이처럼 단순히 부모 클래스의 clone()메서드를 호출하여 clone()메서드를 재정의하면, 배열이나 인스턴스인 필드는 복제되는 것이 아닌 해당 배열이나 인스턴스를 가리키는 주소값만이 복제되는 것이다.

따라서 정확한 복제를 위해서는 (3)번 라인처럼 배열이나 인스턴스인 필드에 대해서는 별도로 clone()메서드를 구현하여 호출해야 한다. (3)번 라인의 주석을 해제하면 다음과 같이 출력된다.

결과
Car01 : 아반떼, [홍길동]

Car02 : 아반떼, [홍길동]
Car02 : 아반떼, [홍길동, 이순신]

Object 메서드

Object클래스의 메서드는 다음과 같다.

String 클래스

java.lang.String 클래스

C언어에서는 문자열을 char형 배열로 표현하지만, 자바에서는 문자열을 위한 String이라는 클래스를 별도로 제공한다. String클래스에는 문자열과 관련된 작업을 할 때 유용하게 사용할 수 있는 다양한 메서드가 포함되어 있다. 이러한 String클래스는 java.lang패키지에 포함되어 제공된다.

String인스턴스는 한 번 생성되면 그 값을 읽기만 할 수 있고, 변경할 수는 없다. 이러한 객체를 자바에서는 불변 객체(immutable object)라고 한다. 즉, 자바에서 덧셈연산자를 이용하여 문자열 결합을 수행하면, 기존 문자열의 내용이 변경되는 것이 아니라 내용이 합쳐진 새로운 String인스턴스가 생성되는 것이다.

charAt() 메서드

charAt()메서드는 해당 문자열의 특정 인덱스에 해당하는 문자를 반환한다. 만약 해당 문자열의 길이보다 큰 인덱스나 음수를 전달하면, IndexOutOfBoundsException오류가 발생한다.

아래 코드는 문자열의 각 문자를 charAt()메서드를 이용하여 하나씩 출력하는 예제이다.

String str = new String("Java");
System.out.println("원본 문자열 : " + str);

for (int i = 0; i < str.length(); i++) {
  System.out.print(str.charAt(i) + " ");
}

System.out.println("\ncharAt() 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 : Java
J a v a
charAt() 메서드 호출 후 원본 문자열 : Java

compareTo() 메서드

compareTo()메서드는 해당 문자열을 인수로 전달된 문자열과 사전 편찬 순으로 비교한다. 이 메서드는 문자열을 비교할 때 대소문자를 구분하여 비교한다. 만약 두 문자열이 같다면 0을 반환하며, 해당 문자열이 인수로 전달된 문자열보다 작으면 음수를, 크면 양수를 반환한다. 만약 문자열을 비교할 때 대소문자를 구분하지 않기를 원한다면, compareToIgnoreCase()메서드를 사용하면 된다.

아래 코드는 compareTo()메서드와 compareToIgnoreCase()메서드를 이용하여 두 문자열을 비교하는 코드이다.

String str = new String("abcd");
System.out.println("원본 문자열 : " + str);

System.out.println(str.compareTo("bcef"));
System.out.println(str.compareTo("abcd") + "\n");

System.out.println(str.compareTo("Abcd"));
System.out.println(str.compareToIgnoreCase("Abcd"));
System.out.println("compareTo() 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 : abcd
-1
0

32
0
compareTo() 메서드 호출 후 원본 문자열 : abcd

concat() 메서드

concat()메서드는 해당 문자열의 뒤에 인수로 전달된 문자열을 추가한 새로운 문자열을 반환한다. 만약 인수로 전달된 문자열의 길이가 0이면, 해당 문자열을 그대로 반환한다.

아래 코드는 concat()메서드를 이용하여 두 문자열을 연결하는 코드이다.

String str = new String("Java");
System.out.println("원본 문자열 : " + str);

System.out.println(str.concat("수업"));
System.out.println("concat() 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 : Java
Java수업
concat() 메서드 호출 후 원본 문자열 : Java

indexOf() 메서드

indexOf()메서드는 해당 문자열에서 특정 문자나 문자열이 처음으로 등장하는 위치의 인덱스를 반환한다. 만약 해당 문자열에 전달된 문자나 문자열이 포함되어 있지 않으면 -1을 반환한다.

아래 코드는 indexOf()메서드를 이용하여 특정 문자나 문자열이 처음 등장하는 위치의 인덱스를 찾는 코드이다.

String str = new String("Oracle Java");
System.out.println("원본 문자열 : " + str);

System.out.println(str.indexOf('o'));
System.out.println(str.indexOf('a'));
System.out.println(str.indexOf("Java"));
System.out.println("indexOf() 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 : Oracle Java
-1
2
7
indexOf() 메서드 호출 후 원본 문자열 : Oracle Java

위 코드처럼 indexOf()메서드는 문자나 문자열을 찾을 때 대소문자를 구분한다.

trim() 메서드

trim()메서드는 해당 문자열의 맨 앞과 맨 뒤에 포함된 모든 공백 문자를 제거해 준다.

아래 코드는 trim()메서드를 이용하여 문자열에 포함된 띄어쓰기와 탭 문자를 제거하는 코드이다.

String str = new String(" Java       ");
System.out.println("원본 문자열 : " + str);

System.out.println(str + '|');
System.out.println(str.trim() + '|');
System.out.println("trim() 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 :  Java       
 Java       |
Java|
trim() 메서드 호출 후 원본 문자열 :  Java  

toLowerCase()와 toUpperCase() 메서드

toLowerCase()메서드는 해당 문자열의 모든 문자를 소문자로 변환시켜 준다. 또한, toUpperCase()메서드는 해당 문자열의 모든 문자를 대문자로 변환시켜 준다.

아래 코드는 toLowerCase()메서드와 toUpperCase()메서드를 이용하여 문자열의 대소문자를 변경하는 코드이다.

String str = new String("Java");
System.out.println("원본 문자열 : " + str);

System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());
System.out.println("두 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 : Java
java
JAVA
두 메서드 호출 후 원본 문자열 : Java

대표적인 String 메서드

String클래스의 메서드는 매우 다양하며, 그중에서 많이 사용되는 메서드는 다음과 같다.

StringBuffer 클래스

java.lang.StringBuffer 클래스

String클래스의 인스턴스는 한 번 생성되면 그 값을 읽기만 할 수 있고, 변경할 수는 없다. 하지만 StringBuffer클래스의 인스턴스는 그 값을 변경할 수도 있고, 추가할 수도 있다.

이를 위해 StringBuffer클래스는 내부적으로 버퍼라고 하는 독립적인 공간을 가진다. 버퍼의 크기는 기본값은 16개의 문자를 저장할 수 있는 크기이며, 생성자를 통해 그 크기를 별도로 설정할 수도 있다. 하지만 인스턴스 생성 시 사용자가 설정한 크기보다 언제나 16개의 문자를 더 저장할 수 있도록 여유 있는 크기로 생성된다. 덧셈 연산자를 이용해 String인스턴스의 문자열을 결합하면, 내용이 합쳐진 새로운 String인스턴스를 생성한다. 따라서 문자열을 많이 결합하면 결합할수록 공간의 낭비뿐만 아니라 속도 또한 매우 느려지게 된다.

하지만 StringBuffer인스턴스를 사용하면 문자열을 바로 추가할 수 있으므로, 공간의 낭비도 없으며 속도도 매우 빨라진다. 이러한 StringBuffer클래스는 java.lang패키지에 포함되어 제공된다.

불변 클래스와 가변 클래스

String클래스와 같이 인스턴스가 한 번 생성되면 그 값을 변경할 수 없는 클래스를 불변 클래스(immutable class)라고 한다. 반대로 StringBuffer클래스와 같이 자유롭게 인스턴스의 값을 변경할 수 있는 클래스를 가변 클래스(mutable class)라고 한다. String클래스와 같은 불변 클래스는 StringBuffer클래스의 append()insert()메서드와 같이 값을 변경하는 set메서드를 포함하지 않는다.

이렇게 불편하기만 할 것 같은 불변 클래스를 사용하는 이유는 멀티 쓰레드 환경에서 객체가 변환되는 상황이라면 불변 인스턴스를 사용하는 것이 좀 더 신뢰할 수 있는 코드를 작성할 수 있기 때문이다. 즉, 하나의 객체에 접근하면서 각각의 객체가 서로 영향을 주어서는 안 되는 경우에 불변 인스턴스를 사용하면 값이 변하지 않는다는 점이 보장된다.

append() 메서드

append()메서드는 인수로 전달된 값을 문자열로 변환한 후, 해당 문자열의 마지막에 추가한다. 이 메서드는 String클래스의 concat()메서드와 같은 결과를 반환하지만, 내부적인 처리 속도가 훨씬 빠르다.

아래 코드는 append()메서드를 이용하여 한 문자열에 다른 문자열을 추가하는 코드입니다.

StringBuffer str = new StringBuffer("Java");
System.out.println("원본 문자열 : " + str);

System.out.println(str.append("수업"));
System.out.println("append() 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 : Java
Java수업
append() 메서드 호출 후 원본 문자열 : Java수업

capacity() 메서드

capacity()메서드는 StringBuffer인스턴스의 현재 버퍼 크기를 반환한다.

아래 코드는 capacity()메서드를 이용하여 StringBuffer인스턴스의 현재 버퍼 크기를 알아보는 코드이다.

StringBuffer str01 = new StringBuffer();
StringBuffer str02 = new StringBuffer("Java");

System.out.println(str01.capacity());
System.out.println(str02.capacity());

결과
16
20

위 코드처럼 길이가 4인 문자열로 StringBuffer인스턴스를 생성하면, 기본적으로 생성되는 여유 버퍼 크기인 16에 문자의 길이인 4를 더한 총 20개의 문자를 저장할 수 있는 버퍼가 생성되는 것을 확인할 수 있다.

delete() 메서드

delete()메서드는 전달된 인덱스에 해당하는 부분 문자열을 해당 문자열에서 제거한다. 또한 deleteCharAt()메서드를 사용하면 특정 위치와 문자 한 개만을 제거할 수도 있다.

아래 코드는 delete()메서드를 이용하여 해당 문자열의 특정 부분을 제거하는 코드이다.

StringBuffer str = new StringBuffer("Java Oracle");
System.out.println("원본 문자열 : " + str);

System.out.println(str.delete(4, 8)); //(1)
System.out.println(str.deleteCharAt(1));
System.out.println("deleteCharAt() 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 : Java Oracle
Javacle
Jvacle
deleteCharAt() 메서드 호출 후 원본 문자열 : Jvacle

위 코드 (1)번 라인에서는 delete()메서드를 사용하여 해당 문자열에서 인덱스가 4인 위치의 문자부터 7인 위치의 문자까지를 삭제하고 있다. 이처럼 delete()메서드는 첫 번째 매개변수로 전달된 인덱스부터 두 번째 매개변수로 전달된 인덱스 바로 앞의 문자까지를 삭제하는 메서드이다.

insert() 메서드

insert()메서드는 인수로 전달된 값을 문자열로 변환한 후, 해당 문자열의 지정된 인덱스 위치에 추가한다. 이때 전달된 인덱스가 해당 문자열의 길이와 같으면, append()메서드와 같은 결과를 반환한다.

아래 코드는 insert()메서드를 이용하여 한 문자열 중간에 다른 문자열을 삽입하는 코드이다.

StringBuffer str = new StringBuffer("Java 만세!!");
System.out.println("원본 문자열 : " + str);

System.out.println(str.insert(4, "Script")); //(1)
System.out.println("insert() 메서드 호출 후 원본 문자열 : " + str);

결과
원본 문자열 : Java 만세!!
JavaScript 만세!!
insert() 메서드 호출 후 원본 문자열 : JavaScript 만세!!

위 코드 (1)번 라인에서 insert()메서드를 사용하여 해당 문자열에서 인덱스가 4인 위치부터 두 번째 매개변수로 전달된 문자열을 추가하고 있다.

대표적인 StringBuffer 메서드

StringBuffer클래스의 메서드는 매우 다양하며, 그중에서 많이 사용되는 메서드는 다음과 같다.

Math 클래스

java.lang.Math 클래스

Math클래스는 수학에서 자주 사용하는 상수들과 함수들을 미리 구현해 놓은 클래스이다. Math클래스의 모든 메서드는 클래스 메서드이므로, 객체를 생성하지 않고도 바로 사용할 수 있다. 이러한 Math클래스는 java.lang패키지에 포함되어 제공된다.

Math.E와 Math.PI

Math클래스에 정의되어 있는 클래스 필드는 다음과 같다.

  • Math.E : 오일러의 수라 불리며, 자연로그의 밑 값으로 약 2.718을 의미한다.
  • Math.PI : 원의 원주를 지름으로 나눈 비율(원주율) 값으로 약 3.14159를 의미한다.

random() 메서드

random()메서드는 0.0 이상 1.0 미만의 범위에서 임의의 double형 값을 하나 생성하여 반환한다. 이 메서드는 내부적으로 java.util패키지의 Random()클래스를 사용한 의사 난수 발생기(pseudorandom-number generator)를 사용하여 임의의 수를 생성한다.

아래 코드는 Math클래스의 random()메서드를 이용하여 0부터 99까지의 난수를 생성하는 코드이다.

System.out.println((int)(Math.random() * 100)); //0~99

Random ran = new Random();
System.out.println(ran.nextInt(100)); //0~99

자바에서는 Math클래스의 random()메서드 뿐만 아니라 java.util패키지에 포함된 Random클래스의 nextInt()메서드를 사용해도 난수를 생성할 수 있다.

만약 특정 범위에 속하는 난수를 생성하려면, 다음과 같이 난수 생성 범위를 조절할 수 있다.

(int)(Math.random() * 6); //0~5
((int)(Math.random() * 6) + 1); //1~6
((int)(Math.random() * 6) + 3); //3~8

abs() 메서드

abs()메서드는 전달된 값이 음수이면 그 값의 절댓값을 반환하며, 전달된 값이 양수이면 전달된 값을 그대로 반환한다.

System.out.println(Math.abs(10)); //10
System.out.println(Math.abs(-10)); //10
System.out.println(Math.abs(-3.14)); //3.14

결과
10
10
3.14

floor() 메서드, ceil() 메서드와 round() 메서드

floor()메서드는 인수로 전달받은 값과 같거나 작은 수 중에서 가장 큰 정수를 반환한다. 또한, ceil()메서드는 반대로 인수로 전달받은 값과 같거나 큰 수 중에서 가장 작은 정수를 반환한다. round()메서드는 전달받은 실수를 소수점 첫째 자리에서 반올림한 정수를 반환한다.

System.out.println(Math.ceil(10.0));      // 10.0
System.out.println(Math.ceil(10.1));      // 11.0
System.out.println(Math.ceil(10.000001)); // 11.0

System.out.println(Math.floor(10.0));     // 10.0
System.out.println(Math.floor(10.9));     // 10.0
 
System.out.println(Math.round(10.0));     // 10
System.out.println(Math.round(10.4));     // 10
System.out.println(Math.round(10.5));     // 11

결과
10.0
11.0
11.0
10.0
10.0
10
10
11

max() 메서드와 min() 메서드

max()메서드는 전달된 두 값을 비교하여 그중에서 큰 값을 반환하며, min()메서드는 그중에서 작은 값을 반환한다.

System.out.println(Math.max(3.14, 3.14159)); // 3.14159
System.out.println(Math.min(3.14, 3.14159)); // 3.14

System.out.println(Math.max(-10, -11));      // -10
System.out.println(Math.min(-10, -11));      // -11

결과
3.14159
3.14
-10
-11

pow() 메서드와 sqrt() 메서드

pow()메서드는 전달된 두 개의 double형 값을 가지고 제곱 연산을 수행한다.

예를 들어, pow(a, b)는 a의 b승, 즉 aba^b를 반환하게 된다. 반대로 sqrt()메서드는 전달된 double형 값의 제곱근 값을 반환한다.

System.out.println((int)Math.pow(5, 2)); //25
System.out.println((int)Math.sqrt(25)); //5

결과
25
5

sin() 메서드, cos() 메서드와 tan() 메서드

자바에서는 삼각 함수와 관련된 다양한 연산을 간편하게 수행할 수 있도록 많은 삼각 함수를 제공하고 있다. sin()메서드는 전달된 double형 값의 사인값을, cos()메서드는 코사인값을, tan()메서드는 탄제트값을 반환한다.

이 외에도 Math클래스에서 제공하는 삼각 함수와 관련된 메소드는 다음과 같습니다.

  • asin(), acos(), atan(), atan2(), sinh(), cosh(), tanh()

아래 코드는 기본적인 삼각 함수숫값을 자바의 삼각 함수 메서드로 확인하는 코드이다.

System.out.println(Math.sin(Math.toRadians(30)));
System.out.println(Math.sin(Math.PI / 6));

System.out.println(Math.tan(Math.toRadians(45)));
System.out.println(Math.tan(Math.PI / 4));

System.out.println(Math.tan(Math.toRadians(60)));
System.out.println(Math.tan(Math.PI / 3));

결과
0.49999999999999994
0.49999999999999994
0.9999999999999999
0.9999999999999999
0.5000000000000001
0.5000000000000001

위 코드처럼 자바의 삼각 함수에 관한 메서드는 정확한 값을 나타내지 못한다. 그 이유는 컴퓨터가 실수를 나타내는 데 사용하는 부동 소수점 방식의 한계로 모든 언어에서 공통으로 발생하는 문제이다.

대표적인 Math 메서드

Math 클래스의 메서드는 매우 다양하며, 그중에서 많이 사용되는 메서드는 다음과 같다.

Wrapper 클래스

래퍼 클래스(Wrapper class)

프로그램에 따라 기본 타입의 데이터를 객체로 취급해야 하는 경우가 있다. 예를 들어, 메서드의 인수로 객체 타입만이 요구되면, 기본 타입의 데이터를 그대로 사용할 수는 없다. 이때에는 기본 타입의 데이터를 먼저 객체로 변환한 후 작업을 수행해야 한다.

이렇게 8개의 기본 타입에 해당하는 데이터를 객체로 포장해 주는 클래스를 래퍼 클래스라고 한다. 래퍼 클래스는 각각의 타입에 해당하는 데이터를 인수로 전달받아, 해당 값을 가지는 객체로 만들어 준다. 이러한 래퍼 클래스는 모두 java.lang패키지에 포함되어 제공된다.

자바의 기본 타입에 대응하여 제공하고 있는 래퍼 클래스는 다음과 같다.

래퍼 클래스 중에서 Integer클래스와 Character클래스만이 자신의 기본 타입과 이름이 다름을 주의해야 한다.

박싱(Boxing)과 언박싱(UnBoxing)

래퍼 클래스는 산술 연산을 위해 정의된 클래스가 아니므로, 인스턴스에 저장된 값을 변경할 수 없다. 단지, 값을 참조하기 위해 새로운 인스턴스를 생성하고, 생성된 인스턴스의 값만을 참조할 수 있다.

위의 그림과 같이 기본 타입의 데이터를 래퍼 클래스의 인스턴스로 변환하는 과정을 박싱이라고 한다. 반면 래퍼 클래스의 인스턴스에 저장된 값을 다시 기본 타입의 데이터로 꺼내는 과정을 언박싱이라고 한다.

오토 박싱(AutoBoxing)과 오토 언박싱(AutoUnBoxing)

JDK 1.5부터는 박싱과 언박싱이 필요한 상황에서 자바 컴파일러가 이를 자동으로 처리해 준다. 이렇게 자동화된 박싱과 언박싱을 오토 박싱과 오토 언박싱이라고 부른다.

아래 코드는 박싱과 언박싱, 오토 박싱과 오토 언박싱의 차이를 보여주는 코드이다.

Integer num = new Integer(17); //박싱
int n = num.intValue(); //언박싱
System.out.println(n);

Character ch = 'X'; //Character ch = new Character('X'); : 오토박싱
char c = ch; //char c = ch.charValue(); : 오토언박싱
System.out.println(c);

결과
17
X

위 코드에서 볼 수 있듯이 래퍼 클래스인 Integer클래스와 Character클래스에는 각각 언박싱을 위한 intValue()메서드와 charValue()메서드가 포함되어 있다.

또한, 오토 박싱을 이용하면 new키워드를 사용하지 않고도 자동으로 Character인스턴스를 생성할 수 있다. 반대로 charValue()메서드를 사용하지 않고도, 오토 언박싱을 이용하여 인스턴스에 저장된 값을 바로 참조할 수 있다.

따라서 아래 코드처럼 오토 박싱과 오토 언박싱을 통해 기본 타입과 래퍼 클래스 간의 다양한 연산도 가능해진다.

public class Wrapper02 {
  public static void main(String[] args) {
    Integer num1 = new Integer(7); //박싱
    Integer num2 = new Integer(3); //박싱
    
    int int1 = num1.intValue(); //언박싱
    int int2 = num2.intValue(); //언박싱
    
    Integer result1 = num1 + num2; //(1) 10
    Integer result2 = int1 - int2; //(2) 4
    int result3 = num1 * int2; //(3) 21
    
    System.out.println(result1);
	System.out.println(result2);
	System.out.println(result3);
  }
}

결과
10
4
21

위 코드의 (1) ~ (3)까지의 연산은 내부적으로 래퍼 클래스인 피연산자를 오토언박싱하여 기본 타입끼리의 연산을 수행하고 있는 것이다.

public class Wrapper03 {
  public static void main(String[] args) {
    Integer num1 = new Integer(10);
    Integer num2 = new Integer(20);
    Integer num3 = new Integer(10);
    
    System.out.println(num1 < num2); //true
    System.out.println(num1 == num3); //(1) false
    System.out.println(num1.equals(num3)); //(2) true
  }
}

결과
true
false
true

래퍼 클래스의 비교 연산도 오토언박싱을 통해 가능해지지만, 인스턴스에 저장된 값의 동등 여부 판단은 (1)번처럼 비교 연산자인 동등 연산자를 사용해서는 안 되며, (2)번처럼 equals()메서드를 사용해야만 한다. 래퍼 클래스도 객체이므로 동등 연산자를 사용하게 되면, 두 인스턴스의 값을 비교하는 것이 아니라 두 인스턴스의 주소값을 비교하게 된다.

따라서 서로 다른 두 인스턴스를 동등 연산자로 비교하게 되면, 언제나 false값을 반환하게 된다. 그러므로 인스턴스에 저장된 값의 동등 여부를 정확히 판단하려면 equals()메서드를 사용해야만 한다.

Enum 클래스

열거체(enumeration type)

C언어와 C++에서는 열거체를 사용할 수 있지만, JDK 1.5이전의 자바에서는 열거체를 사용할 수 없었다.

이와 같은 자바의 열거체는 다음과 같은 장점을 가진다.

  • 열거체를 비교할 때 실제 값뿐만 아니라 타입까지도 체크한다.
  • 열거체의 상숫값이 재정의되더라도 다시 컴파일할 필요가 없다.

열거체의 정의 및 사용

자바에서는 enum키워드를 사용하여 열거체를 정의할 수 있다.

문법
enum 열거체이름 { 상수1이름, 상수2이름, ... }

예제
enum Rainbow { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET }

이렇게 정의된 열거체를 사용하는 방법은 다음과 같다.

문법
열거체이름.상수이름

예제
Rainbow.RED

열거체의 상숫값 정의 및 추가

위와 같이 정의된 열거체의 첫 번째 상숫값은 0부터 설정되며, 그다음은 바로 앞의 상숫값보다 1만큼 증가되며 설정된다. 또한, 불규칙한 값을 상숫값으로 설정하고 싶으면 상수의 이름 옆에 괄호(())를 추가하고, 그 안에 원하는 상숫값을 명시할 수 있다. 하지만 이때에는 불규칙한 특정 값을 저장할 수 있는 인스턴스 변수와 생성자를 아래 코드와 같이 별도로 추가해야만 한다.

enum Rainbow {
  RED(3), ORANGE(10), YELLOW(21), GREEN(5), BLUE(1), INDIGO(-1), VIOLET(-11);
  
  private final int value;
  Rainbow(int value) { this.value = value; }
  public int getValue() { return value; }
}

java.lang.Enum 클래스

Enum클래스는 모든 자바 열거체의 공통된 조상 클래스이다. Enum클래스에는 열거체를 조작하기 위한 다양한 메서드가 포함되어 있다.

values() 메서드

values()메서드는 해당 열거체의 모든 상수를 저장한 배열을 생성하여 반환하다. 이 메서드는 자바의 모든 열거체에 컴파일러가 자동으로 추가해 주는 메서드이다.

enum Rainbow { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET }

public class Enum01 {
  public static void main(String[] args) {
    Rainbow[] arr = Rainbow.values();
    for (Rainbow rb : arr) {
      System.out.println(rb);
    }
  }
}

결과
RED
ORANGE
YELLOW
GREEN
BLUE
INDIGO
VIOLET

valueOf() 메서드

valueOf()메서드는 전달된 문자열과 일치하는 해당 열거체의 상수를 반환한다.

enum Rainbow { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET }

public class Enum02 {
  public static void main(String[] args) {
    Rainbow rb = Rainbow.valueOf("GREEN");
    System.out.println(rb);
  }
}

결과
GREEN

ordinal() 메서드

ordinal()메서드는 해당 열거체 상수가 열거체 정의에서 정의된 순서(0부터 시작)를 반환한다. 이때 반환되는 값은 열거체 정의에서 해당 열거체 상수가 정의된 순서이며, 상숫값 자체가 아님을 명심해야 한다.

enum Rainbow { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET }

public class Enum03 {
  public static void main(String[] args) {
    int idx = Rainbow.YELLOW.ordinal();
    System.out.println(idx);
  }
}

결과
2

아래 코드는 불규칙적인 상숫값을 가지는 열거체에서 ordinal()메서드를 사용한 코드이다.

enum Rainbow {
  RED(3), ORANGE(10), YELLOW(21), GREEN(5), BLUE(1), INDIGO(-1), VIOLET(-11);
  
  private final int value;
  Rainbow(int value) { this.value = value; }
  public int getValue() { return value; }
}

public class Enum04 {
  public static void main(String[] args) {
    System.out.println(Rainbow.YELLOW.ordinal());
  }
}

결과
2

대표적인 Enum 메서드

Enum클래스의 메서드는 다음과 같다.

Arrays 클래스

java.util 패키지

java.util패키지에는 프로그램을 개발하는 데 사용할 수 있는 유용한 유틸리티 클래스가 다수 포함되어 있다. 실제로 java.lang패키지 다음으로 가장 많이 사용되는 패키지가 java.util패키지이다. 하지만 import문을 사용하지 않아도 바로 사용할 수 있는 java.lang패키지와는 달리 java.util패키지는 import문으로 패키지를 불러오고 나서야 클래스 이름만으로 사용할 수 있다.

java.util.Arrays 클래스

Arrays클래스에는 배열을 다루기 위한 다양한 메서드가 포함되어 있다. Arrays클래스의 모든 메서드는 클래스 메서드이므로, 객체를 생성하지 않고도 바로 사용할 수 있다. 이 클래스는 java.util패키지에 포함되므로, 반드시 import문으로 java.util패키지를 불러오고 나서 사용해야 한다.

binarySearch() 메서드

binarySearch()메서드는 전달받은 배열에서 특정 객체의 위치를 이진 검색 알고리즘을 사용하여 검색한 후, 해당 위치를 반환한다. 이 메서드는 이진 검색 알고리즘을 사용하므로, 매개변수로 전달되는 배열이 sort()메서드 등을 사용하여 미리 정렬되어 있어야만 제대로 동작한다.

int[] arr = new int[1000];

for(int i = 0; i < arr.length; i++) {
  arr[i] = i;
}

System.out.println(Arrays.binarySearch(arr, 437));

결과
437

copyOf() 메서드

copyOf()메소드는 전달받은 배열의 특정 길이만큼을 새로운 배열로 복사하여 반환한다. copyOf()메소드는 첫 번째 매개변수로 원본 배열을 전달받고, 두 번째 매개변수로 원본 배열에서 새로운 배열로 복사할 요소의 개수를 전달받는다. 그리고 원본 배열과 같은 타입의 복사된 새로운 배열을 반환합니다.

이때 새로운 배열의 길이가 원본 배열보다 길면, 나머지 요소는 배열 요소의 타입에 맞게 다음과 같은 기본값으로 채워집니다.

int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = Arrays.copyOf(arr1, 3); //(1)

for (int i = 0; i < arr2.length; i++) {
  System.out.print(arr2[i] + " ");
}

System.out.print("\n");

int[] arr3 = Arrays.copyOf(arr1, 10); //(2)
for (int i = 0; i < arr3.length; i++) {
  System.out.print(arr3[i] + " ");
}

결과
1 2 3 
1 2 3 4 5 0 0 0 0 0 

위 예제의 (1)번에서는 copyOf() 메소드를 사용하여 배열 arr1의 첫 번째 배열 요소부터 3개의 요소를 복사하여 배열 arr2에 대입하고 있다.

(2)번에서는 배열 arr1에서 10개의 배열 요소를 복사하여 배열 arr3에 대입하려고 한다. 하지만 배열 arr1의 길이가 5밖에 안되므로, 배열 arr3의 나머지 배열 요소에는 int형의 기본값인 0이 채워지게 된다.

copyOfRange() 메서드

copyOfRange()메서드는 전달받은 배열의 특정 범위에 해당하는 요소만을 새로운 배열로 복사하여 반환한다. copyOfRange()메서드는 첫 번째 매개변수로 복사의 대상이 될 원본 배열을 전달받는다. 두 번째 매개변수로는 원본 배열에서 복사할 시작 인덱스를 전달받고, 세 번째 매개변수로는 마지막으로 복사될 배열 요소의 바로 다음 인덱스를 전달받는다.

즉, 세 번째 매개변수로 전달된 인덱스 바로 전까지의 배열 요소까지만 복사된다. 그리고 원본 배열과 같은 타입의 복사된 새로운 배열을 반환한다.

int[] arr1 = {1, 2, 3, 4, 5};

int[] arr2 = Arrays.copyOfRange(arr1, 2, 4);
for (int i = 0; i < arr2.length; i++) {
  System.out.print(arr2[i] + " ");
}

결과
3 4

fill() 메서드

fill()메서드는 전달받은 배열의 모든 요소를 특정 값으로 초기화해 준다. fill()메서드는 첫 번째 매개변수로 초기화할 배열을 전달받고, 두 번째 매개변수로 초기값을 전달받는다. 따라서 이 메서드는 전달받은 원본 배열의 값을 변경하게 된다.

int[] arr = new int[10];

Arrays.fill(arr, 7);

for (int i = 0; i < arr.length; i++) {
  System.out.print(arr[i] + " ");
}

sort() 메서드

sort()메서드는 전달받은 배열의 모든 요소를 오름차순으로 정렬한다. sort()메서드는 매개변수로 정렬할 배열을 전달받으며, 따라서 이 메서드는 전달받은 원본 배열의 순서를 변경하게 된다.

int[] arr = {5, 3, 4, 1, 2};

Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
  System.out.print(arr[i] + " ");
}

대표적인 Arrays 메서드

Arrays클래스의 메서드는 매우 다양하며, 그중에서 많이 사용되는 메서드는 다음과 같다.

Calendar 클래스

자바에서의 날짜 및 시간 처리

JDK1.0에서는 Date클래스를 사용하여 날짜에 관한 처리를 수행했다. 하지만 Date클래스는 현재 대부분의 메서드가 사용을 권장하지 않고 있다.(deprecated)

JDK1.1부터 새롭게 제공된 Calendar클래스는 날짜와 시간에 대한 정보를 손쉽게 얻을 수 있다. 하지만 Calendar클래스는 다음과 같은 문제점을 가지고 있다.

  1. Calendar인스턴스는 불변 객체(immutable object)가 아니라서 값이 수정될 수 있다.
  2. 윤초(leap second)와 같은 특별한 상황은 고려하지 않는다.
  3. Calendar클래스에서는 월을 나타낼 때 1월부터 12월을 0부터 11까지 표현해야 하는 불편함이 있다.

윤초 : 협정 세계시에서 기준으로 삼고 있는 세슘 원자시계와 실제 지구의 자전, 공전 속도를 기준으로 한 태양시의 차이로 인해 발생한 오차를 보정하기 위하여 추가하는 1초이다. 12월 31일의 마지막에 추가하거나, 혹은 6월 30일의 마지막에 추가한다.

따라서 많은 자바 개발자들은 Calendar클래스뿐만 아니라 더 나은 성능의 Joda-Time이라는 라이브러리를 함께 사용해 왔다. 버전에서는 이러한 Joda-Time라이브러리를 발전시킨 새로운 날짜와 시간API인 java.time패키지를 제공한다. java.time패키지는 위와 같은 문제점을 모두 해결했으며, 다양한 기능을 지원하는 다수의 하위 패키지를 포함하고 있다.

java.util.Calendar 클래스

Calendar클래스는 자바에서 날짜와 시간에 관한 데이터를 손쉽게 처리할 수 있도록 제공하는 추상 클래스이다. 이 클래스가 추상 클래스로 선언된 이유는 나라마다 사용하는 달력 체계가 조금씩 다를 수 있기 때문이다. 이러한 Calendar클래스에는 날짜와 시간을 처리하기 위한 다양한 필드와 메서드가 포함되어 있다. Calendar클래스의 모든 필드는 클래스 변수이므로, 객체를 생성하지 않고도 바로 사용할 수 있다.

java.util.GregorianCalendar 클래스

현재 전 세계적으로 가장 많이 사용되는 달력은 1582년 교황 그레고리오 13세가 개혁한 그레고리오 달력이다. Calendar클래슨느 추상 클래스이므로, 직접 인스턴스를 생성할 수 없다. GregorianCalendar클래스는 이러한 Calendar클래스를 상속받아 그레고리오 달력을 완전히 구현한 하위 클래스이다.

add() 메서드

add()메서드는 전달된 Calendar필드에서 일정 시간 만큼을 더하거나 빼준다. 즉, 특정 시간을 기준으로 일정 시간 전후의 날짜와 시간을 알 수 있다.

아래 코드는 add()메서드를 이용하여 현재 시각에 120초를 더한 코드이다.

Calendar time = Calendar.getInstance();
System.out.println(time.getTime());

time.add(Calendar.SECOND, 120);
System.out.println(time.getTime());

결과
Sun Nov 27 11:36:08 KST 2022
Sun Nov 27 11:38:08 KST 2022

before()와 after() 메서드

두 시간상의 전후 관계만을 알고 싶을 경우에는 before()after()메서드를 사용할 수 있다. before()메서드는 현재 Calendar인스턴스가 전달된 객체가 나타내는 시간보다 앞서는지를 판단한다. 반대로 after()메서드는 현재 Calendar인스턴스가 전달된 객체가 나타내는 시간보다 나중인지를 판단한다.

Calendar time1 = Calendar.getInstance();
Calendar time2 = Calendar.getInstance();
Calendar time3 = Calendar.getInstance();

time2.set(1982, 2, 19);
time3.set(2023, 2, 19);

System.out.println(time1.before(time2));
System.out.println(time1.before(time3));

결과
false
true

get() 메서드

get()메서드는 전달된 Calendar필드에 저장된 값을 반환한다.

Calendar time = Calendar.getInstance();

System.out.println(time.getTime());
System.out.println(time.get(Calendar.DAY_OF_WEEK));
System.out.println(time.get(Calendar.MONTH) + 1);
System.out.println(time.get(Calendar.DAY_OF_MONTH));
System.out.println(time.get(Calendar.HOUR_OF_DAY));
System.out.println(time.get(Calendar.MINUTE));
System.out.println(time.get(Calendar.SECOND));
System.out.println(time.get(Calendar.YEAR));

결과
Sun Nov 27 11:50:00 KST 2022
1
11
27
11
50
0
2022

roll() 메서드

roll()메서드는 전달된 Calendar필드에서 일정 시간 만큼을 더하거나 빼준다. 하지만 add()메서드와는 달리 다른 Calendar필드에는 영향을 주지 않는다.

즉, 계산 결과가 해당 필드의 최댓값이나 최솟값을 넘어가도 다른 필드에 영향을 주지 않는다.

Calendar time1 = Calendar.getInstance();
Calendar time2 = Calendar.getInstance();
System.out.println(time1.getTime());

time1.add(Calendar.SECOND, 60);
System.out.println(time1.getTime());

time2.roll(Calendar.SECOND, 60);
System.out.println(time2.getTime());

결과
Sun Nov 27 11:56:23 KST 2022
Sun Nov 27 11:57:23 KST 2022
Sun Nov 27 11:56:23 KST 2022

위 코드에서 add()메서드를 사용하여 Calendar.SECOND필드를 60초 증가시키면 결과적으로 Calendar.MINUTE필드가 1 증가해야 한다. 하지만 roll()메서드를 사용하여 Calendar.SECOND필드를 60초 증가시키면 Calendar.MINUTE필드에는 아무런 영향을 주지 않는다.

즉, Calendar.SECOND필드만이 60초 증가하여 결과적으로 같은 값이 출력되게 된다.

set() 메서드

set()메서드는 전달된 Calendar필드를 특정 값으로 설정한다.

아래 코드는 set()메서드에 다양한 형태의 인수를 전달하여 시간을 설정하는 코드이다.

Calendar time = Calendar.getInstance();
System.out.println(time.getTime());

time.set(Calendar.YEAR, 2020);
System.out.println(time.getTime());

time.set(1982, 1, 19); //1은 2월을 나타냄
System.out.println(time.getTime());

time.set(1982, 1, 19, 12, 34, 56);
System.out.println(time.getTime());

결과
Sun Nov 27 12:04:11 KST 2022
Fri Nov 27 12:04:11 KST 2020
Fri Feb 19 12:04:11 KST 1982
Fri Feb 19 12:34:56 KST 1982

대표적인 Calendar 메서드

Calendar클래스의 메서드는 매우 다양하며, 그중에서 많이 사용되는 메서드는 다음과 같다.

Reference

profile
Whatever does not destroy me makes me stronger.

0개의 댓글