상속
- 부모가 가진것을 자식에게 물려주는 것 = 부모가 가지고 있는 것을 자식이 사용할 수 있게 됨
- 노트북은 컴퓨터의 한 종류다.
- 침대는 가구의 한 종류다. (= 침대는 가구다.)
- 소방차는 자동차다.
- 이렇게 말할 수 있는 관계를 is a 관계 혹은 kind of 관계라고 함
Java 상속의 특징
- 클래스의 다중 상속(multiple inheritance)를 지원하지 않음
- extends 다음엔은 클래스명을 하나만 지정 가능
- 다중 상속: 클래스를 여러 개 상속받음 (extends 다음에 클래스명이 여러 개 옴)
- 클래스에서 다중 상속이 불가능하기 때문에 대신 인터페이스에서 지원
- 상속의 횟수에 제한이 없음
- 계층 구조의 최상위에 java.lang.Object 클래스가 있음
- 사용자가 만들든 자바패키지에서 제공하든, 자바에서 모든 클래스는 Object 클래스를 자동으로 상속받도록 컴파일됨 (다른 클래스를 상속받는다고 선언하지 않아도 컴파일이 끝나면 Object를 상속받게 됨)
- Object 클래스만이 유일하게 클래스를 가지지 않음
Car를 상속받은 Bus를 class로 표현하는 방법
- 클래스를 생성할 때 Superclass를 Browse해서 상속받고 싶은 클래스를 선택
public class Car{
}
public class Bus extends Car{
}
- public class 클래스명 extends 부모클래스명
- 자식클래스 이름 뒤에 extends 키워드를 적고 부모클래스 이름을 적으면, 부모 클래스가 가지고 있는 것을 자식클래스가 상속받음
부모클래스에 메소드 추가하기
- Car클래스에 run( )메소드 추가
public class Car{
public void run(){
System.out.println("달리다.");
}
}
- Car를 상속받은 Bus클래스 사용
- Bus클래스엔 아무 메소드도 없지만 Car클래스를 상속받았기 때문에 run메소드를 사용할 수 있음
public class BusExam{
public static void main(String args[]){
Bus bus = new Bus();
bus.run();
*//Bus class 는 아무런 코드를 가지지 않음
//그럼에도 run 이라는 메소드를 사용하는 데에 문제가 발생되지 않음*
}
}
- Bus클래스에 메소드 추가
public class Bus extends Car{
public void ppangppang(){
System.out.println("빵빵");
}
}
- Bus클래스는 Car클래스에서 물려받은 run메소드와 자기 자신의 ppangppang메소드를 사용할 수 있게 됨
- 부모가 가지고 있는 메소드 외에 추가로 메소드를 선언하는 것을 확장했다고 표현
부모클래스는 자식클래스가 갖고 있는 걸 사용할 수 없음
public class BusExam {
public static void main(String[] args) {
Car car = new Car();
car.run();
Car car = new Car();
car.run();
car.ppangppang(); //오류 발생
//부모클래스는 자식클래스가 갖고 있는 걸 사용할 수 없음
}
}
실습
- Car클래스
class Car extends Machine {
}
- Machine클래스
class Machine {
public void turnOn() {
System.out.println("켰습니다.");
}
public void turnOff() {
System.out.println("껐습니다.");
}
}
접근제한자 (= 접근제어자, 접근지정자)
클래스 내에서 멤버의 접근을 제한하는 역할을 함
캡슐화
- 관련된 내용을 모아서 가지고 있는 것
- 은닉화를 전제로 함
- 관련 있는 데이터(필드)와 동작(메소드)들을 하나로 묶어 요약하고 사용자에게는 내부적인 접근을 허용하지 않는 대신 사용의 편의성을 제공해줌
- 장점
- 데이터 보호
- 데이터의 접근을 제어할 수 있음
- 유지보수 편리
- 각 기능을 하나의 모듈(부품)처럼 사용해 객체 간의 이식성이 높고 독립적인 면을 유지함
- 사용자 편의성
- 원리를 알 필요 없이 사용법만 익히면 됨
- 데이터 보호
접근제한자의 종류
- public > protected > default > private
자식클래스
접근제한자 | 같은 클래스 | 같은 패키지 | 자식클래스 | 다른 패키지 |
public | O | O | O | O |
protected | O | O | O (다른 패키지여도) | |
default | O | O | ||
private | O |
- public
- 어떤 클래스에서든 접근할 수 있다
- protected
- 자기 자신(같은 클래스), 같은 패키지, 서로 다른 패키지다 하더라도 자기 자신을 상속받은 자식클래스에서는 접근할 수 있다
- default
- 접근제한자를 적지 않으면 default
- 자기자신과 같은 패키지에서만 접근할 수 있다
- private
- 자기 자신(같은 클래스)만 접근할 수 있다
public class AccessObj{
//public: 어떤 클래스에서든 접근 가능
public int pub = 3;
//protected: 자기 자신, 같은 패키지, 다른 패키지더라도 상속받은 클래스면 접근 가능
protected int prot = 4;
//default: 같은 클래스, 같은 패키지에서만 접근 가능
int def = 2;
//private: 같은 클래스에서만 접근 가능
private int priv = 1;
}
접근제한자 사용
public | protected | default | private | |
클래스 | O | O | ||
생성자 | O | O | O | O |
멤버변수 | O | O | O | O |
멤버메소드 | O | O | O | O |
지역변수 |
- 클래스: public, default
- 생성자: public, protected, default, private
- 멤버변수: public, protected, default, private
- 멤버메소드: public, protected, default, private
- 지역변수: 접근제한자 사용 불가능
접근제한자의 사용 예시
- AccessObj를 사용하는 AccessObjExam
- AccessObj의 필드 priv의 접근 제한자는 private이므로 다른 클래스인 AccessObjExam에서 접근할 수 없음
public class AccessObjExam{
public static void main(String args[]){
AccessObj obj = new AccessObj();
System.out.println(obj.pub);
** System.out.println(obj.prot);
System.out.println(obj.def);
//System.out.println(obj.priv);
*//private는 다른 클래스에서 접근 불가능하기 때문에컴파일 오류 발생*
}
}
- AccessObj 와 다른 패키지에서 사용해보기
- 패키지가 달라졌기 때문에 default접근제한자로 지정된 필드 def와 protected 접근제한자로 지정된 필드 prot도 접근할 수 없음
public class AccessObjExam{
public static void main(String args[]){
AccessObj po = new AccessObj();
System.out.println(po.pub);
** System.lout.println(po.prot); *//protected 접근제한자: 컴파일 오류 발생*
System.lout.println(po.def); *//default 접근제한자: 컴파일 오류 발생*
System.lout.println(po.priv); *//private 접근제한자: 컴파일 오류 발생*
}
- AccesObj로부터 AccessObjExam을 상속받도록 수정(+객체 생성도 AccessObj가 아니라 AccessObjExam으로 함)한 후 사용해보기
- 패키지는 다르지만 상속관계에 있으므로 protected 접근제한자로 지정된 필드 prot에 접근할 수 있음
public class AccessObjExam extends AccessObj{
public static void main(String[] args) {
AccessObjExam obj = new AccessObjExam();
System.out.println(obj.pub);
System.out.println(obj.prot);
System.out.println(obj.def); *//default 접근제한자: 컴파일 오류 발생*
System.out.println(obj.priv); *//private 접근제한자: 컴파일 오류 발생*
}
}
실습 1
- Car클래스
public class Car {
public String name;
public int number;
public Car(String name, int number) {
this.name = name;
this.number = number;
}
}
- CarExam클래스
public class CarExam {
public static void main(String[] args) {
Car car = new Car("포니", 1234);
System.out.println("name: " + car.name);
System.out.println("number: " + car.number);
}
}
실습 2
- Car클래스
public class Car{
public void run(){
System.out.println("차가 달립니다.");
}
public void stop(){
System.out.println("차가 멈춥니다.");
}
}
- CarExam클래스
public class CarExam{
public static void main(String[]args){
Car car = new Car();
car.run();
car.stop();
}
}
추상클래스
구체적이지 않은 클래스
- 독수리, 타조는 구체적인 새를 지칭하지만 새, 포유류 같은 것은 구체적이지 않음
추상클래스의 목적
- 추상클래스를 상속받은 자식클래스에서 오버라이딩해 사용하게 함
- 부모클래스에서는 추상메소드로 메소드의 껍데기만 만들어놓고, 실제 내용은 각각의 자식클래스에서 채워넣는 방식
추상클래스 정의하기
- 클래스 앞에 abstract 키워드를 붙여서 정의
접근제한자 abstract class 클래스명{ //필드 및 메소드 정의 }
- 일반 클래스와 객체를 생성할 수 없음(인스턴스화 불가능)
- But, 부모클래스는 될 수 있음
- 자식클래스를 만들 때 추상클래스를 부모클래스로 사용해 자식클래스에 공통점을 넣을 수 있음
- 추상클래스는 추상 메소드를 선언하고 상속을 통해 자식클래스에서 완성하도록 유도하는 클래스 (미완성 설계도라고도 함)
- 추상클래스 내부엔 필드, 일반 메소드, 추상 메소드 모두 존재 가능
- 추상 메소드 구현 가능
- 추상 메소드: 내용이 없는 껍데기 메소드 = 구현이 되지 않은 메소드
접근제한자 abstract 리턴타입 메소드명(매개변수);
- 추상 메소드는 리턴 타입 앞에 abstract라는 키워드를 붙여야 함
- 메소드가 하나라도 추상메소드인 경우 해당 클래스는 추상클래스가 됨
- 일반 메소드 구현 가능
- 추상 메소드 구현 가능
public abstract class Bird{
public abstract void sing(); //추상메소드
public void fly(){ //일반 메소드
System.out.println("날다.");
}
}
추상 클래스를 상속받는 클래스 생성하기
- 추상 클래스를 상속받은 클래스는 추상 클래스가 갖고 있는 추상 메소드를 반드시 구현해야 함
- 추상 클래스를 상속받았는데 추상 클래스가 갖고 있는 추상 메소드를 구현하지 않으면 해당 클래스도 추상 클래스가 됨
public class Duck extends Bird {
@Override
public void Sing() {
}
}
- Duck클래스를 만들 때 Superclass를 Bird로 설정하면 위의 형태가 자동 완성됨
public class Duck extends Bird{
@Override
public void sing() {
System.out.println("꽥꽥");
}
}
- Duck클래스에 Bird의 추상 메소드 sing을 구현한 모습
사용하기
- Bird는 추상클래스이므로 객체가 될 수 없음
public class DuckExam {
public static void main(String[] args) {
Duck duck = new Duck();
duck.sing(); //Duck클래스의 sing메소드 실행 > 결과: 꽥꽥
duck.fly(); //Bird클래스의 fly메소드 실행 > 결과: 날다.
*//Bird b = new Bird(); //*Bird는 추상 클래스이므로 객체가 될 수 없음
**}
}
실습
- Car클래스
class Car extends Machine {
// Machine 클래스를 상속받고, 추상 메소드를 구현하세요.
public void turnOn(){
}
public void turnOff(){
}
}
- Machine클래스
public abstract class Machine {
public abstract void turnOn();
public abstract void turnOff();
}
- CarExam클래스
//아래는 실행을 위한 코드입니다. 수정하지 마세요.
public class CarExam {
public static void main(String[] args) {
Car car = new Car();
if(Machine.class.isInstance(car)){
System.out.println("정답입니다. [제출]을 누르세요.");
}
else{
System.out.println("Car가 Machine을 상속받지 않았습니다.");
}
}
}
super와 부모생성자
- class가 인스턴스화될 때 생성자가 실행되면서 객체를 초기화함
- 그때 부모의 생성자부터 실행된 후에 자신의 생성자가 실행됨
부모 생성자
public class Car{
public Car(){
System.out.println("Car의 기본생성자입니다.");
}
}
public class Truck extends Car{
public Truck(){
System.out.println("Truck의 기본생성자입니다.");
}
}
- 생성자 테스트
public class TruckExam{
public static void main(String args[]){
Truck t = new Truck();
}
}
- 결과
- Car의 기본생성자입니다.
- Truck의 기본생성자입니다.
- new 연산자로 Truck객체를 생성(Truck클래스가 인스턴스화)하면
- 부모인 Car객체가 먼저 메모리에 올라가고 그 후에 Truck객체가 올라감
- 객체가 생성될 때 반드시 생성자를 실행
- 생성자는 객체를 초기화하는 일을 함
- 생성자가 호출될 때 자동으로 부모의 생성자가 자식의 생성자보다 먼저 호출되면서 부모 객체를 초기화함
- 생성자는 객체를 초기화하는 일을 함
super
- 자신을 가리키는 키워드가 this라면, 부모 객체를 가리키는 키워드는 super
- super( ): 부모의 생성자
- 부모의 생성자를 임의로 호출하지 않으면, 부모 class의 기본 생성자가 자동으로 호출됨 (작성하지 않아도 컴파일 단계에서 자동 호출)
- 아래 예제처럼 호출해보면 위에서 super( )를 호출하지 않을 때와 결과가 같음
- 부모클래스에 기본생성자가 있을 때는 super( )를 호출하지 않아도 무방
public Bus(){
super();
System.out.println("Bus의 기본생성자입니다.");
}
- super는 생성자에서 가장 먼저 나와야 함 (다른 것보다 늦게 나오면 오류 발생)
부모의 기본생성자가 아닌 다른 생성자를 호출하는 방법
- 클래스는 기본 생성자가 없는 경우도 존재
public class Car{
public Car(String name){
System.out.println(name + " 을 받아들이는 생성자입니다.");
}
}
- Car class가 위처럼 수정되면, Bus생성자에서 컴파일 오류가 발생
- 부모가 기본생성자가 없기 때문에 컴파일 오류가 발생(컴파일러는 기본생성자만 자동 호출)
- 문제 해결 방법
- 자식클래스의 생성자 안에서 부모클래스의 생성자를 직접 호출해야 함
public Bus(){
super("소방차"); *// 문자열을 매개변수로 받는 부모 생성자를 호출*
System.out.println("Bus의 기본생성자입니다.");
}
super 키워드는 자식클래스에서 부모클래스의 메소드나 필드를 사용할 때도 사용
- 부모클래스의 메소드를 사용할 때: super.부모클래스의메소드명( );
실습
- Bus클래스
public class Bus extends Car {
int fee;
public Bus(String name, int number, int fee) {
//super를 이용해서 Car클래스의 생성자를 이용하세요.
super(name, number);
this.fee = fee;
}
}
- Car클래스
public class Car {
String name;
int number;
public Car(String name, int number) {
this.name = name;
this.number = number;
}
}
- CarExam클래스
//아래는 실행을 위한 코드입니다. 수정하지 마세요.
public class BusExam {
public static void main(String[] args) {
Bus bus = new Bus(new String("뛰뛰"), 3000, 1050);
if(!bus.name.equals("뛰뛰")){
System.out.println("bus의 name이 다릅니다.");
}
else if(bus.number != 3000){
System.out.println("bus의 number가 다릅니다.");
}
else if(bus.fee != 1050){
System.out.println("bus의 fee가 다릅니다.");
}
else{
System.out.println("정답입니다. [제출]을 누르세요.");
}
}
}
오버라이딩
부모클래스의 메소드를 자식클래스에서 재정의해 사용하는 것
메소드 오버라이딩
- Car클래스를 상속받은 Bus클래스는 부모클래스가 가지고 있는 run( ) 메소드를 사용
//run메소드를 가지고 있는 Car클래스
public class Car{
public void run(){
System.out.println("Car의 run메소드");
}
}
//Car클래스를 상속받는 Bus클래스
public class Bus extends Car{
}
public class BusExam{
public static void main(String args[]){
Bus bus = new Bus();
bus.run(); //Car클래스의 run메소드가 실행됨
}
}
- Bus클래스에 부모가 가지고 있는 메소드를 재정의해 사용
public class Bus extends Car{
public void run(){
System.out.println("Bus의 run메소드");
}
}
public class BusExam{
public static void main(String args[]){
Bus bus = new Bus();
bus.run(); //Bus클래스의 run메소드가 실행됨
}
}
- BusExam 실행 결과: "Bus의 run메소드"가 출력됨
- 메소드를 오버라이딩하면, 항상 자식클래스에서 정의된 메소드가 호출됨
- 오버라이딩한다고 해서 부모의 메소드가 사라지는 것은 아님
- super 키워드를 이용하면, 자식클래스에서 부모클래스의 메소드를 호출할 수 있음
public class Bus extends Car{
public void run(){
super.run(); *//부모클래스인 Car클래스의 run()메소드 호출*
System.out.println("Bus의 run메소드");
}
}
메소드 오버라이딩의 제한
- 메소드 앞에 final 키워드를 붙이면, 부모클래스에서 메소드 오버라이딩을 막을 수 있음
- 이렇게 하면 해당 메소드는 자식클래스에서 오버라이딩할 수 없고 부모클래스에서 정의한 대로만 사용해야 함
- final을 붙이면 마지막 메소드가 되니까 상속이 불가능한 것
실습 1
- Bus클래스
public class Bus extends Car {
// run 메소드를 오버라이드 하세요. 메소드의 접근 제한자는 public이어야 합니다.
public void run(){
System.out.println("차가 달리면서 다음 정거장을 안내합니다.");
}
}
- Car클래스
public class Car {
public void run() {
System.out.println("차가 달립니다.");
}
public void stop() {
System.out.println("차가 멈춥니다.");
}
public void horn() {
System.out.println("경적을 울립니다.");
}
}
- BusExam클래스
//다음은 실행을 위한 코드입니다. 수정하지 마세요.
class BusExam {
public static void main(String [] args) {
Bus bus = new Bus();
bus.run();
}
}
실습 2
- Bus클래스
public class Bus extends Car {
public void run(){
// Car 클래스의 run을 호출합니다.
super.run();
System.out.println("다음 정거장을 안내합니다.");
}
}
- Car클래스
public class Car {
public void run() {
System.out.println("차가 달립니다.");
}
public void stop() {
System.out.println("차가 멈춥니다.");
}
public void horn() {
System.out.println("경적을 울립니다.");
}
}
- BusExam클래스
public class BusExam {
public static void main(String [] args) {
Bus bus = new Bus();
bus.run();
}
}
*형변환에 대한 강의자료가 부족해서 <명품 Java Programming>, 교재와 기술블로그를 참고해 내용을 보충했습니다.
클래스 형변환
- 부모타입으로 자식객체를 참조하면 부모가 가지고 있는 메소드만 사용할 수 있음
- 자식객체가 가지고 있는 메소드나 속성을 사용하고 싶다면 형변환을 해야 함
형변환
- 클래스의 형변환은 서로 상속관계에 있는 객체 사이에서만 가능
public class Car{
public void run(){
System.out.println("Car의 run메소드");
}
}
public class Bus extends Car{
public void ppangppang(){
System.out.println("빵빵.");
}
}
- 상속관계란 is a 관계
- “Bus는 Car다." 라는 관계가 성립
- 현실에서도 버스를 가리키면서 ‘차다.’라고 말함
업캐스팅(Upcasting) - 자식클래스를 부모클래스로 형변환
- 부모타입으로 자식타입의 객체를 참조할 때는 묵시적으로 형변환이 일어남
- 자식클래스를 부모클래스로 만든 것 > 부모가 가지고 있는 메소드만 사용 가능, 자식클래스의 메소드 사용 불가능
- 자식클래스 타입 객체를 부모클래스 타입의 참조변수에 저장 = 부모클래스 타입의 참조변수로 자식클래스의 객체를 참조(가리킴)
- 문법
-
- 부모클래스 참조변수 = new 자식클래스( );
- Car car = new Bus( );
- 부모클래스 참조변수; 자식클래스 참조변수 = new 자식클래스( ); 부모클래스의참조변수 = 자식클래스의참조변수;
- Car car; Bus bus = new Bus( ); car = bus;
-
public class BusExam{
public static void main(String args[]){
Car car = new Bus();
//Car클래스 타입의 레퍼런스 car가 Bus클래스 객체 bus를 가리키도록 치환
car.run();
car.ppangppang(); *//ppangppang()메소드는 자식의 메소드라서 컴파일 오류 발생
//자식클래스를 부모클래스로 형변환하면 부모의 메소드만 사용 가능*
}
}
다운캐스팅(Downcasting) - 부모클래스를 자식클래스로 형변환
- 자식타입으로 부모타입의 객체를 참조할 때는 명시적으로 형변환을 해줘야 함
- 부모가 참조하는 인스턴스가 형변환 하려는 자식타입일 때만 가능
- 부모클래스를 자식클래스로 만든 것 > 자식클래스의 메소드도 사용 가능하게 됨
- 자식클래스 타입의 참조변수로 부모클래스의 객체를 참조(가리킴)
- 자식클래스 참조변수 = (자식클래스)부모클래스의참조변수;
- 예시
- ppangppang( ) 메소드를 호출하고 싶다면 Bus타입의 참조변수로 참조해야 함
public class BusExam{
public static void main(String args[]){
Car car = new Bus(); *//자식타입을 부모타입으로 형변환 (업캐스팅)*
car.run();
*//car.ppangppang();
//부모의 메소드만 사용 가능한데 ppangppang 메소드는 자식의 메소드이므로 컴파일 오류 발생*
Bus bus = (Bus)car; *//부모타입을 자식타입으로 형변환 (다운캐스팅)*
bus.run();
bus.ppangppang();
}
}
- 객체(클래스)들끼리도 형변환이 가능 - 상속관계일 때만
- 부모클래스가 자식클래스보다 더 큰 그릇(개념적으로)이니까 명시적으로 형변환해줘야 함
- Bus bus = (Bus)car;
-
- Car타입의 참조변수 car가 참조하는 것을 Bus타입으로 변환
- Bus타입의 참조변수 bus가 변환된 참조변수 car를 참조
- Car클래스를 인스턴스화할 때 Car타입의 참조변수 car가 참조하는 객체가 원래 Bus였기 때문에 참조변수 car의 타입을 Car에서 Bus로 바꾸는 게 가능
- Car타입의 참조변수 car가 참조하는 객체가 그냥 Car였다면 (Car car = new Car( )였다면)
- javaStudy.Car cannot be cast to javaStudy.Bus 오류 발생
-
업캐스팅과 instanceof 연산자
- 업캐스팅한 경우 참조변수가 가리키는 객체의 진짜 클래스 타입을 구분하기 어려움
Person p = new Person();
Person p = new Student(); //업캐스팅
Person p = new Researcher(); //업캐스팅
Person p = new Professor(); //업캐스팅
void print(Person person){
//person이 가리키는 객체가 Person 타입일 수도 있고,
//Student, Researcher, 혹은 Professor 타입일 수 있음
}
public static void main(String[] args){
...
print(p);
}
- Person 타입의 참조변수(레퍼런스) p가 Person 객체, Student 객체, Researcher 객체, Professor 객체를 가리키는지 알 수 없음
- print(Person person) 메소드가 호출되면, 해당 메소드는 매개변수로 전달받은 person에 어떤 클래스의 객체가 전달되어 왔는지 알 수 없음
- Person 타입의 객체일 수도 있고 Student, Researcher, Professor 타입의 객체일 수도 있음
- 오직 아는 것은 Person을 상속받은 객체가 업캐스팅되어 넘어왔다는 사실
- print(Person person) 메소드에서는 매개변수 person에 전달된 객체가 어떤 클래스의 객체인지 구별할 방법이 필요함
- print(Person person) 메소드가 호출되면, 해당 메소드는 매개변수로 전달받은 person에 어떤 클래스의 객체가 전달되어 왔는지 알 수 없음
instanceof 연산자 사용
- instanceof 연산자: 참조변수가 가리키는 객체가 어떤 클래스 타입인지 구분하기 위해 사용하는 이항 연산자
- 문법: 참조변수 instanceof 클래스명
- 결과값: boolean
- 참조변수가 가리키는 객체가 해당 클래스 타입의 객체면 true, 아니면 false
Person jee = new Student();
Person kim = new Professor();
Person lee = new Researcher();
if(jee instanceof Person) //jee는 Person 타입이므로 true
if(jee instanceof Student) //jee는 Student 타입이므로 true
if(kim instanceof Student) //jee는 Student 타입이 아니므로 false
if(kim instanceof Professor) //jee는 Professor 타입이므로 true
if(kim instanceof Researcher) //jee는 Researcher 타입이므로 true
if(lee instanceof Professor) //jee는 Professor 타입이 아니므로 false
if("java" instanceof String) //java 문자열은 String 타입이므로 true
- instanceof는 클래스에만 적용되므로 아래의 코드는 오류
if(3 instanceof int) //int는 클래스가 아니라 데이터타입
getClass( )와 getName( ) (참고)
- getClass( )
- Java 최상위 클래스인 Object클래스의 메소드 중 하나
- 객체의 클래스 정보를 알 수 있는 메소드 (컴파일 타임에 결정됨)
- 해당 정보를 Class 타입의 객체로 반환
- 반환된 Class 타입의 객체는 클래스 정보에 접근할 수 있는 메소드를 가짐
- getName( ) : 클래스의 이름을 리턴하는 메소드
- getSuperclass( ) : 부모클래스의 이름을 리턴하는 메소드
- getDeclaredFields( ) : 클래스에 선언되어 있는 멤버변수 이름을 배열로 리턴하는 메소드
- getDeclaredMethods( ) : 클래스에 선언되어 있는 멤버함수 이름을 배열로 리턴하는 메소드
- 참조변수.getClass().getName( )
- 참조변수가 실제로 어떤 클래스 타입인지 알려줌
- 작동 원리
- 선언된 객체에서 getClass() 메서드를 호출 후 getName() 메서드를 호출
- = getClass( ) 메서드 반환 결과를 Class 객체에 할당 후 getName( ) 메서드를 호출
public class GasStation{
public static void main(String[]args){
GasStation gasStation = new GasStation(); //gasStation인스턴스 생성
Suv suv = new Suv(); //suv 인스턴스 생성
Truck truck = new Truck(); //truck 인스턴스 생성
Bus bus = new Bus(); //bus 인스턴스 생성
//gasStation에서 suv에 기름을 넣습니다.
gasStation.fill(suv);
//gasStation에서 truck에 기름을 넣습니다.
gasStation.fill(truck);
//gasStation에서 bus에 기름을 넣습니다.
gasStation.fill(bus);
}
public void fill(Car car){
//참고. car.getClass().getName()은 car오브젝트가 실제로 어떤 클래스인지를 알려줍니다.
System.out.println(car.getClass().getName()+"에 기름을 넣습니다.");
car.gas += 10;
System.out.println("기름이 "+car.gas+"리터 들어있습니다.");
}
}
- 결과
- Suv에 기름을 넣습니다.
기름이 10리터 들어있습니다.
Truck에 기름을 넣습니다.
기름이 10리터 들어있습니다.
Bus에 기름을 넣습니다.
기름이 10리터 들어있습니다.
- Suv에 기름을 넣습니다.
실습 1
- 문제 설명
- Car클래스는 gas라는 필드를 가집니다. 그리고 Car클래스를 상속받은 Suv, Truck, Bus클래스가 있습니다. GasStation클래스에서는 fill(주유하다)라는 메소드가 있는데요. 총3종류의 fill메소드가 오버로딩 되어 있습니다. 각각 Suv, Truck, Bus의 객체를 매개변수로 받아서 주유를 하는 메소드인데요. Car, Suv, Truck, Bus클래스를 한 번씩 살펴보고 GasStation의 코드를 살펴본 다음 제출을 눌러서 결과를 확인하세요.
- Car클래스
public class Car{
public int gas;
}
- Suv클래스
public class Suv extends Car{
}
- Truck클래스
public class Truck extends Car{
}
- Bus클래스
public class Bus extends Car{
}
- GasStation클래스
public class GasStation{
public static void main(String[]args){
GasStation gasStation = new GasStation(); //gasStation인스턴스 생성
Suv suv = new Suv(); //suv 인스턴스 생성
Truck truck = new Truck(); //truck 인스턴스 생성
Bus bus = new Bus(); //bus 인스턴스 생성
//gasStation에서 suv에 기름을 넣습니다.
gasStation.fill(suv);
//gasStation에서 truck에 기름을 넣습니다.
gasStation.fill(truck);
//gasStation에서 bus에 기름을 넣습니다.
gasStation.fill(bus);
}
public void fill(Suv suv){
System.out.println("Suv에 기름을 넣습니다.");
suv.gas += 10;
System.out.println("기름이 "+suv.gas+"리터 들어있습니다.");
}
public void fill(Truck truck){
System.out.println("Truck에 기름을 넣습니다.");
truck.gas += 10;
System.out.println("기름이 "+truck.gas+"리터 들어있습니다.");
}
public void fill(Bus bus){
System.out.println("Bus에 기름을 넣습니다.");
bus.gas += 10;
System.out.println("기름이 "+bus.gas+"리터 들어있습니다.");
}
}
실습 2
- 문제 설명
- GetStation클래스에 정의되어 있는 3개의 fill메소드를 지우고, 가장 아래에 주석처리 되어 있는 fill메소드의 주석을 해제한 다음 실행을 눌러보세요. 이제 하나의 fill메소드로 같은 동작을 할 수 있습니다.
- GasStation코드를 살펴보면 3개의 fill메소드가 있습니다. 매개변수로 받아들이는 3종류의 다른 차량에 대해서 기름을 넣어주는 동작을 하는것 뿐인데 3개의 중복된 코드가 들어있어서 비효율적이지요. Car클래스에 있는 gas라는 속성을 공통적으로 사용하므로 이럴 경우 fill메소드의 매개변수를 Car로 하면 됩니다. 그러면 Suv, Truck, Bus클래스가 Car클래스로 형변환 되므로 하나의 fill메소드로도 같은 동작을 할 수 있습니다.
- Car클래스
public class Car{
public int gas;
}
- Suv클래스
public class Suv extends Car{
}
- Truck클래스
public class Truck extends Car{
}
- Bus클래스
public class Bus extends Car{
}
- GasStation클래스
public class GasStation{
public static void main(String[]args){
GasStation gasStation = new GasStation(); //gasStation인스턴스 생성
Suv suv = new Suv(); //suv 인스턴스 생성
Truck truck = new Truck(); //truck 인스턴스 생성
Bus bus = new Bus(); //bus 인스턴스 생성
//gasStation에서 suv에 기름을 넣습니다.
gasStation.fill(suv);
//gasStation에서 truck에 기름을 넣습니다.
gasStation.fill(truck);
//gasStation에서 bus에 기름을 넣습니다.
gasStation.fill(bus);
}
// public void fill(Suv suv){
// System.out.println("Suv에 기름을 넣습니다.");
// suv.gas += 10;
// System.out.println("기름이 "+suv.gas+"리터 들어있습니다.");
// }
// public void fill(Truck truck){
// System.out.println("Truck에 기름을 넣습니다.");
// truck.gas += 10;
// System.out.println("기름이 "+truck.gas+"리터 들어있습니다.");
// }
// public void fill(Bus bus){
// System.out.println("Bus에 기름을 넣습니다.");
// bus.gas += 10;
// System.out.println("기름이 "+bus.gas+"리터 들어있습니다.");
// }
public void fill(Car car){
//참고. car.getClass().getName()은 car오브젝트가 실제로 어떤 클래스인지를 알려줍니다.
System.out.println(car.getClass().getName()+"에 기름을 넣습니다.");
car.gas += 10;
System.out.println("기름이 "+car.gas+"리터 들어있습니다.");
}
}
'☕ Java 웹 프로그래밍 > Java' 카테고리의 다른 글
[프로그래머스] Java(자바) 입문 | Part 9. 예외처리 (0) | 2023.05.09 |
---|---|
[프로그래머스] Java(자바) 입문 | Part 8. 인터페이스와 다른 형식의 클래스 (0) | 2023.05.06 |
[프로그래머스] Java(자바) 입문 | Part 6. 클래스 다듬기 (0) | 2023.05.03 |
[프로그래머스] Java(자바) 입문 | Part 5. 클래스와 객체 (0) | 2023.05.02 |
Java | 클래스, 객체, 인스턴스, 인스턴스화 (0) | 2023.05.01 |