본 내용은 KH정보교육원 에서 제공한 교재 내용을 개인적인 공부를 위한 목적으로 작성되었습니다.
만약 아래의 내용을 무료로 배부하거나 상업적으로 이용할 경우 법적 처벌을 받을 수 있음을 경고합니다.
객체지향 프로그래밍이란 객체를 이용하는 프로그래밍 방식을 말한다. 객체(Object)란 컴퓨터, 고객, 학생, 자동차 등 현실 세계에서 흔히 찾아볼 수 있는 대상을 추상화(Abstraction) 하여 프로그램 상에서 만들어낸 결과물이다. 예를 들어 자동차를 만들어야 한다면, 자동차의 각 부품들을 별개로 제작한 다음 필요한 부품을 조립하는 방식으로 자동차를 완성해 나가는 것이 객체지향 프로그래밍 방식이라고 보면 된다.
객체 지향 프로그래밍의 기술적 특징은 추상화(Abstraction), 캡슐화(EnCaptulation), 상속(Inheritance), 다형성(Polymorphism)이 있다. 이 기술들을 이용하여 프로그램을 구현하는 것이 객체지향 프로그래밍이다.
프로그램에서 필요로 하는 데이터와 데이터를 처리하기 위한 동작들을 정리하는 과정을 추상화라고 하며, 추상화 과정에서는 소스 코드의 구현이 아닌 데이터들의 객체화와 객체들 간의 관계에 대한 모델링 과정이 중요하다.
클래스(Class)는 추상화 한 내용을 정리한 설계도와 같은 것이고, 객체는 클래스를 실제 사용한 프로그램 상에서의 결과물이라고 보면 된다.
객체지향 관점에서의 캡슐화는 여러 자료형 변수들을 Class 영역 안에서 접근 제한자 private을 사용하여 멤버변수(Field)로 선언하여 클래스 내부의 데이터에 접근하지 못하도록 막는 것이 핵심이다. 이렇게 클래스 내부의 데이터를 외부에서 접근하지 못하도록 설정함으로써 객체가 가진 데이터를 보호할 수 있게 되며, 이를 정보 은닉(Information Hiding)이라고 한다.
객체를 만들기 위해 필요한 객체의 설계도라고 볼 수 있다. 자료형이 다른 변수들을 배열처럼 메모리에 연속 나열 할당하기 위한 순서를 지정하는 역할을 하며, 클래스 블록 { }이 캡슐, 즉 보호막의 역할을 하여 객체 외부에서 멤버변수(Field)로의 접근을 제한하며, 멤버변수들을 하나의 타입으로 묶어 주는 것이 클래스이다.
추상화가 이루어진 클래스로부터 데이터가 존재하는 실체로 만드는 과정을 인스턴스화(Instantiation)라고 하며, 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스(Instance)라고 한다. 자바에서는 객체, 즉 인스턴스는 무조건 메모리 힙(Heap) 영역에 할당하도록 정하였으며 할당된 객체의 주소를 기록할 참조(Reference)변수를 사용하여 객체를 참조하도록 정해 두었다.
클래스에 속한 멤버변수(Field)와 멤버함수(Method)들을 클래스 외부(다른 클래스)에서의 접근에 대한 제한을 걸기 위해 사용되는 단어를 말한다.
(+) public
(#) protected
(~) default (package private)
(-) private
접근 제한자의 공개 범위는 public > protected > default > private 순으로 좁아지며, protected 접근 제한자는 상속 관계가 아니면 잘 사용되지 않는다.
access.sample.TestPrivate.java
01 package access.sample;
02
03 public class PrivateSample {
04 private int num; // 선언된 클래스 안에서만 접근 가능
05 public int getNum() { // 값 호출을 위한 Getter
06 return num;
07 }
08 public void setNum(int num) { // 값 대입을 위한 Setter
09 this.num = num;
10 }
11 }
access.test.TestPrivate.java
01 package access.test;
02
03 public class TestPrivate {
04 public static void main(String[] args) {
05 privateSample psamp = new PrivateSample();
06
07 psamp.num = 10; // private이므로 접근 제한 에러 발생
08
09 psamp.setNum(10); // public이므로 접근 가능
10 System.out.println(psamp.getNum()); // public이므로 접근 가능
11 }
12 }
access.sample.PublicSample.java
01 package access.sample;
02
03 public class PublicSample {
04 public int.num;
05 }
access.test.TestPublic.java
01 package access.test;
02
03 public class TestPublic {
04 public static void main(String[] args) {
05 publicSample pbt = new PublicSample();
06
07 pbt.num = 10; // public이므로 접근 가능
08 System.out.println(pbt.num); // 접근 가능
09 }
10 }
access.sample.Parent.java
01 package.access.sample;
02
03 public class Parent {
04 protected in num;
05 }
access.sample.Child.java
01 package access.sample
02
03 public class Child extends Parent {
04 public void insert() {
05 num = 10; // protected 멤버는 상속된 후손 내에서만 사용 가능함
06 }
07 public void out() {
08 System.out.println(num);
09 }
10 }
access.test.TestProtected.java
01 package inherit.test;
02
03 import inherit.sample.Child;
04 import inherit.sample.Parent;
05
06 public class TestProtected {
07 public static void main(String[] args) {
08 Parent parent = new Parent();
09 Child child = new Child();
10
11 parent.num = 10; // protected 멤버는 다른 패키지의 클래스 밖에서는 접근 불가
12 child.insert();
13 child.out():
14 }
15 }
access.sample.DefaultSample1.java
01 package access.sample;
02
03 public class DefaultSample1 {
04 int num1; // default는 단어로 명시되지 않음. 접근제한자가 생략되면 default임
05 }
access.test.DefaultSample2.java
01 package access.test;
02
03 public class DefaultSample2 {
04 int num2;
05 }
access.test.TestDefault.java
01 package access.test;
02
03 import access.sample.DefaultSample1; // 다른 패키지에 속한 클래스는 import 해야 사용 가능
04
05 public class TestDefault {
06 public static void main(String[] args) {
07 DefaultSample1 dt1 = new DefaultSample1();
08 DefaultSample2 dt2 = new DefaultSample2();
09
10 dt1.num1 = 100; // 다른 패키지의 클래스에서는 접근 불가능
11 dt2.num2 = 100; // 같은 패키지의 클래스에서는 접근 가능
12 }
13 }
클래스는 클래스 해더(Header) 시그니처(Signature)와 클래스 바디(Body)로 구성된다.
클래스 해더 시그니처는 클래스의 이름과 종류를 지정하는 부분이고, 클래스 바디 안에는 클래스의 멤버들이 정의되는 부분이다.
클래스 작성 시 클래스의 이름과 종류를 정의하는 클래스 시그니처는 다음과 같다.
[접근제한자] [클래스식별자] class 클래스이름
클래스의 접근에 제한을 두는 예약어로 public과 default (package private) 두 가지가 사용된다.
public class 클래스명 : 패키지 안 / 밖 모두 접근(사용) 가능한 클래스를 의미한다.
class 클래스명 : 선언된 패키지 안에서만 접근(사용) 가능한 클래스를 의미한다.
클래스의 종류를 지정하는 예약어로 final, abstract 두 가지가 사용될 수 있다.
- public final class 클래스명 : 종단 클래스, 더 이상 상속시킬 수 없는 클래스, 서브(후손) 클래스를 만들 수 없다.
- public abstract class 클래스명 : 추상(미완성된) 클래스, 상속을 이용해 후손 클래스가 부모의 미완성된 기능을 완성시킨다.
oop.sample.Point.java
01 package oop.sample;
02
03 public class Pont { // 클래스 헤더
04 private int posX; // 멤버필드
05 private int posY; // 멤버필드
06
07 // 메소드
08 public int getPostX() { return posX; }
09 public void setPostX(int posX) { this.posX = posX; }
10 public int getPostY() { return posY; }
11 public void setPostY(int posY) { this.posY = posY; }
12
13 // 생성자
14 public Point() {}
15 public Point(int posX, intposY) {
16
17 this.posX = posX;
18 this.posY = posY;
19 }
20 }
oop.sample.TestPoint.java
01 package oop.test;
02
03 import oop.sample.Point;
04
05 public class TestPoint {
06 public static void main(String[] args) {
07 Point p1 = new Point();
08 Point p2 = new Point(10, 20);
09
10 System.out.println("p1 : " + p1.getPosX() + ", " + p1.getPosY());
11 System.out.println("p2 : " + p2.getPosY() + ", " + p2.getPosY());
12
13 p1.setPosX(12);
14 p1.setPosY(23);
15 System.out.println("p1 : " + p1.getPosX() + ", " + p1.getPosY());
16 }
17 }
클래스에 소속된 멤버 변수와 멤버 상수를 필드(Field)라고 하며, 클래스 내부에 선언한다.
접근제한자 식별자 자료형 변수명 [= 초기값];
접근제한자 자료형 변수명;
클래스 { } 안에 선언되며, 클래스에 대한 객체(Instance == Object)를 동적 메모리에 할당할 때 객체 공간 안에 생성되는 필드들을 말한다.
01 public class Var1 {
02 public int num1; // 인스턴스(Instance) 멤버 변수
03 }
클래스 { } 안에 선언되며 멤버 변수 선언시 자료형 앞에 static 키워드를 붙여 선언한 필드를 말하며, 해당 클래스형 객체들이 모두 공유하는 필드가 된다.
접근제한자 static 자료형 필드명;
01 public class Var2 { 02 private static int num2; // static(정적) 멤버변수(클래스 변수) 03 // var2 클래스에 의해 생성된 모든 객체가 num2 필드를 공유하게 된다. 04 }
메소드 { } 내에 선언된 변수로 메소드 내에서만 사용이 가능하며, 메소드 실행시 메모리에 생성되었다가 메소드가 종료시 메소드와 같이 메모리에서 소멸되어 사용할 수 없게 된다.
01 public class Var3 { 02 public void method3() { 03 int num3 = 10; // method3 메소드 안의 지역 변수임 04 } 05 }
클래스{ } 내에 작성되는 함수로 멤버변수들에 대한 기능 및 동작을 정의한다.
클래스에 소속된 일반 메소드로, 클래스에 대한 객체의 주소(레퍼런스)를 전달받아 해당 주소위치에 있는 객체의 인스턴스 변수 값들을 연산 처리한다.
- 정의 : 접근제한자 반환자료형 메소드명 ([자료형 매개변수명, ......])
- 호출 : [자료형 변수명 =] 레퍼런스.메소드명 ([전달값, ......]);
클래스 메소드 (static method)
클래스 멤버함수 작성시 메소드 반환자료형 앞에 static 키워드를 사용한 메소드이다. 인스턴스 메소드와 다르게 메소드 호출시 객체의 레퍼런스를 사용하지 않고 클래스명을 사용하여 호출한다.
- 정의 : 접근제한자 static 반환자료형 메소드명 ([자료형 매개변수명, ......])
- 호출 : [자료형 변수명 =] 클래스명.메소드명 ([전달값, ......]);
01 [접근제한자][식별자][리턴타입][메소드명][매개변수1, 매개변수2, ...) { // 메소드 헤드 02 // 메소드 Body : 동작, 기능 03 // 처리내용에 대한 코드 구현부 04 return [값]; 05 }
메소드 시그니처(Signature) 선언
메소드 선언부에는 메소드의 접근제한자, 메소드의 리턴타입, 메소드 이름, 매개변수를 선언한다.
프로그램 구동시 모든 메소드들은 메소드 내의 소스 코드에 대한 실행이 끝나면 해당 메소드가 호출된 위치로 되돌아 가도록 되어 있다. 메소드에서 호출 위치로 되돌아 갈 때 메소드의 수행결과를 가지고 되돌아 갈 수 있는데 이 결과값을 반환값이라고 한다.
해당 메소드를 호출할 때 사용하는 소스 코드 묶음의 이름이며, 자바의 Naming Rule 규칙에 맞게 작성한다.
매소드는 매개변수가 없는 경우와 매개변수가 있는 경우로 구분할 수 있으며, 매개변수가 있는 메소드일 때 일반 변수 선언과 동일하게 (자료형 변수명)으로 선언한다. 매개변수가 있는 메소드일 경우 메소드 호출시 매개변수에게 넘길 전달값(Argument)을 메소드 ( ) 안에 기입하여야 한다.
메소드 구현부 맨 마지막에는 return 문이 무조건 존재한다. 메소드가 호출 위치로 리턴될 때 반환값 없이 리턴될 때에는 return; 으로 표현되며 생략될 수도 있다. 반환값이 있는 리턴일 경우에는 return 값; 으로 표기하며 메소드 헤드의 반환자료형과 반환값의 자료형은 반드시 같아야 한다.
하나의 클래스 영역 내에서 이름이 같은 메소드를 여러 개 정의할 수 있다. 이러한 경우에 메소드가 오버로딩 되었다고 표현하며, 동일한 기능을 수행하는 메소드가 전달값을 각각 다르게 받아서 구동되는 경우 매번 메소드의 이름을 다르게 작성하는 것보다 훨씬 더 프로그램 가독성을 좋게 할 수 있다.
메소드 오버로딩 시의 규칙은 메소드 이름은 반드시 같아야 하며, 매개변수의 자료형과 개수는 다르게 구성되어야 한다.
oop.method.sample.OverloadingSample.java
01 package oop.method.sample;
02
03 public class OverloadingSample {
04 public void out() {
05 System.out.println("out() 메소드 실행!");
06 }
07 public void out(int num) { // 메소드 이름은 같고 매개변수가 다르다. 오버로딩 성립!
08 System.out.println("out (" + num + ") 메소드 실행!");
09 }
10 }
oop.method.testOverloading.java
01 package oop.method.test;
02
03 public class TestOverloading {
04 public static void main(String[] args) {
05 OverloadingSample osamp = new OverloadingSample();
06 osamp.out();
07 osamp.out(123);
08 }
09 }
자바에서는 선언된 클래스를 프로그램에서 사용하고자 할 때, 해당 클래스에 대한 객체(Object)를 동적 메모리에 new 키워드를 사용해 할당하도록 정하였다. 이렇게 할당된 객체의 위치(주소)를 해당 클래스 타입의 레퍼런스 변수에 기록하고, 레퍼런스가 가진 주소를 통해 객체에 접근해서 객체 안의 인스턴스 변수 값들을 처리하는 방식의 객체지향 프로그래밍 방법을 사용한다.
oop.sample.vo.Person.java
01 package oop.sample.vo;
02
03 public class Person
04 private String name;
05 private int age;
06 private char gender;
07
08 public Person() { }
09 public Person(String name, int age, char gender) {
10 this.name = name;
11 this.age = age;
12 this.gender = gender;
13 }
14
15 public void display() {
16 System.out.println(name + ", " + age + "세, " + gender + "자");
17 }
18 }
oop.test.TestPerson.java
01 package oop.test;
02
03 import oop.sample.vo.Person;
04 public static void main(String[] args) {
05 Person p1 = new Person();
06 Person p2 = new Person("홍길동", 25, '남');
07
08 p1.display();
09 p2.display();
10 }
11 }
클래스에 대한 객체 생성시 인스턴스 변수의 초기화를 담당하는 메소드이다.
클래스명 레퍼런스변수명 = new 생성자 ([초기값, ......]);
생성자는 이름이 반드시 클래스명과 동일해야 하고 리턴타입이 없다.
생성자는 오버로딩 가능하므로 매개변수 타입과 개수를 다르게 하여 여러 개의 생성자를 만들 수 있다.[접근제한자] 생성자이름 (자료형 매개변수, ...) { this.필드명 = 매개변수; // 매개변수가 전달받은 초기값으로 인스턴스 변수를 초기화함 ...... }
기본(Defalut) 생성자
매개변수가 없는 생성자로 객체 생성시 JVM이 준비한 기본값으로 초기화된다.
(기본값 : Boolean - false, 정수 - 0, 실수 - 0.0, char - '\u0000', 참조형 - null)
클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다. 클래스에 생성자가 작성되어 있지 않을 경우에는 JVM에 의해 컴파일시에 자동으로 추가되는 생성자이다.
매개변수가 있는 생성자가 한 개 작성된 경우에는 기본 생성자는 자동 추가되지 않는다.
01 public class Point {
02 // 기본 생성자 (Default Constructor)
03 public Point() { } // JVM 기본값으로 초기화 됨
04 }
Point 클래스의 posX 필드와 posY 필드에 대해서 각각 0과 0으로 초기화 된다.
매개변수가 있는 생성자
생성자는 오버로딩(Overloading)이 가능하므로 생성자의 매개변수를 다르게 구성하여 여러 개의 생성자를 만들 수 있다. 매개변수가 있는 생성자와 디폴트 생성자 둘 다 필요하다면 둘 다 정의해야 한다.
01 public class Point {
02 private int posX, posY;
03
04 // 기본 생성자(Default Constructor)
05 public Point() // JVM 기본값으로 초기화 됨
06
07 // 매개변수 있는 생성자
08 public Point(int posX, int posY) {
09 this.posX = posX;
10 this.posY = posY;
11 }
12 }
자기 참조 변수라고 하며, 클래스 내에 작성 정의된 필드나 메소드에 접근 가능한 레퍼런스 변수로 프로그램 구동시 할당된 객체의 주소를 전달받아 해당 주소 위치에 있는 객체의 인스턴스 변수에 접근할 수 있는 레퍼런스 변수이다. 해당 클래스의 인스턴스 메소드 내부에 존재하며 해당 메소드 호출시 자동으로 참조할 객체의 주소를 전달받도록 되어 있다.
생성자 내에서 해당 클래스의 다른 생성자를 호출할 때 사용할 수 있다.
사용할 경우에는 반드시 생성자 { } 안의 첫 줄에 기입해야 한다.
생성자의 처리 코드가 중복되었을 때 코드 중복을 제거할 목적으로 사용할 수 있다.
oop.sample.Point.java
01 package oop.sample;
02
03 public class Point() {
04 private int posX; // 멤버필드
05 private int posY; // 멤버필드
06
07 // 메소드
08 public void out() {
09 System.out.println("[" + posX + ", " + posY + "]");
10 }
11
12 // getter, setter 메소드
13 public int getPosX() { return posX; }
14 public void setPosX(int posX) { this.posX = posX; }
15
16 public int getPosY() { return posY; }
17 public void setPosX(int posY) { this.posY = posY; }
18
19 // 디폴트 생성자
20 public Point() {
21 this(10, 20);
22 }
23
24 // 매개변수가 있는 생성자
25 public Point(int posX, int posY) {
26 this.posX = posX;
27 this.posY = posY;
28 }
29 }
oop.test.TestThis.java
01 package oop.test;
02
03 import oop.sample.Point;
04
05 public class TestThis {
06 public static void main(String[] args) {
07 Point p = new Point();
08
09 System.out.println(p.getPosX());
10 System.out.println(p.getPosY());
11 }
12 }
------
[10, 20]
[100, 200]