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 만들기
-
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로 생성되나 생략이 가능함.
- 생성자의 경우
-
-
생성자 직접 만들어서 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 사용해보기(= 객체 만들어서 사용해보기)
-
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(); } }
-
-
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 over와 step into가 있는데
- 여기서 잠시 콘솔창을 보면 아래 사진과 같이 해당 부분까지만 출력되어 있는 것을 확인 할 수 있다.
- step into(f7)클릭, 과정을 보려고 계속 step into(f7)를 클릭
- 따라서 객체가 생성될 때 이 생성자 부분이 호출되는 것을 코드를 통해서 확인했음
- 디버그로 생성자 절차 확인하기
-
생성자(Constructor)
- 객체가 생성될 때 자동으로 호출되는 부분.
- new 클래스명으로 객체가 새로 만들어질 때 자동으로 호출되는 부분
- 생성자의 규칙
- 생성자명 == 클래스명
- 리턴 타입 없음
- void를 쓰는것이 아닌 아예 비워두어야 함
- 생성자는 객체 생성시 받아오는 데이터값을 바로 객체 변수에 할당할 때 사용하는 것 같다.
this, 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로 바꿔서 타입을 바꿔준다거나, 아니면 매개변수 갯수를 늘려준다거나 하는 식으로 만드는것
- sum()인데 ()안에 int, string등으로 다르게 하고 sum은 같게 하는 것
- 기존 클래스에서 차량 정보를 출력해주는 메소드가 있지만
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
- 모두 접근이 가능해서 잘 출력되는것을 확인할 수 있음
- 외부 패키지내 클래스 사용하는 것이므로 import해줘야 함
- MyPrac(main)에서 만들어논 외부 패키지내 클래스(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를 상속받은 자식 클래스로 접근할 경우 접근이 가능해짐
- MyPrac(main)에서 만들어논 외부 패키지내 클래스(Car2)사용해보기
- 일단 생성자도 하나 만들고, public사용해보기
Static
-
변수나 메소드의 특성을 바꾸는 키워드
- 어떻게 바꾸냐?
- static이 붙어있는 변수나 메소드는 객체가 생성되기 전부터 메모리에 한 번 잡혀서 상주하게 됨.
- Static 클래스 변수
- 해당 클래스의 각 객체들이 값을 공유
- Static 클래스 메소드
- 객체를 생성하지 않아도 호출 가능
- Static 클래스 변수
- static이 붙어있는 변수나 메소드는 객체가 생성되기 전부터 메모리에 한 번 잡혀서 상주하게 됨.
- 특징
- 일단 메모리에 한 번만 할당됨
- 이렇게 미리 할당되서 항상 상주되어 있기에 Static 변수나 메소드는 서로 공유되는 특성이 있음
- 일단 메모리에 한 번만 할당됨
- 어떻게 바꾸냐?
-
Static 이용해보기
- 빨간 밑줄이 쳐짐
- 여기서 getName메소드도 Static으로 바꿔주면 해결됨
- 객체 생성을 하지 않았는데도 바로 클래스명을 통해 해당 메소드를 호출할 수 있는 경우!! ⇒ 완전 김띠용임 ⇒ Static을 이용하면!!
- 여기서 getName메소드도 Static으로 바꿔주면 해결됨
- Static메소드로 되어있는 경우
- Static 메소드 안에서 사용할 수 있는 변수도 Static이어야 한다.
- Static이 붙으면 객체가 생성되기 전부터 메모리에 자동으로 올라감으로 당연히 Static 메소드 안에서 사용할 수 있는 변수도 미리 만들어줘야 함.
- 따라서 name 변수는 사용가능하나 type변수는 사용 불가능함.
- Static 메소드 안에서 사용할 수 있는 변수도 Static이어야 한다.
- 변수가 스태틱으로 잡혀 있으면 객체끼리 이 변수를 공유함. 그래서 이 특성으로 인해 다른 객체에서 이 데이터를 바꿔주면 마지막에 바꾼 데이터로 공유되어 출력이 되서 발생되는 현상
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 변수를 수정하면 마지막에 수정된 데이터로 할당되면서 수정된 데이터로 모든 객체에 공유되어 출력됨.
- 따라서 객체생성시 객체들끼리 이 Static name 변수를 공유함.
- 즉, name변수는 Static으로 잡혀 있기에 객체 생성전에 이미 메모리에 올라가 있어 서로 공유될 수 있음.
- 빨간 밑줄이 쳐짐
상속
- 헷갈리는 상속해서? 상속 받아서?
- 부모 클래스를 상속해서
- 자식 클래스가 상속 받아
- 부모 클래스 == 상위 클래스, 기초클래스
- 상속의 대상
- 자식 클래스 == 하위 클래스, 파생 클래스
- 부모 클래스를 상속해서 새롭게 만드는 클래스
- 부모 클래스의 필드와 멤버 변수나 메소드가 상속됨
- 단 생성자, 초기화 블록은 상속되지 않음
- 자식클래스가 여러 부모클래스로부터 상속받을 수 없다!
- 다중 상속 불가능
- 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() {
- 정수형으로 출력
- Plus() ⇒ a + b;
- 더블형으로 더하기
- double Plus() {
return a + b;
}
- double Plus() {
- 더블형으로 출력
- 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(); } }
- name을 초기화 해줄때 부모쪽으로 name을 넘겨서 초기화되어 있고, 자기 객체인 즉, 자식 클래스 Student2 의 객체변수 name에는 아무 값이 할당되어 있지 않기에
- name이 null이 나온 이유는?
다형성
-
다형성 구조
부모클래스타입 부모클래스 이용하여 만든객체명 = 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; } } }
- instanceof를 통해서 우리는 다형성을 쓸 수 있는지 없는지를 미리 확인할 수 있음.
추상 메소드
- 자식클래스에서 반드시 오버라이딩 해야하는 메소드를 지정해줄 수 있는 기능
- 선언만 하고 기능 구현 내용이 없다.
추상 클래스
- 추상 메소드를 하나 이상 포함하는 클래스
- 클래스 {}안 멤버들 중에 적어도 하나는
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(); } }
-
Author And Source
이 문제에 관하여(TIL(22.4.01.FRI)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jr28333/TIL22.4.01.FRI저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)