🥞 BE
home

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

Date
2023/09/13
Category
Programming Language
Tag
Java
Detail
Java Basic

8.1 클래스(Class)

8.1.1 클래스의 속성

클래스는 표현하고자 하는 대상의 공통 속성을 한 군데에 정의해 놓은 것이라고 할 수 있다. 즉, 클래스는 객체의 속성을 정의해 놓은 것이다.
클래스는 멤버 변수(또는 인스턴스 변수), 메소드(함수)를 속성으로 가질 수 있다.
멤버 변수는 앞에서 배운 변수와 특성은 같으며, 클래스에 속한 변수라고 보면 된다.

8.1.2 인스턴스(instance)

클래스와 인스턴스의 관계는 붕어빵과 붕어빵 틀로 이해하면 쉽다.
붕어빵을 만드는 틀(붕어빵의 공통된 특성)이 곧 클래스가 되며, 붕어빵 틀로부터 만들어진 붕어빵(실제 만들어진 객체)이 곧 인스턴스가 되는 것.
public class Main { static class Person { String name; String country; int age; } public static void main(String[] args) { Person minsoo = new Person(); minsoo.name = "민수"; minsoo.country = "대한민국"; minsoo.age = 10; Person paul = new Person(); paul.name = "Paul"; paul.country = "America"; paul.age = 10; Person[] persons = { minsoo, paul }; for(Person cur : persons) { System.out.println("<자기소개>"); System.out.println("안녕하세요. " + cur.name + " 입니다."); System.out.println("저는 "+ cur.country + "에서 태어났습니다."); System.out.println("현재 "+ cur.age + "살 입니다."); } } }
Java
복사
Person이라는 클래스에는 사람의 이름, 나이, 국적에 대한 정보가 담겨져 있다.
이를 활용하여 name, country, age라는 같은 속성을 가진 minsoo, paul으로 각기 다른 인스턴스를 만들었다.
인스턴스의 멤버변수에 접근할 때는 [생성된 인스턴스.멤버변수] 의 형식을 사용한다.
멤버 변수와 지역 변수

8.2 메소드(method)

메소드는 어떠한 작업을 수행하는 코드를 하나로 묶어 놓은 것이라고 생각하면 된다.

8.2.1 메소드가 필요한 이유

재사용성
같은 동작을 하는 코드를 메소드로 만들어 반복적으로 재사용이 가능하다. 메소드를 이용해서 중복된 코드를 줄이고, 같은 동작에 대해서 메소드 하나만 수정해서 메소드를 사용하는 여러 부분에 적용할 수 있다.
프로그램 구조화
구조화에 대해서는 아래 예시를 보면서 이해를 할 수 있다.
int[] heights = new int[5]; // 키가 들어가 있는 배열 initHeight(heights); // 1. 키에 대한 초기화 sortHeight(heights); // 2. 키를 오름차순으로 정렬 printHeight(heights); // 3. 정렬된 키를 출력
Java
복사
보다시피 코드가 어떠한 작업을 하느냐에 따라 구분이 되어 구조화가 된 것을 확인할 수 있다.

8.2.2 메소드 이름 규칙

메소드를 만들 때는 메소드 안에서 동작하는 내용을 잘 표현할 수 있도록 이름을 잘 지어주면 가독성이 좋아진다. 이 가독성의 기본 품질을 위해 메소드를 만들 때 지켜줘야하는 기본 표준이 두가지 있다.
1.
동사로 시작해야한다.
2.
camel case로 작성해야 한다. (첫 단어는 소문자로, 이후 단어의 구분에 따라 첫 글자만 대문자인 단어가 이어진다. 중간에 띄어쓰기나 특수문자는 들어가지 않는다.)

8.2.3 메소드 선언과 구현

접근제어자 반환타입 메소드이름 (타입 변수명,타입 변수명, ...){ 수행되어야 할 코드 }
Java
복사
반환 타입 : 메소드는 return 문을 통해 수행의 결과를 반환하게 된다. 이때 결과의 자료형을 결정하는 부분이 반환 타입.
int add(int x, int y) { int result = x + y; return result; }
Java
복사
메소드의 반환타입은 int이며 이는 반환되어지는 변수인 result와 일치해야 한다.
반환 타입 중 void 는 아무것도 없음을 의미한다.

예제 코드

class Calculation { int add(int x, int y) { int result = x + y; return result; }// 두 값을 더한 결과 int sub(int x, int y) { int result = x - y; return result; }// 두 값을 뺀 결과 } public class Main { public static void main(String[] args) { Calculation calculation = new Calculation(); int addResult = calculation.add(100, 90); int subResult = calculation.sub(90, 70); System.out.println("두 개를 더한 값은 " + addResult); System.out.println("두 개를 뺀 값은 " + subResult); } }
Java
복사
add 메소드와 sub 메소드 모두 x와 y변수가 중복되어 사용된 것을 확인할 수 있다. 하지만, 메소드내의 변수는 지역변수로 써 메소드 내부에서만 사용할 수 있는 변수이다. 즉, 서로 다른 메소드라면 같은 이름의 지역변수를 선언하여 사용해도 된다.
지역변수는 메소드의 실행이 끝나면(return 된 이후) 메모리에서 삭제된다.

8.2.4 정적 메소드(static method)

정적 메소드는 클래스의 인스턴스 없이도 사용 할 수 있는 함수를 말한다.
접근제어자 static 반환타입 메소드이름 (타입 변수명,타입 변수명, ...){ 수행되어야 할 코드 }
Java
복사
클래스이름.메소드이름(파라미터) 의 형식으로 사용할 수 있다.
정적메소드는 객체의 상태에 독립적이어야 한다. (context-free, stateless) 즉, 파라미터로 받은 값 또는 객체와, 메소드 내에서 사용되는 값 또는 객체, 그리고 다른 정적(static) 리소스만을 사용해서 구현되어야 한다.
해당 클래스의 멤버 변수(인스턴스 변수)와 인스턴스 메소드는 사용할 수 없다.(컴파일 에러)

8.2.5 인스턴스 메소드(instance method)

인스턴스 메소드는 클래스의 객체가 생성되어야 호출될 수 있는 함수를 말한다.
접근제어자 반환타입 메소드이름 (타입 변수명,타입 변수명, ...){ 수행되어야 할 코드 }
Java
복사
객체변수이름.메소드이름(파라미터) 의 형식으로 사용할 수 있다.
인스턴스 메소드는 객체의 리소스에 접근할 수 있다. 클래스에 선언된 해당 인스턴스의 멤버 변수의 값을 가져오거나 값을 변경할 수있고, 인스턴스 메소드를 호출할 수 있다.

8.3 생성자

생성자는 인스턴스가 생성될 때 사용되는 '인스턴스 초기화 메소드' 이다. 즉 new 와 같은 키워드로 해당 클래스의 인스턴스가 새로 생성될 때, 자동으로 호출되는 메소드이다.
이 생성자를 이용해서 인스턴스가 생성될 때 수행할 동작을 코드로 짤 수 있는데, 대표적으로 인스턴스 변수를 초기화 하는 용도로 사용한다.

8.3.1 생성자의 형식

클래스이름 (타입 변수명, 타입 변수명, ...){ 인스턴스 생성 될 때에 수행하여할 코드 변수의 초기화 코드 }
Java
복사
생성사의 조건
생성자의 이름은 클래스와 같아야 한다.
생성자는 리턴 값이 없다.

예제 코드

public class Main { static class Person { String name; String country; int age; public Person(String name, String country, int age) { this.name = name; this.country = country; this.age = age; } } public static void main(String[] args) { Person minsoo = new Person("민수", "대한민국", 10); Person paul = new Person("Paul", "America",10); Person[] persons = { minsoo, paul }; for(Person cur : persons) { System.out.println("<자기소개>"); System.out.println("안녕하세요. " + cur.name + " 입니다."); System.out.println("저는 "+ cur.country + "에서 태어났습니다."); System.out.println("현재 "+ cur.age + "살 입니다."); } } }
Java
복사
생성자에서 사용된 this 는 생성된 객체 자신을 가리키며 생성자의 매개변수의 값을 객체에 해당하는 데이터에 넣어주게 된다.

8.3.2 기본 생성자

지금까지 생성자에 대해 모른채로 프로그래밍을 했다. 하지만 모든 클래스에는 반드시 하나 이상의 생성자가 있어야 한다.
사실 클래스에 생성자가 1개도 작성이 되어있지 않을 경우, 자바 컴파일러가 기본 생성자를 추가해주기 때문에 지금까지 기본 생성자를 작성하지 않고도 편리하게 사용할 수 있었다!
기본 생성자는 매개변수와 내용이 없는 생성자이다.

8.3.3 인스턴스(멤버) 변수의 기본값

앞의 자료형 에서 배웠듯, class에 선언된 변수는 instance가 생성될 때 값이 초기화(initialize)된다. 이 때, 변수의 선언부나 생성자를 통해서 초기화를 해주지 않는다면, 기본값(default value)을 가진다.
[코드스니펫] 인스턴스 변수의 기본값 - Main
class DefaultValueTest { byte byteDefaultValue; int intDefaultValue; short shortDefaultValue; long longDefaultValue; float floatDefaultValue; double doubleDefaultValue; boolean booleanDefaultValue; String referenceDefaultValue; } public class Main { public static void main(String[] args) { DefaultValueTest defaultValueTest = new DefaultValueTest(); System.out.println("byte default: " + defaultValueTest.byteDefaultValue); System.out.println("short default: " + defaultValueTest.shortDefaultValue); System.out.println("int default: " + defaultValueTest.intDefaultValue); System.out.println("long default: " + defaultValueTest.longDefaultValue); System.out.println("float default: " + defaultValueTest.floatDefaultValue); System.out.println("double default: " + defaultValueTest.doubleDefaultValue); System.out.println("boolean default: " + defaultValueTest.booleanDefaultValue); System.out.println("reference default: " + defaultValueTest.referenceDefaultValue); } }
Java
복사
> Task :Main.main() byte default: 0 // 1byte를 구성하는 8개의 bit가 모두 0이라는 뜻. short default: 0 int default: 0 long default: 0 float default: 0.0 double default: 0.0 boolean default: false reference default: null
Plain Text
복사

8.4 상속(inheritance)

8.4.1 상속의 특징

→ 상속을 보여주는 UML Class Diagram이다. 자동차의 하위 계층으로 SUV와 SEDAN이 존재한다.
상속이란 기존의 클래스를 재사용하는 방식 중의 하나이다. 한 번 작성한 코드가 재사용이 필요하다면, 변경사항만 코드로 작성하므로 상대적으로 적은 양의 코드를 작성할 수 있게 된다. 이렇게 코드를 재사용하면, 코드와 클래스가 많아질수록 관리가 용이하다는 장점이 있다.
상속을 통해 클래스간의 계층구조를 만들게 된다.
상속의 특징
1.
부모 클래스로부터 정의된 필드와 메소드를 물려 받는다.
2.
새로운 필드와 메소드를 추가할 수 있다.
3.
부모 클래스에서 물려받은 메소드를 수정(덮어쓰기)을 할 수 있다.

8.4.2 상속의 형식

상속은 extends를 이용하여 사용할 수 있다.
class Animal{} class Dog extends Animal{} class Cat extends Animal{}
Java
복사
Animal은 부모 클래스, 조상 클래스(Parant Class)라고 부른다. Dog, Cat 클래스는 자식 클래스 , 자손 클래스(Child Class)라고 부른다.

8.4.3 super() 메소드

부모 클래스에 있는 필드나 메소드, 생성자를 자식 클래스에서 참조하여 사용하고 싶을 때 사용하는 키워드이다.
public class Main { static class Animal { String name; Animal(String name) { this.name = name; } public void cry() { System.out.println(name + " is crying."); } } static class Dog extends Animal { String breed; Dog(String name, String breed) { super(name); this.breed = breed; } public void bark() { System.out.println(name + "(" + breed + ") is barking."); } } public static void main(String[] args) { Dog dog = new Dog("코코", "허스키"); dog.cry(); dog.bark(); Animal animal = dog; animal.cry(); // animal.bark(); 실제 객체는 dog와 같더라도, Animal 타입으로 선언한 이상, Dog 타입에 있는 함수는 호출할 수 없다. } }
Java
복사

8.4.4 오버로딩(overloading)

한 클래스 내에 동일한 이름의 메소드를 여러개 정의하는 것
오버로딩의 조건
메소드 이름이 동일해야 한다.
매개변수의 개수 혹은 타입이 달라야 한다.
오버로딩 예제
int add(int x, int y, int z) { int result = x + y + z; return result; } long add(int a, int b, long c) { long result = a + b + c; return result; } int add(int a, int b) { int result = a + b; return result; } // 오버로딩의 조건에 부합하다.
Java
복사
오버로딩이 아닌 예제
int add(int x, int y, int z) { int result = x + y + z; return result; } long add(int a, int b, int c) { long result = a + b + c; return result; } // 반환타입은 다르지만 매개변수의 자료형과 개수는 같기에 오버로딩이 아니다.
Java
복사

8.4.5 오버라이딩(overriding)

부모 클래스로부터 상속받은 메소드를 재정의 하는 것
상속받은 메소드를 그대로 사용하기도 하지만, 필요에 의해 변경해야할 경우 오버라이딩을 한다.
오버라이딩의 조건
부모 클래스의 메소드와 이름이 같아야 한다.
부모 클래스의 메소드와 매개변수가 같아야 한다.
부모 클래스의 메소드와 반환타입이 같아야 한다.
오버라이딩 예제
public class Main { static class Animal { String name; Animal(String name) { this.name = name; } public void cry() { System.out.println(name + " is crying."); } } static class Giraffe extends Animal { Giraffe(String name) { super(name); } @Override public void cry() { System.out.println(name + " cannot cry."); } } public static void main(String[] args) { Animal giraffe = new Giraffe("기린이"); giraffe.cry(); } }
Java
복사
Overriding을 하더라도 super 키워드를 이용해 부모 클래스에 선언된 메소드를 그대로 호출할 수 있다.
public class Main { static class Animal { String name; } static class Giraffe extends Animal { Giraffe(String name) { super(name); } @Override public void cry() { super.cry(); System.out.println(name + " cannot cry."); } } public static void main(String[] args) { Animal giraffe = new Giraffe("기린이"); giraffe.cry(); } }
Java
복사

8.4.6 오버로딩 vs 오버라이딩 비교

오버로딩 : 기존에 없는 새로운 메소드를 정의하는 것 오버라이딩 : 부모 클래스에 있는 메소드를 재정의하는 것