[Java/자바] 2. 객체지향 Object-Oriented (3)

인터페이스 Interface

  • 추상 클래스보다 추상성이 더욱 심화된 개념
  • 멤버 변수는 상수형으로 선언되어야 한다.
  • 메소드는 모두 추상 메소드로 선언되어야 한다.
  • 👉 인터페이스는 상수와 추상 메소드 외에 다른 멤버를 갖지 못하게 함으로써 추상클래스보다 더 완벽한 추상화를 제공한다.

인터페이스를 사용하는 이유

  • 논리적으로 ‘is a ~’ 관계가 성립할 때 상속을 활용(단일 상속만 허용)
  • 논리적으로 ‘is a ~’ 관계가 성립하지 않을 때 인터페이스를 활용(다중 상속과 유사한 기능 구현 가능)

🗝 자바에서 다중상속이 금지된 이유는 동일한 이름의 변수가 자식 클래스에 중복되어 상속되는 것을 막기 위해서다. -> 인터페이스는 변수를 가질 수 없는 특수한 클래스이므로 다중상속을 가능하게 한다.

public interface 인터페이스명 [extends 부모인터페이스명, ...]{
    // 상수 - final예약어를 사용해서 멤버 변수 선언. 인터페이스는 객체 생성 불가하므로 상수는 static 예약어를 붙여서 선언해야한다.
    public static final int EXAMPLE = 1;
    // 추상 메소드 
    public abstract void example();
}

인터페이스 변수 선언시 static final, abstract는 생략 가능

활용

인터페이스는 추상 클래스와 유사하기 때문에 직접 객체화 되지 못한다.

  1. 인터페이스 상속 using implements예약어
  2. 자식 클래스 생성
  3. 자식 클래스의 객체 생성
  4. 프로그램의 사용

인터페이스를 포함하는 클래스 구문의 형태

[modifiers] class 클래스명 [extends 부모 클래스] [implements 인터페이스1, 인터페이스2, ...]{
    
}

인터페이스를 상속하는 클래스는 인터페이스에 정의된 추상 메소드들을 모두 오버라이딩 해야한다.

❗️오버라이딩 하지 않을 경우 추상 메소드가 상속되기 때문에 클래스는 추상 클래스로 선언 되어야 한다.

형변환

  • 인터페이스 사용 시 클래스와 동일하게 묵시적/명시적 형변환이 가능
  • 인터페이스는 부모 클래스와 동일한 지위/자격을 가진다.

상속

  • 인터페이스 선언 시 다른 인터페이스를 상속하여 정의할 수 있다. using extends예약어

    public interface 인터페이스명 [extends 부모인터페이스1, 부모인터페이스2, …]{

    }

  • 인터페이스도 상속 관계에 따라 계층구조를 가질 수 있다.
  • 다중 상속 가능
  • 상속 받은 추상 메소드를 모두 구현해야한다.

자바 패키지

패키지 Package

자바의 클래스들을 분류하고, 관련된 클래스와 인터페이스를 하나의 폴더에 적절하게 배치할 때 관련된 클래스들이 묶여있는 폴더

윈도우 탐색기에서 폴더 또는 디렉토리 개념과 같다. 따라서 하나의 폴더에 동일한 이름의 파일을 두 개 이상 저장하는 것은 불가능하다.

패키지를 사용하지 않으면 비슷한 목적의 클래스 이름을 매번 다르게 생각해야하고 시스템이 복잡해질수록 클래스 파일들도 상대적으로 많이 작성된다. 이때 기능과 용도가 다른 클래스들이 모두 같은 폴더에 저장되어 있다면 관리상 어려움이 발생한다.

🗝 패키지를 적용하면 개발자가 작성한 클래스를 특정 패키지로 묶어서 관련 클래스들을 폴더 단위로 관리할 수 있다.

package예약어

// 패키지 선언 문장은 반드시 첫 번째 문장으로 기술해야한다.
package 상위 패키지명.패키지명

장점

  • 다른 기능을 구현한, 같은 이름의 클래스를 사용할 때 이름의 충돌을 피할 수 있다.
  • 관리가 용이하며 클래스의 접근 권한을 패키지 단위로 제어할 수 있다.
  • 자바의 경우 API에 제공되는 많은 클래스들을 패키지화하여 제공
  • 사용자가 만든 클래스들을 패키지화 해서 사용할 수 있다.

종류

  • java.applet: 애플릿 작성에 필요한 기능을 모은 패키지
  • java.awt

    • GUI작성 관련 패키지
    • 버튼, 텍스트 필드, 메뉴 등 관련 컴포넌트와 이벤트 기능 제공.
  • ⭐️java.io

    • 자바 입출력 기능과 관련된 패키지
    • 파일이나 버퍼 등의 입출력 기능을 제공
  • ⭐️java.lang

    • 자바 언어의 기초적인 사항을 정의한 클래스와 관련된 패키지
    • Object클래스, 문자열 관련, 시스템 관련, 멀티 Thread 관련 등 기본적인 기능들을 포함한다.
  • ⭐️java.net

    • 자바 네트워크에 관련된 패키지
    • 네트워크와 관련된 패키지는 이 밖에도 여러 가지가 있지만 이 패키지 내에는 소켓과 관련된 기능을 제공
  • java.swing

    • GUI작성 관련 패키지
    • java.awt포함 내용보다 다양하고 융통성 있는 컴포넌트를 제공
  • ⭐️java.util

    • 유틸리티성 기능과 관련된 패키지
    • 날짜 표현이나 여러 자료형을 하나로 취급하는 컬렉션과 관련된 기능을 제공

import 예약어

  • import 예약어를 사용해 패키지화된 클래스를 사용하게끔 할 수 있다.
  • java.langimport 예약어 사용없이 사용 가능
  • import 예약어를 사용하여 JVM 실행 시 클래스를 찾도록 한다.

    1. 클래스가 저장된 폴더
    2. 클래스패스를 저장한 폴더
    3. import 한 클래스

커스텀라이브러리

필요로 하는 기능이 API형태로 JVM에 적재되어 있지 않은 경우 개발자가 직접 커스텀 라이브러리를 생성할 수 있다.

예외 처리

자바 언어를 더욱 강하게(robust)하게 만드는 요소

오류

  • 에러 Error

    • 프로그램으로 처리할 수 없기 때문에 에러 발생 시 프로그램이 중단됨
    • 예) 네트워크 작업 처리 시 랜선이 끊어지는 경우
  • 예외 Exception

    • 프로그램 실행 중에 발생할 수 있는 가벼운 사건
    • 프로그래머가 충분히 예측할 수 있는 사건
    • 프로그램적으로 얼마든지 처리할 수 있는 오류

자바에서 예외가 발생하는 경우

  • 정수를 0으로 나누는 경우
  • 배열 인덱스가 해당 배열의 길이를 초과하거나 음수값을 가지는 경우
  • 부적절한 형변환이 발생하는 경우
  • 입출력 시 인터럽트가 발생하는 경우
  • 입출력을 위해 필요한 파일이 존재하지 않는 경우

자바는 객체 지향 언어이므로 예외도 객체로 처리한다. 👉 예외를 객체로 처리하기 위해 객체를 생성할 클래스가 필요

발생 가능성이 있는 모든 예외 상황을 미리 예측하여 다양한 예외 클래스를 제공한다.

예외 처리 로직을 구현하지 않은 경우

기본 예외 처리 핸들러가 작동하는데, 이는 기본적으로 에러 메세지를 화면에 출력하고 실행을 중지시키는 역할만 수행한다.

예외 클래스

모든 예외는 java.lang.Throwable클래스의 자식 클래스인 java.lang.Exception클래스의 자식 클래스로 만들어진다.

  • ArithmeticException: 정수를 0으로 나눌 때 발생하는 산술 연산 오류
  • IndexOutOfBoundsException: 배열의 인덱스가 배열의 길이를 넘어서면 발생하는 오류
  • IllegalArugumentException: 메소드의 매개변수 유형을 잘못 사용하면 발생하는 오류
  • IOException: 입출력 시에 지정한 파일이 시스템에 존재하지 않으면 발생하는 오류

자바의 예외 처리 방법

예외가 발생한 메소드 내에서 직접 처리하는 방식

  1. try-catch 구문

    try{
        예외 발생 가능이 있는 코드;    
    }catch(예외 타입 매개 변수){
        예외 타입의 예외가 발생할 경우 수행될 코드;
    }
  2. catch 블록의 매개변수는 예외 객체가 발생했을 때 참조하는 변수명으로, 반드시 java.lang.Throwable클래스의 하위 클래스 타입으로 선언되어야 한다.
  3. 다중 catch 블록 여러 개의 예외가 발생할 수 있는 경우 다중 catch블록을 사용하여 예외에 따라 적절하게 예외를 처리할 수 있다.

    try{
        예외 발생 가능이 있는 코드;    
    }catch(예외 타입1 매개 변수){
        예외 타입1의 예외가 발생할 경우 수행될 코드;
    }catch(예외 타입n 매개 변수){
        예외 타입n의 예외가 발생할 경우 수행될 코드;
    }
  4. 상속을 이용한 예외 처리
  5. 자바에서 지원하는 모든 예외 클래스는 Exception 클래스의 자식 클래스로 존재한다.
  6. 이 경우 모든 예외를 하나의 catch 블록으로 처리할 수 있지만 구체적으로 어떤 예외가 발생했는지 알 수 없고, 발생된 예외 객체에 따라서 예외 처리 로직을 분기 처리 할 수 없다.
  7. 다중 catch 문과 exception을 함께 사용하여 예측 가능한 오류, 예측 불가능한 오류를 모두 적절하게 처리할 수 있다. 이 경우 Exception 블록은 catch블록들 중 맨 마지막에 위치해야한다.
  8. finally 블록
  9. try-catch블록과 함께 사용 가능
  10. finally블록의 내용은 예외 발생의 유무나 예외 catch 유무와 상관 없이 무조건 수행된다.
  11. 데이터베이스 연동 로직, 파일입출력 로직을 수행한 후에 반드시 수행되어야할 로직이 있는 경우 사용

    try{
        예외 발생 가능이 있는 코드;    
    }catch(예외 타입 매개 변수){
        예외 타입의 예외가 발생할 경우 수행될 코드;
    }finally{
        예외 발생 여부와 무관하게 무조건 수행될 코드;
    }

해당 메소드를 호출한 곳으로 예외 처리를 넘기는 방식

  1. throws 예약어

    [modifiers] returnType 메소드명(argType argNmae, ...)[throws exceptionName1, exceptionName2, ...]
    // 여러 개의 예외 클래스 이름을 나열할 수 있다.
    • 이 경우 함수 호출부에 예외 처리 로직을 추가해야한다.
    • throws를 사용하여 예외 처리를 다른 메소드로 전달할 때, 발생된 예외 객체가 RuntimeException의 자식 클래스인 경우 명시적으로 throws 사용하지 않아도 자동으로 호출된다.

❗️호출 부에서 예외를 처리하는 이유

  1. 클라이언트에서 서버1에 있는 메소드 호출
  2. 서버1이 서버2에 있는 객체의 메소드 호출
  3. if 서버2에서 예외가 발생하면 예외를 전달하여 사용자가 보는 곳에서 예외 메세지를 출력해야한다.

사용자 예외 클래스

정의

프로그램에서 필요한 예외 객체를 API에서 제공하지 않을 경우 개발자가 직접 예외 클래스를 정의해서 사용해야 한다.

모든 예외 클래스의 최상위 클래스인 java.lang.Exception클래스를 상속받아 정의한다.

  1. 강제로 예외를 발생시켜야 하는 경우

    • 개발자는 throw예약어를 사용해 강제적으로 예외를 발생시킨다.
    • try-catch로 예외 처리

      throw new 사용자 정의 예외 클래스 명();

java.lang 패키지

  • 가장 기본이 되는 필수 클래스들과 인터페이스들이 포함된 패키지
  • 이 패키지의 클래스나 인터페이스의 경우 import 구문 없이 사용가능

java.lang.Object 클래스

  • 자바 API의 모든 클래스와 사용자가 정의한 모든 클래스의 최상위 클래스
  • 모든 자바 클래스들은 Object 클래스로부터 상속받는다는 의미
  • Object 클래스의 상속은 소스 상에 명시하지 않아도 자동으로 이뤄진다.

equals() method

  • Object 클래스가 제공하는 equals()메소드는 비교 대상이 되는 두 개의 주소 값을 비교하는 것이 아니라 객체의 내용을 비교할 때 사용
    ∵ ==은 기본형 변수에서는 값을 비교하지만 참조형 변수의 경우 주소값을 저장하기 때문에 주소 값을 비교하게 된다.
  • Object 클래스가 제공하는 equals()메소드는 기본적으로 객체의 주소값을 비교하는 == 연산자와 동일하게 작동하므로 개발자가 필요에 따라 적절하게 Overriding을 해줘야 한다.

toString() method

  • 수시로 변하는 객체의 변수 값들을 화면에 간단하게 출력하고자 할 때 사용
  • 객체의 클래스 타입 이름 & 코드 값을 16진수 문자열로 리턴하도록 구현함
  • 개발자가 필요에 따라 적절하게 Overriding을 해줘야 한다.
  • Systme.out.println에서 사용 시 자동으로 호출되기 때문에 호출할 필요 없다.

Wrapper 클래스

  • 자바에서는 8가지 기본 데이터 타입을 객체로 사용할 수 있도록, 기본 데이터 타입에 해당하는 클래스가 제공된다.
  • 사용자의 문자열 입력을 다양한 기본 데이터 타입으로 변환하는 편리한 메소드들을 제공
  • Wrapper 클래스 객체는 한 번 생성되면 그 값이 변할 수 없다.
Wrapper 클래스 기본 데이터 타입
Boolean boolean
Character char
Byte byte
Short short
Integer int
Long long
Float float
Double double

Integer 클래스 주요 메소드

  • parseInt(String, int): 문자열을 주어진 radix의 int형으로 변환
  • toString(): 현재 객체를 String형으로 변환
  • toString(int i):ifmf String형으로 변환

String 클래스

  • 문자열 객체를 생성/처리하기 위해서는 String 클래스를 사용해야한다.
  • 변하지 않는 문자열을 다룰 때 사용하는 클래스
  • 한 번 생성되면 그 내용이 변하지 않는다.

String 클래스 주요 메소드

  • char charAt(int index): index번째 문자 반환
  • String concat(String str): 문자열 결합
  • boolean endsWith(String suffix): 특정 문자열로 끝나는지의 여부를 boolean 값으로 return
  • boolean equals(Object anObject): 2개 문자열 값을 비교하여 true, false 반환
  • boolean equalsIgnoreCase(String antherString): 대소문자 구분 없이 2개의 문자열을 비교하여 true, false 반환
  • byte[]getBytes(): 문자열을 byte 배열로 변환
  • int indexOf(int ch): ch를 유니코드로 하는 문자의 위치를 반환
  • int indexOf(String str): str문자열의 위치 반환
  • int length(): 문자열 길이 반환
  • String[]split(String regex): 지정딘 regex를 중심으로 여러 개의 문자열 배열로 분리
  • boolean startsWith(String prefix): 특정 문자열로 시작하는지 비교
  • String substring(int beginIndex): 특정 위치부터의 문자열만 반환
  • String substring(int beginIndex, int endIndex): beginIndex부터 endIndex까지의 문자열 반환
  • String toLowerCase(): 소문자로 변환
  • String toUpperCase(): 대문자로 변환
  • String trim(): 문자열 양쪽의 공백을 제거

생성 및 특징

String name1 = new String("문자");
String name2 = "문자열";
  • new 연산자를 사용하지 않고 문자열을 저장한 변수는 컴파일 시점에 생성되어 메모리를 할당 받는다.
  • 동일한 문자열을 가진 String이 이미 있다면 같은 문자열이 저장된 주소 값을 재사용
  • ’+‘는 문자열 결합 연산자의 역할을 한다.

StringBuffer 클래스

  • 자바 프로그램 내에서 변하는 문자열을 다룰 때 사용
  • StringBufferz 클래스의 객체는 크기가 동적 👉 객체 생성시 크기를 지정하지 않아도 기본적으로 16개의 문자를 저장할 수 있는 버퍼 공간을 가진다.
  • 한 번 생성된 후에도 계속하여 저장하고 있는 문자열의 내용을 변경할 수 있다.

StringBuffer 클래스 주요 메소드

  • StringBuffer append(String str): 문자열 데이터를 현재 문자열 끜에 추가
  • int capacity(): 현재 문자열의 총 용량
  • char charAt(int index): index위치의 문자 반환
  • StringBuffer delete(int start, int end): start에서 end까지의 문자열 중에서 삭제한 문자열을 반환
  • StringBuffer insert(int offset, String str): offset위치에 문자열 데이터를 삽입
  • int lastIndexOf(String str): str의 최종 인덱스 위치를 반환
  • int lastIndexOf(String str, int fromIndex): fromIndex로부터 시작하여 str의 최종 인덱스 위치를 반환
  • int length(): 문자열 내 문자 개수를 반환
  • StringBuffer replace(int start, int end, String str): start에서 end까지의 문자열을 str문자열로 대체하여 반환
  • String substring(int start): start위치로부터의 일부 문자열을 반환
  • String substring(int start, int end): start에서 end까지 문자열을 반환

StringBuffer클래스의 append()와 String 클래스의 concat() 비교

public class StringBufferTest1{
    public static void main(String args[]){
        String str = "안녕";
        str.concat("하세요");
        System.out.println(str);
        
        StringBuffer strbuff = new StringBuffer("안녕");
        strbuff.append("하세요");
        System.out.println(strbuff);
    }
}

//output
// 
// 안녕하세요

결과가 다른 이유

  • String 클래스는 문자열 상수로, String 클래스가 제공하는 모든 메소드의 실행 결과로 새로운 문자열 객체를 생성하여 return하기 때문
  • StringBuffer는 StringBuffer 클래스의 메소드를 사용했을 때 문자열을 직접 조작한다. - 기존의 문자열을 수정한다.

    • ❗️따라서 StringBuffer클래스는 ’+‘연산자를 사용하여 문자열을 결합할 수 없으며, new 연산자 없이 객체를 생성할 수 없다.

StringBuffer vs String

  • String 클래스는 내부적으로 StringBuffer 객체를 생성한 후에 문자열을 결합한 후 toString()메소드 호출을 통해 새로운 String 객체를 리턴한다. 따라서 StringBuffer로 변환되는 과정이 필요해서 상대적으로 느리다.
  • 반면에 StringBuffer 클래스는 하나의 객체만 유지하므로 중간 변환 과정이 생략된다.
  • 👍 StringBuffer 클래스는 문자열 처리 시 속도가 빠르고 메모리 사용을 덜 하게 되므로 효율적이라 StringBuffer 클래스를 선호한다.
  • 👏 String 클래스는 간단한 문자열을 처리할 때 가독성이 좋고, 손쉽게 문자열을 처리할 수 있다.

Hi! I'm @Yeseul Lee
Passionate for what I love

GitHubLinkedIn