2020.10.09 생활코딩 유투브 공부 ( 모든 출처 : 유투버 생활코딩 ) 2 부 : 객체 지향 프로그래밍


시즌 2 


    객체 지향 프로그래밍 ( Object-Oriented Programming ) oop




오리엔테이션 


상태와 행위를 그룹핑해놓은 것

객체 : 상태와 행위로 구분해서 서로 연관되어 있는 상태와 행위를 그룹핑해놓은것 ( categorize) 


객체 : 구체적인 문법적인 기능이 언어차원에서 제공되는 기능 

하나의 객체 안에는 그 객체가 가지고 있는 취지 (댓글인지 본문인지) 에 따라서 기능에 연관되어 있는 변수와 메소드를 객체라는 껍데기에 가둬둔 것이고 서로 연관성이 없는 다른 로직과 구분해주는 역할을 함 : 재활용성 


추상화 ( abstract ) 


추상화는 해결해야하는 문제 반영해야 하는 현실을 소프트웨어적으로 단순화해서 표현 


하나의 프로그램이 여러개의 형태의 로직으로 이루어져있고 그 여러개의 로직안에 관련된 변수와 메소드로 이루어진 객체가 있고 이것을 다른 곳에서 활용(재활용) 할 수 있다. 

객체가 다른 곳에서 여러곳에서 무엇으로 활용되는지 

재활용가능하도록 부품화 하도록 한다. 


컴퓨터 -> 추상화 : 모니터디스플레이, 키보드, cpu메모리등의 본체 

부품들을 그룹핑해서 개념화 -> 추상화 


클래스와 인스턴스 그리고 객체지향 


클래스 class : 설계도 , 정의

인스턴스 instance : 설계도에따라 만들어진 제품, 호출 

객체 object 


중복의 제거가 중요하다.  중복되는 코드는 제거되어야 하는 대상이다. 

하나의 코드를 가지고 중복되는 작업을 할 수 있어야한다. 

건강한 코드 : 중복되는 코드는 없애야 한다. 


중복제거 ---> 메소드를 사용하기  


연관되어 있는 연산, 데이터등을 기능적으로 모아서 -> 객체지향 언어로 시작한 언어가 자바다. 


클래스와 인스턴스 그리고 객체 지향 : 객체화 

left = 10 ; 

right = 20 ;l 

sum ( left, right); 

avg(left, right ) ; 


left = 20 ; 

right = 40 ; 

sum(left, right) ; 

avg(left, right) ; 


두개는 연관되어 있는 두개로 구성되어 있고 그것이 반복되고 있다.  

인스턴스 : 구체적인 객체 

class Calculator {   //설계도 

    int left , right;   //left, right 를 선언 전역변수 

    public void setOprands(int left, int right) { 

        this.left = left;      // 

        this.right = right;      //

밑에 Calculator c1 = new Calculator();  c1 에 담겨있는 Calculator 클래스를 

구체적인 제품으로 만든 인스턴스를 가리키는 것이 바로 this 


}

public void sum(){ 

    System.out.println(this.left+ this.right ) ; 


밑에 보면 c1.setOprands (10, 20 ) 에서 전역변수에다가 (this.left, this.right) 

값을 넣었고 그것을 다시 

c1.sum 을 통해서 메소드를 호출하여 

지금 여기서 전역변수의 애들 ( 10, 20 으로 변경된 ) 을 더해주는것  

}

public void avg() { 

    System.out.println((this.left + this.right ) / 2 ) ; 

    }

}


public class CalculatorDemo4 { 

    public static void main (String[] args ) { 

        Calculator c1 = new Calculator(); //method 라고 하면  앞에 new 는 안붙         어 있지. Calculator() 는 객체이다.  ( 인스턴스화) 

        //Calculator 는 데이터 타입이다. 

        c1.setOprands(10,20); 

        c1.sum(); 

        c1.avg(); 

        Calculator c2 = new Calculator();

        // 여기나오는 Calculator 는 데이터 타입이다. 

        //new Calculator () 는 인스턴스화 -> c2라고 하는 변수에 담김 

         c2.setOprands(20,40); 

          

         c2.sum(); 

         c2.avg(); 

    }        

}

//Calculator c1 = new Calculator(); 라고 할 때 

여기서 Calculator 는 데이터 타입이다. 그것에 대한 설명 : 

프로그램을 메모리에 적재시켜서 프로그램이 작동된다. 

타입을 우리가 직접 만드는 것이라고 할 수 있다. 사용자 정의 데이터타입을 만드는것이라는것

내 이해 : Calculator는 class인데 그 클래스안에 내가 (설계도 안에 ) 변수도 선언하고 메소드도 만들잖아.  그래서 그것은 사용자가 정의한 데이터이고 그래서 c1은 Calculator 라는 데이터타입이라고 하는것 



클래스 맴버 , 인스턴스 맴버 : 맴버란 ? 

객체 : 변수와 메소드의 집합 

클래스 (설계도 )  

인스턴스 1  (제품1) 

인스턴스 2  (제품 2 ) 

.....


10 , 20 과 20 , 40 과같이 인스턴스 안에 들어있는것 :  인스턴스 변수라고 한다. 

인스턴스마다 서로다른 값을 가지고 있기 때문에 

인스턴스의 소유가 아니고 클래스의 소유인 변수가 존재한다. 

클래스의 변수(전역변수) / 인스턴스의 변수 

클래스의 멤버 변수는 모든 인스턴스에서 똑같은 값을 같는다 ( int left, int right ) 

인스턴스에 직접 접근하지 않아도 클래스에 소속되어 있는 변수에 접근해서 값을 사용할 수 있다. 


클래스 맴버, 인스턴스 맴버 : 클래스 변수

class Calculator{ 

    static double PI = 3.14;  // 파이는 모든 객체가 똑같은 값을 가지면 된다. (클래스 변수 : 클래스의 변수이기 때문에 클래스에 따라서 만들어진 모든 인스턴스들은 그 클래스가 가지고 있는 값을 자연스럽게 가지고 있는 것이다. ) 

   static 키워드 : static 뒤에 따라오는 변수는 static 한 변수가 되는 것 -> 이 변수가 class 에     소속이 된다라는 뜻 -> 저 변수를 모든 인스턴스에서 동일한 값으로 가지고 오게 된다. 동     일한 값이 된다. 

    int left, right; 

    public void setOprands(int left, int right) { 

        this.left = left; 

        this.right = right; 

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) ;    //3.14가 출력된다. 

    Calculator c2 = new Calcilator(); 

    System.out.println(c2.PI);     //3.14 가 출력된다. 

    

    System.out.pritnln(Calculator.PI) ;  //인스턴스의 설계도라고 할 수 있는 Calculator 라는                                                       클래스에서 직접 접근도 가능하다. 

 } 



또하나의 예제 ) 

class Calculator2 { 

    static double PI = 3.14 ; 

    static int base = 0 ; //static 이기 때문에 base 는 Calculator 2 의 변수 ( 인스턴스의 변수가 아님 ) 

    int left, right ;   -> static 이 붙어 있지 않기 때문에 이것은 인스턴스에서만 유효한 변수가   된다. 

    

    public void setOprands ( int left , int right) { 

        this.left = left; 

        this.right = right; 

    public void sum () {

        System.out.println(this.left + this.right + base ) ; 

}

    public void avg () {

    



public class CalculatorDemo2 

    public static void main (String[] args) { 

        Calculator2 c1 = new Calculator2(); 

        c1.setOprands(10, 20 ) ;

       //30출력 

        c1.sum(); 

    

    Calculator2 c2 = new Calculator2(); 

    c2.setIOprands(20, 40 ) ; 

    

    *Calculator2.base = 10 ;

     --> 클래스 변수 base 의 값을 10으로 지정했다. 


    c1.sum(); 40 출력


    c2.sum(); 70출력 


클래스 맴버, 인스턴스 맴버 : 클래스 메소드 


*컨트롤 : 롤오버를 하면 링크가 생겨서 원하는 곳으로 갈 수 있다. 이전으로 가려면 위에 있는 옐로 화살표를 쓰면됨.


클래스 맴버, 인스턴스 맴버 : 타입별 비교 


1.인스턴스 메소드는 클래스 맴버에 접근 할 수 있다. 

//인스턴스 메소드 : 인스턴스에 속해있는 메소드 static 이라는 것이 붙어 있지 않은 

2. 클래스 메소드는 인스턴스 멤버에 접근 할 수 없다. 

// 클래스라고 하는 것은 언제나 메소드보다 먼저 존재하고 있다. 

클래스 설계도 -> 구체적인 제품인 인스턴스를 나중에 만든다. 

클래스 메소드는 클래스를 기반으로 만들어진 인스턴스에 접근하는 것이기 때문에 아직 생성되지 않은 인스턴스에 접근하는 것은 불가능하겠지? 

(논리적으로 생각하면 되는 부분 ) 

클래스를 만들때 아직 만들어지지 않은 인스턴스에 접근할 수는 없겠지. 

*static 이 붙어 있어 -> 클래스변수 

class C1 { 

    static int static_variable = 1;  //static 이 붙었으니까 클래스변수

    int instance_variable = 2;    // 인스턴스 변수 

    static void static_static() {        //클래스 메소드가 클래스 변수(static_variable)에 접근

        System.out.println(static_variable) ; 

    } 

    static void static_instance () { 

    //클래스 메소드에서는 인스턴스 변수에 접근 할 수 없다. 

    // System.out.println(instance_variable);

    void instance_static(){ 

    System.out.println(static_variable); 

    }

    void instance_instance() { 

    System.out.println(instance_variable) 

    }

}

public class  ClassMemberDemo { 

    public static void main(String[] args ) { 

        C1 c = new C1(); 

        //인스턴스를 이용하여 정적 메소드에 접근 -> 성공

        //인스턴스 메소드가 정적 변수에 접근 -> 성공 

        c.static_static(); 

        //인스턴스를 이용하여 정적 메소드에 접근 -> 성공

       //정적 메소드가 인스턴스 변수에 접근 -> 실패 

        c.static_instance();

        //인스턴스를 이용하여 인스턴스 메소드에 접근 -> 성공

        //인스턴스 메소드가 클래스 변수에 접근 -> 성공 

        C1.static_static(); 

        //클래스를 이용하여 클래스 메소드에 접근 -> 성공

        //클래스 메소드가 인스턴스 변수에 접근 -> 실패 

        C1.static_instance(); 

        //클래스를 이용해서 인스턴스 메소드에 접근 -> 실패 

        //C1.instance_static(); 

        // 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패

        //C1.instance_instance(); 


* 인스턴스 변수 -> Non-Static Field 

  클래스 변수 -> Static Field 


클래스[인스턴스][클래스메소드][인스턴스메소드] -> 메소드 ->     변수로 접근한다고하면

클래스에서 -> 인스턴스는 안됨. 



유효범위 ( 1/4) 유효범위란 ? scope 

이름중복문제 해결위해 나옴 


public class ScopeDemo { 


    static void a () { 

        int i = 0 ; //지역변수 

}

//i라는 변수는 a라는 메소드 안에서 선언되고 있기에 i 변수는 a 메소드 바깥쪽에 영향을 미칠 수 없다 

public static void main(String[] a 

    for(int i = 0 ; i < 5 ; i++ ) { 

        a();

        System.out.println(i); 

        }

    }

}

//따라서 메인에서 a 메소드 바깥에서 for문의 i에 영향을 미치지 않는다. 



유효범위 (2/4) - 전역변수, 지역변수 

디렉터리 바깥쪽에서 사용하면 이름의 충돌현상을 완화시킬 수 있다. 

public class ScopeDemo2 { 

    static int i ; 

// i 는 클래스에 직접적으로 속해있는 직속변수 : 전역변수 

    static void a(){ 

        i = 0 ; 

    public static void main ( String[] 

        for( i = 0 ; i < 5 ; i++) { 

            a();

            System.out.println(i); 

            }

        }

}

consol : 계속 0 만 나오고 영원히 반복된다. 

a(){} 메소드 안에 i를 int i 로 바꿔주거나 

메인메소드 안에 있는 for 문에 있는 i를 0 으로 바꿔주면 됨 

범위가 전혀 달라짐 

*중괄호 안에서만 유효한 지역변수 


유효범위 ( 3/4) : 다양한 유효범위 


public class ScopeDemo4 { 

    static void a () { 

        String title = "coding evertbody"; 

}

    public static void main (String[] args) { 

        a(); 

        System.out.println(title); 

title 은 컴파일에러가 발생되어 있다. 

title 은 a에 들어있고 main 입장에서는 존재하지 않는것. 


또다른 예 


public class ScopeDemo5 { 

        public static void man(String[] args) {

            for(int i = 0 ; i < 5 ; i++ ) { 

                System.out.println(i);

            }

            System.out.pritnln(i);  //for문의 바깥에 있는거 .. 

       }

여기서도 컴파일 에러가 발생할 수 있다. 


static void a () { 

    int i = 10 ; 

    b(); 

}

static void b () { 

    System.out.pritnln(i);  //얘네는 메소드 b를 호출한 메소드 a 나 메인에 있는 i를 사용하는 것이 아님 

}

public static void main(String[] args) { 

    int i = 1; 

    a(); 

 


* 정적인 유효범위 : lexical ( static ) scope )  ( 자바로 추정 ㅋㅋ ) 

   동적인 유효범위 : dynamic scope  


유효범위 ( 4/4 ) : this 


class C { 

    int v = 10;

    void m() { 

        int v = 20; 

        System.out.println(v);

        System.out.println(this.v); //전역변수를 프린트하고 싶으면 this 붙이기

}

}


main ~~ 


객체지향프로그래밍에서는 전역변수사용을 금기시하고 있다. 


생성자 

객체에서 초기화를 할 수 있도록 하는 것 


class Calculator { 

    int left , right; 

    public Calculator(int left , int right) { 

        this.left = left;                             //클래스의 생성자 ( 만약 없으면 자동으로 생성                                                                                       해줌)

        this.right = right; 

    }

    public void sum() { 

        System.out.println(


Calculator c1 = new Calculator(10, 20) ;   클래스가 아니라 생성자.  

c1.sum();

c1.avg(); 


앞에서는 생성자가 없었는데 생성자를 만들어서 객체를 만들때 객체가 반드시 해야하는 작업이 있다면 그것을 생성자에 만들어줘서 그 절차를 줄일 수 있도록 또는 반드시 해야하는 작업을 놓치지 않을 수 있도록 만들어준것 클래스와 같은 이름이네 



상속


기존의 객체를 그대로 유지하면서 어떤 기능을 추가하는 방법이 없을까? 

이런 맥락에서 등장하는 것이 상속 


부모클래스 = super 클래스 

하위클래스 = sub 클래스 



class Calculator (부모) { 

    int left, right ; 

    public void setOprands(int left, int right) { 

        this.left = left; 

        this.right = right; 


public void sum() { 

        System.out.pritnln(this.left + this.right) ; 

public void avg() { 

        System.out.println(this.left + this.right) / 2 ) ; 

    }

}

class substractionalableCalculator (자녀) extends Calculator { 

    public void substract()

        System.out.println(this.left - this.right) ; 

    }

}


public class CalculatorDemo1 { 

    public static voud main(String[] args) { 

        SubstractionalableCalculator c1 = new  substractionalableCalculator() ; 

        c1.setOprands(10, 20) ; 

        c1.sum(); 

        c1.avg();

        c1.substract(); 

    }  




다양한 종류의 상속 


상속을 여러개 할 수 있고

상속이 된 자녀에 또 자녀를 만들 수 있다 

코드의 중복을 없애준다. 


상속과 생성자 

생성자는 객체를 생성한다. 초기화 작업을 할 수 있다. 

package ~~ 

public class ConstructorDemo { 

    public static void main(String[] args) { 

        constructorDemo c = new ConstructorDemo();

    }

}

여기서는 오류가 생기지 않는다. ConstructorDemo 객체를 생성할 때 생성자를 생성하지 않으면 자바에서 자동으로 매개변수가 없는 기본 생성자(클래스와 이름은 같으면서 메서드를 가지고 있지 않은 ) 를 만들어주기 떄문에 c라는 변수로 객체생성을 해줄 수 있다.


package ~~~ 

public class ConstructorDemo { 

    pubic ConstructorDemo(int param1 ) {} // 생성자를 생성했는데 파라미터가 존재 (기본생성자가 아닌 생성자 )

    public static void main(String[] args) { 

        ConstructorDemo c = new ConstructorDemo(); 여기에 에러가 발생한다. 인자가 없어          ConstructorDemo 는 인자가 없는 생성자를 가지고 있지 않기 때문에 (그리고 자동으로 기본생성자가 만들어 진 것이 아니기 때문에 ) 

     }

}


상속과 생성자 : super 


class Calculator { 

    int left, right ; 


    public Calculator(int left, int right ) { 

        this.left = left; 

        this.right = right; 

}



class SubstractionableCalculator extends Calculator { 

    public SubstractionableCalculator(int left, int right ) { 

        this.left = left; 

        this.right = right;

    }

    public void substract() { 

        System.out.println(this.left - this.right ) ; 

    }

}

public class CalculatorConstructorDemo5 { 

    public static void main(String[] args) { 

        SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20 ) ; 

        // SubstractionableCalculator 객체생성을 하면 그 아이는 Calculator 를 상속받은 것이        기 때문에 자기 클래스를 호출하기 전에  부모클래스의 생성자를 자동으로 먼저 호출하도록 자바는 만들어져있다.  부모 생성자 Calculator는 기본생성자가 아니라 파라미터를 가지고 있다.  -> 하위클래스가 상속을 받을때 상위클래스의 생성자를 호출해줘야 하는데 기본생성자가 아니기때문에 에러가 난다. ---> Calculator 라는 클래스에 기본생성자를 정의해주면 된다는 것 

        Substration

        c1.sum()

        c1.avg();

        c1.substract() ; 



--> class Calculator { 

    int left, right; 

    public calculator () {} //이렇게 부모클래스에 기본생성자를 생성해줘야 한다.


    public Calculator(int left, int right) { 

        this.left = left; 

        this.right = right;

    }


부모클래스가 만약 매개변수가 있는 생성자를 생성하면 기본생성자가 없으니까 하위생성자는 상위생성자를 호출할 수 없기때문에 ->  부모클래스에 기본생성자를 만들어준다.

-> 기본생성자를 만들지 않고도 객체가 동작하도록 할 수 있다. 




: 상위클래스 생성자 하위클래스 생성자가 동일하다면 

하위클래스에서 생성자를 만들지 말고 상위클래스에서 복사해서 사용하기 : super


class SubstractionableCalculator extends Calculator { 

    public SubstractionableCalculator(int left, int right ) { 

        super(left, right) ;   super (부모클래스의 생성자)

SubstractionableCalculator의 매개변수 left와 right 는 super(부모클래스의 ) left, right 에 들어가게 되는것 

    } 

 ......................


정리 : 하위클래스가 상위클래스를 참조할 수 있는 키워드 super 

하위클래스가 부모클래스의 초기화를 먼저하고 자기자신의 초기화를 해야한다면 

super () 밑에 써준다. : 상속받은 자식클래스만이 해야하는 초기화과정을 해줄 수 있다. 

super () 밑에써줘야한다. 항상 하위클래스의 초기화클래스는 super 를 호출한다음에 써줘야한다. 상위클래스초기화먼저 (super) -> 하위클래스의 초기화 그 다음 































댓글