본문 바로가기
Back-End/Java

Java 복습, 예습

by newny 2023. 3. 19.
반응형

클래스 선언

// 클래스 선언
[public] class [클래스명] {
    // 멤버 변수
    [접근제한자] [동적/정적] [타입] [변수명];

    // 상수
    [접근제한자] static final [타입] [상수명];

    // 생성자
    [접근제한자] [클래스명] (매개변수) {}

    // 메소드
    [접근제한자] [동적/정적] [타입] [메소드명] (매개변수) {}
}

접근제한자

클래스 : public, (default)
멤버변수 : public, protected, (default), private
생성자 : public, protected, (default), private
멤버메소드 : public, protected, (default), private
지역변수 : 접근제한자 사용 불가
 

동적, 정적

동적 : 메모리에 올라가 있지 않기 때문에 객체 생성 필요 (default)
static : 객체를 생성하지 않아도 메모리에 올라감
 

싱글톤 패턴

public class [클래스명] {
    private static [클래스명] [싱글톤변수명] = new [클래스명]();
    // 외부에서 객체 생성을 할 수 없기 때문에 내부에 private 접근제한으로 미리 객체 생성

    private [클래스명]() {}
    // 생성자를 직접 선언했으므로 기본 생성자가 생기지 않음
    // 접근제한자를 private로 지정함으로서 외부에서 객체 생성할 수 없음

    public static [클래스명] getInstance() {
        return [싱글톤변수명];
    // 메소드를 이용해서만 객체에 접근 가능
    }
}

 

클래스 상속

자식 객체를 생성하면 부모 객체가 먼저 생성된 후 자식 객체가 생성됨
super()는 작성하지 않아도 컴파일 과정에서 자동 추가됨
→ 만약에 부모 클래스에 기본 생성자가 없다면 자식 생성자 선언에서 컴파일 에러가 발생함
→ 따라서 부모 클래스에 기본 생성자가 없고 매개변수를 갖는 생성자만 있다면 매개변수가 있는 super() 코드를 직접 넣어야 함
 

상속받은 메소드 오버라이딩의 규칙

→ 부모 메소드의 선언부와 동일해야 함
→ 접근 제한을 더 강하게 오버라이딩할 수 없음
→ 새로운 예외를 throws 할 수 없음

public class [자식클래스명] extends [부모클래스명] {
	// 상속 클래스는 무조건 한개만 가능

	public [자식클래스명] () { // 자식 클래스의 생성자
	super();
	// 부모의 기본 생성자를 호출
	// 부모 생성자는 자식 생성자의 맨 첫 줄에 숨겨져있음
	}

	@Override
	[부모메소드의 선언부와 동일하게 선언] { // 부모 클래스 메소드 오버라이딩
		super.[부모메소드명];
		// super 키워드를 이용하여 부모메소드를 재사용
	}
}

 

final 키워드의 사용

final 클래스 → 상속할 수 없음

[접근제한자] final class [클래스명] {}

final 메소드 → 오버라이딩할 수 없음

[접근제한자] [동적/정적] final [리턴타입] [메소드명] (매개변수) {}

 

인터페이스

[public] interface [인터페이스명] {
	(public static final) [타입] [상수명]; // 상수 필드

	(public abstract) [리턴타입] [메소드명] (매개변수); // public 추상메소드 (default)
	(public) default [리턴타입] [메소드명] (매개변수) {실행코드}; // 디폴트 메소드
	(public) static [리턴타입] [메소드명] (매개변수) {실행코드}; // 정적 메소드
	private [동적/정적] [리턴타입] [메소드명] (매개변수) {실행코드}; // private 동적/정적 메소드
}

 

추상메소드(default)

public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙게 됨
실행코드 없음 → 오버라이딩할 구현객체 있어야 함
 

디폴트메소드

public을 생략하더라도 컴파일 과정에서 자동으로 붙게 됨
접근제한자 뒤에 default라는 키워드를 붙여야 함
실행코드 있으나 구현객체가 있어야만 사용가능
 

정적메소드

추상메소드와 디폴트메소드는 구현객체가 필요하지만 정적메소드는 인터페이스만으로 호출 가능함
접근제한자는 public과 private 사용가능 → 접근제한자를 생략하더라도 컴파일 과정에서 public이 붙음 (기본값이 public)
실행코드가 있고 구현객체가 없어도 호출할 수 있음 (static)
static이기 때문에 인터페이스 내부의 동적 메소드 호출 불가
 

private 메소드

동적/정적 둘 다 구현 가능
디폴트와 정적메소드들의 중복 코드를 줄이기 위해 사용
 

자동타입변환

  • 인터페이스 변수에 구현객체를 넣으면 자동 타입변환이 일어남
  • 인터페이스 타입으로 선언되었기 때문에 인터페이스의 메소드만 사용가능
  • 부모클래스가 인터페이스를 구현하고 있다면 자식클래스도 인터페이스 타입으로 자동 타입변환할 수 있음
인터페이스 변수 = 구현객체;

public interface A {} // A 인터페이스
public class B implements A {} // A 인터페이스의 구현클래스 B
public class C extends B {} // B 클래스의 자식클래스 C

A ab = new B(); //인터페이스 자동타입변환
A ac = new C(); //인터페이스 자동타입변환
B bc = new C(); //부모타입 자동타입변환

 

강제타입변환

구현 객체가 인터페이스 타입으로 자동 변환될 경우(매개값을 상위타입으로 받는 경우 등) 인터페이스에 선언된 메소드만 사용 가능하다.
인터페이스 타입으로 자동 타입 변환이 일어난 후 구현 객체만 가지고 있는 메소드(오버라이딩하지 않은 메소드)도 호출하고 싶다면 다시 구현 클래스 타입으로 강제타입변환 해야 한다.

구현클래스 변수 = (구현클래스) 자동타입변환된 인터페이스 변수;

public interface A { // 인터페이스 A
	public void a();
}

public class B implements A { // A 인터페이스의 구현클래스 B
	@Override
	public void a() {} // 오버라이딩 된 a메소드
	public void b() {} // B 클래스 메소드
}

A ab = new B();
ab.a();
// 오버라이딩 된 a메소드만 사용가능
// b메소드 사용 불가

B ba = ab;
ba.a();
ba.b();
// 오버라이딩 된 a메소드와 b메소드 사용가능

 

중첩클래스

멤버 클래스 (A.B)

클래스의 멤버로 선언되는 클래스, 접근제한자에 private 도 가능함
 

인스턴스 멤버 클래스

  • private 접근제한이 일반적임
  • 바깥 클래스의 객체를 생성해야만 사용가능 (a.new B)
    • → A a = new A();
    • → A.B b = a.new B();

 

정적 멤버 클래스

  • default 또는 public 접근제한이 일반적임
  • 객체생성 없이 사용가능 (new A.B)
    • → A.B = new A.B();

 

로컬 클래스

생성자 또는 메소드 내부에 선언되는 클래스
 

멤버변수의 자동초기화, final 키워드

멤버변수는 초기값을 설정해 주지 않으면 그 변수 타입의 초기값으로 자동 초기화 된다. 예를 들면 String과 같이 참조타입 변수는 null로 초기화가 된다. 이 점을 참고해서 아래 예제를 보자.

public class Test {
    final String name1;
    final String name2 = null;

    public Test() {
        name1 = "John Doe";
        name2 = "John Doe"; // 컴파일 에러: final 필드에 다른 값을 할당할 수 없습니다.
    }
}

언뜻 보기엔 변수 name1; 과 변수 name2 = null; 둘 다 null로 초기화되어 있으므로 그 의미가 같아 보인다. 그런데 name1은 생성자로 초기화가 되고, name2는 컴파일 에러가 일어난다.
왜 둘은 차이가 날까?
그 이유는 ‘자동 초기화 되어 null인 상태’와 ‘변수에 = 연산자를 이용하여 null을 대입해 초기값이 null인 상태’는 의미가 다르기 때문이다.
변수 name1은 null로 자동초기화만 되었다는 의미고, 변수 name2는 null로 자동초기화가 되어있는 상태에서 명시적으로 초기값을 null로 지정해 주겠다는 의미이다.
즉, name1 초기값 지정의 기회가 남아있는 상태고, name2는 초기값 지정이 완료되어 더 이상 값을 변경할 수 없는 상태인 것이다.
따라서 final 키워드 사용 시 초기값을 생성자를 통해 주고 싶을 경우엔 어떤 값이던(null 포함) = 연산자로 초기값을 지정하지 않은 상태여야 한다.

반응형

'Back-End > Java' 카테고리의 다른 글

Java(6)  (0) 2023.03.20
Java 예제(3)  (0) 2023.03.17
Java(5)  (0) 2023.03.17

댓글