상속
상속이란 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소이다.
두 클래스를 상위 클래스와 하위 클래스로 나누어 상위 클래스의 멤버(필드, 메서드, 이너 클래스)를 하위 클래스와 공유하는 것을 의미한다.
두 클래스 간 상속 관계를 설정할 때 사용하는 extends 키워드 자체가 "확장하다"라는 의미를 가지고 있다는 점에 유의할 필요가 있다. "~클래스로부터 확장되었다"는 표현이 더 적절하다. 자바의 객체지향 프로그래밍에서는 단일 상속(single inheritance)만을 허용한다.
우리는 이 두 클래스를 서로 상속 관계 있다고 하며, 하위 클래스는 상위 클래스가 가진 모든 멤버를 상속받게 된다.
즉, 하위 클래스의 멤버 개수는 언제나 상위 클래스의 맴버와 비교했을 때 같거나 많다.

왜 상속을 사용할까?
1. 코드를 재사용하여 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있어 코드의 중복을 제거할 수 있다.
2. 다형적 표현이 가능하다는 장점이있다. 위의 예시로 예를 들면, ‘프로그래머는 프로그래머이다'라는 문장은 참입니다. 그와 동시에 ‘프로그래머는 사람이다' 또한 참입니다. 즉 하나의 객체가 여러 모양으로 표현될 수 있다는 것을 우리는 다형성이라 말한다.
자바에서 상속을 구현하는 방법은 클래스명 다음에 extends 상위 클래스명을 사용하여 정의 하면 된다.
class Person {
String name;
int age;
void learn(){
System.out.println("공부를 합니다.");
};
void walk(){
System.out.println("걷습니다.");
};
void eat(){
System.out.println("밥을 먹습니다.");
};
}
class Programmer extends Person { // Person 클래스로부터 상속. extends 키워드 사용
String companyName;
void coding(){
System.out.println("코딩을 합니다.");
};
}
class Dancer extends Person { // Person 클래스로부터 상속
String groupName;
void dancing(){
System.out.println("춤을 춥니다.");
};
}
class Singer extends Person { // Person 클래스로부터 상속
String bandName;
void singing(){
System.out.println("노래합니다.");
};
void playGuitar(){
System.out.println("기타를 칩니다.");
};
}
public class HelloJava {
public static void main(String[] args){
//Person 객체 생성
Person p = new Person();
p.name = "사람";
p.age = 24;
p.learn();
p.eat();
p.walk();
System.out.println(p.name);
//Programmer 객체 생성
Programmer pg = new Programmer();
pg.name = "프로그래머";
pg.age = 26;
pg.learn(); // Persons 클래스에서 상속받아 사용 가능
pg.coding(); // Programmer의 개별 기능
System.out.println(pg.name);
}
}
//출력값
공부를 합니다.
밥을 먹습니다.
걷습니다.
사람
공부를 합니다.
코딩을 합니다.
프로그래머
포함관계
포함(composite)은 상속처럼 클래스를 재사용할 수 있는 방법으로, 클래스의 멤버로 다른 클래스 타입의 참조변수를 선언하는 것을 의미한다.
public class Employee {
int id;
String name;
Address address;
public Employee(int id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
}
void showInfo() {
System.out.println(id + " " + name);
System.out.println(address.city+ " " + address.country);
}
public static void main(String[] args) {
Address address1 = new Address("서울", "한국");
Address address2 = new Address("도쿄", "일본");
Employee e = new Employee(1, "상원윤", address1);
Employee e2 = new Employee(2, "홍길동", address2);
e.showInfo();
e2.showInfo();
}
}
class Address {
String city, country;
public Address(String city, String country) {
this.city = city;
this.country = country;
}
}
// 출력값
1 상원윤
서울 한국
2 홍길동
도쿄 일본
위 예시를 보면 Address 클래스에 포함되어 있는 인스턴스 변수 city와 country를 각각 Employee 클래스의 변수로 정의해주어야 하지만, Address 클래스로 해당 변수들을 묶어준다음 Employee 클래스 안에 참조변수를 선언하는 방법으로 코드의 중복을 없애고 포함관계로 재사용하고 있다.
클래스 간의 관계를 설정하는 데 있어서 상속관계, 포함관계 를 맺을지 어떤 기준으로 판별할 수 있을까?
클래스 간의 관계가 ‘~은 ~이다(IS-A)’ 관계인지 ~은 ~을 가지고 있다(HAS-A) 관계인지 문장을 만들어 생각해보는 것이다.
위 예시를 보면, Employee는 Address이다. 라는 문장은 성립하지 않는 반면, Employee는 Address를 가지고 있다. 는 어색하지 않은 올바른 문장임을 알 수 있습니다. 따라서 이 경우에는 상속보다는 포함관계가 적합하다.

메서드 오버라이딩(Method Overriding)
상위 클래스로부터 상속받은 메서드와 동일한 이름의 메서드를 재정의하는 것을 의미한다.
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"
메서드 오버라이딩을 사용하려면 세 가지 조건을 만족시켜야한다.
- 메서드의 선언부(메서드 이름, 매개변수, 반환 타입)가 상위클래스의 그것과 완전히 일치해야한다.
- 접근 제어자의 범위가 상위 클래스의 메서드보다 같거나 넓어야 한다.
- 예외는 상위 클래스의 메서드보다 많이 선언할 수 없다.
모든 객체를 상위 클래스 타입 하나로 선언하면, 배열로 선언하여 관리할 수 있다.
Vehicle[] vehicles = new Vehicle[] { new Bike(), new Car(), new MotorBike()};
for (Vehicle vehicle : vehicles) {
vehicle.run();
}
// 출력값
Bike is running
Car is running
MotorBike is running
super 키워드와 super()
super
상위 클래스의 객체 호출할때 사용
경우에 따라서 상위 클래스의 변수를 참조해야할 경우가 종종 있는데 그 경우 super 키워드를 사용하면 부모의 객체의 멤버 값을 참고할 수 있다. 즉, 상위 클래스의 멤버와 자신의 멤버를 구별하는 데 사용된다는 점을 제외한다면 this와 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 클래스
Object 클래스는 자바의 클래스 상속계층도에서 최상위에 위치한 상위클래스입니다. 따라서 자바의 모든 클래스는 Object 클래스로부터 확장된다는 명제는 항상 참이다.
실제로 자바 컴파일러는 컴파일링의 과정에서 다른 클래스로부터 아무런 상속을 받지 않는 클래스에 자동적으로 extends Object를 추가하여 Object 클래스를 상속받도록 한다. 최상위에 Object 클래스가 있다.
Object 클래스의 대표적인 몇 가지 메서드
| 메서드명 | 반환 타입 | 주요 내용 |
| toString() | String | 객체 정보를 문자열로 출력 |
| equals(Object obj) | boolean | 등가 비교 연산(==)과 동일하게 스택 메모리값을 비교 |
| hashCode() | int | 객체의 위치정보 관련. Hashtable 또는 HashMap에서 동일 객체여부 판단 |
| wait() | void | 현재 쓰레드 일시정지 |
| notify() | void | 일시정지 중인 쓰레드 재동작 |
캡슐화
캡슐화란 특정 객체 안에 관련된 속성과 기능을 하나의 캡슐(capsule)로 만들어 데이터를 외부로부터 보호하는 것을 말한다.

캡슐화의 가장 큰 장점은 정보 은닉(data hiding)에 있다. 외부로부터 객체의 속성과 기능이 함부로 변경되지 못하게 막고, 데이터가 변경되더라도 다른 객체에 영향을 주지 않기에 독립성을 확보할 수 있다.
패키지(package)
패키지(package)란 특정한 목적을 공유하는 클래스와 인터페이스의 묶음을 의미한다.
- 클래스들을 그룹 단위로 묶어 효과적으로 관리하기 위한 목적을 가지고 있다.
- 자바에서 패키지는 물리적인 하나의 디렉토리(directory)이고, 하나의 패키지에 속한 클래스나 인터페이스 파일은 모두 해당 패키지에 속해있다.
- 디렉토리는 하나의 계층구조를 가지고 있는데, 계층 구조 간 구분은 점(.)으로 표현된다.
- 패키지가 있는 경우 소스 코드의 첫 번째 줄에 반드시 package 패키지명이 표시되어야 하고, 만약 패키지 선언이 없으면 이름없는 패키지에 속하게 된다.
// 패키지를 생성했을 때
package practicepack.test; // 패키지 구문 포함. 패키지가 없다면 구문 필요없음
public class PackageEx {
}
패키지로 클래스를 묶는 것의 또 하나의 장점은 클래스의 충돌을 방지해주는 기능에 있다. 예를 들면, 같은 이름의 클래스를 가지고 있더라고 각각 다른 패키지에 소속되어 있다면 이름명으로 인한 충돌이 발생하지 않는다.
규모가 큰 프로젝트에서 협업시 클래스명 중복으로 인한 충돌이 종종 발생할 수 있는데 패키지를 설정하면 이러한 클래스 간의 충돌을 효과적으로 방지할 수 있다.
Import문
import문은 다른 패키지 내의 클래스를 사용하기 위해 사용하며, 일반적으로 패키지 구문과 클래스문 사이에 작성한다.
import 패키지명.클래스명; 또는 import 패키지명.*;
먼저 import 키워드를 써주고 패키지명과 패키지명을 생략하고자하는 클래스명을 함께 써주면 된다.
만약 같은 패키지에서 여러 클래스가 사용될 때는 import문을 여러번 사용하기보다는 위에 작성된 것처럼 import 패키지명.* 으로 작성하면 해당 패키지의 모든 클래스를 패키지명 없이 사용가능하다.
자바 프로그래밍에서 제어자는 클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드를 의미한다.
자바에서 제어자는 크게 접근 제어자와 기타 제어자로 구분 할 수 있다.
제어자(Modifier)
자바 프로그래밍에서 제어자는 클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드를 의미한다. 파란 하늘', ‘ 붉은 노을'에서 ‘파란'과 ‘붉은'처럼 명사를 꾸며주는 형용사의 역할과 같다고 할 수 있다.
자바에서 제어자는 크게 접근 제어자와 기타 제어자로 구분 할 수 있다.
| 접근 제어자 | public, protected, (default), private |
| 기타 제어자 | static, final, abstract, native, transient, synchronized 등 |
접근 제어자(Access Modifier)
접근 제어자를 사용하면 클래스 외부로의 불필요한 데이터 노출을 방지(data hiding)할 수 있고, 외부로부터 데이터가 임의로 변경되지 않도록 막을 수 있다.
자바 접근 제어자로 private, default, protected, public 4 가지가 있다.

getter와 setter 메서드
setter 메서드는 외부에서 메서드에 접근하여 조건에 맞을 경우 데이터 값을 변경 가능하게 해주고 일반적으로 메서드명에 set-을 붙여서 정의합니다.
예시를 보면 이름 값을 변경하기 위해 setName()이라는 메서드를 사용하고 있음을 확인할 수 있습니다.
getter 메서드는 이렇게 설정한 변수 값을 읽어오는 데 사용하는 메서드이다.
public class GetterSetterTest {
public static void main(String[] args) {
Worker w = new Worker();
w.setName("상원윤");
w.setAge(25);
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;
}
}
// 출력값
근로자의 이름은 상원윤
근로자의 나이는 25
근로자의 ID는 5
이렇게 setter와 getter 메서드를 활용하면 데이터를 효과적으로 보호하면서도 의도하는 값으로 값을 변경하여 캡슐화를 보다 효과적으로 달성할 수 있다.
'Java' 카테고리의 다른 글
| Java기초 사칙연산 계산기 만들기 (0) | 2022.09.09 |
|---|---|
| Java 다향성, 추상화, 인터페이스 (0) | 2022.09.07 |
| Java 생성자, 내부 클래스 (1) | 2022.09.05 |
| Java 객체지향 프로그래밍 기초 (0) | 2022.09.03 |
| Java 배열(Array) (0) | 2022.09.02 |