객체지향 프로그래밍(Object Oriented Programming)
- 객체지향 프로그래밍이란 모든 데이터들을 객체로 표현하고 객체들의 상호작용(책임, 협력, 위임) 등을 프로그램으로 표현하는 프로그래밍 기법
객체란?
- 객체는 물리적으로 존재하거나 추상적으로 생각할 수 있는 것들을 말한다.
- 물리적으로 눈에 보이는 펜, 자동차, 사람과 같은 것과 추상적인 주문, 강의 등과 같은 것도 모두 객체가 될 수 있다.
현실의 객체와 프로그래밍의 객체
객체간의 관계
객체의 속성과 기능
- 속성이란 객체가 가진 정보들을 의미한다.
- 강아지의 속성
- 견종, 나이, 이름, 키, 몸무게, 예방접종 여부 등
- 기능이란 객체가 가진 행위들을 의미한다.
- 강아지의 기능
- 짖다, 놀다, 사료를 먹다, 꼬리를 흔들다 등
클래스란?
- 현실에서 객체가 갑자기 어디선가 나타나는게 아닌 설계도를 바탕으로 만들어졌다.
- 클래스란 프로그래밍에서 객체를 만들기 위한 설계도이다.
- 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스(instance)라고 부른다.
- 하나의 클래스로 여러 개의 인스턴스를 만들 수 있다.
클래스의 구성요소
- 필드
- 필드란 객체의 고유 속성데이터, 객체의 상태데이터, 객체의 부품정보를 저장하는 곳이다.
- 필드 선언 위치는 클래스 블록 안에서만 가능하며, 메서드나 생성자 블록에서는 불가능하다.
- 생성자나 메서드 안에 선언된 변수는 필드가 아닌 지역변수라고 부른다.
- 생성자
- 생성자란 new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당한다.
- 객체 초기화란 필드 값을 세팅하거나 메서드를 호출하여 객체를 사용할 준비를 하는 것이다.
- 생성자를 호출하지 않으면 객체를 생성할 수 없으며, 생성자가 정상 호출되면 객체가 Heap 메모리에 올라가며 객체가 생성되고 그 결과로 객체의 메모리 주소값이 리턴된다.
- 생성자는 여러 개 선언할 수 있으며, 하나도 선언하지 않으면 기본생성자(default constructor)가 자동 선언된다.
- 생성자는 클래스 블록 안에 선언하며, 이름은 필드나 메서드처럼 사용자 정의가 아닌 클래스이름과 반드시 대/소문자까지 동일하게 작성해야 한다.
- 생성자는 리턴을 할 수 없으면, 리턴타입이 존재하지 않는다.
- 매개변수가 없는 생성자를 기본 생성자라고 부른다.
- 메서드
- 메서드는 객체의 동작에 해당하는 블록을 말한다.
- 메서드는 필드를 읽고 수정하는 역할도 하지만, 다른 객체를 생성해서 다양한 기능을 수행하기도 한다.
- 메서드는 객체간의 메시징을 담당하며 객체 상호간 데이터 전달의 수단으로 사용된다.
- static 메서드는 정적 메서드라고 하고 static이 없는 메서드는 인스턴스(instance) 메서드라고 부른다.
패키지란?
- 프로그램을 개발하다보면 수많은 클래스를 작성하게 된다.
- 클래스를 체계적으로 관리하지 않으면 클래스간 관계가 뒤엉켜 유지보수가 난해한 프로그램이 된다.
- 파일을 폴더에 관리하듯 자바에서는 클래스를 관리하기 위해 패키지를 사용한다.
package fruit;
public class Apple {
}
package fruit;
public class Banana {
}
package fruit;
public class Melon {
}
- 클래스를 묶음으로 관리하려면 패키지 선언을 해야 한다.
- 패키지 선언문은 반드시 클래스의 최상단에 위치해야 한다.
package traffic.car;
public class Taxi {
}
package traffic.car;
public class Bus {
}
package traffic;
public class Subway {
}
package juice;
public class Apple {
}
package test;
//작성 중인 패키지와 다른 곳에 위치한 클래스를 사용하려면 import 선언이 필요하다.
//import fruit.Apple;
//import fruit.Banana;
//import fruit.Melon;
import fruit.*; //fruit 패키지의 모든 클래스를 import 선언
public class Test {
public static void main(String[] args) {
fruit.Apple a = new fruit.Apple();
juice.Apple a2 = new juice.Apple();
Banana b = new Banana();
Melon m = new Melon();
}
}
- 다른 패키지 내의 같은 이름을 가진 클래스 객체를 생성할 때는 반드시 패키지 경로를 직접 표시해야 한다.
패키지 생성 방법
- 패키지는 클래스를 컴파일하는 과정에서 자동으로 생성되는 폴더이다. 컴파일러는 클래스를 포함된 패키지 선언을 보고 파일 시스템의 폴더로 자동 생성시킨다.
- 패키지 이름은 임의로 작성할 수 있지만 몇가지 규칙이 있다.
- 숫자로 시작 불가, 특수문자 사용 불가($, _ 제외)
- 최상위 패키지로 java로 시작할 수 없다.
- 소문자로 작성하는 것이 관례이다.
import문
- 같은 패키지에 속하는 클래스는 아무런 조건 없이 다른 클래스를 사용할 수 있지만 다른 패키지에 속한 클래스를 사용하려면 2가지 방법이 있다.
- 패키지 이름이 짧다면 불편함이 없지만 패키지 이름이 길거나, 상하위 구조가 복잡할 경우 사용할 클래스에 전체 패키지명을 사용하면 코드가 복잡하게 보인다.
- import 키워드를 선언해서 사용하는 것이 코드의 복잡성이 줄어들고 간결해진다.
- 특정 패키지의 클래스를 여러 개 사용할 경우 import 구문이 많아지는데 *을 사용해서 import를 처리할 수 있다.
package reference;
public class User {
String id;
String pw;
User(String uId, String uPw) {
id = uId;
pw = uPw;
}
}
package reference;
import java.util.Scanner;
public class CompareStr {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
User kim = new User("abc1234", "aaa1111");
System.out.print("비밀번호를 입력하세요: ");
String password = sc.next();
System.out.println("직접 입력한 비번: " + password);
System.out.println("객체 생성 시 지정한 비번: " + kim.pw);
System.out.println("--------------------------------");
System.out.println("== 연산자의 결과: " + (password == kim.pw));
System.out.println("equals의 결과: " + (password.equals(kim.pw)));
sc.close();
}
}
- String은 객체 타입이다. 여러개의 단일 문자를 변수 하나에 넣을 수가 없기 때문에 String객체가 생성되어서 여러 문자들을 한번에 보관하고 문자열을 다룰 수 있는 여러 기능을 제공한다.
- String 변수에는 문자열이 아니라 문자열 객체의 주소값이 들어 있다.
String 변수를 그냥 사용해도 문자열이 제대로 나오는 것은 자바에서 편하게 사용하도록 제공하고 있고, 실제로는 주소값으로 접근해서 사용하는 것이다. - == 연산자로 변수의 값 자체를 비교하는 것은 주소값을 비교하는 의미와 같다. 문자열의 내용 값 그 자체를 비교할 때는 equals() 메서드를 사용해야 한다.
객체지향 프로그래밍 특징
상속이란?
- 현실에서 상속은 부모가 자식에게 물려주는 행위를 말한다.
- 객체지향 프로그래밍에서도 부모 클래스가 가진 멤버를 자식 클래스에게 물려줄 수 있다.
- 상속은 이미 만들어진 클래스를 기반으로 새로운 클래스를 만들기 때문에 코드의 중복을 줄여준다.
상속을 사용하지 않은 클래스
- 음반, 도서, 영화의 클래스에 동일한 속성을 가지고 있는 상품명, 발매일이 코드 중복이 발생한다.
- 클래스 설계에 상솏을 사용하면 동일한 속성과 기능에 대한 중복 코드를 제거하고 새로운 클래스를 빠르게 만들 수 있다.
상속의 코드 표현
public class 자식클래스 extends 부모 클래스 { ... }
- 자바에서 상속을 표현할 때는 자식 클래스에서 어떤 부모 클래스를 상속받을 지 결정하고, 선택된 부모 클래스는 extends 키워드 뒤에 기술한다.
- 다른 언어와 달리 자바에서는 다중 상속을 허용하지 않는다. 즉, 하나의 자식 클래스가 여러 부모 클래스를 가질 수 없다.
- 상속을 해도 부모 클래스의 private 멤버는 상속 대상에서 제외된다.
- 상속을 사용하면 클래스의 수정을 최소화 할 수 있다. 부모 클래스의 수정으로 모든 자식 클래스가 수정되기 때문이다.
부모 클래스, 상위 클래스 (Parent, super class)
- 데이터를 다른 클래스에게 상속하는 클래스를 부모 클래스라고 한다.
- 상속을 사용하면 데이터를 물려받는 클래스에게 부모 클래스의 멤버변수(필드)와 메서드가 상속된다. 단, 생성자는 상속되지 않는다.
자식 클래스, 하위 클래스 (Child, Sub Class)
- 부모 클래스로부터 멤버변수(필드)와 메서드를 물려받는 클래스를 자식 클래스라고 한다.
- 상속을 적용시키려면 자식 클래스 선언부의 클래스 이름 뒤에 키워드 extends를 쓰고, 물려 받을 부모 클래스의 이름을 적어야 한다.
오버라이딩이란?
- 부모 클래스가 물려준 모든 메서드가 자식 클래스에 맞게 설계되어 있디면 가장 이상적인 상속이지만 어떤 메서드는 자식 클래스가 그대로 사용하기에 적합하지 않을 수 있다.
- 이 때 상속된 일부 메서드는 자식 클래스에서 다시 수정해서 사용할 수 있는데, 메서드 오버라이딩이라고 부른다.
- 메서드가 오버라이딩되면 부모 객체의 메서드는 숨겨지기 때문에 자식 객체에서 해당 메서들르 호출하면 오버라이딩된 메서드가 호출된다.
- 오버라이딩 규칙
- 부모의 메서드와 동일한 시그니처를 가진다. (이름, 리턴타입, 매개변수)
- 접근제한을 더 강화해서 오버라이딩 할 수 없다.
메서드 오버라이딩(재 정의)
- 부모가 물려주는 메서드는 모든 자식들에게 맞게 설계되기 힘들기 때문에 부족한 점이 있거나 아예 맞지 않는 경우에는 자식 클래스 쪽에서 내용을 추가하거나 수정할 수 있다. 이를 오버라이딩 이라고 한다.
- 만약 자식 클래스에서 부모가 물려준 메서드를 새롭게 재정의한다면 자식이 수정한 메서드가 우선적으로 호출된다.
- 메서드 오버라이딩의 규칙
- 부모가 물려주는 메서드와 이름이 똑같아야 한다.
- 부모가 물려주는 메서드와 리턴 타입이 똑같아야 한다.
- 부모가 물려주는 메서드와 매개변수의 선언이 똑같아야 한다.
Bad Class
package inherit.bad;
public class Warrior {
String name;
int level;
int atk;
int hp;
int rage;
void characterInfo() {
System.out.println("*** 캐릭터의 정보 ***");
System.out.println("# 아이디: " + name);
System.out.println("# 레벨: " + level);
System.out.println("# 공격력: " + atk);
System.out.println("# 체력: " + hp);
System.out.println("# 분노: " + rage);
}
}
package inherit.bad;
public class Mage {
String name;
int level;
int atk;
int hp;
int mp;
void characterInfo() {
System.out.println("*** 캐릭터의 정보 ***");
System.out.println("# 아이디: " + name);
System.out.println("# 레벨: " + level);
System.out.println("# 공격력: " + atk);
System.out.println("# 체력: " + hp);
System.out.println("# 정신력: " + mp);
}
}
package inherit.bad;
public class Hunter {
String name;
int level;
int atk;
int hp;
String pet;
void characterInfo() {
System.out.println("*** 캐릭터의 정보 ***");
System.out.println("# 아이디: " + name);
System.out.println("# 레벨: " + level);
System.out.println("# 공격력: " + atk);
System.out.println("# 체력: " + hp);
System.out.println("# 펫 이름: " + pet);
}
}
Good Class
package inherit.good;
public class Player {
String name;
int level;
int atk;
int hp;
void characterInfo() {
System.out.println("*** 캐릭터의 정보 ***");
System.out.println("# 아이디: " + name);
System.out.println("# 레벨: " + level);
System.out.println("# 공격력: " + atk);
System.out.println("# 체력: " + hp);
}
}
package inherit.good;
public class MainClass {
public static void main(String[] args) {
Warrior w1 = new Warrior();
w1.level = 1;
w1.atk = 3;
w1.hp = 50;
w1.name = "전사1";
w1.rage = 60;
w1.characterInfo();
System.out.println("---------------------");
Mage m1 = new Mage();
m1.level = 1;
m1.atk = 2;
m1.hp = 50;
m1.name = "마법사1";
m1.mana = 100;
m1.characterInfo();
System.out.println("---------------------");
Hunter h1 = new Hunter();
h1.level = 1;
h1.atk = 4;
h1.hp = 50;
h1.name = "사냥꾼1";
h1.pet = "댕댕이";
h1.characterInfo();
}
}
package inherit.good;
public class Warrior extends Player {
int rage;
@Override
void characterInfo() {
super.characterInfo();
System.out.println("# 분노: " + rage);
}
}
package inherit.good;
public class Mage extends Player {
int mana;
@Override
void characterInfo() {
super.characterInfo();
System.out.println("# 정신력: " + mana);
}
}
package inherit.good;
public class Hunter extends Player {
String pet;
@Override
void characterInfo() {
super.characterInfo();
System.out.println("# 펫 이름: " + pet);
}
}
Quiz
문제)
- 학생(Student), 선생(Teacher), 종업원(Employee) 클래스를 생성
- 세 클래스는 공통적으로 이름과 나이를 가지고 있다.
- 세 클래스는 공통적으로 info() 메서드를 가지고 있다. (리턴 타입: String, 이름: XXX, 나이: XXX )
- 학생은 학번(studentId)을 가지고 있고, 선생은 담당 과목(subject)를 가지고 있고, 종업원은 부서 (departments)를 가지고 있다.
- 부모 클래스의 이름은 Person으로 지정 Person 클래스 생성 후 각각의 자식 클래스 생성 하고 MainClass에서 정보를 출력
package inherit.quizz; public class Person { String name; int age; String info() { // return "이름: " + name + ", 나이: " + age + "세"; String str = "이름: " + name + ", 나이: " + age + "세"; return str; } }
package inherit.quizz; public class Student extends Person { String studentId; @Override String info() { return super.info() + ", 학번: " + studentId; } }
package inherit.quizz; public class Teacher extends Person { String subject; @Override String info() { return super.info() + ", 담당과목: " + subject; } }
package inherit.quizz; public class Employee extends Person { String departments; @Override String info() { return super.info() + ", 부서: " + departments; } }
package inherit.quizz; public class MainClass { public static void main(String[] args) { Student stu = new Student(); stu.name = "홍길동"; stu.age = 21; stu.studentId = "a001"; System.out.println(stu.info()); System.out.println("--------------------------------"); Teacher t = new Teacher(); t.name = "김철수"; t.age = 40; t.subject = "수학"; System.out.println(t.info()); System.out.println("--------------------------------"); Employee e = new Employee(); e.name = "김영희"; e.age = 28; e.departments = "영업팀"; System.out.println(e.info()); } }
package overloading;
public class Calculator {
void inputData() {}
void inputData(int a) {}
void inputData(int a, int b) {}
// void inputData(int b, int a) {} (x)
void inputData(String s) {}
void inputData(int a, double d) {}
void inputData(double d, int a) {}
// void inputData(int number) {} (x) 정수 매개값 1개 받는 메서드가 이미 선언되어 있음.
// int inputData(int number) {
// return 0;
// } (x) 반환 유형(return type)은 오버로딩에 영향을 미치지 못한다.
int calcRectArea(int r) {
return r * r;
}
int calcRectArea(int width, int height) {
return width * height;
}
double calcRectArea(int c, int f, int h) {
return ((c + f) * h / 2.0);
}
}
package overloading;
public class MainClass {
public static void main(String[] args) {
Calculator c = new Calculator();
System.out.println("직사각형의 넓이: " + c.calcRectArea(5));
c.calcRectArea(10, 20);
System.out.println("직사각형의 넓이: " + c.calcRectArea(10, 20));
c.calcRectArea(5, 13, 7);
System.out.println("사다리꼴의 넓이: " + c.calcRectArea(5, 12, 7));
}
}
인스턴스 멤버
- 클래스의 구성요소(필드, 생성자, 메서드)를 멤버라고 부른다.
- 객체 생성을 통해서만 사용할 수 있는 멤버를 인스턴스(instatnce) 멤버라고 부른다.
- 인스턴스 멤버는 객체 생성 없이는 사용할 수 없다.
클래스 내부에서 인스턴스를 구분하려면?
package this_super;
public class Player {
String name;
int level;
int atk;
int hp;
Player() {
System.out.println("Player의 기본 생성자 호출");
System.out.println("this의 주소값: " + this);
level = 1;
atk = 3;
hp = 50;
}
Player(String name) {
System.out.println("Player의 2번 생성자 호출");
System.out.println("this의 주소값: " + this);
level = 1;
atk = 3;
hp = 50;
this.name = name;
}
void characterInfo() {
System.out.println("*** 캐릭터의 정보 ***");
System.out.println("# 아이디: " + name);
System.out.println("# 레벨: " + level);
System.out.println("# 공격력: " + atk);
System.out.println("# 체력: " + hp);
}
}
package this_super;
public class Warrior extends Player {
int rage;
@Override
void characterInfo() {
super.characterInfo();
System.out.println("# 분노: " + rage);
}
}
package this_super;
public class Mage extends Player {
int mana;
@Override
void characterInfo() {
super.characterInfo();
System.out.println("# 정신력: " + mana);
}
}
package this_super;
public class Hunter extends Player {
String pet;
@Override
void characterInfo() {
super.characterInfo();
System.out.println("# 펫 이름: " + pet);
}
}
package this_super;
public class MainClass {
public static void main(String[] args) {
Player p1 = new Player();
System.out.println("p1의 주소값: " + p1);
p1.name = "플레이어1";
p1.characterInfo();
System.out.println("------------------------");
Player p2 = new Player("플레이어2");
System.out.println("p2의 주소값: " + p2);
p2.characterInfo();
Player p3 = new Player("플레이어3");
System.out.println("p3의 주소값: " + p3);
p3.characterInfo();
}
}
'Programming Language > Java' 카테고리의 다른 글
[국비] Java 내용정리 Day12 (0) | 2021.11.21 |
---|---|
[국비] Java 내용정리 Day11 (0) | 2021.11.20 |
[국비] Java 내용정리 Day09 (0) | 2021.11.06 |
[국비] Java 내용정리 Day08 (0) | 2021.11.05 |
[국비] Java 내용정리 Day07 (0) | 2021.11.03 |
댓글