객체지향 프로그래밍 -1

cutiepazzipozzi·2023년 4월 21일
1

지식스택

목록 보기
22/35
post-thumbnail

드디어...!!!

[객체지향언어]

= 실제 세계는 사물(객체)로 이루어져 있으며, 발생하는 모든 사건들은 사물 간의 상호작용이다.

초창기 객체지향이론은 주로 과학실험과 같은 실제 세계와 유사한 가상 세계를 컴퓨터에 구현하고자 실시했던 모의 실험을 목적으로 등장하였다.

처음에는 절차적 언어가 주류를 이뤘으나, 프로그램의 규모가 점점 커지고 사용자들의 요구를 빠르게 맞춰주기 위해 객체지향언어가 사용하게 되면서 입지를 넓힐 수 있었다.

객체지향언어의 특징

  1. 코드의 재사용성이 높다.
  2. 코드 관리가 용이하다.
  3. 신뢰성이 높은 프로그래밍을 가능하게 한다.
    (제어자와 메서드를 이용해 데이터를 보호하고 올바른 값을 유지하도록 하고, 코드의 중복을 제거함)

= 재사용성, 중복제거, 유지보수이 세 키워드로 기억!!
(어떻게 객체지향 프로그래밍이 잘 유지보수가 되는지 궁금했는데, 아마도 이는 모듈화, 캡슐화, 상속과 다형성 등의 객체지향의 특징이 나타나 있는 다음 단원에서 배운닷)

** 객체지향 프로그래밍은 거시적 관점에서의 설계 능력을 요구하기 때문에 프로그램의 기능을 완성한 후 객체지향으로 코드 개선의 방안을 고려하는 것이 Good!!

[클래스와 객체]

(객체지향적 관점에서의) 클래스

  • 정의: 객체를 정의해놓은 것 = 객체의 설계도 or 틀
  • 용도: 객체를 생성하는데 사용

객체

  • 정의: 실제로 존재하는 것 (ex. 양말, 이불, 추상적 개념)
  • 프로그래밍에서의 객체: 클래스에 정의된 내용대로 메모리에 생성

클래스와 객체의 관계

  • 붕어빵 틀(클래스) - 만들어진 팥 붕어빵(객체)
  • 이처럼 클래스는 객체를 생성하는데만 사용될뿐이다.
  • 그래도 객체 생성 전 클래스 생성이 강제되는 이유는 클래스를 한번 잘 만들어 놓으면, 어떻게 객체를 만들어야 할지 고민하지 않아도 되니까!
    (ex. 설계도를 야무지게 만들어놓으면 이를 이용한 건물 만들기는 수월함)

** 참고로 JDK(Java Development Kit)에서는 프로그래밍을 위한 수많은 클래스를 기본적으로 제공하고 있음

객체와 인스턴스

인스턴스

= 클래스로부터 만들어진 객체
= (인스턴스화) 클래스로부터 객체를 만드는 과정
(ex. 붕어빵 틀로부터 만들어진 팥 붕어빵을 그 클래스의 인스턴스라고 부름)

객체와 인스턴스의 차이?

  • 객체는 포괄적인 의미(모든 인스턴스를 포함하는)
  • 인스턴스는 어떤 클래스로부터 만들어진 것인지를 강조하는 구체적 의미
    (ex. 책상은 객체다 / 책상은 책상 클래스의 인스턴스이다 )

객체의 구성요소

객체는 일반적으로 다수의 속성과 다수의 기능을 가진다.
= 속성과 기능의 집합!

속성 => 멤버변수

  • 멤버변수, 특성, 필드, 상태

기능 => 메서드

  • 메서드, 행위, 함수

tv를 예로 들어보자.

class TV {
	int channel;
    int volume;
    
    void volumeUp() {++volume;}
    void channelUp() {++channel;}
}

인스턴스 생성 및 사용

  • 생성: 클래스명 변수명 = new 클래스명();
  • 사용:
//위의 예제에 이어서 main메서드를 작성해보았다.
public static void main(String[] args) {
	TV tv = new TV();
    //연산자 new에 의해 인스턴스가 메모리의 빈 공간에 생성
    //멤버변수는 각 자료형의 기본값으로 초기화됨
    tv.volume = 10;
    //멤버변수 volume에 10을 저장
    tv.volumeUp();
    System.out.println(t.volume);
}
  • 인스턴스는 참조변수를 통해서만 다룰 수 있으며,
    참조변수 타입 = 인스턴스 타입이다.
    (위의 예시에서 참조변수는 tv(new연산자를 통해 선언하기 전), 인스턴스는 new 연산자를 통해 TV 클래스를 참조한다)
  • 하나의 인스턴스를 여러개의 참조변수가 가리키는 것은 가능,
    여러개의 인스턴스를 하나의 참조변수가 가리키는 경우는 불가능!
public static void main(String[] args) {
	TV t1 = new TV();
	TV t2 = new TV();
    System.out.println("t2의 channel값은: "+t1.channel+"입니다");
    t2 = t1; //t1이 저장하고 있는 값(주소)을 t2에 저장함!!
    t1.channel = 7;
    System.out.println("t2의 channel값은: "+t1.channel+"입니다");
    
    //t2의 channel값은: 0입니다
    //t2의 channel값은: 7입니다
}

** 자신을 참조하고 있는 참조변수가 하나도 없는 인스턴스는 GC에 의해 자동적으로 메모리에서 제거됨

프로그래밍 관점에서의 클래스

  1. 데이터와 함수의 결합

아래의 그림은 데이터 저장개념의 발전 과정이다.

- 변수: 하나의 데이터를 저장할 수 있는 공간
- 배열: 같은 자료형인 여러 데이터를 하나의 묶음으로 저장할 수 있는 공간
- 구조체: 여러 데이터를 종류에 관계없이 하나의 집합으로 저장하는 공간
- 클래스: 데이터와 함수의 결합

자바는 변수와 함수를 하나의 클래스에 저장하여 관계가 깊은 변수와 함수들을 함께 다루도록 하였다. 이렇듯 클래스서로 관련된 변수들을 정리하고 이들에 대한 작업을 수행하는 함수들을 함께 정의한 것이다.

ex. C언어와 달리 자바는 String을 클래스로 분류하는데, 이는 문자열을 다루는데 필요한 함수들을 묶기 위해서이다.

public final class String implements java.io.Serializable, Comparable {
	private char[] value;
    
    pubilc String replace(char oldChar, char newChar) {
    	,,
        char[] val = value; //같은 클래스 내의 변수를 사용해 작업
        // = 변수와 함수가 유기적으로 연결
        ,,
    }
}
  1. 사용자정의 타입

사용자정의 타입 = 프로그래머가 서로 관련된 변수들을 묶어서 하나의 타입으로 새로 추가하는 것

사실 그래서 클래스도 사용자정의 타입!!
** 여기서 참조형의 개수가 제한되지 않는 이유가 나온다. 프로그래머가 새로운 타입을 추가할 수 있기 때문!! (오 신기)

ex. 시간을 예로 들어보자.
3개의 시간을 다뤄야 할 때, 우리가 클래스를 활용하지 않으면

int hour1, hour2, hour3;
int minute1, minute2, minute3;
float second1, second2, second3;

int[] hour = new int[3];

이렇게 비효율적인 코드가 탄생한다ㅠㅠ

그래서 클래스를 활용하면!

class Time {
	int hour;
    int minute;
    float second;
}

Time[] time = new Time[3];

클래스는 여기에서 더 나아가 제어자와 메서드를 이용해 시간, 분, 초의 수 조건(0보다 크고, 시간은 23이하여야 하고 등등)을 제약할 수 있다.

[변수와 메서드]

선언위치에 따른 변수의 종류

변수를 구분하는 기준 = 변수가 선언된 위치

class Ex1 {
	int var1; //인스턴스 변수
    static int var2; //클래스 변수
    
    void method() {
    	int var3 = 0; //지역변수
    }
}
  1. 클래스 변수 => 공유 변수
  • 클래스가 로딩될 때 생성, 프로그램 종료시 까지 유지
  • 모든 인스턴스가 공통된 저장공간을 가짐
  • public을 붙이면 프로그램 내 어디서나 접근 가능
  • 그래서 메모리가 유일하게 method영역(나머지는 heap)
  1. 인스턴스 변수
  • 생성: 클래스 인스턴스 생성 시
    (그래서 인스턴스 변수의 값을 불러오려면 생성해야함)
  • 독립적인 저장 공간 가짐 -> 서로 다른 값을 가질 수 있음
  1. 지역 변수
  • 변수 선언문 수행시 생성되고, 메서드가 종료되면 소멸

클래스 변수와 인스턴스 변수

기본적으로 생각하는 두 변수의 차이는 클래스는 인스턴스 변수 앞에 static키워드가 붙는것!

예시를 통해 이 차이를 완벽히 느껴보다.

Card라는 클래스가 있다고 생각해보자.
여기에는 여러 변수가 존재할 수 있다. (카드 크기, 카드 색깔, 카드 모양, ..)

여기서 모든 카드가 공통적으로 같은 값을 갖는 속성은 무엇일까?
카드의 크기(가로 길이, 세로 길이)이다!

따라서 이렇게 표시할 수 있다.

class Card {
	static int width, height;
    String color;
    String shape;
}

public static void main(String[] args) {
	Card c1 = new Card();
    Card c2 = new Card();
    
    Card.width = 100;
    Card.height = 200;
    c1.color = red;
    c2.color = black;
    System.out.println(c1.width); //100
    System.out.println(c2.height); //200
}

메서드

= 어떤 작업을 수행하기 위한 명령문의 집합
= 반복적으로 사용되는 코드를 줄이기 위해서!!!!
-> 반복적으로 수행되는 문장을 하나의 메서드로 만들것!

  • 1메서드 1기능을 지향
  • 선언문, 구현문으로 나뉨
int add(int a, int b) //선언문
{
	return a+b; //구현문
}

메서드의 호출

  • 같은 클래스의 메서드끼리는 서로 참조변수를 사용하지 않아도 호출 가능
  • but static 메서드는 인스턴스 메서드 호출 안돼!!!!!!
  • 어차피 선언부에서 주어진 메서드의 반환형으로 값이 반환되기 때문에 같은 타입 or 자동형변환으로 저장할 수 있는 자료형 사용해야함

return문

= return문은 현재 실행 중인 메서드를 종료하고 호출한 메서드로 돌아가게 한다.
** [메서드의 종료 case]
1. 메서드 블럭의 마지막 문장까지 수행했을 때
2. 메서드 블럭의 문장 수행 중 return문이 있을 때

  • 메서드 선언부의 반환값을 따름 (반환값이 있다면)
    (ex. void로 선언했다면 return을,
    int로 선언했다면 int형 or int로 자동형변환이 가능한 byte, short, char형)
public int add(int a, int b) {
	return a+b; (a+b라는 int형 값을 반환)
}
  • 참조형 매개변수라면 굳이 return을 해주지 않고 값을 변화시킬 수 있음
    (참조형 변수는 주소값을 저장하기 때문에 값에 접근이 가능함)
void change(int a, int b, int[] arr) {
	int res = a+b;
    arr[0] = res;
}

public static void main(String[] args) {
	int[] arr = {0};
    System.out.println(arr[0]); //0
    change(4, 5, arr);
    System.out.println(arr[0]); //9
}

JVM의 메모리구조

가볍게 정리해놓은 JVM
일단은 이걸 읽고 아래의 내용을 이해해보면 좋을 것 같다.

JVM은 크게 3개의 영역으로 나뉜다.
1. method 영역
= 클래스에 대한 정보를 읽어오는 영역
(ex. 클래스 변수 저장)

2. 콜(호출) stack 영역 (스레드마다 하나씩 생성되는)
= 메서드의 작업에 필요한 메모리 공간 제공
(메서드의 실행이 종료되면 그 메모리는 자연스럽게 비워짐)

  • 기존의 stack 성질처럼 메서드가 쌓인다.
    (먼저 들어온 메서드가 가장 아래)
  • 이전의 메서드가 실행되던 와중 다음 메서드가 호출이 되면 다음 메서드의 실행이 끝난 뒤 이전 메서드를 실행한다.
void first() {
	System.out.println("처음 메서드입니다");
    second();
    Systme.out.println("끝입니다");
}
void second() {
	System.out.println("두번째 메서드입니다");
    System.out.println("두번째 메서드 끝입니다");
}
public static void main(String[] args) {
	first();
}

// stack에 쌓이는 순서
main -> first -> 도중에 second -> second실행 끝 -> first 실행 끝
-> main 실행 끝

3. heap 영역
= 인스턴스가 저장되는 영역(new 연산자를 통해서 생성되는 객체)
(참조형 값 저장)

기본형 매개변수와 참조형 매개변수

앞서 설명했었던 기본형과 참조형 변수의 차이에 집중해서 생각하면 된다.

자바에서는 메서드를 호출할 때 매개변수의 값을 복사해서 넣어준다.
기본형은 값이 복사가 되지만, 참조형은 주소가 복사되기 때문에 값을 읽어옴과 동시에 쓸 수도(write) 있다!

재귀호출

= 메서드 내부에서 자기 자신을 호출하는 것

  • 보통은 팩토리얼 계산 시에 자주 쓰인다.
public long fact(int n) {
	if(n==1) return 1;
    else return n*fact(n-1); //재귀호출!!
}

** main메서드도 재귀 호출이 가능하지만 아무 조건도 없이 재귀 호출을 하게 되면 호출스택의 메모리 초과가 발생하여 StackOverflow가 발생한다!!

클래스 메서드와 인스턴스 메서드

변수와 같다. static이 있으면 클래스 메서드, 아니라면 인스턴스 메서드이다.

** 멤버변수: 클래스 영역에 선언된 all 변수

  • 클래스의 멤버변수 중 모든 인스턴스에 공통된 값을 유지해야 하는 것 O
    => static을 붙여줌
  • 인스턴스 변수나 메서드를 사용하지 않는 메서드라면
    => static 붙이는 것을 고려
    (static 키워드를 사용하면 메서드 호출에 걸리는 시간이 줄어듦.
    반면 인스턴스는 메서드를 찾는 과정이 필요해 시간이 걸림)

클래스멤버와 인스턴스멤버 간 참조와 호출

같은 클래스의 멤버들 안에는 별도의 인스턴스를 생성하지 않고도 참조가 가능하다.
but 클래스 멤버가 인스턴스 멤버를 참조 or 호출하고자 하는 경우에는 인스턴스를 생성해줘야 함!!
(클래스 멤버가 호출하는 시점에 인스턴스 멤버의 존재 유무를 알 수 없기 때문)

//참조변수를 활용
Calculator cal = new Calculator();
int res = cal.generate();

//참조변수 활용X. just 대입
//but 이 인스턴스는 참조변수가 없으므로 더 이상 사용할 수 없음
int res = new Calculator().generate();

[메서드 오버로딩]

이것이 무엇인가

한 클래스 내에 같은 이름이지만 다른 매개변수를 가지는 여러개의 메서드를 정의한 것

조건이 무엇인가

  1. 메서드 이름이 같아야 한다
  2. 매개변수의 개수 or 타입이 달라야 한다
  3. 매개변수가 같지만 반환 타입이 다르면 성립 X

예시는 무엇인가

System.out.println()메서드가 가장 대표적인 예시이다.

우리는 출력할 때 반환된 다양한 자료형의 값을 넣어주어 출력했지만 실은 모두 오버로딩 된 다른 println 메서드를 사용한 것이다.

장점은 무엇인가

  • 오류의 가능성을 줄일 수 있다.
  • 메서드의 이름을 절약할 수 있다.

참조

자바의 정석, 2nd Edition.

profile
노션에서 자라는 중 (●'◡'●)

0개의 댓글