재밌고 어려운 IT를 이해해보자~!

Section 15. 기본 API 클래스 본문

JAVA Book Study

Section 15. 기본 API 클래스

언제나즐거운IT 2024. 1. 1. 04:24

01. 자바 API (Application Programming Interface) 문서

자바에서 제공하는 API는 프로그램 개발에 자주 사용되는 클래스 및 인터페이스의 모음을 말하며 라이브러리 라고도 부른다.

'java api doc' 겁색시 확인가능.

02. java.lang 패키지

System.String 등과 같은 클래스는 따로 선언 없이 사용이 가능했다. 그이유는 기본으로 제공해 주는 java.lang 패키지에 속해 있었기 때문.

클래스 용도
Object 자바 클래스의 최상위 클래스로 사용
System 시스템의 표준 입력/출력 장치로부터 데이터를 입력,출력하기 위해, 자바 가상기계를 종료하기 위해
String 문자열을 저장하고 여러 가지 정보를 얻을 떄
StringBuffer / StringBuilder 문자열을 저장하고 내부 문자열을 조작할 때
Math 수학 함수를 이용할때 

Object 클래스

Object 클래스는 자바의 최상위 클래스이다. 자바에서 생성되는 모든 클래스는 생성될 때 Object를 자동 상속 받는다.

따라서 Object의 메서드를 그대로 사용하거나 override해서 사용할수 있다.

메서드 설명
protected Object clone() 객체 자신을 복사한 후 반환
boolean equlas(Object obj)  다른 객체와 자신이 가진 실제 값을 비교
int hashCode() 객체와 hashCode 값을 반환
String toString() 객체 자신의 정보를 반환

객체비교 equlas( ) 메서드

참조형 데이터의 비교는 객체의 값 비교가 아니라, 객체의 메모리 위치를 비교한다.

Object의 equals()를 override해서 사용한다. 대표적으로 String 클래스를 비교할때 즉, 문자열이 같은지 비교할때 equals를 사용한다.

객체 해시코드 hashCode( ) 메서드

주민등록번호처럼 객체를 식별하는 하나의 정수값을 'hashCode'라고 부른다. hashCode() 메서드는 그것을 반환하는 메서드이다. HashMap, HashSet에서 객체형 데이터를 저장할 때 equals() 메서드와 함께 동일한 객체인지 판별하는 데 사용된다.

 

 

equals() 는 주소값(위치값)을 비교하는 것 같고 hashCode()는 고유한 값을 비교하는 것 같다....

다음 예시를 보자.

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

public class Example {
    public static void main(String[] args) {
        Person person1 = new Person("홍길동");
        Person person2 = new Person("홍길동");

        System.out.println(person1 == person2); // == 은 객체타입인경우 주소값을 비교한다. 서로다른 객체는 다른 주소를 가지고 있기 때문에 false가 출력됨

        System.out.println(person1.equals(person2)) // equals또한 객체타입인경우 주소값을 비교하기 때문에 false가 출력된다.
    }
}

 

equals 오버라이딩
하지만 잘 생각해보자. 위의 person1 변수와 person2 변수는 각기 다른 객체를 초기화하여 힙 영역에 따로 저장해두고 있으니 두 객체를 비교하면 주소가 일치하지 않아 당연히 false가 뜬다.
 
하지만 이는 컴퓨터적인 관점에서 바라본 입장이다.
외부적인 관점에서는 두 객체는 똑같은 Person 클래스 타입이고 똑같은 이름 값 "홍길동"을 지니고 있다.
즉, 사용 입장에서는 두 객체는 어찌보면 같은 데이터다 라고 볼 수도 있는 것이다. 물론 프로그램적인 입장에서는 둘은 다르다는 것이 옳지만, 데이터적인 입장에서는 둘은 어찌보면 같다고 봐야 할지도 모른다.
 
따라서 만일 객체 자료형을 비교를 할때, 주소 값이 아닌 객체의 필드값을 기준으로 동등 비교 기준을 변경하고 싶다면, equals 메서드를 오버라이딩해서 주소가 아닌 필드값을 비교하도록 재정의 해주면 된다.

import java.util.Objects;

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    // 객체 주소 비교가 아닌 Person 객체의 사람 이름이 동등한지 비교로 재정의 하기 위해 오버라이딩
    public boolean equals(Object o) {
        if (this == o) return true; // 만일 현 객체 this와 매개변수 객체가 같을 경우 true
        if (!(o instanceof Person)) return false; // 만일 매개변수 객체가 Person 타입과 호환되지 않으면 false
        Person person = (Person) o; // 만일 매개변수 객체가 Person 타입과 호환된다면 다운캐스팅(down casting) 진행
        return Objects.equals(this.name, person.name); // this객체 이름과 매개변수 객체 이름이 같을경우 true, 다를 경우 false
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("홍길동");
        Person p2 = new Person("홍길동"); // 동명이인

        System.out.println(p1.equals(p2)); // true
    }
}

equals만 재정의할 경우

우선 예제로 사용될 Person 클래스를 살펴보자.

Person 클래스에는 equals 메서드만 오버라이딩 하였다. 따라서 p1 과  p2 객체는 해시코드가 다름에도 불구하고 논리적으로 같은 객채로 판단된다.

package class01;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

class Person {
    public String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person p = (Person) o;
        return Objects.equals(name, p.name);
    }
}

public class Test06 {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person("홍길동");
        Person p2 = new Person("홍길동");

        // 두 객체의 해시 코드
        System.out.println(p1.hashCode()); 
        System.out.println(p2.hashCode()); 

        // 해시코드가 달라도, equals를 재정의 했기 때문에 동등함
        System.out.println(p1.equals(p2)); // true

        // 리스트를 생성하고 두 객체 데이터를 추가한다.
        List<Person> peopleList = new ArrayList<>();
        peopleList.add(p1);
        peopleList.add(p2);
        
        Set<Person> peopleSet = new HashSet<>();
        peopleSet.add(p1);
        peopleSet.add(p2);
        
        // 그리고 리스트의 길이를 출력한다.
        System.out.println(peopleList.size()); // 2
        System.out.println(peopleSet.size());
        
        
    }
}

 

List 자료형대신 중복된 값을 허용하지 않는 리스트인 Set 자료형을 이용한 결과값을 보자.

로직의 결과를 예상해보자면, 우리는 Person 객체의 name 문자열이 같으면 같은 객체로 판별하도록 equals 메서드를 재정의 하였었다. 따라서 같은 이름을 가진 p1과 p2 객체를 HashSet 자료형에 넣는다면 중복 판별되어 하나의 데이터만 컬렉션에 들어가 있어야 한다.
그러나 HashSet의 size가 1이 나올 거라 예상했지만, 예상과 다르게 그대로 2가 출력된다.
p1과 p2는 논리적으로 같다고 정의하였지만 해시코드가 다르기 때문에 중복된 데이터가 컬렉션에 추가된 것이다.

따라서 이러한 오작동을 방지하기 위해 hashCode 메서드도 재정의하여 두 객체의 필드인 name의 문자열값이 같을 경우 같은 해시코드를 갖게 하도록 재구성할 필요가 있다.

class Person {
    public String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person p = (Person) o;
        return Objects.equals(name, p.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name); // name 필드의 해시코드를 반환한다.
    }
}

public class ClassTest {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person("홍길동");
        Person p2 = new Person("홍길동");

        // 두 객체의 해시 코드
        System.out.println(p1.hashCode()); // 54150093
        System.out.println(p2.hashCode()); // 54150093

        // 해시코드가 달라도, equals를 재정의 했기 때문에 동등함
        System.out.println(p1.equals(p2)); // true

        // SET를 생성하고 두 객체 데이터를 추가한다.
        Set<Person> people = new HashSet<>();
        people.add(p1);
        people.add(p2);

        // 그리고 SET의 길이를 출력한다.
        System.out.println(people.size()); // 1
    }
}

hashCode 메서드를 재정의함에 따라, 두 객체의 해시코드는 같은 값이 나오는 걸 볼수 있고, Set 자료형에도 중복된 데이터 적재로 판단되어 한번만 추가됨을 볼수 있다.

IDE로 오버라이딩 자동화하는 것도 가능하니 추후에 알아보쟈....

객체 문자 정보 toString() 메서드

toString( ) 메서드는 객체의 문자 정보를 반환해 주는 클래스이다.

객체를 System.out.println( )을 통해 출력하면, 원래는 주소값이 나오지만 
toString을 override함으로써,  System.out.println( ) 을 했을때 객체의 정보가 나오게할 수 있다.

StringBuffer와 StringBuilder

 

 

 

StringBuffer : 뒤에서 배울 스레드 환경에서 안정성 기능을 추가로 가지고 있다.

StringBuilder : 스레드 환경이 아닌 경우 StringBuilder의 성능이 좋으므로 일반적으로 프로그래밍에선 StringBuilder 사용을 권장한다. 

둘의 기능에서는 차이가 없다.

 

메서드명 설명
append(String str) 기존 문자열 뒤에 더하여 반환
delete(int start, int end) 시작 위치부터 끝 위치 전까지 삭제
insert(int offset, String str) 시작 위치부터 문자열을 삽입
reverse() 문자열을 반대로 출력

Math 클래스

Math 클래스는 수학에서 자주 사용하는 상수들과 함수들을 미리 구현해 놓은 클래스로 자바에서 수학 계산이 필요할 때 사용한다. 객체 선언을 하지않고 바로 사용할 수 있도록 해당 클래스가 제공하는 모든 메서드는 모두 정적 메서드로 이루어져 있다.

Wrapper 클래스

프로그램에 따라 기본 타입의 데이터를 객체형으로 표현해야 하는 경우가 있다. 이를 위해서 자바에서는 기본 자료형을 객체로 다루기 위한 클래스를 제공하는데, 이러한 클래스를 'Wrapper 클래스' 라고 합니다. 

Wrapper 클래스라고 부르는 이유는 기본형의 데이터 타입을 클래스로 포장했기 떄문.

 

사용하는 이유
1. 기본 데이터 타입을 Object로 변환할 수 있다.
2. java.util 패키지의 클래스는 객체만 처리하므로 Wrapper class는 이 경우에도 도움이 된다.
3. ArrayList 등과 같은 Collection Framework의 데이터 구조는 기본 타입이 아닌 객체만 저장하게 되고, Wrapper class를 사용하여 자동박싱/언박싱이 일어난다.
4. 멀티스레딩에서 동기화를 지원하려면 객체가 필요하다.
5. 기본 자료형의 값을 단순히 값으로만 사용하지 않고 그 값에 대한 메서드를 사용 혹은 null값을 이용하기 위해 사용된다.

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

박싱 : 기본 타입의 데이터를 Wrapper 클래스의 인스턴스로 변환하는 과정.

언박싱 :  Wapper클래스의 스턴스에 저장된 값을 기본 타입의 데이터로 꺼내는 과정.

03.  java.util 패키지

java.util에는 프로그램을 개발하는데 사용할 수 있는 유용한 유틸리티 클래스가 포함되어 있다.

Calender 클래스 

Calender cal = Calender.GetInstance(); //Calendar 선언은 이렇게한다.

int year = cal.get(Calender.YEAR);
int mont = cal.get(Calender.MONTH) +1;
int day = cal.get(CAlendar.DAY_OF_MONTH);

여러가지 상수 필드들이 존재하며, 날짜를 표시하기 위해 자주 사용하는 값들을 상수화하여 관리한다.

YEAR, MONTH, DATE, DAY_OFMONTH, HOUR_OF_DAY ....etc

 

 

 

*참조

https://inpa.tistory.com/entry/JAVA-%E2%98%95-equals-hashCode-%EB%A9%94%EC%84%9C%EB%93%9C-%EA%B0%9C%EB%85%90-%ED%99%9C%EC%9A%A9-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0

https://ssdragon.tistory.com/124

https://2jinishappy.tistory.com/259?category=936901

 

'JAVA Book Study' 카테고리의 다른 글

Section 17. 람다식  (0) 2024.01.02
Section 16. 컬렉션 프레임워크  (1) 2024.01.01
Section 14. 예외 처리  (0) 2023.12.31
Section 13. 내부 클래스  (0) 2023.12.29
Section 12. 추상 클래스와 인터페이스  (1) 2023.12.29
Comments