1주차 ( 상속 [오버로딩 , 오버라이딩] , 다형성 , String , StringBuffer , StringBuilder )

2019. 7. 13. 14:09카테고리 없음

STEP 01 . 상속 [오버로딩 , 오버라이딩] ( inheritance [ Overloading , Overriding ] )


 

상속의 정의 및 특징

 

 - 말그대로 자식이 부모로부터 물려받는 다는 의미이다.

 - 상속은 ( IS - A 관계 ) 이다.

 - 부모 클래스( ParentClass ) 와 자식 클래스( ChildClass )는 자바의 예약어인 extends에 의하여 정해진다.

 - 하나의 부모 클래스 ( ParentClass )는 여러개의 자식 클래스( ChildClasss ) 를 가질 수 있지만 반대로 자식 클래스(Child Class)는 여러개의 부모 클래스(ParentClass)를 가질 수 없다.

 - 자식 클래스 ( ChildClass ) 는 부모 클래스 ( ParentClass ) 의 자원 (resource) 를 사용 할 수 있다. 반대로 부모 클래스는 자식 클래스의 자원을 가져다 쓸 수 없다 . 

 - 자식 클래스는 부모 클래스로부터 물려받은 자원들을 Override 하여 수정해서 사용할 수 있다.

 - 부모 클래스가 상속받은 자원도 자식 클래스가 사용 가능하다.

 - 생성자와 초기화 블럭은 상속되지 않는다 . 멤버만 상속된다.

 - 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

 

// 다음과 같은 방식으로 상속을 구현한다
class Child extends Parent{} 

 

다음과 같은 용어를 사용해서 표현하기도 한다.

 

조상클래스
  • 부모(Parent)클래스 , 상위(super)클래스 , 기반(base)클래스
자손 클래스
  • 자식(child)클래스 , 하위(sub)클래스 , 파생된(derived)클래스

[출처] 자바의정석 남궁성

 

간단한 동물 예제를 통해서 알아보겠습니다.

 

package com.juniordev.firstweek;

public class Animal {
	
	String name; 
	
	protected void setName(String name) {
		this.name = name;
	} // end of setName method
	
	protected void printName() {
		System.out.println("이름 : " + this.name);
	} // end of printName method
	
	protected void sleep() {
		System.out.println("Animal.java sleep() 메소드");
	} // end of sleep method	
} // end of Animal class
package com.juniordev.firstweek;

public class Dog extends Animal{

	public static void main(String[] args) {
		
		Dog dog = new Dog();
		dog.setName("말티즈");
		
		dog.printName();
		dog.sleep();
		
	} // end of main method
	
	@Override
	public void sleep() {
		
		System.out.println("Dog.java sleep() 메소드");
	} // end of sleep method
}// end of Dog class

/** 
출력 결과

이름 : 말티즈
Dog.java sleep() 메소드

*/
package com.juniordev.firstweek;

public class Cat extends Animal{
	public static void main(String[] args) {
		
		Cat cat = new Cat();
		cat.setName("페르시안 친칠라");
		
		cat.printName();
		cat.sleep();
		
	}// end of main method
	
	@Override
	public void sleep() {
		
		System.out.println("Cat.java sleep() 메소드");
	}// end of sleep method
}// end of Cat class

/**
출력 결과

이름 : 페르시안 친칠라
Cat.java sleep() 메소드

*/

 

 

위의 3개의 Class를 추가해준다. 부모 클래스로 사용할 Animal class 파일과 자식 클래스로 사용할 Dog , Cat class 파일입니다.

출력 결과는 위의 코드블럭에 포함시켰습니다.

 

다만 Animal , Cat , Dog 클래스에 sleep()이라는 공통된 메소드가 있습니다.

Dog 와 Cat에서 sleep메소드를 실행시켰을때 각각 클래스가 가지고 있는 결과가 나온다.

하지만 sleep()메소드를 주석처리하고 실행하게 된다면 에러없이 실행은된다. 다만 결과는 Animal class가 가지고 있는 sleep()이 실행되게 된다.  ( 밑에서 나올 오버라이딩 정의 )

그이유는 자신이 해당 메소드를 가지고 있지 않다면 부모클래스들이 가지고 있는지 확인하고 그래도 없다면( 최상위 클래스인 Object Class 까지 검색) Error를 발생하게 된다.

 

메소드 오버라이딩 (Method Overriding)


[출처 : 자바의 정석 P.327]

오버라이딩의 정의

조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩이라고한다. 상속받은 메서드를 그대로 사용하기도 하지만 자손 클래스에 맞게 변경해서 사용해야할 경우 조상의 메서드를 오버라이딩한다고 말한다.

 

오버라이딩(Overriding)의 조건

  1.  자손 클래스에서 오버라이딩하는 메서드는 부모 클래스의 메서드와 이름이 같아야한다.
  2.  자손 클래스에서 오버라이딩하는 메서드는 부모 클래스의 메서드와 매개변수가 같아야한다.
  3.  자손 클래스에서 오버라이딩하는 메서드는 부모 클래스의 메서드와 반환타입이 같아야한다.

 

Overriding 조건에 해당되는 경우

부모클래스의 overiding(int) 메소드와 A클래스를 상속받은 B,C클래스에서 반환타입 , 매개변수 , 메소드 이름까지 다 같게하였을 경우 에러가 발생하지 않는다.

class A {

public int overriding(int b) {
return b;
}// end of overriding(int) method
}// end of A class

class B extends A {

@Override
public int overriding(int b) {
return b;
}// end of overriding(int) method
}// end of B class

class C extends A {

@Override
public int overriding(int b) {
return b;
}// end of overriding(int) method
}// end of C class

Overriding 조건에 해당되지 않는 경우

B클래스의 경우 A클래스의 overriding메소드와 다르게 반환타입이 void 이고 , C클래스는 매개변수가 한개 더 많다는것을 볼 수 있다.

class A {

public int overriding(int b) {

return b;

}// end of overriding(int) method

}// end of A class

 

class B extends A { //The return type is incompatible with A.overriding(int) // 반환 타입이 A.overriding(int) 와 같지 않습니다.

 

@Override public void overriding(int b) {

return b;

}// end of overriding(int) method }// end of B class

 

class C extends A {

// The method overriding(int, int) of type C must override or implement a supertype method

// superType의 메소드를 재구현하거나 새로운 메소드를 만들어야한다.

// 이경우 @Overriding이 없다면 에러가 나는경우는 아닙니다. 어노테이션은 뒤에서 살펴보겠습니다.

@Override public int overriding(int a , int b) {

return a;

}// end of overriding(int , int) method }// end of C class

 

 

오버로딩 vs 오버라이딩 ( 면접질문에 자주나옴 명확히 구분 가능해야함 )


오버로딩 (overloading) - 기존에 없는 새로운 메서드를 정의하는 것 , 같은 클래스 내에서 같은 이름의 메서드를 매개변수의 타입 , 갯수에 따라 다른 호출을 하게하는것

오버라이딩 (overriding) - 상속받은 메서드의 내용을 변경하는 것 ( 가능한 조건이 있음 )

 

 

class ParentClass{
	public void parent() {}
}

class ChildClass extends ParentClass{

	@Override
	public void parent() {		super.parent();	}
	
	// Overloading
	public String parent(String name) {return name;}
	public int parent(int number) {return number;}
}

 

 

 

STEP 02 . Interface


Interface 의 정의 및 특징

 

 - 인터페이스는 표준 , 약속 , 규칙이다.

 - 인터페이스는 몸통이 없는 추상 메소드와 상수로 이루어진다.

 - Interface로는 인스턴스를 생성할 수 없다.

 - Interface를 일종의 설계도이다.

 - 서로 조건이 없는 클래스 사이에 관계를 맺어줄 수 있다.

 - interface의 구현은 implements keyword로 작성한다.

 - class exnteds상속과는 다르게 implements를 통한 다중 상속 가능. ( extends는 확장의 의미 , implements는 구현의 의미 )

 

제약 사항

 - 모든 멤버 변수는 public static final이여야 한다 . (default 생략 가능)

 - 모든 멤버 메서드는 public abstract 이여야 한다 . (default 생략 가능)

// 인터페이스의 작성 방법
interface interfaceName {
    public static final constantName = value;
    public abstract methodName(argu);
}

 

abstract를 사용했을떄와 interface를 사용했을때이 차이점은 무엇일까?

 - abstract와 interfacesms 스스로 인스턴스를 생성하지 못하고 , 상속관계에서만 존재할수 있다는 공통점이 있다.

 - abstract는 extends로 다중상속이 되지 않는 반면 interface 는 implements를 통한 다중상속이 가능하다.

 - abstract는 필요한 부분만 구현하거나 몸통만 구현하도록 할 수 있지만 , interface는 모든 메서드를 구현해야한다.

 

 

STEP 03 . 다형성 


 

다형성이란 같은 자료형에 여러 가지 객체를 대입하여 다양한 결과를 얻어내는 성질을 의미한다. 

 

package com.juniordev.firstweek;

import java.text.DecimalFormat;
import java.util.Scanner;


/**
 * @author YunJin Choi <zzdd1558@gmail.com>
 */
public class Polymorphism {
	public static void main(String[] args) {
		
		Scanner scan = new Scanner(System.in);
		Buyer buyer = new Buyer();
		Product product = null;
		System.out.print(" 구입할 물건 ( 1. TV , 2. Radio )  : " );
		
		switch (scan.nextInt()) {
		case 1:
			product = new TV("LG 40인치 TV", 2000000);
			break;

		case 2:
			product = new Radio("삼성 라디오", 500000);
			break;
		}
		
		buyer.buy(product);
		product.on();
	}
}


abstract class Product {
	
	private String className;
	private String name;
	private int price;
	public static double pointPercent = 0.10;
	
	public String getClassName() {	return className;}
	public void setClassName(String className) {	this.className = className;}
	public String getName() {	return name;}
	public void setName(String name) {	this.name = name;}
	public int getPrice() {	return price;}
	public void setPrice(int price) {	this.price = price;}
	public abstract void on();
	
	public String getDecimalPrice(int price) {
		DecimalFormat form = new DecimalFormat("#,###");
		form.setDecimalSeparatorAlwaysShown(false);
		return form.format(price);
	}
	
	public void getProductInfo() {
		// TODO Auto-generated method stub
		System.out.println("---구매 제품 정보---");
		System.out.println(getClassName() + " 클래스");
		System.out.println("가전제품 종류 : " + getName() );
		System.out.println("가전제품 가격 : " + getDecimalPrice(getPrice()) + "원");
		System.out.println("---------------");
	}
}


class Radio extends Product{
	
	// Constructor
	public Radio() {
		String[] array = this.getClass().getName().toString().split("\\.");
		setClassName(array[array.length - 1]);
	}
	
	public Radio(String name , int price ) {
		this();		
		setName(name);
		setPrice(price);
	}
	
	@Override
	public void on() {	System.out.println("Radio 스위치 ON!!");}
}

class TV extends Product{
	
	// Constructor
	public TV() {
		String[] array = this.getClass().getName().toString().split("\\.");
		setClassName(array[array.length - 1]);
	}
	
	public TV(String name , int price ) {
		this();		
		setName(name);
		setPrice(price);
	}
	
	@Override
	public void on() {	System.out.println("TV 스위치 ON!");}
}

class Buyer{
	
	private String name ;
	private int money;
	
	public Buyer() {
		this.name = "최 윤진";
		this.money = 50000000;
	}
	public Buyer (String name , int money) {
		this.name = name;
		this.money = money;
	}
	
	public String getName() {	return name;}
	public void setName(String name) {	this.name = name;}
	public int getMoney() {	return money;}
	public void setMoney(int money) {	this.money = money;}
	
	public void buy(Product product) {	
		product.getProductInfo();
		money -= product.getPrice();
		System.out.println("잔금 : " + getDecimalPrice(money) + "원");
	}
	
	public String getDecimalPrice(int price) {
		DecimalFormat form = new DecimalFormat("#,###");
		form.setDecimalSeparatorAlwaysShown(false);
		return form.format(price);
	}
	
}

STEP 04 . String , StringBuffer , StringBuilder


 

[참조 : http://jeong-pro.tistory.com/85]

 

다양한 코드를 보거나 프로그래밍을 하다보면 String도 문자열이고 StringBuffer , StringBuilder도 문자열을 저장하고 관리하는 클래스라는것을 확인할 수 있다.

하지만, 그 차이를 모르고 쓴다면 안쓰니만못하다.

 

각각의 차이가 무엇인지 알아보기 앞서 정의를 알아보겠습니다.

 

String- 짧은 문자열을 더할 경우 사용.

StringBuffer- Thread에 안전한 프로그램이 필요하거나 , 개발 중인 시스템의 부분이 Thread에 안전한지 모를경우 사용하면 좋다.

StringBuilder- Thread 안전한지 여부가 전혀 관계 없는 프로그램을 개발할때 사용하면 좋다.

 

 

STEP 05 . == 와 equals의 차이


 

String 관련 문자열을 비교하면서 항상 equals만 사용했을 것이다.

또한 ==도 비교 연산이고 equals도 비교연산인데 왜 ==을 안쓰고 equals를 사용하는지 궁금하지만 모르는사람이 있을수도 있다.

== 는 무엇이고 equals는 무엇인가 코드를 먼저 보고 출력결과를 예측해보고 알아보도록 하겠습니다.

 

 

package com.juniordev.firstweek;

public class StringEquals {
	public static void main(String[] args) {
		
		String first = "ABC";
		String second = "ABC";
		
		String equalsFirst = new String("ABC");
		String equalsSecond = new String("ABC");
		
		// 출력결과를 예상해봅시다.
		System.out.println(first == second);
		System.out.println(first == equalsFirst);
		System.out.println(equalsFirst == equalsSecond);
		
		System.out.println(first.equals(equalsFirst));
		System.out.println(equalsFirst.equals(equalsSecond));
	}
}

 

equals -

 객체가 가지고 있는 값을 비교.

==

- 객체가 가지고 있는 값의 비교

 

그렇다면 일반 리터럴 변수로 생성한 String과 new를 이용하여 객체형식으로 생성된 String 에는 어떠한 차이가 있는지 알아보도록 하겠습니다 .

 

Java 7 미만에서는 String Constant Pool 영역이 Perm 영역에 저장되었으나 Java 7 이상부터는 String Constant Pool 영역이 Heap 영역에 저장된다. 

 

리터럴과 객체로 생성하는것이 Heap 영역에 어떻게 저장되는지 사진으로 알아보곘습니다.

 

 

[이미지 참조 : https://brunch.co.kr/@kd4/1 ] 

 

사진을 보면 알 수 있듯이 String 리터럴로 생성된 문자열은 Heap영역의 String Constant Pool 에 저장이 되어 다른 String 리터럴로 같은 Cat을 지정해도 같은 참조변수로 String Constant Pool 에 있는 Cat을 참조하게 되므로 ==와 equals로 비교했을때 True가 나온다.

 

하지만 String 클래스로 객체로 생성하였을경우 객체가 저장되는 Healp영역에 저장되어 실제 값은 같을수 있지만 그 객체를 참조하는 참조변수값이 달라 참조값으로 비교하는 == 연산자를 사용했을때는 참조변수 값이 달라 False가 출력된다.

따라서 위의 그림을 보면 s1 == s3를 하였을때 일반 리터럴 변수와 객체를 비교하였기 때문에 False가 출력된것을 확인 할 수 있다.

 

이제 다시 위의 작성된 예제를 돌려보고 한번더 이해하는 시간을 가져보는것을 추천한다.