
- 클래스의 기본 형태를 정의하고, 이를 상속받아 구체적인 구현을 제공할 수 있도록 하는 클래스
- 직접적으로 인스턴스화될 수 없으며, 다른 클래스가 이를 상속받아 구현을 완성해야 한다.
- 상속을 통해 공통적인 기능을 제공하면서, 자식 클래스에서 구체적인 동작을 정의할 수 있는 기반을 마련한다.
Abstract Class는 추상 메서드(Abstract Method)를 하나 이상 보유할 수 있다. 추상 메서드는 메서드의 선언만 포함하며, 메서드 본체가 없다.
Abstract Class는 구현된 메서드도 보유할 수 있다. 이러한 일반 메서드는 자식 클래스에서 재정의(오버라이딩)할 필요 없이 그대로 사용할 수 있다.
Abstract Class는 직접 인스턴스로 생성하는 것이 불가능하다. 즉, new 키워드를 사용하여 추상 클래스의 객체를 생성할 수 없다. 추상 클래서는 오직 자식 클래스에서 구현된 다음에 생성하여 사용할 수 있다.
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 + "만큼 낮잠을 잤습니다. 개운합니다.");
}
}
- 클래스들이 필수로 구현해야 하는 추상자료형
- 객체를 생성하기 위한 클래스들이 어떤 내용의 기능을 구현하고, 속성을 가지고 있어야 하는지 지정해놓은 설계도 같은 것
추상 클래스는 필드와 구현된 메소드를 가질 수 있다.
vs 인터페이스는 기본적으로 메서드의 시그니처만 정의한다.
추상 클래스는 단일 상속만 가능하며, 클래스 간의 공통된 동작을 제공하는 데 주로 사용한다.
vs 인터페이스는 다중 상속이 가능하며 이를 통해 클래스에 제공하는 역할에 중점을 둔다.
public interface 인터페이스_이름 {
// 추상 메서드와 상수만 올 수 있다.
}
EX) A interface, B interface 모두 a()라는 메서드를 가짐.
- 클래스가 인터페이스를 구현할 때 쓰는 keyword
- 인터페이스를 구현하는 클래스는 인터페이스에서 정의된 모든 메서드를 구체적으로 구현해야 한다.
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 메서드는 인터페이스와 사용은 가능하지만 재정의는 불가능하다.
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)과는 별개로 정적 영역에 있는 메서드이기 때문에 구현과 상관없이 호출 가능
}
}
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;
}
}
- 연산자이며 왼쪽, 오른쪽이 같은 타입인지 판단해준다.
- 참조형 타입에만 사용할 수 있다. (기본형 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 타입으로 분류되니 이를 참조형 캐스틴 (업캐스팅/다운캐스팅)으로 볼 수 있다.

- 자식 클래스에서 부모 클래 타입으로 형변환
- 캐스팅 연산자 괄호를 생략할 수 있다.
- 단, 부모 클래스로 캐스팅된다는 것은 멤버의 개수 감소를 의미한다. 이는 곧 자식 클래스에서만 있는 속성과 메서드는 실행하지 못한다는 것이다.
- 업캐스팅을 하고 메소드를 실행할 때, 만일 자식 클래스에서 오버라이딩한 메서드가 있을 경우, 부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행된다.
- 부모 클래스에서 자식 클래스 타입으로 형변환
- 캐스팅 연산자 괄호를 생략할 수 없다.
- 다운 캐스팅의 목적은 업캐스팅한 객체를 다시 자식 클래스 타입의 객체로 되돌리는 데에 목적을 둔다. (복구)
public interface Template extends OtherTemplate, AnotherTemplate {
// OtherTemplate, AnotherTemplate도 Interface
void method1();
}
public class ExtendedAndImplementedTemplate extends TheTemplate implements Template {
//public class ExtendedAndImplementedTemplate implements Template {
@Override
public void method1() {
}
@Override
public void run() {
}
}
// 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);
}
duplicatedName 은 static X, 인스턴스 변수이기 때문에 클래스 이름으로 참조하는 것은 불가능하다. (InstanceOuterClass.duplicatedName - X)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 를 그냥 사용하는 것은 불가능하다. constValue 는 InnerClass에서 static 변수이기 때문에 직접 참조가 가능하다. => InnerClass.constValuenew 로 만들어준 후, 사용 가능하다.innerClass.duplicatedName : duplicatedName은 private 변수인데 가능하다 ? -> 이것이 Inner class 이기 때문에 가능하다. 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차 프로젝트를 잘 끝내고 싶다. 어쩌다가 팀장을 맡게 됐는데, 팀원들에게 많이 배우면서 열심히 잘! 소통하는 팀장이 되어야겠다.
팀원들이나 스터디원들이랑 대화해보면 다들 비슷한 것 같다. 조금 헷갈리고, 조금은 어렵기 시작하고, 처음이고,,, 나만 이제 슬 어려워지나? 나만 코테 어렵나.. 나만 스터디 처음인가.. 했는데 절대 아니다 ! 모두 배우려는 마음으로 들어온 곳이니 조바심 내지 말고 남들과 비교하지 말고 모두 파이팅하는 것이 중요할 것 같다. 내 눈에 다른 분들이 다 초고수일 것 같듯이 다른 분들의 눈에도 내가 그렇게 보이겠지.. = 정말 남과 비교는 의미가 없다는 것 !!
암튼 하고자 하는 말은 아.. 이제 슬 내용이 많아지네~ 했는데 오늘도 살아남았다는 것이다 ! 하루하루를 이렇게 해내보자 파이팅 :)