학습 개요
- 클래스를 정의하고, 정의된 클래스를 사용하는 것에 관해 살펴봄
static
필드와 메소드 및final
필드와 메소드의 의미 학습- 객체의 생성과 초기화 및 메소드의 오버로딩 학습
- 기존 클래스를 이용해 새로운 클래스 정의하기 위한 클래스 상속 개념과 오버라이딩 이해
학습 목표
- 클래스 정의 문법 숙지할 수 있음
static
과final
키워드의 의미와 사용법을 설명할 수 있음- 메소드 오버로딩과 오버라이딩 구분하고 적용 가능함
- 상속 개념 활용해 클래스 정의 가능함
강의록
클래스 정의와 사용
static
필드
static
필드1 2 3 4 5
class Circle { int x, y; int radius; static int count; }
정적 필드 or 클래스 변수
- 클래스의 모든 객체가 공유하는 데이터
- 객체의 생성이 없어도 항상 사용 가능
- 어떤 객체도 값을 변경할 수 있음
- 사용 방법
클래스이름.정적필드
객체변수.정적필드
도 가능
- 클래스의 모든 객체가 공유하는 데이터
static
메소드
static
메소드1 2 3 4 5
class Math { public static double sqrt(double a) { } }
- 정적 메소드 or 클래스 메소드
- non-static 메소드와 달리 객체와 무관하게 호출되고 실행
- 메소드의 몸체에서
this
사용 불가 static
메소드는static
필드를 다룸
- 메소드의 몸체에서
- non-static 메소드와 달리 객체와 무관하게 호출되고 실행
- 호출 방법
클래스이름.정적메소드()
- 객체와 무관하므로 클래스 이름으로 호출 가능
객체변수.정적메소드()
도 가능
정적 메소드를 호출하는 예시
1
Math.sqrt(2.0);
1
Integer.parseInt("102");
final
필드와 final
메소드
final
필드- 상수 데이터
- 선언할 때 초기 값 지정 필요
static과 자주 함께 사용
1
final static double PI = 3.141592;
final
메소드- 자식 클래스로 상속은 가능하나 자식 클래스에서 재정의(오버라이딩) 할 수 없는 메소드
필드의 초기화
- 객체 생성 시 데이터 필드에 초기 값 지정하는 것
- 데이터 필드는 자동으로 초기 값(0,false 또는 null)이 주어질 수 있음
- 클래스 변수(
static
필드)는 프로그램 시작 시에 초기화 됨
- 객체 초기화를 위해 초기화 블록을 사용할 수 있음
- 초기 값 지정을 위한 코드로, 클래스 정의에서 메소드 바깥의 임의 위치에 들어갈 수 있음
static
필드는static
초기화 블록을 사용
필드 초기화 방법의 실행 순서
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
class IniTest { static int nStatic = 0; int nValue = 1; // 초기값 선언하지 않을경우 0으로 자동 초기화 { // 초기화 블록 nValue = 2; // 초기값 변경 } static { nStatic = 1; // 클래스 변수 초기화 블록 } public IniTest() { // 생성자에서 초기화 nValue = 3; // 초기값 변경 } }
static
필드의 선언문에서 초기화static
초기화 블록 실행- non-static 필드의 선언문에서 초기화
- non-static 초기화 블록 실행
- 생성자 실행
필드 초기화 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import java.awt.Point; class Rectangle { public int width = 0; // non-static 변수 초기화 public int height = 0; public Point origin; // Point 클래스 : 좌표상의 어떤 위치를 나타내는데 사용하는 클래스 public Rectangle() { origin = new Point(0,0); } public Rectangle(Point p, int w, int h) { // 생성자를 사용해 데이터 필드 초기화 origin = p; width = w; height = h; } }
1 2 3 4 5 6
public class Main { public static void main(String args[]) { Point originOne = new Point(23, 94); // Point 클래스에 x와 y좌표값을 저장하기 위한 멤버 변수 존재 Rectangle rectOne = new Rectangle(originOne, 100, 200); } }
메소드 오버로딩
1
2
3
4
5
6
7
8
9
class PrintStream {
public void println() {
}
public void println(String x) {
}
}
- 매개 변수의 개수나 매개 변수의 자료형이 다르면 같은 이름의 메소드를 한 클래스에서 여러 개 정의 가능
- 매개 변수의 개수와 자료형이 일치하면 중복 정의 불가
- 리턴형과 접근 제어자는 구분의 기준이 되지 못함
- 메소드 시그니처를 가지고 메소드 구분
- 메소드의 이름, 매개 변수 리스트
- 메소드를 호출할 때, 가장 가까운 매개 변수 목록을 가진 메소드 호출
ex)
1 2 3 4 5
System.out.println(); // 인자 없음 System.out.println("문자열"); // 인자는 String System.out.println(241); // 인자는 int System.out.println(34.5); // 인자는 double mc.add(10, 10.5); // add(int, int)와 add(double, double) → 자동 형변환 후 add(double, double) 호출
클래스와 객체의 사용 예
1
2
3
4
5
6
7
8
9
10
11
12
13
class Cylinder {
private Circle c; // 원 (Circle 클래스 변수 정의)
private int h; // 높이
public Cylinder(Circle a, int b) {
c = a;
h = b;
}
public double getVolume() {
return c.getArea() * h;
}
}
1
2
3
4
5
6
7
8
9
10
11
class Circle {
double radius;
public Circle(double a) {
radius = a;
}
public double getArea() {
return radius * radius * 3.14;
}
}
1
2
3
4
5
6
7
public class Main {
public static void main(String args[]) {
Circle c = new Circle(3); // Circle 객체 생성
Cylinder cy = new Cylinder(c, 10);
System.out.println(cy.getVolume());
}
}
상속
클래스의 재사용
합성
1 2 3
class Line { Point begin, end; }
- 기존 클래스를 새로운 클래스 정의에서 데이터 필드의 자료형으로 사용
- has-a 관계
상속
- 기존 클래스(부모)를 사용해 새로운 클래스(자식)를 정의
- 코드의 중복 작성을 줄이고 프로그램의 확장성 증가
- 기존 클래스를 확장 or 특화하는 것
- 자식 is -a 부모의 관계
클래스의 상속
- 상속은 부모 클래스와 자식 클래스 간의 관계
- 자식 클래스가 부모 클래스의 필드와 메소드를 상속 받음
- 기존 클래스를 상속 받을 때 키워드
extends
사용ex)
1 2 3 4
class Manager extends Employee { // 자식 클래스(새로운 클래스) Manager // 부모 클래스(기존 존재 클래스) Employee }
- 자식 클래스에서 상속받은 메소드를 오버라이딩(재정의) 할 수 있음
- 클래스의 상속은 단일 상속만 가능
- 인터페이스 상속의 경우는 다중 상속 가능
클래스의 상속
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
class CSuper { private int f1; public int f2; public void setPrivate(int i) { f1 = i; } public void setPublic(int i) { f2 = i; } private void mPrivate() { f1 = 30; } } class CSub extends CSuper { private int f3; public int f4; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public class Main { public static void main(String args[]) { CSub sub = new CSub(); // sub.f1 = 40; // 오류 발생 sub.f2 = 50; // sub.f3 = 60; // 오류 발생 sub.f4 = 70; sub.setPrivate(10); sub.setPublic(20); // sub.mPrivate(); // 오류 발생 } }
메소드 오버라이딩
- 부모로부터 상속 받은 메소드의 몸체를 자식 클래스에서 재정의하는 것
- 부모와 자식에서 같은 이름의 메소드가 다른 기능 수행하게 됨
- 오버라이딩 방법
- 메소드의 이름, 인자의 개수와 자료형, 반환형이 같은 메소드를 정의
- 단, 반환형은 서브 타입(상속 관계에서 자식 클래스)도 가능함
- 접근 제어자의 가시성(접근 범위)은 같거나 커져야 함
protected
인 경우protected
또는public
public
인 경우public
만 가능
메소드 오버라이딩
1 2 3 4 5 6 7 8 9 10 11
class Shape { // 부모 클래스 public double getArea(double h, double w) { return h * w; } } class Triangle extends Shape { // 자식 클래스 public double getArea(double h, double w) { return h * w * 0.5; } } // getArea 메소드 재정의 -> 메소드 오버라이딩
1 2 3 4 5 6
public class Main { public static void main(String args[]) { Shape t = new Triangle(); // Shape 자식 클래스 Triangle 객체 생성 System.out.println(t.getArea(3.0, 4.0)); // 3.0 * 4.0 * 0.5 = 6.0 출력 } }
this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Circle {
static double PI = 3.14;
double radius = 2.0; // 반지름
public double getArea() {
return this.radius * this.radius * PI; // c1.radius 호출 시 this값은 c1값을 가지게 됨
}
public void display() {
System.out.println("반지름:" + this.radius + " 면적:" + this.getArea());
}
public static void main(String args[]) {
Circle c1 = new Circle();
Circle c2 = new Circle();
c2.radius = 5.0; // c2의 반지름을 5.0으로 변경
c1.display();
c2.display();
}
}
- 메소드 호출 시, 숨은 인자로
this
가 메소드에 전달됨this
는 현재 객체에 대한 참조 값을 가지고 있음c1.display()
과c2.display()
의 결과가 다른 이유임
- 인스턴스 메소드나 생성자에서 필드를 참고하거나 메소드를 호출할 때 사용 가능(생략 가능)
this.필드 이름
this.메소드 이름(인자)
super
1
2
3
4
5
6
7
class CSuper {
public double x = 0;
public void f() {
System.out.println("CSuper");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
class CSub extends CSuper {
public double x = 1;
public void f() {
System.out.println("CSub");
}
public void print() {
System.out.println("super.x");
super.f();
}
}
this
와 같으나 자료형이 부모 클래스 유형임- 자식 클래스의 인스턴스 메소드나 생성자에서 사용됨
this
와 마찬가지로static
메소드에서 사용할 수 없음
- 부모 클래스에서 오버로딩 당한 메소드를 호출하거나 상속되었으나 감춰진 필드에 접근할 때 필요함
super.필드 이름
super.메소드 이름(인자)
this
와super
의 사용1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
class CSuper { // 부모 클래스 public double x; } class CSub extends CSuper { // 자식 클래스 public double x; public CSub(double new_x) { this.x = new_x; // 자식 클래스의 x 필드 값 지정 super.x = new_x * 10; // 부모 클래스의 x 필드 값 지정 } public double getSuper() { return super.x; // 상속받은 x 리턴 } public double getSub() { return this.x; // 재정의한 x 리턴 } }
1 2 3 4 5 6 7 8 9 10
public class Main { public static void main(String args[]) { CSub sub = new CSub(10.0); System.out.println(sub.x); // 10.0 System.out.println(sub.getSuper()); // 100.0 출력 System.out.println(sub.getSub()); // 10.0 출력 } }
상속과 생성자
this()
- 같은 클래스의 다른 생성자를 호출하는 것
super()
- 자식 클래스의 생성자에서 부모 클래스의 생성자를 호출하는 것
- 상속받은 데이터 필드를 초기화하기 위해 사용
- 자식 클래스의 생성자 몸체에서 부모 클래스 생성자의 명시적 호출이 없다면, 인자가 없는 생성자인
super()
가 자동 호출됨
- 자식 클래스의 생성자에서 부모 클래스의 생성자를 호출하는 것
this()
와super()
는 생성자 몸체의 맨 앞에 위치해야 함
super
와 super()
의 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Cyliner extends Circle {
private double height;
static final double PI = 3.14;
public Cylinder() {
super(); // 부모클래스의 생성자 초기화
height = 1.0;
}
public Cylinder(double radius, double h) {
super(radius); // double값을 가지는 인자로 부모클래스의 생성자 초기화
this.height = h;
}
public double getHeight() {
return height;
}
public void setHeight(double h) {
this.height = h;
}
/**
* 원기둥의 겉넓이 계산
* 2 * 3.14 * 반지름 * 높이 + 2 * 원의 면적
*/
public double getArea() { // Cylinder의 getArea() 메소드
return 2 * PI * getRadius() * height + 2 * super.getArea(); // 상속받은 this.getRadius() 호출
}
/**
* 원기둥의 부피 계산
* 원의 면적 * 높이
*/
public double getVolume() {
return super.getArea() * height; // Circle의 getArea() 메소드 호출
}
public String toString() {
return "Cylinder of radius = " + getRadius() + " height = " + height;
}
}
1
2
3
4
5
public class Main {
public static void main(String args[]) {
System.out.println(new Cylinder(3, 10).getVolume());
}
}
메소드 체이닝
- 하나의 명령문에서 동일 객체에 대해 연속적으로 메소드 호출을 하는 프로그래밍 기법
- 메소드 체이닝에 사용되는 메소드는 현재 객체의 참조 값(
this
)을 반환해야 함
- 메소드 체이닝에 사용되는 메소드는 현재 객체의 참조 값(
- 메소드 체이닝은 프로그램의 가독성을 향상 시키고, 코딩을 단순화 함
ex)
1
p.setName("홍길동").setAge(30).setAddress("서울");
메소드 체이닝의 사용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
class People { String name; int age; String address; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Main { public static void main(String[] args) { People p = new People(); p.setName("홍길동"); p.setAge(30); p.setAddress("서울"); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14
class People { String name; int age; String address; public String getName() { return name; } public People setName(String name) { this.name = name; return this; } }
1 2 3 4 5 6
public class Main { public static void main(String[] args) { People p = new People(); p.setName("홍길동").setAge(30).setAddress("서울"); } }
학습 정리
- 클래스를 정의할 때, 클래스의 모든 객체가 공유하는 데이터는
static
데이터 필드로 정의함 - 클래스 정의에 있는 데이터 필드의 선언문, 초기화 블록, 생성자를 통해서 객체 생성 시 필요한 데이터 필드의 초기값 지정 가능
- 메소드 오버로딩이란 한 클래스에서 이름이 같은 여러 메소드가 존재하는 상황을 말함
- 이때 오버로딩된 메소드끼리는 매개 변수 목록이 달라 구별될 수 있음
- 부모로부터 상속받은 메소드의 몸체를 자식 클래스에서 다시 정의하는 것을 메소드 오버라이딩이라고 함
- 인스턴스 메소드와 생성자에서 숨은 인자인
this
,super
사용할 수 있음 this()
는 같은 클래스에 있는 다른 생성자를,super()
는 자식 클래스 생성자에서 부모 클래스 생성자를 호출하기 위한 것
연습 문제
키워드
final
에 관한 설명으로 틀린 것은?a.
final
클래스의 객체를 생성할 수 없다.- 키워드
final
final
클래스의 자식 클래스를 정의할 수 없음- 부모 클래스의
final
메소드는 자식 클래스로 상속될 때 재정의될 수 없음 final
변수는 상수로 사용됨
- 키워드
Circle 클래스에서 원주율 PI를 상수로 선언하기 위해 밑줄 친 ㉠에 들어갈 적당한 내용은 무엇인가?
1 2 3 4 5 6 7 8 9 10
class Circle { // ㉠ private double radius; public Circle(double radius) { // ㉡ } public double getArea() { return radius * radius * PI; } }
a.
static final double PI = 3.14;
Circle 클래스의 생성자에서 밑줄 친 ㉡에 들어갈 적당한 내용을 작성하시오
a.
this.radius = radius;