관리 메뉴

개발 노트

7/25 Communication Cost, Debug, 상속, OverRiding, 인터페이스, implements, 추상 메소드 본문

프로젝트 기반 JAVA 응용 SW개발 : 22.07.19~23.01.20/JAVA

7/25 Communication Cost, Debug, 상속, OverRiding, 인터페이스, implements, 추상 메소드

hayoung.dev 2022. 7. 25. 20:08

[Communication Cost]

커뮤니케이션 비용 = N * (N-1) / 2

N은 사람수.

 

[Debug하는 방법]

1. Debug하려는 줄 > 오른쪽 마우스 >

를 하면 오른쪽에 표시가 된다.

 

함수 안으로 들어가는 것
다음줄
다시 나오는 것.

많이 사용하진 않지만 시스템 내부 메모리 구조를 보고 싶을 때 가끔씩 사용. 시스템이 복잡해져서 내가 메모리를 잘 찾지 못할 때 사용.

 

 

[7. 자바 상속-중첩 Class]

p.2 상속의 정의와 장점

- 기존의 클래스를 재사용해서 새로운 클래스를 작성

- 두 클래스를 조상과 자손으로 관계를 맺어주는 것

- 자손은 조상의 모든 멤버를 상속받는다. (생성자, 초기화블럭 제외)

- 자손의 멤버개수는 조상보다 적을 수 없다. (같거나 많다.)

 

p.3 클래스간의 관계 - 상속관계

- 공통부분은 조상에서 관리하고 개별부분은 자손에서 관리

- 조상의 변경은 자손에 영향을 미치지만, 자손의 변경은 조상에 아무런 영향을 미치지 않는다.

 

p.4 상속(클래스간의 관계)

부모클래스(객체)의 멤버들을 자식클래스(객체)가 물려받는것

1. 상속을 사용하는 이유 : 기존에 만들어놓은 클래스의 재사용(=Reuse), 확장을 위해 사용.

2. 자바에서는 단일상속만이 가능하다(부모클래스가 한 개만 가능)

3. 부모클래스(super)와 자식클래스(sub)가 존재한다.

4. 자바에서 제공되어지는 모든 클래스들은 Object 라고 하는 최상위 클래스로부터 상속되어진다.

5. 사용자정의 클래스들도 Object 클래스라는 최상위클래스를 상속 받음

 

ch07 > Car2.java

package ch07;

public class Car2 {
	int a = 7;
	void move() {
		System.out.println("차 이동...");
	}
}

//상속받은 메소드를 OverRiding 한 것임. 부모 등에 OverRiding 했다고 생각하기.
//OverLoading과 OverRiding의 차이점 알아두기.
//OverLoading은 같은 class 내에서 메소드 이름이 똑같지만 파라미터의 타입이나 갯수로 메소드들이 구별되어 자동으로 출력되는 것이다.
//OverRiding은 부모클라스를 상속받아 부모의 등에 업혀서(Riding되어) 수행되는 것이다.
class Bus extends Car2 {
	int a = 25;
	void move() {
		System.out.println("40명 태우고 이동");
	}
}

class Ambulance extends Car2 {
	int a = 100;
	void move() {
		System.out.println("사이렌 울리며 이동");
	}
	void special() {
		System.out.println("환자를 태우고 있다.");
	}
}

class FireEngine extends Car2 {
	int a = 11;
	void move() {
		System.out.println("물뿌리면서 이동");
	}
}

 

Car2Ex.java

package ch06;

class Car2 {
	String 	name;
	String 	color;
	int 	speed;
	
	//외우기
	//생성자는 클래스명과 메소드명이 같다. 하지만 void라 하지 않는다.
	//생성자의 역할은 메모리를 생성시켜준다. 기본생성자가 아닌 것은 멤버변수의 값을 세팅시켜서 초기화시켜준다.
	Car2(String name, String color, int speed) {
		//이름이 같으면 지역변수로 세팅되기 때문에 this연산자를 세팅해줘야 인스턴스변수(코드상 4~6줄)를 뜻한다는 것을 알 수 있다.
		//this 인스턴스는 자신을 가리키는 참조변수
	    //모든 인스턴스 메서드에 지역변수로 숨겨진 채로 존재
	    //인스턴스변수와 지역변수를 구별하기 위해 참조변수 this사용
		//헷갈리지 않도록 생성자에 this라는 참조변수를 쓰는 것을 권장.
		//파라미터의 name을 가리키는 건지 멤버변수의 name을 가리키는 건지 모름. this를 쓰면 '나' 즉 나의 멤버변수를 뜻하는 것.
		this.name = name;
		this.color = color;
		this.speed = speed;
	}
	void print() {
		System.out.println(name +"\t" + color + "\t" + speed);
	}
}

public class Car2Ex {

	public static void main(String[] args) {
		Car2 c1 = new Car2("소나타","빨강",150);
		c1.print();

	}

}

출력 결과

 

Outer.java

package ch07;

public class Outer {
	int width = 10;
	int height = 10;
	
	Outer(int width, int height) {
		this.width = width;
		this.height = height;
	}
	//method 앞에 붙는 것은 Return형이다.(알아두기)
	public Inner getInner() {
		return new Inner();
	}
	
	class Inner { //class 안에 class가 있는 형태. Outer class가 존재할 때만 Inner 클래스를 쓸 때 이렇게 해둠.
		int depth = 10;
		public int volume() {
			return width * height * depth;
		}
	}
}

 

OuterEx.java

package ch07;

public class OuterEx {

	public static void main(String[] args) {
		Outer out1 = new Outer(100, 100);
		Outer.Inner in1 = out1.getInner();	//메소드를 사용함
		//class 안의 inner class를 정의할 수 있다.
		System.out.println("부피 : " + in1.volume());
		
		Outer out2 = new Outer(200, 200);
		Outer.Inner in2 = out2.new Inner();	//메소드를 사용하지 않음
		System.out.println("부피 : " + in2.volume());
	}

}

출력 결과

 

 

Person2.java

package ch07;

public class Person2 {
	private String 	name;
	private int 	age;
	
	Person2(String name, int age) {
		this.name	= name;
		this.age	= age;
	}
	
	public void print() {
		System.out.println("-----------");
		System.out.println("이름 : " + name);
		System.out.println("나이 : " + age);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

 

Student2.java

상속받을 때 SuperClass 오른쪽의 Browse를 눌러서 상속받을 코드를 지정해준다.

package ch07;

public class Student2 extends Person2 {
	private String sno;	//학번
	
	Student2(String name, int age, String sno) {	//자동완성으로 작성하기.
		super(name, age);	//부모
		this.sno = sno;		//나(자식)
	}
	
	public void print() {
		super.print();
		//내 것은 내가 처리한다.
		System.out.println("학번 : "+sno);
	}

	public String getSno() {
		return sno;
	}

	public void setSno(String sno) {
		this.sno = sno;
	}
}

class Teacher2 extends Person2{
	private String subject;

	Teacher2(String name, int age, String subject) {	//자동완성으로 작성하기.
		super(name, age);
		this.subject = subject;
	}
	
	public void print() {
		super.print();
		System.out.println("과목 : " + subject);
	}

	public String getSubject() {
		return subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}
}

class Manager2 extends Person2 {
	private String part;

	Manager2(String name, int age, String part) {	//자동완성으로 작성하기.
		super(name, age);
		this.part = part;
	}
	public void print() {
		super.print();
		System.out.println("담당 : " + part);
	}
	public String getPart() {
		return part;
	}
	public void setPart(String part) {
		this.part = part;
	}
}

 

Person2Ex.java

package ch07;
//실행은 부모와 같은 이름의 자식 메소드가 실행 (자식 메소드 수행)

//부모 rv = new 자식(); up casting OK
//부모 method()와 자식 method() 이름을 같을 때는  자식 메소드 실행
//부모의 멤버변수와 자식의 멤버변수가 이름이 같을 때는 부모 멤버변수 실행(*class 내에 선언되는 변수가 멤버변수)

//자식 rv2 = new 부모(); down casting X
//자식 rv3 = (자식)부모객체;  down casting. 부모타입을 자식타입으로 변환하는 것.
//강제 형변환 하면 
//1) 문법적 에러가 없음 
//2) 에러가 있을 때도 없을 때도 있다
//그래서 권장하지 않는다.

public class Person2Ex {

	public static void main(String[] args) {
		Person2[] ps = new Person2[5];
		ps[0] = new Student2("옥주현", 23, "1203667");
		ps[1] = new Student2("김유신", 53, "1386889");
		ps[2] = new Teacher2("대조영", 48, "JAVA");
		
		ps[3] = new Manager2("조정은", 28, "뮤지컬");
		ps[4] = new Manager2("전현무", 40, "MC");
		
		for (Person2 p : ps) {
			p.print();
			//과제 - Manager2 객체 비교해서 manager가 맞다면 m.getPart() 출력
			//instanceof 연산자 : 객체가 어떤 타입인지 조사할 때 사용. 주로 강제타입 변환에 변환이 가능한지 조사할 때 사용.
			if (p instanceof Manager2) {
				Manager2 m = (Manager2) p;
				System.out.println("추가 : " + m.getPart());
			System.out.println("2번째 방법 : " +((Manager2)p).getPart());
			//32줄이 30+31줄과 같음.
			}
		}
	}
}

 

Super1.java

package ch07;
//자식과 부모의 변수이름이 같을 때 각각 불러오는 방법
//근데 보통 자식과 부모는 멤버변수 이름을 굳이 같게 사용하지 않는다. 헷갈리기 때문임.

class Parent {
	int 	a = 7;
	int 	b = 7;
	void display() {
		System.out.println("Parent 대박");
	}
	void print() {
		System.out.println("Parent 난 부모 print 메소드");
	}
}

class Child extends Parent {
	int 	 a = 10;
	void print() {
		super.print(); 	//1. 부모 Method를 찾는다.
		System.out.println("Child 부모멤버 a = " + super.a);	//부모를 불러올 때는 super.를 사용
		System.out.println("Child 난 자식 메소드 a = " +a);	//내 클래스 안에선 this를 생략할 수 있음. This가 생략된 것으로 봄. 내껄 호출하고 싶으면 this. 를 붙이면 됨.
		System.out.println("Child 난 자식 메소드 b = " +b);
		System.out.println("=========================");
	}
}

public class Super1 {

	public static void main(String[] args) {
		Child child = new Child();
		child.print();
		child.display();

	}

}

출력 결과

 

Super2.java 

package ch07;

class Parent1 {
	Parent1() {
		System.out.println("매개변수 없는 부모 생성자");
	}
	Parent1(String str) { 
		System.out.println("Parent 매개변수 있는 부모생성자 1 str-->" + str);
	}
	void parentPrint() {
		System.out.println("parentPrint 야호! 월요일이다");
	}
}

class Child1 extends Parent1 {
	Child1() {
		super("헐~!");
		System.out.println("매개변수 없는 자식 생성자");
		//super("헐~!");	//매개변수 1개짜리 부모 생성자를 또 호출하면 오류남. 왜냐면 위에서 이미 했기 때문.
	}
	void childCc1() {
		System.out.println("cc1 야호! 월요일이다");
	}
	void childCc2() {
		System.out.println("cc2 자식 Method cc2");
	}
}

public class Super2 {

	public static void main(String[] args) {
		Child1 child1 = new Child1();
		child1.childCc2();
		child1.childCc1();
		//chidl1.super.cc1(); //이렇게 하면 오류남. 
	}
}

호출 춘서는 Child1 > Parent1 이지만 실행 순서(출력 결과)는 Parent1 > Child1 이다.

부모 클래스의 "기본 생성자"를 자동으로 호출하게 된다.

 

출력 결과

 

Super3.java

package ch07;
//실제로는 이렇게 코드를 짜지 않지만 생성자의 흐름을 보기에 좋음.

class Parent3 {
	Parent3() {
		System.out.println("1. 매개변수 없는 조상 생성자 Parent3");
		
	}
	Parent3(int x) {
		this();	//자기 자신 class의 생성자
		System.out.println("매개변수 1개 Parent3 : x = " + x);
	}
}

class Child3 extends Parent3 {
	Child3(int x) {		//Parent3 기본 생성자
		System.out.println("2. Child3 매개변수 1개 Child3: x = " + x);
	}
	Child3(int x, int y) {
		this(x);	//자신 class의 1개 매개변수 생성자 -> 16줄 먼저 실행됨.
		System.out.println("3. Child3 매개변수 2개 부모 : x = " + x + " , y " +y);
		
	}
}

class Child5 extends Child3 {

	Child5(int x, int y) {
		super(x, y);	//Child3 Class의 2개 매개변수 생성자
		System.out.println("4. Child5 매개변수 2개 자식 : x = " + x + " , y " + y);
	}
	void cc5() {
		System.out.println("Child5 cc5 메소드...");
	}
	
}

public class Super3 {

	public static void main(String[] args) {
		Child5 child5 = new Child5(7, 15);

	}

}

 

출력 결과

 

 

지금까지 extend를 해본 것임. Implements를 할 차례. 인터페이스는 일종의 추상클래스. 추상클래스를 구현하는 것이 Implements임.

[8. 자바 추상 Interface]

p.14 

인터페이스는 구현된 것이 전혀 없는 것이다.

1) 일종의 추상클래스. 추상클래스(미완성 설계도)보다 추상화 정도가 높다.

2) 실제 구현된 것이 전혀 없는 기본 설계도.(알맹이 없는 껍데기)

3) 추상메서드와 상수만을 멤버로 가질 수 있다.

4) 인스턴스를 생성할 수 없고, 클래스 작성에 도움을 줄 목적으로 사용된다

5) 미리 정해진 규칙에 맞게 구현하도록 표준을 제시하는 데 사용된다

6) class’대신 ‘interface’를 사용한다는 것 외에는 클래스 작성과 동일하다

 

p.15

- 모든 멤버변수는 public static final이어야 하며, 이를 생략할 수 있다.

- 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.

(보통 생략이 가능하면 대부분 생략함.)

 

p.16

- 인터페이스는 클래스와 달리 다중상속을 허용한다.

- 인터페이스는 Object 클래스와 같은 최고 조상이 없다.

 

p.17

인터페이스 구현 : 클래스를 상속받는 것과 같다. 다만 extend 대신 implements를 사용한다.

class 클래스이름 implements 인터페이스 {
	}

 

 

p.20 인터페이스의 장점

1. 개발시간을 단축시킬 수 있다.

- 일단 인터페이스가 작성되면, 이를 사용해서 프로그램을 작성하는 것이 가능. 메서드를 호출하는 쪽에서는 메서드의 내용에 관계없이 선언부만 알면 되기 때문.

- 동시에 다른 한 쪽에서는 인터페이스를 구현하는 클래스를 작성하도록 하여, 인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도 양쪽에서 동시에 개발을 진행

2. 표준화가 가능하다.

- 프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음, 개발자들에게 인터 페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능.

3. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.

- 서로 상속관계에 있지도 않고, 같은 조상클래스를 가지고 있지 않은 서로 아무런 관계도 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 매핑.

4. 독립적인 프로그래밍이 가능하다.

- 인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제 구현에 독립적인 프로그램을 작성하는 것이 가능

- 클래스와 클래스간의 직접적인 관계를 인터페이스를 이용해서 간접적인 관계로 변경하면, 한 클래스의 변경이 관련된 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능

 

 

ch08 > Inter1.java

(마우스 우클릭 > new > interface)

package ch08;

public interface Inter1 {
	//Static Final. 마지막 변수이므로 수정할 수 없음. 인터페이스에 있는 변수는 전부 상수이다.
	int AAA = 3;
	//추상메소드(Abstract methods)
	//인터페이스의 메소드들은 전부 추상 메소드이다.
	void display();
	void print();

}

//Inter1을 상속받아서 ImplClass1을 구현하겠다는 뜻.
class ImplClass1 implements Inter1 {

	@Override
	public void display() {
		//AAA++; 값을 더이상 할당할 수가 없다. 그래서 오류 남.
		System.out.println("재정의 했어..AAA->" + AAA);
		
	}

	@Override
	public void print() {
		System.out.println("Interface 상속받아 구현");
		
	}
	
}

 

Inter1Ex.java

package ch08;

public class Inter1Ex {

	public static void main(String[] args) {
		ImplClass1 implClass1 = new ImplClass1();
		implClass1.display();
		implClass1.print();

	}

}

출력 결과

 

 

Inter2.java

package ch08;

interface Interfa2 {
	void ifa2();	//추상 메소드
	
}

//인터페이스를 상속받는데 구현이 아니고 확장이기 때문에 Implements를 쓰지 않고 extends를 쓰는 것이다.
//interface를 interface로 상속받을 때 extends를 쓴다.
interface Interfa21 extends Interfa2 {	
	void ifa21();	//추상 메소드
	
}

//interface만 계속 확장되는 중
interface Interfa22 extends Interfa21 {	
	void ifa22();	//추상 메소드
	
}

//앞이 클래스면 여기있는 것들을 구현해야 하기 때문에 implements를 쓰는 것임.
//interface를 class로 상속받을 때 implements를 쓴다.
//위와 헷갈리면 안됨.
class ImplClass2 implements Interfa22 {

	@Override
	public void ifa21() {
		System.out.println("ImplClass2 Interfa21의 매소드");		
	}

	@Override
	public void ifa2() {
		System.out.println("ImplClass2 Interfa2의 매소드");		
	}

	@Override
	public void ifa22() {
		System.out.println("ImplClass2 Interfa22의 매소드");		

	}
}

public class Inter2 {
	public static void main(String[] args) {
		ImplClass2 implClass2 = new ImplClass2();
		implClass2.ifa2();
		implClass2.ifa21();
		implClass2.ifa22();
		System.out.println("-----------Interfa21 interfa21 사용가능 범위------------");
		Interfa21 interfa21 = new ImplClass2();	//부모에게 자식을 개입할 수 있다.
		interfa21.ifa21();
		//interfa21.ifa22();	//21을 상속받아서 22가 만들어졌기 때문에 못 쓴다.
	}
}

출력 결과

 

Abstract1.java

추상클래스 만들 때 abstract 찍기

package ch08;

abstract class AbsClass {
	int kkk = 0;
	
	public void setKkk(int kkk) {
		this.kkk = kkk;
	}

	public AbsClass() {
		System.out.println("추상 Class 생성자");
	}
	
	//추상 클라스는 하나 이상의 추상 메소드가 존재해야 한다.
	abstract void print(); 
}

class ImplAbsClass extends AbsClass {

	//추상메소드를 상속받으면 반드시 구현해야 한다. 단 인터페이스를 상속받으면 인터페이스이기 때문에 구현할 필요 없다.
	@Override
	void print() {
		System.out.println("ImplAbsClass + kkk --->" + kkk);
	}
	
}

public class Abstract1 {

		
	public static void main(String[] args) {
		//추상 Class는 instance 생성 안됨.
		//AbsClass absClass = new AbsClass();
		AbsClass absClass = new ImplAbsClass();
		absClass.setKkk(12);
		absClass.print();
	}

}

출력 결과

 

인터페이스와 추상클래스 비교 문제 면접에 많이 나옴.(중요 외우기, 면접 단골 손님 별 3개)

추상클래스는 변수와 메소드와 추상메소드로 구성되어 있고, 인터페이스는 상수와 추상메소드로 구성되어 있다.

추상클래스는 상속받을 때 extends 키워드를 사용, 인터페이스는 implements(구현하다) 키워드를 사용한다.

선언 구문 외우기.

두 개의 공통점은 overRiding.

추상클래스는 단일상속만 가능하고 인터페이스는 다중상속이 가능하다.

 

 

Lenderable.java

package ch08;

//인터페이스이기 때문에 메소드들은 전부 추상 메소드이다.
public interface Lenderable {	//대여 가능 인터페이스
	//인간이 이해할 수 있는 용어로 바꿔줄 때 상수를 쓰기도 함.
	//인터페이스이기 때문에 얘네들은 상수이다.(추상 메소드라서)
	int BORROW = 1;	//빌려간 상태
	int NORMAL = 0;	//대여 가능한 상태
	
	void checkOut(String borrower, String date);
	void checkin();

}

class SeperateVolume implements Lenderable{
	String title;	//초기 생성 시 값 입력됨.
	String date;
	String borrower;
	int status;	//상태값 (기본값 0)
	
	SeperateVolume(String title) {
		this.title = title;
	}

	@Override
	public void checkOut(String borrower, String date) {	//대여
		if (status != NORMAL) return;	//status의 초기값은 아무것도 없기 때문에 0이다.
		this.date = date;
		this.borrower = borrower;
		status = BORROW;
		// BORROW = 1; 안됨
		System.out.println(borrower + "가 " + date + "일에 " + title+ "을 대여했다.");
	}

	@Override
	public void checkin() {		//반납
		System.out.println(borrower + "가 " + title+ "을 반납했다");
		//전부 초기화
		date = null;
		title = null;
		borrower = null;
		status = NORMAL;	//0
		
	} 
	
}

 

LendarableEx.java

package ch08;

public class LendarableEx {

	public static void main(String[] args) {
		SeperateVolume sv = new SeperateVolume("젊은 베르테르 슬픔");
		sv.checkOut("정승환/박윤하", "2022/07/25");
		sv.checkin();
	}
}

출력 결과

 

부모와 자식 예제 한 개 더

class Super { 
    int index = 5; 
    public void printVal() {
        System.out.println("Super");
    } 
} 
class Sub extends Super {   
    int index = 2;   
    public void printVal() {   
        System.out.println("Sub"); 
    } 
} 
public class Exam {   
    public static void main(String[ ] args) {   
        Super sup = new Sub();   
        System.out.print(sup.index + " ");   
        sup.printVal(); 
    } 
  }

부모 method()와 자식 method() 이름을 같을 때는  자식 메소드 실행
부모의 멤버변수와 자식의 멤버변수가 이름이 같을 때는 부모 멤버변수 실행(*class 내에 선언되는 변수가 멤버변수)

 

출력 결과 : 5 Sub

 

반응형