그림으로 배우는 자바 - 클래스
클래스의 기본 (chapter8)
클래스는 사물의 상태 및 특성, 그에 관련된 기능을 정리하여 프로그램으로 표현하기
위해 사용하는 개념
car1 = new Car();
// ‘new를 사용한 결과를 변수 car1을 사용하여 다룰 수 있는 상태가 된다.
클래스 명 변수명;
// Car car1; 객체를 담을 변수 선언
변수 명 = new 클래스 명();
// new를 사용하여 객체를 생성하고 변수에 대입
클래스명 변수명 = new 클래스명();
// Car car1 = new Car();
※ 참조형 변수 , 변수 car1은 ‘객체 그 자체’가 아니다. 그저 참조를 하는 변수일 뿐.
참조형 변수의 종류에는 클래스 변수 외에도 배열형 변수와 인터페이스형 변수가 있다.
2개의 클래스 파일 생성되는 이유
컴파일러는 각 클래스별로 클래스 파일을 하나씩 만들어준다.
클래스를 사용하는 순서 정리
- 클래스를 선언하기
- 클래스로 객체를 생성하기
메소드의 호출
① 메소드를 호출하면 ② 메소드 안의 코드가 실행된다. ③ 메소드의 내부의 코드가 모두 실행되면 메소드가 호출되었던 코드의 다음 코드가 실행된다.
this
필드가 ‘객체 자신’의 소유임을 강조하기 위해 this라 불리는 키워드를 사용할 수 있다.
class Car
{
int num;
double gas;
void show()
{
System.out.println("차량 번호는" + this.num + "입니다.");
}
}
멤버(필드와 클래스)를 클래스 안에서 접근할 때 this.를 앞에 붙여줄 수 있다.
메소드의 변수들
메소드에 선언되어 있는 인수(변수)를 가인수(parameter)라고 부른다. 반면, 메소드 호출 시 전달되는 인수(값)를 실인수(argument)라고 부른다.
클래스의 기능
멤버에 대한 접근 제한
클래스를 외부에서 마음대로 접근할 수 없는 멤버를 만드는 기능을 지원
이러한 멤버를 private멤버라고 부름.
public 멤버는 클래스 외부에서 접근할 수 있다.
캡슐화
클래스의 데이터와 기능을 한 곳에 모은 다음, 보호하고 싶은 멤버에 private를 붙여 접근을 제한하는 기능을 일컬어 캡슐화라고 한다.
※ 제한자(private,public)를 붙이지 않으면, '같은 패키지', 즉 같은 폴더 안의 클래스라면 마음껏 접근이 가능하다.
메소드의 오버로딩
같은 이름을 가지는 메소드 여러 개를 같은 클래스에 정의할 수 있다. ※단 메소드의 인수의 형과 개수를 다르게 만들어야 함
다형성
하나의 이름이 상황에 맞추어 다른 기능을 가지는 것을 일컬어 다형성이라고 부른다.
생성자
그 클래스의 객체가 생성될 때, 생성자 안의 코드가 자동으로 실행
=> 객체를 초기화하기 위해 생성자를 정의할 수 있다.
생성자 오버로딩 => 여러 개의 생성자 정의
#### 다른 생성자 호출하기
public Car()
{
num = 0;
gas = 0.0l
System.out.println('자동차가 만들어졌습니다.');
}
public Car(int n, double g)
{
this();
num = n;
gas = g;
System.out.println("차량 번호를 "+ num + "으로, 연료 양을 " + gas로 " 바꾸었습니다.");
}
이와 같이 2개의 인수를 가지는 생성자의 시작 부분에서 ‘this();’라는 명령을 내리면
특성 생성자 안에서 특별히 다른 생성자를 호출
만약 인수가 2개인 생성자를 다른 생성자에서 호출하고 싶다면 인수를 this()에 전달하면 호출이 된다. ※ 단 ,this()는 반드시 생성자 안에서 가장 먼저 호출이 되어야 한다.
Q) 생성자를 생략하면?
인수 없는 생성자를 기본 생성자(default constructor)라고 부른다.
즉 Car car1 = new Car();
생성자를 정의하지 않으면, 인수없는 생성자가 호출된다.
생성자에 접근 제한자 붙이기
private을 설정하면 클래스 외부에서 이 생성자를 호출하여 객체를 만들 수 없다. public을 설정하면 외부에서 이 생성자를 호출하여 객체를 만들 수 있다.
예를 들어 private Car() 라는 생성자를 선언했다면
Car car1 = new Car();
이렇게 생성을 못해.
클래스 변수, 클래스 메소드
인스턴스 변수란 ? 각 객체와 연결된 필드를 가리켜 인스턴스 변수, 각 객체에 연결된 필드를 가리켜 인스턴스 메소드라고 부른다.
클래스는 객체에 연결되지 않은 멤버를 가질 수 있다.
이를, 클래스 전체에 연결되었다라고 부를 때가 있다.
즉 클래스 변수와 클래스 메소드란 클래스 전체와 연결된 필드, 클래스 전체와 연결된 메소드를 말한다.
static을 붙인 것들을 말한다.
클래스의 객체를 다루는 데이터를 저장하는 필드가 클래스 변수이다.
즉 클래스 변수는 각 객체가 공유하는 필드다.
ex) Car 클래스를 통해 몇 대의 자동차가 만들어졌는지(객체의 개수가 몇 개인지)
또한 클래스 메소드는
객체를 생성하지 않더라도 호출할 수 있는
기능을 가지고 있다.
인스턴스 메소드처럼 객체에 연결된 메소드가 아니다. ※ ‘this’는 인스턴스 메소드 안에서만 붙일 수 있다. 특정 객체 스스로를 가리키는 클래스 메소드에는 ‘this’는 사용 불가 또한 클래스 메소드 안에서는 인스턴스 변수와 인스턴스 메소드에 접근할 수 없다. 래스 메소드 또한 특정한 객체와 연결된 것이기 때문이다.
chapter 10 클래스 이용
String 클래스 (문자열의 개념을 클래스로 구현한 것)
- String 클래스의 charAt() 메소드는 문자열에서 지정된 위치의 문자를 반환한다.
- String 클래스의 length() 메소드는 문자열의 길이를 반환한다.
※ String 클래스의 객체는 매우 자주 사용하기 때문에 new를 사용하지 않고도 만들 수 있다.
String str = "Hello";
- toUpperCase() 메소드는 문자열을 대문자로 변환
- toLowerCase() 메소드는 문자열을 소문자로 변환
- indexOf 메소드는 문자열 안에서 지정한 문자를 검색가능 지정된 문자를 찾지 못한 경우 -1 반환. 찾으면 찾은 위치 반환
StringBuffer 클래스
String 클래스는 일단 생성된 개체의 내용(문자열)을 변경할 수 없다. 문자열의 문자를 변경하는 경우 StringBuffer 클래스를 사용해야 한다.
기타 클래스
Interger 클래스
Interger 클래스는 ‘int형’을 위해 다양한 기능을 제공하는 클래스이다. 기본형을 다루는 클래스는 wrapper class 라고도 불린다.
static int parseInt(String s)
: 문자열을 정수로 변환해서 반환static interger valueOf(String s)
: 인수로 전달된 문자열의 값으로 초기화된 Integer
Math 클래스
static double abs(double a)
: double형 인수의 절대값을 반환static int abs(int a)
: int 형 인수의 절대값을 반환static double ceil(double a)
: double 형 인수의 올림값을 반환. 숫자의 올림값은 이 숫자 보다 크거나 같은 갖아 가까운 double형 값. ( 기본적인 클래스 외에 다른 클래스를 사용할 때 ‘임포트’를 해야할 경우가 있다.)
클래스형 변수
클래스형 변수에 같은 클래스 형 변수를 대입하는 것이 가능 단 하지만 대입 받은 변수가 대입 하는 변수를 가리킨다는 뜻
null의 원리 이해하기
null을 대입하면 변수가 객체를 가리킬 수 없게 된다. (만약 위와 같은 상황으로 객체를 어떤 변수에서도 참조하지 않게 되면 Java의 판단에 따라 객체는 소멸되고 메모리는 객체를 생성하기 전의 상태로 돌아간다. 이 매커니즘을 가리켜 가비지컬렉션이라고 한다.)
finalize() 메소드
객체가 소멸될 때에는 finalize()
라는 메소드가 자동으로 호출된다.
이 메소드를 선언하더라도, finalize() 메소드가 호출되는 타이밍까지 관리할 수는 없다.
값의 전달과 참조의 전달
클래스형 변수를 인수로 전달하게되면, parameter와 argument는 같은 객체를 가리키게 된다. 하지만 기본형 변수를 인수로 전달한 경우, 호출된 쪽의 argument와 호출한 쪽의 parameter는 각각 다른 것이 된다. 값이 복사되어 수신자에게 전달되기 때문이다. 즉 정리하자면 객체는 참조가 전달된다. 기본형은 값이 전달된다.
객체 배열
Car[] cars;
// Car[]형 배열 인수를 준비한다
cars = new Car[3]
// Car 형 배열 요소를 세 개 생성한다
객체를 배열로 다루려면 준비과정이 하나 더 필요
배열 요소가 그 객체를 가리키도록 대입하는 과정이다
for(int i = 0; i < cars.length; i++){
cars[i] = new Car();
}
즉 정리하자면
- 배열을 준비한다
- 객체를 만들고 배열 요소가 그들을 가리키도록 대입한다. 이 두 가지 작업이며, 두 작업 모두에서 new가 사용된다.
chapter 11 새로운 클래스
새로 확장된 클래스가 기존 클래스의 멤버를 물려받는 것을 가리켜 상속이라고 부른다. 이 때 바탕이되는 클래스는 슈퍼 클래스, 새로운 클래스는 서브 클래스 라고 부른다,.
서브 클래스 선언
class 서브 클래스 이름 extends 슈퍼 클래스 이름
{
서브 클래스에 추가하는 멤버 ...
서브 클래스의 생성자(인수 목록)
{
...
}
}
슈퍼 클래스의 생성자 호출하기
서브 클래스의 생성자 호출에 앞서, 슈퍼 클래스의 생성자가 호출되는 것이 기본이다. 슈퍼 클래스의 서브 클래스에게 상속되지 않습니다. 그 대신, 슈퍼 클래스의 인수 없는 생성자가 자동적으로 호출이 된다. 이렇게 슈퍼 클래스로부터 상속받은 멤버가 성곡적으로 초기화되는 메커니즘을 갖추고 있다.
슈퍼 클래스의 생성자 지정하기
Q) 슈퍼클래스의 생성자가 여러 개 존재한다면? 호출되는 생성자를 명시적으로 지정해야할 수도 있을 것이다.
그럴 때 서브 클래스의 생성자 선언부의 시작 부분에서 super()를 호출할 수 있다.
예를 들어
public Car () {}
public Car (int n, double g) {}
이렇게 생성자가 2개 있다면
racingCar 생성 후
super(n,g); 이런 식으로 슈퍼 클래스에 존재하는 두 개의 인수를 받는 생성자가 호출되도록 한다.
즉 super()를 사용하면 슈퍼 클래스의 생성자를 골라서 호출할 수 있다.
정리하자면
this() : 그 클래스의 다른 생성자를 호출
super() : 슈퍼 클래스의 생성자를 호출 ※ 두 메소드 모두 생성자의 가장 윗 부분에 위치해야한다 . / 오버로딩된 생성자를 선택해야하는 경우 안에 생성자의 인수를 대입
멤버에 접근하기 - 서브 클래스 안에서. protected
슈퍼 클래스의 필드를 private 멤버로 지정했을 경우, 상속자인 서브 클래스조차 접근할 수 없다. 그러한 경우 protected라는 제한자 사용 가능 protected는 서브 클래스가 접근할 수 있다.
오버라이딩
서브 클래스에서 새로운 메소드를 선언할 때 슈퍼 클래스의 완전히 동일한 메소드를 선언할 수 있는 기능 메소드 이름과 인수의 개수와 형까지. 호출은 어떻게 될까? 서브 클래스의 메소드가 슈퍼 클래스의 메소드를 대신하는 기능을 오버라이딩이라고 한다. Q) 서브 클래스 객체로 슈퍼 클래스 객체를 다룰 수 있다. 즉
Car car1;
car1 = new RacingCar();
이 때는 어떤 show() 메소드가 호출이될까. -> 이번에도 서브클래스의 메소드가 호출이 된다. 즉 Java에서는 객체를 가리키는 변수의 클래스에 관계없이 객체 자신의 클래스에 따라 적절한 메소드가 호출된다. 단 슈퍼 클래스 변수로 서브 클래스를 다루게 되면 setCourse() 메소드처럼 서브 클래스에서 새로 정의한 메소드를 호출할 수 없다. => 슈퍼클래스의 배열 이용한 후, 각각의 인덱스에 슈퍼 클래스 객체와 서브 클래스객체를 생성 한 뒤 연결해주면 함께 사용이 가능하다. show 메소드를 호출해도 각각에 맞는 메소드가 호출된다. -> 다형성
※ 오버라이딩 : 메소드 이름이 같지만 인수의 형과 개수가 다른 메소드를 정의하는 것
※ 오버로딩 : 서브 클래스에서 이름은 물론이거니와 인수의 형과 개수까지 슈퍼 클래스의 메소드와 동일한 메소드를 정의하는 기능
슈퍼 클래스와 같은 이름의 멤버 사용하기
class RacingCar extends Car
{
...
public void show()
{
super.show();
System.out.println("코스 번호는 " + course + "입니다.");
}
}
서브 클래스 안에서 메소드 이름 앞에 super.을 붙이고 호출하면 슈퍼 클래스의 메소드를 호출할 수 있다. 이렇게 하면, 슈퍼 클래스의 메소드의 정의를 사용하여 서브 클래스의 메소드를 작성할 수 있다. ‘super.’은 필드 이름 앞에서도 붙일 수 있다. 슈퍼 클래스와 서브 클래스에 같은 이름의 필드가 있을 경우, 슈퍼 클래스의 필드에 접근하려면 변수 이름 앞에 super.을 붙이면 된다. ※ 메소드 안에서 this.을 붙이면 인스턴스 변수, 붙이지 않으면 로컬 변수를 뜻하게 된다.
Class Car
{
int x; // 인스턴스 변수 x
public void show(int x) // 로컬 변수(인수) x가 있다.
{
this.x = x; // 인스턴스 변수 x가 왼쪽, 오른쪽은 로컬 변수 x
}
}
final붙이기
슈퍼클래스의 메소드 중에서는 서브 클래스에게 결코 오버라이딩 하면 안되는 메소드가 있을 수 있다.
이런 경우 슈퍼클래스의 메소드 이름 앞에 final을 붙이면 오버라이딩 되지 않도록 막을 수 있다.
ex) public final void show(){}
만약 서브 클래스 자체를 만들 수 없는 클래스를 설계하고 싶다면, 클래스 앞 부분에 final을 붙일 수 있다.
final class Car{}
마지막으로 final을 필드이름 앞에 붙이면 ‘이 필드의 값을 변경할 수 없음’을 뜻한다.
이 필드는 선언할 때, 반드시 초기화해야 한다. 이처럼 고정된 수를 나타내는 필드를 가리켜 상수라고 한다.
ex) ` static fianl int NUM_TIRE = 4;`
Object 클래스의 상속
2개 이상의 클래스가 여러 개의 슈퍼클래스로부터 상속을 받을 수 없다!. 하지만 이러한 다중 상속은 인터페이스라는 매커니즘을 통하여 비슷하게 구현할 수는 있다.
만약, Java에서는 클래스를 만들 때, 슈퍼클래스를 지정하지 않으면,
그 클래스는 Object 클래스를 상속받는다.
는 약속이 있다.
따라서 Java의 클래스 모두 Object 클래스의 멤버를 상속 받게 된다.
Object클래스의 메소드
- toString() 메소드 . 객체를 나타내는 문자열을 반환
System.out.println(car);
이 메소드를 호출하면. Object 클래스로부터 상속받은 toString()메소드가 호출된다.
따라서 스스로 설계한 클래스에서 toString() 메소드를 재정의해두면 우용하다.
예시 코드 한번 써봤다.
class Car
{
//서브 클래스만 접근 가능하도록 protected로 선언 필드
protected int num;
protected double gas;
//생성자 선언
public Car()
{
num = 0;
gas = 0.0;
System.out.println("자동차가 만들어졌습니다.");
}
// 메소드
public void setCar(int n, double g)
{
num = n ; gas = g;
System.out.println("차량 번호를 " + num + "으로, 연료양을 "+ gas + "로 바꾸었습니다." );
}
//toString() 메소드를 재정의
public String toString()
{
String str = "차량 번호 : " + num + " 연료 양 : " + gas;
return str;
}
}
public class Main {
public static void main(String[] args)
{
Car car1 = new Car();
car1.setCar(1234,20.5);
System.out.println(car1);
}
}
객체를 빈번히 출력해야 하는 경우에는 오버라이딩 해주면 편하다.
eauals() 메소드 사용하기
두 변수가 가리키는 객체가 동일한 경우 true를 반환 ※ string 클래스의 equals() 메소드는 ‘두 변수가 동일한 객체를 가리키는지’ 조사하는게 아니라 ‘두 변수가 가리키는 문자열이 동일한지’로 재정의되어있다.
getClass() 메소드 사용하기
객체가 속한 클래스의 정보를 반환한다.
참고자료
그림으로 배우는 자바