[즐거운 자바 강좌] 정리

[즐거운 자바 강의] 섹션4 - 객체지향 문법 (3)

haeyoon 2024. 3. 31. 00:36

1. 생성자

  - 메소드와 비슷

  - return type이 없다. 클래스 이름과 같아야한다.

 

① 기본생성자

   매개변수가 0개인 생성자

public class Car {
    public Car(){
        System.out.println("자동차가 한대 생성됩니다.");
    }
}

 클래스 작성시 생성자를 하나도 만들지 않았다면 기본생성자가 생성된다

public class Car2Exam02 {
    public static void main(String[] args) {
        Car c1 = new Car();
        System.out.println(c1);
    }
}

 

② 이름을 가지고 인스턴스를 만들어지게 하고 싶을때

public abstract class Car2 {
    //생성자는 return type이 없어야함
    public Car2(String name){
        super();
        System.out.println("Car2() 생성자 호출");
    }
}

public class Bus2 extends Car2 {
    public Bus2(){
        super("Bus!"); //부모의 기본 생성자를 호출하는 코드가 자동으로 삽입
        // 이 코드가 자동으로 들어간다
        System.out.println("Bus2 기본 생성자");
    }
}

 


2. 추상 클래스

  1) 추상클래스란?

      = 미완성된 클래스

 

[예제] 추상클래스인 Car2와 Bus2를 작성하자

[코드1]

public abstract class Car2 {
    //생성자는 return type이 없어야함
    public Car2(String name){
        super();
        System.out.println("Car2() 생성자 호출");
    }
    //추상메소드 car2를 만든 사람은 run()이라는 메소드가 필요하다고 생각을 했다
    //run()은 자동차마다 다르게 구현할 것 같다
    public abstract void run();
}
public class Bus2 extends Car2 {
    public Bus2(){
        super("Bus!"); //부모의 기본 생성자를 호출하는 코드가 자동으로 삽입
        // 이 코드가 자동으로 들어간다
        System.out.println("Bus2 기본 생성자");
    }

    //부모가 가지고 있는 추상메소드는 자식에서 반드시 구현을 해줘야한다
    @Override
    public void run() {
        System.out.println("후륜구동으로 동작한다.");
    }
}
public class Car2Exam02 {
    public static void main(String[] args) {
        //Car2 c = new Car2("urstory"); //car2가 추상class라서 오류
        Bus2 b = new Bus2();
        b.run();
    }
}

 

[코드2]

public class Car2Exam02 {
    public static void main(String[] args) {
        //Car2 c = new Car2("urstory"); //car2가 추상class라서 오류
        Bus2 b = new Bus2();
        b.run();

        SportsCar s1 = new SportsCar("SportsCar!!");
        s1.run();
    }
}
public class Bus2 extends Car2 {
    public Bus2(){
        super("Bus!"); //부모의 기본 생성자를 호출하는 코드가 자동으로 삽입
        // 이 코드가 자동으로 들어간다
        System.out.println("Bus2 기본 생성자");
    }

    //부모가 가지고 있는 추상메소드는 자식에서 반드시 구현을 해줘야한다
    @Override
    public void run() {
        System.out.println("후륜구동으로 동작한다.");
    }
}
public class SportsCar extends Car2{
    //부모가 기본생성자가 없기때문에 반드시 super()을 호출한다.
    public SportsCar(String name) { //car2가 기본생성자가 없기때문에 에러
        super(name); // 생성자를 만들어주고 부모의 생성자에 값 전달해줘야함
    }

    @Override
    public void run() { //car2가 추상생성자라 오류
        System.out.println("사륜구동");
    }
}

 

[코드3]

public class Car2Exam02 {
    public static void main(String[] args) {
        //Car2 c = new Car2("urstory"); //car2가 추상class라서 오류
        Bus2 b = new Bus2();
        b.run();

        SportsCar s1 = new SportsCar("SportsCar!!");
        s1.run();

        Car2 c = new Bus2(); // car2의 자손만가능
        c.run(); // -->후륜구동으로동작한다
        //Car2 c = new SportsCar(); // car2의 자손만가능
        //c.run(); // --> 사륜구동으로동작한다

        Car2[] array = new Car2[2]; // car2를 2개 참조할 수 있는 배열을 선언
        array[0]= new Bus2();
        array[1] = new SportsCar("SportsCar!");
        for (Car2 c2 : array){
            c2.run();
        }
    }
}

 

2) 템플릿 메소드 패턴(Template Method Pattern)

템플릿 메소드: 정해진 순서에 따라 실행되는 메소드

[코드1]

package com.example.fw;/*
controller의 종류가 여러개
초기화 - 항상 같은코드
실행 - 이거만 다른코드가 실행
마무리 - 항상 같은코드
 */

public abstract class Controller {
    public void init(){
        System.out.println("초기화하는 코드");
    }
    public void close(){
        System.out.println("마무리하는 코드");
    }
    public abstract void run(); //메번 다른 코드
    
    //내가 가지고 있는 메소드를 호출한다.
    //어떤 순서를 가지고 있다. 이런 메소드를 템플릿 메소드라고 한다.
    public void execute(){
        this.init();//초기화
        this.run();//실행
        this.close();//마무리
    }
}

 

 

오류 발생!

추상클래스 상속받을때는 항상 오버라이드 필수!

package com.example.myproject;

import com.example.fw.Controller;

public class FirstController extends Controller{
    @Override
    public void run() {
        System.out.println("별도로 동작하는 코드 111111");
    }
}
package com.example.main;

import com.example.fw.Controller;
import com.example.myproject.FirstController;

public class ControllerMain {
    public static void main(String[] args) {
        Controller c1 = new FirstController();
        c1.execute();
    }
}

→ 출력

execute만 호출했는데 세개 모두 출력

 

+

[주의사항1]

c1.init(); 

하면 init만 출력 → .execute()만 호출해야함!

 

[해결방법]

public이 아닌 protected로 코드 수정

package com.example.fw;

public abstract class Controller {
    //protected는 같은 package이거나 상속받았을 때 사용 가능
    protected void init(){
        System.out.println("초기화하는 코드");
    }
    protected void close(){
        System.out.println("마무리하는 코드");
    }
    protected abstract void run(); //메번 다른 코드

    //내가 가지고 있는 메소드를 호출한다.
    //어떤 순서를 가지고 있다. 이런 메소드를 템플릿 메소드라고 한다.
    public void execute(){
        this.init();//초기화
        this.run();//실행
        this.close();//마무리
    }
}

execute()만 호출되게 된다

[주의사항2]

init을 오버라이드

package com.example.myproject;

import com.example.fw.Controller;

public class FirstController extends Controller{
    @Override
    public void run() {
        System.out.println("별도로 동작하는 코드 111111");
    }

    @Override
    protected void init() {
        System.out.println("내맘대로 init");
    }
}

[해결방법]

abstract의 반대인 final 사용 → 오버라이드 불가

package com.example.fw;

public abstract class Controller {
    protected final void init(){
        System.out.println("초기화하는 코드");
    }
    protected final void close(){
        System.out.println("마무리하는 코드");
    }
    protected abstract void run(); //메번 다른 코드
    
    public void execute(){
        this.init();//초기화
        this.run();//실행
        this.close();//마무리
    }
}

 

final 클래스

= 부모가 될 수 없는 클래스 

 - 대표 예시) String 클래스(=불변클래스)

오류 발생

package com.example;

public class StringExam {
    public static void main(String[] args) {
        String str1="Hello";
        String str2="Hello";
        String str3= new String("Hello");
        String str4= new String("Hello");

        if (str1 == str2)
            System.out.println("str1==str2");
        if (str1 == str3)
            System.out.println("str1==str3");
        if (str3 == str4)
            System.out.println("str3==str4");

        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str3);
        System.out.println(str4);
    }
}

→ 출력:

 

[이유]

"==" : 같은곳을 참조하는지?

   - new로 생성하지 않으면 Hello가 상수처럼 취급됨 → 같은 객체 참조

   - new로 생성하면 메모리상에 다른 영역 차지함 → 다른 객체 참조

  → String을 사용할때는 new 사용하지 말것 (메모리 용량 절약)

 

".equals()" : 같은 값을 가지는지?

if (str1.equals(str2)){
    System.out.println("str1과 str2는 값이 같다."); //true
}

 


2. 접근 제한자

접근 가능한 영역 /  default = 아무것도 안쓴 경우

private 쓰는 이유?

메서드가 너무 길때 메서드가 분리 필요!

외부에서 호출하는 것이아니라 내부에서 구조적으로 나누고 싶을때 private 사용

→ 메서드 이름만 봐도 코드가 어떤 코드인지 알 수 있음