과목명 : 객체지향 프로그래밍(Object oriented programming)
수업일자 : 2022년 11월 16일 (수)
1. 클래스 상속(Inheritance)
1-1. 클래스 상속(Inheritance)의 정의와 여러 가지 특징
- 상속(Inheritance)이란, 부모 클래스(상위 클래스)와 자식 클래스(하위 클래스)가 존재하며, 자식 클래스가 부모 클래스의 멤버(변수, 메서드)를 그대로 이어받아 새로운 클래스를 작성하는 것을 의미합니다.
- 두 클래스를 부모(조상)와 자식 관계로 맺어주는 것을 의미하기도 합니다.
- extends 키워드를 사용해 정의합니다.
- 자식 클래스는 부모(조상) 클래스의 생성자, 초기화 블럭을 제외한 나머지 모든 멤버를 상속받습니다.
- 자식 클래스의 맴버 개수는 부모(조상) 클래스의 개수보다 같거나 더 많습니다.
- 상속, 포함 등 클래스 간의 관계를 맺어주어 코드의 재사용성과 관리(유지보수)를 용이하게 할 수 있습니다.
1-2. 기본적인 상속 방법
- 문법 : class 자식 클래스 extends 부모 클래스 {
}
- 부모 클래스를 확장(Extend)하여 새로운 자식 클래스를 생성하는 개념으로 볼 수 있습니다.
class ChildClass extends ParentClass {
// ...
}
1-3. 클래스의 상속(Inheritance) 관계
- 멤버의 공통 부분은 부모(조상)클래스에서 관리하고, 개별적인 부분은 자식 클래스에서 관리합니다.
- 부모(조상) 클래스의 변경은 자식 클래스에 영향을 주지만, 자식 클래스의 변경은 부모(조상) 클래스에 영향을 줄 수 없습니다.
class ParentClass {
int number1;
int number2;
}
// 클래스 상속(Inheritance)
class ChildClass extends ParentClass {
int number3;
int number4;
int number5;
}
public class InheritanceTest {
public static void main(String[] args) {
// 자식 클래스의 객체 생성
ChildClass cc = new ChildClass();
// 자식 클래스의 인스턴스 변수에 값을 할당한다.
cc.number1 = 10;
cc.number2 = 20;
System.out.println(cc.number1 + cc.number2); // 30
}
}
1-4. 클래스의 포함(Composite) 관계
- 클래스의 멤버(변수, 메서드)로 참조변수(Reference variable)를 선언하는 것을 말합니다.
즉, 한 클래스의 멤버변수로 다른 클래스를 생성하는 것을 의미합니다.
(자식 클래스 내부에 부모 클래스의 객체를 생성(참조변수도 생성)하는 것)
- 작은 단위의 클래스를 먼저 생성 후 이를 통해 더 큰 단위의 부모 클래스를 생성합니다.
class ParentClass {
int number1;
int number2;
}
// 클래스 포함(Composite) 관계
class ChildClass {
ParentClass pc = new ParentClass();
int number3;
int number4;
}
public class CompositeTest {
public static void main(String[] args) {
// 포함 관계로 생성된 자식 클래스의 객체 생성
ChildClass cc = new ChildClass();
// 자식 클래스의 인스턴스 변수에 값을 할당한다.
cc.pc.number1 = 10;
cc.pc.number2 = 30;
cc.number3 = 50;
System.out.println(cc.pc.number1 + cc.pc.number2 + cc.number3); // 90
}
}
1-5. 단일 상속(Single inheritance)
- 단일 상속(Single inheritance)이란, 자식 클래스가 단 하나의 부모(조상) 클래스를 상속 받는 것을 의미합니다.
- Java에선 단일 상속만을 허용하고 있습니다.
- 다중 상속처럼 편리하게 코드를 작성하는 방법은 비중이 높은 특정 클래스 하나만 상속 관계, 나머지 클래스들은 포함 관계로 설정합니다.
- 다중 상속의 문제점 : 부모 클래스가 2개 이상일 시, 각각의 부모 클래스에 메서드명이 동일하고 기능이 다른 메서드가 존재할 때, 자식 클래스의 관점에서 어떠한 부모 클래스의 메서드를 상속받아야 할지 충돌 문제가 발생하게 됩니다. 이를 해결하기 위해 Java에선 인터페이스(Interface)를 지원하며 인터페이스를 사용하면 다중 상속의 효과, 충돌 문제를 방지하는 효과를 기대할 수 있습니다.
1-6. Object 클래스
- Java에서 클래스 간의 상속 계층도에서 Object 클래스는 최상위 클래스입니다.
- 부모 클래스가 없는 특정 클래스는 자동적으로 컴파일러에 의해 Object 클래스를 상속받게 됩니다.
- Object 클래스는 모든 클래스들의 최고 부모(조상) 클래스이며, Object 클래스에 정의된 11개의 메서드를 상속받습니다.
대표적인 메서드 : toString(), equals(Object obj), hashCode(), ...
2. 메서드 오버라이딩(Overriding)
2-1. 메서드 오버라이딩의 정의와 특징
- 자신의 부모(조상) 클래스들에 의해 상속받은 메서드를 자신의 클래스의 의도나 목적에 맞게 변경하는 것을 의미합니다.
2-2. 메서드 오버라이딩의 조건
- 오버라이딩할 메서드의 선언부(메서드명, 매개변수, 반환 타입)가 부모(조상) 클래스의 메서드와 일치해야 합니다.
(메서드의 선언부는 변경할 수 없고, 구현부만 변경할 수 있습니다.)
- 접근 제어자를 부모(조상) 클래스의 메서드보다 좁은 범위로 변경할 수 없습니다.
부모 클래스의 메서드가 protected라면, 접근 제어자의 범위가 더 넓거나 같은 protected, public으로만 변경 가능합니다.
(접근 제어자의 종류 : public, protected, default, private)
- 예외 선언(Exception declaration)은 부모(조상) 클래스보다 많이 선언할 수 없습니다.
class ExposureNameAge {
String name = "wonjun";
int age = 25;
public void exposureInfo() {
System.out.printf("이 사람의 이름은 %s이고 나이는 %d살입니다.%n", name, age);
}
}
class ExposureNameAgeJob extends ExposureNameAge {
String job = "student";
// 메서드 오버라이딩(Overriding)
public void exposureInfo() {
System.out.printf("이 사람의 이름은 %s이고 나이는 %d살이며 직업은 %s입니다.%n", name, age, job);
}
}
2-3. 메서드 오버로딩, 메서드 오버라이딩의 차이점
- 둘의 연관성은 없으며, 명확한 차이를 가지고 있습니다.
(1) 메서드 오버로딩(Overloading)
- 기존에 없던 특정 메서드에 대해 메서드명을 동일하게, 매개변수의 개수나 타입을 다르게 하여 새로운 메서드를 재정의하는 것을 의미합니다. (클래스의 상속과 관련이 없습니다.)
(2) 메서드 오버라이딩(Overriding)
- 부모 클래스로부터 상속받은 메서드의 내용을 자식 클래스에서 자신의 목적에 맞게 재정의(변경)하는 것을 의미합니다. (클래스 상속과 연관이 있습니다.)
3. 참조변수 super, 생성자 super()
3-1. 참조변수 super의 정의와 특징
- 객체(인스턴스) 자신을 가리키는 참조변수입니다. (정의는 참조변수 this와 유사합니다.)
- 부모(조상) 클래스의 멤버와 자신의 멤버를 구분할 때 사용합니다.
- 참조변수 this와의 차이점 : this 참조변수의 경우 인스턴스 자신을 가리키는 참조변수이며 생성된 인스턴스의 주소를 가지고 있습니다. 모든 인스턴스 메서드에 지역변수로 숨겨진 채 존재합니다.
class ParentClass {
int a = 10;
}
class ChildClass extends ParentClass {
int b = 100;
void numberAdd() {
System.out.println("a = " + a);
System.out.println("this.b = " + this.b);
System.out.println("super.a = " + super.a);
}
}
3-2. 부모(조상) 클래스의 생성자 super()의 정의와 특징
- 부모 클래스의 인스턴스가 생성될 때 이에 대한 멤버를 초기화할 수 있는 메서드입니다.
- 자식 클래스의 인스턴스를 생성하면 자식 클래스의 멤버와 부모 클래스의 멤버가 합쳐진 채 하나의 인스턴스가 생성됩니다.
- 이때 생성자 super()는 부모(조상) 클래스의 멤버들도 초기화해야 하기 때문에, 자식 클래스 내부 생성자에서 반드시 첫 문장에 부모 클래스의 생성자를 호출해야 합니다.
4. 패키지(Package)
4-1. 패키지(Package)의 정의와 특징
- Java에서 패키지란, 서로 연관 있는 클래스와 인터페이스를 묶어놓은 개체이며, 물리적으로 하나의 디렉토리를 의미합니다.
- 패키지 내부에도 또 다른 패키지(서브 패키지)를 넣을 수 있으며 "." 으로 구분합니다.
- 클래스의 실제 이름은 패키지명이 포함된 것입니다.(String 클래스의 경우 java.lang.String입니다.)
- rt.jar 파일은 Java의 클래스들을 압축한 파일이며 Java API의 기본 클래스들을 압축한 파일입니다.
4-2. 패키지의 선언
- 패키지를 선언할 때, package 키워드를 이용하여 한 번만 선언하며 주석을 제외한 소스코드의 맨 첫 번째 줄에서 선언해야 합니다.
- 하나의 소스 파일에 두 개 이상의 클래스가 포함된 경우 모두 동일한 패키지에 속하게 됩니다.
(단 하나의 소스파일에 1개의 public 클래스만 허용합니다.)
- 모든 클래스는 하나의 패키지에 속해 있으며, 패키지가 선언되지 않은 클래스는 이름 없는 패키지(Default package)에 속하게 됩니다.
4-3. 클래스 패스(Class path) 설정
- 클래스 패스는 클래스 파일을 찾는 경로이며 "." 으로 구분합니다.
- 클래스 패스에 패키지가 포함된 폴더나 jar(.jar) 파일을 나열합니다.
- 클래스 패스가 없으면 자동적으로 현재 폴더가 포함되지만, 클래스 패스를 지정할 때는 현재 디렉토리(.)도 추가해 주어야 합니다.
5. import 문, static import 문
5-1. import 문
- 사용할 클래스가 속한 패키지를 지정할 때 import 키워드를 사용해 지정합니다.
- import 문을 사용하면 클래스를 사용할 때 패키지명을 생략할 수 있습니다.
- Java의 핵심 클래스들은 java.lang 패키지에 속한 클래스이므로 java.lang에 속한 클래스들은 import 하지 않아도 사용할 수 있습니다.
- 이름이 같은 클래스가 속한 두 패키지를 import할 땐, 클래스 앞에 패키지명을 모두 붙여줘야 합니다.
- import 문은 컴파일 시 처리되므로 프로그램의 성능에 별도의 영향을 미치지 않습니다.
5-2 import 문 선언 방법
- import 문은 패키지문과 클래스 선언 사이에 위치합니다.
- import 문 선언 방법
- 문법 : import 패키지명.클래스명; / import 패키지명.*;
6. 실습 내용
6-1. 메서드 정의와 호출
// 메서드 정의와 호출
import java.util.Scanner;
public class Print99Dan {
static void print99(int dan) {
for (int i = 1; i <=9; i++) {
System.out.printf("%d * %d = %d%n", dan, i, (dan * i));
}
}
public static void main(String[] args) {
// 사용자로부터 입력받을 구구단 단수
int dan;
System.out.printf("구구단 몇 단을 출력할까요? ");
Scanner scanInt = new Scanner(System.in);
// 사용자로부터 입력받은 단을 정수로 변환
dan = scanInt.nextInt();
// 사용자가 입력한 단수를 파라미터로 전달하여 함수 호출
print99(dan);
}
}
<실행 결과>
6-2. 3항 연산자, 재귀 호출(Recursive call), 팩토리얼(Factorial), 피보나치 수열
// 3항 연산자, 재귀호출(reculsive call) 실습, 팩토리얼, 피보나치 수열
import java.util.Scanner;
public class MyMathClass {
// 3항 연산자
static int max1(int a, int b) {
return a > b ? a : b;
}
// 3항 연산자를 사용하지 않음
static int max2(int a, int b) {
if (a > b) return a;
else return b;
}
// 재귀호출(reculsive call)
public static int arrayMax(int[] a, int n) {
// 재귀호출을 이용해 큰 값을 탐색한다.
int x;
if (n == 1) return a[0];
else x = arrayMax(a, n -1 );
if ( x > a[n - 1]) return x;
else return a[n - 1];
// 만약 삼항연산자를 이용한다면?
// return x > a[n - 1] ? x : a[n - 1];
}
// 팩토리얼(Factorial)
static long factorial(int x) {
if ( x == 1) return x;
else
return x * factorial(x - 1);
}
// 피보나치 수열
public static int fibonacci(int n) {
if (n == 1 || n == 2) return 1;
else
return fibonacci(n - 1) + fibonacci(n - 2);
}
public static void main(String[] args) {
int x, y, n;
long xx;
int arr[] = {1010, 10, 20, 56, 78, 24, 456};
x = max1(10, 20);
System.out.println(x);
y = max2(10, 200);
System.out.println(y);
System.out.print(" \n arrayMax : ");
System.out.println(arrayMax(arr, 6));
// 사용자로부터 값을 입력받는다. (팩토리얼, 피보나치 수열(재귀호출) 실습)
System.out.printf("input a number (n!) : ");
Scanner scanInt = new Scanner(System.in);
n = scanInt.nextInt();
System.out.printf(n + "! = ");
System.out.println(factorial(n));
System.out.println(n + "개의 fibonacci 수열");
for (int i = 1; i <= n; i++) {
System.out.printf(fibonacci(i) + " ");
}
}
}
<실행 결과>
- 학부에서 수강했던 전공 수업 내용을 정리하는 포스팅입니다.
- 내용 중에서 오타 또는 잘못된 내용이 있을 시 지적해 주시기 바랍니다.
'전공 수업 > 객체 지향 프로그래밍(Java)' 카테고리의 다른 글
[13주 차] - 제어자, 접근 제어자, 캡슐화, 다형성 (0) | 2022.11.27 |
---|---|
[11주 차] - 메서드 오버로딩, 생성자, 참조변수 this, 멤버 변수 초기화 (0) | 2022.11.10 |
[10주 차] - 클래스 변수, 인스턴스 변수, 지역 변수, 클래스 메서드와 인스턴스 메서드 (0) | 2022.11.06 |
[9주 차] - 메서드의 개념, 클래스와 객체 (0) | 2022.11.04 |
[7주 차] - Math.random() 메서드, 제어문(반복문, 조건문)을 사용한 코드 예제 (0) | 2022.10.12 |
댓글