객체는 필드(데이터)와 데이터를 기능하게 하는 메서드(함수)를 가진다.
Stduent
객체를 설계해보자.
학생마다 가지고 있는 데이터는 다를 수 있다.
클래스 : 객체가 가질 수 있는 필드와 메서드는 어떤 게 있다라고 정의해놓은 설계도
객체 : 설계도(클래스)를 통해 실제 행동하는 대상
객체 == 인스턴스??
객체 = new 사용자(); → 객체가 (new 연산자를 통해) 인스턴스화 되었다. → 객체가 메모리를 사용하고 있다.
객체는 필드로 가지고 있는 자료형의 메모리를 합친만큼의 메모리를 할당하고 있다.
객체 생성과 동시에 원하는 값으로 초기화할 수 있는 메소드
생성자 이름은 클래스 이름과 동일 Car car = new Car();
Object.hashcode()
객체는 각각의 고유한 id(해시코드)를 가진다. → 해시코드가 다르면 다른 객체이다.
해시코드 또한 유효한 범위가 있기 때문에, 서로 다른 두 객체가 같은 해시코드를 가지게 되는 해시코드의 충돌이 발생할 수 있다.
Object.equals()
그렇기 때문에, 해시코드가 같더라도 즉시 같은 객체라고 판단하지 않고, Object.equals() 메서드를 통해 다시 한 번 비교한다.
equals() 메서드에서는 객체의 내용물을 모두 비교하며 같은 객체인지 판단한다. → 내용물(필드)의 값이 모두 같으면 같은 객체이다.
hashcode를 먼저 비교함으로써 equals() 메서드가 내용물까지 비교하는 과정을 생략할 수 있다.
String의 경우 두 객체를 비교할 때 값이 같으면 String Pool에서 같은 주소를 가지기 때문에 ‘==’을 통해 비교해도 올바른 결과값이 나오긴 한다.
객체에 담긴 필드는 여러개가 있는데 이 중에서 어떤 것을 기준으로 정렬할지 정해야 된다.
→ Comparable 인터페이스의 compareTo 메서드를 구현하여 정렬 기준을 정한다.
o1.compareTo(o2)
양수 반환시 비교대상과 순서 바꾼다.
주소값만 복사하는 Shallow Copy를 통해 원본의 데이터가 수정되는 것을 주의!!
참고 : https://velog.io/@db_jam/Java-얕은-복사Shallow-Copy와-깊은-복사Deep-Copy
객체에 접근할 때, 필드를 숨기고 메소드를 통해서만 접근 가능하도록 한다.
→ 객체 모듈화 가능
→ 코드 이식성 좋아진다.
클래스 멤버를 정적 필드로 선언하면 클래스 로딩시 같이 로딩되어 메모리 공통영역에 존재하게 된다. → 객체 생성 필요 없다.
선언 방법 - static
으로 선언, 접근제어자 static 타입필드 = 초기값;
static 변수를 선언해놓으면, 이는 메모리 공통영역에 한 번만 load 된다
→ 인스턴스를 생성할 때, 해당 변수에 대한 메모리를 할당할 필요가 없다.
→ 어떤 인스턴스든 해당 메모리에 접근할 수 있다.
—>메모리 관리에 용이하다!
static 필드를 반환하는 메소드 또한 static으로 선언해줘야 한다.
class Person {
private static String name;
// 생성자 생략..
public static String getName(Person person){
return person.name;
}
}
학생의 totalNum을 필드로 두고 싶을 때, 학생 하나하나가 이 정보를 필드로 가지고 있을 필요가 없다 → 정적 필드로 두고 모든 인스턴스가 접근할 수 있도록 한다.
클래스 로더에 의해 메인함수가 먼저 메모리에 올라가야 하기 때문에 메인함수는 public static void로 선언되어있는 것을 알 수 있다.
클래스로더 (클래스 / 정적변수 / 정적메소드 / 메인함수) → 함수가 스택에 쌓임 → 스택 해제 → 클래스로더 해제
클래스 로더 : 어떤 메모리를 먼저 할당해야 될지 정하는 역할을 한다.
객체를 하나만 두겠다. == 객체를 여러개 생성하지 않겠다.
목적
구현방법 1 - 동기화 문제 해결
멀티스레드 환경에서는 다른 스레드에서 객체를 생성하여 객체가 여러개 생성되지 않도록 주의해야 된다.
이러한 방법도 JVM에서는 멀티스레드 환경에서 여러 스레들들 왔다갔다하면서 실행시키기 때문에 동시에 객체를 생성하는 상황이 발생할 수 있다.
어떻게 해결할까?
synchronized
키워드 → 한 스레드가 해당 작업 진행 중일 때는 다른 스레드가 같은 작업을 진행하지 못하도록 한다.
구현방법 2 - 처음부터 인스턴스 생성
인스턴스를 필요할 때 생성하지 않고, 처음 클래스 로딩시에 생성한다.
구현방법 3 - DCL(Double-Checking Locking)
volatile
키워드 → 매번 Read할 때마다 cache에 저장된 값이 아닌, 메인 메모리에서 읽고 변수 값을 Write 할 때마다 메인 메모리에 쓴다.
메타데이터(metadata) → 컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할 것인지 알려주는 정보
부모가 가진 필드를 자식에게 물려주는 것
자바 계층 구조 최상위에 java.lang.Object
클래스가 존재한다 → 자바에서 클래스를 생성하면 자동으로 Object
클래스를 상속받는다
→ 클래스를 생성하면 Object 클래스의 toString() / equals() 메소드를 사용할 수 있는 이유
장점
특징
부모로부터 상속한 메소드를 자식 클래스에 맞는 메소드로 재정의하는 것 ≠ 오버로드
규칙
동일한 리턴타입 / 메소드 이름 / 매개변수
를 가져야 함@Override 어노테이션 사용하면 부모에 있는 메소드인지 checking 할 수 있음
→ 부모에 있는 메소드가 아니면 컴파일 오류 발생
상속이 안 되는 경우
Circle 클래스가 Shape 클래스를 상속받았을 때,
Shape shape = new Circle();
은 가능하지만, Circle circle = new Shape();
는 컴파일 오류가 발생한다.
전자의 경우, 실제 메모리는 Circle을 기준으로 할당받기 때문에 shape의 필드를 모두 상속받은 Shape의 필드에 접근할 수 있지만, 후자의 경우에는 실제 메모리를 Shape를 기준으로 할당받기 때문에 circle의 필드에 접근할 수 없을 수도 있다.
Shape shape = new Circle();
Shape 클래스에 draw() 메소드가 정의되어 있고, Circle 클래스가 draw() 메소드를 오버라이드 했을 떄, shpae.draw()를 호출하면 Circle이 오버라이드한 draw()가 호출된다.
→ 자바에서 가상 함수
매핑 테이블을 참조하며 동적 바인딩을 수행하게 되는데, 가상 함수 매핑 테이블에 자식이 오버라이드한 메서드가 있다면, 해당 메서드를 호출해준다.
자바에서는 모든 함수가 가상 함수로 되어있는데, 런타임 중에 가상 함수 매핑 테이블을 보고 어떤 함수를 호출할지 결정한다.
메서드를 생성하면 메모리의 Code 영역에 주소가 저장되고, 메서드의 주소가 가상 함수 매핑 테이블에서 관리되며 런타임 중에 각상 함수 매핑 테이블을 참조해서 어떤 함수 호출할지 결정한다.
객체를 만들기에는 너무 추상적인 클래스 == 구체화할 수 없는 클래스
ex) Shape 클래스 : 단순히 Shape라는 정보만 가지고는 도형을 그릴 수 없다.
↔ 실체 클래스 : 실제 인스턴스가 생성될 수 있는, 구체화할 수 있는 클래스
객체로 생성하지도 못 하는 추상 클래스를 왜 만들까??
Shape[] shapes = {new Circle(), new Triangle(), new Rectangle()};
특징
컴파일 오류
new
를 통해서 객체를 생성할 수 없다.Shape trapezoid = new Shape() {
Line[] lines;
public Line[] getLines() {
return lines;
}
public void setLines(Line[] lines) {
this.lines = lines;
}
@Override
public void draw() {
System.out.println("trapezoid draw ...");
}
@Override
public String toString() {
return "$classname{" +
"lines=" + Arrays.toString(lines) +
", centerPoint=" + centerPoint +
'}';
}
};
단점
클래스를 상속 계층도는 아니지만 해당 객체가 특정 작업이 가능한가?에 따라 나눌 수 있다.
인터페이스 정의
public
이다!static final
int a = 0;
→ public static final int a = 0;
자동으로 변환abstract
void renew();
→ public abstract void renew();
자동으로 변환상속과 같은 상속 계층도는 아니지만, 구현 클래스를 인터페이스에 담아 객체를 생성할 수 있다.
DriveLicenseAble driveLicenseAble = new UnivStudent();
인터페이스에 추상 메서드가 없다면 직접 new
해서 객체를 생성할 수 있을까?
특징
extends
)