ex) nameList 객체의 add() 메서드는 변경자이다.
List<String> nameList = new ArrayList<>();
nameList.add("Paul");
를 수행 후 nameList 객체의 내부 배열에는 "Paul"이 추가된다.
ex) LocalDate 객체의 plusDays() 메서드는 접근자 메서드이다.
LocalDate today = LocalDate.of(2021, 1, 1);
LocalDate tomorrow = date.plusDays(1);
를 수행 후 date 객체는 2021년 1월 2일에 대한 정보를 가지고 있다.
이 경우 date.plusDays(1) 만 호출할 경우 메서드 수행을 해도 date 객체에 변화가 없다.
자바에서는 변수에 객체 자체를 담을 수 없다. 오직 객체에 대한 참조(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 객체는 자바의 가비지 컬렉터가 메모리를 정리해 재사용할 수 있게 한다.
인스턴스 메서드들은 메서드 호출을 받는 객체에 대한 참조가 항상 인자로 전달된다. 엄밀하게는 이 참조또한 명시적으로 전달하는 다른 인자들과 같다. 그러나 자바에서는 다른 객체 지향 언어와 마찬가지로 이 인자가 특별한 역할을 하므로 이를 메서드 호출의 수신자라고도 한다.
객체에 있는 메서드를 호출할 때 해당 객체를 this로 접근할 수 있다.
(Employee 클래스)
public void setSalary(double salary) {
this.salary = salary;
}
이는 아래와도 같다.
public void setSalary(Employee this, double salary) {
this.salary = salary;
}
위처럼 this를 메서드의 파라미터로도 선언할 수 있다(생성자의 파라미터로는 생성할 수 없다) - 이는 거의 사용하지 않음
Java는 call by value
생성자는 new 연산자로 실행할 수 있다.
new Employee("James Bond", 500000);
위 표현식은 Employee 클래스의 객체를 할당하고, 생성자 구현부를 호출한다. 그리고 생성된 객체의 참조를 반환한다.
이름은 같지만 파라미터(혹은 시그니처)가 다른 여러 메서드들 <- 오버로딩된 메서드들. 생성자도 오버로딩 가능
public Employee(double salary) {
this("", salary); // Employee(String, double)을 호출
// 이후 다른 작업 가능
초기화 작업을 한 생성자에 몰아넣고, 다른 생성자들에서 해당 생성자를 호출하여 코드 중복을 제거할 수 있다.
(! 위 생성자 호출에 사용된 this는 생성될 객체에 대한 참조가 아니다. 이때의 this는 같은 클래스에 속한 다른 생성자를 호출할 때 사용하는 특수문법)
숫자 : 0, boolean : false, 객체 참조 : null
null 주의
final로 선언한 변수는 반드시 모든 생성자가 작업을 마치기 전에 초기화해야 한다. 초기화한 수 변수를 수정할 수 없다.
클래스 안에 있는 변수를 static 으로 선언하면 해당 변수는 클래스당 하나만 존재하게 된다.
(그렇지 않은 인스턴스 변수들은 각 객체마다 해당 변수의 사본을 가진다)
static final
상수는 주로 대문자로 선언.
숫자가 아니라 객체를 정적 상수로 사용할 수 있다. (ex random generator)
System.out <- System 클래스에 static final로 선언되어 있다.
객체로 동작하지 않는다. 클래스를 통해 바로 사용할 수 있는 메서드이다.
(ex Math.pow(x, a) )
객체를 통해 정적 메서드를 호출할 수는 있지만 의미 없는 방법이다.
정적 메서드는 인스턴스 변수에 접근할 수 없고, 자신이 속한 정적 변수에는 접근할 수 있다.
클래스의 새로운 인스턴스를 반환하는 정적 메서드
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
파라미터 타입이 같은데 다른 종류의 객체를 생성하고 싶을 때,
서브클래스의 객체를 생성하고 싶을 때
생성사 대신 팩토리 메서드를 사용하여 객체를 생성할 수 있다.
불필요하게 새 객체를 생성하는 대신 공유 객체를 반환하는 것도 가능. (ex. Collections.emptyList() - [JAVA] COLLECTIONS.EMPTY_LIST)
작업을 조직화, 다른 사람이 제공한 코드 라이브러리와 분리가 편리
클래스 이름의 유일성 보장.
유일하다고 알려진 인터넷 도메인 이름을 뒤집어서 사용.
ex) naver.com
com.naver.packagename
소스 파일의 첫번째 문장에 package 문 추가.
package com.naver.packagename;
public class Employee {
...
}
위와 같이 선언하면 위의 Employee 클래스의 전체 이름은 com.naver.packagename.Employee 가 된다.
파일 시스템에서 클래스 파일을 읽어올 때 경로 이름이 패키지 이름과 일치해야 한다.
클래스 파일을 파일시스템이 저장 안하고 하나 이상의 JAR 파일이라는 아카이브 파일들에 넣을 수 있다.
보통 패키지 라이브러리를 이렇게 아카이브 파일로 만든다.
접근 제어자를 명시하지 않으면 default 접근 제어자로 설정된다.
이 경우 같은 패키지에 속한 모든 메서드에서 접근할 수 있다.
import java.util.Random;
위와 같은 import 문을 사용하면 java.util.Random 클래스를 Random으로 사용할 수 있다.
import 문은 편의를 위한 것.
import static java.lang.Math.*;
위 import 문을 통해 Math.sqrt나 Math.pow 등의 메서드들을 sqrt나 pow 처럼 클래스 이름 접두어를 생략하고 사용할 수 있다.
static이 아닌 내부 중첩 클래스는 외부 클래스의 인스턴스 변수에 접근할 수 있다.
내부 클래스의 각 객체는 외부 클래스의 객체에 대한 참조를 포함하는 것이다.
outer 가 외부 클래스의 숨은 참조를 나타낸다.
// 내부 클래스의 메서드 내에서,
// members 가 외부 클래스의 인스턴스 변수일 때
members.remove(this);
outer.members.remove(this); // 위와 같음
정적 중첩 클래스에는 이런 참조가 없다.
중첩 클래스의 인스턴스가 외부 클래스(enclosing class)의 어느 인스턴스에 속하는지 알 필요가 없을 때 정적 중첩 클래스를 사용하면 좋다.
내부 클래스는 외부 클래스에 대한 참조를 가지고 외부 클래스의 메서드를 호출할 수 있다.
// 내부 클래스의 메서드 내에서,
// unenroll 이 외부 클래스의 메서드일 때
unenroll(this);
outer.unenroll(this); // 위와 같음