본문 바로가기
Everyday Study

2024.07.16 (화) { valueOf, 직렬화(Serialize), is-a, 마커인터페이스 }

by xogns93 2024. 7. 16.

String 클래스의 valueOf() 메소드

 

String 클래스의 valueOf() 메소드는 다양한 타입의 값을 문자열(String)로 변환하는 데 사용됩니다. valueOf()메소드는 오버로딩되어 여러 가지 타입을 인자로 받을 수 있으며, 각 타입에 따라 적절한 문자열 표현을 반환합니다. 다음은 String 클래스의 valueOf() 메소드에 대한 설명과 예제입니다.

 

valueOf() 메소드의 형태

  1. static String valueOf(boolean b)
  2. static String valueOf(char c)
  3. static String valueOf(char[] data)
  4. static String valueOf(char[] data, int offset, int count)
  5. static String valueOf(double d)
  6. static String valueOf(float f)
  7. static String valueOf(int i)
  8. static String valueOf(long l)
  9. static String valueOf(Object obj)

 

valueOf() 메소드는 다양한 데이터 타입을 문자열로 변환하는 데 매우 유용합니다. 이를 통해 데이터를 쉽게 조작하고 출력할 수 있습니다.


직렬화(Serialize)

 

직렬화(Serialization)는 객체의 상태를 바이트 스트림(Byte Stream)으로 변환하여 파일이나 네트워크를 통해 저장하거나 전송할 수 있게 하는 과정입니다. 이 바이트 스트림은 나중에 역직렬화(Deserialization)를 통해 다시 객체로 복원할 수 있습니다. 직렬화는 자바에서 Serializable 인터페이스를 사용하여 구현됩니다.

 

직렬화의 목적

  1. 파일 저장: 객체의 상태를 파일로 저장하고, 나중에 이를 다시 불러와 사용할 수 있습니다.
  2. 네트워크 전송: 객체를 네트워크를 통해 전송하고, 수신 측에서 이를 다시 객체로 복원할 수 있습니다.
  3. 세션 유지: 웹 애플리케이션에서 사용자 세션을 유지하기 위해 객체를 직렬화하여 저장합니다.

 

자바에서 직렬화 구현

직렬화를 구현하려면, 해당 클래스가 java.io.Serializable 인터페이스를 구현해야 합니다. 이 인터페이스는 아무 메소드도 포함하지 않는 마커 인터페이스입니다. 직렬화 과정에서 ObjectOutputStream을 사용하여 객체를 파일에 쓰고, 역직렬화 과정에서는 ObjectInputStream을 사용하여 파일로부터 객체를 읽어옵니다.

 

 

직렬화 예제

import java.io.*;

// Person 클래스는 Serializable 인터페이스를 구현합니다.
class Person implements Serializable {
    // 직렬화 버전 UID는 클래스의 변경 사항을 추적하는 데 사용됩니다.
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    // 생성자
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // toString 메소드 오버라이드
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + '}';
    }
}

public class SerializationExample {
    public static void main(String[] args) {
        // 직렬화할 객체 생성
        Person person = new Person("John Doe", 30);

        // 객체를 파일에 직렬화
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person);
            System.out.println("객체가 직렬화되었습니다.");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 파일에서 객체를 역직렬화
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person deserializedPerson = (Person) ois.readObject();
            System.out.println("객체가 역직렬화되었습니다.");
            System.out.println(deserializedPerson);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

코드 설명

  1. Person 클래스는 Serializable 인터페이스를 구현하여 직렬화 가능하도록 합니다.
  2. SerializationExample 클래스의 main 메소드에서 Person 객체를 생성합니다.
  3. ObjectOutputStream을 사용하여 person.ser 파일에 객체를 직렬화합니다.
  4. ObjectInputStream을 사용하여 person.ser 파일에서 객체를 역직렬화합니다.
  5. 직렬화된 객체를 읽어와 원래의 객체와 동일한 데이터를 가진 객체로 복원합니다.

 

주의 사항

  • 직렬화된 클래스의 변경 시 serialVersionUID를 명시적으로 지정해야 합니다. 그렇지 않으면 클래스 변경 시 역직렬화 과정에서 InvalidClassException이 발생할 수 있습니다.
  • 직렬화 대상 클래스의 일부 필드를 직렬화하지 않으려면 transient 키워드를 사용할 수 있습니다.
private transient int age;

이렇게 하면 age 필드는 직렬화되지 않습니다.

 

직렬화(Serialize)

  • 자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 byte 형태로 데이터를 변환하는 기술.
  • JVM(Java Virtual Machine 이하 JVM)의 메모리에 상주(힙 또는 스택)되어 있는 객체 데이터를 바이트 형태로 변환하는 기술

역직렬화(Deserialize)

  • byte로 변환된 Data를 원래대로 Object나 Data로 변환하는 기술을 역직렬화(Deserialize)라고 부릅니다.
  • 직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태.

 

 

직렬화는 객체를 저장하거나 전송할 때 매우 유용한 기능이지만, 보안과 성능 문제를 고려하여 신중하게 사용해야 합니다.

 


오토박싱(Autoboxing)과 언박싱(Unboxing)

 

자바에서 기본 데이터 타입과 객체 래퍼 클래스 간의 자동 변환을 의미합니다. 자바는 기본 타입(primitive type)과 이들의 대응 객체 래퍼 클래스(wrapper class)를 가지고 있습니다. 오토박싱과 언박싱은 이러한 타입 간의 변환을 자동으로 처리하여 코드의 가독성과 편의성을 높여줍니다.

 

기본 타입과 래퍼 클래스

기본 타입 래퍼 클래스

int Integer
boolean Boolean
char Character
byte Byte
short Short
long Long
float Float
double Double

 

오토박싱 (Autoboxing)

오토박싱은 기본 타입의 값을 자동으로 그에 대응하는 래퍼 클래스 객체로 변환하는 것을 의미합니다. 자바 5부터 도입되었습니다.

public class AutoboxingExample {
    public static void main(String[] args) {
        // 기본 타입 int
        int num = 10;
        
        // 오토박싱: int 타입이 Integer 객체로 자동 변환됨
        Integer boxedNum = num; // Integer.valueOf(num) 호출과 동일

        System.out.println("오토박싱된 값: " + boxedNum);
    }
}

 

언박싱 (Unboxing)

언박싱은 래퍼 클래스 객체를 다시 기본 타입으로 자동 변환하는 것을 의미합니다. 오토박싱과 마찬가지로 자바 5부터 도입되었습니다.

public class UnboxingExample {
    public static void main(String[] args) {
        // Integer 객체
        Integer boxedNum = 20;
        
        // 언박싱: Integer 객체가 int 타입으로 자동 변환됨
        int num = boxedNum; // boxedNum.intValue() 호출과 동일

        System.out.println("언박싱된 값: " + num);
    }
}

 

오토박싱과 언박싱의 혼합 사용

오토박싱과 언박싱은 함께 사용될 수 있으며, 이는 연산이나 컬렉션 사용 시 특히 유용합니다.

import java.util.ArrayList;

public class AutoboxingUnboxingExample {
    public static void main(String[] args) {
        // 오토박싱 예제
        ArrayList<Integer> list = new ArrayList<>();
        list.add(10); // int -> Integer 오토박싱
        
        // 언박싱 예제
        int num = list.get(0); // Integer -> int 언박싱

        System.out.println("리스트의 첫 번째 요소: " + num);
    }
}

 

 

장점과 단점

장점

  • 코드 간결성: 개발자가 명시적으로 변환 코드를 작성할 필요 없이, 자바가 자동으로 처리해줌으로써 코드가 간결해집니다.
  • 가독성 향상: 코드가 더 직관적이고 읽기 쉬워집니다.

단점

  • 성능 오버헤드: 오토박싱과 언박싱은 객체 생성과 메모리 사용을 수반하므로 성능에 영향을 줄 수 있습니다.
  • NullPointerException: 언박싱 시 래퍼 클래스 객체가 null인 경우, NullPointerException이 발생할 수 있습니다.
public class NullPointerExample {
    public static void main(String[] args) {
        Integer boxedNum = null;
        
        // 언박싱 시 NullPointerException 발생
        try {
            int num = boxedNum; // boxedNum이 null이므로 예외 발생
        } catch (NullPointerException e) {
            System.out.println("NullPointerException 발생!");
        }
    }
}

"is-a" 관계

 

자바에서 "is-a" 관계는 객체 지향 프로그래밍의 상속(Inheritance)을 나타내는 중요한 개념입니다. "is-a" 관계는 특정 클래스가 다른 클래스의 하위 클래스임을 의미하며, 이는 하위 클래스가 상위 클래스의 특성과 행동을 상속받음을 나타냅니다.

 

"is-a" 관계 설명

"is-a" 관계는 두 클래스 간의 상속 관계를 의미합니다. 만약 클래스 B가 클래스 A를 상속한다면, 우리는 "B는 A이다" ("B is an A")라고 말할 수 있습니다. 이는 B가 A의 특성을 물려받아 사용할 수 있음을 의미합니다.

 

다음은 상속을 사용하여 "is-a" 관계를 구현한 예제입니다.

// 상위 클래스
class Animal {
    void eat() {
        System.out.println("This animal eats food.");
    }
}

// 하위 클래스
class Dog extends Animal {
    void bark() {
        System.out.println("The dog barks.");
    }
}

public class IsARelationshipExample {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        
        // Dog는 Animal을 상속받았으므로, Animal의 메소드를 사용할 수 있습니다.
        myDog.eat(); // "This animal eats food." 출력
        myDog.bark(); // "The dog barks." 출력
    }
}

 

위 예제에서 Dog 클래스는 Animal 클래스를 상속받습니다. 따라서 우리는 Dog 클래스가 Animal 클래스와 "is-a" 관계를 가지고 있다고 말할 수 있습니다.

 

"is-a" 관계의 장점

  1. 재사용성: 상위 클래스의 코드와 메소드를 하위 클래스에서 재사용할 수 있습니다.
  2. 유지보수성: 공통 기능을 상위 클래스에 정의하면, 하위 클래스에서 중복 코드를 줄일 수 있어 유지보수가 용이합니다.
  3. 다형성: "is-a" 관계를 통해 다형성을 구현할 수 있습니다. 다형성은 동일한 인터페이스를 통해 서로 다른 클래스의 객체를 다룰 수 있게 합니다.

요약

  • "is-a" 관계는 상속을 통해 클래스 간의 관계를 나타냅니다.
  • 하위 클래스는 상위 클래스의 특성과 행동을 상속받아 사용할 수 있습니다.
  • 다형성을 통해 동일한 인터페이스를 사용하여 서로 다른 클래스의 객체를 다룰 수 있습니다.

이러한 "is-a" 관계는 객체 지향 프로그래밍의 핵심 원리 중 하나로, 코드의 재사용성, 유지보수성, 다형성을 높여줍니다.

 


마커 인터페이스(Marker Interface)

 

자바에서 아무 메소드도 가지지 않는 인터페이스로, 클래스가 특정 속성이나 동작을 가진다는 것을 표시하는 데 사용됩니다. 마커 인터페이스는 런타임에 특정 클래스가 어떤 기능을 지원하는지 확인하는 데 도움을 줍니다. 자바에서 대표적인 마커 인터페이스로는 Serializable, Cloneable, Remote 등이 있습니다.

 

마커 인터페이스의 용도

  1. 타입 체크: 마커 인터페이스는 클래스가 특정 인터페이스를 구현하는지 확인하여 타입 체크를 수행할 수 있습니다.
  2. 특정 동작 부여: 마커 인터페이스를 구현한 클래스에 특정 동작을 부여할 수 있습니다. 예를 들어, Serializable 인터페이스를 구현한 클래스는 직렬화(Serialization)를 지원합니다.
  3. 의미 부여: 마커 인터페이스를 통해 클래스에 의미를 부여하고, 이를 통해 코드의 명확성을 높일 수 있습니다.

 

예제: Serializable 인터페이스

Serializable 인터페이스는 클래스가 직렬화를 지원한다는 것을 표시합니다. 객체를 바이트 스트림으로 변환하여 파일에 저장하거나 네트워크를 통해 전송할 수 있게 합니다.

import java.io.*;

public class MarkerInterfaceExample implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;

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

    public String getName() {
        return name;
    }

    public static void main(String[] args) {
        MarkerInterfaceExample example = new MarkerInterfaceExample("example");

        // 객체를 파일에 직렬화
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("example.ser"))) {
            oos.writeObject(example);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 파일에서 객체를 역직렬화
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("example.ser"))) {
            MarkerInterfaceExample deserializedExample = (MarkerInterfaceExample) ois.readObject();
            System.out.println("Deserialized Name: " + deserializedExample.getName());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

위 예제에서 MarkerInterfaceExample 클래스는 Serializable 인터페이스를 구현하여 객체를 직렬화하고, 이를 파일에 저장하거나 역직렬화하여 파일에서 읽어올 수 있습니다.

 

예제: Cloneable 인터페이스

Cloneable 인터페이스는 클래스가 복제 가능함을 나타냅니다. 이 인터페이스를 구현한 클래스는 clone() 메소드를 통해 객체를 복제할 수 있습니다.

public class CloneableExample implements Cloneable {
    private String name;

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

    public String getName() {
        return name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) {
        CloneableExample example = new CloneableExample("example");

        try {
            CloneableExample clonedExample = (CloneableExample) example.clone();
            System.out.println("Original Name: " + example.getName());
            System.out.println("Cloned Name: " + clonedExample.getName());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

위 예제에서 CloneableExample 클래스는 Cloneable 인터페이스를 구현하여 객체를 복제할 수 있습니다. clone()메소드를 오버라이드하여 객체 복제를 지원합니다.

 

 

결론

마커 인터페이스는 자바에서 클래스가 특정 속성이나 동작을 가진다는 것을 표시하는 데 사용됩니다. 이는 런타임에 타입 체크와 특정 동작을 부여하는 데 유용합니다. 대표적인 마커 인터페이스로는 Serializable, Cloneable, Remote 등이 있으며, 사용자 정의 마커 인터페이스도 만들 수 있습니다. 마커 인터페이스를 적절히 활용하면 코드의 명확성과 유지보수성을 높일 수 있습니다.

 

 


타입 추론 (Type Inference)

 

타입 추론은 메서드 호출에 적용할 수 있는 타입 아규먼트(또는 아규먼트)를 결정하기 위해 해당 메소드 호출 및 해당 메서드 선언을 살펴보는 Java 컴파일러의 기능입니다.

추론 알고리즘은 타입 아규먼트의 타입과 (가능한 경우)리턴되는 타입을 결정합니다.

마지막으로 추론 알고리즘은 모든 아규먼트와 함께 작동하는 특정 타입을 찾으려고 시도합니다.