Language/Java

[Java] 클래스와 객체

sssbin 2022. 2. 11. 23:00

 

객체 지향

- 컴퓨터가 수행하는 작업을 객체들 간의 상호작용으로 표현

- 클래스 혹은 객체들의 집합으로 프로그램 작성

 

캡슐화

- 객체를 캡슐로 싸서 그 내부를 보호하고 볼 수 없게 하는 것

 

상속

- 상위 개체의 속성을 하위 개체에 물려줌.

 

다형성

- 같은 이름의 메소드가 클래스나 객체에 따라 다르게 구현되는 것

- 메소드 오버로딩: 한 클래스 내에서 같은 이름이지만 다르게 작동하는 여러 메소드

- 메소드 오버라이딩: 슈퍼클래스의 메소드를 동일한 이름으로 서브 클래스마다 다르게 구현

 

클래스와 객체

- 클래스: 객체의 속성과 행위 선언. 객체를 만들어내기 위한 틀

- 객체를 클래스의 인스턴스라고 함.

- 객체들은 클래스에 선언된 동일한 속성을 가지지만 속성 값은 서로 다르다.

 


자바 클래스 만들기

클래스 구성

1. 클래스 구성

- class 키워드로 클래스 선언

2. 클래스 멤버

- 클래스의 구성 요소. 필드와 메소드

- 필드: 객체의 상태 값을 저장할 멤버 변수

- 메소드: 실행 가능한 함수이고 객체의 행위를 구현

3. 접근 지정자

- public: 다른 클래스에서 호출, 접근 가능하도록 공개

자바 클래스 선언과 구성

 

객체 생성과 활용

// 반지름과 이름을 가진 Circle 클래스를 작성하고,
// Circle 클래스의 객체 생성하기

public class Circle {
	int radius;		// 원의 반지름 필드
	String name;	// 원의 이름 필드 
	
	public double getArea() {	// 멤버 메소드 
		return 3.14*radius*radius;
	}
	
	public static void main(String[] args) {
		Circle pizza;	// 객체에 대한 레퍼런스 변수 pizza 선언
		pizza = new Circle();	// Circle 객체 생성 
		pizza.radius = 10;	// 피자의 반지름을 10으로 설정
		pizza.name = "자바피자";	// 피자의 이름 설정
		double area = pizza.getArea();	// 피자의 면적 알아내기
		System.out.println(pizza.name + "의 면적은 " + area);
		
		Circle donut = new Circle();	// Circle 객체 생성 
		donut.radius = 2;	// 도넛의 반지름을 2로 설정
		donut.name = "자바도넛";	// 도넛의 이름 설정
		area = donut.getArea();	 // 도넛 면적 알아내기
		System.out.println(donut.name + "의 면적은 " + area);
	}
}

pizza와 donut 객체

1. 레퍼런스 변수 선언

Circle pizza;
// 레퍼런스 변수 pizza 선언

2. 객체 생성

pizza = new Circle();
// new 연산자를 사용하여 Circle 객체 생성

3. 객체 멤버 접근

객체레퍼런스.멤버

pizza.radius = 10; // pizza 객체의 radius 값을 10으로 설정
pizza.name = "자바피자"; // pizza 객체의 name에 "자바피자" 대입
double area = pizza.getArea(); // pizza 객체의 getArea() 메소드 호출

생성자

- 객체가 생성될 때 초기화 목적으로 실행되는 메소드

- 객체가 생성되는 순간 자동 호출 (리턴값 X)

package test1;

public class Circle2 {
	int radius;
	String name;
	
	public Circle2() {	// 매개변수 없는 생성자 
		radius = 1; name= "";	// radius의 초기값은 1 
	}
	
	public Circle2(int r, String n) {	// 매개변수를 가진 생성자 
		radius = r; name = n;	// radius와 name 필드 초기화 
	}
	
	public double getArea() {
		return 3.14 * radius * radius;
	}
	
	public static void main(String[] args) {
		Circle2 pizza = new Circle2(10, "자바피자");	// Circle2 객체 생성, 반지름 10
		double area = pizza.getArea();
		System.out.println(pizza.name + "의 면적은 " + area);
		
		Circle2 donut = new Circle2();	// Circle2 객체 생성, 반지름 1 
		donut.name = "도넛피자";
		area = donut.getArea();
		System.out.println(donut.name + "의 면적은 " + area);
	}
}

- 생성자 이름은 클래스 이름과 동일하다.

- 생성자를 여러 개 작성할 수 있다.

- 생성자는 객체를 생성할 때 한 번만 호출된다.

- 생성자에 리턴 타입을 지정할 수 없다.

- 생성자의 목적은 객체가 생성될 때, 필요한 초기 작업을 위함이다.

 

기본 생성자

class Circle {
	public Circle() { }	// 기본 생성자
}

- 매개 변수가 없고 실행 코드가 없어 아무 일도 하지 않고 단순 리턴하는 생성자

- 디폴트 생성자라고도 부른다.

- 클래스에 생성자가 하나도 선언되어 있지 않은 경우, 컴파일러는 기본 생성자를 자동으로 생성한다.

 

this 레퍼런스

- 객체 자신에 대한 레퍼런스로서 메소드 안에서 사용된다.

- 현재 객체를 가리킨다.

- static 메소드에서는 사용할 수 없다.

class Circle {
	int radius;
	public Circle() { this.radius = 1; }
	public Circle(int radius) { this.radius = radius; }
	public double getArea() { return 3.14*this.radius*this.radius; }
	...
}

 

this()

- 클래스 내에서 생성자가 다른 생성자를 호출할 때 사용

- 생성자에서만 사용된다.

- 반드시 같은 클래스의 다른 생성자를 호출할 때 사용된다.

- 반드시 생성자의 첫 번째 문장으로 사용되어야 한다.

public class Book {
	String title;
	String author;
	
	void show() { System.out.println(title + " " + author); }
	
	public Book() {
		this("", "");
		System.out.println("생성자 호출됨");
	}
	
	public Book(String title) {
		this(title, "작자미상");
	}
	
	public Book(String title, String author) {
		this.title = title;
		this.author = author;
	}
	
	public static void main(String[] args) {
		Book littePrince = new Book("어린왕자", "생텍쥐페리");
		Book loveStory = new Book("춘향전");
		Book emptyBook = new Book();
		loveStory.show();
	}
}

실행결과

 


객체 배열

- 객체에 대한 레퍼런스를 원소로 갖는 배열

// 5개의 Circle 객체 배열을 생성하고 활용하는 코드 

Circle [] c;	// Circle 배열에 대한 레퍼런스 변수 c 선언 
c = new Circle[5];	// 레퍼런스 배열 생성 

for (int i=0; i<c.length; i++)
	c[i] = new Circle(i);	// 각 원소 객체 생성 

for (int i=0; i<c.length; i++)
	System.out.print((int)(c[i].getArea()) + " ");	// 배열의 원소 객체 사용

Circle 객체 배열의 생성 과정

 

배열 선언 및 생성

1. 배열에 대한 레퍼런스 선언

- 레퍼런스 변수만 선언할 뿐 배열을 생성하는 것은 아니기 때문에 배열의 개수를 지정하면 안 된다.

Circle [] c;

2. 레퍼런스 배열 생성

c = new Circle[5];	// Circle 객체에 대한 레퍼런스 5개 생성

3. 객체 생성

- 다음 코드는 Circle 객체를 하나씩 생성하여 배열 c[]의 각 레퍼런스에 대입한다.

for (int i=0; i<c.length; i++)
	c[i] = new Circle(i);

 

배열의 원소 객체 접근

- 다음과 같이 배열 c의 i번째 객체를 접근하기 위해서는 c[i] 레퍼런스를 사용한다.

for (int i=0; i<c.length; i++)
	System.out.print((int)(c[i].getArea()) + " ");

 


메소드 활용과 객체 치환

메소드 형식

자바의 메소드 형식

1. 접근 지정자

- public: 클래스 내부/외부에서 모두 호출 가능

- private: 클래스 멤버들만 호출 가능

- protected: 동일한 패키지의 클래스들과 상속받은 서브 클래스에서 호출 가능

- 디폴트 접근 지정(생략): 동일한 패키지 내의 모든 클래스에서 호출 가능

2.  리턴 타입

- 메소드가 호출자에게 리턴할 값의 타입

 

인자 전달

- 값에 의한 호출 (call-by-value)

- 호출하는 실인자의 값이 복사되어 메소드의 매개 변수에 전달된다.

1. 기본 타입의 값이 전달되는 경우

- 호출자가 건네는 값이 메소드의 매개 변수에 복사되어 전달

public class CallbyValue {
	public static void main(String[] args) {
		int n = 10;
		
		increase(n);	// increase 메소드 호출
		
		System.out.println(n);	// increase()가 끝나고 지역 변수 n의 값은 여전히 10
	}
	
    	// 실행될 때, 매개 변수 m이 생기고 n의 값을 전달받아 10으로 초기화
	static void increase(int m) {	
		m = m + 1;	// m의 값을 1 증가시키지만, 변수 n의 값을 바꾸지는 못한다.
	}
}

2. 객체가 전달되는 경우

- 객체의 레퍼런스 값이 전달된다.

public class ReferencePassing {
	public static void main(String[] args) {
		Circle pizza = new Circle(10);
		
		increase(pizza);	// increase 메소드 호출
		
		System.out.println(pizza.radius);	// 11 출력
	}
	
    	// 매개변수 m이 생기고, pizza 변수에 저장된 값(레퍼런스)이 m에 복사되어, 
    	// m은 pizza가 가리키는 객체를 함께 가리키게 된다.
	static void increase(Circle m) {	
		m.radius++; // radius는 11로 변경되고 increase()가 종료해도 그대로 남는다.
	}
}

3. 배열이 전달되는 경우

- 배열에 대한 레퍼런스만 전달된다.

public class ArrayPassing {

	public static void main(String[] args) {
		int a[] = {1, 2, 3, 4, 5};
		
		increase(a);
		
		for (int i=0; i<a.length; i++)
			System.out.print(a[i] + " ");
	}
	
	// 매개변수 array는 정수형 배열에 대한 레퍼런스로서
	// 배열 a를 가리키게 되어 두 레퍼런스는 하나의 배열을 공유한다. 
	static void increase(int[] array) {
		for (int i=0; i<array.length; i++)
			array[i]++;
	}

}

 

메소드 오버로딩

- 클래스 내에 이름이 같지만 매개 변수의 타입이나 개수가 서로 다른 여러 개의 메소드

- 메소드의 리턴 타입은 상관없음.

public static void main(String args[]) {
	MethodSample a = new MethodSample();
	
	int i = a.getSum(1, 2);
	
	int j = a.getSum(1, 2, 3);
	
	double k = a.getSum(1.1, 2.2);
}

public class MethodSample {
	public int getSum(int i, int j) {
		return i + j;
	}
	
	public int getSum(int i, int j, int k) {
		return i + j + k;
	}
	
	public double getSum(double i, double j) {
		return i + j;
	}
}

 

객체 치환 시 주의할 점

- 객체의 치환은 객체를 복사하는 것이 아니다.

public class Samp {
	int id;
	public Samp(int x) {this.id = x;}
	public void set(int x) {this.id = x;}
	public int get() {return this.id;}
	
	public static void main(String[] args) {
		Samp ob1 = new Samp(3);
		Samp ob2 = new Samp(4);
		Samp s;
		
		s = ob2; 
        	// 객체의 치환. s는 ob2의 객체를 가리킴 
		ob1 = ob2; 
        	// 객체의 치환. ob1은 ob2의 객체를 가리키게 됨 
        	// 원래 ob1이 가리키던 객체는 아무것도 가리키지 않게 되어,
        	// 더이상 프로그램에서 접근할 수 없는 상태가 된다. => 가비지
		System.out.println("ob1.id="+ob1.get());
		System.out.println("ob2.id="+ob2.get());
	}
}

 


가비지

- 참조하는 레퍼런스가 하나도 없는 객체나 배열

- 프로그램에서 더이상 접근할 수 없다.

 

가비지 컬렉션

- 가비지가 많아지면 응용 프로그램에게 할당해줄 수 있는 가용 메모리의 양이 줄어든다.

- 가비지가 점점 늘어나 가용 메모리가 0이 되면, 자바 응용프로그램은 더이상 실행할 수 없다.

- 가용 메모리 공간이 일정 크기 이하로 줄어들면, 자바 가상 기계는 자동으로 가비지를 회수하여 가용 메모리 공간을 늘린다.

 

가비지 컬렉션 강제 수행

- System 또는 Runtime 객체의 gc() 메소드 호출

Systme.gc()	// 가비지 컬렉션 자동 요청

 


접근 지정자

패키지

- 자바에서는 상호 관련 있는 클래스 파일들을 패키지에 저장하여 관리한다.

- 자바 프로그램은 하나 이상의 패키지로 구성된다.

 

접근 지정자

- private (외부로부터 완벽 차단)

- 디폴트 (동일 패키지 허용)

- protected (동일 패키지 + 자식 클래스 허용)

- public (모든 클래스)

 


static 멤버

특성

- 클래스당 하나만 생성되는 멤버

- 동일한 클래스의 모든 객체들이 공유

- 클래스 멤버라고 부름. (non-static 멤버는 인스턴스 멤버)

- 객체가 생성하기 전, 소멸된 후에도 static 멤버는 공간을 차지하고 있다. (외부에 별도로 존재)

 

사용

- 객체 이름이나 클래스 이름으로 모두 활용할 수 있다.

StaticSample.m = 3; // 클래스 이름으로 static 필드 접근
StaticSample.f(); // 클래스 이름으로 static 메소드 호출

 

활용

- 전역 변수와 전역 함수를 만들 때 활용

- 공유 멤버를 만들고자 할 때 활용

 

제약 조건

- static 메소드는 오직 static 멤버만 접근할 수 있다.

- static 메소드에서는 this를 사용할 수 없다.

 


final

final 클래스

- 더이상 클래스 상속 불가능

 

final 메소드

- 더이상 오버라이딩 불가능

 

final 필드

- 상수 선언

- 초기값 지정. 실행중 값 변경 X