1. 상속
1) 개념
- 부모클래스의 멤버 변수, 멤버 메서드를 자식 클래스가 상속 받아 사용
- 클래스 재사용 => 중복 코드 줄여서 효율성 증대
2) 특징
- '자식클래스 extends 부모클래스' 형식으로 상속
- 한 번에 하나의 부모 클래스만 상속 받을 수 있음.
=> C extends A, B (x)
=> C extends B , B extends A (o)
- private 접근 제한을 갖는 멤버 변수 및 메서드는 직접 사용이 불가
=> 그럼 어떻게 사용하냐? getter, setter 사용
3) 상속 관련 키워드
- extends 상속 받을 때 클래스명 옆에 사용
- super 자식 클래스에서 부모를 명시적으로 사용하고자 할 때 사용
=> 부모의 생성자, 메서드, 멤버 변수를 지정 가능
- this 자신의 클래스에 선언된 멤버 변수, 메서드 지칭
2. 생성자
1) 개념
- 인스턴스가 호출 될 때 가장 먼저 호출되는 메서드
- 멤버변수 값을 초기화
2) 특징
- 인스턴스 생성시 초기값 지정
- 자바에서 생성자는 클래스의 이름과 같다
- 생성자는 인자를 달리하여 여러 개 가질 수 있다
- 상속 관계에 있을 때 생성자는 부모의 생성자 먼저 호출
(부모의 생성자를 명시적으로 호출하지 않은 경우, 묵시적으로 부모의 인자 없는 생성자 호출)
- 부모의 생성자를 명시적으로 호출하기 위해 super 키워드 사용.
- super.getX(), super.a
[예시코드 1] 상속 기본 예제
class Subject1 {
protected int a = 1000;
public int fun1() {
return a;
}
}
class Subject2 extends Subject1 {
private int b = 5;
public int fun2() {
return a/b;
}
}
class Example {
public static void main(String[] args) {
Subject2 sub = new Subject2();
System.out.println( sub.fun1() );
System.out.println( sub.fun2() );
}
}
1000
200
코드는 main 영역에서부터 읽는다
현재 Subject2 가 Subject1을 상속받은 상황이다.
main 영역에서 Subject2 타입의 인스턴스 sub을 생성하고 Subject2의 생성자를 태웠다.
Subject2 클래스에서는 super키워드를 찾아 볼 수 없기 때문에 부모의 생성자를 명시적 호출 하고 있지 않다.
일단 부모 클래스에 먼저 올라가서 멤버변수 a를 할당하고 1000 값을 가진다
또한 Subject2 에서는 private한 정수 변수 b를 선언하고 5값을 집어 넣는다
이후 sub 인스턴스가 가진 fun1를 호출하면 자식은 fun1 메서드를 가지고 있지 않기 때문에
부모 클래스의 fun1 메서드가 수행되어 그 공간에 있는 a값 1000이 리턴되고,
이후 내가 가진 fun2 메서드를 수행하게 되는데 이 때 a값은 부모의 멤버변수를 참조해서 1000/5를 수행하여 200 리턴
[예시코드 2] 변수 이름이 동일한 상황 (변수의 유효 범위)
class Parent {
public int a = 1000;
public int fun1() {
return a;
}
}
class Child extends Parent {
public int a = 500;
public int fun2() {
return a;
}
}
class Example {
public static void main(String[] args) {
Child c1 = new Child();
System.out.print( c1.fun1() + "," );
System.out.print( c1.fun2() + "," );
c1.a = 300;
System.out.print( c1.fun1() + "," );
System.out.print( c1.fun2() );
Parent p2 = new Child();
p2.a = 700;
System.out.print(p2.a + "," );
System.out.print(c1.a);
}
}
1000, 500, 1000, 3000
700, 300
Child라는 클래스가 Parent 클래스를 상속 받고 있는 상황
여기서 부모의 멤버변수도 a 이고 자식의 멤버변수도 a
서로 이름이 동일한 멤버 변수를 가질 때 main 영역에서 호출되는 a가 어떤 값을 가리키는지 알아 보는 문제
main 영역에 2개의 인스턴스 생성
Child c1 = new Child();
변수의 타입도 Child, 객체의 클래스도 Child();
이 코드는 "Child" 클래스의 객체를 생성하여 "Child" 타입의 변수 c1 에 할당하겠다는 뜻
반면 Parent 타입 변수 p2 => 부모가 날 낳은 형태로
"Child" 클래스의 객체를 생성했으나 "Parent" 타입의 변수 p2에 할당하겠다는 뜻
c1 이 생성되면 자식이 상속받고 있는 부모의 클래스로 먼저 올라가 부모의 멤버 변수를 생성해 준다
P.a 1000
C.a 500
인스턴스는 이 두개의 변수를 가지고
c1. fun1() 과 fun2() 의 메서드를 수행하여 1000, 500 을 리턴하게 된다.
이 문제에서 메서드명은 서로 다르기 때문에 오버라이딩이 문제되지는 않는다.
그냥 자식한테 없는 메서드는 가장 가까운 부모에게 올라가서 수행하면 된다.
그리고 c1.a 에 300을 대입해주는데 여기서 가리키는 a는 나의 a이다
어떻게 알 수 있는가?
Child c1 = new Child(); 에서 앞에 있는 타입 따라간다.
그래서 Child 가 가진 500에 300값을 넣어주는 것
반면 p2.a = 700 이라는 코드는 부모의 멤버변수 a를 가리킨다.
왜? Parent p2 = new Child(); 라서...
앞에 Parent 타입으로 선언된 변수임. 뒤에 Child 있어도 아무 의미 없다.
[예시코드 3] 부모, 자식의 메서드명이 동일- 메서드 오버라이딩 @Override
실제로 코딩을 하게 되면 보통은 오버라이드 키워드 붙여 주는데 여기서는 그냥 코드 써 보겠다
또한 메서드 오버라이딩과 비교되는 개념인 오버로딩, 하이딩 개념은 다음 포스팅에서 작성하겠음
아래 코드는 Animal 클래스를 상속받은 Cat 에서 displayInfo() 메서드를 호출한 경우
Animal, Cat 이 해당 메서드를 둘 다 가지고 있을 때 누구의 메서드가 수행되느냐 하는 문제
class Animal {
public String name = "Animal";
public int legCount = 4;
public void displayInfo() {
System.out.println("Name: " + name + ", Legs: " + legCount);
}
}
class Cat extends Animal {
public String name = "Cat";
public void displayInfo() {
System.out.println("Cat's name:" + this.name);
System.out.println("Animal's name: " + super.name + ", Legs: " + this.legCount
}
}
class Example {
public static void main(String[] args) {
Cat cat - new Cat();
cat.displayInfo();
}
}
Cat's name: Cat
Animal's name: Animal, Legs: 4
이 문제는 메서드 오버라이딩 문제인데
아버지와 내가 동일한 이름과 매개변수의 메서드를 가질 때 문제됨
오버라이딩 = 재정의(즉 재정의된 자식의 메세드 메서드가 수행됨)
오버로딩 = 중복정의(기능은 비슷한데 매개변수 타입이나 갯수만 다른 메서드)
아버지가 날 낳은 형태든, 자식이 날 낳은 형태든,
중요한 건 메서드 오버라이딩을 하면
아버지처럼 살기 싫어요!! 만 기억하면 된다.
즉, 자식이 가진 메서드가 수행된다
Cat a = new Cat();
Animal b = new Cat(); 이든 모두 메서드 오버라이딩이면 자식 메서드가 수행
해당 문제에서 this키워드는 자식 본인이 가진 name을 말함
영어로는 self 개념으로
this.name
this = self 나, 내가
. = . 은 클래스나 어떤 공간에 '접근하여' 또는 어떤 클래스가 '가진' 이라는 뜻
name = 변수명
[예시코드 4] 아버지가 날 낳은 형태에서 자식의 메서드 사용 불가
아래 코드는 아버지가 날 낳은 형태에서
아버지에게는 없는데 자식만이 가진 메서드를 활용할 수 있는가 문제
답은 X
class Parent {
protected int a = 1000;
public int fun1() {
return a;
}
}
class Child extends Parent {
private int b = 5;
public int fun2() {
return a/b;
}
}
class Example {
public static void main(String[] args) {
Child c1 = new Child();
Parent p2 = new Child();
System.out.println( c1.fun1() ); // 1
System.out.println( c1.fun2() ); // 2
System.out.println( p1.fun1() ); // 3
System.out.println( p1.fun2() ); // 4
}
}
아래 1-4 중 오류 나는 것은 4번
왜 ? Parent p1 = new Child();
아버지가 자식을 낳았기 때문에 자식만 가지고 있는 메서드는 아버지가 사용 불가
반대로 자식은 아버지의 메서드, 나의 메서드 모두 사용 가능
즉, 자식이 가지지 않은 메서드도 아버지가 가졌다면 사용 가능...
에러나는 이유
1. 타입 제한
p1 은 Parent 클래스타입의 변수. 이 변수는 Child의 객체를 참조하고 있지만 변수의 타입이 Parent이기 때문에 Parent 클래스에 정의된 객체만 사용 가능, 따라서 fun2()는 Child 클래스에만 정의되어 있으므로 p1을 통해 호출 불가
2. 컴파일 타임 체크
컴파일러는 p1의 타입이 Parent이기 때문에 fun2() 메서드 찾을 수 없고 따라서 오류 남
이러한 경우 fun2()사용하려면?
p1을 Child타입으로 캐스팅해야 함
Child c1 = (Child) p1;
c1.fun2();
이렇게 하면 가능
왜?
실제로 p1이 Child타입의 객체를 참조하고 있기 때문에
하지만 캐스팅 할 때는 항상 해당 객체가 실제로 Child타입인지 확인해야 함
그렇지 않으면? ClassCastException 발생할 수 있음.
[예시코드 5] 부모의 인자 없는 생성자 호출
class A {
int a;
A() {
this.a = 1;
}
A(int a) {
this.a = a;
}
void display() {
System.out.println("a="+a);
}
}
class B extends A {
B(int a) {
super.display();
}
}
public class Main {
public static void main(String[] args) {
B obj = new B(10);
}
}
a=1
B는 A를 상속 받음
Main 에서 B 타입 변수에 B 를 참조하는 객체 생성하였고 10을 매개변수로 전달
부모의 멤버변수 int a 생성
자식의 멤버변수는 없음
이 때 자식의 생성자를 태웠어도 부모의 생성자 먼저 수행해야 함.
묵시적으로 부모를 호출 안 한 경우 부모의 인자 없는 생성자를 호출한다
현재 B(int a) 안에 super.display() 가 있다고 해서 부모의 생성자를 호출했다고 착각하면 안됨
이건 부모의 메서드를 호출한 것이지 생성자를 호출한 게 아님
그래서 결국 부모의 인자 없는생성자인 A() 가 호출된 후, 자식의 인자 있는 생성자가 호출되어 인자값 10으로 연산이 이루어짐
즉, A() 먼저 호출되어 this.a에 1이 담긴 후, B()의 생성자가 호출되며
System.out.println("a="+a);가 생성됨
여기서의 a는 부모의 a를 말한다. 그래서 a=1 값이 출력됨.
왜? 부모의 메서드가 수행되는 공간의 a는 부모의 a이기 때문..
자식의 인자 있는 생성자에 매개변수로 들어간 a를 말하는게 아님<<<
a=10 이라 생각하면 틀리는 거..
'정보처리기사' 카테고리의 다른 글
[정보처리기사] 디스크 스케줄링(FCFS, SSTF, SCAN, CSAN, LOOK, C-LOOK) & RAID (1) | 2025.03.16 |
---|---|
[Java] 메서드 오버로딩 / 오버라이딩 / 하이딩 (2) | 2024.10.23 |
[Java] 자바의 예외처리 키워드, 예시, 종류 정리 (1) | 2024.10.18 |
[Java] 정보처리기사 추상클래스 / 인터페이스 정리 (오류 발생 문제 풀이) (3) | 2024.10.18 |
[Python] 파이썬 학습노트3 - 문제풀이, 슬라이싱, lambda(람다) (3) | 2024.10.16 |
1. 상속
1) 개념
- 부모클래스의 멤버 변수, 멤버 메서드를 자식 클래스가 상속 받아 사용
- 클래스 재사용 => 중복 코드 줄여서 효율성 증대
2) 특징
- '자식클래스 extends 부모클래스' 형식으로 상속
- 한 번에 하나의 부모 클래스만 상속 받을 수 있음.
=> C extends A, B (x)
=> C extends B , B extends A (o)
- private 접근 제한을 갖는 멤버 변수 및 메서드는 직접 사용이 불가
=> 그럼 어떻게 사용하냐? getter, setter 사용
3) 상속 관련 키워드
- extends 상속 받을 때 클래스명 옆에 사용
- super 자식 클래스에서 부모를 명시적으로 사용하고자 할 때 사용
=> 부모의 생성자, 메서드, 멤버 변수를 지정 가능
- this 자신의 클래스에 선언된 멤버 변수, 메서드 지칭
2. 생성자
1) 개념
- 인스턴스가 호출 될 때 가장 먼저 호출되는 메서드
- 멤버변수 값을 초기화
2) 특징
- 인스턴스 생성시 초기값 지정
- 자바에서 생성자는 클래스의 이름과 같다
- 생성자는 인자를 달리하여 여러 개 가질 수 있다
- 상속 관계에 있을 때 생성자는 부모의 생성자 먼저 호출
(부모의 생성자를 명시적으로 호출하지 않은 경우, 묵시적으로 부모의 인자 없는 생성자 호출)
- 부모의 생성자를 명시적으로 호출하기 위해 super 키워드 사용.
- super.getX(), super.a
[예시코드 1] 상속 기본 예제
class Subject1 { protected int a = 1000; public int fun1() { return a; } } class Subject2 extends Subject1 { private int b = 5; public int fun2() { return a/b; } } class Example { public static void main(String[] args) { Subject2 sub = new Subject2(); System.out.println( sub.fun1() ); System.out.println( sub.fun2() ); } }
1000
200
코드는 main 영역에서부터 읽는다
현재 Subject2 가 Subject1을 상속받은 상황이다.
main 영역에서 Subject2 타입의 인스턴스 sub을 생성하고 Subject2의 생성자를 태웠다.
Subject2 클래스에서는 super키워드를 찾아 볼 수 없기 때문에 부모의 생성자를 명시적 호출 하고 있지 않다.
일단 부모 클래스에 먼저 올라가서 멤버변수 a를 할당하고 1000 값을 가진다
또한 Subject2 에서는 private한 정수 변수 b를 선언하고 5값을 집어 넣는다
이후 sub 인스턴스가 가진 fun1를 호출하면 자식은 fun1 메서드를 가지고 있지 않기 때문에
부모 클래스의 fun1 메서드가 수행되어 그 공간에 있는 a값 1000이 리턴되고,
이후 내가 가진 fun2 메서드를 수행하게 되는데 이 때 a값은 부모의 멤버변수를 참조해서 1000/5를 수행하여 200 리턴
[예시코드 2] 변수 이름이 동일한 상황 (변수의 유효 범위)
class Parent { public int a = 1000; public int fun1() { return a; } } class Child extends Parent { public int a = 500; public int fun2() { return a; } } class Example { public static void main(String[] args) { Child c1 = new Child(); System.out.print( c1.fun1() + "," ); System.out.print( c1.fun2() + "," ); c1.a = 300; System.out.print( c1.fun1() + "," ); System.out.print( c1.fun2() ); Parent p2 = new Child(); p2.a = 700; System.out.print(p2.a + "," ); System.out.print(c1.a); } }
1000, 500, 1000, 3000
700, 300
Child라는 클래스가 Parent 클래스를 상속 받고 있는 상황
여기서 부모의 멤버변수도 a 이고 자식의 멤버변수도 a
서로 이름이 동일한 멤버 변수를 가질 때 main 영역에서 호출되는 a가 어떤 값을 가리키는지 알아 보는 문제
main 영역에 2개의 인스턴스 생성
Child c1 = new Child();
변수의 타입도 Child, 객체의 클래스도 Child();
이 코드는 "Child" 클래스의 객체를 생성하여 "Child" 타입의 변수 c1 에 할당하겠다는 뜻
반면 Parent 타입 변수 p2 => 부모가 날 낳은 형태로
"Child" 클래스의 객체를 생성했으나 "Parent" 타입의 변수 p2에 할당하겠다는 뜻
c1 이 생성되면 자식이 상속받고 있는 부모의 클래스로 먼저 올라가 부모의 멤버 변수를 생성해 준다
P.a 1000
C.a 500
인스턴스는 이 두개의 변수를 가지고
c1. fun1() 과 fun2() 의 메서드를 수행하여 1000, 500 을 리턴하게 된다.
이 문제에서 메서드명은 서로 다르기 때문에 오버라이딩이 문제되지는 않는다.
그냥 자식한테 없는 메서드는 가장 가까운 부모에게 올라가서 수행하면 된다.
그리고 c1.a 에 300을 대입해주는데 여기서 가리키는 a는 나의 a이다
어떻게 알 수 있는가?
Child c1 = new Child(); 에서 앞에 있는 타입 따라간다.
그래서 Child 가 가진 500에 300값을 넣어주는 것
반면 p2.a = 700 이라는 코드는 부모의 멤버변수 a를 가리킨다.
왜? Parent p2 = new Child(); 라서...
앞에 Parent 타입으로 선언된 변수임. 뒤에 Child 있어도 아무 의미 없다.
[예시코드 3] 부모, 자식의 메서드명이 동일- 메서드 오버라이딩 @Override
실제로 코딩을 하게 되면 보통은 오버라이드 키워드 붙여 주는데 여기서는 그냥 코드 써 보겠다
또한 메서드 오버라이딩과 비교되는 개념인 오버로딩, 하이딩 개념은 다음 포스팅에서 작성하겠음
아래 코드는 Animal 클래스를 상속받은 Cat 에서 displayInfo() 메서드를 호출한 경우
Animal, Cat 이 해당 메서드를 둘 다 가지고 있을 때 누구의 메서드가 수행되느냐 하는 문제
class Animal { public String name = "Animal"; public int legCount = 4; public void displayInfo() { System.out.println("Name: " + name + ", Legs: " + legCount); } } class Cat extends Animal { public String name = "Cat"; public void displayInfo() { System.out.println("Cat's name:" + this.name); System.out.println("Animal's name: " + super.name + ", Legs: " + this.legCount } } class Example { public static void main(String[] args) { Cat cat - new Cat(); cat.displayInfo(); } }
Cat's name: Cat
Animal's name: Animal, Legs: 4
이 문제는 메서드 오버라이딩 문제인데
아버지와 내가 동일한 이름과 매개변수의 메서드를 가질 때 문제됨
오버라이딩 = 재정의(즉 재정의된 자식의 메세드 메서드가 수행됨)
오버로딩 = 중복정의(기능은 비슷한데 매개변수 타입이나 갯수만 다른 메서드)
아버지가 날 낳은 형태든, 자식이 날 낳은 형태든,
중요한 건 메서드 오버라이딩을 하면
아버지처럼 살기 싫어요!! 만 기억하면 된다.
즉, 자식이 가진 메서드가 수행된다
Cat a = new Cat();
Animal b = new Cat(); 이든 모두 메서드 오버라이딩이면 자식 메서드가 수행
해당 문제에서 this키워드는 자식 본인이 가진 name을 말함
영어로는 self 개념으로
this.name
this = self 나, 내가
. = . 은 클래스나 어떤 공간에 '접근하여' 또는 어떤 클래스가 '가진' 이라는 뜻
name = 변수명
[예시코드 4] 아버지가 날 낳은 형태에서 자식의 메서드 사용 불가
아래 코드는 아버지가 날 낳은 형태에서
아버지에게는 없는데 자식만이 가진 메서드를 활용할 수 있는가 문제
답은 X
class Parent { protected int a = 1000; public int fun1() { return a; } } class Child extends Parent { private int b = 5; public int fun2() { return a/b; } } class Example { public static void main(String[] args) { Child c1 = new Child(); Parent p2 = new Child(); System.out.println( c1.fun1() ); // 1 System.out.println( c1.fun2() ); // 2 System.out.println( p1.fun1() ); // 3 System.out.println( p1.fun2() ); // 4 } }
아래 1-4 중 오류 나는 것은 4번
왜 ? Parent p1 = new Child();
아버지가 자식을 낳았기 때문에 자식만 가지고 있는 메서드는 아버지가 사용 불가
반대로 자식은 아버지의 메서드, 나의 메서드 모두 사용 가능
즉, 자식이 가지지 않은 메서드도 아버지가 가졌다면 사용 가능...
에러나는 이유
1. 타입 제한
p1 은 Parent 클래스타입의 변수. 이 변수는 Child의 객체를 참조하고 있지만 변수의 타입이 Parent이기 때문에 Parent 클래스에 정의된 객체만 사용 가능, 따라서 fun2()는 Child 클래스에만 정의되어 있으므로 p1을 통해 호출 불가
2. 컴파일 타임 체크
컴파일러는 p1의 타입이 Parent이기 때문에 fun2() 메서드 찾을 수 없고 따라서 오류 남
이러한 경우 fun2()사용하려면?
p1을 Child타입으로 캐스팅해야 함
Child c1 = (Child) p1;
c1.fun2();
이렇게 하면 가능
왜?
실제로 p1이 Child타입의 객체를 참조하고 있기 때문에
하지만 캐스팅 할 때는 항상 해당 객체가 실제로 Child타입인지 확인해야 함
그렇지 않으면? ClassCastException 발생할 수 있음.
[예시코드 5] 부모의 인자 없는 생성자 호출
class A { int a; A() { this.a = 1; } A(int a) { this.a = a; } void display() { System.out.println("a="+a); } } class B extends A { B(int a) { super.display(); } } public class Main { public static void main(String[] args) { B obj = new B(10); } }
a=1
B는 A를 상속 받음
Main 에서 B 타입 변수에 B 를 참조하는 객체 생성하였고 10을 매개변수로 전달
부모의 멤버변수 int a 생성
자식의 멤버변수는 없음
이 때 자식의 생성자를 태웠어도 부모의 생성자 먼저 수행해야 함.
묵시적으로 부모를 호출 안 한 경우 부모의 인자 없는 생성자를 호출한다
현재 B(int a) 안에 super.display() 가 있다고 해서 부모의 생성자를 호출했다고 착각하면 안됨
이건 부모의 메서드를 호출한 것이지 생성자를 호출한 게 아님
그래서 결국 부모의 인자 없는생성자인 A() 가 호출된 후, 자식의 인자 있는 생성자가 호출되어 인자값 10으로 연산이 이루어짐
즉, A() 먼저 호출되어 this.a에 1이 담긴 후, B()의 생성자가 호출되며
System.out.println("a="+a);가 생성됨
여기서의 a는 부모의 a를 말한다. 그래서 a=1 값이 출력됨.
왜? 부모의 메서드가 수행되는 공간의 a는 부모의 a이기 때문..
자식의 인자 있는 생성자에 매개변수로 들어간 a를 말하는게 아님<<<
a=10 이라 생각하면 틀리는 거..
'정보처리기사' 카테고리의 다른 글
[정보처리기사] 디스크 스케줄링(FCFS, SSTF, SCAN, CSAN, LOOK, C-LOOK) & RAID (1) | 2025.03.16 |
---|---|
[Java] 메서드 오버로딩 / 오버라이딩 / 하이딩 (2) | 2024.10.23 |
[Java] 자바의 예외처리 키워드, 예시, 종류 정리 (1) | 2024.10.18 |
[Java] 정보처리기사 추상클래스 / 인터페이스 정리 (오류 발생 문제 풀이) (3) | 2024.10.18 |
[Python] 파이썬 학습노트3 - 문제풀이, 슬라이싱, lambda(람다) (3) | 2024.10.16 |