[자바기초] 객체지향 프로그래밍 특징(상속성) 정리노트
* 값에 의한 호출(Call by Value) / 참조에 의한 호출(Call by reference)
- 메소드 호출에는 값에 의한 호출과 참조에 의한 호출이 있습니다.
class CallByValueTest {
public static void add(int n){
n = n + 1;
System.out.println(n);
//return 생략
}
public static void main(String[] args) {
int data;
data = 20;
System.out.println(data); //20
add(data); //21
System.out.println(data); //20
}
- 참조에 의한 호출 : 메소드 호출시에 배열이나 객체변수가 전달되는 경우를 참조에 의한 호출이라 하며 메소드 호출시에 전달한 데이터 값이 메소드에 의해 변경될 때에 호출한 쪽에서도 변경이 내용이 지속적으로 적용 됩니다.
class Person{
String name;
int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
class CallByReferenceObject{
public static void add( Person p ){ // 클래스를 매개변수로 받음.
p.age = p.age + 1 ;
}
public static void main(String[] args) {
Person kim = new Person("김유신", 20);
add(kim); //21
System.out.println(kim.age); //김유신,21
}
}
> 객체나 배열은 힙데이터에 저장이 되고 클래스를 매개변수로 받아 같은 힙메모리의 저장공간을 사용하기에 이후에 호출 값에도 영향을 주어 변경된 값이 고정됩니다.
* 클래스의 상속
- 현실세계의 부모의 상속과 같은 상속의 개념이 있듯이 클래스의 세상에도 상속이 있습니다.
이미 있는 클래스를 확장하여 새로운 클래스를 만들 수 있는데 이것을 "상속"이라고 합니다.
class A{
---
}
class B extends A{ <-- A클래스를 확장하여 B를 만들겠다.
}
> 기본적으로 사용되는 상속문입니다. A 부모 클래스를 B 자식클래스가 상속 받는 모습입니다.
상속을 받을 때에는 extends 를 사용합니다.
- 이렇게 함으로 해서 B는 자동으로 A의 모든 것을 물려받게 됩니다. A클래스의 코드라인의 수가 수백줄이라고 할 때에 자동으로 B가 이것을 물려받게 됩니다. 새로 추가되는 내용에 대해서만 B안에 써 주면 됩니다.
* 상속의 장점
1. "코드의 재사용"을 높일 수 있다.
2. 개발속도를 향상시킨다.
3. 비용을 절감할 수 있다.
- 객체 지향 프로그래밍의 특징 중 은닉성에 대해 먼저 알아보았었습니다.
은닉성(Encapsulation)이란 : private 를 사용하여 외부의 다른 클래스로 부터 맴버변수들을 보호하는 것입니다.
* 상속성 (Inheritance)
- 상속성이란 이미 있는 클래스를 확장하여 코드를 재사용 하는 것을 말합니다.
class A{
---
}
class B extends A{ <-- A클래스를 확장하여 B를 만들겠다.
}
자식클래스는 부모의 모든 속성과 동작을 자동으로 물려받게 됩니다. 그러나 부모의 private영역에는 아무리 자식클래스라 하더라도 접근할 수 없어요!
그렇기 때문에 외부의 클래스로 부터 보호는 하고 싶고, 자식 클래스에서만 맴버변수를 사용하게끔 하기 위해 protected 를 사용합니다.
* protected
또, 사용자가 생성자를 만들지 않아도 기본생성자가 제공됩니다.
그런데 만약 사용자가 생성자를 하나라도 만들기 시작하면 기본생성자를 제공하지 않습니다.
필요하다면 만들어야 해요!
class A{
public A(){
//부모생성자
}
}
class B extends A{
public B(){
//자식생성자
}
}
알고 있듯이 사용자가 생성자를 하나라도 만들기 시작하면 기본생성자를 제공하지 않습니다.
만약 부모클래스에 매개변수를 갖는 생성자를 만들고 기본생성자를 따로 만들지 않았다고 합시다.
그리고 자식클래스에서는 생성자를 하나도 만들지 않았어요.
class A{
protected String name;
public A(String name){
this.name = name;
}
}
class B extends A{
}
B ob = new B(); // B클래스 객체는 기본 생성자가 없어서 오류발생
> 이때, 자식클래스의 객체를 생성하면 자동으로 (묵시적으로) 부모의 기본생성자를 요구하기 때문에 오류가 발생합니다!
만약, 부모의 매개변수를 갖는 생성자를 동작시키고자 한다면 super()를 이용합니다.
class A{
protected String name;
public A(String name){
this.name = name;
}
}
class B extends A{
public B(String name){
super(name); // super을 이용
}
}
B ob = new B(); // 객체 생성 가능
> super()는 생성자의 첫번째 문장에 와야합니다.
* 메소드 오버라이딩 (method overriding)
상속관계에 있을때에 자식클래스는 부모클래스의 모든 속성(맴버변수)과 동작(맴버메소드)을 물려 받게 됩니다.
즉, 자식클래스 안에서 부모의 메소드를 자유롭게 내 것 처럼 사용할 수 있어요!
- 그런데 만약 부모의 어떠한 메소드가 자식클래스에게는 맞지 않거나 마음에 들지 않을 때 다시 재정의 하는 것을 "메소드 오버라이딩"이라고 합니다.
- 메소드 오버라이딩을 위해서 메소드 이름뿐 아니라 매개변수의 개수와 자료형이 일치되게 재정의 하여야 합니다.
(* 메소드 오버로딩과의 차이점 : 메소드 오버로딩은 메소드 중복이며 같은 클래스 안에서 메소드를 여러개 만들 고 싶을 때 사용됩니다. 자료형이 서로 다르거나 매개변수의 개수가 다른 경우 여러개의 중복 메소드를 사용 할 수 있습니다.)
class Person{
String name;
String addr;
public Person(String name, String addr){
this.name = name;
this.addr = addr;
}
public void info(){ //부모 클래스의 메소드
System.out.println(name+","+addr);
}
}
class Customer extends Person{
int no;
public Customer(String name, String addr, int no){
super(name, addr);
this.no = no;
}
public void info(){ //자식 클래스의 메소드
System.out.println(no+","+name+","+addr);
}
}
class MethodOverridingTest02 {
public static void main(String[] args) {
Customer c = new Customer("홍길동","서울", 100);
c.info();
}
}
> 메소드 이름뿐 아니라 매개변수의 개수와 자료형이 똑같은 모습으로 재정의 된 것을 확인할 수 있습니다.
하지만 자식 클래스에서는 기존 클래스를 확장하여 맴버 변수 하나를 추가하였기에 메소드의 변화를 주고 싶습니다.
그러므로 맴버 변수의 값을 추가하여 오버라이딩 해줍니다.
* 추상 클래스와 추상 메소드
- 이때에 미래의 자식클래스들이 반드시 가져야 할 메소드가 있다고 합시다. 이것을 일반화 하는 시점에서는 그 메소드의 body를 구체화 할 수 없을때에 메소드의 선언문만 써요. 이러한 메소드를 "추상메소드"라고 합니다.
- "추상메소드"임을 나타내기 위하여 메소드이름 왼쪽에 abstract 키워드를 써 줍니다.
abstract class Employee{ //추상클래스 //부모클래스
protected String name; // 추상클래스인 Employee는 객체를 가질 수 없음.
protected String no;
protected int salary;
public abstract void computeSalary(); //추상메소드
}
class SalariedEmployee extends Employee{ //자식클래스
private int level;
private int base;
private int sudang;
public void computeSalary(){ //추상메소드를 구체화 하는 메소드
switch(level){
case 1:base=2000000;sudang=200000;break;
case 2:base=3500000;sudang=350000;break;
case 3:base=5000000;sudang=500000;break;
}
salary = base + sudang;
}
}
class HourlyEmployee extends Employee{ //자식클래스
private int base;
private int hours;
public void computeSalary(){ //추상메소드를 구체화 하는 메소드
salary = base * hours;
}
> 추상 메소드를 사용하면 자식클래스에서는 이를 구체화 해주는 메소드를 꼭 작성해야 합니다.
그렇지 않으면 오류가 납니다.
*** 추상클래스의 객체는 생성할 수 없어요!!! 어떻게 동작해야 하는지 메소드의 body가 구체화 되지 않은 추상메소드를 갖고 있기 때문에 그 클래스의 객체는 생성할 수 없어요!!
그러나 배열을 사용하면 가능합니다.
class EmployeeTest05 {
public static void main(String[] args) {
String name,no;
int type;
int level, base, hours;
Scanner sc = new Scanner(System.in);
Employee []e = new Employee[3];
for(int i=0; i<e.length; i++){
System.out.print("사원이름==>");
name = sc.next();
System.out.print("사원번호==>");
no = sc.next();
System.out.print("급여의 종류 입력==>[1.월급, 2:시간]");
type = sc.nextInt();
switch(type){
case 1:
System.out.print("호봉==>");
level = sc.nextInt();
e[i] = new SalariedEmployee(name,no,level);
break;
case 2:
System.out.print("시간당 임금==>");
base= sc.nextInt();
System.out.print("작업시간==>");
hours= sc.nextInt();
e[i] = new HourlyEmployee(name,no,base,hours);
break;
}
}
//급여를 계산하고 출력
for(int i=0; i<e.length; i++){
e[i].computeSalary();
System.out.println(e[i].toString());
}
/*public String toString(){
return "이름:"+name+",사원번호:"+no+",실수령액:"+salary;
}
public String toString(){
return super.toString()+",호봉:"+level+",기본금:"+base+",수당:"+sudang;
}
public String toString(){
return super.toString()+",시간당급여:"+base+",작업시간:"+hours;
}
*/
Employee []e = new Employee[3];
e[i] = new SalariedEmployee(name,no,level);
e[i] = new HourlyEmployee(name,no,base,hours);
- 상속관계에 있는 것을 is a 관계라고 하고 부모의 참조변수가 자식클래스의 객체를 참조할 수 있어요!
Employee e1;
e1 = new SalariedEmployee();
e1 = new HourlyEmployee();