*본 내용은 [Do it! 자바 프로그래밍 입문] 책과 강의를 보고 공부하면서 요점 정리한 내용입니다.
(this == 자신의 메모리의 주소)
ThisExample.Java
package thisex;
class Birthday{
int day;
int month;
int year;
public void setYear(int year) { //8번줄
// year = year; //9번줄
// 코드는 가장 가까운 곳을 참조한다. 위 코드에서 left value는 8줄의 year 참조, right value는 9줄의 left value 참조
this.year = year; //따라서 다음과 같이 this를 이용해 코딩
}
public void printThis() { //this 출력
System.out.println(this);
}
}
public class ThisExample {
public static void main(String[] args) {
Birthday b1 = new Birthday();
Birthday b2 = new Birthday();
System.out.println(b1);
b1.printThis();
System.out.println(b2);
b2.printThis();
}
}
콘솔창 결과
b1을 출력한 값과 b1을 printThis()한 값은 같지만 b1과 b2의 this는 서로 값이 다르다.
같은 this라도 인스턴스마다 매번 가리키는 값이 달라진다.
thisex.Birthday@1d81eb93
thisex.Birthday@1d81eb93
thisex.Birthday@7291c18f
thisex.Birthday@7291c18f
CallAnotherConst.java
package thisex;
class Person{
String name;
int age;
public Person() {
this("이름없음", 1); //this를 이용해 생성자에서 다른 생성자 호출
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class CallAnotherConst {
public static void main(String[] args) {
Person p1 = new Person();
System.out.println(p1.name);
}
}
콘솔창 결과
이름없음
※주의
생성자에서 다른 생성자 부르는 this 사용할 때는 앞에 어떤 문장도 올 수 없다.
따라서 다음과 같이 this("이름없음", 1);
앞에 int i = 0;
를 작성하면 오류가 발생한다.
생성자에서 다른 생성자를 호출할 때 this를 포함한 문장은 가장 먼저 나오는 문장이어야한다.
public Person() {
int i = 0; //오류 발생
this("이름없음", 1);
}
package thisex;
class Person{
String name;
int age;
public Person() {
this("이름없음", 1);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person returnSelf() {
return this;
}
}
public class CallAnotherConst {
public static void main(String[] args) {
Person p1 = new Person();
System.out.println(p1.returnSelf()); //this 이용해 자신의 주소 출력
}
}
콘솔창 결과
thisex.Person@1d81eb93
학생이 버스, 지하철을 이용하면 학생의 돈이 줄어들고 버스, 지하철의 수입은 늘어나게 만들고자 한다.
Student.java
package cooperation;
public class Student {
String studentName;
int grade;
int money;
public Student(String studentName, int money) {
this.studentName = studentName;
this.money = money;
}
public void takeBus(Bus bus) { //객체의 협업 (버스~학생)
bus.take(1000);
money -= 1000;
}
public void takeSubway(Subway subway) { //객체의 협업 (지하철~학생)
subway.take(1500);
money -= 1500;
}
public void showInfo() {
System.out.println(studentName + "님의 남은 돈은" + money + "입니다.");
}
}
Bus.java
package cooperation;
public class Bus {
int busNumber;
int passengerCount;
int money;
public Bus(int busNumber) {
this.busNumber = busNumber;
}
public void take(int money) { //승객을 한 명 태웠을 때
this.money += money;
passengerCount++;
}
public void showInfo() {
System.out.println("버스" + busNumber + "번의 승객은" + passengerCount +
"명이고, 수입은" + money + "입니다");
}
}
Subway.java
package cooperation;
public class Subway {
int lineNumber;
int passengerCount;
int money;
public Subway(int lineNumber) {
this.lineNumber = lineNumber;
}
public void take(int money) { //승객을 한 명 태웠을 때
this.money += money;
passengerCount++;
}
public void showInfo() {
System.out.println("지하철" + lineNumber + "번의 승객은" + passengerCount +
"명이고, 수입은" + money + "입니다");
}
}
TakeTrans.java
package cooperation;
public class TakeTrans {
public static void main(String[] args) {
Student james = new Student("James", 5000);
Student tomas = new Student("Tomas", 10000);
Bus bus100 = new Bus(100);
james.takeBus(bus100);
james.showInfo();
bus100.showInfo();
Subway subwayGreen = new Subway(2);
tomas.takeSubway(subwayGreen);
tomas.showInfo();
subwayGreen.showInfo();
}
}
콘솔창 결과
James님의 남은 돈은4000입니다.
버스100번의 승객은1명이고, 수입은1000입니다
Tomas님의 남은 돈은8500입니다.
지하철2번의 승객은1명이고, 수입은1500입니다
static예약어 자료형 변수이름;
//예시
static int serialNum;
static int serialNum = 100; //serailNum이 static 변수
static 변수는 클래스 변수라고도 한다.
static 변수는 인스턴스가 생성될 때마다 다른 메모리를 가지는 것이 아니라 프로그램이 메모리에 적재(load) 될 때 데이터 영역의 메모리에 생성 된다. 따라서 인스턴스의 생성과 관계없이 클래스 이름으로 직접 참조 한다.
Student.getSerialNum(); //getSerialNum()이 static 메서드
static 메서드는 클래스 메서드라고도 한다.
메서드에 static 키워드를 사용하여 구현한다. 주로 static 변수를 위한 기능을 제공한다.
static 메서드도 인스턴스의 생성과 관계 없이 클래스 이름으로 직접 메서드를 호출한다.
static 메서드에서 인스턴스 변수를 사용할 수 없다.
인스턴스의 변수의 경우 꼭 인스턴스가 먼저 생성되어야 하므로 static 메서드에서는 생성이 불확실한 인스턴스 변수를 사용할 수 없다.
학생이 생성될 때마다 학번이 증가해야 하는 경우를 코드로 작성하고자 한다.
Student studentJ = new Student();
할 때마다 자동으로 1씩 추가된 studentID값이 들어가게 만들고자 한다.
기준이 되는 값은 static 변수로 생성하여 유지한다.
각 학생이 생성될 때마다 static 변수 값을 복사해 와서 하나 증가 시킨 값을 생성된 인스턴스의 학번 변수에 저장해 준다.
Student.java
package staticex;
public class Student {
static int serialNum = 10000; //static 변수
int studentID;
String studentName;
public Student() {
serialNum++;
studentID = serialNum;
}
public static int getSerialNum() { //static 메서드
return serialNum;
}
public static void setSerialNum(int serialNum) { //static 메서드
Student.serialNum = serialNum;
}
}
System.out.println(Student.getSerialNum());
인스턴스 생성과 상관 없이 다음 코드는 사용 가능하다. 즉 인스턴스가 만들어지지도 않은 채로 다음과 같이 "홍길동"값을 입력하고자 하면 오류가 발생할 수 있다. public static int getSerialNum() {
//[정상 작동]
int i = 10; //지역변수
i++;
System.out.println(i);
//[오류 발생]
studentName = "홍길동"; //인스턴스 변수, 멤버 변수
return serialNum; //static 변수, 클래스 변수
}
StudentTest1.java
studentJ.serialNum
studentT.serialNum
보단 Student.serialNum
으로 작성해야 하고, studentJ.getSerialNum()
studentT.getSerialNum()
보단 Student.getSerialNum()
으로 작성해야 한다.package staticex;
public class StudentTest1 {
public static void main(String[] args) {
Student studentJ = new Student();
System.out.println(studentJ.studentID);
Student studentT = new Student();
System.out.println(studentT.studentID);
System.out.println(Student.serialNum); //static 변수 출력
System.out.println(Student.getSerialNum()); //static 메서드 활용해 static 변수 출력
}
}
콘솔창 결과
10001
10002
10002
10002
+참고) static 변수 강화
static 변수에 private를 이용하고, get/set 함수 중 set 함수를 없애 참조만 할 수 있게 만들면 중요한 static 변수 값을 외부에서 변경하지 못하게 할 수 있다. (인스턴스 만들어질 때만 변경시킬 수 있음)
Student.java
package staticex;
public class Student {
private static int serialNum = 10000; //private 이용
int studentID;
String studentName;
public Student() {
serialNum++;
studentID = serialNum;
}
public static int getSerialNum() {
return serialNum;
}
//가져갈수만 있게 값 변경 못하게
public static void setSerialNum(int serialNum) {
Student.serialNum = serialNum;
}
}
변수 유형 | 선언 위치 | 사용 범위 | 메모리 | 생성과 소멸 |
---|---|---|---|---|
지역 변수 (로컬 변수) | 함수 내부에 선언 | 함수 내부에서만 사용 | 스택 | 함수가 호출될 때 생성되고 함수가 끝나면 소멸함 |
멤버 변수 (인스턴스 변수) | new 예약어를 사용하여 클래스 멤버 변수로 선언 | 클래스 내부에서 사용하고 private이 아니면 참조 변수로 다른 클래스에서 사용 가능 | 힙 | 인스턴스가 생성될 때 힙에 생성되고 가비지 컬렉터가 메모리를 수거할 때 소멸됨 |
static 변수 (클래스 변수) | static 예약어를 사용하여 클래스 내부에 선언 | 클래스 내부에서 사용하고 private이 아니면 클래스 이름으로 다른 클래스에서 사용 가능 | 데이터 영역 | 프로그램이 처음 시작할 때 상수와 함께 데이터 영역에 생성되고 프로그램이 끝나고 메모리를 해제할 때 소멸됨 |
*데이터 영역 - static, literal 등이 생성되는 영역
*static 변수는 프로그램이 사용하는 메모리가 한정되어 있으므로 100크기의 배열과 같이 너무 큰 메모리를 선언하는 것을 지양해야 한다.
프로그램을 구현하면서 하나의 인스턴스(객체)만 필요한 경우 싱글톤 패턴을 이용하는데,
객체 지향 프로그램에서 인스턴스를 단 하나만 생성하는 디자인 패턴을 싱글톤 패턴이라고 한다.
하나의 객체만 필요한 경우 C언어는 글로벌 변수를 선언하면 되지만 Java는 클래스 외부에 변수를 선언할 수 없다. 어떻게 해야할까? 회사 클래스를 구현하면서 알아보자
단 하나 있어야 하는 회사를 구현해보자
[클래스 다이어그램]
※ 클래스 다이어그램 읽는 방법
Company.java
package singleton;
public class Company {
//클래스 내부에서 Company를 생성한다. 전체에서 유일하게 사용 될 인스턴스이다.
//함부로 값을 바꿀수 없으므로 private, 단 하나이므로 static을 이용한다.
private static Company instance = new Company();
//private로 외부에서 함부로 Company를 생성하지 못하게 한다.
private Company(){}
//생성된 인스턴스는 외부에서 가져다 사용할 수 있게 한다.
//객체를 생성하지 않고 메서드를 부르기 위해 static 메서드를 이용한다.
public static Company getInstance() {
return instance;
}
}
CompanyTest.java
package singleton;
import java.util.Calendar;
public class CompanyTest {
public static void main(String[] args) {
Company c1 = Company.getInstance();
Company c2 = Company.getInstance();
//동일한 주소값 출력된다.
System.out.println(c1);
System.out.println(c2);
//Company c3 = new Company(); //error
//System.out.println(c1 == c2); //true
}
}
콘솔 창
singleton.Company@1d81eb93 //동일한 주소값
singleton.Company@1d81eb93
※ 안전장치를 위해 getInstance()를 다음과 같이 작성하기도 한다.
public static Company getInstance() {
if(instance == null)
instance = new Company();
return instance;
}
※ 날짜도 싱글톤 패턴으로 구현되어 있어 다음과 같이 사용한다.
Calendar cal = Calendar.getInstance();