객체지향 프로그래밍은
현실세계에서 이루어지는 업무를 객체를 사용해서 프로그래밍하는 방법으로
실제 이루어지고 있는 보여지고 구체화된 사물을 객체로 표현하여 메모리 상에 데이터로 표현하고
그 객체들을 이용하여 업무를 수행하게 한다.
예를들어
주차장을 객체 지향 프로그래밍으로 표현하면
주차장 관리를 서비스로 하고
주차장의 차를 객체로 표현할 수 있다.
또한 주차장 전체를 객체로 하여 주차장 객체 안에 차 객체가 있다고 표현할 수도 있다.
클래스
객체 인스턴스를 찍어내는 틀로 멤버필드(변수), 멤버메소드를 가지고 있다.
멤버필드는 그 객체가 가진 특성(데이터), 또는 그 객체가 포함하고 있는 다른 객체를 말한다.
멤버 메소드는 그 객체가 수행하는 동작이라고 할 수 있다.
주차장을 예로들면 주차장에는 많은 차들이 있는데
차 객체는 모두 차량번호, 차종 등.. 공통된 특성들을 가지고 있고 주차하고, 주차장을 빠져나가는 행위를 한다.
여기서 차량번호, 차종 이라는 멤버필드를 가지고 주차하고, 주차장을 나가는 행위를 하는 차를 클래스로 표현하고
"1111번", "벤츠" 인 차량을 차 클래스로 생성한 인스턴스라고 한다.
public class Car {
/*
* 멤버필드(변수)[속성]
*/
String no; //차량번호
int inTime; //입차시간
int outTime;//출차시간
int fee; //주차요금
/*
* 멤버메소드[기능]
*/
/*
* 입차시 데이터 대입
*/
void setIpChaData(String no, int inTime) {
this.no = no;
this.inTime = inTime;
}
/*
* 출차시간 데이터 대입
*/
void setOutTime(int outTime) {
this.outTime = outTime;
}
/*
* 주차요금 계산
*/
void calculateFee() {
fee= (outTime-inTime)*1000;
return;
}
}
객체 생성
Car car1;//차 객체의 주소값을 저장할 Car클래스타입 참조변수를 선언
car1 = new Car();
//Car클래스를 사용하여 차 객체를 생성한 후 차 객체의 주소값이 car1참조변수에 대입
car1.no = "1234";//차 객체(car1)의 멤버 필드 no에 "1234" 값 대입
car1.inTime = 12;
++this. 셀프참조
호출 주체객체의 주소값을 가진 변수
++return
메서드를 호출한 곳으로 실행 흐름을 반환한다.
return type이 void 인 경우 생략 가능하고 이 경우, compiler가 자동으로 return문을 삽입한다.
변수의 scope( 범위 )
- 지역변수 : 메소드 블록 안에 생성된 변수
지역변수는 반드시 초기화되어야 한다.
<<The local variable tot may not have been initialized>>
int tot;
System.out.println(tot);
tot값이 초기화되어있지 않아 에러
Order order;
System.out.println(order); // 에러
order.print(); // 에러
- 멤버변수 : 클래스 블록 안에 생성된 변수
인스턴스 멤버필드는 객체생성시
* 기본값으로 자동 초기화
public class MemberField {
/*
* 1-1.인스턴스 멤버필드는 객체생성시
* 기본값으로 자동 초기화
*/
public int member1;//0
public double member2;//0.0
public char member3;//0 '/u0000'
public boolean member4;//false
public String member5;//null
}
멤버 변수는 클래스에서 변수값을 초기화하지 않아도
객체 생성시 기본값으로 자동 초기화된다.
Encapsulation 캡슐화 : 의존성 감소
접근 지정자
- public : 어떤 외부클래스(객체)에서든지 접근가능
- private : 어떤 외부클래스(객체)에서든지 접근불가능
- default : 같은 패키지 내에서만 접근 가능, 예약어 생략 가능
- protected : 상속 받은 클래스, 같은 패키지 내에서만 접근 가능
멤버 변수는 private으로 변수에 직접 접근 불가하게 만들고
멤버 메소드는 public으로 하여 Getter, Setter 메소드를 통해 변수에 접근할 수 있게 한다.
Overloading 다중정의
같은 이름의 메소드를 여러개 정의할 수 있다.
메소드 인자의 개수나 타입이 다르면 오버로딩 할 수 있다.
**리턴 타입이 다른 것은 오버로딩에 해당되지 않는다.
생성자 메소드 Constructor
- 객체를 생성하기 위해 호출하는 메소드로 사용하면 생성된 객체의 주소를 반환한다.
- 클래스 이름과 동일하고 리턴타입을 명시하지 않음
- 하나의 클래스는 반드시 하나 이상의 생성자를 가지고 있어야하며 생성자 메소드 코드를 직접 명시하지 않으면
컴파일러가 default constructor를 만들어 준다.
- 주로 객체 멤버 필드의 초기화 용도로 사용된다.
- 생성자 메소드도 오버로딩이 가능하다.
//멤버필드의 기본값을 가진 차 객체 생성
public Car() {
}
기본생성자
public Car(String no, int inTime, int outTime, int fee) {
super();
this.no = no;
this.inTime = inTime;
this.outTime = outTime;
this.fee = fee;
}
Car cara = new Car("1234",1,2,1000);
All Arguments Constructor
만약 AllArgsConstructor를 정의 해줬다면 인자가 없는 기본 생성자가 자동으로 생성되지 않는다.
만약 다른 생성자들과 기본 생성자가 모두 필요하다면 모두 직접 정의해 주어야 한다.
제한자, 제어자, modifier
Static
: static 제한자가 붙어있는 멤버 변수나 메소드는 객체의 생성 없이도 사용할 수 있다.
클래스로부터 생성되는 객체들이 공유하는 필드로 공용필드라고도 한다.
Static.static_field = 8888;
Static static = new Static();
new Static() 생성자로 static을 생성하지 않아도
바로 Static.static_field로 클래스.필드 로 접근 가능하다.
추상클래스
Abstract
: 하나 이상의 추상 메소드가 정의되어 있는 클래스
추상메소드는 구현부분인 {바디}가 없고 선언부(signature)만 존재한다.
추상메소드가 존재하지 않더라도 객체의 생성을 막기 위해 클래스에 abstract를 붙일 수 있다.
추상 클래스는 불완전한 추상 메소드를 가지므로 객체 생성이 불가능하고,
추상클래스를 상속하여 추상 메소드를 구현하는 자식 클래스를 만들어 자식 객체를 생성해 사용해야한다.
abstract class AbstractParent{
public void method1() {
System.out.println("난 구상(concrete)메소드");
}
public abstract void method2(); //추상 메소드 {} body가 없
}
추상메소드를 가진 추상 클래스 정의
class AbstractChild extends AbstractParent {
@Override
public void method2() {
System.out.println("AbstractChild에서 AbstractParent의 추상 메소드를 재정의");
}
}
추상클래스를 상속한 자식 클래스에서는 추상클래스의 추상메소드를 구현한다.
//AbstractParent ap = new AbstractParent(); 객체 생성불가
AbstractChild abstractChild1=new AbstractChild();
추상클래스는 객체 생성이 불가하고 추상클래스를 상속한 자식클래스는 생성이 가능하다.
제한자 Final
final
: 클래스 앞에 붙일 경우 상속 금지,
public final class Test{}
멤버 메소드 앞에 붙일 경우 오버라이딩 금지,
public final void print(){}
멤버 변수 앞에 붙일 경우, 캡슐화가 금지되고(public 사용), 변수는 상수화되며 한번 초기화하면 변경할 수 없다.
public final int PORT_NUMBER=80;
final 변수명은 관례적으로 대문자와 _밑줄의 조합으로 정한다.
final은 불변의 값이므로 객체마다 생성할 필요가 없어 보통 static과 함께 사용한다.
final 변수는 객체가 생성시점까지 반드시 초기화되어야 하는데
변수의 선언 때 초기화하지 않았다면 생성자에서 초기화해줘야한다.
final public Product product;
만약 product라는 객체를 선언하였는데 초기화하지 않았다면
public FinalFieldClass() {
The blank final field producxt may not have been initialized
this.product = new Product(1,"새우깡");
}
생성자에서 product를 초기화하지 않으면
The blank final field producxt may not have been initialized
이란 컴파일에러가 난다.
final 변수는 상수화되어 변경이 불가능하다고 했는데
그 변수가 객체인 경우
변수가 가지고 있는 것은 객체의 필드가 아니라 객체의 주소이므로
객체가 가지고 있는 필드는 변경이 가능하다.
객체간의 관계
- 포함
public class Car {
private int no;//차량번호
private String model;//차량모델명
private Engine engine; //엔진객체주소를 저장할 멤버필드
Car 객체는 no, model, Engine객체를 가지고 있다.
Engine porscheEngine = new Engine();
Car porscheCar = new Car();
porscheCar.setNo(1234);
porscheCar.setModel("PORCHE");
porscheCar.setEngine(porscheEngine);
Car volvoCar = new Car(5678,"XC90",new Engine("S", 4900));
- 클래스 상속 ( extends )
부모 클래스의 멤버들을 자식 클래스의 객체가 물려받는다.
> 기존에 만들어 놓은 클래스의 재활용이 가능하다. 확장성
> 자바에서는 클래스의 단일 상속만이 가능하다.
> 사용자 정의 클래스를 포함해 자바에서 제공되는 모든 클래스는 Object라는 최상위 클래스를 상속하고 있다.
public class Parent {
public int member1;
public void method1() {
System.out.println("Parent.method1()");
}
}
부모 클래스가 member1과 method1을 가지고 있다.
public class Child extends Parent {
/*
* public int member1; public int member2;
*
* public void method1() { System.out.println("Prent.method1()"); }
*
* public void method2() { System.out.println("Prent.method2()"); }
*/
public int member3;
public void method3() {
System.out.println("Child1.method3()");
}
}
Parent를 상속한 Child는 member3과 method3을 정의하고 있다.
하지만 Parent를 상속했기 때문에 member1과 method1도 사용할 수 있다.
Child child = new Child();
child.member1 = 1;
child.member3 = 3;
child.method1();
child.method3();
객체의 형변환 type casting
public class AcademyMember{
private int no;
private String name;
public void print() {
System.out.print(this.no+"\t"+this.name+"\t");
}
}
AcademyMember 클래스는 no, name, print를 멤버로 갖는다.
public class AcademyGangsa extends AcademyMember{
public String subject;//과목
public void print() {
super.print();
System.out.print(this.subject+"\n");
}
}
AcademyMember를 상속한 AcademyGangsa는 subject를 정의하고 print메소드를 재정의 했다.
++super()는 상속하고 있는 부모 클래스의 참조를 갖는다.
super.print()는 AcademyGangsa의 부모클래스인 AcademyMember.print() 와 같다.
부모클래스 변수 = new 자식클래스();
Child c = new Child();
Parent p = c;
p 변수에 c의 주소가 담긴다.
즉, Child의 객체의 주소를 담고 있던 참조변수 c는
Parent 타입의 p 변수에 들어가더라도 Child 객체의 주소를 그대로 가지고 간다.
p와 c 모두 같은 주소를 갖게 된다.
==p의 멤버 변수를 초기화하면 같은 주소( 같은 객체 )를 갖고 있는 c의 멤버 변수도 변한다.
AcademyMember m = new AcademyGangsa();
m.print();
m.no=1;
// m.subject = 3; AcademyMember는 subject멤버를 가지고 있지 않아 subject변수를 사용할 수 없다.
자식클래스인 AcademyGangsa로 객체를 생성했지만 변수 m 의 타입이 AcademyMember 이므로
AcademyMember의 멤버만 사용할 수 있다.
++반대의 경우, 부모의 객체를 자식 클래스의 변수에 담을 수 없다.
Parent p=new Parent();
Child c = p;(X)
AcademyMember 타입의 m이 AcademyGangsa의 멤버를 사용하기 위해선
다시 AcademyGangsa 타입으로 캐스팅 해줘야한다.
if(m instanceof AcademyGangsa) {
m.print();
AcademyGangsa tempGangsa = (AcademyGangsa)m;
System.out.println("과목: "+tempGangsa.getSubject());
}
++instanceof 연산자
참조변수 instanceof 클래스이름
참조변수의 클래스가 instanceof 이후의 클래스와 일치하면 true, 그렇지 않으면 false 반환
자식 클래스의 멤버를 사용하지 못해 다시 자식 클래스로 타입 캐스팅을 해줘야 하는 것은 굉장히 불편한데,
이렇게 부모 클래스로 타입 캐스팅을 해주는 이유는 부모 클래스로 여러 자식 클래스를 담을 수 있기 때문이다.
Member 의 자식이 Student, Gangsa, Staff 라면
Student 배열, Gangsa 배열, Staff 배열을 따로 핸들링 하지 않고
Student, Gangsa, Staff 를 모두 Member 타입으로 하여
Member 배열이나 Member 컬렉션에 담아 핸들링 할 수 있다.
이후 제네릭을 배우면 매번 자식클래스로 다시 캐스팅해주는 반복적인 코드를 줄일 수 있다.