과제 (Optional)
자바의 프로그램은 모두 클래스로 이루어져 있다. 클래스는 자바를 구성하는 가장 기본적인 구성 요소이다. 따라서 자바 클래스를 벗어나서는 절대로 코드를 작성할 수 없다
클래스는 Java에서 제공하는 5개의 reference type 중 하나이다. 하지만 가장 중요하다고 할 수 있다. Class
에서 가장 중요한 내용은 새로운 데이터 타입을 정의할 수 있다는 내용이다.
reference type의 종류
1. Annotation
2. Array
3. Class
4. Enumeration
5. Interface
클래스를 정의하자면 값을 저장하고 있는 데이터 타입의 집합체이며 그 데이터 타입들을 연산하는 메소드들의 집합체이다.
이 클래스의 인스턴스(instance)를 객체(object)라고 부른다.
그래서
C
언어를 공부한 후Java
를 처음으로 배웠을 때 구조체랑 비슷한 녀석이구나 하는 생각이 들었다. _Class
정의를 예시로 들 때 자주Point
클래스를 정의하는데 이건 관습인가?
어쨌든 x,y 의 좌표를 표현할 수 있는Point
클래스를 정의해보자
클래스 정의는 signature
와 body
로 구성된다.
signature
: 클래스명과 같은 중요한 정보를 특정한다.body
: field, method, constructor, initializers, nested types 을 포함한다.한국 서적에서는 클래스안에 존재하는 변수를 멤버변수라고 부르던데 (static은 클래스 변수라고 부르더라) 영어 서적에서는 field라고 부른다.
Point
클래스에서는 두개의 field를 선언할 것이다.
public class Point {
public double x, y;
}
클래스는 field 외에도 method
나 또 다른class
를 포함할 수 있다.
Point
클래스를 사용하여 2차원 좌표계의 점 사이의 거리를 계산하기 위해서 생성자와 메소드를 아래와 같이 추가 했다.
public class Point {
public double x, y;
// 메소드 정의
public double distanceFromOrigin() {
return Math.sqrt(x*x + y*y);
}
}
메소드를 선언할 때 오버라이딩, 오버로딩을 사용할 수 있다.
오버로딩은 같은 이름의 함수를 여러 개 선언할 때 사용한다.
이름은 같지만 매개변수나 리턴타입이 달라야한다.
...
// 기본형
public double distanceFromOrigin() {
return Math.sqrt(x*x + y*y);
}
// 이름은 같지만 매개변수가 다른 것
public double distanceFromOrigin(Point p) {
return Math.sqrt(p.x*x + p.y*y);
}
// 이름은 같지만 매개변수와 접근제어자가 다른 것
// 단, 매개변수는 같으면서 접근제어자만 다를 수는 없다
private double distanceFromOrigin(double x, double y) {
return Math.sqrt(this.x*x + this.y*y);
}
// 이건 안된다는 뜻
public double distanceFromOrigin(double x, double y) {
return Math.sqrt(this.x*x + this.y*y);
}
// static일 수도 있음
static public double distanceFromOrigin(Point p1, Point p2) {
return Math.sqrt(p1.x*p2.x + p1.y*p2.y);
}
...
오버라이딩은 상속 받은 메소드나 구현해야하는 메소드를 재정의할 때 사용한다. 상속 받은 클래스는 부모의 private
메소드를 제외하고 모든 메소드를 상속받기 때문에 당연히 private
메소드는 오버라이딩 할 수 없다.
모든 클래스는 Object
클래스를 상속받기 때문에 toString()
을 오버라이딩 해보자
...
@Override
public String toString() {
return "X: " + x + " Y: " + y;
}
...
@Override
어노테이션은 안써도 동작한다. 그러나 반드시 사용해서 오버라이딩 메소드라고 명시하자
@Overrid
는 컴파일 단에서 어노테이션이 붙은 함수가 오버라이딩된 함수인지 아닌지를 검사해준다.
new
연산자에 의해서 호출된다. 객체 초기화에 필요한 로직을 작성하자.
...
// 다른 생성자를 생성하면 기본 생성자가 생략된다.
Point() {
}
// 생성자 여러개 생성 가능
Point(double x, double y) {
this.x = x;
this.y = y;
}
// 생성자 여러개 생성 가능 매개변수는 달라야한다.
Point(Point p) {
this = p;
}
...
클래스를 선언한 후에 객체를 생성해야한다. 객체를 생성할 때 항상 살펴봐야 하는 것은 생성자이다. 위에서 선언한 생성자에 맞게 객체를 생성하자
public class Point {
// 기본 생성자 선언 안하면 없음
Point(double x, double y) {
this.x = x; this.y = y;
}
}
...
...
Point p = new Point(2.0, -3.5);
// Point p; // 기본 생성자가 없어서 이건 불가능
만약 생성자가 하나도 없다면 public Point() {}
처럼 기본 생성자를 바이트 코드에 자동으로 추가시킨다. 반면 기본 생성자 이외의 생성자를 선언했다면 기본 생성자를 생성해주지 않는다. 만약 기본 생성자가 필요하다면 따로 선언해줘야한다
객체를 생성할 때 new
연산자가 필요하다. new
연산자는 인스턴스를 생성해주는 역할을 담당하는데 내부적으로 어떤 동작을 하는지 살펴보자
public class Point {
double x;
Point (double x) { this.x=x; }
}
위와 같은 코드가 있을 때 this
를 사용해야한다.
Point
클래스에는 x
라는 변수가 선언되어 있다.Point
의 생성자는 외부에서 x
라는 변수를 매개변수로 받는다.x
와 내부의 x
가 겹치는 문제가 발생한다.그래서 this.x
라는 코드를 작성하면 유저가 생성한 Point
의 인스턴스가 가지고 있는 x
를 가리키게 된다.
만약 겹치지 않으면 this
연산자를 사용하지 않아도 상관없다.
this()
는 같은 클래스의 다른 생성자를 호출할 때 사용한다.
...
Point(Point p) { this(p.x, p.y); } // this()로 다른 생성자 호출
Point(double x, double y) { this.x=x; this.y=y; }
...