* 인스턴스(instance) 멤버 : 객체를 생성한 후 사용할 수 있는 필드와 메소드를 말한다. 이들은 각각 인스턴스 필드, 인스턴스 메소드라고 부른다. 인스턴스 멤버는 객체 소속된 멤버이기 때문에 객체 없이는 사용할 수 없다.
- 외부 클래스에서 인스턴스 멤버로의 접근 : 외부에서 접근할 때는 객체의 주소가 들어간 참조 변수에 도트 연산자(.)를 통해 접근할 수 있다.
Car myCar = new Car(); //객체 생성
myCar.speed = 10; //도트 연산자를 통한 해당 객체의 인스턴스 필드로 접근
인스턴스 필드는 heap영역에 객체마다 따로 존재하고, 인스턴스 메소드는 객체 마다 존재하지 않고 메소드 영역에 저장되고 공유된다. 변수는 스택(Stack) 영역에 저장되고 참조 변수일 경우 스택 영역에서 힙 영역에 있는 객체의 주소를 가리킨다.
- 해당 클래스에서 인스턴스 멤버로의 접근
객체 내부에서도 인스턴스 멤버에 접근하기 위해서는 this를 사용할 수 있다. 즉, 객체는 자신을 “this"라고 한다. 따라서 this.필드명 = 데이터; 은 자신이 가지고 있는 필드에 데이터를 대입하는 것이다. 또는 this.메소드명(); 은 자신이 가지고 있는 메소드를 실행하는 것이다.
this를 사용하는 이유는 주로 생성자와 메소드의 매개 변수 이름이 해당 객체의 필드와 동일한 경우, 인스턴스 멤버인 필드임을 명시하기 위해서이다.
Car(String model) { //생성자
this.model = model;
}
void setModel(String model) { //메소드
this.model = model;
}
* 정적(static) 멤버 : 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 의미한다. 이들은 각각 정적 필드, 정적 메소드라고 부른다. 정적 멤버는 객체에 소속된 멤버가 아니라 클래스에 소속된 멤버이기 때문에 클래스 멤버라 부르기도 한다. 따라서 클래스 로더가 클래스(바이트 코드)를 로딩해서 메소드 영역에 추가할 때 클래스별로 관리된다. 클래스의 로딩이 끝나면 바로 사용할 수 있다.
- 정적 멤버 선언 : 선언 시 static 키워드를 추가적으로 붙이면 된다.
public class 클래스명 {
//정적 필드
static int field1 = 10;
//정적 메소드
static 리턴타입 method1(int parameter) { .... }
}
- 정적 멤버 사용 : 클래스 이름과 함께 도트 연산자(.)로 접근한다. 원칙적으로는 클래스 이름으로 접근해야 하지만 객체 참조 변수로도 접근이 가능하다.
클래스명.필드;
클래스명.메소드();
참조변수명.필드;
참조변수명.메소드();
- 정적 초기화 블록 : 정적 필드는 객체 없이도 사용해야 하므로 객체 생성 시에만 실행되는 생성자에서는 초기화 할 수 없다. 따라서 정적 블록(block)을 제공한다.
static { //정적 블록
...
}
정적 블록은 클래스가 메모리로 로딩될 때 자동적으로 실행된다. 정적 블록은 클래스 내부에서 여러 개 선언되어도 상관없다. 선언된 순서대로 실행된다.
단, 정적 블록 안에 객체 자신의 참조인 this키워드와 인스턴스 멤버를 절대 사용할 수 없다.
왜? 객체가 없어도 실행되기 때문이다.
public class 클래스명 {
static int info;
static String str;
static {
info = 100;
str = "안녕“ + "하세요.”;
}
}
- 구분 : 필드 선언 시 객체마다 가지고 있어야 할 데이터라면 인스턴스 필드로 선언하고, 객체마다 가지고 있을 필요성이 없는 공용적인 데이터라면 정적 필드로 선언하는 것이 좋다.
메소드 선언 시 인스턴스 필드를 이용해서 실행해야 한다면 인스턴스 메소드를 선언하고, 인스턴스 필드를 이용하지 않는다면 정적 메소드로 선언한다.
* 싱글톤(Singleton) : 전체 프로그램에서 단 하나의 객체만 만들고 싶은 경우에 이용된다. 즉, 싱글톤은 단 하나만 생성되는 객체이다. 싱글톤을 만들려면 클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 막아야 한다. 즉, 생성자의 앞에 private라는 접근 제한자를 붙여주면 된다. 접근 제한자는 뒤에서 배울 것이다.
- 싱글톤 사용방법 : 자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성하여 초기화한다. 클래스 내부에서는 new 연산자 사용이 가능하다. 정적필드도 private 접근 제한자를 붙여 외부에서 필드값을 변경하지 못하게 한다.
public class 클래스명 {
//정적 필드
private static 클래스명 변수이름 = new 클래스();
//생성자
private 클래스명() { ... }
//정적 메소드
static 클래스명 정적메소드명() { //메소드이름은 getXXX로 하는 것이 좋다.
return 변수이름
}
}
외부에서 객체를 얻기 위한 방법은 싱글톤 객체 내부에 있는 정적 메소드를 호출하는 것이다. 여기서 정적메소드는 단 하나의 객체만 리턴하기 때문에 다른 변수에서 호출해도 같은 객체를 가리키는 것이다.
클래스 변수1 = 클래스.정적메소드명();
클래스 변수2 = 클래스.정적메소드명();
변수1과 변수2는 같은 객체의 번지를 참조한다.
* final 필드와 상수(static final)
- final 필드 : 최종적인 필드라는 의미로, final 필드는 초기값이 저장되면 바꿀 수가 없으므로 프로그램 내에서 수정이 불가능하다.
- final 필드 선언 : 선언과 동시에 초기값을 주거나 생성자를 통해서 주는 방법 두 가지만 가능하다. 또한 final은 초기화되지 않을 경우 컴파일 에러가 발생한다.
ex) 아이디나 주민등록번호와 같은 데이터를 저장할 때 사용된다.
- final 적용 범위 : 필드, 메소드(해당 클래스 상속 시 오버라이딩 불가), 클래스(타 클래스 상속 불가)
- 상수(static final) : 변하지 않는 데이터를 의미한다.
- 상수의 선언 : 일반적으로 선언과 동시에 초기화하지만 복잡한 초기화일 경우 정적 블록을 통해 이루어지기도 한다. 상수의 이름은 모두 대문자로 작성하는 것이 관례이다.
ex) 원주율과 같은 데이터를 저장할 때 사용된다.
- 상수의 범위 : 필드, 메소드(해당 클래스 상속 시 오버라이딩 불가), 초기화 블록(클래스가 초기화될 때 수행되고, main()함수보다 먼저 실행된다.)
- 상수(static final)과 final은 유사해보이지만 엄연히 다르다.
final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있지만, 상수는 객체마다 저장할 필요가 없기 때문에 클래스의 모든 객체가 공유하게 되는 공용성을 띄므로 객체를 생성하지 않아도 사용할 수 있다, 또한 불변의 값이므로 여러 가지 값으로 초기화될 수 없다. 상수를 클래스 변수라고 부르며, 클래스에 속한다고 표현한다.
* 패키지(package) :물리적인 형태는 파일 시스템의 폴더로 컴파일 과정에서 자동적으로 생성되는 폴더이다, 패키지는 클래스를 유일하게 만들어주는 식별자 역할을 한다. 클래스 이름이 동일하더라도 패키지가 다르면 다른 클래스로 인식한다.
- 패키지 선언 클래스의 첫 줄에 선언한다. 패키지의 계층을 구분할 때 도트 연산자(.)를 사용한다. 단, 이클립스는 선언이 없는 패키지를 default 패키지에 포함시킨다.
package 상위패키지.하위패키지; //패키지 선언
- 패키지 작성 규칙
- 숫자로 시작해서는 안 되고, _, $를 제외한 문자를 사용해서는 안 된다.
- java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용해서는 안 된다. ex) java.awt.event;
- 모두 소문자로 작성하는 것이 관례이다.
- 다른 패키지의 사용
첫 번째 방법은 패키지와 클래스를 모두 기술하는 것이다. 서로 다른 패키지에 동일한 클래스 이름이 존재하고, 두 패키지가 모두 import되어 있을 경우에는 꼭 필요한 방법이다.
//com.hankook패키지의 Tire클래스를 이용해서 필드를 선언하고 객체를 생성한 것이다.
com.hankook.Tire tire = new com.hankook.Tire();
두 번째 방법은 사용하고자 하는 패키지를 import문으로 선언하고, 객체를 생성할 때는 패키지명을 생략하는 것이다. 단, import 문이 작성되는 위치는 패키지 선언과 클래스 선언사이이다.
package com.mycompany;
import com.hankook.Tire;
// 또는 import com.hankook.*;
//여기서 *는 해당 패키지내의 모든 클래스를 import한다는 의미이다.
public class Car {
Tire tire = new Tire();
}
주의할 점은 import 문으로 지정된 패키지까지만 허용된다. 즉, 지정된 패키지의 하위 패키지의 클래스를 사용하고 싶다면 import문을 하나 더 사용해야 한다.
* 접근 제한자
접근 제한자 |
적용 대상 |
접근가능한 클래스 |
public |
클래스, 필드, 생성자, 메소드 |
모두 허용 (다른 패키지 ok) |
protected |
필드, 생성자, 메소드 |
다른 패키지라도 자식 클래스(상속)까지 허용 |
default |
클래스, 필드, 생성자, 메소드 |
같은 패키지에 소속된 클래스 |
private |
필드, 생성자, 메소드 |
같은 패키지라도 해당 클래스만 허용 |
- 접근 제한 정도 : public < protected < default < private
- 접근 가능 범위 : public > protected > default > private
* 메소드(Method)
- 일반적으로 객체 지향 프로그래밍에서 객체의 데이터는 객체 외부에서 직접적으로 접근하는 것을 막는다. 이는 정보 은닉이라는 특성이 있기 때문이다. 즉, 외부에서 변경할 경우 객체의 무결성이 깨어질 수 있기 때문이다.
- 주로 객체 지향 프로그래밍에서는 메소드를 통해 데이터를 변경한다.
- Setter 메소드는 setXXX(매개변수) 로 메소드 이름을 짓는 것이 관례이며, 외부에서 해당 객체의 데이터에 접근할 때 사용하는 메소드이다. 메소드는 매개값을 검증해서 유효한 데이터만 받아들일 수 있기 때문이다.
void setSpeed(double speed) { // 데이터를 설정하므로 리턴값이 없기 때문에 리턴 타입은 void로 한다.
if(speed < 0) {
this.speed = 0;
return;
} else {
this.speed = speed;
}
}
- Getter 메소드는 getXXX() 로 메소드 이름을 짓는 것이 관례이며, 외부에 해당 객체의 데이터를 전달할 때 사용하는 메소드이다. 주로 필드값을 가공한 후 외부로 전달한다.
double getSpeed() { //전달할 데이터에 맞는 리턴타입을 써야한다.
double km = speed * 1.6;
return km;
}
- 단, 필드 타입이 boolean인 데이터에 접근할 때는 Getter 메소드는 getXXX()의 형태가 아닌 isXXXX()로 시작하는 것이 관례이다.
private boolean stop;
//Getter
public boolean isStop() {
return stop;
}
//Setter
public void setStop(boolean stop) {
this.stop = stop;
}
* 이클립스에서는 필드 선언 후 메뉴에서 [source → Generate Getters and Setters] 를 선택하면 자동으로 만들어진다.
'Programming Language > JAVA' 카테고리의 다른 글
10. Abstract Class - 추상 클래스 (0) | 2017.04.08 |
---|---|
9. Extend - 상속 (0) | 2017.04.08 |
7. Class and Object - 클래스와 객체 (+ 쓰레기 수집) (0) | 2017.04.06 |
6. About Java and Exception - 예외 (0) | 2017.03.31 |
5. Control Statement, Looping - 제어문과 루핑(조건문과 반복문) (0) | 2017.03.23 |