TOP
본문 바로가기
프로그래밍 및 기타/Java

[java] 접근 제한자, 클래스 상속

by 기록자_Recordian 2025. 4. 4.
728x90
반응형
이전 내용
 

[java] 정적 멤버와 Static, 싱글톤

이전 내용 [java] java 예제 (메소드 오버로딩)이전 내용 [java] 메소드 선언 : 가변 인수 모를 때 (feat. enhanced for문)이전 내용 [java] java - 클래스, this() 코드이전 내용 [java] 예제 및 풀이 (Switch, 배열

puppy-foot-it.tistory.com


접근 제한자(access modifier)

 

접근 제한자는 말 그대로 접근을 제한하기 위해 사용된다. 접근은 클래스 및 인터페이스 그리고 이들이 가지고 있는 멤버의 접근을 말한다.

 

접근 제한자는 네 가지 종류가 있다.

  • public: 외부 클래스가 자유롭게 사용 가능
  • protected: 같은 패키지 또는 자식 클래스에 사용 가능 (패키지가 다르더라도 상속 관계에 있으면 사용 가능)
  • private: 외부에서 사용 불가. 같은 클래스 내부에서만 사용 가능
  • default: 위 세 가지 접근 제한자가 적용되지 않은 것. 같은 패키지에 소속된 클래스에서만 사용 가능

클래스 상속

 

클래스를 상속받으려면 상속받고자 하는 자식 클래스명 옆에 extends 키워드를 붙이고, 상속할 부모 클래스명을 적으면 된다. (자바의 클래스는 단일 상속으로, extends 뒤에는 하나의 클래스만 올 수 있다.)

public class People {
	public String name;
	public String ssn;

	public People(String name, String ssn) {
		this.name = name;
		this.ssn = ssn;
	}
}

 

Student 클래스는 People 클래스를 상속받는다.

부모 클래스의 생성자를 호출하려면 super() 라는 키워드를 사용하면 되며, super( 매개값, ... ) 는 매개값의 타입과 일치히는 부모 생성지를 호출한다. 만약 매개값의 타입과 일치히는 부모 생성자가 없을 경우 컴파일 에러가 발생한다. 또한, super는 생성자의 가장 첫줄에 위치해야 한다.

public class Student extends People {
	public int studentNo;

	public Student(String name, String ssn, int studentNo) {
		super(name, ssn); // 부모 생성자 호출
		this.studentNo = studentNo;
	}
}
public class StudentExample {
	public static void main(String[] args) {
		Student student = new Student("홍길동", "123456-1234567", 1);
		System.out.println("name: " + student.name);
		System.out.println("ssn: " + student.ssn);
		System.out.println("studentNo: " + student.studentNo);
	}
}

 


접근 제한자
+ Getter와 Setter

 

예를 들어, People 클래스를 상속 받는 Student 클래스의 studentNo 변수를 private로 바꾸게 되면 에러가 발생한다.

 

또한, StudentExample에서 studentNo 역시 private로 설정되어 외부에서 접근할 수 없기 때문에 에러가 발생한다.

 

◆ Getter

이때 Student 클래스에 getter인 getStudentNo() 메소드를 만들어주고

public class Student extends People {
	private int studentNo;

	public Student(String name, String ssn, int studentNo) {
		super(name, ssn); // 부모 생성자 호출
		this.studentNo = studentNo;
	}

	public int getStudentNo() {
		return studentNo;
	}
}

 

StudentExample 에서 getStudentNo() 메소드를 받도록 바꿔주면 된다.

public class StudentExample {
	public static void main(String[] args) {
		Student student = new Student("홍길동", "123456-1234567", 1);
		System.out.println("name: " + student.name);
		System.out.println("ssn: " + student.ssn);
		System.out.println("studentNo: " + student.getStudentNo());
	}
}

▶ studentNo 필드는 private로 설정되어 외부에서 접근할 수 없으며, getStudentNo() 메소드를 통해서만 접근할 수 있다.

 

◆ Setter

Setter 메소드는 특정 필드의 값을 외부에서 안전하게 설정(변경)할 수 있도록 하는 메소드이다.

setStudentNo(int studentNo) 메소드는 studentNo 필드에 값을 설정하는 역할을 하고, 이를 통해 객체의 상태를 변경할 수 있다.

 

[역할]

1. 캡슐화 (Encapsulation):
studentNo 필드는 private로 정의되어 외부에서 직접 접근할 수 없다. 이로 인해 데이터 은닉을 구현할 수 있다.
Setter 메소드를 통해 외부에서 값을 변경할 수 있도록 함으로써, 객체의 필드에 대한 직접적인 접근을 차단하고, 데이터의 무결성을 유지할 수 있다.


2. 입력 값 검증 (Validation):
필요할 경우, Setter 메소드 내에서 전달된 값에 대한 검증을 추가하여 유효성을 체크할 수 있다. 예를 들어, studentNo가 음수일 수 없다면, 값을 설정하기 전에 해당 조건을 검사할 수 있다. 이는 코드의 안정성을 높이는 데 도움이 된다.


3. 변경 감지 (Change detection):
Setter 메소드를 사용하여 값이 변경될 때마다 추가적인 작업(예: 로그 출력, 상태 업데이트 등)을 수행할 수 있다. 예를 들어, 특정 값으로 설정될 때 알림을 보내거나 관련된 다른 필드의 값을 조정할 수 있다.

 

 studentNo 필드를 새로운 값으로 변경하려면, Student 클래스에 Setter 메소드를 생성하고

public class Student extends People {
	private int studentNo;

	public Student(String name, String ssn, int studentNo) {
		super(name, ssn); // 부모 생성자 호출
		this.studentNo = studentNo;
	}

	public int getStudentNo() { // studentNo를 꺼내주는 역할
		return studentNo; // return 있음
	}

	public void setStudentNo(int studentNo) { // studentNo 값을 받는 역할
		this.studentNo = studentNo; // return 없음
	}
}

 

StudentExample 클래스에서 메소드에 새로운 값을 지정해주고 getter로 불러오면 된다.

public class StudentExample {
	public static void main(String[] args) {
		Student student = new Student("홍길동", "123456-1234567", 1);
		System.out.println("name: " + student.name);
		System.out.println("ssn: " + student.ssn);
		System.out.println("studentNo: " + student.getStudentNo());

		student.ssn = "123456-22234567";
		System.out.println("ssn: " + student.ssn);
		student.setStudentNo(150);
		System.out.println("studentNo: " + student.getStudentNo());
	}
}


메소드 재정의
(오버라이딩)

 

메소드 재정의는 자식 클래스에서 부모 클래스의 메소드를 다시 정의하는 것을 말한다.

  • 부모의 메소드와 동일한 시그니처(리턴 타입, 메소드 이름, 매개 변수 목록)를 가져야 한다.
  • 접근 제한을 더 강하게 재정의할 수 없다.
  • 새로운 예외(Exception)를 던질 수 없다.
public class Calculator {
	double areaCircle(double r) {
		System.out.println("Calculator 객체의 areaCircle() 실행");
		return 3.14159 * r * r;
	}
}

▶ Calculator의 areaCircle() 메소드는 파이의 값을 3.14159로 계산하였지만, 좀 더 정밀한 계산을 위해 Computer의 areaCircle() 메소드는 Math.PI 상수를 이용한다.

 

@Override 애너테이션은 메소드를 부모 클래스의 메소드를 재정의(오버라이드)한다는 것을 명시한다. 이를 통해 컴파일러는 부모 클래스에서 정의된 메소드와의 일관성을 확인한다.

애너테이션은 생략해도 좋으나, 이것을 붙여주면 areaCircle() 메소드가 정확히 재정의된 것인지 컴파일러가 확인하기 때문에 실수를 줄여준다.

public class Computer extends Calculator {
	@Override
	double areaCircle(double r) {
		System.out.println("Computer 객체의 areaCircle() 실행");
		return Math.PI * r * r;
	}
}
public class ComputerExample {

	public static void main(String[] args) {
		int r = 10;
		Calculator calculator = new Calculator();
		System.out.println("원 면적: " + calculator.areaCircle(r));
		System.out.println();
		Computer computer = new Computer();
		System.out.println("원 면적: " + computer.areaCircle(r));
	}
}


메소드 재정의 예제1

 

◆ Employee와 Manager

사원은 보너스 5%, 매니저는 보너스 10%

 

1.  Manager

  • 필드: name, salary
  • 매개 변수 생성자
  • 메소드: calculateBonus()

2. Employee

  • Manager의 자식
  • 매개 변수 생성자
  • 메소드: caculateBonus()

Manager 클래스

public class Manager {
	// 필드
	String name;
	int salary;

	// 생성자
	public Manager(String name, int salary) {
		this.name = name;
		this.salary = salary;
	}

	double calculate() {
		double bonus = salary * 0.1;
		return bonus;
	}
}

 

Employee 클래스 (Manager 클래스 상속)

자식 클래스에서 부모 클래스의 메소드를 재정의하게 되면, 부모 클래스의 메소드는 숨겨지고 재정의된 자식 메소드만 사용된다. 그러나 자식 클래스 내부에서 재정의된 부모 클래스의 메소드를 호출해야 하는 상황이 발생할 경우, super 키워드를 붙여 부모 메소드를 호출할 수 있다.

public class Employee extends Manager {
	// 생성자
	public Employee(String name, int salary) {
		super(name, salary);
	}

	@Override
	double calculate() {
		double bonus = super.salary * 0.05;
		return bonus;
	}

}

 

PrintBonus 클래스 (보너스 금액 출력)

public class PrintBonus {

	public static void main(String[] args) {
		Manager manager1 = new Manager("김매니저", 5_000_000);
		System.out.println(manager1.name + "의 보너스는 " + manager1.calculate());

		System.out.println();
		Employee employee1 = new Employee("이직원", 3_000_000);
		System.out.println(employee1.name + "의 보너스는 " + employee1.calculate());
	}
}


예제2

 

◆ 학생 관리 시스템 구현

1. Person 클래스

  • 속성: name(문자열), age(정수)
  • 생성자: 이름과 나이를 매개변수로 받아 초기화
  • 메소드: displayinfo() - 이름과 나이 출력

2. Student 클래스

  • Person 클래스 상속 받음
  • 추가 속성: studentId(문자열), major(문자열)
  • 생성자: 이름, 나이, 학번, 전공을 매개변수로 받아 초기화
  • 메소드: displayinfo() - 부모 클래스의 정보를 포함해 학번과 전공까지 출력 (오버라이딩)

3. StudentManagement 클래스

  • main 메소드
    • a. Person 객체 1개를 생성하고 정보 출력
    • b. Student 객체 1개를 생성하고 정보 출력

Person 클래스

public class Person {
	// 필드 생성
	String name;
	int age;

	// 생성자
	Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	// 메소드
	String displayinfo() {
		return name + age;
	}
}

 

Student 클래스 (Person 클래스 상속)

public class Student extends Person {
	// 필드
	String studentId;
	String major;

	// 생성자
	Student(String name, int age, String studentId, String major) {
		super(name, age);
		this.studentId = studentId;
		this.major = major;
	}

	// 메소드
	@Override
	String displayinfo() {
		return super.name + super.age + studentId + major;
	}
}

 

StudentManagement 클래스 (정보 출력)

public class StudentManagement {

	public static void main(String[] args) {
		Person person1 = new Person("김영희", 25);
		System.out.println("=== 일반인 정보 ===");
		System.out.println("이름: " + person1.name + ", 나이: " + person1.age);

		System.out.println();
		Student student1 = new Student("이철수", 20, "2023001", "컴퓨터공학");
		System.out.println("=== 학생 정보 ===");
		System.out.println("이름: " + student1.name + ", 나이: " + student1.age);
		System.out.println("학번: " + student1.studentId + ", 전공: " + student1.major);
	}
}


예제3

 

◆ 은행 계좌 관리 시스템 구현

은행 계좌를 관리하는 시스템을 설계. 일반 계좌와 VIP 계좌의 이자를 다르게 계산

1. Account 클래스

  • 계좌 번호(accountNumer), 잔액(balance)을 멤버 변수로 받음
  • calculateInterest() 메소드로 연 이자 (잔액 * 1%) 계산

2. VIPAccount 클래스 (Account 클래스 상속)

  • calculateInterest() 오버라이딩해서 VIP 이자율(잔액 * 5%) 적용 ▶ 이때 super를 사용해 부모의 이자를 포함

3. main 클래스

두 계좌를 생성하고 출력

 

Account 클래스

public class Account {
	String accountNumber;
	int balance;

	Account(String accountNumber, int balance) {
		this.accountNumber = accountNumber;
		this.balance = balance;
	}

	double calculateInterest() {
		double interest = balance * 0.01;
		return interest;
	}
}

 

VIPAccount 클래스 (Account 클래스 상속)

super.calculateInterest() 로 부모의 메소드를 호출

public class VIPAccount extends Account {
	VIPAccount(String accountNumber, int balance) {
		super(accountNumber, balance);
	}

	@Override
	double calculateInterest() {
		double interest = super.calculateInterest();
		double vipInterest = interest + (balance * 0.05);
		return vipInterest;
	}
}

 

printAccount 클래스 (고객 정보 출력)

public class printAccount {

	public static void main(String[] args) {
		Account account1 = new Account("123-456-12345", 1_000_000);
		System.out.println("일반 고객 계좌: " + account1.accountNumber);
		System.out.println("원금: " + account1.balance);
		System.out.println("이자: " + account1.calculateInterest());

		System.out.println();
		VIPAccount vipacc1 = new VIPAccount("011-123-5678", 1_000_000);
		System.out.println("VIP 고객 계좌: " + vipacc1.accountNumber);
		System.out.println("원금: " + vipacc1.balance);
		System.out.println("이자: " + vipacc1.calculateInterest());
	}
}

 

 


[참고]

혼자 공부하는 자바

 


다음 내용

 

 

728x90
반응형