오늘의하루

[JAVA 공부 6일차] 객체지향 Part 4 본문

JAVA

[JAVA 공부 6일차] 객체지향 Part 4

오늘의하루_master 2022. 7. 27. 12:05

1. 상속

public class object_oriented_2 {

	public static void main(String[] args) {
		SmartTv stv = new SmartTv();
		stv.channel = 10; // 상속받은 멤버
		stv.channelUp(); // 상속받은 멤버
		System.out.println(stv.channel);
		stv.caption = true;
		stv.displayCaption("Hello JAVA");
	}
}

class Tv2{ // 멤버 7개
	String color; // 인스턴스 변수  1
	boolean power; // 인스턴스 변수 2
	int channel; // 인스턴스 변수 3
	static int cv; // 클래스 변수 (static 변수) / 공통속성 4
	
	void power() { power = !power; } // 인스턴스 매서드 5
	void channelUp() { ++channel; } // 인스턴스 매서드 6
	void channelDown() { --channel; } // 인스턴스 매서드 7
	
	
	Tv2(){} // 기본 생성자
    
	Tv2(String color, boolean power, int channel){ // 매개변수 있는 생성자
		this.color = color;
		this.power = power;
		this.channel = channel;
	}
}

// 상속받는 방법 : extends 부모 클래스
class SmartTv extends Tv2{ // 자식 멤버 2개 + 부모 멤버 7개 = 9개
	boolean caption; // 인스턴스 변수 1
    
	void displayCaption(String text) { // 인스턴스 매서드 1
		if (caption) {
			System.out.println(text);
		}
	}
}

Java에서 상속을 사용할때는 단일 상속이라는 원칙이 있기 때문에 extends A, B 는 불가능합니다. (C++에서는 가능)

그리고 부모 클래스의 변수와 매서드는 모두 상속이 되지만 생성자 및 초기화 블록은 상속이 되지 않습니다.

만약 2개의 클래스를 상속받고 싶다면 1개는 상속으로 1개는 포함으로 하면 해결할 수 있다.

2. 포함

public class object_oriented_2 {

	public static void main(String[] args) {
		Circle c2 = new Circle(); // 포함
		c2.p.x = 1;
		c2.p.y = 2;
		c2.r = 3;
		System.out.println("c2.p.x = " + c2.p.x);
		System.out.println("c2.p.y = " + c2.p.y);
		System.out.println("c2.r = " + c2.r);
	}
}

class Point{
	int x;
	int y;
}

class Circle{
	Point p = new Point(); // 포함 관계 만들기
	/*
	 Q. 만약 참조변수 p의 초기화를 바로 해주지 않았다면??
	 Point p;
	 
	 Circle(){ // 생성자로 초기화 해줘야 한다.
	 	p = new point();
	 }
	 Q. p를 초기화 해주지 않으면 어떻게 되나요?
	 A. p는 참조변수이기 때문에 null로 자동 초기화 됩니다.
	 */
	int r;
}

포함이 경우는 참조변수에 인스턴스에 생성하여 저장 후 사용하는 방법을 말합니다.

상속보다 많이 사용되는 방법이며 상속을 사용해야할지 포함을 사용해야할지 모르겠다면 포함을 사용하면 됩니다.

포함을 했을때 어떤식으로 만들어지는 알아보자.

      0X100     0X200  
C2 0X100 => 0X200 p => 0 x
      0 r   0 y

이런 구조로 만들어지기 때문에 만약 x의 값을 지정해주고 싶다면 C2가 가르키는 인스턴스의 참조변수 P가 가르키는 인스턴스의 인스턴스 변수 x에 접근해줘야합니다. (c2.p.x)

3. 상속을 안받는 클래스는 없다?

사실 상속을 받지 않는 클래스는 없습니다.

부모 클래스로부터 상속 받지 않는 클래스들은 Object라는 부모 클래스를 가지게 됩니다.

public class object_oriented_2 {

	public static void main(String[] args) {
		Imsolo my = new Imsolo();
		System.out.println(my.toString());
	}
}

class solo{
	int x;
	int y;
}

class Imsolo extends solo{
	int r;
}

main 함수를 보게 되면 toString()이라는 매서드를 사용하는 것을 볼 수있지만 아래 클래스들을 보게 되면 그 어디에도 toString()이라는 매서드를 정의해준 적이 없습니다.

위에 있는 코드들을 한번 살펴 보겠습니다.

  • solo 클래스
    • 별도 부모 클래스를 상속 받지 않았다.
      • 별도 부모 클래스가 없기 때문에 Object 클래스를 상속받는다.
      • Object 클래스로 부터 상속받은 매서드로는 toString(), equals() 등등 11가지 정도 있다.
    • 인스턴스 변수 x,y가 있다.
  • Imsolo 클래스
    • solo 클래스를 상속 받았다.
    • 인스턴스 변수 x,y,z가 있다.
    • solo 클래스가 상속받은 매서드를 사용할 수 있다.

4. 오버라이딩 (매서드 확장)

public class object_oriented_2 {

	public static void main(String[] args) {
		point3D pp = new point3D();
		pp.x = 3;
		pp.y = 5;
		pp.r = 7;
		System.out.println(pp.getAll());
	}
}

class Point2 {
	int x;
	int y;
	
	String getAll() {
		return "x = " + x + ", y =" + y;
	}
}

class point3D extends Point2{
	int r;
	
	String getAll() {
		return "x = " + x + ", y =" + y + ", r =" + r;
	}
}

오버라이딩이란 상속받은 매서드의 구현부 내용을 바꿔주는 것을 말합니다.

오버라이딩을 하기 위한 원칙을 알아보겠습니다.

  1. 메소드의 선언부가 조상 클래스의 메소드와 일치해야한다.
    • 선언부란? 타입, 메스드명, 매개변수를 말한다
  2. 접근 제어자를 조상 클래스의 메소드보다 좁은 범위로 변경할 수 없다.
    • public > protected > default > private (public이 가장 넓음)
  3. 예의는 조상 클래스의 메소드보다 많이 선언할 수 없다.

※ 오버로딩(새로운 기능을 만듬) 과 오버라이딩(기본 기능을 수정 함)은 전혀 다른 개념이다.

5. 참조변수 super

class Parent2 {
	int x = 10;
}

class Child2 extends Parent2{
	int x = 20;
	
	void method() {
		System.out.println("x = " + x); // 가까운 x을 찾는다.
		System.out.println("this.x = " + this.x); // 자기 자신의 x
		System.out.println("super.x = " + super.x); // 부모의 x
	}
}

class Child3 extends Parent2{
	void method() {
		System.out.println("x = " + x); // 가까운 x을 찾는다.
		System.out.println("this.x = " + this.x); // 자기 자신의 x
		System.out.println("super.x = " + super.x); // 부모의 x
        
		// Child3의 경우 자기 자신이 변수를 가지고 있지 않는다.
		// 이럴때는 부모의 인스턴스 변수를 this와 super 모두로 가져올 수 있게 된다.
	}
}

참조변수 super는 부모 클래스와 자식 클래스의 인스턴스를 구분하기 위해 사용되며 인스턴스 매서드에서만 사용할 수 있습니다.

6. 생성자 super()

💥Error Code💥

class point5 {
	int x;
	int y;

	point5(int x, int y){ // 매개변수 있는 생성자
		this.x = x;
		this.y = y;
	}
}

class mypoint3D extends point5{
	int z;
	
	mypoint3D(int x, int y, int z){ // 매개변수 있는 생성자
		this.x = x;
		this.y = y;
		this.z = z;
	}
}

 

지금까지는 몰랐지만 생성자의 첫줄에는 무조건 다른 생성자( this(), super() )를 호출해야만 합니다.

코드를 컴파일 했을때 만약 생성자의 첫줄에 다른 생성자 호출이 없다면 컴파일러가 자동으로 super() 생성자를 만듭니다.

위 코드가 왜 Error 코드인지 아래 코드를 통해 알아보겠습니다.

💥이유에 대해 알아보자💥

class point5 {
	int x;
	int y;

	point5(int x, int y){
		// 컴파일러가 자동으로 만들어준다.
		super(); // Object 클래스를 상속받는 클래스는 문제가 없습니다.
		// 의미 : Object() 호출
		this.x = x;
		this.y = y;
	}
}

class mypoint3D extends point5{
	int z;
	
	mypoint3D(int x, int y, int z){
		// 컴파일러가 자동으로 만들어준다.
		super();
		// 의미 : point5() 호출
		// point5 클래스를 보면 지금 매개변수 없는 point5() 생성자가 없기 때문에 Error 발생
        
		// 해결하는 방법
		// 1. point5 클래스에 point5(){} 기본 생성자를 만들어준다.
		// 2. super(x,y)를 통해 매개변수 있는 부모 클래스의 생성자를 호출한다.
        
		this.x = x;
		this.y = y;
		this.z = z;
	}
}

아래에서 2가지 해결 케이스를 모두 만들어보자

⭐해결방법 1⭐

class point5 {
	int x;
	int y;
	
	point5(){} 기본생성자로 자식 클래스의 super()가 호출할 수 있도록 만들어준다.
	point5(int x, int y){
		//super(); 컴파일러가 만들어준다.
		this.x = x;
		this.y = y;
	}
}

class mypoint3D extends point5{
	int z;
	
	mypoint3D(int x, int y, int z){
		//super(); 컴파일러가 만들어준다.
		this.x = x;
		this.y = y;
		this.z = z;
	}
}
⭐해결방법 2⭐

class point5 {
	int x;
	int y;
	
	point5(int x, int y){
		//super(); 컴파일러가 만들어준다.
		this.x = x;
		this.y = y;
	}
}

class mypoint3D extends point5{
	int z;
	
	mypoint3D(int x, int y, int z){
		super(x,y); // 직접 생성자 super()를 만들어준다.
        // 매개변수 있는 생성자를 사용할 수 있도록 직접 만들어준다.
		this.z = z;
	}
}

코드가 길어지고 집중력이 떨어지면 생성자 호출 관련해서 문제가 발생할 수 있기때문에 처음 생성자를 만들때 꼭 기본생성자와 매개변수 있는 생성자를 만들어두면 편하다.

Comments