Do it 자바 프로그래밍 입문 [클래스와 객체2]

wish17·2022년 10월 27일
0

Do it 자바

목록 보기
2/10

클래스와 객체2 (1)

this가 하는일

  • 자신의 메모리를 가리킨다.
  • 생성자에서 다른 생성자를 호출한다.
  • 자신의 주소를 반환한다. (이건 자주 쓰진 않음)

자신의 메모리를 가리킨다.

package thisex;

class BirthDay{

	int day;
	int month;
	int year;
	
	public void setYear(int year) {
		this.year = year;
//     멤버변수     지역번수
//     위 코드에서 this를 생략하게 되면 year은 파라미터로 사용되는 yrar로 인식 된다.
	}	
//  public void setYear(int y) {
//		year = y;     <<this 안쓸거면 이렇게 변수명을 다르게 써주면 되긴 됨
//  }
	
	public void printThis() {
		System.out.println(this);  //this를 출력하라는 하나의 메소드
	}
}

public class ThisExample {
	public static void main(String[] args) {
		BirthDay b1 = new BirthDay();
		BirthDay b2 = new BirthDay();
		
		System.out.println(b1);
		b1.printThis();         //BirthDay class에서 printThis라고 
		System.out.println(b2);
		b2.printThis();        //정의한 메소드 가져다 쓴거
	}
}

위 출력과 같이 코드는 똑같이 다 this지만 매번 가리키는 것은 그때의 instence의 주소값이라는 것을 확인할 수 있다.

생성자에서 다른 생성자를 호출한다.

class Person{
	String name;
	int age;
	
	public Person() {
		this("이름없음", 1);
	}
	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);
	}
}

p1 변수에 Person 객체를 default 생성자로 호출했고 default 생성자에서는 name과 age를 사용한 생성자를 호출하여 name이 "이름없음"으로 출력된다.

유의할점

public Person() {
	int i = 0;
    name = "test"
	this("이름없음", 1);
}

위처럼 constructor에서 다른 constructor를 호출하기 위해 this를 쓸 때에는 this 앞에 문장(statement)이 들어갈 수 없다.


왜냐하면 이 객체가 생성되기 위해서 다른 생성자를 호출했다는 것은 이 객체가 아직 다 제대로 생성이 안됐다는거고 그러기 위해서 할 일이 마무리되지 않았기 때문에 this 앞에 그 어떤 statement도 오는것이 허락되 않는 것이다.

자신의 주소를 반환한다.

package thisex;

class Person{
	String name;
	int age;
	
	public Person(){
		this("이름없음", 1);  // Person(String, int)
	}
	
	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.name);
		System.out.println(p1.returnSelf());
	}
}

자주 쓰이지 않는다.

클래스와 객체2 (2)

객체 간의 협력

  • 학생이 버스나 지하철을 타는 상황을 객체지향으로 프로그래밍하는 예시

학생이 버스나 지하철을 타는 상황을 프로그래밍하기 위해서는 클래스가 학생, 버스, 지하철이 필요하다.

예시

- 학생

학생의 속성은 이름, 가진 돈, 학년(해당 상황에 필요한 것은 아님)으로 만들었고, 버스를 타거나 지하철을 타면 가진 돈이 줄어드는 메서드로 만들었다.

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 + "원 입니다.");
	}
}

- 버스

버스의 속성
버스 번호, 승객 수, 수입으로 만들었고, 탑승하는 경우에 승객과 수입이 증가하는 메서드를 만들었다.

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 +"입니다.");
	}
}

- 지하철

지하철의 속성은 노선 번호, 승객 수, 수입으로 만들었고, 버스와 마찬가지로 탑승하는 경우에 승객과 수입이 증가하는 메서드를 만들었다.

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 +"입니다.");
	}
}
  • main

2명이 버스와 지하철에 각각 한명씩 탄다고 생각하고
2개의 Student 생성자를 만들어 넣어주었다.
또 타게될 100번버스, 2호선 지하철 각각 생성자를 만들었다.

public class TakeTrans {

	public static void main(String[] args) {
    
		Student james = new Student("James", 5000);
		Student tomas = new Student("Tomes", 10000);
		
		Bus bus100 = new Bus(100);		
		james.takeBus(bus100);
		james.showInfo();
		bus100.showInfo();
		
		Subway subway2 = new Subway(2);
		tomas.takeSubway(subway2);
		tomas.showInfo();
		subway2.showInfo();
	}
}

예시응용

take는 추상메소드로 두고 taxi를 추가하고 탈 것들을 Trans로 묶어 Student와 Trans가 협업을 할 수 있게끔 코드를 변환해보자.

모르겠음 ㅠㅡㅠ 후에 다시 할 예정...
뒤에서 배우는 상속에서 extend를 쓰면 어찌저찌 될 것 같은데...
안쓰고 어떻게 하지...

클래스와 객체2 (3)

static 변수

static 변수의 정의

static"고정된"

⇒ 정적 멤버는 클래스에 고정된 멤버
  ⇒ 객체를 생성하지 않고 사용할 수 있는 필드와 메소드
  • 이들을 각각 정적 필드, 정적 메소드라고 부름
  • 정적 멤버는 객체(인스턴스)에 속해있지 않음 ⇒ 클래스에 속해있음 ⇒ 클래스 멤버라고도 부름
  • static 변수는 자료형 앞에 static 예약어를 써서 사용하며, 여러 개의 인스턴스가 같은 메모리의 값을 공유하기 위해 사용

  • static 변수는 heap 메모리가 아닌 다른 영역(데이터 영역 메모리)에 저장되고, 프로그램이 메모리에 적재(load)될 때 값이 할당된다.
    (인스턴스는 new 키워드로 객체가 생성될 때 heap 메모레어 할당되고, 객체가 소멸되면 사라진다.)
    따라서 인스턴스의 생성과 관계없이 클래스 이름으로 직접 참조함

  • static 변수는 클래스 변수라고도 한다. (멤버변수는 인스턴스 변수라고도 한다.)

예시

Student class

package staticex;

public class Student {
	
	static int serialNum = 10000;
	
	int studentID;
	String studentName;
	
		public Student() {
			serialNum++;
			studentID = serialNum;
		} //ㄴ> 객체 생성 시 serailNum이 증가하고 그 값을 studentID에 대입

}

생성자가 생성됨에(학생수가 늘어남에) 따라 학번이 순차적으로 1씩 늘어나면서 배정되도록 하였다.

main class

package staticex;

public class StudentTest1 {
	public static void main(String[] args) {
		
		Student studentJ = new Student();
		System.out.println("J학생의 학번은"+studentJ.studentID);
		
		
		Student studentT = new Student();
		System.out.println("T학생의 학번은"+studentT.studentID);
		System.out.println("J학생의 학번은"+studentJ.studentID);
		System.out.println("생성자가 추가되어도 학번은 다르고 instans값만 같다");
		System.out.println("T학생의 instans값은"+studentT.serialNum);
		System.out.println("J학생의 instans값은"+studentJ.serialNum);
	}
}

T,J 학생이 학번은 각각 다르게 가져갔지만 instans값은 같은 것을 볼 수 있다.

경고문

그런데 위 코드는 아래와 같이 경고가 뜨는 것을 볼 수 있다.

경고문: The static field Student.serialNum should be accessed in a static way


serialNum이 static변수인데 왜 class이름으로 참조하지 않고 instance변수로 참조 했냐는 경고문이다.

package staticex;

public class StudentTest1 {
	public static void main(String[] args) {
		
		Student studentJ = new Student();
		System.out.println("J학생의 학번은"+studentJ.studentID);
		
		
		Student studentT = new Student();
		System.out.println("T학생의 학번은"+studentT.studentID);
		System.out.println("J학생의 학번은"+studentJ.studentID);
		System.out.println("생성자가 추가되어도 학번은 다르고 instans값만 같다");
		System.out.println("T학생의 instans값은"+Student.serialNum);
		System.out.println("J학생의 instans값은"+Student.serialNum);
	}
}


경고문에 따라 수정해도 같은 결과가 나오는 것을 볼 수 있다.

(어차피 serialNumber를 static으로 선언하면 모든 student instance에 대해 하나의 변수로 유지 되는데, 굳이 class로 호출하지 않고 하나의 instance를 통해 각각 호출하려고 할 필요가 없다.)

Static 변수 vs instance 변수

serialNumber를 static으로 선언하면 모든 student instance에 대해 하나의 변수로 유지 되고 이러한 변수를 class변수라 한다.

Static 변수 예

  1. 여러 인스턴스가 하나의 메모리 값을 공유 할 떄 필요
  2. 학생이 생성될 때마다 학번이 증가해야 하는 경우 기준이 되는 값은 static 변수로 생성하여 유지 함

각 학생이 생성될 떄 마다 static 변수 값을 복사해 와서 하나 증가시킨 값을 생성된 인스턴스의 학번 변수에 저장해 줌

Static 메서드

클래스 메서드라고도 함
메서드에 static 키워드를 사용하여 구현
주로 static 변수를 위한 기능 제공

static 메서드에서 인스턴스 변수를 사용할 수 없음

static 메서드도 인스턴스의 생성과 관계 없이 클래스 이름으로 직접 메서드 호출

예시

Student class

package staticex;

public class Student {
	
	static int serialNum = 10000;
	
	int studentID;
	String studentName;
	
	public Student() {
		serialNum++;
		studentID = serialNum;
	} //ㄴ> 객체 생성 시 serailNum이 증가하고 그 값을 studentID에 대입

	public static int getSerialNum() {
		int i = 10;  // 지역변수
		
		i++;
		System.out.println(i);
		
		studentName = "홍길동"; //멤버변수, 인스턴스 변수
		//ㄴ> 생성되지도 않은 인스턴스 변수에 홍길동을 넣게 되는 꼴이라 오류남
		
		return serialNum; // static 변수, 클래스 변수
		
	}
		
		
		
}

StudentTest1 class

package staticex;

public class StudentTest1 {
	
	public static void main(String[] args) {
		
		System.out.println(Student.getSerialNum());
		
		Student studentJ = new Student();
		System.out.println(Student.getSerialNum());
		
		
		Student studentT = new Student();
		System.out.println(Student.getSerialNum());
		
		
	}
}
Student.getSerialNum();
getSerialNum()               << 이게 static 메서드

인스턴스의 변수의 경우 꼭 인스턴스가 먼저 생성 되어야 하므로 static 메서드에서는 생성이 불확실한 인스턴스 변수를 사용할 수 없음

변수의 유효 범위

지역 변수(로컬 변수)

선언 위치 = 함수 내부에 선언


사용 범위 = 함수 내부에서만 사용
메모리 = 스택
생성과 소멸 = 함수가 호출될 때 생성되고, 함수가 끝나면 소멸함

멤버변수(인스턴스 변수)

선언 위치 = 클래스 멤버 변수로 선언


사용 범위 = 클래스 내부에서 사용하고 private이 아니면 참조 변수로 다른 클래스에서 사용 가능
메모리 = 힙
생성과 소멸 = 인스턴스가 생성될 때 힙에 생성되고, 가비지 컬렉터가 메모리를 수거할 때 소멸됨
new할 때 사용 되던거! class 내부에서 class속성을 나타내는데 사용 했었음

static 변수(클래스 변수)

선언 위치 = static 예약어를 사용하여 클래스 내부에 선언


사용 범위 = 클래스 내부에서 사용하고 private이 아니면 클래스 이름으로 다른 클래스에서 사용 가능
메모리 = 데이터 영역
생성과 소멸 = 프로그램이 처음 시작할 때 상수와 함께 데이터 영역에 생성되고 프로그램이 끝나고 메모리를 해제할 때 소멸됨 ( 따라서 너무 크게 만들면 로딩 커진다~)
class 이름으로 사용하던 애들
변수 유형선언 위치사용 범위메모리생성과 소멸
지역
변수
(로컬 변수)
함수 내부에
선언
함수 내부에서만 사용스택함수가 호출될 때
생성되고,함수가
끝나면 소멸함
멤버
변수
(인스턴스
변수)
클래스 멤버
변수로 선언
클래스 내부에서 사용하고
private이 아니면 참조
변수로 다른 클래스에서 사용 가능
인스턴스가 생성될 때 힙에 생성되고,
가비지 컬렉터가 메모리를 수거할 때
소멸됨
static 변수
(클래스 변수)
static 예약어를 사용하여 클래스 내부에 선언클래스 내부에서 사용하고 private이 아니면 클래스 이름으로 다른 클래스에서 사용 가능데이터 영역프로그램이 처음 시작할 때 상수와 함께 데이터
영역에 생성되고
프로그램이 끝나고 메모리를 해제할 때 소멸됨

static 응용: singleton 패턴

  • 전 시스템에 단 하나의 인스턴스만이 존재하도록 구현하는 방식이다.
  • 자바에는 글로벌 변수가 없으므로 static 변수를 사용한다.
  • 생성자를 private으로 만들고 public으로 선언된 static 메서드를 제공하여 외부에서 사용할 수 있도록 한다.
  • 여러 개의 객체가 생성되면 문제가 되는 경우나, framework에서 많이 쓰인다.

Company class

public class Company {
	
	private static Company instance = new Company();
    //ㄴ> Company 객체는 현재 클래스 내부에서 단 한 개만 존재함.
	
	private Company(){}//외부에서 constructor 호출을 제한
	
	public static Company getInstance() { //외부에서 instance 객체를 사용할 수 있게 함
		if(instance == null) { //<< 매우 방어적인 코드
        //null일리가 없지만 혹시 모르니 조치를 한번 더 취한거
			instance = new Company();
		}
		return instance;
	}
}

이처럼 Company instance가 함부로 바뀌거나 null이면 안되니 private으로 생성자를 만들고, 유일하게 사용하려는 instence기 때문에 static으로 선언한 후, get 메서드를 제공하여 객체를 사용할 수 있도록 만든다.

CompanyTest class

public class CompanyTest {
	public static void main(String[] args) {
		Company c1 = Company.getInstance();
		Company c2 = new Company();	//error
	}
}

main함수가 있는 다른 class에서 Company객체를 사용하고 싶다면 new 생성자가 아닌 getInstance() 메서드를 이용해야 사용할 수 있다.

0개의 댓글