Notice
Recent Posts
Recent Comments
Link
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Tags
more
Archives
Today
Total
관리 메뉴

백엔드 개발 공부 일지

JAVA Basic - 기본 클래스 본문

Java 입문

JAVA Basic - 기본 클래스

JungCat 2022. 12. 12. 15:42

● Object 클래스

- java.lang 패키지

지금까지 자바로 프로그램을 구현하면서 String, Integer와 같은 클래스를 사용했다.

근데 그럼 그 클래스들은 어디에 있는 걸까?

 

이러한 클래스가 java.lang 패키지에 속해 있따. String 클래스오 Integer클래스의 전체 이름은 java.lang.String, java.lang.Integer이다.

 

java.lang 패키지에는 기본적으로 많이 사용하는 클래스들이 포함되어 있다. java.lang 패키지에서는 구디 import 하지 않아도 java.lang 패키지의 모든 하위 클래스를 참조 할 수 있다. (import java.lang.* 문장이 자동으로 추가됌)

 

이번 장에서 이야기할 클래스는 모두 java.lang패키지에 속해있다. (모두 프로그램에 가장 많이 사용하는 기본 클래스)

 

그러면 모든 클래스의 최상위 클래스인 java.lang.Object부터 알아보자.

- Object 클래스

Object  클래스는 모든 자바 클래스의 최상위 클래스 이므로 모든 클래스는 Object 클래스로부터 상속을 받는다. (extends Object는 자동으로 생성)

 

주로 사용되는 Object 메서드는 다음과 같다.

 

Stirng toString() : 객체를 문자열로 표현하여 반환한다. 재정의하여 객체에 대한 설명이느 특정 멤버 변수값을 반환한다.

boolean equals(Object obj) : 두 인스턴스가 동일한지 여부를 반환한다. 재정의하여 논리적으로 동일한 인스턴스임을 정의한다.

int hashCode() : 객체의 해쉬코드값을 반환한다.

Object clone() : 객체를 복제하여 동일한 멤버 변수 값을 가진 새로운 인스턴스를 생성한다.

Class getClass() : 객체의 Class 클래스를 반환하다.

void finalize() : 인스턴스가 힙 메모리에서 제거될 때 가비지 컬렉터에 의해 호출되는 메서드이다. 네트워크 연결 해제, 열려있는 파일 스트림 해제등을 구현한다.

void wait() : 멀티스레드 프로그램에서 사용하는 메서드이다. 스레드를 기다리는 상태로 만든다.

void notify() : wait()메서드에 의해 기다리고 있는 스레드(non runnable 상태)를 실행 가능한 상태(runnable)로 가져온다.

 

* Object클래스의 모든 메서드를 재정의할 수 있는 것은 아니다.

모든 클래스는 Object 메서드를 사용할 수 있고 필요에 따라 재정의하여 사용할 수 있다.

그러나 Object클래스도 final 예약어로 선언한 메서드는 자바 스레드에서 사용하거나 클래스를 로딩하는 등 가상 머신과 관련된 메서드이기 때문에 재정의 할 수 없다. (ex ) Class getClass())

 

- toString() 메서드

Object 클래스가 기본으로 제공하는 toString() 메서드는 이름처럼 객체 정보를 문자열(String)으로 바꾸어 준다. toString()은 재정의하여 사용이 가능하며 String이나 Integer등 여러 JDK클래스에는 toString()메서드가 이미 재정의 되어 있다.

 

toString() 메서드는 인스턴스 정보를 문자열로 반환하는 메서드이다.

toString() 메서드의 원형은 생성된 인스턴스의 클래스 이름과 주소값을 보여준다.

 

package object;

class Book {
    int bookNumber;
    String bookTitle;

    Book(int bookNumber, String bookTitle) {
        this.bookNumber = bookNumber;
        this.bookTitle = bookTitle;

    }
}

public class ToStringEx {
    public static void main(String[] args) {
        Book book1 = new Book(200, "개미");

        System.out.println(book1);
        System.out.println(book1.toString());
    }

}
object.Book@27d6c5e0
object.Book@27d6c5e0

 

System.out.println() 출력문에 참조 변수를 넣으면 인스턴스 정보가 출력되는데, 이때 자동으로 호출되는 메서드가 toString()이다.

 

여기서 호출된 toString()은 Book클래스의 메서드가 아닌 Object 클래스의 메서드이다. Object 클래스의 toString() 메서드의 원형은 다음과 같다.

 

getClass().getName() + '@' + Integer.toHexString(hashCode())

그런데, toString() 메서드가 호출된 경우 출력력 결과가 다른 경우가 있다.

 

String str = new String("test");

System.out.println(str);

== > test 출력

 

Integer i1 = new Integer(100);

System.out.println(i1);

== > 100 출력

 

이 이유는 String과 Integer 클래스는 toString() 메서드를 미리 재정의해두었기 때문이다. JDK에서 제공하는 클래스 중에는 toString() 메서드를 미리 재정의한 클래스가 많다.

 

- equals() 메서드

equals() 메서드의 원래 기능은 인스턴스의 주소값을 비교하여 boolean 값을 반환해주는 것이다.

 

이말은, 주소가 같다면 당연히 같은 인스턴스일 것인데, 주소가 달라도 같은 인스턴스라고 정의할 수 있는 경우가 있다.

물리적 동일성(인스턴스의 메모리 주소가 같음)뿐 아니라 논리적 동일설(논리적으로 두 인스턴스가 같음)을 구현할 떄도 equals() 메서드를 재정의 하여 사용한다.

 

물리적 동일성은 간단하므로 건너뛰고 String과 Integer클래스에서 재정의되어 있는 equals()메서드만 살펴보자

 

package object;

public class StringEquals {
    public static void main(String[] args){
        String str1 = new String("abc");
        String str2 = new String("abc");

        System.out.println(str1 == str2); // 두 인스턴스 주소값이 같은지 비교하여 출력
        System.out.println(str1.equals(str2));// String 클래스의 equal()메소드

        Integer i1 = new Integer(100);
        Integer i2 = new Integer(100);

        System.out.println(i1==i2); // 두 인스턴스 주소값이 같은지 비교하여 출력
        System.out.println(i1.equals(i2));
    }
}
false
true
false
true

'=='의 비교연산자를 사용할 경우 서로 다른 인스턴스를 가리키기 때문에 false를 반환하지만, 재정이된 equals()메서드는 둘의 반환값이 동일하기 때문에 true를 출력한다. (재정의 되지 않았더라면 '=='연산자와 같이 false를 반환했을것)

 

그리고, 자바 최신버전을 사용했더니 Integer생성은 deprecated가 나와서 나중에 한번 확인해보자.(자바 9부터)

 

- hashCode() 메서드

hash는 정보를 저장하거나 검색할 때 사용하는 자료 구조이다. 정보를 어디에 저장할 것인지, 어디서 가져올 것인지 해시 함수를 사용하여 구현한다.

 

해시 함수는 객체의 특정정보를 매개변수 값을 넣으면 그 객체가 저장되어야 할 위치 나 저장된 해시 테이블 주소를 반환한다. (따라서 객체 정보를 알면 해당 객체의 위치를 빠르게 검색할 수 있따.) 해시함수는 개발하는 프로그램 특성에 따라 다르게 구현된다.

 

index= hash(key)

저장위치 = 해시함수(객체정보)

 

자바에서는 두 인스턴스가 같다면  hashCode()메서드에서 반환하는 해시 코드값은 같아야 한다. 따라서 논리적으로 같은 두 객체도 해시 코드값을 반환하도록 hashCode()메서드를 재정의 햐아한다. 

 

다시말해, equals() 메서드를 재정의 했다면 hashCode() 메서드도 재정의 하여야 한다.

 

package object;

public class HashCodeTest {
    public static void main(String[] args){
        String str1 = new String("abc");
        String str2 = new String("abc");

        System.out.println(str1.hashCode()); // 두 인스턴스 주소값이 같은지 비교하여 출력
        System.out.println(str2.hashCode());// String 클래스의 equal()메소드

        Integer i1 = new Integer(100);
        Integer i2 = new Integer(100);

        System.out.println(i1.hashCode()); // 두 인스턴스 주소값이 같은지 비교하여 출력
        System.out.println(i2.hashCode());
    }
}
96354
96354
100
100

이 경우, 재정의 된 equals()메서드가 True를 반환하는 경우이므로, hashCode()메서드도 재정의 하여 같은 해시코드 값을 반환하도록 하였다.

 

하지만 이 둘은 논리적으로 같지만, 실제로는 다른 인스턴스 인것은 마찬가지다.

 

- clone() 메서드

객체 원본을 유지해 놓고 복사본을 사용한다거나, 기본 틀의 복사본을 사용해 동일한 인스턴스를 만들어 복잡한 생성과정을 간단히 하려는 경우에 clone()메서드를 사용한다.

 

clone() 메서드는 Object에 오른쪽과 같이 선언되어 있으며, 객체를 복제해 또 다른 객체를 반환해주는 메서드이다.

 

protected(public) Object clone();

 

class Point{
    int x;
    int y;

    Point(int x, int y){
        this.x = x;
        this.y = y;
    }

    public String toString(){
        return "x = " + x + "," + "y = " + y;
    }
}
// 객체를 복제해도 된다는 의미로 Cloneable 인터페이스를 함께 선언
class Circle implements Cloneable {
    Point point;
    int radius;

    Circle (int x, int y, int radius){
        this.radius = radius;
        point = new Point(x,y);
    }

    public String toString(){
        return "원점은" + point + "이고, " + "반지름은 " + radius + "입니다.";
    }

	// clone()메서드를 사용할 떄 발생할 수 있는 오류를 예외 처리함
    @Override
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

public class ObjectCloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Circle circle = new Circle(10,20,30);

        // clone() 메서드를 사용해 circle 인스턴스를 copyCircle에 복제함
        Circle copyCircle = (Circle)circle.clone( );

        System.out.println(circle);
        System.out.println(copyCircle);
        System.out.println(System.identityHashCode(circle));
        System.out.println(System.identityHashCode(copyCircle));

    }
}

 

원점은x = 10,y = 20이고, 반지름은 30입니다.
원점은x = 10,y = 20이고, 반지름은 30입니다.
500977346
20132171

 

clone() 메서드를 사용하려면 객체를 복제해도 된다는 의미로 클래스에 Cloneable인터페이스를 구현해야 한다. (Cloneable인터페이스 내에서는 별도 구현해야 하는 메서드는 없다. 이러한 인터페이스를 마커 인터페이스(marker interface)라고 한다.)

 

만약 clone()메서드만 재정의하고 Cloneable 인터페이스를 명시 하지 않으면 clone() 메서드를 호출할 떄 CloneNotsupportedException 이 발생한다. 이 예제의 clone()메서드는 Object의 clone()메서드를 그대로 사용하고 있다. Object의 clone()메서드는 클래스의 인스턴스를 새로 복제하여 생성해준다.

 

멤버변수가 동일한 인스턴스가 다른 메모리에 새로 생성되는것..!

 

출력 결과를 보면 인스턴스의 멤버 변수 값은 같고 주소 값은 다른 copyCircle이 생성되었음을 알 수 있다.

 

● String 클래스

자바는 문자열을 사용할 수 있도록 String 클래스를 제공한다.

문자열은 프로그램을 구현할 때 많이 활용된다. String을 사용할 때 문자열을 생성자의 매개변수로 하여 생성하는 방식과 이미 생성된 문자열 상수를 가리키는 방식 이 있따.

 

String str1 = new String("abc"); // 생성자의 매개변수로 문자열 생성
String str2 = "test"; // 문자열 상수를 가리키는 방식

언뜻 비슷해보이지만, 두 방식은 내부적으로 큰 방식차이가 있다.

new 예약어를 사용하여 객체를 생성하는 경우 "abc" 문자열을 위한 메모리가 할당되고 새로운 객체가 생성된다.

하지만, 2번째 예시와 같이 바로 문자열 상수를 가리키는 경우에 str2가 기존에 만들어 져있던 "test"라는 문자열 상수의 메모리 주소를 가리키게 된다.

따라서 String str3 = "test"코드를 작성하면 str2와 str3는 주소값이 같게 된다.

 

test나 10, 20 등과 같이 프로그램에서 사용되는 상수값을 저장하는 공간을 '상수 풀(constat pool)' 이라고 한다.

 

- String 클래스의 final char[ ] 변수

다른 프로그래밍 언어는 문자열을 구현할 떄 일반적으로 char[ ] 배열을 사용하는데, 자바는 String 클래스를 제공해 char[ ] 배열을 직접 구현하지 않고 편리하게 문자열을 사요할 수 있다.

 

String 클래스의 구현 내용을 보면 private final char value[ ] 라고 선언된 char형 배열이 있따.

즉, String s = new String("abc")라고 쓰면 이 변수는 immutable임을 의미 한다.

(프로그램에서 두개의 문자열을 연결하면 문자열이 변경되는 것이 아닌 두문자열이 연결된 새로운 문자열이 생성된다.)

 

아래는 그 예시이다.

package string;

public class StringTest {
    public static void main(String[] args){
        String javaStr = new String("java");
        String androidStr = new String("android");
        System.out.println(javaStr);
        System.out.println("처음 문자열 주소 값: " + System.identityHashCode(javaStr));

        javaStr = javaStr.concat(androidStr);

        System.out.println(javaStr);
        System.out.println("연결된 문자열 주소 값: " + System.identityHashCode(javaStr));
    }
}
java
처음 문자열 주소 값: 2129789493
javaandroid
연결된 문자열 주소 값: 189568618

위와 같이 같은 메모리에 새로운 문자열로 수정된 것이 아닌 다른 메모리에 연결된 문자열이 저장된 것을 확인 할 수 있다.

 

- StringBuffer와 StringBuilder 클래스 활용하기

프로그램을 만들다 보면 문자열을 변경하거나 연결해야 하는 경우가 많다. 그런데 String 클래스는 한번 생성되면 "그 내부의 문자열이 변경되지 않기 때문에" String 클래스를 사용하여 문자열을 계속 연결하거나 변경하는 프로그램을 작성하면 메모리가 많이 낭비된다. 이 문제를 해결하는 것이 바로 StringBuffer와 StringBuilder이다.

 

StringBuffer와 StringBuilder는 내부에 변경 가능한(=final이 아닌) char[ ]를 변수로 가지고 있다. 이 두 클래스를 사용하는 문자열을 연결하면 기존에 사용하던 char[ ] 배열이 확장되므로 추가 메모리를 사용하지 않는다.

 

따라서, 문자열을 연결하거나 변경할 경우 두 클래스 중 하나를 사용하면 된다. 두 클래스의 차이는 여러작업(스레드) 이 동시에 문자열을 변경하려 할 떄 문자열을 안전하게 변경하도록 보장해 주는가 그렇지 않은가의 차이이다.

 

StringBuffer 클래스는 문자열이 안전하게 변경되도록 보장하지만, StringBuilder 클래스는 보장되지 않는다. 프로그램에서 따로 스레드를 생성하는 멀티 스레드 프로그램이 아니라면 StringBuilder를 사용하는 것이 실행속도가 좀 더 빠르다.

 

StringBuilder 예제

package string;

public class StringBuilderTest {
    public static void main(String[] args){
        String javaStr = new String("java");
        System.out.println("javaStr 문자열 주소 : " + System.identityHashCode(javaStr));

        StringBuilder buffer = new StringBuilder(javaStr);
        System.out.println("연산 전 buffer 메모리 주소 : " + System.identityHashCode(buffer));

        buffer.append(" and");
        buffer.append(" android");
        buffer.append(" programming is fun!");
        System.out.println("연산 후 buffer 메모리 주소 : " + System.identityHashCode(buffer));

        javaStr = buffer.toString();
        System.out.println(javaStr);
        System.out.println("새로 만들어진 javaStr 문자열 주소 : " + System.identityHashCode(javaStr));

    }
}
javaStr 문자열 주소 : 2129789493
연산 전 buffer 메모리 주소 : 189568618
연산 후 buffer 메모리 주소 : 189568618
java and android programming is fun!
새로 만들어진 javaStr 문자열 주소 : 793589513

 

위와 같이 StringBuilder를 사용할 경우 연산 후에도 Stringbuilder 객체의 주소값이 변환되지 않은 것을 확인할 수 있다.

 

 

● Wrapper 클래스

지금까지 정수를 사용할 때 기본형 int를 사용했다. 그런데, 정수를 객체형으로 사용해야 하는 경우가 있다. 예를들어 매개변수가 객체거나 반환 값이 객체형인 경우가 이에 해당한다.

 

이를 위해 자바에서 기본 자료형처럼 사용할 수 있는 클래스를 제공하는데 이것이 Wrapper클래스이다.

기본형에 따른 Wrapper 클래스는 다음과 같다.

 

boolean -> Bollean

byte -> Byte

char -> Charater

short -> Short

int -> Integer

long -> Long

float -> Float

double -> Double

 

integer클래스를 살펴보면 int자료형의 특성이 그대로 구현되었으며, 사용가능한 최댓값과 최솟값이 static변수로 정의되어있따. 또한 Integer클래스의 멤버 변수로 기본 자료형 int를 가지고 있고, int값을 객체로 활용할 수 있는 여러 메서드 또한 제공한다. 그리고 int value는 final 변수이며 한번 생성 되면 변경할 수 있다.

 

- Integer 클래스의 메서드

integer 클래스의 대표적인 메서드는 다음과 같다.

intValue() : Integer클래스 내부의 int 자료형 값을 가져온다.

valueOf() : 정적 메소드로 생성자를 사용하지 않고 정수나 문자열을 바로 Integer클래스로 반환할 수 있다.

parseInt() : 문자열을 숫자로 반환하여 나타낸다.

 

- 오토박싱과 언박싱

자바 5부터 다음과 같이 객체형과 기본형의 연산이 가능하다.

Integer num1 = new Integer(100);
int num2 = 200;
int sum - num1 + num2; // num1이 num1.intValue()로 변환(언박싱)
Integer num3 = num2; // num2가 Integer.valueOf(num2)로 변환 (오토박싱)

이와 같이 기본형을 객체형으로 바꾸는 것을 오토박싱(autoboxing), 객체형을 기본형으로 꺼내는 것을 언박싱(unboxing)이라고 한다. (이것은 자바 연산방식이 변경된 것이 아닌 컴파일러가 변경하는 것이다)

 

여기서는 Wrapper 클래스 내의 Integer클래스만 살펴보고 나머지는 JavaDoc을 참고하여 공부해보자.

● Class 클래스

자바의 모든 클래스와 인터페이스는 컴파일이 되고 나면 class파일로 생성된다. 이 파일에는 클래스나 인터페이스에 대한 변수, 메서드, 생성자 등의 정보가 들어 있다.

Class 클래스는 컴파일 된 class파일에 저장된 클래스나 인터페이스 정보를 가져오는데 사용한다.

 

추후 정리하자 (아픔)

 

 

'Java 입문' 카테고리의 다른 글

JAVA Basic - 자바 입출력  (0) 2022.12.14
JAVA Basic - 예외처리  (0) 2022.12.12
JAVA Basic - 인터페이스  (0) 2022.12.08
JAVA Basic - 추상 클래스  (0) 2022.12.08
JAVA Basic - 상속  (0) 2022.12.07
Comments