[자바] 4. 클래스와 인터페이스

개발자 춘식이·2023년 7월 22일
0

Java

목록 보기
4/8
post-thumbnail

도서 자바의신, 이것이 자바다를 참고하였습니다.

1. 패키지

패키지는 수많은 클래스를 분류하기 위해 사용됩니다. 패키지는 소스의 가장 첫 줄에 있어야 하며, 패키지 선언은 한 번만 해야 합니다.

package 상위패키지.하위패키지;

public class classA {
	...
}

패키지 이름과 위치한 폴더 이름이 같아야하는데, 그렇지 않다면 컴파일이 되지 않습니다. 또한 패키지 이름을 java로 시작하면 안됩니다. 자바 표준 API에서만 사용하기 때문입니다. 그럴 경우 java.lang.SecurityException "Prohibited package name:java" 라는 에러가 발생하게 됩니다.

1) 패키지명 규칙

패키지 이름은 모두 소문자로 작성하는 것이 관례이며, 자바 예약어를 사용하면 안됩니다. 숫자로 시작하면 안되고, _$를 제외한 특수 문자 사용이 안됩니다. 주로 com.회사명.프로젝트명으로 작성합니다.

패키지 시작 이름 내용
java 자바 기반 패키지 (Java 벤더에서 개발)
javax 자바 확장 패키지 (Java 벤더에서 개발)
org 비영리단체(오픈소스)의 패키지
com 영리단체(회사)의 패키지

2) 패키지 선언이 포함된 클래스 컴파일

javac -d .           ClassName.java  <- 현재 폴더 내에서 생성
javac -d ..\bin      ClassName.java  <- 현재 폴더와 같은 위치의 bin 폴더에 생성
javac -d C:\Temp\bin ClassName.java  <- C:\Temp\bin 폴더에 생성

2. 접근 제어자

  • public : 누구나 접근 가능합니다. 소스 파일의 이름은 public인 클래스 이름과 동일해야 합니다.
  • protected : 같은 패키지 혹은 상속받은 관계에서 접근 가능합니다.
  • (default) : package-private라고도 하며 같은 패키지에서만 접근 가능합니다.
  • private : 같은 클래스에서만 접근 가능합니다.

3. 상속

class 자식클래스 extends 부모클래스 {...}

자바에서의 상속은 단일 상속만 가능합니다. 상속을 받으면 부모 클래스에 있는 publicprotected로 선언된 모든 메소드나 변수를 자식 클래스에서 사용 가능합니다. 단, 접근 제어자가 없거나 private은 상속 대상에서 제외됩니다. 자식 클래스의 생성자를 호출하면, 부모 클래스의 기본 생성자가 실행됩니다.

1) 상속과 생성자

  • 부모 클래스에 기본 생성자가 없다면? 괜찮습니다.
  • 그런데, 매개변수가 있는 생성자 있다면? 부모 클래스에 기본 생성자가 없다는 컴파일 에러 부모 클래스에 기본 생성자가 없다는 컴파일 에러가 발생합니다.
    부모 클래스의 상속을 받는 자식 클래스의 모든 생성자가 실행될 때, 개발자가 따로 지정하지 않아도 컴파일러가 자식 클래스를 컴파일할 때 자동으로 super()라는 문장이 들어가게 됩니다.
    super()는 부모 클래스의 생성자를 호출한다는 의미로, super()는 반드시 자식 클래스의 생성자에서 가장 첫줄에 선언되어야 합니다.
  • 매개 변수를 갖는 생성자가 여러개거나, 혹은 null을 넘겨준다면? 부모 클래스로의 참조가 매우 모호하다는 컴파일 에러가 발생합니다. super()를 사용하여 생성자를 호출할 때에는 모호하게 null을 넘기는 것보다는 호출하고자 하는 생성자의 기본 타입을 넘겨주는 것이 좋습니다.
  • 자식 클래스에서 부모 클래스와 같은 생성자를 만들면, 그 매개변수로 하는 부모 클래스의 생성자를 호출하나? 아닙니다. 자바는 부모의 기본 생성자를 찾는 것이 기본입니다. super()를 이용해서 부모 생성자를 꼭 호출해야 합니다.

4. 메소드 오버라이딩(Overriding)

오버라이딩은 부모 클래스에서 정의한 메소드를 자식 클래스에서 재정의해서 사용하고 싶을 경우 사용됩니다. 오버라이딩은 부모 클래스와 동일한 시그니처(메소드명, 매개 변수의 타입과 개수)와 동일한 리턴 타입을 갖는 자식 클래스의 메소드가 존재할 때 성립됩니다. 접근 제어자는 달라도 되지만, 확장되는 경우에만 허용됩니다. 축소될 경우에는 컴파일 에러가 발생합니다.
ex) private -> pulic ⭕️, public -> private ❌

오버로딩: 확장 (메소드의 매개 변수들을 확장)
오버라이딩: 재정의 (부모 클래스의 메소드 시그니처를 복제해서 자식 클래스에서 재정의)


5. 참조 자료형의 형변환

ParentClass parentClass = new ChildClass(); //⭕️ UpCasting

ChildClass childClass = new ParentClass();  //❌
//Incompatible types. Found: 'com.test.inheritance.ParentClass', required: 'com.test.inheritance.ChildClass'

부모 클래스는 자식 클래스에 있는 모든 메소드와 변수 사용 불가능합니다. 컴파일러에서는 자식 객체를 생성할 때 부모 생성자를 사용하면 안된다고 위와 같이 컴파일 에러를 발생시킵니다. 따라서 명시적으로 형 변환(casting)을 한다고 알려줘야 합니다.

ChildClass childClass = (ChildClass) new ParentClass(); //DownCasting

이 경우에는 컴파일 에러는 발생하지 않지만, ClassCastException이 발생합니다.

참고로, 기본 자료형의 경우 데이터의 범위가 넓어져도 값이 바뀌지 않기 때문에(int -> long) 형변환을 굳이 명시적으로 하지 않아도 자동 형 변환이 됩니다. 하지만 long -> int로 형 변환을 하면 값이 바뀔 수도 있기 때문에 명시적으로 형 변환을 해야 합니다. 이 때, 값이 동일하다는 보장은 전혀 없습니다.


6. instanceof

객체 instanceof 클래스(타입)

객체의 타입을 확인할 때 사용됩니다. 만약 객체가 해당 클래스의 타입이라면 true값을 리턴받습니다. 주의할 점은 부모 타입도 true를 리턴하기 때문에, 가장 하위에 있는 자식 타입부터 확인해야한다는 것입니다.


7. 다형성(Polymorphism, 多形性)

선언 시 모두 Parent로 선언해도 실제로 호출되는 메소드는 생성자를 사용한 클래스에 있는 것이 호출됩니다. 각 객체의 실제 타입은 다르기 때문입니다.
즉, 형변환을 하더라도 실제 호출되는 것은 원래 객체에 있는 메소드가 호출되는 것을 다형성이라고 합니다.


8. 인터페이스(Interface)

public interface 인터페이스A {
	몸통없는메소드();
}

public class 구현클래스 implements 인터페이스A, 인터페이스B {
	//인터페이스에 정의된 모든 메소드 구현...
}

인터페이스와 추상 클래스를 사용하는 이유는 무엇일까요?
우선 설계 시 선언해 두면 개발할 때 기능을 구현하는 데에만 집중이 가능하고, 개발자의 역량에 따른 메소드명과 매개변수 선언의 격차가 완화됩니다. 공통적인 인터페이스와 추상 클래스를 선언해 놓으면, 선언과 구현 구분할 수 있습니다.
인터페이스는 상속이 아니라, implements 키워드를 사용함으로써 해당 클레스에서 구현해야 하는 인터페이스들을 정의함으로써 클래스에 짐을 지어주고, 인터페이스에 정의된 모든 메소드를 구현해야 컴파일이 됩니다.
객체를 생성할 때는 인터페이스 변수명 = new 구현클래스();로 선언해주면 되며, 단일 상속을 해야 하는 클래스와는 달리 여러 인터페이스를 구현할 수 있습니다.


9. 추상(abstract) 클래스

public abstract classA {
	public abstract void methodA(); //추상 메서드
    
    public String test() {
    	//몸통 있는 메서드 사용 가능
    }
}

추상 클래스는 자바에서 마음대로 초기화하고 실행 불가능합니다. 단, 추상 클래스를 구현한 클래스로는 초기화와 실행 가능합니다.
추상 클래스는 abstract으로 선언된 메소드가 0개 이상이면 되고, 만약 1개라도 있으면 그 클래스는 반드시 abstract으로 선언되어야 합니다. 또한 몸통있는 메소드가 있어도 상관 없으며 static이나 final 메소드도 상관 없습니다.(단, 인터페이스는 안됩니다.)
어떤 메소드들은 미리 구현해놔도 상관없는데, 그렇다고 클래스를 만들자니 애매한 경우 사용됩니다.


10. final

final 키워드는 어디에 선언하느냐에 따라 그 용도가 다릅니다.

  • 클래스(final class ClassA {...}) : 상속 불가 -> 더 이상 확장해서는 안되는 클래스로, 누군가 이 클래스를 상속받아서 내용 변경해서는 안될 때 사용합니다.
  • 메소드(final void methodA() {...}) : Overriding 불가
  • 변수(final int variableA = 1;) : 더 이상 바꿀 수 없음 -> 인스턴스 변수나 static으로 선언된 클래스 변수는 변수 생성과 동시에 초기화해야 합니다.

클래스가 final이라 해서 그 안에 있는 인스턴스 변수나 클래스 변수가 final일 필요는 없습니다.

final MemberDTO dto = new MemberDTO();
dto = new MemberDTO(); 	//❌
dto.name = "Java";		//⭕️ MemberDTO 안에 있는 name 변수는 final이 아님.

클래스, 인터페이스, 추상 클래스를 표로 정리하면 다음과 같습니다.
interface, abstract class, class


11. Enum 클래스(=열거형 클래스)

enumeration : n. 셈, 계산, 열거, 목록, 열람표

public enum EnumA (extends java.lang.Enum){
	MON, TUE, WED
}

public enum EnumB {
	RED(2), BLUE(350), GREEN(6) //상수 값 지정 가능하나 값 동적 할당은 불가능
}

Enum 클래스는 JDK 1.5부터 추가된 클래스로, 상수의 집합 클래스를 의미합니다. 별도로 타입이나 값을 지정할 필요 없고, 상수들을 쉼표로 나열하면 됩니다. 만약 상수들만 선언할 경우 세미콜론도 필요 없습니다. 각각의 상수들은 public static final입니다.

Enum 클래스에 지정된 상수를 사용하려면 EnumA.MON처럼 enum클래스명.상수명으로 지정함으로써 객체 생성이 완료됩니다. Enum 클래스에 생성자를 만들 수는 잇지만 생성자를 통한 객체 생성은 불가능하며, 각 상수를 Enum 클래스 내에서 선언할 때만 생성자 사용이 가능합니다. 생성자의 접근제어자는 package-privateprivate만 사용 가능합니다.

Enum 클래스는 무조건 java.lang.Enum클래스를 상속받는데, 컴파일러가 알아서 protected Enum(String name, int ordinal)생성자를 호출해줍니다. 여기서 ordinal은 enum 상수의 순서이며, 선언된 순서대로 0부터 증가합니다. 만약 클래스 내 같은 키워드가 있으면 컴파일 에러를 발생시킵니다.

1) Enum 클래스 메소드

  • int compareTo(E e) : 매개변수 e와의 순서(ordinal) 차이를 리턴함. e가 더 앞에 있으면 양수, 뒤면 음수, 같으면 0 리턴함.
  • Class<E> getDeclaringClass() : 클래스 타입의 enum 리턴함.
  • String name() : 상수 이름을 리턴함.
  • int ordinal() : 상수 순서를 리턴함.
  • static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
  • values() : API에는 없는 메소드로, enum 클래스에 선언되어 있는 모든 상수를 배열로 리턴함.

지속해 수정해 나갈 예정입니다.
2023-07-08 v1.0
2023-07-22 v1.1 - 인터페이스, 추상클래스, enum 내용 추가함

profile
춘식이를 너무 좋아하는 주니어 백엔드 개발자입니다.

0개의 댓글