sm 기술 블로그

객체지향 (API /접근제어자 /abstract /final /static /interface /다형성) 본문

Java

객체지향 (API /접근제어자 /abstract /final /static /interface /다형성)

sm_hope 2022. 4. 17. 21:49
API (Application Programming Interface)
자바 시스템을 제어하기 위해 자바에서 제공하는 명령어들이다.

자바는 기본적으로 import 하는 패키지가 있다. 그래서

package org.opentutorials.javatutorials.library;
public class LibraryDemo1 {
    public static void main(String[] args) {
        System.out.println(1);
    }
}
package org.opentutorials.javatutorials.library;
import java.lang.*; //API
public class LibraryDemo1 {
    public static void main(String[] args) {
        System.out.println(1);
    }
}

위아래 코드는 같은 코드가 된다.

 

API와 UI(User Interface)

UI : 사람과 컴퓨터 사이의 중계자 [사용자를 대면하는 접점] -> 홈페이지의 버튼도 UI

API : 무엇인가를 제어하기 위한 명령어

 

일반적인 사용자들은 UI를 사용해서 시스템을 제어한다면, 개발자들은 API를 통해서 시스템을 제어한다.

 

ㅈ버튼이나 경고창은 UI, URL부분에 alter 명령어는 API이다.

 

프로그램을 만든다는것은 API를 조작하여 만드는 것뿐이다.

 

API문서

API에 대한 정보를 얻고 싶다면 문서를 읽는다. 그 방법은 동영상으로 확인하자.

https://www.youtube.com/watch?v=E2uyQlgdB34&list=PLuHgQVnccGMCeAy-2-llhw3nWoQKUvQck&index=102 


접근제어자

추상 클래스, final, 접근 제어자, 인터페이스 등은 규제에 해당된다.

다양한 규제는 변화를 수용하기 위해서는 필요한 것이다.

하지만 규제는 도구 설계자의 취향이나, 도구의 목적에 따라서 채택되기도, 배제되기도 한다.

 

이러한 규제는 프로그래밍을 문제없이 자유롭게 하기 위한 수단이다.

 

class A {
    public String y(){
        return "public void y()";
    }
    private String z(){
        return "public void z()";
    }
    public String x(){
        return z();
    }
}
public class AccessDemo1 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.y());
        // 아래 코드는 오류가 발생한다.
        //System.out.println(a.z());
        System.out.println(a.x());
    }
}

public 은 누구든지 그 클래스에 메소드를 호출해서 사용할 수 있다.

private는 클래스 내부에서는 사용이 가능하지만 외부의 클래스에서는 사용할 수 없다.

 

즉 System.out.println(a.x()); 는 x()를 호출하는 것으로 z()를 호출할 수 있는 것이다. 

 

접근제어자 사용 이유

사용자가 입력하는 것을 통제(private)하여 고유의 기능을 보호하고, 의도된 대로 사용할 수 있게 한다.

사용자는 public 부분만 신경 쓰면 되므로 더 효율적으로 프로그래밍을 할 수 있다.

 

세밀한 제어

  public protected default private
같은 패키지, 같은 클래스 허용 허용 허용 허용
같은 패키지, 상속 관계 허용 허용 허용 불허
같은 패키지, 상속 관계 아님 허용 허용 허용 불허
다른 패키지, 상속 관계 허용 허용 불허 불허
다른 패키지, 상속 관계 아님 허용 불허 불허 불허

 

클래스의 접근제어자

클래스는 default와 public 만 가질 수 있다.

package org.opentutorials.javatutorials.accessmodifier.inner;
public class PublicClass {}
package org.opentutorials.javatutorials.accessmodifier.inner;
class DefaultClass {}

다음과 같이 패키지에 클래스가 정의되어 있다.

1번은 public으로 2번은 default로 정의되어 있다.

 

(1) 동일한 패키지

package org.opentutorials.javatutorials.accessmodifier.inner;
public class ClassAccessModifierInnerPackage {
    PublicClass publicClass = new PublicClass();
    DefaultClass defaultClass = new DefaultClass();
}

(2) 다른 패키지, inner을 로드함

package org.opentutorials.javatutorials.accessmodifier.outter;
import org.opentutorials.javatutorials.accessmodifier.inner.*;
public class ClassAccessModifierOuterPackage {
    PublicClass publicClass = new PublicClass();
    //DefaultClass defaultClass = new DefaultClass();
}

default는 같은 패키지에 있는 클래스만을 사용 가능하다.

따라서 2번에서 default를 사용할 때 오류가 발생한다.

 

※ public으로 클래스를 선언할 때, 클래스 이름과 와 소스코드 이름은 반드시 일치해야 한다.

즉, 하나의 소스코드 파일 안에 하나의 public 클래스만을 가질 수 있다.

[ public class PublicNameDemo {} , PublicNameDemo.java ]

 


 abstract
상속 없이는 사용이 불가능한 규제 (상속을 강제함)
abstract class A{
    public abstract int b();
    //본체가 있는 메소드는 abstract 키워드를 가질 수 없다.
    //public abstract int c(){System.out.println("Hello")}
    //추상 클래스 내에는 추상 메소드가 아닌 메소드가 존재 할 수 있다. 
    public void d(){
        System.out.println("world");
    }
}
public class AbstractDemo {
    public static void main(String[] args) {
        A obj = new A();
    }
}

A를 obj로 인스턴스화 하려 하지만 오류가 발생할 것 -> 상속없이 사용했기 때문

package org.opentutorials.javatutorials.abstractclass.example2;
abstract class A{
    public abstract int b();
    public void d(){
        System.out.println("world");
    }
}
class B extends A{
    public int b(){return 1;}
}
public class AbstractDemo {
    public static void main(String[] args) {
        B obj = new B();
        System.out.println(obj.b());
    }
}

추상 (abstract) 메소드는 {}의 본체를 가질 수 없다. 

 

기본적으로 상속을 받았더라도 추상 클래스 내에 추상 메소드를 오버라이딩해야 한다.

[오버라이딩 : 부모 클래스의 메소드를 상속받아 재정의 한 것]

 

※ 만약 추상 메소드가 하나라도 있다면, 추상 클래스여야 한다.

 

abstact 사용 이유

추상 클래스는 기본적으로 메소드의 시그니처만 정의하고 실제 동작은 상속받은 하위 클래스의 책임으로 위임.

package org.optutorials;

abstract class Calculator{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    } 
    
    int _sum(){
    	return this.left + this.right;
    } //default 접근제어자 (같은 패키지 내에서만 사용 가능)
    
    public abstract void sum();  
    public abstract void avg();
    public void run(){
        sum();
        avg();
        
        // run()은 실제로 Calculator의 메소드를 사용하는 것이 아니라 자식클래스에 있는
        // sum과 avg가 구현된다.
    }
}
class CalculatorDecoPlus extends Calculator {
    public void sum(){
        System.out.println("+ sum :"+_sum());
    }
    public void avg(){
        System.out.println("+ avg :"_sum()/2);
    }
} 
class CalculatorDecoMinus extends Calculator {
    public void sum(){
        System.out.println("- sum :"+_sum());
    }
    public void avg(){
        System.out.println("- avg :"_sum()/2);
    }
} 
public class CalculatorDemo {
    public static void main(String[] args) { 
        CalculatorDecoPlus c1 = new CalculatorDecoPlus();
        c1.setOprands(10, 20);
        c1.run();
         
        CalculatorDecoMinus c2 = new CalculatorDecoMinus();
        c2.setOprands(10, 20);
        c2.run();
    }
   
}
// 출력결과
+ sum :30
+ avg :15
- sum :30
- avg :15

 

※ 추상 클래스를 사용할 때, 패키지를 해주지 않으면 오류가 발생한다. 

-> 추상 클래스는  상속을 통해서 이루어진다. 상속이 이루어지면 다수의 클래스가 생긴다. 이들을 연결해주기 위해 패키지가 필요하다.

 

디자인 패턴

 

프로그래밍을 조금 더 쉽게 설계하는 전형적인 해결 방식 또는 예제 

 

정보처리기사에서 다루는 내용 (세부적인 사항은 아래 홈페이지를 보자)

https://velog.io/@programmer930323/%EC%A0%95%EB%B3%B4%EC%B2%98%EB%A6%AC%EA%B8%B0%EC%82%AC%ED%95%84%EA%B8%B01%EA%B3%BC%EB%AA%A9%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4

 

정보처리기사필기_1과목[디자인패턴]

🟢 디자인패턴(Design Pattern) 👉디자인패턴 이란? 💡 각 모듈의 세분화된 역할 or 모듈들 간 인터페이스와 같은 코드를 작성하는 수준의 세부적인 구현 방안을 설계할 때 참조할 수 있는 전형적

velog.io


final
상속이나 변경을 금지하는 규제
class Calculator {
    static final double PI = 3.14;
    int left, right;
 
    public void setOprands(int left, int right) {
        this.left = left;
        this.right = right;
        //Calculator.PI = 6;
    }
 
    public void sum() {
        System.out.println(this.left + this.right);
    }
 
    public void avg() {
        System.out.println((this.left + this.right) / 2);
    }
}
 
public class CalculatorDemo1 {
 
    public static void main(String[] args) {
 
        Calculator c1 = new Calculator();
        System.out.println(c1.PI);
        //Calculator.PI = 10;
 
 
    }
 
}

fianl로 PI값을 확정했기 때문에 클래스 내에서든, 밖에서든 변경을 할 수 없다.

+ 클래스와 메소드도 final로 정의가 가능하다. 그렇게 되면 상속이나 변경이 불가능하다.

 


static
객체를 만들지 않고 바로 사용 가능하도록 만들어 준다.

 

기본적으로는 인스턴스화 하고 사용을 하지만 static은 이러한 과정 없이 바로 사용 가능하게 해 준다.

 

static의 자세한 설명은 아래를 참고하자.

https://blog.naver.com/imm7745/222567633642

 

[JAVA]자바제어자_static(정적)

static -객체 생성없이 바로 사용가능하도록 만들어 준다. -즉 보통의 경우 객체를 만들고 객체.필드 또는 ...

blog.naver.com


인터페이스(Interface)
어떤 클래스가 있고 그 클래스가 특정한 인터페이스를 사용한다면 그 클래스는 반드시 인터페이스의 메소드를 구현해야 한다.

 

interface I{
    public void z();
}
 
class A implements I{
	// 클래스 A는 I라는 인터페이스를 구현해야한다.

    public void z(){}
}

클래스 A에서 인터페이스 I에 존재하는 메소드를 구현해야 한다.

 

여러 개발자가 공동으로 개발 작업을 시행할 때, 동일한 메소드를 만들도록 하여 상대에 일정이나 구현하는 방식에 영향을 덜 수 있도록 하기위해 사용됨.

 

마지막에는 interface 메소드를 호출 함으로써 작업을 마친다.

 

 

인터페이스의 규칙

1) 하나의 클래스가 여러 개의 인터페이스를 구현할 수 있다.

interface I1{
    public void x();
}
 
interface I2{
    public void z();
}
 
class A implements I1, I2{
    public void x(){}
    public void z(){}   
}

2) 인터페이스도 상속이 된다.

interface I3{
    public void x();
}
 
interface I4 extends I3{
    public void z();
}
 
class B implements I4{
    public void x(){}
    public void z(){}   
}

3) 인터페이스의 멤버는 반드시 public이다.

interface I5{
    private void x();
}

abstract는 일반적인 클래스와 다름없다. -> 상속이 꼭 필요하다는 것 외에는..

interface는 인터페이스는 반드시 본체가 없는 메소드를 가지고 있어야 한다.

 


다형성
하나의 메소드나 클래스가 있을 때 이것들이 다양한 방법으로 동작하는 것을 의미한다.

오버로딩이 다형성의 가장 큰 예제이다

[오버로딩 : 이름은 같지만 다른 매개변수를 가진 메소드]

 

class A{
    public String x(){return "x";}
}
class B extends A{
    public String y(){return "y";}
}
public class PolymorphismDemo1 {
    public static void main(String[] args) {
        A obj = new B();
        obj.x();  
        obj.y();  // 오류발생
    }
}

클래스 B로 obj를 인스턴스화 시키는데, 객체가 담겨있는 데이터 타입은 A이다.

클래스 B는 A를 상속받고 있다. 

 

B에는 y()가, 그 상위 클래스의 A에는 x()가 정의되어 있다. 

 

※obj는 실질적으로 B를 인스턴스화 하고 있지만 A 행세를 하고 있다. 따라서 obj.x()는 잘 실행이 된다.

그러나 A로 데이터 타입을 했기 때문에 자바는 y()가 없다고 판단하고 오류가 발생하게 된다.

 

class A{
    public String x(){return "A.x";}
}
class B extends A{
    public String x(){return "B.x";}
    public String y(){return "y";}
}
public class PolymorphismDemo1 {
    public static void main(String[] args) {
        A obj = new B();
        System.out.println(obj.x());
    }
}

그러면 이러한 상황에 출력 결과는 어떻게 될까?....... B.x

 

 

메인에서 obj.x()는 클래스 B에 담겨 있는 x()를 출력할 것이다.

클래스 B를 인스턴스화 시킨 obj를 생성하고 클래스 A의 행세를 하고 있지만, 클래스 B에서 정의되어 있는 것을 실행한다.

 

하지만 obj는 A 행새를 하고 있기 때문에 클래스 A에 정의되어 있지 않은 y()를 실행한다면 오류가 발생한다.

 

class A{
    public String x(){return "A.x";}
}
class B extends A{
    public String x(){return "B.x";}
    public String y(){return "y";}
}
class B2 extends A{
    public String x(){return "B2.x";}
}
public class PolymorphismDemo1 {
    public static void main(String[] args) {
        A obj = new B();
        A obj2 = new B2();
        System.out.println(obj.x());
        System.out.println(obj2.x());
    }
}
// 출력결과
B.x
B2.x

 

다형성의 실제 예제

package org.optutorials;

abstract class Calculator{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    } 
    int _sum() {
        return this.left + this.right;
    }
    public abstract void sum();  
    public abstract void avg();
    public void run(){
        sum();
        avg();
    }
}
class CalculatorDecoPlus extends Calculator {
    public void sum(){
        System.out.println("+ sum :"+_sum());
    }
    public void avg(){
        System.out.println("+ avg :"+(this.left+this.right)/2);
    }
} 
class CalculatorDecoMinus extends Calculator {
    public void sum(){
        System.out.println("- sum :"+_sum());
    }
    public void avg(){
        System.out.println("- avg :"+(this.left+this.right)/2);
    }
} 
public class CalculatorDemo {
    public static void execute(Calculator cal){
        System.out.println("실행결과");
        cal.run();
    }
    public static void main(String[] args) { 
        Calculator c1 = new CalculatorDecoPlus();
        c1.setOprands(10, 20);
         
        Calculator c2 = new CalculatorDecoMinus();
        c2.setOprands(10, 20);
         
        execute(c1);
        execute(c2);
    }
}

다형성으로 인해 

다형성을 사용하지 않음 (매개변수를 Plus만 다룸) - Minus는 에러발생

 

에러는 없지만, 중복 발생

다음과 같은 문제를 해결할 수 있다.

 

 

인터페이스와 다형성

interface I{}
class C implements I{}
public class PolymorphismDemo2 {
    public static void main(String[] args) {
        I obj = new C();
    }
}

[어떤 클래스가 인터페이스를 구현하고 있을 때]

 

c라고 하는 클래스를 obj에 인스턴스화 하는데 데이터 타입은 obj이다.

 

예시 1

interface I2{
    public String A();
}
interface I3{
    public String B();
}
class D implements I2, I3{
    public String A(){
        return "A";
    }
    public String B(){
        return "B";
    }
}
public class PolymorphismDemo3 {
    public static void main(String[] args) {
        D obj = new D();
        I2 objI2 = new D();
        I3 objI3 = new D();
         
        obj.A();
        obj.B();
         
        objI2.A();
        //objI2.B();
         
        //objI3.A();
        objI3.B();
    }
}

[좋은 말]

다형성은 그 기능만을 집중시키게 한다.

-> 우리에게 스마트폰은 정말 스마트폰이지만 전화 기능만 생각하는 어르신은 스마트폰이 전화 기능을 하는 전화기이고 어르신이 하고자 하는 기능인 전화를 문제없이 사용할 수 있다.

 

예시 2 

interface father{}
interface mother{}
interface programmer{
    public void coding();
}
interface believer{}
class Steve implements father, programmer, believer{
    public void coding(){
        System.out.println("fast");
    }
}
class Rachel implements mother, programmer{
    public void coding(){
        System.out.println("elegance");
    }
}
public class Workspace{
    public static void main(String[] args){
        programmer employee1 = new Steve();
        programmer employee2 = new Rachel();
         
        employee1.coding();
        employee2.coding();
    }
}

출력 결과는 fast와 elegance가 출력될 것이다.

Comments