본문 바로가기
Backend/Java

[Java]OOP - 기타제한자 - (2) final & abstract

by sukii 2024. 1. 3.
반응형

제한자(Modified)란 클래스, 필드, 생성자, 메소드 작성시 특별한 제한을 목적으로 사용하는 키워드로,
대표적으로 4가지 접근제한자(private, package, protected, public)가 있다.

그리고 그 외에 static, final, abstract라는 기타 제한자가 있다. static은 다음에 정리 할 예정.

 

*4가지 접근제한자가 기억이 안나면⬇️

2023.12.26 - [Java/Java] - [Java]접근제한자(Access Modifier) - (1)

 

[Java]접근제한자(Access Modifier) - (1)

🤔접근제한자(Access Modifier) 클래스, 필드, 생성자, 메소드 작성시 클래스 외부에서의 접근을 제한하기 위해 사용하는 제한자 => 정보 은닉(data hiding)을 통해 사용자가 최소한의 정보만으로 프로

sukis.tistory.com



abstract : 클래스 또는 메소드 작성시 사용할 수 있는 제한자
=> 클래스 작성시 abstract 제한자를 사용하여 추상클래스로 선언
=> 메소드 작성시 abstract 제한자를 사용하여 추상메소드로 선언

 

🔘추상클래스(Abstract Class) : 객체 생성이 목적이 아닌 상속이 목적인 클래스를 작성하기 위해 선언하는 클래스 
형식) public abstract class 클래스 { }
=> new 연산자로 생성자를 호출하여 객체를 만들 수 없는 클래스

 

🔘 추상메소드(Abstract Method) : abstract 제한자를 사용하여 작성된 메소드
형식) 접근제한자 abstract 반환형 메소드명(자료형 변수명, ...);
=> 메소드의 머릿부만 작성하고 몸체부가 없는 메소드(명령이 존재하지 않는 메소드) - 미완성된 메소드
=> 자식클래스에서 반드시 오버라이드 선언해야만 하는 메소드를 작성할 때 선언하는 메소드
=> 추상메소드를 자식클래스에서 오버라이드 선언하지 않으면 에러 발생 - 강제성 O
=> 추상메소드가 하나라도 선언된 클래스는 반드시 추상클래스로 작성

final 제한자 : 클래스, 필드, 메소드 작성시 사용할 수 있는 제한자
=> 필드 작성시 final 제한자를 사용하여 final 필드로 선언 
=> 메소드 작성시 final 제한자를 사용하여 final 메소드로 선언 
=> 클래스 작성시 final 제한자를 사용하여 final 클래스로 선언 

🔘 final 필드 : 필드에 저장된 값을 변경할 수 없도록 제한하는 필드
형식) 접근제한자 final 자료형 필드명=값; 
=> final 필드 선언시 반드시 필드에 저장될 초기값 설정
=> final 필드에 저장된 초기값을 변경할 경우 에러 발생

🔘 final 메소드 : 자식클래스에서 오버라이드 선언하지 못하도록 제한하는 메소드
형식) 접근제한자 final 반환형 메소드명(자료형 변수명,...) { 명령; ... } 
=> final 메소드를 오버라이드 선언하게 되면 에러 발생

🔘 final 클래스 : 상속을 허용하지 않도록 제한하는 클래스
형식) public final class 클래스명 { }

 

더보기

*상수필드(Constant Field)

상수필드(Constant Field)란 프로그램 작성시 리터럴(값) 대신 사용하기 위해 의미있는 단어를 사용하여

작성된 필드(값) - 유지보수의 효율성 증가
형식) public static final 자료형 필드명 = 초기값;
--------------------------------------------------------------------------------------------------------------------------------------------------------

일반적으로 불변의 값을 상수라고 함.

final 필드는 한 번 초기화 하면 수정할 수 없는 필드라고 했다.

그러면 final 필드를 상수라고 불러도 되지 않을까 하지만, 상수라고 부르지 않는다.

 

불변의 값은 객체마다 저장할 필요가 없는 공용성을 띄고, 여러가지 값으로 초기화 될 수 없다.

final 필드는 객체마다 저장되고, 생성자의 매개값을 통해 여러가지 값을 가질 수 있기 때문에 상수가 될 수 없다.

상수는 static 이면서 final 이어야 한다.

static final 필드는 객체마다 존재하지 않고 클래스에만 존재하고, 한 번 저장되면 변경할 수 없다.

상수 이름은 모두 대문자로 작성하는 것이 관례 !

예) static final double PI = 3.14159;

 

 

실습예제👩‍💻

1)Employee 클래스

package oop;

//사원정보(사원번호, 사원이름)를 저장하기 위한 클래스
//=> 고용형태에 따른 모든 사원 관련 클래스가 반드시 상속받아야 되는 부모클래스
//=> 객체 생성이 목적이 아닌 상속을 목적으로 작성된 클래스 - 추상클래스로 선언하는 것을 권장
public abstract class Employee {
	
   	//필드
   	private int empNo;
	private String empName;
   	//상수필드
	public static final double INCENTIVE_RATE=1.5; 
    
	
   	//기본 생성자
	public Employee() {
	}

	//생성자 오버로딩
	public Employee(int empNo, String empName) {
		super();
		this.empNo = empNo;
		this.empName = empName;
	}
	
    
   	//getter & setter 메소드
	public int getEmpNo() {
		return empNo;
	}

	public void setEmpNo(int empNo) {
		this.empNo = empNo;
	}

	public String getEmpName() {
		return empName;
	}

	public void setEmpName(String empName) {
		this.empName = empName;
	}
	
    
	//메소드1)사원의 급여를 계산하여 반환하는 메소드   
    
   	/*
	// => 자식클래스에서 메소드 오버라이드 선언하기 위해 작성된 메소드
	// => 자식클래스에서 반드시 오버라이드 선언하지 않아도 에러 미발생 - 강제성 X
	public int computePay() {
		return 0;
	}
	*/
	
   	//위에 주석처리한 메소드를 추상메소드로 작성할 경우
	//추상메소드(Abstract Method) : abstract 제한자를 사용하여 작성된 메소드
	// => 자식클래스에서 반드시 오버라이드 선언해야만 함 - 강제성 O
	// => 추상메소드가 하나라도 선언된 클래스는 반드시 추상클래스로 작성
	public abstract int computePay();
	
    
    
   	//메소드2)성과급을 계산하여 반환하는 메소드
    
	// => 모든 사원에게 사원급여의 150%를 성과급으로 계산하여 반환
	// => 자식클래스에서 메소드를 오버라이드 선언할 경우 비정상적인 결과 발생
	// => 자식클래스에서 메소드를 오버라이드 선언하지 못하도록 final 메소드로 선언하는 것을 권장 
	public final int computeIncetive() {
		//return (int)(computePay()*1.5);
		//리터럴(프로그램에서 표현되는 값, 즉 윗줄에 1.5라고 쓰인 값) 대신에 상수를 사용하는 것을 권장
		// => 유지보수의 효율성 증가
		return (int)(computePay()*INCENTIVE_RATE);
	}
}

 

2) 1번 Employee 클래스를 상속받는 EmployeeContract, EmployeeTime, EmployeeRegular 클래스

package oop;

//계약직 사원정보(사원번호, 사원이름, 계약급여)를 저장하기 위한 클래스
//=> Employee 클래스 상속받아 작성
public class EmployeeContract extends Employee {
	private int contractPay;
	
	public EmployeeContract() {
		// TODO Auto-generated constructor stub
	}

	public EmployeeContract(int empNo, String empName, int contractPay) {
		super(empNo, empName);
		this.contractPay = contractPay;
	}

	public int getContractPay() {
		return contractPay;
	}

	public void setContractPay(int contractPay) {
		this.contractPay = contractPay;
	}
	

	/*
   	//방법1)
	//급여를 계산하여 반환하는 메소드
	public int computeContract() {
		return contractPay;
	}
	*/
	
   	//방법2)
	@Override
	public int computePay() {
		return contractPay;
	}
}

 

package oop;

//시간제 사원정보(사원번호, 사원이름, 시급, 근무시간)를 저장하기 위한 클래스
// => Employee 클래스 상속받아 작성
public class EmployeeTime extends Employee {
	private int moneyPerHour;
	private int workedHour;
	
	public EmployeeTime() {
		// TODO Auto-generated constructor stub
	}

	public EmployeeTime(int empNo, String empName, int moneyPerHour, int workedHour) {
		super(empNo, empName);
		this.moneyPerHour = moneyPerHour;
		this.workedHour = workedHour;
	}

	public int getMoneyPerHour() {
		return moneyPerHour;
	}

	public void setMoneyPerHour(int moneyPerHour) {
		this.moneyPerHour = moneyPerHour;
	}

	public int getWorkedHour() {
		return workedHour;
	}

	public void setWorkedHour(int workedHour) {
		this.workedHour = workedHour;
	}
	
   	/*
   	//방법1)
	//급여를 계산하여 반환하는 메소드
	public int computeTimePay() {
		return moneyPerHour*workedHour;		
	}
	*/

	//방법2)
	@Override
	public int computePay() {
		return moneyPerHour*workedHour;
	}
}

 

package oop;


//정규직 사원정보(사원번호, 사원이름, 연봉)를 저장하기 위한 클래스
// => Employee 클래스를 상속받아 작성
//추상메소드가 존재하는 추상클래스를 상속받은 자식클래스에서 모든 추상메소드를 오버라이드
//선언하지 않은 경우 에러 발생
public class EmployeeRegular extends Employee {
	private int annualSalary;
	
	public EmployeeRegular() {
		// TODO Auto-generated constructor stub
	}

	public EmployeeRegular(int empNo, String empName, int annualSalary) {
		super(empNo, empName);
		this.annualSalary = annualSalary;
	}

	public int getAnnualSalary() {
		return annualSalary;
	}

	public void setAnnualSalary(int annualSalary) {
		this.annualSalary = annualSalary;
	}
	
		
	/*
   	//방법1)
	//급여를 계산하여 반환하는 메소드
	public int computeSalary() {
		return annualSalary/12;
	}
	*/
	
   	//방법2)
	@Override
	public int computePay() {
		return annualSalary/12;
	}
	
	/*
	//final 메소드를 오버라이드 선언한 경우 에러 발생
	//다른 사원관련 클래스들도 마찬가지로 에러 발생
	@Override
	public int computeIncetive() {
		return 1000000000;
	}
	*/
}

 

3) 사원들의 급여 관리 프로그램을 작성하는 클래스

package oop;

//사원 급여 관리 프로그램
public class EmployeeApp {
	public static void main(String[] args) {
		/*
		//추상클래스를 사용하여 객체를 생성할 경우 에러 발생 
		Employee employee=new Employee(1000, "홍길동");
		
		System.out.println("사원번호 = "+employee.getEmpNo());
		System.out.println("사원이름 = "+employee.getEmpName());
		*/
		
		/*
      	//방법1)
		//추상클래스로 참조변수를 생성하여 자식클래스의 생성자로 만들어진 객체 저장 가능
		// => 자식클래스의 생성자에 의해 추상클래스의 생성자를 사용해 부모 객체 생성 가능
		Employee employee1=new EmployeeRegular();
		Employee employee2=new EmployeeTime();
		Employee employee3=new EmployeeContract();
		*/
		
		//다수의 객체를 저장하기 위한 배열 선언
		Employee[] empArray=new Employee[5];
		
		//배열 요소에는 EmployeeRegular 객체, EmployeeTime 객체, EmployeeContract 객체 저장 가능
		empArray[0]=new EmployeeRegular(1000, "홍길동", 96000000);
		empArray[1]=new EmployeeTime(2000, "임꺽정", 50000, 150);
		empArray[2]=new EmployeeContract(3000, "전우치", 7000000);
		empArray[3]=new EmployeeTime(4000, "일지매", 20000, 100);
		empArray[4]=new EmployeeRegular(5000, "장길산", 60000000);
		
		//반복문을 사용하여 모든 사원정보가 출력되도록 일괄 처리
		for(Employee employee : empArray) {
			System.out.println("사원번호 = "+employee.getEmpNo());
			System.out.println("사원이름 = "+employee.getEmpName());
			
			/*
           	//방법1)
			//급여를 계산하여 반환하는 메소드를 호출해 급여를 반환받아 출력
			// => 참조변수는 부모 객체를 이용하여 부모클래스의 메소드만 호출되므로 자식클래스의 메소드 호출 불가능
			// => 명시적 객체 형변환을 사용하여 참조변수를 자식클래스로 변경하여 참조변수에
			//자식 객체가 저장되도록 만들어 자식클래스의 메소드 호출
			// => instanceof 연산자를 사용하여 자식클래스를 구분하여 명시적 객체 형변환 - ClassCastException 방지
			if(employee instanceof EmployeeRegular) {
				System.out.println("사원급여 = "+((EmployeeRegular)employee).computeSalary());
			} else if(employee instanceof EmployeeTime) {
				System.out.println("사원급여 = "+((EmployeeTime)employee).computeTimePay());
			} else if(employee instanceof EmployeeContract) {
				System.out.println("사원급여 = "+((EmployeeContract)employee).computeContract());
			}
			*/
			
          	//방법2)
			//부모클래스의 메소드를 오버라이드 선언한 메소드를 부모클래스의 참조변수로 호출한 경우
			//묵시적 객체 형변환이 발생하여 참조변수가 자식클래스로 변환되어 자식객체를 참조해
			//자식클래스의 메소드 호출
			// => 참조변수가 참조할 수 있는 자식 객체에 따라 다른 자식클래스의 메소드 호출 - 메소드 오버라이딩 의한 다형성
			//자식클래스에서 부모클래스의 메소드를 오버라이드 선언하지 않으면 부모클래스의 메소드 호출
			// => 비정상적인 결과 발생(실행 오류)
			System.out.println("사원급여 = "+employee.computePay());
          	
			//성과급을 계산하여 반환하는 메소드를 호출해 성과급을 반환받아 출력
			System.out.println("성과급 = "+employee.computeIncetive());
			System.out.println("==========================================================");
		}
		
	}
}

 

실행 결과⬇️

사원번호 = 1000

사원이름 = 홍길동

사원급여 = 8000000

성과급 = 12000000

==========================================================

사원번호 = 2000

사원이름 = 임꺽정

사원급여 = 7500000

성과급 = 11250000

==========================================================

사원번호 = 3000

사원이름 = 전우치

사원급여 = 7000000

성과급 = 10500000

==========================================================

사원번호 = 4000

사원이름 = 일지매

사원급여 = 2000000

성과급 = 3000000

==========================================================

사원번호 = 5000

사원이름 = 장길산

사원급여 = 5000000

성과급 = 7500000

==========================================================

 

[꼭 기억할 부분]

부모클래스의 메소드를 오버라이드 선언한 메소드를 부모클래스의 참조변수로 호출한 경우,

묵시적 객체 형변환이 발생하여 참조변수가 자식클래스로 변환되어 자식객체를 참조해 자식클래스의 메소드 호출
=> 참조변수가 참조할 수 있는 자식 객체에 따라 다른 자식클래스의 메소드 호출 - 메소드 오버라이딩 의한 다형성

 

 

[짚고 넘어가기]instanceof 연산자란?

객체 타입을 확인하는 연산자이다.

- 형변환 가능 여부를 확인하며 true / false로 결과를 반환한다.

- 주로 상속 관계에서 부모객체인지 자식 객체인지 확인하는 데 사용된다.

 

 


▶️참고

 

 Java 싱글톤, final 필드, 상수, 접근제한자, getter와 setter, 상속, 오버라이딩|작성자 YesJi

 

어서오세옹 : 네이버 블로그

소개를 어떻게 할까나…

blog.naver.com

https://sisiblog.tistory.com/401

 

[javascript] 자바스크립트 instanceof 연산자

참고: https://flexiple.com/javascript/instanceof-javascript/ 이 포스트에서는 instanceof 연산자를 알아보고 예제를 통해 실제로 수행되는 작업을 확인해 보겠습니다. instanceof 란? 자바스크립트의 instanceof 연산

sisiblog.tistory.com

 

반응형