객체지향 프로그래밍 심화
- -
객체지향 프로그래밍에는 설계를 지탱하는 핵심적인 4가지 기둥(상속, 캡슐화, 다형성, 추상화)이 있다.
1. 상속
- 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법요소
- 간단하게 말은 상속이라고 돼있지만 상속보다는 확장의 의미가 더 정확
- 하위 클래스가 상위 클래스에게 상속 받았을 시 상위 클래스의 매서드를 추가해서 사용할 수 있음
- 단일 상속만 가능, 다중은 불가능
● 포함 관계
- 상속처럼 클래스를 재사용할 수 있는 방법, 클래스의 멤버로 다른 클래스 타입의 참조변수를 선언하는 것
- 간단하게 설명하면 상속은 클래스 간의 관계가 '~은 ~이다.' 이고 포함은 '~은 ~을 가지고 있다' 이다.
● 메서드 오버라이딩
- 상위 클래스로부터 상속받은 메서드와 동일한 이름의 메서드를 재정의하는 것
- 메서드의 선언부(메서드 이름, 매개변수, 반환타입)이 상위클래스의 그것과 완전히 일치해야한다.
- 접근 제어자의 범위가 상위 클래스의 메서드보다 같거나 넓어야 한다.
- 예외는 상위 클래스의 메서드보다 많이 선언할 수 없다.
class Vehicle {
void run() {
System.out.println("Vehicle is running");
}
}
public class Bike extends Vehicle { // Vehicle 클래스 상속
void run() {
System.out.println("Bike is running"); // 메서드 오버라이딩
}
public static void main(String[] args) {
Bike bike = new Bike();
bike.run();
}
}
// 출력값
"Bike is running"
● super 키워드와 super()
- this, this() 메서드와 비슷한 개념
- super는 상위 클래스의 객체, super()는 상위 클래스의 생성자를 호출
super 예시)
public class Super {
public static void main(String[] args) {
Lower l = new Lower();
l.callNum();
}
}
class Upper {
int count = 20; // super.count
}
class Lower extends Upper {
int count = 15; // this.count
void callNum() {
System.out.println("count = " + count);
System.out.println("this.count = " + this.count);
System.out.println("super.count = " + super.count);
}
}
// 출력값
count = 15
count = 15
count = 20
super() 예시)
public class Test {
public static void main(String[] args) {
Student s = new Student();
}
}
class Human {
Human() {
System.out.println("휴먼 클래스 생성자");
}
}
class Student extends Human { // Human 클래스로부터 상속
Student() {
super(); // Human 클래스의 생성자 호출
System.out.println("학생 클래스 생성자");
}
}
// 출력값
휴먼 클래스 생성자
학생 클래스 생성자
● Object 클래스
- 자바의 클래스 상속계층도에서 최상위에 위치한 상위클래스
대표적인 메서드)
메서드명반환 타입주요 내용
toString() | String | 객체 정보를 문자열로 출력 |
equals(Object obj) | boolean | 등가 비교 연산(==)과 동일하게 스택 메모리값을 비교 |
hashCode() | int | 객체의 위치정보 관련. Hashtable 또는 HashMap에서 동일 객체여부 판단 |
wait() | void | 현재 쓰레드 일시정지 |
notify() | void | 일시정지 중인 쓰레드 재동작 |
2. 캡슐화
- 특정 객체 안에 관련된 속성과 기능을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것
- 데이터 보호와 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출 방지가 목적
- 캡슐화는 말 그대로 알약처럼 사용하는 것이기 때문에 내부의 메서드를 끌어다 쓰는 것이 아니라 메서드나 변수 등을 private하고 종합한 메서드를 통해서만 접근할 수 있게 만드는게 핵심이다.
● 패키지
- 특정한 목적을 공유하는 클래스와 인터페이스의 묶음
- 협업을 하다보면 클래스명이 겹쳐 충돌하는 경우가 있어서 사용
- import문은 다른 패키지 내의 클래스를 사용하기 위해 사용 일반적으로 패키지 구문과 클래스 구문 사이에 작성
import 패키지명.클래스명; 또는 import 패키지명.*; 으로 사용
예시)
package practicepack.test;
public class ExampleImp {
public int a = 10;
public void print() {
System.out.println("Import문 테스트")
}
package practicepack.test2; // import문을 사용하는 경우
import practicepack.test.ExampleImp // import문 작성
public class PackageImp {
public static void main(String[] args) {
ExampleImp x = new ExampleImp(); // 이제 패키지명을 생략 가능
}
}
● 제어자
- 클래스, 필드, 메섣, 생성자 등에 부가적인 의미를 부여하는 키워드
- 하나의 대상에는 여러 제어자를 사용할 수 없다.
● 접근 제어자
- 클래스 외부로의 불필요한 데이터 노출 방지, 외부로부터 데이터가 임의로 변경되지 않도록 막을 수 있음
접근 제어자 | 클래스 내 | 패키지 내 | 다른 패키지의 하위 클래스 | 패키지 외 |
Private | O | X | X | X |
Default | O | X | X | X |
Protected | O | O | X | X |
Public | O | O | O | X |
package package1; // 패키지명 package1
//파일명: Parent.java
class Test { // Test 클래스의 접근 제어자는 default
public static void main(String[] args) {
Parent p = new Parent();
// System.out.println(p.a); // 동일 클래스가 아니기 때문에 에러발생!
System.out.println(p.b);
System.out.println(p.c);
System.out.println(p.d);
}
}
public class Parent { // Parent 클래스의 접근 제어자는 public
private int a = 1; // a,b,c,d에 각각 private, default, protected, public 접근제어자 지정
int b = 2;
protected int c = 3;
public int d = 4;
public void printEach() { // 동일 클래스이기 때문에 에러발생하지 않음
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
// 출력값
2
3
4
다음 패키지2 작성 코드)
package package2; // package2
//파일명 Test2.java
import package1.Parent;
class Child extends package1.Parent { // package1으로부터 Parent 클래스를 상속
public void printEach() {
// System.out.println(a); // 에러 발생!
// System.out.println(b);
System.out.println(c); // 다른 패키지의 하위 클래스
System.out.println(d);
}
}
public class Test2 {
public static void main(String[] args) {
Parent p = new Parent();
// System.out.println(p.a); // public을 제외한 모든 호출 에러!
// System.out.println(p.b);
// System.out.println(p.c);
System.out.println(p.d);
}
}
* 위의 표와 코드를 통해서 상황에 따른 접근제어자 사용을 확인할 수 있음
● getter와 setter 메서드
- private 접근제어자가 포함되어 있는 객체의 변수의 데이터 값을 추가하거나 수정하고 싶을 때 사용
- getter 메서드는 외부에서 메서드에 접근하여 조건에 맞을 경우 데이터 값을 변경 간으하게 해줌
- setter 메서드는 설정한 변수 값을 읽어오는데 사용
예시 코드)
public class GetterSetterTest {
public static void main(String[] args) {
Worker w = new Worker();
w.setName("김코딩");
w.setAge(30);
w.setId(5);
String name = w.getName();
System.out.println("근로자의 이름은 " + name);
int age = w.getAge();
System.out.println("근로자의 나이는 " + age);
int id = w.getId();
System.out.println("근로자의 ID는 " + id);
}
}
class Worker {
private String name; // 변수의 은닉화. 외부로부터 접근 불가
private int age;
private int id;
public String getName() { // 멤버변수의 값
return name;
}
public void setName(String name) { // 멤버변수의 값 변경
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 1) return;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
// 출력값
근로자의 이름은 김코딩
근로자의 나이는 30
근로자의 ID는 5
3. 다형성
- 한 타입의 참조변수를 통해 여러 타입의 객체를 참조할 수 있도록 만든 것
- 상위 클래스 탑입의 참조변수를 통해 하위 클래스의 객체를 참조할 수 있도록 허용한 것 (반대는 성립 X)
- 참조변수가 사용할 수 있는 멤버의 개수는 상위 클래스의 멤버의 수가 됨
● 참조 변수의 타입 변환
1) 서로 상속관계에 있는 상위 클래스 - 하위 클래스 사이에만 타입 변환이 가능합니다.
2) 하위 클래스 타입에서 상위 클래스 타입으로의 타입 변환(업캐스팅)은 형변환 연산자(괄호)를 생략할 수 있습니다.
3) 반대로 상위 클래스에서 하위 클래스 타입으로 변환(다운캐스팅)은 형변환 연산자(괄호)를 반드시 명시해야합니다.
public class VehicleTest {
public static void main(String[] args) {
Car car = new Car();
Vehicle vehicle = (Vehicle) car; // 상위 클래스 Vehicle 타입으로 변환(생략 가능)
Car car2 = (Car) vehicle; // 하위 클래스 Car타입으로 변환(생략 불가능)
MotorBike motorBike = (MotorBike) car; // 상속관계가 아니므로 타입 변환 불가 -> 에러발생
}
}
class Vehicle {
String model;
String color;
int wheels;
void startEngine() {
System.out.println("시동 걸기");
}
void accelerate() {
System.out.println("속도 올리기");
}
void brake() {
System.out.println("브레이크!");
}
}
class Car extends Vehicle {
void giveRide() {
System.out.println("다른 사람 태우기");
}
}
class MotorBike extends Vehicle {
void performance() {
System.out.println("묘기 부리기");
}
}
● instanceof 연산자
- 참조변수의 타입 변환이 가능한 지 여부를 boolean 타입으로 확인할 수 있는 자바 문법요소
public class InstanceOfExample {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal instanceof Object); //true
System.out.println(animal instanceof Animal); //true
System.out.println(animal instanceof Bat); //false
Animal cat = new Cat();
System.out.println(cat instanceof Object); //true
System.out.println(cat instanceof Animal); //true
System.out.println(cat instanceof Cat); //true
System.out.println(cat instanceof Bat); //false
}
}
class Animal {};
class Bat extends Animal{};
class Cat extends Animal{};
● 다형성의 활용 예제
4. 추상화
- 기존 클래스들의 공통적인 요소들을 뽑아서 상위 클래스를 만들어 내는 것
● avstract 제어자
- '추상적인'이라는 사전적 의미를 가지고 있고 자바 맥락에서는 미완성으로 정의한다.
- 메서드 앞에 붙으면 추상 메서드, 클래스 앞에 붙으면 추상 클래스라고 부름
'📘 기초 지식 > Java' 카테고리의 다른 글
[Java] 빌더 패턴(Builder Pattern)이란? (0) | 2023.05.17 |
---|---|
[JAVA]컬렉션 (0) | 2022.11.10 |
객체지향 프로그램 (0) | 2022.11.04 |
소중한 공감 감사합니다