클래스와 객체

growdeveloper ㅣ 2021. 3. 16. 14:19

세상 모든 것이 객체

자바뿐 아니라 객체 지향 언어를 배울 때마다. 당면하는 어려움이 객체(object)에 대한 이해이다, 객체는 어려운 개념이 아니고 TV, 컴퓨터 사람 등 실세계는 객체들의 집합이다. 실세계의 객체들은 자신만의 고유한 특성(state)과 행동(behavior)을 가진다.

 

객체 지향 언어의 특성

객체 지향 언어는 실세계의 객체를 프로그램 내에 표현하기 위해 클래스와 객체 개념을 도입 하였다, 객체 지향 언어는 다음과 같은 특성을 가진다.

 

캡슐화(Encapsulation)

캡슐화란 객체를 캡슐로 싸서 내부를 보호하고 볼 수 없게 하는 것으로 객체의 가장 본질적인 특징이다. 캡슐 약을 생각하면 이해하기 쉽다. 캡슐에 든 약은 어떤 색인지 어떤 성분인지 보이지 않으며, 외부의 접근으로부터 안전하다. 객체는 캡슐화가 기본 원칙이지만 외부와의 접속을 위해 몇 부분만 공개 노출한다(ex 리모컨의 볼륨 조절 채널 조절 등이 이에 해당한다.) 실세계와 달리 자바에서 객체는 클래스(class)라는 캡슐을 사용하여, 필드(멤버 변수)와 메서드(멤버 함수)로 구성된다.

 

상속(Inheritance)

실세계에서 상속은 상위 개체의 속성이 하위 개체에 물려서 하위 개체가 상위 개체의 속성을 모두 가지는 관계이다. 자바의 상속은 자식 클래스가 부모 클래스의 속성을 물려받고 기능을 추가하여 확장(extends)하는 개념이다. 자바에서 부모 클래스를 슈퍼 클래스(super class)라고 부르며 자식 클래스를 서브 클래스(sub class)라고 부른다. 상속은 슈퍼 클래스의 필드와 메서드를 물려받아 코드를 재사용함으로써, 코드 작성에 드는 시간과 비용을 주인다.

public class Animal {
	String name;
	int age;
	
	void eat() {
		System.out.println("먹는중");
	}
	
	void sleep() {
		System.out.println("자는중");
	}
	
	void love() {
		System.out.println("사랑하는중");
	}
}
public class Human extends Animal{
	
	String hobby;
	String job;
	
	void work() {
		System.out.println("일합니다");
	}
	
	void cry() {
		System.out.println("cry입니다");
	}
	
}
public class main {

	public static void main(String[] args) {
		Animal animal;
		Human human;
		animal = new Animal();
		human = new Human();
		
		animal.eat();
		animal.sleep();
		animal.love();
		
		human.eat();
		human.sleep();
		human.love();
		human.work();
		human.cry();
	}
}
먹는중
자는중
사랑하는중
먹는중
자는중
사랑하는중
일합니다

상위의 코드는 Animal 클래스를 물려받은 human 클래스를 작성한 사례이다. 상속 관계에 있는 Animal 클래스의 객체와 Human 클래스의 객체 모양을 보여준다. Animal 클래스의 객체는 Animal 클래스에 선언된 name, age 필드와 eat(), speak(), love()의 5개의 속성으로 구성된다. 한편 Human 클래스의 객체는 Animal 클래스를 물려받기 때문에, Animal 클래스의 5개 속성에 hobby, job, work(), cry()등 Human 클래스에서 추가로 작성된 속성을 모두 가질 수 있다.

 

다형성(Polymorphism)

다형성은 같은 이름의 메서드가 클래스 혹은 객체에 따라 다르게 동작하도록 구현되는 것을 말한다.

자바에서는 다형성의 2가지 종류로 분간하면 편하다.

  • 오버 라이딩(overriding) : 이것은 슈퍼 클래스(부모 클래스)에 구현된 메서드를, 서브 클래스(자식 클래스)에서 동일한 이름으로 자신의 특징에 맞게 다시 구현하는 이른바 메소드 오버 라이딩(overring)으로 불린다.
  • 오버 로딩(overloading) : 클래스 내에서 이름이 같지만 서로 다르게 동작하는 메서드를 여러 개 만드는 메소드 오버 로딩(overloading)이 있다.

growdeveloper.tistory.com/5?category=954840


클래스와 객체

자바를 비롯한 객체 지향 언어에서, 클래스는 객체를 만들어 내기 위한 설계도 혹은 틀이며, 클래스 모양 그대로 생성된 실체가 객체이다. 이러한 연유로 객체를 클래스의 인스턴스(instance)라고도 부른다.

 

클래스 구성

자바 클래스는 class 키워드를 사용하여 선언한다. 멤버는 필드(멤버 변수)와 메서드(멤버 함수)의 두 가지이다.

/*접근권한,클래스 선언,클래스 이름*/
public class Circle {
	
	/*필드 변수*/
	public int radius;//원의 반지름 필드
	public String name;//원의 이름 필드

	/*메서드*/
	public Circle(int radius, String name) {
		super();
		this.radius = radius;
		this.name = name;
	}
	
	public double getArea() { //원의 면적 계산 메소드
		return 3.14*radius*radius;
	}
	
}

 

  • 클래스 선언, class Circle
    이 코드는 이름이 Circle 인 클래스를 선언한다. class 키워드와 클래스 이름으로 선언하고 중괄호({}) 안에 필드(field)와 메서드(method)를 모두 작성한다, 클래스 외부에는 어떤 필드나 메소드를 둘 수 없다. 캡슐화의 원칙 때문이다.
  • 필드와 메소드
    객체 내에 값이 저장할 멤버 변수를 필드라고 부른다. Circle 클래스에는 radius와 name의 두 필드가 있다. 메서드는 함수이며 객체의 행동을 구현한다. getArea() 메소드는 Circle 객체의 반지름 정보(radius)를 이용하여 면적을 계산하여 알려준다.
  • 접근 지정자, public
    Circle이나 필드, 메서드에 붙은 public을 접근 지정자(access specifire)라고 한다. public은 다른 클래스에서 활용하거나 접근할 수 있음을 선언한다. 접근 지정자를 생략할 때 디폴트 접근이라고 부른다.
  • 생성자(constructor)
    클래스의 이름과 동일한 메서드를 특별히 생성자(constructor)라고 한다. 생성자는 객체가 생성될 때 자동으로 호출되는 특별한 메서드다.

 

new연산자와 객체 생성, 그리고 레퍼런스 변수

public class main {

	public static void main(String[] args) {
		
		Circle pizza;// 레퍼런스 변수 생성
		
		//1.객체생성
		//2.생성자를 활용 radius, name값 셋팅
		pizza = new Circle(10,"퇴근마려운피자");
		
		//getArea 메소드 실행
		double r = pizza.getArea();
		System.out.println(r);
		

	}

}
  • 레퍼런스(reference) 변수 선언 
    객체를 생성하기 전, 객체를 가리킬 레퍼런스 변수를 먼저 선언한다. 이 선언문으로는 Circle 타입의 객체가 생성되지 않는다. 변수 pizza는 Circle 클래스의 객체에 대한 주소, 정확히 말해 래퍼런스를 가지는 변수일뿐 객체 자체는 아니다.
    ※레퍼런스 변수는, 객체를 직접 저장하는 것이 아니라 객체의 위치를 저장하는 방식, 객체를 간접적으로 가리키는 방식을 취합니다.
  • 객체 생성, new 연산자 이용
    자바에서는 반드시 new 연산자를 사용하여 객체를 생성한다.
    생성된 Circle 객체의 주소 즉 레퍼런스를 pizza 변수에 대입한다.
    1. Circle 타입 크기의 메모리 할당.
    2. Circle() 생성자 코드 실행.

생성자

생성자(constructor)는 객체가 생성될 때 객체의 초기화를 위해 실행되는 메서드다. 

 

생성자 선언 및 활용

생성자는 객체가 생성되는 순간에 자동으로 호출되는 메서드로서 객체에 필요한 초기화를 실행하는 코드를 담아야 한다.

class Circle {

	/* 필드 변수 */
	public int radius;// 원의 반지름 필드
	public String name;// 원의 이름 필드

	public Circle() {

	}

	/* 메서드 */
	public Circle(int radius, String name) {
		super();
		this.radius = radius;
		this.name = name;
	}

	public double getArea() { // 원의 면적 계산 메소드
		return 3.14 * radius * radius;
	}
}

public class main {

	public static void main(String[] args) {

		Circle pizza;// 레퍼런스 변수 생성
		Circle dount;
		
		// 1.객체생성
		// 2.생성자를 활용 radius, name값 셋팅
		pizza = new Circle(10, "퇴근마려운피자");
		dount = new Circle(1,"퇴근마려운 도넛");
		// getArea 메소드 실행
		System.out.println(pizza.getArea());
		System.out.println(dount.getArea());
		
		
		
	}

}
  • 생성자의 이름은 클래스 이름과 동일하다.
    생성자의 이름은 반드시 클래스 이름과 동일하게 작성해야 한다.
public class circle {
	public circle(int r, String n) {---}
}
  • 생성자는 여러 개 작성 (오버 로딩)할 수 있다.
    객체 생성은 반드시 new를 통해서만 이루어지며, 생성자는 이때 자동으로 한 번만 호출된다. 호출하고 싶을 때 아무 때나 호출할 수 있는 메서드가 아니다.
public class Circle {
	public Circle() {---}
    public Circle(int r, String n) {---} //생성자
}
  • 생성자는 리턴 타입을 지정할 수 없다.
    생성자는 어떤 값도 리턴하지 않기 때문에 다음과 같이 리턴 타입을 선언해서는 안 된다.
    리턴 값이 없다고 해서. void를 리턴 값으로 지정하면 안된다. 하지만, 생성자에서 return 문을 사용할 수 없다는 뜻은 아니다. return 문은 메서드의 실행을 끝내고 호출한 곳으로 돌아가라는 명령이므로, 생성자의 실행을 끝내고자 하면 생성자 코드 내 어디서든 return; 문을 사용하면 된다.
public Circle() {---}//리턴 타입 선언하지 않음
public Void Circle() {---}//오류 void를 리턴 타입으로 사용할 수 없음
  • 생성자의 목적은 객체가 생성될 때, 필요한 초기 작업을 위함이다.
    다음 객체 생성 문은 Circle(int r, String n) 생성자를 호출하여 객체의 radius를 10으로 이름을 퇴근 마려운 피자로 초기화한다.
pizza = new Circle(10, "퇴근마려운피자");

기본 생성자

기본 생성자(default contstructor)란 매개변수와 실행 코드가 없어 아무 일도 하지 않고 단순 리턴하는 생성자이다. 

  • 기본 생성자가 자동으로 생성되는 경우
    생성자가 없는 클래스는 있을 수 없다, 객체가 생성될 때 반드시 생성자가 실행되기 때문이다. 그러므로 생성자가 하나도 없는 경우, 컴파일러는 기본 생성자를 자동으로 생성한다.
public class Circle {
	int radius;
    void set(int r) {radius = r;}
    
    public static void main(String [] args){
 		Circle pizza = new Circle();   
    	pizza.set(3);
    }
}

 

  • 기본 생성자가 자동으로 생성되지 않는 경우.
public class Circle {
	int radius;
    void set(int r) {radius = r;}
    double getArea() {return 3.14 * radius * radius};
    
    public Circle(int r){
    	radius = r;
    }
    
    public static void main(String [] args) {
    	Circle pizza = new Circle(10);
        System.out.println(pizza.getArea());
        
        Circle dount = new Circle(); //오류발생 The constructor Circle() is undefined
        System.out.println(dount.getArea());
    }
}
  • 생성자가 하나라도 존재하는 클래스에는 컴파일러가 기본 생성자를 삽입해 주지 않는다.
  • Circle 클래스에는 매개변수를 가진 생성자가 작성되어 있기 때문에, 컴파일러는 기본 생성자를 만들어진 않는다. 그러므로 앞의 new 문장은 컴파일 오류를 발생시킨다.

this 레퍼런스

this는 자바의 중요한 키워드로서 단어 뜻 그대로 객체 자신을 가리키는 레퍼런스이다.

 

this의 기초 개념

this는 현재 객체 자신에 대한 레퍼런스이다. 보다 정확히 말하면 현재 실행되고 있는 메서드가 속한 객체에 대한 레퍼런스이다. this는 컴파일러에 의해 자동관리되므로 개발자는 this를 사용하기만 하면 된다.

public class Circle {
	int radius;
	public Circle(int r) {
		this.radius = r;
	}
	public int getRadius() {
		return radius;
	}

}

this의 필요성

앞의 Circle 클래스에는 메서드 getRadius()는 다음과 같이 this를 사용하지 않았다. 클래스 내에서 멤버 radius를 접근할 때 굳이 this.radius로 할 필요가 없다.

return radius;// return this.radius;와 동일

그렇다면 this는 언제 필요할까? 매개변수의 이름은 그 자체로서 코드를 읽는 사람에게 그 용도를 나타내므로, 적합한 이름을 붙이는 것은 매우 중요하다. 그래서 Circle(int r) 생성자의 매개변수를 r 대신 다음과 같이 radius로 변경하는 것이 좋다.

public Circle(int radius) {radius = radius;}

하지만 이렇게 변경하면 어떤 일이 발생할까? 이 질문은 의외로 까다로운 질문이다. 생성자의 코드를 보자.

radius = radius;

이 코드에서 2개의 radius는 모두 Circle(int radius)의 매개변수 radius를 접근 하기 때문에, 멤버 radius를 변경하지 못한다. 자바에서는 이 경우처럼 매개변수의 이름을 멤버 변수와 같은 이름으로 붙이고자 하는 경우가 허다한데. 이때 다음과 같이 this를 이용하면 된다.

public Circle(int radius) {this.radius = radius;}

또 메서드가 객체 자신의 레퍼런스를 리턴해야 하는 경우가 있는데. 이때 다음과 같이 this를 리턴하면 된다.

public Circle getMe() {return this;}//getMe() 메소드는 객체 자신의 레퍼런스 리턴

this()로 다른 생성자 호출

this()는 클래스 내에서 생성자가 다른 생성자를 호출할 때 사용하는 자바 코드이다.

public class Book {

	String title;
	String author;
	
	void show() {
		System.out.println(title + "" + author);
	}
	
	public Book() {
		this("","");
		System.out.println("생성자 호출됨");
	}
	
	public Book(String title) {//2
		this(title,"작자미상");
	}
	
	public Book(String title, String author) {
		this.title = title;
		this.author = author;
	}
	
	public static void main(String[] args) {
		Book littlePrince = new Book("어린왕자","생택쥐페리");
		Book loveStory = new Book("춘향전"); //1
		Book emptyBook = new Book();
		loveStory.show();
		
	}

}

 1번과 같이 생성자를 호출한다 이때 2번으로 가는데 this(title, "작자미상"); 부분에서 아래의 오버 로드된 Book으로 전달된다.

 

this 사용 시 주의할 점

this() 사용 시 다음과 같이 주의할 사항이 있다.

  • this()는 반드시 생성자 코드에서만 호출할 수 있다.
  • this()는 반드시 같은 클래스 내 다른 생성자를 호출할 때 사용된다.
  • this()는 반드시 생성자의 첫 번째 문장이 되어야 한다.

'자바' 카테고리의 다른 글

JVM 구조  (0) 2021.04.19
스레드  (0) 2021.04.08
추상 메소드  (0) 2021.04.08
다형성  (0) 2021.03.16
메소드 오버로딩  (0) 2021.03.13