2장 객체 지향 프로그래밍

Jasik·2021년 12월 7일
0

2.1 객체 이용하기

2.1.1 접근자 메서드와 변경자 메서드

  • 변경자(mutator)
    호출 대상 객체를 변경

ex) nameList 객체의 add() 메서드는 변경자이다.

List<String> nameList = new ArrayList<>();
nameList.add("Paul");

를 수행 후 nameList 객체의 내부 배열에는 "Paul"이 추가된다.

  • 접근자(accessor)
    객체를 변경하지 않음. 접근자 메서드만 제공하여 객체를 변경할 수 없게 해 안전한 동시 접근 가능.

ex) LocalDate 객체의 plusDays() 메서드는 접근자 메서드이다.

LocalDate today = LocalDate.of(2021, 1, 1);
LocalDate tomorrow = date.plusDays(1);

를 수행 후 date 객체는 2021년 1월 2일에 대한 정보를 가지고 있다.
이 경우 date.plusDays(1) 만 호출할 경우 메서드 수행을 해도 date 객체에 변화가 없다.

2.1.2 객체 참조

자바에서는 변수에 객체 자체를 담을 수 없다. 오직 객체에 대한 참조(reference)만 담을 수 있다.

ArrayList<String> people = friends;

위 경우 people과 friends는 같은 객체에 대한 참조를 가지게 된다.

LocalDate date = LocalDate.of(2021, 1, 1); // 1
date = date.plusDays(1); // 2

위 코드 1번 라인의 date 객체와 2번 라인의 date.plusDays(1)가 반환한 LocalDate 객체는 서로 다른 객체이다.
위 코드를 수행한 후 1번 라인의 date 객체는 자바의 가비지 컬렉터가 메모리를 정리해 재사용할 수 있게 한다.

2.2 클래스 구현하기

2.2.4 인스턴스 메서드 호출

인스턴스 메서드들은 메서드 호출을 받는 객체에 대한 참조가 항상 인자로 전달된다. 엄밀하게는 이 참조또한 명시적으로 전달하는 다른 인자들과 같다. 그러나 자바에서는 다른 객체 지향 언어와 마찬가지로 이 인자가 특별한 역할을 하므로 이를 메서드 호출의 수신자라고도 한다.

2.2.5 this 참조

객체에 있는 메서드를 호출할 때 해당 객체를 this로 접근할 수 있다.

(Employee 클래스)

public void setSalary(double salary) {
	this.salary = salary;
}

이는 아래와도 같다.

public void setSalary(Employee this, double salary) {
	this.salary = salary;
}

위처럼 this를 메서드의 파라미터로도 선언할 수 있다(생성자의 파라미터로는 생성할 수 없다) - 이는 거의 사용하지 않음

2.2.6 값을 이용한 호출 (call by value)

Java는 call by value

2.3 객체 생성

2.3.1 생성자 구현하기

생성자는 new 연산자로 실행할 수 있다.

new Employee("James Bond", 500000);

위 표현식은 Employee 클래스의 객체를 할당하고, 생성자 구현부를 호출한다. 그리고 생성된 객체의 참조를 반환한다.

2.3.2 오버로딩

이름은 같지만 파라미터(혹은 시그니처)가 다른 여러 메서드들 <- 오버로딩된 메서드들. 생성자도 오버로딩 가능

2.3.3 다른 생성자에서 특정 생성자 호출하기

public Employee(double salary) {
	this("", salary); // Employee(String, double)을 호출
	// 이후 다른 작업 가능

초기화 작업을 한 생성자에 몰아넣고, 다른 생성자들에서 해당 생성자를 호출하여 코드 중복을 제거할 수 있다.

(! 위 생성자 호출에 사용된 this는 생성될 객체에 대한 참조가 아니다. 이때의 this는 같은 클래스에 속한 다른 생성자를 호출할 때 사용하는 특수문법)

2.3.4 기본 초기화

숫자 : 0, boolean : false, 객체 참조 : null

null 주의

2.3.6 최종 인스턴스 변수 (final)

final로 선언한 변수는 반드시 모든 생성자가 작업을 마치기 전에 초기화해야 한다. 초기화한 수 변수를 수정할 수 없다.

2.4 정적 변수와 정적 메서드

2.4.1 정적 변수

클래스 안에 있는 변수를 static 으로 선언하면 해당 변수는 클래스당 하나만 존재하게 된다.
(그렇지 않은 인스턴스 변수들은 각 객체마다 해당 변수의 사본을 가진다)

2.4.2 정적 상수

static final
상수는 주로 대문자로 선언.

숫자가 아니라 객체를 정적 상수로 사용할 수 있다. (ex random generator)
System.out <- System 클래스에 static final로 선언되어 있다.

2.4.4 정적 메서드

객체로 동작하지 않는다. 클래스를 통해 바로 사용할 수 있는 메서드이다.
(ex Math.pow(x, a) )

객체를 통해 정적 메서드를 호출할 수는 있지만 의미 없는 방법이다.

정적 메서드는 인스턴스 변수에 접근할 수 없고, 자신이 속한 정적 변수에는 접근할 수 있다.

2.4.5 팩토리 메서드

클래스의 새로운 인스턴스를 반환하는 정적 메서드

NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.getPercentInstance();

파라미터 타입이 같은데 다른 종류의 객체를 생성하고 싶을 때,
서브클래스의 객체를 생성하고 싶을 때
생성사 대신 팩토리 메서드를 사용하여 객체를 생성할 수 있다.

불필요하게 새 객체를 생성하는 대신 공유 객체를 반환하는 것도 가능. (ex. Collections.emptyList() - [JAVA] COLLECTIONS.EMPTY_LIST)

2.5 패키지

작업을 조직화, 다른 사람이 제공한 코드 라이브러리와 분리가 편리
클래스 이름의 유일성 보장.

2.5.1 패키지 선언

유일하다고 알려진 인터넷 도메인 이름을 뒤집어서 사용.

ex) naver.com
com.naver.packagename

소스 파일의 첫번째 문장에 package 문 추가.

package com.naver.packagename;

public class Employee {
	...
}

위와 같이 선언하면 위의 Employee 클래스의 전체 이름은 com.naver.packagename.Employee 가 된다.

파일 시스템에서 클래스 파일을 읽어올 때 경로 이름이 패키지 이름과 일치해야 한다.

2.5.2 클래스 패스

클래스 파일을 파일시스템이 저장 안하고 하나 이상의 JAR 파일이라는 아카이브 파일들에 넣을 수 있다.

보통 패키지 라이브러리를 이렇게 아카이브 파일로 만든다.

2.5.3 패키지 유효 범위

접근 제어자를 명시하지 않으면 default 접근 제어자로 설정된다.
이 경우 같은 패키지에 속한 모든 메서드에서 접근할 수 있다.

2.5.4 클래스 임포트하기

import java.util.Random;

위와 같은 import 문을 사용하면 java.util.Random 클래스를 Random으로 사용할 수 있다.

import 문은 편의를 위한 것.

2.5.5 정적 임포트

import static java.lang.Math.*;

위 import 문을 통해 Math.sqrt나 Math.pow 등의 메서드들을 sqrt나 pow 처럼 클래스 이름 접두어를 생략하고 사용할 수 있다.

2.6 중첩 클래스

2.6.1, 2.6.2 정적 중첩 클래스, 내부 클래스

static이 아닌 내부 중첩 클래스는 외부 클래스의 인스턴스 변수에 접근할 수 있다.
내부 클래스의 각 객체는 외부 클래스의 객체에 대한 참조를 포함하는 것이다.
outer 가 외부 클래스의 숨은 참조를 나타낸다.

// 내부 클래스의 메서드 내에서,
// members 가 외부 클래스의 인스턴스 변수일 때

members.remove(this);

outer.members.remove(this); // 위와 같음

정적 중첩 클래스에는 이런 참조가 없다.

중첩 클래스의 인스턴스가 외부 클래스(enclosing class)의 어느 인스턴스에 속하는지 알 필요가 없을 때 정적 중첩 클래스를 사용하면 좋다.

내부 클래스는 외부 클래스에 대한 참조를 가지고 외부 클래스의 메서드를 호출할 수 있다.

// 내부 클래스의 메서드 내에서,
// unenroll 이 외부 클래스의 메서드일 때

unenroll(this);

outer.unenroll(this); // 위와 같음
profile
가자~

0개의 댓글