1. 오리엔테이션
객체 지향 프로그래밍(Object-Oriented Programming)
- 객체란?
프로그램을 구성하는 로직을 상태와 행위로 그룹화 한 것
※ 앞의 6개 챕터는 절차 지향 프로그래밍이라고 함
- 절차 지향 : 개체를 순차적으로 처리하는 방식 (선형적)
- 객체 지향 : 개체를 캡슐화하여 상호작용하여 처리하는 방식 (구조적)
- 객체 지향 프로그래밍 컨셉
문법 : if나 for문처럼 객체를 만드는 방법
설계 : 현실의 추상화, 로직의 부품화, 부품들의 은닉화(캡슐화), 부품들간의 연결점 표준화, 로직의 상속성, 로직들의 다형성(오버로딩)
2. 클래스와 인스턴스 그리고 객체
1) 클래스와 인스턴스
- 클래스 : 설계도, 메소드의 정의
코딩의 소양은 중복의 제거, 로직이 중복된다면 메소드를 사용.
public class _01_Class {
// public static void main(String[] args) {
// System.out.println(10 + 20);
// System.out.println(20 + 40);
// }
// refectoring : 기존의 코드와 같게 동작하지만 코드의 내용을 개선하여 효율적으로 만든 행위
public static void sum(int left, int right) {
System.out.println(left + right);
}
public static void main(String[] args) {
sum(10, 20);
sum(20, 40);
}
}
※ 클래스는 한 파일 안에 여러개를 정의할 수 있지만 대표 클래스는 앞에 'public'이라는 접근 제어자를 붙여야 하며 파일명과 같아야 함
※ static = static + heap, 전자는 메모리를 공유하고 클래스가 할당, 후자는 메모리를 공유하지 않으며 객체(인스턴스)가 할당함
class Calculator { // Calculator 클래스 정의, 설계
int left, right;
public void setOprends(int left, int right) {
this.left = left; // this. 객체 속성값 입력 시 사용
this.right = right;
}
public void sum() { // 덧셈 메소드 정의, static 이 없음 > 객체 선언이 필요함
System.out.println(this.left + this.right); // static 은 각 정의들이 같은 메모리를 공유하여 객체 선언 필요 없음
}
public void avg() { // 평균 메소드 정의
System.out.println((this.left + this.right) / 2);
}
}
public class _02_Object2 { // public class 는 파일명과 같아야 함. 즉, 대표 클래스
public static void main(String[] args) {
Calculator c1 = new Calculator(); // new 이후가 객체, c1 변수에 담음, 담긴 것들을 인스턴스
c1.setOprends(10, 20);
c1.sum();
c1.avg();
Calculator c2 = new Calculator();
c2.setOprends(20, 40);
c2.sum();
c2.avg();
}
}
※ 객체 : 변수와 메소드의 집합, 상기 빌드의 class Calculator 하위 영역에 해당함
- 인스턴스 : 제품, 메소드의 호출
c1은 new를 통해 인스턴스를 생성하고 담는 변수
- Calculator : 설계도 (클래스)
c1 : 제품 (인스턴스)
left 10, right20 : 상태 (변수)
sum(), avg() : 행위 (메소드)
- 즉, 상태와 행위로 이루어진 객체로 만드는 것이 객체지향 프로그램
2) 객체
- Calculator + c1 : 데이터타입 + 변수(컨테이너, 인스턴스)
변수에 테이터타입에 맞추어 정보를 입력
- String[] args : 문자열 데이터 컨테이너
입력된 정보는 메모리 내에서 그 정보를 위한 자리가 마련되어있다.
- sum(), avg() : 컨테이너 안에 변수를 입력하면 결과값을 만들어 낼 메소드
즉, 수학에서 미지수 x, y 자리를 main영역을 정의할 때 마련하고 그 하위에는 변수를 정의해서 어떤 계산식을 가져올 것인지 인스턴스를 통해 클래스 안의 메소드를 불러와서 미리 정의 된 입력 형식에 맞추어 입력, 그리고 실행을 하면 계산 된 결과값을 출력 함
3. 클래스 맴버와 인스턴스 맴버
1) 맴버
- 객체의 구성원 : 변수, 메소드
- 클래스의 구성원 : 변수, 메소드
- 인스턴스의 구성원 : 변수, 메소드
2) 각 그룹의 구성원 정의
- 클래스 변수 : 클래스에서 정의한 변수로 하위의 인스턴스는 클래스 변수 정보도 가지게 됨
class Calculator { // Calculator 클래스 정의, 설계
static double PI = 3.14;
// 클래스 변수인 base가 추가
static int base = 0;
int left, right;
public void setOprends(int left, int right) {
this.left = left; // this. 객체 속성값 입력 시 사용
this.right = right;
}
public void sum() { // 덧셈 메소드 정의, static 이 없음 > 객체 선언이 필요함
// 더하기에 base 의 값을 포함
System.out.println(this.left + this.right + base); // static 은 각 정의들이 같은 메모리를 공유하여 객체 선언 필요 없음
}
public void avg() { // 평균 메소드 정의
// 평균치에 base 의 값을 포함시킨다.
System.out.println((this.left + this.right + base) / 2);
}
}
여기에 base의 변수를 다시 정의하면
public class _02_Object2 { // public class 는 파일명과 같아야 함. 즉, 대표 클래스
public static void main(String[] args) {
Calculator c1 = new Calculator();// new 이후가 객체, c1 변수에 담음, 담긴 것들을 인스턴스
c1.setOprends(10, 20);
c1.sum();
c1.avg();
System.out.println(c1.PI); // 클래스에 인스턴스를 생성할 필요가 없는 값을 정의 : 클래스 맴버인 변수
Calculator.base = 10;
c1.sum(); // 40
Calculator c2 = new Calculator();
c2.setOprends(20, 40);
c2.sum(); // 70 (c1에서 정의된 10)
c2.avg(); // 35 (c1에서 정의된 10)
System.out.println(c2.PI);
Calculator.base = 20;
c2.sum(); // 80 (c2에서 base 를 20으로 변경, 20 + 40 + 20)
}
}
- 클래스 메소드 : 클래스에서 정의되는 메소드로 인스턴스 메소드에 접근 불가, 반대 방향은 접근 가능
class Calculator3 {
public static void sum (int left, int right) { // static 이 붙으면 직접적으로 불러올 수 있음(class.meth ())
System.out.println(left + right); // static 이 없으면 인스턴스를 지정하여 불러옴(c1, c2)
}
public static void avg (int left, int right) {
System.out.println((left + right) / 2);
}
}
public class _04_Object4 {
public static void main(String[] args) {
Calculator3.sum (10, 20);
Calculator3.avg(10, 20);
Calculator3.sum (20, 40);
Calculator3.avg(20, 40);
}
}
// 인스턴스를 사용하는 이유는 인스턴스가 다른 인스턴스와 구분된 상태를 가지고 있어야하기 때문에 사용
// 굳이 할 필요 없으면 static 을 사용
- 인스턴스 변수 : 인스턴스마다 다른 값을 가지는 변수, 정의된 클래스에는 영향을 주지 않음
class C1 {
static int static_variable = 1;
int instance_variable = 2;
static void static_static() {
System.out.println(static_variable);
}
static void static_instance() {
// 클래스 메소드에서는 인스턴스 변수에 접근 할 수 없다.
// System.out.println(instance_variable);
}
void instance_static() {
// 인스턴스 메소드에서는 클래스 변수에 접근할 수 있다.
System.out.println(static_variable);
}
void instance_instance() {
System.out.println(instance_variable);
}
}
public class _05_Member2 {
public static void main(String[] args) {
C1 c = new C1();
// 인스턴스를 이용해서 정적 메소드에 접근 -> 성공
// 인스턴스 메소드가 정적 변수에 접근 -> 성공
c.static_static(); // 1, 인스턴스로 클래스 접근 가능
// 인스턴스를 이용해서 정적 메소드에 접근 -> 성공
// 정적 메소드가 인스턴스 변수에 접근 -> 실패
c.static_instance(); // 에러 : static 메소드에서 비 static 접근 불가
// 인스턴스를 이용해서 인스턴스 메소드에 접근 -> 성공
// 인스턴스 메소드가 클래스 변수에 접근 -> 성공
c.instance_static(); // 1
// 인스턴스를 이용해서 인스턴스 메소드에 접근 -> 성공
// 인스턴스 메소드가 인스턴스 변수에 접근 -> 성공
c.instance_instance(); // 2
// 클래스를 이용해서 클래스 메소드에 접근 -> 성공
// 클래스 메소드가 클래스 변수에 접근 -> 성공
C1.static_static(); // 1, 클래스를 통해 클래스를 접근 가능
// 클래스를 이용해서 클래스 메소드에 접근 -> 성공
// 클래스 메소드가 인스턴스 변수에 접근 -> 실패
C1.static_instance(); // 에러 : 클래스가 인스턴스 접근 불가
// 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패
// C1.instance_static(); // 에러 : 인스턴스 생성이 되지 않았음
// 클래스를 이용해서 인스턴스 메소드에 접근 -> 실패
// C1.instance_instance(); // 에러 : 인스턴스가 인스턴스 접근 불가
}
}
※ 인스턴스 변수 -> Non-Static Field(variable)
※ 클래스 변수 -> Static Field
※ 결론 : 클래스 > 클래스, 인스턴스 > 인스턴스, 인스턴스 > 클래스는 접근 가능 / 클래스 > 인스턴스 접근 불가 (변수, 메소드 같은 결과)
5. 유효범위 (Scope)
1) 개념 설명
- 프로그램이 커지면 이름이 충돌하는 상황이 발생하기도 하는데, 해결하기 위한 개념
- 클래스 안에서 선언된 변수는 클래스 안에서만 영향을 미침
2) 다양한 유효범위들
- 디렉터리 : 파일 위계 상 파일 이름이 같다면 충돌을 피하기 위해 이름을 바꾸게 되는데, 디렉터리는 내부의 파일들의 이름이 밖의 파일명과 중복돼도 문제가 없음
- 정적 유효범위(Static Scope, Lexical Scope) : 자기 자신 또는 전역변수에만 가지는 고정적인 형태의 유효범위
static int i = 5;
static void a() {
int i = 10;
b();
}
static void b() { // 정적인 유효범위 : 자기 자신 또는 전역변수에만 접근하는 고정된 형태의 유효범위
// int i = 30;
System.out.println(i);
}
public static void main(String[] args) {
a(); // 5, a()의 지역변수가 10, b()는 전역변수 5를 가져오므로 우선함
// b()의 지역변수 30이 있다면 30을 가져올 것
// 전역변수 i = 5를 삭제하면 에러
}
}
- 동적 유효범위(Dynamic Scope) : 메인 영역 내부 또는 호출 메소드의 지역변수에 접근하는 유효범위
class C {
int v = 10;
void m() {
int v = 20;
System.out.println(v); // 20, 지역변수가 우선
System.out.println(this.v); // 10, 전역변수 지정
}
}
public class _11_Scope6 {
public static void main(String[] args) {
C c1 = new C ();
c1.m();
}
}
※ 동적 유효범위에서 전역변수를 배제하고 있지만 지정해야한다면 this를 사용
6. 동향
- 유효범위란 변수를 전역변수와 지역변수를 나눠서 좀 더 관리하기 편하도록 함
- 절차지향 프로그래밍에서는 모든 메소드에서 접근이 가능한 변수의 사용을 금기시하는 경향이 있음 (사용 목적이 분명한 경우에만 전역변수를 사용)
- 객체지향 프로그래밍은 이러한 문제를 극복하기 위한 노력 > 연관된 변수와 메소드를 그룹핑할 수 있도록 하여 객체 안에서 전역변수를 사용할 수 있도록 함
- 커진 객체 안에서의 메소드 추출의 경우 전역변수에 의존성이 생기기 때문에 가급적 전역변수의 사용을 자제하는 것이 좋으며 단일 객체가 너무 비대해지지 않도록 적적하게 규모를 쪼개는 것도 중요하다.
'Development > JAVA' 카테고리의 다른 글
(Java) Chapter13. Object (0) | 2023.01.23 |
---|---|
(Java) Chapter12. 예외 (0) | 2023.01.23 |
(Java) Chapter11. 접근 제어자 (0) | 2023.01.17 |
(Java) Chapter10. API (0) | 2023.01.10 |
(Java) Chapter8. 생성자와 상속 (0) | 2023.01.10 |