본문 바로가기
대학강의정리/24.1 자바

ch4. 객체지향

by 피스타0204 2024. 4. 9.

1. 객체 지향의 개요

1)객체의 개념

객체는 상태와 동작으로 이루어져 있고 상태는 필드, 동작은 메서드로 구현합니다.

 

2) 절차지향과 객체 지향

절차지향 프로그래밍(procedural programming, imperative preogramming)은 일련의 동작을 순서에 맞추어 단계적으로 실행하도록 명령어를 나열한 것을 말합니다. 직관적이지만 순서와 흐름에 중점을 맞추어 소프트웨어의 규모가 커지면 유지 보수가 어렵습니다. 

객체 지향 프로그래밍은 각각의 객체를 조합해 프로그래밍할 수 있기 때문에 대규모 프로그래밍에 알맞습니다.

 

3) 객체와 클래스

클래스는 객체를 찍어낼 수 있는 설계도, 탬플릿을 말합니다. 붕어빵 틀로 비유되기도 하는데 이것으로 찍어내는 붕어빵, 객체는 서로 다른 객체입니다.

객체를 인스턴스라고 하기도 합니다. 인스턴스는 붕어빵A란는 특정 객체를 말하기 보다 두루뭉실하게 클래스가 찍어내는 붕어빵을 말할 때씁니다. 좀 더 추상적인 표현입니다.

 

2. 객체 지향 프로그래밍의 특징

1) 캡슐화(encapsulation; 정보 은닉: infromation hiding)

캠슐화는 관련된 데이터 필드와 메서드를 하나의 캡슐로 포장해 세부 내용을 외부에서 알수 없도록 감추는 것입니다. 캡슐화의 주목적은 내부 데이터를 숨겨 외부의 영향을 덜 받게 하는 것입니다. 

 

2) 상속(inheritance)

상속이란 상위 객체를 상속받은 하위 객체가 상위 객체의 메서드와 필드를 사용하는 것을 말합니다. 하위 객체가 더 많은 정보량을 담으면서 상위 객체를 재사용할 수 있습니다.

상위 클래스를 parent class, super class, base class라고 하며 하위 객체 클래스를 child class, subclass, derived class, extended class라고 합니다.

 

3) 다형성(polymorphism)

대입이 되는 객체에 따라 메서드를 다르게 동작하도록 구현하는 기술입니다.

move()가 새는 fly, 개는 run, fish는 swim이라 다르지만 상위 클래스에서 동일하게 move가 전달되지만 동물 객체마다 다르게 반응할 수 있도록 하는 것이 다형성입니다.

 

4) 추상화(abstraction)

클래스에 모든 것을 포함하기는 어렵기 때문에 불필요한 것을 빼고 추상화할 필요가 있습니다. 이러한 모델링 기법은 개발자마다 조금씩 다릅니다.

 

3. 자바에서 클래스의 선언과 객체 생성

1) 클래스의 선언

자바에서는 모든 것을 객체화하려고 하기 때문에 클래스 이름은 반드시 파일이름과 동일하게 작성해야 합니다. Ball.java파일을 컴파일하면 Ball.class 파일이 생성됩니다. 아래의 Ball.java는 main() 메서드가 없기 때문에 단독으로 실행할 수 없고 Ball 객체를 생성하는 데에만 사용합니다.

public class Ball{
	double radius = 2.0; //필드
    double getVolumn(){ //메서드
    	return 4 / 3* 3.14*radius*radius*radius;
    }
}

 1.2) 클래스의 선언과 파일

보통 소스 파일마다 하나의 클래스를 선언하지만 2개이상의 클래스를 하나의 파일로 선언할 수 있습니다. 하지만 하나의 파일에 클래스가 2개이상 있으면 하나만 public선언할 있습니다.(해당 클래스의 이름은 소스 파일과 동일해야 합니다.)

보통 하나의 .java 파일에는 하나의 클래스가 있고 여러개의 .java 파일을 패키지로 묶어서 사용합니다.

 

2) 객체의 생성과 참조 변수

객체를 클래스로부터 만들기 위해서 new를 사용합니다. 아래와 같이 참조변수를 사용하는 것이 기본이고 클래스를 한번만 사용한다면 참조 변수 없이 사용할 수도 있습니다.

클래스이름 변수; //참조 변수
변수 = new 클래스이름(); //객체 생성
//참조변수 선언과 동시에 객체 생성 및 초기화
클래스이름 변수 = new  클래스이름();

 

참조 변수를 사용하지 않는 경우 ▼

//매개변수로 객체를 전달할 때 참조 변수를 사용하지 않을 때도 있음.
Greeting.greet(new Person("김수룡"))

 

2.2) 기초 타입과 참조 변수

참조 변수는 객체를 가리키는 포인터(메모리 주소 저장)입니다. 그렇기 때문에 참조변수A = 참조변수B이면 값이 복사되어 저장되는 것이 아니라 가리키고 있는 참조값이 변화합니다. 참조변수 A는 참조변수 B와 동일한 객체B를 가리키게 됩니다.

또, 객체는 참조 변수가 없다면 다시 사용할 수 없기 때문에 참조변수 A가 가리키던 객체A는 JVM의 가비지 콜렉터에 의해 삭제되게 됩니다.

package main;

class Phone {
	String model; //필드
	int value; // 필드

	void print() { //메서드
		String currency = "만원"; // 지역 변수
		System.out.println(value + currency +  " 짜리 " + model + " 스마트폰");
	}
}

public class Main {
	public static void main(String[] args) {
		Phone myPhone = new Phone();
		myPhone.model = "갤럭시 S23";
		myPhone.value = 100;
		myPhone.print();

		Phone yourPhone = new Phone();
		yourPhone.model = "아이폰 14";
		yourPhone.value = 120;
		yourPhone.print();
		
		myPhone = yourPhone;
		myPhone.print();
	}
}

//100만원 짜리 갤럭시 S23 스마트폰
//120만원 짜리 아이폰 14 스마트폰
//120만원 짜리 아이폰 14 스마트폰

 

4. 클래스의 구성요소와 멤버 접근

1.1) 필드와 지역변수

클래스는 필드와 메서드로 구성된다. 멤버 변수와 멤버 메서드라고 하고 둘이 합쳐서 클래스의 멤버라고 합니다. 이 외에 객체를 생성하는 생성자(constructor)오 클래스의 구성요소이며 new 연산자로 호출하는 특수한 메서드 입니다.

 

1.2)지역변수와 필드의 차이

지역변수는 메서드 내에 선언된 변수를 말하고 매개변수도 일종의 지역변수 입니다.

필드는 기본값이 있어서 반드시 초기화할 필요없지만 지역변수는 반드시 초기화해주어야 합니다.

필드 자동 초기화 기본값

또, 필드는 클래스 전체에서 사용될 수 있지만 지역변수는 선언된 블록 내부이고 선언된 이후에만 선언 가능합니다.

필드는 static으로 광범위하게 사용할 수 있지만 지역변수는 불가능합니다. final은 둘 모두에서 사용됩니다.

지역변수는 public을 사용할 수 없습니다.

package main;

public class Main {
	int ddddd; //클래스 멤버 변수이기 때문에 오류x
	public static void main(String[] args) {
		int a = 0;
		double b;

		// System.out.print(b); // 메서드 안의 지역변수 이므로 초기화해야됨
		// System.out.print(a + c); // c를 초기화 해야됨

		int c = 0;

		// public double d = 0.0; // 지역 변수는 public x

		for (int e = 0; e < 10; e++) {
			// int a = 1; // 이미 선언됨
			System.out.print(e);
		}
	}
}

 

2) 필드와 메서드 접근

메서드와 필드는 객체가 없다면 접근할 수 없습니다. 먼저 객체를 생성하고 멤버접근연산자.을 이용해 접근합니다.

myCircle.radius // 필드 접근

myCircle.findArea() // 메서드 접근

package main;
//메인 클래스
public class Main {
	public static void main(String[] args) {
		Circle myCircle = new Circle();
		myCircle.radius = 10.0;
		myCircle.show(myCircle.radius, myCircle.findArea());
	}
}//메인 클래스 끝

class Circle {
	double radius;
	double findArea() {
		return 3.14 * radius * radius;
	}

	void show(double x, double y) {
		System.out.printf("반지름 = %.1f, 넓이 = %.1f\n", x, y);
	}
}
//반지름 = 10.0, 넓이 = 314.0

클래스 내부에서 자신의 멤버에 접근하려면 참조변수 this나 멤버이름을 사용합니다.

this.radius 나 radius 사용

this.findArea 나 findArea() 사용

 

5. 접근자(accessors, getters)와 설정자(mutators, setters)

접근자와 설정자는 클래스 내부에 캡슐화된 멤버(private한)를 외부에서 사용해야 할 때 필요합니다. private으로 보호되는 필드를 읽어(read)주는 getter와 값을 변경(write)해주는 accessor이 있습니다.

접근자와 설정자는 함수이기 때문에 오버헤드가 발생할 수 있습니다. 하지만 필드를 외부와 차단할 수 있고 데이터도 쉽게 겁증할 수 있습니다.

package main;

public class Main {
	public static void main(String[] args) {
		Circle myCircle = new Circle();

		myCircle.setRadius(10.0);
		//double fff = myCircle.radius;
        //public으로 바꾸거나 get으로 가져와야 한다.
		myCircle.show(myCircle.getRadius(), myCircle.findArea());
	}
}

class Circle {
	private double radius; 

	public double getRadius() {
		return radius;
	}

	public void setRadius(double r) {
		if(r >=0)
			this.radius = r;
	}
// 자바에서 default는 package-private이라고 해서 같은 피키니 내부의 클래스에게만 엑서스를 허용한다.
	double findArea() {
		return 3.14 * radius * radius;
	}

	void show(double x, double y) {
		System.out.printf("반지름 = %.1f, 넓이 = %.1f\n", x, y);
	}
}
//반지름 = 10.0, 넓이 = 314.0

 

6. 생성자

1) 생성자의 의미와 선언

객체를 생성해주며 동시에 필드를 초기화해주는 역할을 합니다. 생성자는 메서드와 비슷하지만 이름이 클래스 이름과 같고, 반환 타입이 없습니다. 

//생성자 선언
클래스이름(..){...}

//생성자 사용
클래스이름 변수 = new 클래스이름(...);

 

2) 디폴트 생성자

모든 클래스는 최소한 하나의 생성자를 가집니다. 클래스 안에 생성자를 직접 선언하지 않아도 컴파일러가 자동으로 default constructor를 추가합니다. 디폴트 생성자는 매개변수도 없고 본체에서 실행할 수도 없습니다. 또, 클래스에 생성자가 하나라도 있다면 디폴트 생성자를 자동으로 추가하지 않습니다.

또 생성자는 반드시 public으로 작성해야 합니다.

package main;

public class Main {
	public static void main(String[] args) {
		//Circle myCircle = new Circle();  안됨
        Circle myCircle = new Circle(10);
	}
}

class Circle {
	public double radius; 
	public Circle(double r) {
		radius=r;
	}

}

 

2.2) 생성자 오버로딩

생성자는 오버로딩 할 수 있습니다.

package main;

class Circle {
	double radius;
	String color;

	public Circle(double r, String c) {
		radius = r;
		color = c;
	}

	public Circle(double r) {
		radius = r;
		color = "파랑";
	}

	public Circle(String c) {
		radius = 10.0;
		color = c;
	}

	public Circle() {
		radius = 10.0;
		color = "빨강";
	}
}

public class Main {
	public static void main(String[] args) {
		Circle c1 = new Circle(10.0, "빨강");
		Circle c2 = new Circle(5.0);
		Circle c3 = new Circle("노랑");
		Circle c4 = new Circle();
	}
}

 

3) this

매개변수 이름과 멤버필드 이름이 동일하다면 this를 사용해서 구분합니다.

오른쪽이 개발자 입장에서 편한 코드이다.

3.2) this()

동일 클래스에서 다른 생성자를 호출할 때 사용합니다. 생성자들끼리 조합해 사용하고 싶을 때 씁니다. this()를 사용할 때에는 반드시 생성자의 첫번째 줄에서만 사용할 수 있습니다. 앞에 다른 변수를 초기화하는 코드를 쓴다든가 하면 제대로 기동하지 않습니다.

package main;

class Circle {
	double radius;
	String color;

	public Circle(double radius, String c) {
		this.radius= radius;
		color =c;
	}

	public Circle(double r) {
		radius = r;
		color = "파랑";
	}

	public Circle(String c) {
		this(10.0,c);
	}

	public Circle() {
		this(10.0, "빨강");
	}
}

public class Main {
	public static void main(String[] args) {
		Circle c1 = new Circle(10.0, "빨강");
		Circle c2 = new Circle(5.0);
		Circle c3 = new Circle("노랑");
		Circle c4 = new Circle();
	}
}

 

4. 연속호출(method chaining)

package main;

public class Main {
	public static void main(String[] args) {
		Person person = new Person();
		person.setName("민국").setAge(21).sayHello();
	}
}

class Person {
	String name;
	int age;

	public Person setName(String name) {
		this.name = name;
		return this;
	}

	public Person setAge(int age) {
		this.age = age;
		return this;
	}

	public void sayHello() {
		System.out.println("안녕, 나는 " + name + "이고 " + age + "살이야.");
	}
}
//안녕, 나는 민국이고 21살이야.

07. 정적 멤버

1) 인스턴스 멤버와 정적 멤버

static 키워드로 클래스의 필드를 각각의 객체들에게 공유할 수 있게 만들 수 있습니다. 이러한 변수를 정적 변수(static variable, class variable)이라고 하고 일반 멤버 변수를 인스턴스 변수라고 합니다.

정적 변수와 정적 함수는 객체를 생성하기 전에도 접근할 수 있습니다. 하지만 정적 메서드는 객체와 관련된 인스턴스 변수, 인스턴스 함수,this키워드를 사용할 수 없습니다. 인스턴스 변수, 함수를 사용하려면 객체를 생성한 후에 접근해야 합니다. 정적 메서드는 정적 변수와 지역변수를 사용할 수 있고 다른 정적 변수나 생성자를 호출할 수 있습니다. 

2) 정적 멤버의 활용

정적 멤버는 객체의 개수를 세거나 값이 영원히 변하지 않는 상수 (final키워드 사용)에 쓰입니다. main메서드도 정적 메서드입니다.

//fourtimes 호출 예제

 

3) 정적 블록

블록 단위로 static으로 만드는 것을 만합니다.

//sumOneToten 예제