[13일차]자바 상속,오버라이딩,super,Object,패캐지,접근제어자,getter와setter

유태형·2022년 5월 12일
0

코드스테이츠

목록 보기
14/77

오늘의 목표

  1. 상속
  2. 포함관계
  3. 오버라이딩
  4. super, super()
  5. Object
  6. 캡슐화
  7. 패키지
  8. 접근제어자
  9. setter(), getter()



내용

객체 지향 프로그래밍의 4가지 핵심

  • 상속 : 부모가 자식에게 물려주는 것과 마찬가지로 상위클래스(부모클래스)가 하위클래스(자식클래스)에게 멤버들을 물려주어 상속받은 하위클래스가 멤버들을 그대로 사용할 수 있습니다.
  • 캡슐화 : 객체 지향 프로그래밍(OOP)에서 객체의 데이터와 기능을 하나로 묶고 외부에 노출되지 않도록 숨김 처리 하는 것을 말합니다. 변수, 메서드 클래스에 대해 접근 제어자를 사용함으로써 캡슐화를 할 수 있습니다.
  • 다형성 : 하위 클래스는 상위 클래스로 부터 상속 받습니다. 하위 클래스의 인스턴스는 상위 클래스의 멤버를 모두 사용할 수 있으므로 상위 클래스의 참조 변수가 하위 클래스의 인스턴스를 가리킬 수 있습니다.
  • 추상화 : 클래스들의 불필요하거나 집약적인 부분을 생략하고 공통적이고 핵심적인 성질과 속성을 묶어 상위 클래스를 만드는 개념입니다. 재사용성, 가독성, 유지보수에 있어 많은 시간을 절약할 수 있습니다.



상속 관계

package JAVA_OOP_Extend;
//상위 클래스
class Person{
    String name;
    int age;

    void learn(){
        System.out.println("공부를 합니다.");
    }
    void walk(){
        System.out.println("걷습니다.");
    }
    void eat(){
        System.out.println("밥을 먹습니다.");
    }
}
//하위클래스, Person으로부터 상속
class Programmer extends Person{
    String companyName;

    void coding(){
        System.out.println("코딩을 합니다.");
    }
}
//하위클래스, Person으로부터 상속
class Dancer extends Person{
    String groupName;

    void dancing(){
        System.out.println("춤을 춥니다.");
    };
}
//하위클래스, Person으로부터 상속
class Singer extends 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 = 28;
        System.out.println(p.name);
        System.out.println(p.age);
        p.learn();
        p.eat();
        p.walk();


        //Programmer
        Programmer pg = new Programmer();
        //Person 멤버 상속
        pg.name = "김개발";
        pg.age = 26;
        System.out.println(pg.name);
        System.out.println(pg.age);
        pg.learn();
        pg.eat();
        pg.walk();
        //Programmer 멤버
        pg.companyName = "네카라쿠배당토";
        System.out.println(pg.companyName);
        pg.coding();


        //Dancer
        Dancer dc = new Dancer();
        //Person 멤버 상속
        pg.name = "박댄서";
        pg.age = 23;
        System.out.println(pg.name);
        System.out.println(pg.age);
        pg.learn();
        pg.eat();
        pg.walk();
        //Dancer 멤버
        dc.groupName = "JYPSMYG";
        System.out.println(dc.groupName);
        dc.dancing();


        //Singer
        Singer sg = new Singer();
        //Person 멤버 상속
        pg.name = "최가수";
        pg.age = 32;
        System.out.println(pg.name);
        System.out.println(pg.age);
        pg.learn();
        pg.eat();
        pg.walk();
        //Singer 멤버
        sg.bandName = "버스킹";
        System.out.println(sg.bandName);
        sg.playGuitar();
        sg.singing();

    }
}
//Consle
//Person
이사람
28
공부를 합니다.
밥을 먹습니다.
걷습니다.
//Programmer
김개발
26
공부를 합니다.
밥을 먹습니다.
걷습니다.
네카라쿠배당토
코딩을 합니다.
//Dancer
박댄서
23
공부를 합니다.
밥을 먹습니다.
걷습니다.
JYPSMYG
춤을 춥니다.
//Singer
최가수
32
공부를 합니다.
밥을 먹습니다.
걷습니다.
버스킹
기타를 칩니다.
노래합니다.

자바에서는 하위클래스 작성 시 extends키워드를 통하여 상속관계를 정의할 수 있습니다.
Programmer, Dancer, Singerextends키워드를 통해 Person 클래스를 상속 받습니다. 이 때 상위 클래스는 Person 하위클래스는 Programmer, Dancer, Singer가 됩니다.
또 하위클래스에서는 상위클래스에서 부터 상속 받은 멤버들을 별도의 선언 없이 사용할 수 있습니다.
Programmer, Dancer, Singer는 자기자신에서 선언한 멤버들 뿐만 아니라 Person으로 부터 상속받은 멤버 변수들과 메서드들을 선언 없이 자유롭게 사용할 수 있습니다. 단 자바에서는 다중 상속을 허용하지 않습니다. 여러 부모를 둘 수 없고 하나의 부모만 둘 수 있습니다.




포함 관계

상속 관계와는 다르지만 다른 클래스에서 유용하게 사용할 수 있으므로 익혀 두는 것이 좋습니다.

package JAVA_OOP_Extend;

class Address{
    String city, country;

    public Address(String city, String country){
        this.city = city;
        this.country = country;
    }
}

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();

        e = new Employee(1,"김한국",address2);
        e2 = new Employee(2,"제임스",address1);
        e.showInfo();
        e2.showInfo();
    }
}

//Console
//e
1 김한국
서울 한국 //address1
//e2
2 제임스
워싱턴 미국 //address2
//e
1 김한국
워싱턴 미국 //address2
//e2
2 제임스
서울 한국 //address1

Employee클래스의 멤버변수로 Address클래스 타입의 참조변수가 있습니다. 외부에서 묶어 두었던 클래스를 멤버로 활용 하는 관계를 포함 관계라고 부릅니다.

포함관계와 상속관계는 클래스들 간 엄연히 다른 관계이며 개발자는 이를 잘 이해하고 활용해야 합니다.

  • 상속 관계 : ~은~이다(IS-A)관계가 성립하면 상속관계가 성립합니다. Programmer는 Person이다., Dancer는 Person이다., Singer는 Person이다.와 같이 어색하지 않으면 상속 관계입니다.
  • 포함 관계 : ~은~을 가지고 있다(HAS-A)관계가 성립하면 포함관계가 성립합니다. Employee는 Address를 가지고 있다.는 어색하지 않습니다. 반대로 Employee는 Address다.Address는 Employee다 와 같이 IS-A관계는 어색함을 알 수 있습니다.



오버라이딩

메서드 오버라이딩(Overriding)과 메서드 오버로딩(Overloading)은 이름도 비슷하고 메서드를 다룬다는 점에서 혼동이 자주 발생하는 객체지향 프로그래밍 개념 중 하나입니다. 하지만 둘은 엄연히 다릅니다.

  • 오버로딩 : 같은 클래스 내에 필요한 만큼 여러개의 메서드를 만들 수 있습니다.
  • 오버라이딩 : 상위 클래스에서 상속받은 메서드를 하위 클레스에 맞게 재 정의합니다.

메서드 오버라이딩 시 3가지 조건을 만족해야 합니다.

1.메서드 시그니처(반환타입,메서드명,매개변수)는 상위 클래스의 메서드 시그니처와 완전히 동일해야 합니다.
2. 접근 지정자의 범위가 상위 클래스의 메서드보다 같거나 넓어야 합니다.
3. 상위 클래스보다 예외를 더 많이 선언할 수 없습니다.

package JAVA_OOP_Extend;

class Vehicle{
    void run() {
        System.out.println("Vehicle is running");
    }
}
class Bike extends Vehicle{ //Vehicle 상속
    void run(){ //Vehicle.run()을 오버라이딩
        System.out.println("Bike is running");
    }
}
class Car extends Vehicle { //Vehicle 상속
    void run() { //Vehicle.run()을 오버라이딩
        System.out.println("Car is running");
    }
}
class MotorBike extends Vehicle { //Vehicle 상속
    void run() { //Vehicle.run()을 오버라이딩
        System.out.println("MotorBike is running");
    }
}
public class OverridingExample {
    public static void main(String[] args) {
        //각각의 하위클래스타입 레퍼런스 = new 하위클래스타입 객체
        Bike bike = new Bike();
        Car car = new Car();
        MotorBike motorBike = new MotorBike();
        bike.run();
        car.run();
        motorBike.run();

        //상위클래스타입 레퍼런스 = new 하위클래스타입 객체 (업캐스팅)
        Vehicle bike2 = new Bike();
        Vehicle car2 = new Car();
        Vehicle motorBike2 = new MotorBike();
        bike2.run();
        car2.run();
        motorBike2.run();

        //상위클래스타입 래퍼런스를 배열로 (업캐스팅)
        Vehicle[] vehicles = new Vehicle[]{new Bike(), new Car(), new MotorBike()};
        for(Vehicle vehicle : vehicles)
            vehicle.run();
    }
}
//Console
//각각의 하위클래스타입 레퍼런스 = new 하위클래스타입 객체
Bike is running
Car is running
MotorBike is running
//상위클래스타입 레퍼런스 = new 하위클래스타입 객체 (업캐스팅)
Bike is running
Car is running
MotorBike is running
//상위클래스타입 래퍼런스를 배열로 (업캐스팅)
Bike is running
Car is running
MotorBike is running

Vehicle을 상속받은 Bike, Car, MotorBike클래스는 각각 run()메서드를 오버라이딩 하여 자신에 맞게 재 정의 하였습니다.

하위 클래스의 인스턴스는 상위 클래스의 멤버를 모두 받았음으로, 상우 ㅣ클래스로 형변환이 가능합니다. 이것을 업 캐스팅이라고 합니다. 반대로업 캐스팅 된 객체를 다시 하위 클래스로 형변환 하는 것을 다운 캐스팅이라고 합니다.

Bike,Car,MotorBike의 인스턴스를 모두 Vehicle업캐스팅을 하면 Vehicle참조 변수로 가리킬 수 있게 되고 배열로 묶어 관리할 수 있는 편리성은 다형성을 응용한 것입니다.




super, super()

super

this, this()와 역할이 매우 유사합니다. super는 객체의 상위 클래스로 부터 상속받은 멤버를 가리키고 super()는 상위클래스의 생성자를 가리킵니다.

package JAVA_OOP_Extend;

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);
    }
}

public class Super {
    public static void main(String[] args) {
        Lower lower = new Lower();
        lower.callNum();
    }
}
//Console
count = 15
this.count = 15
super.count = 20

Upper클래스로부터 count멤버를 상속 받았고 Lower클래스에서 자체적으로 count멤버를 선언하였습니다. 상속받은 멤버와 선언한 멤버의 이름이 같다면 선언한 멤버 > 상속받은 멤버의 우선순위를 가지므로 변수이름만 사용시 선언한 멤버 즉 this.멤버를 지칭하게 됩니다.(this는 생략됩니다)

상속받은 멤버와 선언한 멤버의 이름이 같을 때 상속받은 멤버를 가리키고 싶다면 super.변수super.를 명시적으로 컴파일러에게 알려 주어야 합니다.



super()

package JAVA_OOP_Extend;

class Human{ //상위 클래스
    Human(){ //super()
        System.out.println("휴먼 클래스 생성자");
    }
}

class Student extends Human{ //하위 클래스
    Student(){ //this()
        super(); //Human() 으로
        System.out.println("학생 클래스 생성자");
    }
}

public class SuperTest {
    public static void main(String[] args) {
        Student s = new Student();
    }
}
//Console'
휴먼 클래스 생성자 //super()
학생 클래스 생성자 //this()

Studnet()생서자에서 출력하기 전 맨 첫 줄에서 Human()생성자를 super()키워드로 호출 합니다. 출력값으로 "휴먼 클래스 생성자" 와 "학생 클래스 생성자"가 순차적으로 출력됩니다.

super()의 조건 2가지

  1. 생성자의 첫 줄에 와야 합니다.
  2. 상위 클래스의 생성자에 맞게 매개변수를 전달해야 합니다.(없다면 기본 생성자)



Object

자바의 모든 객체들의 최상위 부모는 Object 객체입니다. 만약에 extends키워드를 생략 하였다면 자바 컴파일러가 자동으로 extends Object를 추가하여 Object클래스를 상속 받도록 합니다.

class 클래스 { //자동으로 extends Object 추가

}

Ojbect클래스는 모든 클래스들이 사용할 수 있는 몇가지의 메서드들을 제공합니다. 이미 배열과 문자열에서 봐왔던 메서드들도 있습니다.

  • String Object.toString() : 객체를 문자열로 반환
  • boolean Object.equals(Object obj) : 등가 비교 연산(==)
  • int Object.hashCode() : 객체의 위치정보를 기반으로 Hashtable 또는 HashMap에서 동일 객체 여부 판단
  • void Object.wait() : 쓰레드 일시 정지
  • void Object.notify() : 쓰레드 재동작



캡슐화

캡슐화는 객체 안에 속성과 기능들을 묶어 하나의 캡슐로 만들어 보호하는 것을 의미합니다.
캡슐화의 목적

  1. 외부로부터의 데이터 보호
  2. 불필요한 노출을 방지

캡슐화를 함으로써 외부로부터 객체의 속성과 기능을 함부로 수정하지 못하게 막고, 변경되더라도 다른 객체에 영향을 주지 않도록 독립성을 확보 합니다.

캡슐화를 위한 핵심 수단은 접근제어자패키지 입니다.




패키지

패키지란 클래스와 인터페이스의 묶음을 의미합니다. OS에서 사용하는 폴더 개념과 유사하다고 생각하시면 됩니다. 폴더와 마찬가지로 그룹단위로 묶어서 효율적으로 관리하기 위함에 목적이 있습니다.

패키지는 디렉터리(폴더)와 유사한 구조를 가지고 있습니다. 패키지 하나당 하나의 계층구조를 가집니다. 패키지와 패키지, 패키지와 클래스, 패키지와 인터페이스는 .으로 구분합니다. 패키지에 포함된 경우 소스코드 첫번째 줄에 package 패키지로 표시해야 합니다.(없을 시 익명 패키지)

package 패키지1.패키지2; //패키지 구분

패키지의 장점

  1. 디렉토리 계층 구조 구현
  2. 클래스명이 같을 때 패키지명으로 구분함으로써 충돌을 방지


import

import문은 다른 패키지에 존재하는 클래스를 불러와서 사용하기 위해 작성합니다. 원래대로라면 매번 불러올 패키지명도 적어야 하지만 import문으로 컴파일러에게 미리 알려 줌으로써 클래스만 적어도 됩니다.

import 패키지명.클래스명; //패키지의 해당 클래스만 import
import 패키지명.*; //패키지의 모든 클래스를 import

다른 패키지에 존재하는 클래스

package JAVA_OOP_Extend.test1;

public class ExampleImp {
    public int a = 10;
    public void print(){
        System.out.println("Import문 테스트");
    }
}

import문을 사용하지 않고 다른 패키지에서 사용

package JAVA_OOP_Extend.test2;

public class PackageImp { //패키지 Import X
    public static void main(String[] args) {
        //패키지명.클래스로 패키지명을 모두 명시해야 합니다.
        JAVA_OOP_Extend.test1.ExampleImp example = new JAVA_OOP_Extend.test1.ExampleImp();
        example.print();
    }
}
//Console
Import문 테스트

import문을 사용하여 패키지를 생략합니다.

package JAVA_OOP_Extend.test2;

import JAVA_OOP_Extend.test1.ExampleImp;
//import JAVA_OOP_Extend.test1.*; 는 모든 클래스를 불러옵니다.
public class PackageImp2 {
    public static void main(String[] args) {
        //Import문으로 패키지를 불러왔으므로 클래스명만 명시하면 됩니다.
        ExampleImp example = new ExampleImp();
        example.print();
    }
}
//Console
Import문 테스트



제어자(Modifier)

  • 접근 제어자 : public, protected, (default), private
  • 기타 제어자 : static, final, abstarct, native, transient, synchronized 등


접근 제어자

접근 제어자는 캡슐화를 위한 핵심적인 방법입니다. 접근 제어자를 통해서 외부로부터 데이터 노출을 방지하고 임의로 데이터가 변경되지 않도록 막을 수 있습니다.

  • private : 같은 클래스 내에서만 접근 가능합니다.
  • default : 같은 패키지 내에서만 접근 가능합니다.
  • protected : 같은 패키지 안이거나 다른 패키지여도 상속 받은 클래스는 접근 가능합니다.
  • public : 어디에서든 접근 가능합니다.

package 패키지1;

class 클래스1{
	public static void main(String[] args){
		상위클래스 up = new 상위클래스();
		//System.out.println(up.a); //다른 클래스여서 접근 불가!
		System.out.println(up.b); 
		System.out.println(up.c);
		System.out.println(up.d);
	}
}

public class 상위클래스{
	private int a = 1;
	int b= 2;
	protected int c =3;
	public int d =4;
}
package 패키지2;

public class 하위클래스 extends 상위클래스{
	public void 메서드(){
		//System.out.println(a); //다른 클래스여서 접근 불가!
		//System.out.println(b); //다른 패키지여서 접근 불가!
		System.out.println(c);
		System.out.println(d);
	}

class 클래스2{
	public static void main(String[] args){
		하위클래스 low = new 하위클래스();
		//System.out.println(low.a); //다른 클래스여서 접근 불가!
		//System.out.println(low.b); //다른 패키지여서 접근 불가!
		//System.out.println(low.c); //다른 패키지, 상속 아니어서 접근 불가
		System.out.println(low.d);
	}
}

클래스1의 경우 클래스만 다르므로 private는 접근할 수 없습니다.
하위 클래스인 경우 비록 상위클래스를 상속 받았더라도 클래스가 다르므로 private, 패키지가 다르므로 default에 접근할 수 없습니다.
클래스2의 경우 클래스,패키지,상속x이므로 private,default,protected에서 접근이 불가능하고 public만 가능합니다.




getter(), setter()

접근 지정자로 인해 public을 제외한 다른 접근 지정자는 접근이 제한 될 수도 있습니다. 데이터를 은닉하고 보호하는 데는 효과적일 지라도 정작 데이터를 받아오거나 수정해야 할 대 하지 못하고 에러가 발생할 수도 있습니다. 이러한 문제를 해결하기 위해 getter()setter()는 객체의 데이터에 접근할 수 있도록 public접근지정자를 사용하여 객체의 데이터로의 통로 역할을 수행합니다.

  • getter() : 멤버 변수를 단순히 리턴 합니다.
  • setter() : 멤버 변수를 갱신 합니다.
package JAVA_OOP_Extend;

class Person2{
    //private로 선언함으로써 클래스 외부에선 접근이 불가능합니다.
    private String name;
    private int age;
    private int id;

    //public으로 getter()와 setter()를 선언함으로써 해당클래스에 접근 가능합니다.
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

public class GetSet {
    public static void main(String[] args) {
        Person2 p = new Person2(); //인스턴스 생성
        //객체의 멤버는 private여서 접근 불가능하지만 setter()는 public이므로 접근가능
        p.setName("아무사람");
        p.setAge(28);
        p.setId(5);

        //getter()도 public이므로 접근가능합니다.
        String name = p.getName();
        System.out.println("근로자의 이름은 " + name);
        int age = p.getAge();
        System.out.println("근로자의 나이는 " + age);
        int id = p.getId();
        System.out.println("근로자의 ID는 " + id);
    }
}
//Console
근로자의 이름은 아무사람
근로자의 나이는 28
근로자의 ID는 5

IDE에서 조금씩은 다르겠지만 우클릭 - Generate - Getter and Setter를 클릭하면 손쉽게 getter()와 setter()메서드를 구현할 수 있습니다.




후기

객체지향을 구현하기 위해선 고려해야할 것이 많은 것 같습니다. 상속을 받으면 생성자와 멤버 또 접근 제어자와 super등등 많은 것을 함께 고려하여 설계해야 잘 짜여진 객체지향이라 할 수 있습니다. 또 캡슐화를 통하여 보안을 높이기 위해 추가적으로 해주어야 하는 작업도 중요한것 같습니다.




GitHub

https://github.com/ds02168/CodeStates/tree/main/src/JAVA_OOP_Extend

profile
오늘도 내일도 화이팅!

0개의 댓글