5일차 Java [ 객체 지향 프로그램 , 클래스, 접근 제한자 ] ☆★
[ 객체 지향 프로그래밍 ? ]
소프트웨어를 개발할 때에도 부품에 해당하는 객체들을 먼저 만들고, 이 객체들을 하나씩 조립해서 완성된 프로그램을 만드는 기법을 객체 지향 프로그래밍 ( Object Oriented Programming , OOP ) 라고 한다.
[ 객체란 ? ]
객체 ( Object ) 란 물리적으로 존재하거나 개념적인 것 중에서 다른 것과 식별 가능한 것을 말한다. 예를 들어 물리적으로 존재하는 자동차, 자전거, 책, 사람은 물론 개념적인 학과, 강의, 주문 등의 모두 객체가 될 수 있다. 객체는 속성과 동작으로 구성된다. 사람은 이름, 나이 등의 속성과 웃다, 것다 등의 동작 (=기능)이 있고, 자동차는 색상, 모델명 등의 속성과 달린다, 멈춘다 등의 동작이 있다.
[ 객체의 상호작용 ]
모든 현상은 객체와 객체 간의 상호작용으로 이루어져 있다. 예를 들어 사람은 전자계산기의 기능을 이용하고, 전자계산기는 계산 결과를 사람에게 리턴하는 상호작용을 한다.
int result = add(10,20);
add(10,20)으로 리턴한 값을 int 변수 result에 저장
[ 객체 간의 관계 ]
객체는 단독으로 존재할 수 있지만 대부분 다른 객체와 관계를 맺고 있다. 관계의 종류에는 집합 관계, 사용관계, 상속 관계가 있다.
※ 집합 관계 : 완성품과 부품의 관계를 말한다. 예를 들어 자동차는 엔진, 타이어, 핸들 등으로 구성되므로 자동차와 부품들은 집합 관계라고 볼 수 있다.
※ 사용 관계 : 다른 객체의 필드를 읽고 변경하거나 메소드를 호출하는 관계를 말한다. 예를 들어 사람이 자동차에게 달린다, 멈춘다 등의 메소드를 호출하면 사람과 자동차는 사용 관계라고 볼 수 있다.
※ 상속 관계 : 부모와 자식 관계를 말한다. 자동차가 기계의특징 (필드, 메소드)를 물려받는다면 기계 (부모) 와 자동차 (자식)은 상속관계에 있다고 볼 수 있다.
[ 객체 지향 프로그래밍 특징 ]
※ 캡슐화 : 캡슐화 ( Encapsulation )란 객체의 데이터 (필드), 동작(메소드) 를 하나로 묶고 실제 구현 내용을 외부에 감추는 것을 말한다. 외부 객체는 객체 내부의 구조를 알지 못하며 객체가 노출해서 제공하는 필드와 메소드만 이용할 수 있다. 필드와 메소드를 캡슐화하여 보호하는 이유는 외부의 잘못된 사용으로 인해 객체가 손상되지 않도록 하는 데 있다.
(정보 은닉성 = 캡슐화 목적) 자바 언어는 캡슐화 된 멤버를 노출시킬 것인지 숨길 것인지를 결정하기 위해 접근 제한자(Access Modifier)를 사용한다.
※ 상속 : 부모 객체는 자기가 가지고 있는 필드와 메소드를 자식 객체에게 물려주어 자식 객체가 사용할 수 있도록 한다. 이것이 상속 ( Inheritance ) 이다. 상속을 하는 이유는 1) 코드의 재사용성을 높여준다 ( 중복 코딩을 하지 않아도 돼서 코드가 깔끔해짐) 2) 유지 보수 시간을 최소화시켜 준다.
※ 다형성 : 다형성 ( Polymorphism ) 이란 실행 결과가 다양하게 나오는 성질을 말한다. 자동차의 부품을 교환하면 성능이 다르게 나오듯이 프로그램을 구성하는 객체 (부품)을 바꾸면 프로그램의 실행 성능이 다르게 나올 수 있다. 다형성 ( 업캐스팅 / 다운캐스팅 )
[ 객체와 클래스 ]
객체를 생성할 때에는 설계도가 필요하다. 객체를 생성하려면 설계도에 해당하는 클래스 ( Class )가 필요하다. 클래스로부터 생성된 객체를 해당 클래스의 인스턴스 ( Instance )라고 부른다.
( 개발자가 => 설계 => 클래스(설계도) => 인스턴스화 => 인스턴스(객체) 생성 )
[ 객체 생성과 클래스 변수 ]
클래스로부터 객체를 생성하려면 객체 생성 연산자인 new가 필요하다 ( new 클래스 (); ). new 연산자 뒤에는 생성자 호출 코드가 오는데, 클래스 () 형태를 가진다. new 연산자는 객체를 생성시킨 후 객체의 주소를 리턴한다.
( 클래스 변수 = new 클래스 (); )
Student s1 = new Student();
Student s2 = new Student(); = > 하나의 class ( =Student )에 객체(=s1, s2)의 갯수 제한은 없다.
[ 클래스의 두 가지 용도 ]
1) 라이브러리 (library) 클래스 : 실행할 수 없으며 다른 클래스에서 이용하는 클래스 => 실행 클래스(=main)에서 사용하기 위해 만든 것.
2) 실행 클래스 (main () ) : 메소드를 가지고 있는 실행 가능한 클래스.
[ 클래스 구성 멤버 ]
1) 필드 : 객체의 데이터가 저장되는 곳. 필드 (Field)
2) 생성자 : 객체 생성 시 초기화 역할 담당. 생성자 (Constructor)
3) 메소드 : 객체의 동작으로 호출 시 실행하는 블록. 메소드 (Method)
[ 필드 선언과 사용 ]
필드는 객체의 데이터를 저장하는 역할을 한다. 객체의 데이터에는 고유 데이터, 현재상태 데이터, 부품 데이터가 있다. 자동차 객체를 예로 들면 제작회사, 모델, 색깔, 최고속도는 고유 데이터에 해당하고, 현재속도, 엔진 회전수는 상태 데이터에 해당한다. 그리고 차체, 엔진, 타이어는 부품에 해당한다.
※ 필드 선언 : 필드를 선언하는 방법은 변수를 선언하는 방법과 동일하다. 단, 반드시 클래스 블록에서 선언되어야만 필드 선언이 된다. ( 타입 필드명 [ = 초기값] ; ). 타입은 필드에 저장할 데이터의 종류를 결정한다. 기본타입 ( byte, char, short, int, long, float, double, boolean )과, 참조 타입( 배열, 열거, 클래스, 인터페이스 )이 모두 가능하다. 필드명은 첫 문자를 소문자로, 캐멀 스타일로 작성한다. ( Tire tire = new Tire(); => 이름이 같아도 ok. ) 초기값을 제공하지 않을 경우 필드는 객체 생성 시 자동으로 기본값으로 초기화 된다. ( 0, 0.0, false, null .. ) ( 예를 들어 String name; int age; => 선언만 되어 있지 초기화 되어 있지 않아서 기본값 (null,0)으로 설정되어 있음 )
※ 필드 사용 : 필드를 사용한다는 것은 필드값을 읽고 변경하는 것을 말한다. 클래스에서 필드를 선언했다고 해서 바로 사용할 수 있는 것은 아니다. 필드는 객체의 데이터 이므로 객체가 존재하지 않으면 필드도 존재하지 않는다. 클래스로부터 객체가 생성된 후에 필드를 사용할 수 있다. 필드는 객체 내부의 생성자와 메소드 내부에서 사용할 수 있고, 객체 외부에서도 접근해서 사용할 수 있다. 객체 내부에서는 단순히 필드명으로 읽고 변경할 수도 있지만 외부 객체에서는 참조 변수와 도트(.) 연산자를 이용해서 필드를 읽고 변경해야 한다. 도트(.)는 객체의 접근 연산자로, 객체가 가지고 있는 필드나 메소드에 접근하고자 할 때 참조 변수 뒤에 붙인다.
☆★ [ 생성자 선언과 호출 ] ☆★
new 연산자는 객체를 생성한 후 연이어 생성자 ( Constructor )를 호출해서 객체를 초기화하는 역할을 한다. 객체 초기화란 필드 초기화를 하거나 메소드를 호출해서 객체를 사용할 준비를 하는 것을 말한다. ( 주로 필드 초기화로 사용 됨 )
※ 기본 생성자 : 모든 클래스는 생성자가 존재하며, 하나 이상을 가질 수 있다. 클래스에 생성자 선언이 없으면 컴파일러는 다음과 같은 기본 생성자 ( Default Constructor )를 바이트코드 파일에 자동으로 추가시킨다. 클래스가 public class로 선언 되면 기본 생성자에도 public이 붙지만, 클래스가 public 없이 class로만 선언되면 기본 생성자에도 public이 붙지 않는다.
※ 생성자 선언 : 생성자는 메소드와 비슷한 모양을 가지고 있으나, 리턴 타입이 없고 클래스 이름과 동일하다. ( 클래스에 개발자가 선언한 생성자가 있다면 컴파일러는 기본 생성자를 추가하지 않는다. )

[ 필드 초기화 ]
객체마다 동일한 값을 갖고 있다면 필드 선언 시 초기값을 대입하는 것이 좋고, 객체마다 다른 값을 가져야 한다면 생성자에서 필드를 초기화 하는 것이 좋다. ( 대부분이 객체마다 다른 값을 가지므로 필드 초기화를 하는 것이 좋음 )
매개변수명이 필드명과 동일하기 때문에 필드임을 구분하기 위해 this 키워드를 필드명 앞에 붙여주었다. this는 현재 객체를 말하며, this.name은 현재 객체의 데이터 (필드) 로서의 name을 뜻한다.
public Korean ( String name, String ssn ) {
this.name = name ;
this.ssn = ssn;
}

☆★ [ 생성자 오버로딩 ] ☆★
매개값이 객체의 필드를 다양하게 초기화하려면 생성자 오버로딩 ( Overloading )이 필요하다. 생성자 오버로딩이란 매개변수를 달리하는 생성자를 여러 개 선언하는 것을 말한다. 매개변수의 타입,개수, 순서가 다르게 여러 개의 생성자 선언.
매개변수의 타입과 개수 그리고 선언된 순서가 똑같을 경우 매개변수 이름만 바꾸는 것은 생성자 오버로딩이 아니다.
생성자가 오버로딩 되어 있을 경우, new 연산자로 생성자를 호출할 때 제공되는 매개값의 타입과 수에 따라 실행될 생성자가 결정된다.
public class Car { // Car의 생성자가 밑에 4개 생성 // 매개변수의 타입, 개수, 순서가 다르게 여러 개의 생성자 선언
Car() { ... } // 기본 생성자 매개변수값이 없어서
Car(String model) { ... }
Car(String model, String color) { ... }
Car(String model, String color, int maxSpeed) { ... }
매개변수의 타입과개수 그리고 선언된순서가 똑같을경우 매개변수 이름만 바꾸는것은 생성자 오버로딩이 아니다.
Car(String model, String color) { ... }
Car(String color, String model) { ... } // 오버로딩의 예가 아님. 컴파일 에러 발생
[ 다른 생성자 호출 ]
생성자 오버로딩이 많아질 경우 생성자 간의 중복된 코드가 발생할 수 있다. 매개변수의 수만 달리하고 필드 초기화 내용이 비슷한 생성자에서 이러한 중복 코드를 많이 볼 수 있다. 이 경우에 공통 코드를 한 생성자에게만 집중적으로 작성하고, 나머지 생성자 this(...)를 사용하여 공통 코드를 가지고 있는 생성자를 호출하는 방법으로 개선할 수 있다.
Car(String model) {
this(model, "은색", 250); // => 호출
}
Car(String model, String color) {
this(model, color, 250); // => 호출
}
Car(String model, String color, int maxSpeed) {
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed; // => 공통 초기화 코드
}

class Book{
String title;
String author;
int price; //필드
Book(String title, String author, int price){
this.title=title;
this.author=author;
this.price=price;
}
Book(String title, int price){
this(title, "작자미상", price); //다른 생성자 호출
}
Book(){
this(null,null,0); //다른 생성자 호출
System.out.println("생성자가 호출되었음");
}
void show() {
System.out.println(title+" "+author+" "+price);
}
}
public class Test {
public static void main(String[] args) {
Book b1=new Book("고양이","베르나르",30000); //생성자 호출
Book b2=new Book("자바",40000);
Book b3=new Book();
b1.show();
b2.show();
b3.show();
}
}
[ this 키워드 ]
객체 내부에서는 인스턴스 멤버에 접근하기 위해 this를 사용할 수 있다. 객체는 자신은 'this' 라고 한다. 생성자와 메소드의 매개변수명이 인스턴스 맴버 필드명과 동일한 경우, 인스턴스 필드임을 강조하고자 할 때 this를 주로 사용한다.
[ 접근 제한자 ? ]
경우에 따라서는 객체의 필드를 외부에서 변경하거나 메소드를 호출할 수 없도록 막아야할 필요가 있다. => 캡슐화
중요한 필드와 메소드가 외부로 노출되지 않도록 해 객체의 무결성( 결점이 없는 성질 )을 유지하기 위해서 이다.
접근 제한자 | 제한 대상 | 제한 범위 |
public | 클래스, 필드, 생성자, 메소드 | 없음 |
protected | 필드, 생성자, 메소드 | 같은 패키지이거나, 자식 객체만 사용 가능 |
(default) | 클래스, 필드, 생성자, 메소드 | 같은 패키지 |
private | 필드, 생성자, 메소드 | 객체 내부 |
※ 접근 제한이 강화 되는 순서 : public -> protected -> default -> private
( default & public을 제일 많이 사용. 예외적으로 private 도 종종 사용 protected 는 거의 쓰이지 않음 )
[ 클래스의 접근 제한 ]
클래스를 어디에서나 사용할 수 있는 것은 아니다. 클래스가 어떤 접근 제한을 갖느냐에 따라 사용 가능 여부가 결정 된다. 클래스는 public과 default 접근 제한을 가질 수 있다. default는 다른 패키지에서는 사용할 수 없다. public을 접근 제한자에 붙였다면 같은 패키지 뿐만 아니라 다른 패키지에서도 사용할 수 있다.
[ 생성자의 접근 제한 ]
객체를 생성하기 위해 생성자를 어디에서나 호출할 수 있는 것은 아니다. 생성자가 어떤 접근 제한을 갖느냐에 따라 호출 가능 여부가 결정된다. 생성자는 public, default, private 접근 제한을 가질 수 있다.
















Tip ) 이클립스에서는 필드 색깔을 파란색, 매개변수 색깔을 갈색으로 보여줌.
필드와 매개변수를 쉽게 구별할 수 있다.
객체 : 현실세계에 있는 사물 / 상황
핸드폰 -> 모양, 크기, 색, (필드) - 속성
+/-, on/off, (메소드) - 기능
int size;
String color;
void volum( ) { }
캡슐화 목적 -> 정보 은닉 -> 접근 제한자 (private, default, public)
다형성 ( 업캐스팅 / 다운캐스팅 )
절차 지향과 객체지향 ( 1. 갖고 싶은 것을 물어본다 2. 사기 위해 백화점을 간다 3. 구매한다 )