클래스 다이어그램
클래스와 클래스의 관계 - 객체 관계
→ UML(Unified Modeling Language)를 사용하여 클래스 다이어그램(Class Diagram)으로 표현할 수 있다.
** UML : 통합 모델링 언어 >> 모델을 만들고 설명하는 표준 언어(약속)
1. 일반화 관계(Generalization) : 상속 관계 - X is a Y (X는 Y이다)
→ 클래스를 선언할 때 기존 클래스를 상속받아 작성
→ 사원 클래스와 관리자 클래스의 관계 - 관리자는 사원이다.(0), 사원은 관리자다.(X)
2. 실체화 관계(Realization) : 상속 관계
→ 클래스를 선언할 때 기존 인터페이스를 상속받아 작성
→ 인터페이스 : 현실에 존재하는 대상을 클래스 보다 추상적으로 표현하기 위한 자료형
자바에서는 위의 일반화 관계, 실체화 관계 모두 '상속 관계'로 본다 (UML에서는 따로 구분)
3. 연관 관계(Association) : 포함 관계 - X has a Y (X는 Y를 가지고 있다)
→ 직접 연관 관계(Direct Association) : 한 방향으로만 도구로써 기능을 제공하는 관계
→ 컴퓨터 << CPU + MainBoard + Memory (CPU가 바뀌면 컴퓨터에 영향)
4. 집합 연관 관계(Aggregation) : 포함 관계로 설정된 객체들의 생명주기가 다른 포함 관계
→ 컴퓨터 << 프린터
5. 복합 연관 관계(Composition) : 포함 관계로 설정된 객체들의 생명주기가 같은 포함 관계
→ 게임 << 캐릭터 (게임이 종료되면 캐릭터도 같이 소멸 )
6. 의존 관계(Dependency) : 포함 관계로 설정된 객체가 변경돼도 다른 객체에 영향을 주지 않는 포함 관계
→ TV << 리모콘 (리모콘이 바껴도 TV에 영향을 주지 않음)
포함관계 예시
Engine Class - 엔진 정보를 저장하기 위한 클래스
Car Class - 자동차 정보(모델명, 생산년도, 엔진정보)를 저장하기 위한 클래스
Engine Class 생성
public class Engine {
//필드 생성
private String fualType; //연료타입
private int displacement; //배기량
//생성자 생성
public Engine() {
// TODO Auto-generated constructor stub
}
public Engine(String fualType, int displacement) {
super();
this.fualType = fualType;
this.displacement = displacement;
}
//Getter Setter 메소드
public String getFualType() {
return fualType;
}
public void setFualType(String fualType) {
this.fualType = fualType;
}
public int getDisplacement() {
return displacement;
}
public void setDisplacement(int displacement) {
this.displacement = displacement;
}
//엔진정보(필드값)를 출력하는 메소드 (원래 생성하지 않아도 편리성 위해 임의로 생성)
public void displayEngine() {
System.out.println("연료타입 = " + fualType);
System.out.println("배기량 = " + displacement);
}
}
Car Class 필드 생성
public class Car {
private String modelName;
private int productionYear;
//엔진정보를 저장하기 위한 필드 - Engine 클래스를 자료형으로 선언된 필드
// => 필드에는 Engine 객체를 생성자 또는 Setter 메소드를 사용하여 제공받아 저장 - 포함관계
private Engine carEngine; // >> Car클래스와 Engine클래스는 포함관계
}
Car Class 생성자 생성
public class Car {
public Car() {
// TODO Auto-generated constructor stub
}
public Car(String modelName, int productionYear, Engine carEngien) {
super();
this.modelName = modelName;
this.productionYear = productionYear;
this.carEngine = carEngien;
}
}
Car Class 메소드 생성
public class Car {
//Setter Getter 메소드
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
public int getProductionYear() {
return productionYear;
}
public void setProductionYear(int productionYear) {
this.productionYear = productionYear;
}
public Engine getCarEngien() {
return carEngine;
}
public void setCarEngien(Engine carEngine) {
this.carEngine = carEngine;
}
//자동차 정보(필드값)를 출력하는 메소드
public void displayCar() {
System.out.println("모델명 = " + modelName);
System.out.println("생산년도 = " + productionYear);
//엔진 정보 출력하는 메소드
//1)
//System.out.println("엔진정보 = " + carEngine); //객체의 메모리 주소 출력 >> 잘못된 방식
//2)
//필드에 저장된 객체를 이용하여 메소드 호출
//=> 포함관계로 설정된 클래스(객체)의 메소드 호출하여 원하는 기능 구현
//=> 포함관계가 설정되지 않은 상태에서 메소드가 호출될 경우 NullPointerException 발
//System.out.println("연료타입 = " + carEngine.getFualType());
//System.out.println("배기량 = " + carEngine.getDisplacement());
//3)
carEngine.displayEngine(); //코드의 중복성 최소화
}
CarApp Class 생성 - 실행 목적
1)Setter 메소드 호출
public class CarApp {
public static void main(String[] args) {
//엔진 생성
Engine engine = new Engine();
engine.setFualType("경유");
engine.setDisplacement(2000);
//engine.displayEngine();
//자동차 생성
Car carOne = new Car();
carOne.setModelName("쏘렌토");
carOne.setProductionYear(2020);
//Setter 메소드를 호출하여 매개변수에 엔진정보(Engine 객체)를 전달받아 필드에 저장
//=> 인위적인 포함관계 성립
//이 때 포함관계가 완전히 만들어짐
carOne.setCarEngien(engine);
carOne.displayCar();
/*
모델명 = 쏘렌토
생산년도 = 2020
연료타입 = 경유
배기량 = 2000
*/
}
}
2)생성자 호출
public class CarApp {
public static void main(String[] args) {
//자동차 생성 >> 엔진을 생성하여 필드에 저장 - 포함관계 성립
// => 생성자를 호출하여 매개변수에 엔진정보(Engine 객체)를 전달받아 필드에 저장
Car carTwo = new Car("싼타페", 2023, new Engine("휘발유",3000));
carTwo.displayCar();
/*
모델명 = 싼타페
생산년도 = 2023
연료타입 = 휘발유
배기량 = 3000
*/
}
}
엔진정보 출력
System.out.println(carOne.getModelName() + "의 엔진정보 >> ");
engine.displayEngine();
/*
쏘렌토의 엔진정보 >>
연료타입 = 경유
배기량 = 2000
*/
//두번째 자동차는 Engine 객체가 변수에 저장되어 있지 않음. 필드에 저장되어 있다.
//자동차 (Car 객체)에 저장된 엔진정보(engine 필드값 - Engine 객체)을 Getter 메소드로
//반환받아 Engine 객체의 메소드 호출
System.out.println(carTwo.getModelName() + "의 엔진정보 >> ");
carTwo.getCarEngien().displayEngine();
상속(Inheritance)
• 상속(Inheritance) : 클래스를 선언할 때 기존 클래스를 물려받아 사용하는 기능
→ 기존 클래스를 재활용하여 새로운 클래스를 쉽고 빠르게 작성 - 프로그램 생산성 증가
→ 공통적인 속성과 행위를 포함한 다수의 클래스를 선언할 때 공통적인 속성과 행위의 클래스를 선언하고 작성된 클래스를 상속받아 사용
→ 코드의 중복성을 최소화 하여 프로그램 생산성 및 유지보수의 효율성 증가
물려주는 클래스 - 부모클래스, 선조클래스, 기본클래스, 슈퍼클래스(SuperClass)
물려받는 클래스 - 자식클래스, 후손클래스, 파생클래스, 서브클래스(SubClass)
형식)
public class 자식클래스 extends 부모클래스 {
//자식 클래스에서는 부모 클래스의 필드 또는 메소드 사용 가능
}
→ 부모클래스의 생성자는 자식클래스에게 상속되지 않으며 부모클래스의 은닉화 선언된 필드와 메소드는 자식클래스에서 접근이 불가능하다
→ Java에서는 하나의 부모클래스만 상속이 가능하다 - 단일상속
Super 키워드
• super 키워드 : 자식클래스의 메소드에서 부모클래스 객체의 메모리 주소를 저장하기 위한 키워드
→ 자식클래스의 메소드에서 부모클래스 객체의 필드 또는 메소드를 참조하기 위해 사용
→ 자식클래스의 메소드에서 super 키워드를 사용하지 않아도 자식클래스의 메소드에서는 this 키워드로
참조되는 필드와 메소드가 없으면 자동으로 부모클래스 객체의 필드 또는 메소드 참조
• super 키워드를 사용하는 경우
1. 자식 클래스의 생성자에서 부모클래스의 매개변수가 있는 생성자를 호출하여 초기화 처리하기 위해 super 키워드 사용
- 부모클래스 객체가 생성될 때 필드에 원하는 초기값 저장 가능
형식)
super(값, 값, ...);
→ 생략된 경우 부모클래스의 매개변수가 없는 기본생성자를 호출하여 객체 생성
2. 자식클래스의 메소드에서 오버라이드 선언되어 숨겨진 부모클래스의 메소드를 호출할 경우 super 키워드를 사용한다.
→ 잘 사용하는 방법은 아니다
@Override
public void display() {
// TODO Auto-generated method stub
super.display(); //super 키워드로 부모클래스의 숨겨진 메소드를 호출한다
}
상속 예시
부모 클래스인 Member 클래스 생성 - 회원정보(아이디, 이름)를 저장하기 위한 클래스
package inheritance;
//회원정보(아이디, 이름)를 저장하기 위한 클래스
public class Member {
//값 저장하는 필드
private String id;
private String name;
//생성자
public Member() {
// TODO Auto-generated constructor stub
}
public Member(String id, String name) {
super();
this.id = id;
this.name = name;
}
//Setter Getter 메소드
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//필드값 출력하는 메소드
public void display() {
System.out.println("아이디 = " + id);
System.out.println("이름 = " + name);
}
}
자식 클래스인 MemberEvent 클래스 생성 - 이벤트 관련 회원정보(아이디, 이름, 이메일)를 저장하기 위한 클래스
=> 회원정보를 저장하기 위한 Member 클래스를 상속받아 작성하는 것 권장 - 클래스의 재사용성 증가
필드 선언
부모클래스(Member)를 상속받아 사용할 수 있으므로 필드는 선언하지 않아도 된다
부모클래스(Member)에서 상속받은 필드 또는 메소드 미선언
public class MemberEvent extends Member {
//private String id;
//private String name;
private String email;
}
생성자 생성-1
public class MemberEvent extends Member {
public MemberEvent() {
//super(); //부모클래스의 매개변수가 없는 기본 생성자 호출 - 생략 가능
}
public MemberEvent(String id, String name, String email) {
super(); //부모클래스의 매개변수가 없는 기본 생성자 호출
//이것도 생략 가능
//자식클래스 메소드에서는 this 키워드로 자식클래스 객체의 필드 또는 메소드를 참조하고
//자식클래스 객체의 필드 또는 메소드가 없는 경우 super 키워드를 이용하여
//부모 클래스 객체의 필드 또는 메소드 참조
// => 부모클래스의 필드 또는 메소드가 은닉화 선언된 경우 자식클래스에서 접근 불가능
//부모클래스의 필드가 은닉화되어있기 때문에 Setter 메소드 사용해야 한다
//this.id = id;
setId(id);
//this.name = name;
setName(name);
this.email = email;
}
}
생성자 생성-2 (더 효율적인 방법)
[Alt] + [Shift] + [S] >> [O] >> 부모클래스의 생성자 선택 >> 필드 선택 >> Generate
super 사용해 부모클래스의 매개변수가 있는 생성자 호출 가능
public class MemberEvent extends Member {
public MemberEvent(String id, String name, String email) {
super(id, name); //부모클래스의 매개변수가 있는 생성자 호출
this.email = email;
}
}
메소드 생성
부모클래스(Member)에서 상속받은 메소드 생성 X
public class MemberEvent extends Member {
//Setter Getter 메소드 => 생성하지 않아도 된다
/*
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
*/
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
상속은 부모클래스의 요소를 그대로 물려받는 것이 장점이자 단점이다.
상속받은 메소드 중 자식이 쓰기 부적절한 메소드가 존재할 수 있는데, 이는 메소드를 재선언 함으로써 해결할 수 있다
부모클래스의 메소드를 쓰지 않고, 자식클래드가 선언한 메소드를 그대로 사용하는 것 - 메소드 오버라이딩
메소드 오버라이딩
• 메소드 오버라이딩(Method Overriding) : 상속 관계에서 부모클래스 메소드를 자식클래스에서 재선언하는 기능
→ 부모클래스의 메소드를 자식클래스의 객체가 사용하기 부적절한 경우 부모클래스의 메소드를 자식클래스에서 재선언하여 사용하는 방법
→ 부모클래스의 메소드(Hide Method)는 숨겨지고 자식클래스의 메소드만 접근 가능
**숨겨진 부모클래스 : Hide Method
• 메소드 오브라이딩의 작성 규칙 : 부모클래스의 메소드와 같은 접근제한자,반환형,메소드명,매개변수,예외 전달을 사용하여 자식클래스의 메소드를 작성해야한다.
이클립스에서는 부모클래스의 메소드를 자식 클래스에서 오버라이드 선언되도록 자동 완성 하는 기능을 제공한다.
→ 오버라이드 선언하고 싶은 부모클래스의 메소드명 입력 >> [Ctrl] + [Space] >> Override Method 선택
• @Override : 오버라이드 선언된 메소드를 표현하기 위한 어노테이션
• 어노테이션(Annotation) : 원래는 API 문서에서 특별한 설명을 제공하기 위한 기능의 자료형(인터페이스)을 제공할 수 있도록 만들어진 것이다. → 현재는 Java Source 작성에 필요한 특별한 기능을 제공하기 위해 사용되는 자료형으로 쓰인다.
ex) @Override, @Deperecated, @SuppessWarings
필드값 출력하는 메소드
public class MemberEvent extends Member {
@Override
public void display() {
// TODO Auto-generated method stub
super.display(); //super 키워드로 부모클래스의 숨겨진 메소드를 호출한다
System.out.println("이메일 = " + email);
}
}
MemberEvent(자식 클래스) 실행 위한 MemberEventApp 클래스 생성
자식클래스(MemberEvent)의 생성자로 객체를 생성할 경우 부모클래스(Member)의 생성자가 먼저 호출되어 부모클래스(Member)의 객체가 먼저 생성된 후 자식클래스(MemberEvent)의 생성자로 객체를 생성하여 상속관계가 자동으로 성립된다.
→ 자식클래스의 참조변수에는 자식클래스 객체의 메모리 주소 저장되어 자식클래스 객체의 필드와 메소드를 참조하지만 상속관계에 의해 부모클래스 객체의 필드 또는 메소드 참조 가능
public class MemberEventApp {
public static void main(String[] args) {
MemberEvent member1 = new MemberEvent();
//Setter Getter 이용
member1.setId("abc123");
member1.setName("홍길동");
member1.setEmail("abc@itwill.xyz");
member1.display();
/*
아이디 = abc123
이름 = 홍길동
이메일 = abc@itwill.xyz
*/
//생성자 이용
MemberEvent member2 = new MemberEvent("xyz879","임꺽정","xyz@itwill.xyz");
member2.display();
/*
아이디 = xyz879
이름 = 임꺽정
이메일 = xyz@itwill.xyz
*/
}
}
상속관계의 클래스에서 참조변수와 객체와의 관계
부모클래스의 객체를 생성한다.
부모클래스 참조변수 = new 부모클래스();
// => 부모클래스의 생성자로 객체를 생성하여 부모클래스의 참조변수에 저장 - 가능
public class MemberCastApp {
public static void main(String[] args) {
Member member1 = new Member();
//참조변수에 저장된 부모클래스의 객체를 사용하여 부모클래스의 메소드 호출 가능
member1.setId("abc123");
member1.setName("홍길동");
member1.display();
/*
아이디 = abc123
이름 = 홍길동
*/
}
}
-> 부모만 이용했기 때문에 '상속'은 아니다.
만들어진 부모클래스의 객체를 자식클래스의 참조변수에 저장한다.
자식클래스 참조변수 = new 자식클래스();
// => 부모클래스의 생성자로 부모클래스의 객체를 생성하고 자식클래스의 생성자로 자식 클래스 객체를 생성하여
//자식클래스의 참조변수에 자식클래스의 객체를 저장
public class MemberCastApp {
public static void main(String[] args) {
MemberEvent member2 = new MemberEvent();
//참조변수에 저장된 자식클래스의 객체를 사용하여 자식클래스의 메소드를 호출할 수
//있으며 상속관계에 의해 부모클래스 객체를 참조하여 부모클래스도 호출 가능
member2.setId("xyz789");
member2.setName("홍길동");
member2.setEmail("xyz@itwill.xyz");
member2.display();
/*
아이디 = xyz789
이름 = 홍길동
이메일 = xyz@itwill.xyz
*/
}
}
부모클래스의 생성자로 부모클래스의 객체를 생성하고 자식클래스의 참조변수에 부모클래스의 객체 저장 - 불가능(에러 발생)
자식클래스 참조변수 = new 부모클래스();
//MemberEvent member3 = new Member();
부모클래스의 생성자로 부모클래스 객체를 생성하고 자식클래스의 생성자로 자식 클래스 객체를 생성하여 부모클래스의 참조변수에 부모클래스의 객체 저장 - 가능
부모클래스 참조변수 = new 자식클래스();
Member member4 = new MemberEvent();
//참조변수에 저장된 부모클래스의 객체를 사용하여 부모클래스의 메소드를 호출 가능
// => 자식클래스의 객체를 참조할 수 없으므로 자식클래스도 호출 불가능
//member4 -> MemberEvent(자식클래스)는 참조할 수 없다
member4.setId("opq456");
member4.setName("전우치");
객체형변환
객체 형변환을 이용하면 부모클래스의 참조변수로 자식클래스 메소드 호출이 가능하다.
→ 명시적 객체 형변환(강제 형변환), 묵시적 객체 형변환(자동 형변환)
→ 상속관계의 클래스에서만 객체 형변환 사용 가능
• 명시적 객체 형변환 : Cast 연산자를 사용하여 부모클래스의 참조변수 자료형을 자식 클래스로 변경하면 일시적으로 참조변수에 자식클래스의 객체가 자동 저장된다.
→ 명시적 객체 형변환에 의해 자식클래스로 자료형이 변경된 참조변수는 자식클래스의 객체가 저장되므로 자식클래스의 메소드 호출이 가능하다.
/*
MemberEvent event = (MemberEvent)member4; //event에는 자식객체의 주소가 저장
event.setEmail("opq@itwill.xyz");
*/
//변수를 만드는것보다 형변환하자마자 메소드를 바로 호출하는 것이 좋다
//객체 연산자(.)보다 Cast 연산자가 먼저 실행될 수 있도록 () 연산자 사용 (형변환을 먼저 해야해서)
((MemberEvent)member4).setEmail("opq@itwill.xyz");
• 묵시적 객체 형변환 : 부모클래스의 메소드를 자식클래스에서 오버라이드 선언하면 부모클래스의 숨겨진 메소드 대신 자식클래스의 메소드를 호출하기 위해 참조변수의 자료형을 자동으로 자식클래스로 변경하여 일시적으로 자식클래스의 객체가 참조변수에 저장되어 자식클래스의 메소드를 호출한다.
**오버라이드 되지 않은 것은 형변환 되지 않는다
//((MemberEvent)member4).display();
member4.display();
/*
아이디 = xyz789
이름 = 홍길동
이메일 = xyz@itwill.xyz
*/
모두 출력되는 이유 => 묵시적 객체 형변환
※ 객체형변환을 해야하는 이유?
부모로 참조변수를 만들고 객체형변환을 이용하면 , 부모 뿐만 아니라 모든 자식들을 다 참조할 수 있기 때문이다.
단 1대 1인 상속관계인 경우에는,
자식클래스 참조변수 = new 자식클래스(); >> 의 방법이 더 효율적.
'학원 > 복기' 카테고리의 다른 글
0412 - 인터페이스, 기본메소드, Enum 자료형, 중첩 클래스 (0) | 2023.04.12 |
---|---|
0411 - instanceof, 오버라이드에 의한 다향성, Object클래스, 추상 클래스, 추상 메소드, final 키워드, 상수필드 (0) | 2023.04.11 |
0407 - This, Static, Runtime Class, 싱글톤 클래스(Singleton Class) (0) | 2023.04.07 |
0406 - 클래스, 접근 제한자(private, public),캡슐화,메소드 오버로딩, 다향성, VO클래스, 필드/생성자/메소드 선언 (0) | 2023.04.06 |
0405 - 객체 중심 프로그래밍(OOP), 메소드 선언 및 호출, return 키워드 (0) | 2023.04.05 |