객체 지향 프로그래밍(oop) (1)

김용민·2023년 3월 23일
0

객체 지향 프로그래밍 (Object Oriented Programming, oop)

절차 지향 프로그래밍과 반대되는 말로
(Procedure Programming)
절차 지향은 함수가 코드의 기본 단위가 된다는 말, 그래서 대표적인 절차지향인 C언어에서는
구조체와 함수는 결합할 수 없다 (간접적으로 포인터를 활용하면 가능)

반대로 객체지향 프로그래밍의 특징을 살펴보자면

  • 객체는 변수와 함수(속성과 기능)을 내장할 수 있다
  • 객체의 내부 속성과 기능에 따라 대부분의 객체가 상호작용하여 진행한다
  • 객체 외부에서 객체를 작동시키는 건 함수 호출정도의 간단한 코드만 필요

클래스는 자료형이고 객체는 실제 데이터이기 때문에
하나의 클래스를 이용하여 여러 객체를 생성할 수 있다

public static void main(String[] args) throws Exception {
int n1 = 10;
		int n2 = 20;
		
		System.out.println(n1);	// 함수를 내장하고 있지 않아서 외부에서 직접 출력함수를 사용해서 처리해야 한다 (println은 n1이라는 변수에 내장된 함수가 아님)
		System.out.println(n2);
		
		Process p1 = Runtime.getRuntime().exec("notepad");	// 메모장 실행 프로세스를 저장한 객체 p1
		Process p2 = Runtime.getRuntime().exec("mspaint");	// 그림판 실행 프로세스를 저장한 객체 p2
		
		Thread.sleep(2000);
		p1.destroy();		// 객체는 스스로 어떤 작업을 수행할 수 있는 함수(메서드)를 내장하고 있다
		
		
		Thread.sleep(2000);	// 어떤 메서드는 객체 없이 클래스에서 바로 호출할 수도 있다
		p2.destroy();
        }

클래스의 구성요소

  • 객체는 속성정보를 저장할 수 있어야 한다 (멤버 필드) - 데이터 저장 공간
  • 객체는 기능을 포함할 수 있어야 한다(멤버 메서드) - 움직임(출력, 전송)
  • 객체는 생성과 소멸단계를 포함한 생명주기가 있다 (생성자) - 객체의 생성과 생성 시 추가 필요 작동구성
  • 객체는 외부의 다른 객체와 상호작용할 수 있어야 한다 (접근 제한자) - 객체의 상호작용을 허용할 것인지 거부할 것인지 결정
  • 객체의 소멸은 자바 가상 머신의 Garbage Collector가 처리한다
public class EX02 {
	public static void main(String[] args) {
		// 클래스를 활용하지 않은 형태로 코드를 진행한다면
		String name = "홍길동";
		int age = 20;
		
		System.out.printf("이름은 %s이고, 나이는 %d살 입니다\n", name, age);
		
		// 클래스를 활용하여 코드를 진행한다면
		// 1) 만들어진 클래스로 객체를 생성
		Student st1 = new Student();
		
		// 2) 객체의 멤버 필드는 일반 변수처럼 사용하면 된다
		st1.name = "이지은";
		st1.age = 31;
		
		// 3) 객체의 멤버 메서드는 객체를 통하여 바로 호출할 수 있다
		st1.show();	// 메서드의 내용이 아무리 길어도 호출을 통하여 손쉽게 지시할 수 있다
		
		
		Student st2 = new Student();
		st2.name = "나단비";	// 멤버 필드는 객체마다 다른 속성을 저장할 수 있다
		st2.age = 5;
		st2.show();			// 같은 메서드를 호출해도 저장된 값이 다르니 결과가 달라진다
		
	}
}

▶생성자

  • 객체를 생성할 때 호출하는 특수한 함수
  • 자바에서는 오로지, 생성자를 통한 객체 생성만 가능하다
  • 예외적으로 함수형 인터페이스의 람다식 객체 생성이 있으나 방식이 다소 다르다
  • 참조변수를 여러개 만들었다고 하더라도, 생성자를 호출하지 않았다면 객체가 없는 것이다

생성자의 특징

  • 이름은 클래스의 이름과 동일하다
  • 생성자는 함수이지만, 반환형을 지정하지 않는다
  • 생성자는 함수이므로, 오버로딩이 가능하다
  • 생성자의 주 목적은 객체를 생성하는 것이고
  • 또 다른 목적은 객체 생성시 초기작동을 추가하는 것이다
  • 만일 클래스 작성시, 생성자를 아예 만들지 않으면 컴파일 시에 클래스에 기본 생성자 코드가 추가된다

❗기본 생성자 : 매개변수를 전달 받지 않는 생성자

class Product{
	String name;
	int price;
	
	// 메서드 (상품정보를 간략히 출력하는 기능)
	void show() {
		System.out.printf("%s : %,d원\n",name, price);
	}
	
	// 생성자
	Product() {
		System.out.println("비어있는 Product 객체 하나 생성");
	}
	
	// 생성자는 함수이므로, 오버로딩이 가능하다
	// 함수이름이 같아도, 매개변수 타입이 다르거나 순서가 다르다면 여러개 정의할 수 있다
	Product(String name, int price){
		// 하나의 클래스로 여러 객체를 생성할 때, 현재 작업중인 객체 자기 자신을 가리키기 위해
		// this라는 키워드를 사용한다
		this.name = name;		// 전달받은 이름을 멤버 필드 name에 저장한다
		this.price = price;		// 전달받은 가격을 멤버 필드 price에 저장한다
		System.out.println("데이터를 전달받아서 Product 객체 하나 생성");
	}
}

public class Ex03_Constructor {
	public static void main(String[] args) {
		Product p1 = new Product();
		p1.name = "아메리카노";
		p1.price = 2000;
		p1.show();
		
		// 초기값을 전달하여 객체를 생성했기 때문에, 대입 과정을 생략할 수 있다
		Product p2 = new Product("돌체라떼", 4000);
		p2.show();
		
		// 자바는 생성자를 호출하지 않으면 객체를 생성하지 않는다
//		Product p3 = null;
//		p3.show();
		
		// 클래스를 작성할 때, 생성자를 아예 손대지 않으면(만들지 않으면)
		// 컴파일 시에 클래스에 기본 생성자 코드를 추가해준다
		
		// 기본 생성자 : 매개변수를 전달받지 않는 생성자
	}
}

▶클래스의 구성요소 좀 더 파헤치기 !

  • 필드(field)

    1. 객체가 생성되면 객체의 속성을 저장할 수 있는 변수
    2. 필드는 초기값을 지정할 수 있다. 단, 초기값을 지정하지 않으면 자료형에 맞는 0 으로 채워진다
    3. 메서드에서 조건없이 필드를 참조할 수 있다
    4. 메서드의 지역변수와 이름이 중복되면 this를 이용하여 구분할 수 있다
  • 메서드(method)

    1. 객체의 기능을 담당하는 함수이다
    2. 메서드는 함수이므로 반환형, 함수이름, 매개변수를 지정하고 내용을 작성한다
    3. 메서드에서 조건없이 필드를 참조할 수 있다
    4. 서로 다른 객체라도 같은 메서드를 수행할 수 있다. 내용을 필드의 값에 따라서 달라진다
    5. 메서드 내부에서 연산자 및 제어문을 사용할 수 있다
    6. 메서드는 오버로딩이 가능하다
  • 생성자

    1. 객체를 생성하기 위해서 반드시 호출해야 하는 함수
    2. 반환형을 작성하지 않는다 (자기 자신을 반환하기 때문에)
    3. 함수 이름은 클래스의 이름과 같아야 한다
    4. 역시나 함수이므로 오버로딩이 가능하다
    5. 생성자 안에서 또 다른 생성자를 호출할 수 있다
    6. 생성자 내부에서 다른 생성자를 호출할때는 this() 형식으로 호출해야 한다
    7. 생성자 내부에서 다른 생성자를 호출하기 위해서는 반드시! 첫번째 줄에만 호출해야한다
package oop;

class Pos {
	
	// 필드
	int x,y;
	
	// 메서드
	void show() {
		System.out.println("x : " + x + ", y : " + y);
	}
	
	// 생성자
	// 여기서 this의 3가지 용법 
    // (this.(member) : 해당 객체의 멤버 호출, this() : 나와 같은 이름의 함수(생성자)
	// 	this : 객체의 참조 주소값 자체)
	// 생성자 내부에서 다른 생성자를 호출하기 위해서는 반.드.시 첫번쨰 줄에서만 호출할 수 있다
	// why? 생성이 마무리 되고나서 값을 채워주는것이 낫기 때문에
    //(만약에 생성이 뒤에 되면 초기값으로 초기화 될 수 있기 때문)
    
	Pos() {
		System.out.println("기본 생성자 호출 !!");
	}
	
    // Constructor call must be the first statement in a constructor
	// 생성자 호출은 반드시 생성자 내의 첫번째 줄에서만 이루어져야 합니다
	Pos(int x , int y){
		this();
		System.out.println("오버로딩 생성자 호출 !!");
		this.x = x;
		this.y = y;
	}
}

public class Ex04 {
	public static void main(String[] args) {
		Pos ob1 = new Pos();	// 값을 전달하지 않으면
		ob1.show();				// 필드의 초기값은 0이다
		
		System.out.println("---------------------");
		Pos ob2 = new Pos(3,4);
		ob2.show();
	}
}

위의 코드를 실행하면 콘솔창은 아래와 같이 뜬다

▶접근 제어자(Access Modifier)

클래스에 작성된 멤버 요소에 대해 접근할때 그 제한을 설정하는 키워드

  • private : 클래스 내부에서만 접근 가능, 같은 클래스의 서로 다른 객체끼리는 접근 가능
  • package : 같은 패키지 폴더 내부의 클래스 끼리 접근 가능, 기본값(아무것도 안적으면 이걸로 들어감)
  • protected : 패키지 범위를 포함하며, 패키지가 달라도 상속관계라면 접근 가능
  • public : 클래스 외부에서도 누구나 접근 가능한 요소

접근 제어자에 대한 간단한 코드를 보자

class Test{
	private int n1 =1 ;
	int n2 = 2;
	protected int n3 = 3;
	public int n4 = 4;
	
	void show() {
		System.out.println("n1 : " +  n1);
		System.out.println("n2 : " +  n2);
		System.out.println("n3 : " +  n3);
		System.out.println("n4 : " +  n4);
	}
}

public class Ex06_AccessModifier {
	public static void main(String[] args) {
		Test t1 = new Test();
		t1.show();
		
		// The field Test.n1 is not visible
		// 공개하지 않았기 때문에, 외부에서 볼 수 없다 (존재하지 않는다라고 하지 않고 보이지 않는다 라고 함)
		
//		System.out.println("n1 : " +  t1.n1);
		System.out.println("n2 : " +  t1.n2);
		System.out.println("n3 : " +  t1.n3);
		System.out.println("n4 : " +  t1.n4);
		
		// private 필드의 값은 직접 참조할 수 없다
		// 그러나, t1.show()의 형태처럼 [내부 메서드]를 통해서는 접근할 수 있다
		
		// 특정 필드의 값을 반환하는 메서드를 getter라고 한다
		// 특정 필드에 값을 대입하는 메서드를 setter라고 한다
		
		// getter와 setter는 접근제한자 개념에 의해 만들어진 요소이고
		// 필드는 private, 메서드에는 public을 걸어주는  것이 일반적이다
		
		// 단, 누구나 참조할 수 있어야 하는 static final 필드는 public을 걸어준다
		
		// java.lang.Integer
		System.out.println(Integer.MIN_VALUE);
	}
}

위의 코드를 보면 System.out.println("n1 : " + t1.n1);을 실행했을때
오류 메세지인 (The field Test.n1 is not visible)이 뜨는 것을 볼 수 있는데
not exist가 아니라 not visible인 것은 위의 Test클래스에 n1은 private처리가 되어있기 때문에
Ex06_AccessModifier 클래스 소속인 메인 메서드는 Test클래스의 n1을 가져 올 수 없다

그러나, private의 특징이 같은 클래스 내에서는 활용이 가능하므로 메서드를 이용해서는 가능하다.

profile
안녕하세요

0개의 댓글