dev-course day5

2rlokr·2025년 3월 10일

dev-course

목록 보기
5/43

오늘 배운 내용

추상 클래스 (Abstract Class)

  • 클래스의 기본 형태를 정의하고, 이를 상속받아 구체적인 구현을 제공할 수 있도록 하는 클래스
  • 직접적으로 인스턴스화될 수 없으며, 다른 클래스가 이를 상속받아 구현을 완성해야 한다.
  • 상속을 통해 공통적인 기능을 제공하면서, 자식 클래스에서 구체적인 동작을 정의할 수 있는 기반을 마련한다.

추상 메서드 (Abstract Method)

Abstract Class는 추상 메서드(Abstract Method)를 하나 이상 보유할 수 있다. 추상 메서드는 메서드의 선언만 포함하며, 메서드 본체가 없다.

구현된 메서드

Abstract Class는 구현된 메서드도 보유할 수 있다. 이러한 일반 메서드는 자식 클래스에서 재정의(오버라이딩)할 필요 없이 그대로 사용할 수 있다.

인스턴스 생성 불가

Abstract Class는 직접 인스턴스로 생성하는 것이 불가능하다. 즉, new 키워드를 사용하여 추상 클래스의 객체를 생성할 수 없다. 추상 클래서는 오직 자식 클래스에서 구현된 다음에 생성하여 사용할 수 있다.

abstract

public abstract class Animal {
	
    public abstract void yelling();
    
    public void eat(int amount) {
    	System.out.println(amount + "만큼 먹었습니다. 배가 부릅니다.");
        
    }
    
    public void sleep(int hours){
    	System.out.println(hours + "만큼 낮잠을 잤습니다. 개운합니다.");
    }
}
  • 추상 클래스에는 일반 메서드 (구현된 메서드)와 추상 메서드(구현되지 않은 메서드) 모두를 포함할 수 있다.
  • 자식 클래스는 이 추상 메서드를 반드시 구현해야 하며, 그렇지 않으면 자식 클래스 또한 추상 클래스가 되어야 한다.

인터페이스 (Interface)

  • 클래스들이 필수로 구현해야 하는 추상자료형
  • 객체를 생성하기 위한 클래스들이 어떤 내용의 기능을 구현하고, 속성을 가지고 있어야 하는지 지정해놓은 설계도 같은 것

추상 클래스 vs 인터페이스

  1. 추상 클래스는 필드와 구현된 메소드를 가질 수 있다.
    vs 인터페이스는 기본적으로 메서드의 시그니처만 정의한다.

  2. 추상 클래스는 단일 상속만 가능하며, 클래스 간의 공통된 동작을 제공하는 데 주로 사용한다.
    vs 인터페이스는 다중 상속이 가능하며 이를 통해 클래스에 제공하는 역할에 중점을 둔다.

interface

public interface 인터페이스_이름 {
 // 추상 메서드와 상수만 올 수 있다.
}
  • 메서드의 시그니처만을 포함하며, 기본적으로 public과 abstract가 붙어있다고 간주한다. (생략 가능)
  • 인터페이스는 상수만을 가지며, 이 상수들은 자동으로 public, static, final로 선언된다.
  • 인터페이스는 다중 상속이 가능하다.

EX) A interface, B interface 모두 a()라는 메서드를 가짐.

  • C에서 a()를 구현했을 때, A interface 에서 가져온 건지, B interface 에서 구현한 건지 중요하지 않음. 결론적으로 a()를 구현했기 때문 ! => 다중 상속이 가능하게 되는 것임. (다중 구현이 더 정확한 표현이긴 함.)

implements

  • 클래스가 인터페이스를 구현할 때 쓰는 keyword
  • 인터페이스를 구현하는 클래스는 인터페이스에서 정의된 모든 메서드를 구체적으로 구현해야 한다.

default 메서드

default 메서드는 인터페이스 내에서 기본 구현을 제공하며, 이를 구현하는 클래스에서 재정의(Overriding)가 가능하다.

interface InterfaceA {
	default void print() {
    	System.out.println("InterfaceA");
    }
}

interface InterfaceB {
	default void print() {
    	System.out.println("InterfaceB");
    	// default 메서드는 기본 구현이 가능하다. 
    }
}

// 다중 구현이 가능하다
class MyClass implements InterfaceA, InterfaceB {
	
    // 겹치는 메서드는 명시적으로 오버라이드 해야 한다.
    @Override
    public void print() {
    	System.out.println("MyClass's print");
    
    // 특정 인터페이스의 default 메서드를 호출할 수 있다.
    InterfaceA.super.print();
    InterfaceB.super.print();
    }
}

static 메서드

static 영역에 인터페이스와 함께 정의되는 static 메서드는 인터페이스와 사용은 가능하지만 재정의는 불가능하다.

interface InterfaceA {
	static void staticMethod() {
    	System.out.println("InterfaceA");
        // static 메서드도 구현 가능
    }
}

class MyClass implements InterfaceA {
	// static 영역에 있는 메서드는 재정의할 수 없다.
}

public class Main {
	public static void main(String[] args) {
    	// 인터페이스 이름을 통해 static 메서드를 호출할 수 있다.
        InterfaceA.staticMethod();
        // InterfaceA안에 있는 static 영역의 메서드는 구현(Implementation)과는 별개로 정적 영역에 있는 메서드이기 때문에 구현과 상관없이 호출 가능

    }
}

실습

extends & final keyword

  • final class는 상속이 불가능하다.
public class BaseClass {

    private String uniqueName = "BaseClass";
    public String name = "BaseClass";
    protected String nickname = "super";
}

public class SubClass extends BaseClass {

    public SubClass(String name, String nickname) {
        this.name = name;
        this.nickname = nickname;
//        this.uniqueName = nickname; uniqueName은 private로 선언되었기 때문에 불가능하다 !
    }
}
  • 부모 클래스에서의 private 데이터는 자식 클래스에서 접근할 수 없다.

  • final 상수는 보통 대문자로 명명해주며, Snake case를 사용한다.

  • final 상수는 한 번은 할당이 가능하나, 재할당은 불가능하다.

  • 인터페이스에서 매개변수에 final을 붙여주어도, 구현하는 메소드에서 오버라이드를 할 때, final이 붙어서 나오지 않는다. (다시 final을 붙여줘도 되고, 두어도 되고 상관없음)
    - 여기서 final 은 효력이 없기 때문에 오버로드가 아니라 여전히 오버라이딩이다.

  • final 메소드는 오버라이딩 자체가 불가능하다.

상속

public class Bob extends Person {

    public boolean isHandsome;

    public Bob(String name, int age, int height, boolean isHandsome) {
        super(name, age, height);
        this.isHandsome = isHandsome;
    }
}

public class Person {
    public String name;
    public int age;
    public int height;

    public Person(String name, int age, int height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }


}
  • 자식 클래스에서 부모의 생성자를 super를 이용해 호출하고, 이외의 변수를 두어도 된다.

instanceof

  • 연산자이며 왼쪽, 오른쪽이 같은 타입인지 판단해준다.
  • 참조형 타입에만 사용할 수 있다. (기본형 X)

추상 클래스

Phone phone = new Phone(10, 10) {
            @Override
            public void call() {
                System.out.println("따르릉!");
            }

            @Override
            public void msg() {
                System.out.println("문자!");
            }
        }; // 여기서 Phone은 public abstract class
  • 추상 클래스를 바로 구현하면 가능하다. 그냥은 못 쓴다.

형 변환

기본형 타입을 서로 형변환 할 수 있듯이, 자바의 상속 관계에 있는 부모와 자식 클래스 간에도 서로 간의 형변환이 가능하다. 클래스는 Reference 타입으로 분류되니 이를 참조형 캐스틴 (업캐스팅/다운캐스팅)으로 볼 수 있다.

  • 자식 클래스의 객체는 부모 클래스를 상속받고 있기 때문에 부모의 멤버를 모두 가지고 있다. 반면 부모 클래스의 객체는 자식 클래스의 모든 멤버를 가지고 있지 않는다.

업캐스팅 (Upcasting)

  • 자식 클래스에서 부모 클래 타입으로 형변환
  • 캐스팅 연산자 괄호를 생략할 수 있다.
  • 단, 부모 클래스로 캐스팅된다는 것은 멤버의 개수 감소를 의미한다. 이는 곧 자식 클래스에서만 있는 속성과 메서드는 실행하지 못한다는 것이다.
  • 업캐스팅을 하고 메소드를 실행할 때, 만일 자식 클래스에서 오버라이딩한 메서드가 있을 경우, 부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행된다.

다운 캐스팅 (Downcasting)

  • 부모 클래스에서 자식 클래스 타입으로 형변환
  • 캐스팅 연산자 괄호를 생략할 수 없다.
  • 다운 캐스팅의 목적은 업캐스팅한 객체를 다시 자식 클래스 타입의 객체로 되돌리는 데에 목적을 둔다. (복구)

interface 다중 상속

public interface Template extends OtherTemplate, AnotherTemplate {
	// OtherTemplate, AnotherTemplate도 Interface
    void method1();
}
  • 이와 같이 인터페이스는 다중 상속이 가능하다.
  • 만약, MultipleTests라는 class가 Template를 implements한다면 ? => 이게 다중 상속 기능을 내는 건가?

extends 와 implements

 public class ExtendedAndImplementedTemplate extends TheTemplate implements Template {
//public class ExtendedAndImplementedTemplate implements Template {

    @Override
    public void method1() {

    }
    @Override
    public void run() {

    }
}
  • 클래스의 메소드가 인터페이스의 메소드보다 우선한다.
  • TheTemplate는 Template를 구현했다고 생각할 거고, ExtendedAndImplementedTemplate는 TheTemplate를 상속받았다고 생각하는 것이다.

내부 클래스 (Inner Class) & 외부 클래스 (Outer Class)

// Outer Class
public class InstanceOuterClass {

    private final String name = "InstOuterClass";
    protected final String protectedValue = "protectedValue";
    public final Integer sharedValue = 100;
    String defaultValue = "defaultValue";

    private final String duplicatedName = "duplicatedName1";

    // Inner Class
    // 인스턴스 클래스 - 외부 클래스의 인스턴스 멤버 변수처럼 다룬다.
    class InnerClass {

        public static final String constValue = "CONST";
        public final Integer sharedValue = 90;
        private final String duplicatedName = "duplicatedName2";

		// 내부 클래스에서 외부 클래스 접근이 가능한가?
        public void printOuterMemberVariables() {
            System.out.println("name = " + name); // private라도 접근 가능함.
            System.out.println("protectedValue = " + protectedValue);
            System.out.println("sharedValue = " + sharedValue);
            System.out.println("defaultValue = " + defaultValue);
//            System.out.println("duplicatedName = " + duplicatedName); //this가 자동으로 붙기 때문에 2가 출력됨.
        }

        public void printDuplicatedValue() {
            String innerVariable = this.duplicatedName;
            String outerValue = InstanceOuterClass.this.duplicatedName; //duplicatedName -> 인스턴스 변수 그래서 클래스 이름으로 참조하는 것은 불가능함.
            // 인스턴스 변수가 안에 존재하기 때문에 InstanceOuterClass의 ! 를 this가 의미해줌. this keyword !

            System.out.println("innerVariable = " + innerVariable);
            System.out.println("outerValue = " + outerValue);
        }
    }
    
    public void printInnerClassVariable() {
//        System.out.println("constValue = " + constValue);
        
        System.out.println("InnerClass.constValue = " + InnerClass.constValue); // static 이라서 직접 참조가 가능함.

        InnerClass innerClass = new InnerClass();

        String innerStringValue = innerClass.duplicatedName; // private인데? 가능하다 ! 이것이 inner class기 때문에 가능하다.
        System.out.println("innerStringValue = " + innerStringValue);
        System.out.println("innerClass.sharedValue = " + innerClass.sharedValue);
    }
    
}
  • 이너 클래스는 인스턴스 클래스 -> 외부 클래스의 인스턴스 멤버 변수처럼 다룬다.

내부 클래스에서 외부 클래스 접근이 가능한가?

YES

// 내부 클래스에서 외부 클래스 접근이 가능한가?
public void printOuterMemberVariables() {
	System.out.println("name = " + name); // private라도 접근 가능함.
    System.out.println("protectedValue = " + protectedValue);
    System.out.println("sharedValue = " + sharedValue);
    System.out.println("defaultValue = " + defaultValue);
	//  System.out.println("duplicatedName = " + duplicatedName); 
	//this가 자동으로 붙기 때문에 2가 출력됨.
        }
  • private라도 접근이 가능하다.
  • static 상관없이 가능하다.
  • 만약, 이름이 같은 변수가 있다면 ? -> this가 자동으로 붙기 때문에 Inner Class의 변수를 가리킴.
  • printOuterMemberVariables 참고

그렇다면 내부, 외부 클래스 변수명이 같을 때는?

public void printDuplicatedValue() {
	String innerVariable = this.duplicatedName;
    String outerValue = InstanceOuterClass.this.duplicatedName; //duplicatedName -> 인스턴스 변수 그래서 클래스 이름으로 참조하는 것은 불가능함.
    // 인스턴스 변수가 안에 존재하기 때문에 InstanceOuterClass의 ! 를 this가 의미해줌. this keyword !

    System.out.println("innerVariable = " + innerVariable);
    System.out.println("outerValue = " + outerValue);
}
  • duplicatedNamestatic X, 인스턴스 변수이기 때문에 클래스 이름으로 참조하는 것은 불가능하다. (InstanceOuterClass.duplicatedName - X)
  • 인스턴스 변수가 안에 존재하기 때문에 'InstanceOuterClass'를 this가 의미해준다.

외부 클래스에서 내부 클래스 변수에 접근하고 싶다면?

public void printInnerClassVariable() {

//        System.out.println("constValue = " constValue); 
        
        System.out.println("InnerClass.constValue = " + InnerClass.constValue); // static 이라서 직접 참조가 가능함.

        InnerClass innerClass = new InnerClass();

        String innerStringValue = innerClass.duplicatedName; // private인데? 가능하다 ! 이것이 inner class기 때문에 가능하다.
        System.out.println("innerStringValue = " + innerStringValue);
        System.out.println("innerClass.sharedValue = " + innerClass.sharedValue);
}
  • constValue 를 그냥 사용하는 것은 불가능하다.
  • constValueInnerClass에서 static 변수이기 때문에 직접 참조가 가능하다. => InnerClass.constValue
  • 다른 인스턴스 변수들은 객체를 new 로 만들어준 후, 사용 가능하다.
  • innerClass.duplicatedName : duplicatedName은 private 변수인데 가능하다 ? -> 이것이 Inner class 이기 때문에 가능하다.

InstanceOuterClass 안의 내부 클래스인 InnerClass도 직접 생성하는 것이 가능할까?

YES,InnerClass도 Class이기 때문에 생성자 호출이 가능하다.

public class InstanceOuterClassTest {
    public static void main(String[] args) {
    
        InstanceOuterClass instOuterCls = new InstanceOuterClass();
        instOuterCls.printInnerClassVariable();

        InstanceOuterClass.InnerClass innerClass = instOuterCls.new InnerClass();
        innerClass.printOuterMemberVariables();

        InstanceOuterClass.InnerClass innerClass2 = new InstanceOuterClass().new InnerClass();
        innerClass2.printOuterMemberVariables();
    }
}
  • instOuterCls.new InnerClass(); : new 라는 키워드를 반드시 사용해야 하며, 자료형은 InstanceOuterClass.InnerClass로 표현해야 한다.
  • new InstanceOuterClass().new InnerClass()

내부 클래스에서의 메모리 누수

public class InstanceOuterClassMemoryLeakTest {

    private String name = "MemoryLeakTest";
    class InnerClass {
        void printName() {
            System.out.println(name);
        }
    }

    public static void main(String[] args) {
        InstanceOuterClassMemoryLeakTest instance = new InstanceOuterClassMemoryLeakTest();
        InnerClass innerClass = instance.new InnerClass();

        instance = null;
        innerClass.printName(); // nullException이 떠야 하는데, 안 뜸 => 메모리 누수 발생
        // printName에서 name을 사용하고 있기 때문에 메모리에서 해제 되지 않았다. 그래서 메모리 누수 발생함.
        // => inner class를 사용할 땐 static을 사용해주는 게 좋다.
    }
}
  • 외부 클래스를 null 로 바꿨다면, 이를 참조하는 printName()함수를 호출했을 때, NullPointException이 떠야 한다. 하지만, 안 뜬다.
  • printName()함수에서 name이라는 변수를 사용하고 있기 때문에, 메모리에서 해제하지 않았다.
    -> 강한 참조 때문
    => 메모리 누수 발생 !
    => 내부 클래스를 사용할 땐 static을 사용해주는 것이 좋다.

오늘 어려웠던 것

내부 클래스 나오는데 헷갈리기 시작했다. 수업을 듣을 때는 다 이해가 되는데, 파워 N으로서 누군가 나에게 내부 클래스, 외부 클래스에 대해 질문한다고 생각해보자.. 나 100% 정확하게 대답할 수 있을까? .. ㅎ 앱솔루틀리 노우.... 였다. 그래서!! 오늘은 TIL 작성하는 데에 조금 오래 걸렸다. 한 번 더 실습했던 걸 복기하면서 다시 머리에 넣었다 !

나를 칭찬해요 ~

오늘 실습한 건 100% 내 것으로 만들진 못 한 것 같아서 실습 코드를 하나하나 보면서 다시 복습했다 ! 그리고, 저녁 6시 이후에 운동도 하고, 쉬기도 쉬고, 공부도 하고, To-do lists를 하나씩 체크하고 있다 !

첫 팀 미팅 소감

오늘 첫 팀미팅을 했다 ! 팀원들과 어색..했지만 이 또한 지나가리라 ㅎㅋ 언젠간 편하게 말을 하겠지 ! 하나 하나 할 것들을 같이 해냈고, 팀원들과 열심히해서 1차 프로젝트를 잘 끝내고 싶다. 어쩌다가 팀장을 맡게 됐는데, 팀원들에게 많이 배우면서 열심히 잘! 소통하는 팀장이 되어야겠다.

느낀점

팀원들이나 스터디원들이랑 대화해보면 다들 비슷한 것 같다. 조금 헷갈리고, 조금은 어렵기 시작하고, 처음이고,,, 나만 이제 슬 어려워지나? 나만 코테 어렵나.. 나만 스터디 처음인가.. 했는데 절대 아니다 ! 모두 배우려는 마음으로 들어온 곳이니 조바심 내지 말고 남들과 비교하지 말고 모두 파이팅하는 것이 중요할 것 같다. 내 눈에 다른 분들이 다 초고수일 것 같듯이 다른 분들의 눈에도 내가 그렇게 보이겠지.. = 정말 남과 비교는 의미가 없다는 것 !!
암튼 하고자 하는 말은 아.. 이제 슬 내용이 많아지네~ 했는데 오늘도 살아남았다는 것이다 ! 하루하루를 이렇게 해내보자 파이팅 :)

0개의 댓글