: 인스턴스(instance)멤버란 객체(인스턴스)를 생성한 후
사용할 수 있는 필드(인스턴스 필드)와 메소드(인스턴스 메소드)
: 인스턴스 필드와 메소드는 객체에 소속된 멤버이기 때문에
객체 없이는 사용할 수 없음
- 인스턴스 멤버 선언
public class Car {
//필드
int gas;
//메소드
void setSpeed(int speed) { ··· }
}
→ gas 필드, setSpeed()메소드는 인스턴스 멤버이기 때문에
외부 클래스에서 사용하기 위해서는 Car객체(인스턴스)를 생성하고
참조 변수 myCar 또는 yourCar로 접근해야함
Car myCar = new Car();
myCar.gas = 10;
myCar.setSpeed(60);
Car yourCar= new Car();
yourCar.gas = 20;
yourCar.setSpeed(80);
→ 인스턴스 필드 gas는 객체마다 따로 존재하고,
인스턴스 메소드 setSpeed()는 메소드 영역에 저장되고 공유됨
Q. 인스턴스 메소드는 객체에 소속된 멤버인데,
왜 객체 내부에 존재하지 않고 메소드 영역에 저장되고 공유된다고 했을까?
A. 메소드는 코드 블록이므로 객체마다 동일한 코드 블록을 가지고 있을 필요x
Q. 인스턴스라는 용어는 왜 붙였을까?
A. 메모드 블록 내부에 인스턴스 필드 등이 사용되는 경우가 있기 때문
→ 인스턴스 필드가 사용되면 메소드 역시 객체 없이는 실행할 수 없음
: 객체 내부에서도 인스턴스 멤버에 접근하기 위해 this를 사용할 수 있음
: 주로 생성자와 메소드의 매개 변수 이름이 필드와 동일한 경우,
인스턴스 멤버인 필드임을 명시하고자 할 때 사용
Car(String model){
this.model = model;
}
void setModel(String model){
this.model = model;
}
👩💻 인스턴스 멤버와 this
class Car{
//필드
String model;
int speed;
//생성자
Car(String model){
this.model = model;
}
//메소드
void setSpeed(int speed) {
this.speed = speed;
}
void run() {
for(int i=10; i <= 50; i+=10) {
this.setSpeed(i);
System.out.println(this.model + "가 달립니다,
(시속:" + this.speed + "km/h)");
}
}
}
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car("포르쉐");
Car yourCar = new Car("벤츠");
myCar.run();
yourCar.run();
}
}
💻 결과
포르쉐가 달립니다,(시속:10km/h)
포르쉐가 달립니다,(시속:20km/h)
포르쉐가 달립니다,(시속:30km/h)
포르쉐가 달립니다,(시속:40km/h)
포르쉐가 달립니다,(시속:50km/h)
벤츠가 달립니다,(시속:10km/h)
벤츠가 달립니다,(시속:20km/h)
벤츠가 달립니다,(시속:30km/h)
벤츠가 달립니다,(시속:40km/h)
벤츠가 달립니다,(시속:50km/h)
: 클래스에 고정된 멤버 로서
객체를 생성하지 않고 사용할 수 있는 필드(정적 필드)와 메소드(정적 메소드)
- 정적 멤버 선언
public class 클래스 {
//정적 필드
static 타입 필드 [= 초기값];
//정적 메소드
static 리턴 타입 메소드( 매개변수선언, ··· ) { ··· }
}
: 정적 필드와 정적 메소드를 선언 시 static 키워드를 추가적으로 붙이면 됨
: 정적(필드, 메소드)는 클래스에 고정된 멤버이므로
클래스 로더가 클래스(바이트 코드)를 로딩해서
메소드 메모리 영역에 적재할 때 클래스별로 관리됨
따라서 클래스의 로딩이 끝나면 바로 사용할 수 있음
- 인스턴스 필드 vs 정적 필드
: 객체마다 가지고 있어야 할 데이터라면 인스턴스 필드로 선언
객체마다 가지고 있을 필요가 없는 공용 데이터라면 정적 필드로 선언
ex)
public class Calculator {
String color; //계산기별로 색깔이 다를 수 있음
static double pi = 3.14159; //계산기에서 사용하는 파이 값은 동일
}
- 인스턴스 메소드 vs 정적 메소드
: 인스턴스 필드를 포함하고 있다면 인스턴스 메소드로 선언
인스턴스 필드를 포함하고 있지 않다면 정적 메소드로 선언
ex)
public class Calculator {
String color; // 인스턴스 필드
void setColor(String color) { this.color = color; } // 인스턴스 메소드
static int plus(int x, int y) { return x + y; } // 정적 메소드
static int minus(int x, int y) { return x - y; } //정적 메소드
}
- 정적 멤버 사용
: 클래스가 메모리로 로딩되면 정적 멤버를 바로 사용가능
클래스.필드;
클래스.메소드( 매개값, ··· );
ex)
// 클래스 작성
public class Calculator {
static double pi = 3.14159;
static int plus(int x, int y) { ··· }
static int minus(int x, int y) { ··· }
}
// 정적필드, 메소드 사용
double result1 = 10 * 10 * Calculator.pi;
int result2 = Calculator.plus(10, 5);
int result3 = Calculator.minus(10, 5);
- 정적 필드와 정적 메소드는 원칙적으로 클래스 이름으로 접근해야함
하지만 객체 참조 변수로도 접근 가능
ex)
Calculator myCalcu = new Calculator();
double result1 = 10 * 10 * myCalcu.pi;
int result2 = myCalcu.plus(10, 5);
int result3 = myCalcu.minus(10, 5);
// 하지만 정적 요소는 클래스 이름으로 접근하는 것이 좋음
// 그렇지 않고 객체 참조 변수로 접근했을 경우 경고 표시가 나타남
- 정적 메소드 선언 시 주의할 점
: 객체가 없어도 실행된다는 특징 때문에 정적 메소드를 선언할 때는
이들 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없음
: 객체 자신의 참조인 this 키워드도 사용이 불가
ex)
public class ClassName {
//인스턴스 필드와 메소드
int field1;
void method1() { ··· }
//정적 필드와 메소드
static int field2;
static void method2() { ··· }
//정적 메소드
static void Method2 {
this.field1 = 10; //x 컴파일 에러
this.method1(); //x 컴파일 에러
field2 = 10; //o
method2(); //o
}
}
- 정적 메소드에서 인스턴스 멤버를 사용하고 싶다면
객체를 먼저 생성하고 참조 변수로 접근해야함
ex)
static void Method3(){
ClassName obj = new ClassName();
obj.field1 = 10;
obj.method1();
}
- main()메소드 또한 인스턴스 필드, 메소드 바로 사용 불가
ex)
public class Car{
int speed;
void run() { ··· }
public static void main(String[] args){
speed = 60; //x 컴파일에러
run(); //x 컴파일에러
// 수정
Car myCar = new Car();
myCar.speed = 60;
myCar.run();
}
}
public class 클래스{
//정적 필드
private static 클래스 singleton = new 클래스();
//생성자
private 클래스() {}
//정적 메소드
static 클래스 getInstance() {
return singleton;
}
}
: 단 하나만 생성된다고 해서 이 객체를 싱글톤(Singleton)이라고 함
: 싱글톤을 만들려면 클래스 외부에서
new 연산자로 생성자를 호출할 수 없도록 막아야함
→ 생성자를 호출한 만큼 객체가 생성되기 때문
: 생성자를 외부에서 호출할 수 없도록 하려면
생성자앞에 private 접근 제한자를 붙여주면 됨
: 자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성해 초기화한다.
: 클래스 내부에서는 new 연산자로 생성자 호출이 가능함
: 정적 필드도 private 접근 제한자를 붙여
외부에서 필드값을 변경하지 못하게 막아야함
대신 외부에서 호출할 수 있는 정적 메소드인 getInstance()를 선언하고
정적 필드에서 참조하고 있는 자신의 객체를 리턴해줌
- 외부에서 객체를 얻는 유일한 방법은 getInstance()메소드를 호출하는 방법
: getInstance()메소드는 단 하나의 객체만 리턴함
//동일한 객체를 참조
클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();
👩💻 싱글톤
class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
static Singleton getInstance() {
return singleton;
}
}
public class SingletonExample {
public static void main(String[] args) {
/*
* 컴파일 에러
Singleton obj1 = new Singleton();
Singleton obj2 = new Singleton();
*/
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
if(obj1 == obj2) {
System.out.println("같은 Singleton 객체입니다.");
} else {
System.out.println("다른 Singleton 객체입니다.");
}
}
}
💻 결과
같은 Singleton 객체입니다.
final 타입 필드 [= 초기값];
: final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어서
프로그램 실행 도중에 수정할 수 없다는 것
- final 필드의 초기값을 줄 수 있는 방법
1. 필드 선언 시에 주는 방법
2. 생성자에서 주는 방법
👩💻 final 필드 선언과 초기화
class Person{
final String nation = "Korea";
// 생성자는 final 필드의 최종 초기화를 마쳐야 하는데,
// 만약 초기화되지 않은 final 필드를 그대로 남겨두면 컴파일 에러가 발생
final String ssn; //Person 객체가 생성될 때 초기값 설정 가능
String name;
//Person 객체 생성
public Person(String ssn, String name) {
this.ssn = ssn;
this.name = name;
}
}
public class PersonExample {
public static void main(String[] args) {
Person p1 = new Person("123456-1234567", "홍길동");
System.out.println(p1.nation);
System.out.println(p1.ssn);
System.out.println(p1.name);
//p1.nation= "usa"; 지정된 값이라 변경 불가
//p1.ssn = "654321-7654321"; 지정된 값이라 변경 불가
p1.name = "홍삼원";
}
}
💻 결과
Korea
123456-1234567
홍길동
static final 타입 상수= 초기값;
: 불변의 값을 저장하는 필드
: static이면서 final이어야함
: static final 필드는 객체마다 존재하지 않고 클래스에만 존재,
한번 초기값이 저장되면 변경 불가
ex)
static final double PI = 3.14159;
static final double EARTH_RADIUS = 6400;
static final double EARTH_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;
- final vs 상수
- final: 객체마다 저장되고,
생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 때문
- 상수: 불변의 값은 객체마다 저장할 필요가 없는 공용성을 띠고 있으며,
여러 가지 값으로 초기화 될 수 없기 때문
👩💻 상수선언 및 사용
class Earth {
static final double EARTH_RADIUS = 6400;
static final double EARTH_AREA =
4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;
}
public class EarthExample {
public static void main(String[] args) {
System.out.println("지구의 반지름: " + Earth.EARTH_RADIUS + "km");
System.out.println("지구의 표면적: " + Earth.EARTH_AREA + "km^2");
}
}
💻 결과
지구의 반지름: 6400.0km
지구의 표면적: 5.147185403641517E8km^2