부모로 참조변수를 만들고 객체형변환 해야하는 이유는 부모 뿐만 아니라 모든 자식들을 다 참조할 수 있기 때문이다.
단 1대 1인 상속관계인 경우에는,
자식클래스 참조변수 = new 자식클래스(); 의 방법이 더 효율적이다.
상속 이용해 학원인적자원(강사,학생,직원) 관리 프로그램 작성하기
프로그램 작성에 필요한 클래스)
AcademyApp 클래스 - 학원인적자원(강사,학생,직원) 관리 프로그램
AcademyStudent 클래스 - 학생정보(학생번호,학생이름,수강과목)를 저장하기 위한 클래스
AcademyInstructor 클래스 - 강사정보(강사번호,강사이름,강의과목)를 저장하기 위한 클래스
AcademyStaff 클래스 - 직원정보(직원번호,직원이름,근무부서)를 저장하기 위한 클래스
중복된 코드를 최소화 하기 위해, 상속을 이용하자. 공통적인 속성(번호,이름)에 대한 행위를 가진 클래스를 만들고 해당 클래스를 상속받아 AcademyStudent, AcademyInstructor, AcademyStaff 클래스를 생성하는 것이 더 효율적이다.
사람정보(번호,이름)를 저장하기 위한 부모클래스인 AcademyPerson 클래스 생성
package inheritance;
//사람정보(번호, 이름)를 저장하기 위한 클래스
// => 학생, 강사, 직원에 대한 공통적인 속성과 행위를 표현하기 위한 클래스
// => 학원인적자원 관련 클래스가 반드시 상속받아야 되는 부모클래스
public class AcademyPerson {
//필드
private int num;
private String name;
//생성자
public AcademyPerson() {
// TODO Auto-generated constructor stub
}
public AcademyPerson(int num, String name) {
super();
this.num = num;
this.name = name;
}
//Setter Getter 메소드
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//출력 메소드
public void display() {
System.out.println("번호 = " + num);
System.out.println("이름 = " + name);
}
}
부모인클래스인 AcademyPerson 클래스를 상속받아 학생정보, 강사정보,직원정보가 저장된 각각의 자식클래스 생성
AcademyStudent 클래스 생성
package inheritance;
//학생정보(학생번호, 학생이름, 수강과목)를 저장하기 위한 클래스
// => 학생번호와 학생이름 관련 속성과 행위는 AcademyPerson 클래스를 상속받아 작성
public class AcademyStudent extends AcademyPerson {
//필드
private String course; //수강과목
//생성자
public AcademyStudent() {
// TODO Auto-generated constructor stub
}
//부모클래스의 매개변수가 있는 생성자
public AcademyStudent(int num, String name, String course) {
super(num, name);
this.course = course;
}
//Setter Getter 메소드
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
//출력 메소드
//오버라이드 통해 부모클래스 메소드를 다시 선언
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("학생번호 = " + getNum()); //private 돼있기 때문에 getter 메소드 이용
System.out.println("학생이름 = " + getName());
System.out.println("수강과목 = " + course);
}
}
AcademyInstructor 클래스 생성
package inheritance;
//강사정보(강사번호, 강사이름, 강의과목)를 저장하기 위한 클래스
// => 강사번호와 강사이름 관련 속성과 행위는 AcademyPerson 클래스를 상속받아 작성
public class AcademyInstructor extends AcademyPerson {
//필드
private String subject;
//생성자
public AcademyInstructor() {
// TODO Auto-generated constructor stub
}
//부모클래스의 매개변수가 있는 생성자
public AcademyInstructor(int num, String name, String subject) {
super(num, name);
this.subject = subject;
}
//Setter Getter 메소드
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
//출력 메소드
//오버라이드 통해 부모클래스 메소드를 다시 선언
@Override
public void display() {
System.out.println("강사번호 = " + getNum());
System.out.println("강사이름 = " + getName());
System.out.println("강의과목 = " + subject);
}
}
AcademyStaff 클래스 생성
package inheritance;
//직원정보(직원번호, 직원이름, 근무부서)를 저장하기 위한 클래스
//=> 직원번호와 직원이름 관련 속성과 행위는 AcademyPerson 클래스를 상속받아 작성
public class AcademyStaff extends AcademyPerson {
//필드
private String depart;
//생성자
public AcademyStaff() {
// TODO Auto-generated constructor stub
}
//부모클래스의 매개변수가 있는 생성자
public AcademyStaff(int num, String name, String depart) {
super(num, name);
this.depart = depart;
}
//Setter Getter 메소드
public String getDepart() {
return depart;
}
public void setDepart(String depart) {
this.depart = depart;
}
//출력 메소드
//오버라이드 통해 부모클래스 메소드를 다시 선언
@Override
public void display() {
System.out.println("직원번호 = " + getNum());
System.out.println("직원이름 = " + getName());
System.out.println("근무부서 = " + depart);
}
}
학원인적자원 관리 프로그램 작성 위한 AcademyApp 클래스 생성
사람정보(AcademyPerson 객체)를 저장하기 위한 요소들이 존재하는 배열을 생성한다.
AcademyPerson은 AcademyStudent, AcademyInstructor, AcademyStaff의 부모이며, 부모는 모든 자식들을 저장할 수 있다. 따라서, 배열요소에 (AcademyStudent 객체), 강사정보(AcademyInstructor 객체), 직원정보(AcademyStaff 객체) 저장이 가능하다. 이는 부모클래스의 참조변수에 자식클래스의 생성자로 (부모클래스의) 객체를 생성하여 저장이 가능하기 때문이다.
public class AcademyApp {
public static void main(String[] args) {
AcademyPerson[] persons = new AcademyPerson[5];
//자식클래스의 생성자로 객체를 생성하면 부모클래스의 객체를 먼저 생성한 후
//자식 클래스의 객체를 생성
// => 배열 요소에는 부모클래스의 객체가 저장되어 부모클래스의 메소드를 호출
// => 객체 형변환을 이용하면 배열 요소를 자식 클래스의 객체를 일시적으로 저장하여
//자식클래스의 메소드 호출 가능
persons[0] = new AcademyStudent(1000,"홍길동","웹개발자 과정");
persons[1] = new AcademyInstructor(2000,"임꺽정","Java 과목");
persons[2] = new AcademyStaff(3000,"전우치","운영관리팀");
persons[3] = new AcademyStudent(4000,"일지매","웹디자인 과정");
persons[4] = new AcademyStaff(5000,"장길산","경영회계팀");
}
}
오버라이드 선언되지 않은 자식 클래스의 메소드를 호출하기 위해 명시적 객체 형변환을 이용하여 참조변수에 자식클래스의 객체를 일시적으로 저장하여 자식 클래스의 메소드를 호출할 수 있다.
이 때의 문제점은 상속관계가 아닌 클래스로 명시적 객체 형변환을 할 경우 ClassCastException 발생가 발생한다는 것이다. 이를 해결하기 위해서 참조변수로 객체 형변환 가능한 클래스를 확인한 후 명시적 객체 형변환을 이용해야 한다.
>> instanceof 연산자를 사용하여 참조변수의 객체 형변환 가능 클래스를 검사할 수 있다.
• intstanceof 연산자 : 참조변수로 참조 가능한 클래스를 확인하여 [false] 또는 [true]를 제공하는 연산자
형식) 참조변수 instanceof 클래스
다형성
• 오버로드에 의한 다형성: 매개변수에 전달되는 값에 따라 메소드를 선택 호출
• 오버라이드에 의한 다향성 : 객체 형변환에 의해 참조변수에 저장된 객체에 따라 다른 자식클래스의 메소드를 선택해 호출
public class AcademyApp {
public static void main(String[] args) {
//배열 요소에 저장된 객체를 하나씩 제공받아 참조변수에 저장하여 일괄처리
for(AcademyPerson person : persons) {
if(person instanceof AcademyStudent) {
System.out.println(((AcademyStudent)person).getCourse() + "의 학생정보 >> ");
} else if(person instanceof AcademyInstructor) {
System.out.println(((AcademyInstructor)person).getSubject() + "의 강사정보 >> ");
} else if(person instanceof AcademyStaff) {
System.out.println(((AcademyStaff)person).getDepart() + "의 직원정보 >> ");
}
//오버라이드 선언된 메소드는 묵시적 객체 형변환에 의해 부모클래스의 메소드를 호출하지 않고 자식 클래스의 메소드를 호출
//참조변수에 저장된 자식클래스의 객체에 의해 자식클래스의 메소드가 선택 호출된다
// => 오버라이드에 의한 다형성 : 객체 형변환에 의해 참조변수에 저장된 객체에
//따라 다른 자식클래스의 메소드를 선택 호출
person.display();
}
}
클래스 선언시 상속받은 부모클래스가 없는 경우 기본적으로 Object 클래스를 자동으로 상속받는다.
모든 Java 클래스는 무조건 Object 클래스를 상속받아 사용 가능하다.
• Object 클래스 : 모든 Java 클래스의 최선조 클래스
=> Object 클래스로 생성된 참조변수에는 모든 클래스로 생성된 객체를 저장 or 반환이 가능하다 (중요)
• Object.toString()
: 참조변수에 저장된 객체를 참조하여 "클래스명@메모리주소" 형식의 문자열로 변환하여 반환하는 메소드
예시) 자동차정보(모델명, 소유자명)를 저장하기 위한 클래스 Car와 실행 프로그램 CarApp 작성
public class Car /* extends Object */ {
//필드
private String modelName;
private String userName;
//생성자
public Car() {
// TODO Auto-generated constructor stub
}
public Car(String modelName, String userName) {
super();
this.modelName = modelName;
this.userName = userName;
}
//Setter Getter 메소드
public String getModelName() {
return modelName;
}
public void setModelName(String modelName) {
this.modelName = modelName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
//Object 클래스의 toString() 메소드를 오브라이드 선언
// => Object 클래스의 toString() 메소드는 숨겨지고, Car 클래스의 toString() 메소드가 호출된다
// => VO 클래스에서는 필드값을 문자열로 변환하여 반환하는 명령 작성 - 필드값 확인
@Override
public String toString() {
return "모델명 = " + modelName + ", 소유자 = " + userName;
}
}
public class CarApp {
public static void main(String[] args) {
Car car = new Car("싼타페","홍길동");
System.out.println("모델명 = " + car.getModelName());
System.out.println("소유자명 = " + car.getUserName());
System.out.println("===================================================================");
/*
//Object.toString() : 참조변수에 저장된 객체를 참조하여 "클래스명@메모리주소" 형식의
//문자열로 변환하여 반환하는 메소드
System.out.println("car.toString() = " + car.toString()); //car.toString() = inheritance.Car@3b6eb2ec
//참조변수를 출력할 경우 자동으로 toString() 메소드 호출
System.out.println("car.toString() = " + car); //car.toString() = inheritance.Car@3b6eb2ec
*/
//Object 클래스의 toString() 메소드가 아닌 Car 클래스의 toString() 메소드 자동 호출
System.out.println("car = " + car); //car = 모델명 = 싼타페, 소유자 = 홍길동
System.out.println("===================================================================");
//문자열이 저장된 String 객체를 생성하여 객체의 메모리주소를 참조변수에 저장
String name = "홍길동"; //'홍길동'이라는 문자열을 저장한 String객체를 만들어 String객체의
//메모리 주소를 name 이라는 참조변수에 저장하는 것
//String 클래스에 오버라이드 선언된 toString() 메소드 호출
// => String.toSTring() : String 객체에 저장된 문자열을 반환하는 메소드
System.out.println("name = " + name.toString()); //name = 홍길동
//String 클래스의 참조변수를 출력할 경우 자동으로 String 클래스의 toString() 메소드 호출
System.out.println("name = " + name); //name = 홍길동
System.out.println("===================================================================");
}
}
이처럼 VO클래스 만들 때 일반적으로
값을 저장하기 위한 필드, 필드 초기화 위한 생성자, Setter Getter메소드 , toString 메소드를 오버라이드해서 필드값들을 문자열로 변환하는 메소드까지 만들어준다
• 추상클래스(Abstract Class) : abstract 제한자를 사용하여 선언된 클래스
=> 객체 생성이 불가능한 ' 상속 전용 클래스 '
=> abstract 제한자 : 클래스와 메소드에 설정에 사용 가능한 제한자
형식) public abstract class 클래스명 { }
• 추상메소드(Abstract Method) : abstract 제한자를 사용하여 선언된 메소드
=> 메소드의 머릿부만 작성하고 몸체부는 작성하지 않는 미완성된 메소드
형식) 접근제한자 abstract 반환형 메소드명(자료형 매개변수명, ...);
=> 추상메소드가 선언된 클래스는 반드시 추상클래스로 선언해줘야 한다.
=> 추상메소드가 선언된 클래스를 상속받은 자식클래스는 무조건 모든 추상메소드를 오버라이드 선언해줘야 한다
- 자식클래스에서 추상메소드를 오버라이드 선언하지 않으면 자식클래스도 추상클래스로 설정되어 객체를 생성할 경우 에러 발생
자식클래스에서 부모클래스의 메소드를 무조건 오버라이드 선언하도록 설정하기 위해 abstract 제한자를 사용하여 추상메소드로 선언하는 것이다.
• final 제한자 : 클래스, 메소드, 필드에 사용하는 제한자
1.final 제한자를 필드에 사용하여 선언 - final 필드
=> final 필드값 변경 불가능 - 필드에 저장된 값을 변경할 경우 에러 발생 (초기값을 절대 바꿀 수 없도록 하기 위해 사용)
=> 필드를 선언할 때 반드시 초기값을 필드에 저장시켜야 한다
형식) 접근제한자 final 자료형 필드명 = 초기값;
2. final 제한자를 메소드에 사용하여 선언 - final 메소드
=> 자식클래스에서 메소드를 오버라이드 선언하지 못하도록 제한하는 기능 제공
형식) 접근제한자 final 반환형 메소드명(자료형 매개변수명, ...) { }
3. final 제한자를 클래스에 사용하여 선언 - final 클래스
=> final 클래스를 상속 받지 못하도록 제한하는 기능 제공
형식) 접근제한자 final class 클래스명 { }
• 상수필드(Constant Field)
: 프로그램에서 값(리터럴) 대신 사용하기 위한 의미있는 단어로 제공되는 필드값
=> 상수명은 대문자로 작성하여 스네이크 표기법을 이용하여 작성하는 것을 권장한다.
장점 : 상수를 보고 의미를 파악할 수 있으므로 프로그램을 유지보수할때 효율적이다
형식) public static final 자료형 변수명 = 초기값;
ex)
public static final double INCENTIVE_RATE = 1.5;
'학원 > 복기' 카테고리의 다른 글
0413 - 정적 내부 클래스,익명의 내부 클래스, 패키지, import 키워드, 어노테이션, 접근 제한자,예외처리 (0) | 2023.04.13 |
---|---|
0412 - 인터페이스, 기본메소드, Enum 자료형, 중첩 클래스 (0) | 2023.04.12 |
0410 - 클래스 다이어그램, 상속관계, 포함관계, Super 키워드, 메소드 오버라이딩, 객체형변환 (0) | 2023.04.10 |
0407 - This, Static, Runtime Class, 싱글톤 클래스(Singleton Class) (0) | 2023.04.07 |
0406 - 클래스, 접근 제한자(private, public),캡슐화,메소드 오버로딩, 다향성, VO클래스, 필드/생성자/메소드 선언 (0) | 2023.04.06 |