객체란 모든 실재하는 것을 의미한다.
객체 지향이란 이러한 객체 개념을 기반으로 프로그램을 설계할 때 객체 단위로 파악하고, 이들 간의 상호작용을 통해 기능을 구현하는 프로그래밍 패러다임이다.
OOP(Object Oriented Programming)라고도 한다.
OOP의 4가지 특징은 아래와 같다.
클래스는 객체가 어떤 상태, 어떤 동작을 가져야 하는지에 대한 설계도이다.
public class Person{
// 필드
private String name;
private int age;
// 생성자
public Person(String name){
this.name = name;
}
// 메서드
public Stirng getName(){
return name;
}
}
클래스는 위처럼 필드, 생성자, 메서드로 구성돼있다.
인스턴스는 실체화된 객체로, 클래스의 설계도로 만들어진 객체라고 생각하면 된다.
인스턴스는 new연산자를 활용해 생성해준다.
Person p = new Person();
위 코드는 Person 클래스의 p 라는 인스턴스를 생성한 것이다.
객체의 상태와 동작을 나타내는 클래스에서 객체의 상태에 해당한다.
일반적인 변수 선언 방식과 동일하며 타입 앞에 접근제어자를 추가해준다.
필드는 private String name = "홍길동";과 같이 선언과 동시에 초기화도 해줄 수 있다.
필드에 접근하기 위해서는 .이라는 접근연산자가 필요하다. 접근 제어자에 따라 필드에 접근 가능여부가 결정된다.
여기서 접근이란 변수를 호출하거나 값을 변경한다는 의미이다.
p.name = "홍길순"; // error
p.age = 15;
Person 클래스의 name은 private이기 때문에 접근이 불가하고, age는 public이기 때문에 접근이 가능하다.
메서드란 객체가 행하는 동작을 정의한 것이다.
C/C++의 함수와 같다고 보면된다.
public String getName(){
return name;
}
메서드는 위 4가지를 꼭 포함시켜야 한다.
한 클래스 네에서 동일한 이름을 가진 메서드를 여러개 선언 가능하며 이를 오버로딩이라한다.
단, 오버로딩을 할 땐 메서드명은 같되, 파라미터의 타입과 개수 중 하나 이상이 달라야한다.
public class Person{
public void walk(){
System.out.println("walking");
}
public void walk(String place){
System.out.println("walking at " + place);
}
자바는 메서드를 구분할 때에 메서드 시그니처를 이용해 판별하는데,
메서드 시그니처는 메서드명, 파라미터의 자료형과 개수이다.
생성자는 인스턴스를 생성할 때 호출되는 메서드이다.
각 클래스마다 생성자는 하나 이상 꼭 포함되어야 하며 생성자를 명시하지 않았을 경우, 파라미터와 내용이 없는 기본 생성자가 자동으로 추가된다.
생성자는 주로 클래스의 필드를 초기화하는 목적으로 사용된다.
public Person(String name, int age){
this.name = name;
this.age = age;
}
생성자의 특징은 아래와 같다.
this 를 이용해 객체의 필드와 파라미터를 구분
this는this를 사용하는 클래스 자기자신을 가리킨다. 즉, 자기 자신을 참조한다는 뜻이다.
또한,this()는 자기자신의 생성자를 호출한다.
상속이란 기존에 존재하던 클래스의 필드드와 메서드를 그대로 물려받아 멤버 변수 및 메서드를 추가하거나 재정의하여 새로운 클래스를 정의하는 것이다.
물려주는 클래스를 상위클래스, super class, 부모 클래스라 칭하며,
물려받는 클래스를 하위클래스, sub class, 자식 클래스라 칭한다.
public class Person{
private String name;
private int age;
public void walk(){
System.out.println("walking");
}
public class Programmer extends Person{
private String department;
public void coding(){
System.out.println("coding");
}
}
위 코드는 Student 클래스가 Person을 상속 받은 것으로 Programmer는 Person의 필드와 메서드를 사용할 수 있다.

클래스의 상속 관계는 위 그림과 같이 자식이 부모를 포함하고 있는 관계이다.
자식 클래스는 부모 클래스의 멤버를 모두 포함하고, 추가적으로 본인의 멤버까지 포함한다.
그렇기 때문에 부모 객체는 자식의 멤버를 호출할 수 없다.
오버라이딩은 상속 받은 부모의 메서드를 자식클래스에서 재정의 하는 것이다.
같은 메서드 시그니처를 가진 동작을 자식 클래스에 맞게 수정이 가능하여 새로운 메서드 선언이 불필요해진다.
@Override
public void walk(){
System.out.println("programmer walking");
}
@Override는 에노테이션으로 오버라이딩 된 메서드임을 명시해준다.
자식 클래스의 생성자는 부모 클래스의 생성자를 포함시켜야 한다.
자식을 생성할 때 부모 클래스를 먼저 생성해야 하기 때문이다.
public class Person{
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
public class Programmer extends Person{
private String department;
public Programmer(String name, int age, String department){
super(name, age);
this.department = department;
}
}
super는this와 같이 참조 변수 역할을 하는데 차이점은 부모 클래스의 멤버를 참조한다는 것이다.
마찬가지로super()는 부모의 생성자를 호출할 때 사용한다.
캡슐화는 외부로부터 클래스에 정의된 멤버 변수와 메서드를 보호하고 필요한 데이터만 외부에 노출함으로써 데이터를 보호하고 은닉하는 것이 주목적이다.

위 그림처럼 멤버 변수와 메서드를 클래스라는 캡슐안에 담는다는 의미에서 캡슐화라한다.
캡슐화를 구현할 때의 핵심은 접근제어자와 getter/setter 메서드를 이용하는 것이다.
외부에서 객체가 가진 정보에 접근하거나 변경할 수 있다면 의도하지 않은 값으로 조작될 위험이 있다.
특히 협업을 진행하는 과정에서 변경하면 안되는 값을 다른 사람이 변경하거나 삭제를 해버리는 상황이 올 수도 있다.
이러한 불상사를 겪지 않기 위해서 아래의 접근 제어자를 적재적소에 잘 활용해야한다.
publicprotecteddefaultprivate| 접근 제어자 | 같은 클래스 | 같은 패키지 | 자식 클래스 /다른 패키지 |
다른 패키지 |
|---|---|---|---|---|
public |
O | O | O | O |
protected |
O | O | O | X |
default |
O | O | X | X |
private |
O | X | X | X |
getter/setter 메서드는 접근 제어자에 의해 접근이 불가한 멤버 변수에 간접적으로 접근 할 수 있는 메서드다.
getter 메서드는 멤버 변수를 반환하고, setter 메서드는 멤버 변수를 변경하는 메서드이다.
public class Person{
private int age;
public String getAge(){
return age;
}
public void setAge(int age){
if(age < 0)
throw new IllegalArgumentException("나이는 0세 이상이어야 합니다.");
this.age = age;
}
}
setter 메서드에서 검증 절차를 거쳐 유효한 데이터만 저장할 수 있도록 설계가 가능하다.
다형성의 생물학적 의미는 동종 개체에서 대립형질이 뚜렷이 구별되어 나타나는 것이다.
이와 같이 프로그래밍에서 다형성은 하나의 이름을 가진 변수, 메서드, 클래스가 다양한 의미로 해석 될 수 있도록 구현하는 것이다.
다형성을 구현하는 핵심적인 3가지 방법은 오버로딩 , 오버라이딩, 클래스 간의 계층 관계를 활용하는 것이다.
多形性(다형성)을 직역하면 여러가지 형태를 가질 수 있는 능력이다.
상속 관계를 활용하여 한 타입의 변수로 여러 객체를 참조 가능하다.
즉, 상위 클래스 타입의 참조 변수로 하위 클래스 객체를 참조할 수 있다.
Person person1 = new Programmer();
Person person2 = new Singer();
Person person3 = new Dancer();
위와 같이 Person이란 한 타입에 Person을 상속한 여러 객체를 참조한다.
하위 클래스를 참조한 객체는 기본적으로 타입이 부모이기 때문에 부모 클래스의 필드와 메서드만 사용할 수 있다.
단, 동적 바인딩에 의해 오버라이딩 된 메서드는 사용가능하다.
ex)
public class Person{
public String name;
public int age;
public void work(){
System.out.println("working");
}
}
public class Programmer extends Person{
public String company;
@Override
public void work(){
System.out.println("coding");
}
}
public class Main{
public static void main(String[] args){
Person person = new Programmer();
person.name;
person.age;
person.company; // compile error
person.work(); // coding
}
}
추상화의 사전적 의미는 사물이나 표상을 어떤 성질, 공통성, 본질에 착안하여 그것을 추출하여 파악하는 것이다.
이에 착안하여 프로그래밍에서의 추상화는 클래스 간 공통적인 속성을 찾아내어 공통의 조상을 만드는 작업을 의미한다.
추상 클래스는 abstract를 앞에 붙여 선언한다.
public abstract class Animal{
public String name;
public int age;
public abstract void eat();
public void run(){
System.out.println("run");
}
}
추상 메서드는 메서드 시그니처만 있고 구현 로직이 없는 메서드이다.
public abstract void eat();
추상 클래스의 인스턴스를 생성하려면 생성과 동시에 추상 매서드를 정의해줘야 한다.
Animal animal = new Animal() {
@Override
public void run() {
System.out.println("Animal run");
}
}
인터페이스는 구현해야 하는 동작(메서드)를 작성하고, 구현을 강제하는 일종의 명세서이다.
인터페이스도 접근 제어자 사용이 가능하며 모든 메서드가 public abstract 제어자로 선언이된다.
즉, 모든 메서드가 추상메서드인것이다.
인터페이스를 구현한다는 것은 상속을 받아서 오버라이딩하는 것과 유사하다고 볼 수 있다.
인터페이스 또한 extends를 이용해 다른 인터페이스를 상속 받을 수 있다.
interface A{}
interface B{}
interface Driving extends A, B{}
class Car{}
class GasolineCar extends Car implements Driving{}
다음은 위 GasolineCar 클래스의 상속과 구현 관계를 그림으로 표현한 것이다.
