TIL(22.4.01.FRI)

클래스(Class)

  • 객체를 정의하는 설계도
  • 붕어빵 틀, 레시피, 자동차의 특성(디자인, 무게, 가격)&기능(이동, 브레이크, 경적)이 정의된 설계도
    • 객체: 붕어빵1, 붕어빵2
    • 객체: 요리1, 요리2
    • 객체: car1
  • 클래스
    • 객체 변수와 메소드로 이루어져있다.
      • 객체 변수
        • 여태껏 사용했던 변수로 이해해주면 됨.
      • 메소드
        • 리턴 타입 메소드명(입력파라미터) { 메소드 기능에 대한 내용 }
        • 대표적예시
          • System.out.println();
            • println 메소드명 () 안에 입력파라미터로는 우리가 출력하고자 하는 내용들(“hello”)을 썻는데 “hello”를 출력해주고 이 때 메소드는 따로 변수를 만들어서 내용을 받지 않았으므로 리턴타입이 없는 경우임
              • 리턴타입이 없는 경우 void라고 명시함.
          • Integer.toBinaryString();
            • () 안에 입력파라미터로 Integer값을 넣어주면 2진법 표현으로 만들어줌. 이거 같은 경우 2진법으로 만든 String을 반환을 해줬던 것
              • 이 메소드 기능 리턴타입에 String이라고 써져 있던것
    • 이런식으로 클래스 안에 어떤 특성들(객체 변수?)이나 그 기능들(메소드?)을 메소드로 쭉 만들어 줄 수 있다
    • 클래스를 가지고 객체를 만드는 방법
      • 클래스명 객체명 = new 클래스명();
  • Class 만들기
      1. class Car {
            // 객체변수 만들기 == 변수 두 개 만들기
            String name;
            String type;
        
            // 생성자의 경우
            // 기본적으로 Car() {}
            // default로 있음. 생략이 가능함.
        
            //메소드 생성 == 기능들도 몇 가지 만들어보기
            public void printCarInfo() {
                System.out.println("== Car Info ==");
                System.out.println("name = " + name);
                System.out.println("type = " + type);
            }
            // 반환값이 없다는 void사용
            // 그 다음 메소드명 printCarInfo
            // {} 안에 메소드 호출됐을 시 실행하고자 하는 코드들 작성해줌
        
            public void move() {
                System.out.println("이동!");
            }
        
            public void brake() {
                System.out.println("정지!");
            }
        }
        • 생성자의 경우
          • 기본적으로 클래스 생성 시 default로 생성되나 생략이 가능함.
      1. 생성자 직접 만들어서 Class 만들기

        class Car2 {
            String name;
            String type;
        
            // 생성자 직접만들어보기 클래스이름과 동일하게Car2로 만든다 => Car2() {}
            // name 과 type을 객체를 생성할 때 바로 받을 수 있도록 생성자에 만들어주기
            Car2(String name, String type) {
                // 받아온 name과 type을 Car2객체의 변수에 할당하기 위해 this 이용하여 아래와 같이 작성
                // this는 자기 객체를 가리키는거라서 Car2의 객체 변수를 의미함.
                this.name = name;
                this.type = type;
                //생성자가 호출되었을때를 확인해보기 위해 출력문구 작성해봄
                System.out.println("생성자 출력!");
            }
        
            public void printCarInfo() {
                System.out.println("== Car Info ==");
                System.out.println("name = " + name);
                System.out.println("type = " + type);
            }
            // 기능 두 가지만 간단하게 더 추가해줌
            public void load() {
                System.out.println("짐을 주세요!");
            }
            public void horn() {
                System.out.println("빵빵!");
            }
        }
  • Class 사용해보기(= 객체 만들어서 사용해보기)
      1. public class MyPrac {
            public static void main(String[] args) {
                // Car class 이용하여 myCar1객체 생성
                Car myCar1 = new Car();
                // myCar1객체를 통해서 name에는 "a"
                myCar1.name = "a";
                // 차종을 의미하는 type에는 "SUV"라고 작성
                myCar1.type = "SUV";
                // 각각의 메소드들을 출력해보기
                myCar1.printCarInfo();
                myCar1.move();
                myCar1.brake();
        	 }
        }

      1. public class MyPrac {
            public static void main(String[] args) {
                // Car2 class를 이용하여 myCar2객체 생성
                // 이번에는 우리가 생성자를 데이터를 바로 입력받게끔 만들었기에 객체 생성시 ()안에 데이터를 넣어줘야 함.
                Car2 myCar2 = new Car2("b", "sedan");
                myCar2.printCarInfo();
                myCar2.load();
                myCar2.horn();
        	 }
        }

        • 이번에는 우리가 생성자를 데이터를 바로 입력받게끔 만들었기에 객체 생성시 ()안에 데이터를 넣어줘야 함.
        • 생성자 출력이 객체를 생성할 때 잘 들어가는지 확인해보자
          • 디버그로 생성자 절차 확인하기
            • Car2 myCar2 = new Car2("b", "sedan"); 부분에서 왼쪽 여백 클릭하면 빨간 동그라미 즉, 중단점이 표시됨
            • 그 상태로 Run → Debug 실행
            • 코드가 실행되다가 중단점 부분에서 멈춤
              • 여기서 잠시 콘솔창을 보면 아래 사진과 같이 해당 부분까지만 출력되어 있는 것을 확인 할 수 있다.
                • step over와 step into가 있는데
                  • step over는 그냥 다음줄로 이동하는 기능
                  • step into는 코드에서 안으로 들어갈 부분이 있으면 그 부분으로 이동하는것
            • step into(f7)클릭, 과정을 보려고 계속 step into(f7)를 클릭




          • 따라서 객체가 생성될 때 이 생성자 부분이 호출되는 것을 코드를 통해서 확인했음

생성자(Constructor)

  • 객체가 생성될 때 자동으로 호출되는 부분.
    • new 클래스명으로 객체가 새로 만들어질 때 자동으로 호출되는 부분
  • 생성자의 규칙
    • 생성자명 == 클래스명
    • 리턴 타입 없음
      • void를 쓰는것이 아닌 아예 비워두어야 함
  • 생성자는 객체 생성시 받아오는 데이터값을 바로 객체 변수에 할당할 때 사용하는 것 같다.

this, this()

  • this
    • 객체 자신을 의미
  • this()
    • 생성자의 호출을 의미
  • 명이 같을 때 구분을 위해 쓰이는 게 많은 것 같음
    • this는 객체 변수를 지칭하는 듯함.
      • 객체 생성시 생성자가 호출되는데 이 때 객체변수로 객체 생성시 입력한 데이터를 할당하는 식으로 쓰일 때 객체변수를 지칭하는 용도로 활용했음.

오버로딩(Overloading)

  • 위의 예제는 생성자를 오버로딩한 것
    • 클래스명이 있는데 이것과 동일하게 생성자명에 적어준다. ⇒ 클래스명 == 생성자명
    • default 생성자가 존재 ⇒ 클래스명() {}
    • 그 아래에는 어떤 데이터를 넣어 초기화하고 싶을때 생성자를 추가로 만들면 사실 기존 생성자를 오버로딩하여 만든것이다.
  • 한 클래스 안에서 같은 이름을 같는 메소드가 여러개 있는 것. 이것을 구현 관점에서 오버로딩이라고 함.
  • 오버로딩의 조건
    • sum()인데 ()안에 int, string등으로 다르게 하고 sum은 같게 하는 것
      • 메소드 이름이 같아야 함
      • 매개변수의 개수 또는 타입이 달라야 함.
      • 리턴 타입의 차이로는 오버로딩이 되지 않음
    • 메소드가 int값을 반환하는 sum메소드, input파라미터는 int a, int b로 두 개를 받는 메소드가 있다 가정하에 ⇒ int sum(int a, int b);
      • 위 메소드를 사용할 때는 sum(1, 2); 호출,
      • 결과값은 새로운 변수를 받아서 받는다 ⇒ int result = sum(1, 2);
    • 여기서 정수형 데이터 뿐만 아니라 실수형 데이터를 넣어서 쓰고 싶다 ⇒ sum(1.3, 2.5);
      input파라미터의 갯수가 2개가 아닌 여러개를 넣어서 쓰고 싶다 ⇒ sum(1, 2, 3, 4, ....);
      - 메소드명을 sum으로 동일하게 작성하되 매개변수에 들어가는 타입이나 갯수가 달라져서 기존의 int sum(int a, int b); 을 사용할 수 없을 때
      - 이때 오버로딩을 사용해서 똑같은 메소드명인 sum을 동일하게 사용하되 int를 double로 바꿔서 타입을 바꿔준다거나, 아니면 매개변수 갯수를 늘려준다거나 하는 식으로 만드는것
  • 기존 클래스에서 차량 정보를 출력해주는 메소드가 있지만
    class Car {
        String name;
        String type;
    
        Car(String name, String type) {
            this.name = name;
            this.type = type;
        }
        // 차량 정보를 출력하는 메소드
        public void printCarInfo() {
            System.out.println("== Car Info ==");
            System.out.println("name: " + name);
            System.out.println("type: " + type);
        }
    • 외부로부터 날짜를 추가로 입력받겄나, 차 번호를 추가로 입력받아 더 출력해주는 기능을 구현하고 싶다 가정
      public class MyPrac {
          public static void main(String[] args) {
              Car myCar1 = new Car("a", "sedan");
              //기본적으로 초기화됐던 정보를 출력하는거에 더해서
              myCar1.printCarInfo();
      
              // 오버로딩 사용하여 날짜를 출력해주거나
              myCar1.printCarInfo("2022");
              // 오버로딩 사용하여 차량 번호를 출력해주거나
              myCar1.printCarInfo(1234);
              // 오버로딩 사용하여 날짜&차량 번호 둘다 출력해주는 것을 가정
              myCar1.printCarInfo("2022", 1234);
          }
      }
      • 오버로딩을 구현한다.
        class Car {
            String name;
            String type;
        
            Car(String name, String type) {
                this.name = name;
                this.type = type;
            }
            // 차량 정보를 출력하는 메소드
            public void printCarInfo() {
                System.out.println("== Car Info ==");
                System.out.println("name: " + name);
                System.out.println("type: " + type);
            }
        
            // 오버로딩 구현
            //String 형태의 파라미터 받는 메소도 구현 => 날짜
            public void printCarInfo(String date) {
        				// 차량 정보를 출력하는 메소드지칭
                this.printCarInfo();
        				// String 형태의 파라미터 받아 날짜 출력
                System.out.println("date : " + date);
            }
            //  int 형태의 파라미터 받는 메소드 구현 => 차량 번호
            public void printCarInfo(int number) {
                this.printCarInfo();
                System.out.println("number : " + number);
            }
        
            //    int 형태와 String 형태 두 개의 파라미터 받는 메소드 구현
            public void printCarInfo(String date, int number) {
                this.printCarInfo();
                System.out.println("date :" + date);
                System.out.println("number : " + number);
            }
        }

접근제어자

  • 클래스의 변수나 메소드의 접근에 제한을 두는 키워드
  • private
    • 내 클래스 즉, 자기자신의 클래스안에서만 적용가능하도록 접근을 제한
      • 자기 자신을 제외하고 아무도 접근 못함.
        • 동일 패키지내에 있는 일반 클래스 접근 불가능
        • 동일 패키지내에 있는 일반 클래스로부터 상속받은 자식 클래스도 접근 불가능
        • 외부 패키지내에 있는 모든 클래스들도 접근 불가능
    • 제한 정책상 가장 강력
  • public
    • 접근 제한을 풀어줌.
      • 즉, 어디서든 접근 가능
  • default
    • 접근제어자 키워드로 default라고 쓰는게 아니라 그냥 아무것도 명시하지 않는것
    • 해당 패키지 안에서만 접근 가능
      • 동일 패키지내에 있는 모든 클래스들이 접근 가능
      • 외부 패키지내에 있는 모든 클래스들은 접근 불가능
      • 패키지
        • 여러 클래스들이 모여있는 그런 각각 구분되는 영역을 의미
  • protected
    • 해당 패키지나 상속받은 클래스에서 접근 가능
      • 동일 패키지 내에서 모든 클래스들이 접근 가능
      • 외부 패키지 내에서는 일반 클래스는 접근 불가능
        • 그러나 외부 패키지 내에서 일반 클래스로부터 상속받은 자식 클래스는 접근 가능
  • 따라서 접근권한에 맞도록 적절히 사용해주면 됨.
  • src폴더에 패키지 car를 만든다.
  • 패키지 폴더 안에 Car2라는 class를 만든다.
  • Car2 Class안에 접근제어자 실습코드를 만든다.
    • 일단 생성자도 하나 만들고, public사용해보기
      package car;
      
      public class Car2 {
          public String name1;
          public String name2;
          public String name3;
          public String name4;
      
          public Car2(String name1, String name2, String name3, String name4) {
              this.name1 = name1;
              this.name2 = name2;
              this.name3 = name3;
              this.name4 = name4;
          }
      }
      • MyPrac(main)에서 만들어논 외부 패키지내 클래스(Car2)사용해보기
        public class MyPrac {
            public static void main(String[] args) {
        
        //       다른 패키지에 있는 클래스를 메인에서 사용
                Car2 myCar2 = new Car2("a", "b", "c", "d");
                System.out.println(myCar2.name1);
                System.out.println(myCar2.name2);
                System.out.println(myCar2.name3);
                System.out.println(myCar2.name4);
            }
        }
        • 외부 패키지내 클래스 사용하는 것이므로 import해줘야 함
          • import.car.Car2
        • 모두 접근이 가능해서 잘 출력되는것을 확인할 수 있음
    • 일단 생성자도 하나 만들고, private, protected, default사용해보기
      package car;
      
      public class Car2 {
          public String name1;
          // private 사용
          private String name2;
          // protected 사용
          protected String name3;
          // default 사용
          String name4;
      
          public Car2(String name1, String name2, String name3, String name4) {
              this.name1 = name1;
              this.name2 = name2;
              this.name3 = name3;
              this.name4 = name4;
          }
      }
      • MyPrac(main)에서 만들어논 외부 패키지내 클래스(Car2)사용해보기
        • private, protected, default다 접근이 안된다고 빨간 표시 뜸
        • 그래서 우선은 외부에서 접근을 위해서는 public으로 만들어줘야 한다!
          • protected는 만약에 Car2를 상속받은 자식 클래스로 접근할 경우 접근이 가능해짐

Static

  • 변수나 메소드의 특성을 바꾸는 키워드

    • 어떻게 바꾸냐?
      • static이 붙어있는 변수나 메소드는 객체가 생성되기 전부터 메모리에 한 번 잡혀서 상주하게 됨.
        • Static 클래스 변수
          • 해당 클래스의 각 객체들이 값을 공유
        • Static 클래스 메소드
          • 객체를 생성하지 않아도 호출 가능
    • 특징
      • 일단 메모리에 한 번만 할당됨
        • 이렇게 미리 할당되서 항상 상주되어 있기에 Static 변수나 메소드는 서로 공유되는 특성이 있음
  • Static 이용해보기

    • 빨간 밑줄이 쳐짐
      • 여기서 getName메소드도 Static으로 바꿔주면 해결됨
        • 객체 생성을 하지 않았는데도 바로 클래스명을 통해 해당 메소드를 호출할 수 있는 경우!! ⇒ 완전 김띠용임 ⇒ Static을 이용하면!!
    • Static메소드로 되어있는 경우
      • Static 메소드 안에서 사용할 수 있는 변수도 Static이어야 한다.
        • Static이 붙으면 객체가 생성되기 전부터 메모리에 자동으로 올라감으로 당연히 Static 메소드 안에서 사용할 수 있는 변수도 미리 만들어줘야 함.
      • 따라서 name 변수는 사용가능하나 type변수는 사용 불가능함.
    • 변수가 스태틱으로 잡혀 있으면 객체끼리 이 변수를 공유함. 그래서 이 특성으로 인해 다른 객체에서 이 데이터를 바꿔주면 마지막에 바꾼 데이터로 공유되어 출력이 되서 발생되는 현상
      class Car3 {
          //    스태틱 변수
          static String name = "None";
          String type;
      
          Car3(String name, String type) {
              this.name = name;
              this.type =type;
          }
      
          public void printCarInfo() {
              System.out.println("== Car Info ==");
              System.out.println("name: " + name);
              System.out.println("type: " + type);
          }
      
          //    스태틱 메소드
          /*스태틱 메소드으로 되어 있으면서 이 안에서 사용하고 있는 변수 또한 스태틱이어야 함*/
          public static void getName() {
              System.out.println("Car name: " + name);
          }
      }
      
      public class MyPrac {
          public static void main(String[] args) {
      //        3. Static
              Car3.getName();
      
      // 변수가 스태틱으로 잡혀 있으면 객체끼리 이 변수를 공유함. 그래서 이 특성으로 인해
      // 다른 객체에서 이 데이터를 바꿔주면 마지막에 바꾼 데이터로 공유되어 출력이 되서 발생되는 현상
              Car3 myCar3_1 = new Car3("a", "sedan");
              Car3 myCar3_2 = new Car3("b", "suv");
              Car3 myCar3_3 = new Car3("c", "rv");
              myCar3_1.printCarInfo();
              myCar3_2.printCarInfo();
              myCar3_3.printCarInfo();
          }
      }
      • 즉, name변수는 Static으로 잡혀 있기에 객체 생성전에 이미 메모리에 올라가 있어 서로 공유될 수 있음.
        • 따라서 객체생성시 객체들끼리 이 Static name 변수를 공유함.
          • 다른 객체에서 이 Static name 변수를 수정하면 마지막에 수정된 데이터로 할당되면서 수정된 데이터로 모든 객체에 공유되어 출력됨.

상속

  • 헷갈리는 상속해서? 상속 받아서?
    • 부모 클래스를 상속해서
    • 자식 클래스가 상속 받아
  • 부모 클래스 == 상위 클래스, 기초클래스
    • 상속의 대상
  • 자식 클래스 == 하위 클래스, 파생 클래스
    • 부모 클래스를 상속해서 새롭게 만드는 클래스
  • 부모 클래스의 필드와 멤버 변수나 메소드가 상속됨
    • 단 생성자, 초기화 블록은 상속되지 않음
  • 자식클래스가 여러 부모클래스로부터 상속받을 수 없다!
    • 다중 상속 불가능
  • private
    • 해당클래스내에서만 접근 가능
    • 나머지는 다 접근 불가능
  • default
    • 동일 패기지 내의 자식 클래스는 접근이 가능
    • 외부 패키지의 자식 클래스는 접근 불가능
  • 자식클래스 부모클래스로부터 상속받기
    //Java 프로그래밍 - 상속-
    // 부모 클래스인 Person
    class Person  {
        // 문자형 변수, 정수형 변수
        String name;
        int age;
        // 접근제어자가 있는 변수
        public int a1;
        private int a2;
    
        // 기본 생성자
        Person() {}
        // 초기화를 위해 만들어준 오버로딩된 생성자
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        // 간단한 정보 출력 메소드가 있음
        public void printInfo() {
            System.out.println("Person.printInfo");
            System.out.println("name: " + name);
            System.out.println("age:" + age);
        }
    }
    
    // 부모 클래스인 Person 상속받아 자식클래스 만들기
    // Student 클래스 - Person 상속, 접근제어자 확인
    class Student extends Person {
    
    }
    
    public class MyPrac {
        public static void main(String[] args) {
            Student s1 = new Student();
            s1.name = "a";
            s1.age = 25;
            s1.printInfo();
        }
    }
    • Student class 안에는 사실 아무것도 없지만 Person을 상속받아 왔기에 호출을 해도 Student class안으로 Person 기능들을 끌어와서 사용한 것!
  • 접근 제어자에 따른 상속 범위 테스트 해보기
    //Java 프로그래밍 - 상속-
    // 부모 클래스인 Person
    class Person  {
        // 문자형 변수, 정수형 변수
        String name;
        int age;
        // 접근제어자가 있는 변수
        public int a1;
        private int a2;
    
        // 기본 생성자
        Person() {}
        // 초기화를 위해 만들어준 오버로딩된 생성자
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        // 간단한 정보 출력 메소드가 있음
        public void printInfo() {
            System.out.println("Person.printInfo");
            System.out.println("name: " + name);
            System.out.println("age:" + age);
        }
    }
    
    // 부모 클래스인 Person 상속받아 자식클래스 만들기
    // Student 클래스 - Person 상속, 접근제어자 확인
    class Student extends Person {
    
    }
    
    public class MyPrac {
        public static void main(String[] args) {
            Student s1 = new Student();
            s1.name = "a";
            s1.age = 25;
            s1.printInfo();
        }
    }
    • 접근 제어자가 private이기에 상속을 받았으나 다른 클래스라서 접근이 불가능
    • 접근 제어자가 public이기에 어디서든 접근이 가능

super, super()

  • this, this()와 같은 맥락?
  • super
    • 상속받은 클래스⇒자식 클래스에서 자신의 부모쪽을 가리킬때 사용한다 함.
    • 부모 클래스와 자식 클래스의 멤버 이름이 같을 때 구분할 때 쓰는 키워드
  • super()
    • 부모 클래스의 생성자 호출

오버라이딩

  • 오버로딩과는 다른내용임!! 구분해두자!
    • 오버로딩
      • 메소드명이 같고, 매개변수의 타입과 갯수를 달리하여 정의
      • 예시
        • 덧셈기능
          • 메소드명: Plus()
          • 오버로딩
            • 정수형으로 더하기
              • Plus() {
                return a + b;
                }
            • 정수형으로 출력
              • Plus() ⇒ a + b;
            • 더블형으로 더하기
              • double Plus() {
                return a + b;
                }
            • 더블형으로 출력
              • Plus( double a, double b) ⇒ a + b;
    • 오버라이딩
      • 부모 클래스의 메소드를 자식 클래스에서 재정의
      • 조건
        • 메소드의 선언부는 부모 클래스의 메소드와 동일해야 함
          • 오버로딩처럼 입력 파라미터에 타입과 갯수를 달리해서 만드는게 아님. 동일하게 만들어줘야 함!
            • 그 안에 내용들을 바꿔주면은 자식 클래스에서는 그냥 그 메소드들을 그 재정의한 내용으로 사용하는 것
        • 자주 사용하지는 않을 것 같으나..(오버라이딩에 대한 사용부터 먼저 해보고, 아래의 내용들은 점점 확장해 나가면 될것 같음)
          • 반환 타입에 한해서,
            부모 클래스의 반환 타입으로 형변환할 수 있는 타입으로는 변경 가능
          • 부모 클래스의 메소드보다 접근제어자를 더 좁은 범위로 변경 불가
          • 부모 클래스의 메소드보다 더 큰 범위의 예외 선언 불가
  • 부모클래스의 메소드를 자식클래스에서 상속하고 나서 메소드를 아예 재정의하는것
  • 자식클래스 부모클래스로부터 상속받기 + super사용 + 오버라이딩사용
    //Java 프로그래밍 - 상속-
    // 부모 클래스인 Person
    class Person  {
        String name;
        int age;
        public int a1;
        private int a2;
    
        Person() {}
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public void printInfo() {
            System.out.println("Person.printInfo");
            System.out.println("name: " + name);
            System.out.println("age:" + age);
        }
    }
    
    // 부모 클래스인 Person 상속받아 자식클래스 만들기
    // Student2 클래스 - Person 상속, super 사용, 오버라이딩
    class Student2 extends Person {
        // 문자형 변수 만들기, 부모 클래스에도 name이라는 문자형 변수가 있지만
        // 자식클래스에서도 똑같이 한 번 만들어 본 것
        String name;
        // stdId를 넣어줄 stdId라는 정수형 변수 만들기
        int stdId;
    
        // 생성자 하나 만들기- 오버로딩
        // super 사용
        Student2(String name, int age, int stdId) {
            // 부모 클래스에도 String name이 있지만 자식 클래스 Student2에도 똑같은 String name 객체 변수가 있기 때문에
            // this가 자기 객체이므로 Student2의 객체 변수인 String name을 뜻함.
            // this.name = name;
    
            // super를 이용해서 하나 더 할 수 있는 것은
            // super()하면 부모 클래스쪽의 생성자를 호출하는 건데
            // ()안에 name, age를 넣어 호출해보면
             super(name,age);
            // 부모 클래스의 생성자가 호출됨.
            // 즉, 데이터를 받아서 부모 클래스 쪽으로 넘겨 부모 클래스쪽에서 데이터 초기화가 될 수 있도록 해주는 방법임
    
            // 자식 클래스에 Int age가 없으니까 상속받은 부모 클래스의 객체 변수인 Int age를 뜻함
            this.age = age;
    
            // 부모 클래스에는 없는 객체 변수이니까. 자기 객체인 즉, 자식 클래스의 객체 변수인 int stdId를 뜻함
            this.stdId =stdId;
    
            // 만약 자기 객체인 즉, 자식 클래스의 객체 변수인 String name이 아니라 부모 클래스쪽 객체 변수인 String name을 할당하고 싶을 경우
            // 여기서 super를 이용하면 됨.
            // super.name = name;
            // 이러면 부모 클래스의 객체 변수인 String name 즉, 부모 객체쪽의 변수를 뜻함
    
        }
    
        // 오버라이딩 사용
        // 부모 클래스에 있는 printInfo()메소드랑 똑같이 작성한다.
        public void printInfo() {
            // 이 안에다가 오버라이딩하고 싶은 내용을 자유자재로 작성하면 됨.
            System.out.println("Student2.printInfo");
            System.out.println("name: " + name);
            System.out.println("age:" + age);
            System.out.println("stdId: " + stdId);
        }
    }
    
    public class MyPrac {
        public static void main(String[] args) {
    //        2. super, super(), 오버라이딩
    
            Student2 s2 = new Student2("b", 32, 1);
            s2.printInfo();
        }
    }
    • name이 null이 나온 이유는?
      • name을 초기화 해줄때 부모쪽으로 name을 넘겨서 초기화되어 있고, 자기 객체인 즉, 자식 클래스 Student2 의 객체변수 name에는 아무 값이 할당되어 있지 않기에
        • 그리고 기본적으로 출력시 자기 객체의 것을 먼저 가져다 쓰기에 아무 데이터도 들어있지 않다라는 null이 출력되고 있는 것
      • 해결법
        //Java 프로그래밍 - 상속-
        // 부모 클래스인 Person
        class Person  {
            String name;
            int age;
            public int a1;
            private int a2;
        
            Person() {}
            Person(String name, int age) {
                this.name = name;
                this.age = age;
            }
        
            public void printInfo() {
                System.out.println("Person.printInfo");
                System.out.println("name: " + name);
                System.out.println("age:" + age);
            }
        }
        
        // 부모 클래스인 Person 상속받아 자식클래스 만들기
        // Student2 클래스 - Person 상속, super 사용, 오버라이딩
        class Student2 extends Person {
            // 문자형 변수 만들기, 부모 클래스에도 name이라는 문자형 변수가 있지만
            // 자식클래스에서도 똑같이 한 번 만들어 본 것
            String name;
            // stdId를 넣어줄 stdId라는 정수형 변수 만들기
            int stdId;
        
            // 생성자 하나 만들기- 오버로딩
            // super 사용
            Student2(String name, int age, int stdId) {
                // 부모 클래스에도 String name이 있지만 자식 클래스 Student2에도 똑같은 String name 객체 변수가 있기 때문에
                // this가 자기 객체이므로 Student2의 객체 변수인 String name을 뜻함.
                this.name = name;
        
                // super를 이용해서 하나 더 할 수 있는 것은
                // super()하면 부모 클래스쪽의 생성자를 호출하는 건데
                // ()안에 name, age를 넣어 호출해보면
                // super(name,age);
                // 부모 클래스의 생성자가 호출됨.
                // 즉, 데이터를 받아서 부모 클래스 쪽으로 넘겨 부모 클래스쪽에서 데이터 초기화가 될 수 있도록 해주는 방법임
        
                // 자식 클래스에 Int age가 없으니까 상속받은 부모 클래스의 객체 변수인 Int age를 뜻함
                this.age = age;
        
                // 부모 클래스에는 없는 객체 변수이니까. 자기 객체인 즉, 자식 클래스의 객체 변수인 int stdId를 뜻함
                this.stdId =stdId;
        
                // 만약 자기 객체인 즉, 자식 클래스의 객체 변수인 String name이 아니라 부모 클래스쪽 객체 변수인 String name을 할당하고 싶을 경우
                // 여기서 super를 이용하면 됨.
                // super.name = name;
                // 이러면 부모 클래스의 객체 변수인 String name 즉, 부모 객체쪽의 변수를 뜻함
        
            }
        
            // 오버라이딩 사용
            // 부모 클래스에 있는 printInfo()메소드랑 똑같이 작성한다.
            public void printInfo() {
                // 이 안에다가 오버라이딩하고 싶은 내용을 자유자재로 작성하면 됨.
                System.out.println("Student2.printInfo");
                System.out.println("name: " + name);
                System.out.println("age:" + age);
                System.out.println("stdId: " + stdId);
            }
        }
        
        public class MyPrac {
            public static void main(String[] args) {
        //        2. super, super(), 오버라이딩
        
                Student2 s2 = new Student2("b", 32, 1);
                s2.printInfo();
            }
        }

다형성

  • 다형성 구조

    • 부모클래스타입 부모클래스 이용하여 만든객체명 = new 자식클래스의 클래스명() => 자식클래스의객체
      • Person p1 = new Student();
  • 업캐스팅(다형성)

    • 자식 클래스의 객체가 부모 클래스의 타입으로 형변환 되는 것
    • 다형성의 참조가능 방향성은 부모가 자식쪽
      • 부모가 자식의 객체를 받아 쓸 수 있음(부모→자식)
      • 자식이 부모의 객체를 받아 쓸 수 없음(자식→부모)
  • 다운 캐스팅

    • 원래 자기 객체가 부모 클래스의 타입으로 업캐스팅 됐던 것을 다시 자신의 클래스 쪽으로 타입변환 해주는것.
  • 다형성을 잘 이해한다면 코딩 관점에서 좀 더 멋지게 작성할 수 있따!!!!!!

    • 다형성의 장점
      • 부모클래스타입으로 자식클래스들을 다 가리킬수 있기에 객체들을 데이터처럼 쓸 수 있다.
        • 배열에서 활용됐음 → 연습문제 설명-3 참고
  • 한 객체가 여러가지 타입을 가질 수 있는 것

  • 부모클래스 타입의 참조 변수로 자식클래스 인스턴스를 참조하는 것

  • 사진코드 설명

    • class Person이 있다
    • Person을 상속받아서 새로만든 자식클래스인 Student가 있음
    • 객체생성시
      • Person p1= new Person()이 아니라
      • Person p1 = new Student()로 되어 있음
        • 자식 클래스의 객체를 만들어서 할당해주고 있는 경우
        • 이와 같이 Person이지만 Student같이 다른 타입도 가질 수 있는 것을 기본적으로 다형성이다라고 함
          • person 타입이지만 sutdent타입을 가질 수 있는
        • 다형성의 참조가능 방향성은 부모가 자식쪽으로
          • 부모 → 자식 0(부모가 자식의 객체를 받아 쓸 수 있음), 자식 → 부모 X(자식이 부모의 객체를 받아 쓸 수 없음)
  • Class설명

    // Person이라는 클래스가 있음
    class Person {
        // Person.print를 출력하는 간단한 메소드가 있음
        public void print() {
            System.out.println("Person.print");
        }
    }
    
    // Person을 상속받아 만든 자식클래스인 Student가 있음.
    class Student extends Person {
        // print()를 오버라이딩 해서 Student.print를 출력하는 메소드 재정의
        public void print() {
            System.out.println("Student.print");
        }
        // print2()라는 새로운 메소드 생성하여 print2를 출력하게끔 작성
        public void print2() {
            System.out.println("Student.print2");
        }
    }
    
    // Person을 상속해서 Collegestudent 클래스를 만듬.
    class CollegeStudent extends Person {
        // print()메소드를 CollegeStudent.print라고 출력하게끔 오버라이딩시켜줌
        public void print() {
            System.out.println("CollegeStudent.print");
        }
    }
  • 다형성 실습

    public class MyPrac {
    
        public static void main(String[] args) {
    
    //      1. 다형성
            System.out.println("== 다형성 ==");
            Person p1 = new Person();
        // 자기 클래스 객체를 자기 클래스 타입이 가리키기에 문제가 없음
    
            Student s1 = new Student();
        // 자기 클래스 객체를 자기 클래스 타입이 가리키기에 문제가 없음
    
            Person p2 = new Student();
        // Person이 new Student쪽을 가리키는 경우. 우리는 이것을 다형성이라 함.
        // 자식이 객체인데 부모클래스의 타입으로 만들어준것. 이것을 다형성이라 함.
    
        // Student s2 = new Person();
        // Student가 new Person쪽을 가리키는 경우
        // 부모가 객체인데 자식클래스의 타입으로 만들어주는 것은 안 되는 방법.
    
            p1.print();
        // p1은 Person여서 print()메소드를 가지고있기에 Person 객체 안에 있는 print()가 출력됨
    
            s1.print();
        // s1은 Student여서 오버라이딩한 print()메소드를 가지고있기에 Student객체 안에 있는 print()가 출력됨
    
            s1.print2();
        // s1은 Student여서 새로운 print2()메소드를 가지고 있기에 Student객체 안에 있는 print2()가 출력됨.
    
            p2.print();
        // p2의 대상은 Student 객체이기에 Student 객체 안에 있는 print()가 출력됨
        // p2의 타입은 Person이지만 출력 결과는 Student쪽의 print()가 출력됨
    
        // p2.print2();
        // 가능한 것 아닐까?
        // Student에는 print2()라는 메소드가 있다.
        // 타입이 Person인 p2지만 객체 대상은 Student이므로 print2()메소드도 출력가능할 거 같은데...
        // 근데 사용이 안 된다.
        // Person의 타입에 맞는 부분까지만 Student객체의 메소드에 접근가능하다. 즉, 오버라이딩 한 부분만 가능하다는 것 같음
        //  p2의 타입은 person이고 거기에 해당하는 메소드는 print이므로 student의 print부분까지만 접근가능하다는 말.
    
            Person p3 = new CollegeStudent();
            // CollegeStudent 가 Person에 상속받아 생긴 자식클래스이므로, CollegeStudent객체가 person을 가리킬 수 있다.
            p3.print();
    
        // CollegeStudent c1 = new Student();
        // 자식 클래스끼리 같은 부모를 상속받았다하더라도 다형성을 할 수 없다
        // 같은 부모로부터 상속받았다 할 지라도, 자식 클래스끼리 서로 가리킬 수 없다
        }
    }

  • 타입 변환

    public class MyPrac {
    
        public static void main(String[] args) {
            System.out.println("== 타입 변환 ==");
            Person pp1 = null;
            Student ss1 = null;
    
            Person pp2 = new Person();
            Student ss2 = new Student();
            Person pp3 = new Student();
        // 업캐스팅
        // 다형성을 다른말로 업캐스팅이라고 함.
        // 자식 클래스의 객체가 부모 클래스의 타입으로 형변환 되는 것
    
            pp1 = pp2;
            // pp1이 pp2를 가리키는 것은 같은 타입이 같은 타입 가리키므로 문제 없음
    
            pp1 = ss2;
        // pp1 이 ss2를 가리키는 것은 다형성이기에 문제 없음.
    
            ss1 = ss2;
            // s1이 ss2를 가리키는 것은 같은 타입이 같은 타입 가리키므로 문제 없음
    
         // ss1 = pp2;
        // ss1이 pp2를 가리키는 것은 안 됨. 자식이 부모를 가리킬순 없다.
    
         // ss1 = pp3;
        // ss1이 pp3를 가리키는 것은 업캐스팅이기에 살펴봐야 함
        // ss1은 student이고, pp3은 person이기에 방향이 거꾸로이니 안되는게 맞으나, pp3는 업캐스팅 된 녀석. 즉, 실객체는 student임
    
        //그래서 중간에 형변환 즉, 타입을 student로 변환해준다.
            ss1 = (Student)pp3;
        // 다운 캐스팅이라 함.
        // 원래 자기 객체가 부모 클래스의 타입으로 업캐스팅 됐던 것을 다시 자신의 클래스 쪽으로 타입변환 해주는것.
        // pp3의 원래 객체는 student였으니까 잠시 업캐스팅되어 타입만 person이였으니까, 이 타입을 다시 원대 자기 자신의 student로 타입 변환을 해주면 사용할 수 있게 됨.
    
         // CollegeStudent cc1;
         // CollegeStudent cc2 = new CollegeStudent();
    
         // ss1 = (Student)cc2;
         // cc1 = (Student)ss2;
        // 안 되는 이유는?
        // ss1이 student로 형변환한 cc2를 가리킬 수 없다.
        // 같은 부모 클래스에 상속받았다 하더라도 자식클래스 끼리의 형변환도 되지 않는다.
        }
    }
    • 같은 부모 클래스에 상속받았다 하더라도 자식클래스 끼리의 다형성을 할 수 없고, 형변환도 할 수 없다.

instaceof

  • 다형성을 사용하다보면 실제 참조하고 있는 객체가 타입이 뭐지?하는 경우가 있다
    • 그런 경우에 사용할 수 있는 것이 instanceof임
    • 실제 참조하고 있는 객체의 타입이 궁금할 때 사용
  • p1(만든 객체명== instance)이 Person(해당클래스,해당객체)의 객체인지 아닌지 확인할 수 있음. == 해당 클래스, 해당객체의 instance가 맞는지 체크해주는 기능 System.out.println(만든객체명.instanceof 해당클래스, 해당객체)
  • instanceof
    public class MyPrac {
    
        public static void main(String[] args) {
            System.out.println("== instanceof ==");
            // Person pe1의 객체가 하나 만들어짐
            Person pe1 = new Person();
            // Student st1의 객체가 하나 만들어짐
            Student st1 = new Student();
            // 업캐스팅
            // 자식 클래스의 객체가 부모 클래스의 타입으로 형변환 되는 것
            Person pe2 = new Student();
            Person pe3 = new CollegeStudent();
    
            // instanceof로 출력해보기
            // 해당 객체(instance)의 instance가 맞는지 체크해주는 기능
    
            // pe1이 Person클래스의 instance인지 출력해보기
            System.out.println(pe1 instanceof Person);
                // pe1은 Person의 객체이므로 Person의 instance다
            // pe1이 Student클래스의 instance인지 출력해보기
            System.out.println(pe1 instanceof Student);
                // pe1은 Person의 객체이므로 Student의 instance가 아니다
            System.out.println();
            // st1이 Student클래스의 instance인지 출력해보기
            System.out.println(st1 instanceof Student);
                // st1은 Student의 객체이므로 Student의 instance다
            // st1이 Person클래스의 instance인지 출력해보기
            System.out.println(st1 instanceof Person);
                // st1은 Student의 객체이지만, Person 부모클래스로부터 상속받아 만들어진 Student 자식클래스로부터 만들어진 객체라서 st1은 Person의 instance다
                // 다형성을 쓸 수 있는지 없는지 판별해준다. 😀
            System.out.println();
            // pe2가 Person클래스의 instance인지 출력해보기
            System.out.println(pe2 instanceof Person);
                // pe2는 Student의 객체이고, Person 부모클래스로부터 상속받아 만들어진 Student 자식클래스로부터 만들어진 객체라서 st1은 Person의 instance다
            // pe2가 Student클래스의 instance인지 출력해보기
            System.out.println(pe2 instanceof Student);
    
            System.out.println();
            // pe3가 Person클래스의 instance인지 출력해보기
            System.out.println(pe3 instanceof Person);
                // pe3는 CollegeStudent의 객체이고, Person 부모클래스로부터 상속받아 만들어진 CollegeStudent 자식클래스로부터 만들어진 객체라서 st1은 Person의 instance다
            // pe3가 CollegeStudent의 instance인지 출력해보기
            System.out.println(pe3 instanceof CollegeStudent);
            System.out.println();
        }
    }
    • instanceof를 통해서 우리는 다형성을 쓸 수 있는지 없는지를 미리 확인할 수 있음.
      import java.net.CookieHandler;
      
      public class MyPrac {
          public static void main(String[] args) {
              System.out.println("== instanceof ==");
              // Person pe1의 객체가 하나 만들어짐
              Person pe1 = new Person();
              // Student st1의 객체가 하나 만들어짐
              Student st1 = new Student();
              // 업캐스팅
              // 자식 클래스의 객체가 부모 클래스의 타입으로 형변환 되는 것
              Person pe2 = new Student();
              Person pe3 = new CollegeStudent();
      
             // instanceof를 통해서 우리는 다형성을 쓸 수 있는지 없는지를 미리 확인할 수 있음
              // pe1이 Student의 instance인지를 조건문으로 check해보고 난 후 형변환하기
              if (pe1 instanceof Student) {
                  // pe1이 Student의 instance라면 pe1을 Student타입으로 형변환하여
                  // Student stu1에 할당하라
                  Student stu1 = (Student) pe1;
              }
              // st1이 Person의 instance인지를 조건문으로 check해보고 난 후 형변환하기
              if (st1 instanceof Person) {
                  // st1이 Person의 instance라면 st1을 Person타입으로 형변환하여
                  // Person per1에 할당하라
                  Person per1 = (Person) st1;
              }
          }
      }

추상 메소드

  • 자식클래스에서 반드시 오버라이딩 해야하는 메소드를 지정해줄 수 있는 기능
  • 선언만 하고 기능 구현 내용이 없다.

추상 클래스

  • 추상 메소드를 하나 이상 포함하는 클래스
    • 클래스 {}안 멤버들 중에 적어도 하나는 abstract가 붙은 추상 메소드가 존재해야 함.
  • 반드시 구현해야하는 부분에 대해 명시적으로 표현해줄 수 있는 그런 클래스를 만드는 것
  • 추상 클래스 자체는 객체 생성 불가함.
    • 추상메소드는 기능구현 부분이 없어 불안정한 클래스임
    • 따라서 자체적으로 객체 생성을 못함.
  • 추상 클래스 만들어보기 & 추상 클래스를 상속받은 자식클래스 만들어보기
    // 추상 클래스 Person을 만들어보기
    abstract class Person {
        // 추상 메소드 만들어보기
        // 구현 부분 없이 세미클론(;)으로 마무리 해준다
        abstract void printInfo();
    }
    
    // 추상 클래스인 Person을 상속해서 Student 클래스 만들어보기
    // 보통때는 상속을 받으면 아무런 빨간줄이 안났는데 빨간줄이 발생됨.
    // 추상 클래스를 상속받았기에 내부에 추상 메소드를 반드시 구현해줘야 되는 부분때문에 그럼.
    class Student extends Person {
        // 추상 메소드 구현
        // 오버라이딩을 했기에 빨간줄이 없어짐.
        // "Student.printInfo"가 출력되는 메소드 작성
        public void printInfo() {
            System.out.println("Student.printInfo");
        }
    }
  • 추상 클래스의 사용
    • 상속받아서 만든 후 사용하는 방법
      public class MyPrac {
      
          public static void main(String[] args) {
      
              // 추상 클래스의 사용
      
              // 추상 클래스를 이용하여 객체 생성해보려 했지만 안 됨.
              // 아무런 기능 구현된 부분이 없어서 추상 클래스를 바로 객체로 만드는 것은 불가능
              // Person p1 = new Person();
      
              // 자식 클래스인 Student를 이용해서 객체 생성하기
              Student s1 = new Student();
              // Student클래스를 이용해 만든 객체 s1의 기능중 printInfo()메소드 사용
              s1.printInfo();
          }
      
      }
  • 추상 클래스사용의 다른 방법
    • 익명클래스로 바로 그 부분에서 만들어 사용하는 방법도 있다

      public class MyPrac {
      
          public static void main(String[] args) {
      
              // 추상 클래스의 사용
              // Person p2 = new Person() 엔터 눌러주면 자동으로 아래와 같이 작성이 됨.
              // 오버라이드 하라는 부분이 자동으로 맵핑되어 아래와 같이 작성이 됨.
              // 가장 끝 중괄호에 세미클론(;)으로 마무리 해줌.
              Person p2 = new Person() {
                  @Override
                  void printInfo() {
                      System.out.println("Main.printIhfo");
                  }
              };
              // 이렇게 하면 이 Person 추상클래스를 통해서 위의 부분에서 완성 시켜준것
              // 이것을 익명 클래스라고 하는데 이 부분은 뒤에서 한 번 다시 설명할 것
      
              // 출력해보기
              p2.printInfo();
      
          }
      
      }


좋은 웹페이지 즐겨찾기