[211111] 교육 11일차

oxllz·2022년 1월 26일
0

교육

목록 보기
9/41

객체지향 언어의 3대 특징

  1. 상속성 - 클래스를 상속받아 클래스를 만든다.
    - extend
  2. 은닉성 - 감추고자 하는 것들을 감출 수 있어야 한다. ( 존재하지만 접근할 수 없는 )
    - private, protected, public
  3. 다형성 - 하나의 심볼(이름 - 변수명 함수명 )이 다양한 대상에 접근할 수 있어야 한다.
    - 오버라이딩, 오버로딩

상속

class A {
	int data = 0;
	void print() {
		System.out.println( "print " + this.data );
	}
}
//
class B extends A {	// extends A 가 있을때와 없을때의 차이를 정리하기
	// class A를 상속받음과 동시에 B class의 변수, 함수 선언 가능
	void print2() {
		System.out.println( "print2" );
	}
}
//
public class Test085 {
	public static void main( String[] args ) {
		A t = new A();
		t.data = 100;
		t.print();  // print 100
		// 조상에서 선언된 함수 변수 , B에서 선언된 함수 변수 모두 사용이 가능하다.
		B r = new B();
		r.data = 200; 
		r.print();  // print 200
		r.print2(); // print2
	}
}

B클래스에서 보이지는 않지만 A클래스의 함수와 변수는 존재하고 있다. A 클래스에서 선언된 함수와 변수를 옮겨오는 효과를 얻는 것이다.

"class B 는 class A 로 부터 상속받았다. A 는 B 의 부모클래스, B 는 A 의 자식클래스"


오버라이딩

class A {
	int data = 100;
	void print() {
		System.out.println("A print");		
	}
}
// 부모 클래스의 변수와 같은 이름의 변수는 선언하지 않는게 좋다.
class B extends A {
	int data = 200;
	void print() {
		System.out.println("B print");		
	}
}
//
public class Test086 {
	public static void main( String[] args ) {
		B t = new B();
		t.print(); // B print
		A t2 = new A();
		t2.print(); // A print
	}
}


클래스를 상속받으면 함수포인터를 물려받는데, 자식클래스에서 함수가 똑같이 선언되는 경우에는 새로운 포인터를 만들지 않고 기존(상속받은)의 포인터가 자식클래스에서 선언된 실체를 가리키게 된다.

부모클래스에서 선언된 함수를 자식클래스에서 그대로재 정의하면 갈아엎는 형태로 구현되게 된다. 그것을 method overriding 이라고 한다.

#include <stdio.h>
typedef struct a {
	void (*print)( void* );
} A;
//
typedef struct b {
	void (*print)( void* );
} B;
//
void A_print( void* self ) { printf("A print\n"); }
void B_print( void* self ) { printf("B print\n"); }
//
int main() {
	//  new B() 했을때 벌어지는 일
	B* t;
	t = (B*)malloc( sizeof(B) );
	// 상속된 인스턴스를 만들때는 먼저 조상에서 선언된 내용을 만들고 오버라이딩 된 경우는 갈아 엎는다.
	t->print = A_print; // 조상에서 선언된 함수와 연결
	t->print = B_print; // 자손에서 선언된 함수로 갈아엎음 -- 이 시점에서 오버라이딩 !!
	t->print( t ); // B에서 오버라이딩된 내용 출력
	free( t );
	return 0;
}

java Test086 코드를 C 로 구현해보자. 처음 B의 함수포인터는 A의 print() 를 가리키고 있다. 이때 자식클래스 (B) 에서 print() 함수를 재정의하면 B의 함수포인터는 B의 print() 를 가리키게 된다.


오버로딩

class Temp {
	int add( int i, int j ) { return 100; }
	int add( int i ) { return 200; }
	void print( double i ) { System.out.println( i ); }
	void print( float i ){ System.out.println("^^*"); }
}
//
public class Test093 {
	public static void main( String[] args ) {
		Temp t = new Temp();
		int r = t.add( 10, 20 );	// t.add( 10, 20 ) 호출하는 코드를 return 값이 대신하게 하면 이해 됨
		System.out.println( r );
		//
		int j = t.add( 10 );
		System.out.println( j );
		t.print( 100 );
	}
}

Method Overloading : 이름은 같지만 매개변수의 형식이 틀린 함수는 하나의 클래스 안에 공존할 수 있다. 함수명이 같은것이 여러개 있을때는 매개변수 형식이 일치하는 선언을 찾아서 호출하게 된다.

같은 이름을 쓸때는 리턴타입은 동일해야 한다. ( 매개변수의 형식만 달라진다 )

오버로딩을 사용하는 이유 : 거의 비슷한데 조금 틀린 기능의 함수를 매번 이름을 달리주는 것 보다 하나의 이름으로 묶어주는 것이 훨씬 편리하기 때문 ( 아래의 Test092의 String method 사용하는 것으로 이해 )


String

public class Test092 {
	public static void main( String[] args ) {
		String l = null;
		String t = "HelloWorld";
		//
		System.out.println( t.startsWith("Apple") ); // flase
		System.out.println( t.startsWith("Hel") ); // true
        	//
		boolean b = t.endsWith("rld");
		System.out.println( b ); // true
   		//	
		int idx = t.indexOf("oWo");
		System.out.println( idx ); // 4
		//
		int idx2 = t.lastIndexOf("l");
		System.out.println( idx2 ); // 8
		//
		String r1 = t.substring( 2 );
		System.out.println( r1 );	// lloworld
		String r2 = t.substring( 2, 5 );
		System.out.println( r2 ); // llo
	}
}

선언 타입에 따라 참조형 변수, 자료형 변수일때 String은 new 없이 인스턴스를 생성하는 유일한 클래스다.

따라서 String 으로 선언한 변수는 참조형 변수이고, 문자열 "HelloWorld" 는 인스턴스다.

startsWith 해당 문자열로 시작하는지
endsWith 해당 문자열로 끝나는지
indexOf 해당 문자열이 위치하고 있는 시작 인덱스 번호
lastIndexOf 해당 문자열이 위치하고 있는 마지막 인덱스 번호
substring( 시작 인덱스 ) 시작 인덱스부터 끝까지 String 추출
substring( 시작 인덱스, 끝 인덱스 ) 끝 인덱스 - 1 까지 String 을 추출


부모클래스 = new 자식클래스

class A {
	void print(){ System.out.println("print"); }
}
//
class B extends A {
	void print(){ System.out.println("printXX"); }
	void print2(){ System.out.println("print2"); }
}
//
public class Test088 {
	public static void main( String[] args ) {
		A t = new B();
		t.print();
		//	t.print2();
	}
}


부모형 변수로 자식 클래스의 인스턴스를 가리킬 수 있다. 이 때, 부모형 변수로는 부모에서 선언된 멤버(변수,함수)만 호출 가능하고, 오버라이딩 된 경우엔 오버라이딩 된 함수가 호출된다.

class A {
	int data = 100;
}
//
class B extends A {
	int data = 200;
}
// 부모 클래스에서 선언된 변수와 같은 이름의 변수 선언 안하는게 좋다!
public class Test089 {
	public static void main( String[] args ) {
		A t = new B();
		System.out.println( t.data ); // 100
		//
		B t2 = (B)t;
		// 같은이름의 변수가 2개 있으면 자신(B)안에서 선언된 것을 호출한다.
		System.out.println( t2.data ); // 200
	}
}


"멤버변수는 오버라이딩 개념이 없다 - data 라는 이름의 변수가 두개 존재한다"


생성자

class A {
	A(){
		System.out.println("A Constructor");
	}
}
//
class B extends A {
	B() {
		System.out.println("B Constructor");
	}
}
//
public class Test091 {
	public static void main( String[] args ) {
		B t = new B();
	}
}
실행결과
A Constructor
B Constructor

자식클래스의 인스턴스를 생성하면 부모의 생성자부터 자식의 생성자가 차례로 호출된다. 생성자는 멤버함수가 아니므로 상속되지 않는다. (포인터 통해 접근도 안됨) 다만 인스턴스 생성시 호출될 뿐이다.

0개의 댓글