객체와 클래스, 은닉성

haribo·2021년 2월 27일
0

Java

목록 보기
4/17

Why? 이 복잡한거 왜 배워야 합니까?

컴퓨터는 노가다에 최적화된 기계고, 그걸 이용하는 사람은 똑똑하고 게을러야한다. (애당초 똑똑하고 게으른 인간이 만든게 컴퓨터 아닌가.) 따라서 문장을 입력할때 중복되는 값은 최대한 줄이고 짧게만 치고싶은게 컴퓨터를 이용하는 사람, 즉 프로그래머의 마인드셋이다. 이걸 우리는 효율성이라고도 부른다.

예시를 한번 들겠다. 메서드를 공부할때 '청소'라는 행위를 예로 들었다.

청소기 돌리기 + 걸레질하기 + 쓰레기 버리기 = 청소

근데 '집안일'이라는 차원에서 접근해보자. 과연 집안일이 청소뿐인가? 아니다, 요리도 있고 빨래도 있고 설거지도 있다.

장봐오기 + 재료 손질하기 + 조리하기 = 요리

더러운 그릇 불리기 + 수세미로 닦기 + 물로 헹구기 = 설거지

청소하고 요리하고 설거지해란 말은 '집안일' 해라로 축약될수 있다. 이게 바로 메서드에서 객체로 넘어가는 순간이다.

똑같은 코드를 여러번 치기 싫어서 반복문을 만들고, 그 반복문도 치기 싫어서 반복문을 묶어 메서드를 만든 우리의 자랑스러운 인간. 근데 메서드도 쌓이고 쌓이니 호출하고 또 호출하고, 그러는게 영 귀찮다. 메서드도 묶어서 이렇다 할만한게 필요하다고 생각했고, 그렇게 나온게 객체다.

(그럼 객체의 반대는 무엇이 될까? 바로 절차이다. C언어의 특징인 절차지향은 데이터, 기능, 함수 등을 합치지 않고 다 따로 둔다. 따라서 단위가 작고 속도가 빠르다.)

객체는 메서드와 변수의 묶음이다. 근데 이 조합이란게 제멋대로 하면 영 거시기하다. 다양한 재료라도 잘 조합하면 월남쌈, 잡채가 되는데 마음대로 조합하면 개밥이 되는것 처럼 우리는 레시피가 필요하고, 그 레시피가 클래스라 보면 되겠다.

객체에서의 변수는 데이터고, 메서드는 기능이다.

객체

사전적 의미 : 물건이나 어떠한 대상

프로그래밍에서의 의미 : 프로그램에서 표현하고자 하는 기능을 묶기 위한 단위

객체 = 데이터(변수,프로퍼티=멤버변수) + 기능(메서드=함수)

클래스와 객체의 관계 : 객체를 생성하기 위해서는 객체의 설계도(클래스)가 필요하다.

클래스

<자동차 클래스의 예>

  • 엔진, 문, 바퀴 등과 같은 명사적인 특성은 멤버변수(프로퍼티)
  • 전진, 후진 등과 같은 동사적인 특성은 메서드
  • 동일한 설계로 만들어진 자동차라 하더라도 각각의 자동차를 구성하는 부품들은 그 형태만 같을 뿐, 실제로는 각각 존재하게 된다. (버스, 승용차, 스포츠카에 들어가는 부품의 형태는 같지만 다른 결과물로 나오듯)
  • 클래스를 작성하면서 그 안에 생성되는 멤버변수들은 여러 개의 객체간에 서로 동일한 이름으로 존재하지만 실제로는 서로 다른 값이라는 의미

<작성 방법>

class 클래스이름 {
		멤버변수;
		
		void 메서드이름() {
				...
		}
}
  • class라는 예약어와 클래스의 이름을 명시하고 {...} 블록을 구성한다.
  • 멤버변수(프로퍼티)는 여러 개 존재할 수 있다. 이 때, 값을 선언과 동시에 할당할 수도 있고, 객체를 통해 간접적으로 할당할 수도 있다.
  • 메서드는 여러 개 존재할 수 있다.
  • 클래스 안에 포함된 멤버변수와 메서드를 특성에 따라 그룹화 하기 위한 기법이 클래스를 작성하는 것이라고 이해할 수 있다.

<객체의 생성 방법>

// 객체의 선언
클래스이름 객체이름;

// 객체의 할당
객체이름 = new 클래스이름(); // 생성자

// 선언과 할당의 통합
클래스이름 객체이름 = new 클래스이름();

<객체의 사용>

  • 객체 안에 포함된 변수나 메서드는 점(.)으로 연결하여 접근한다.
  • 점(.)은 '~의'로 해석하면 된다. (ex : arr.length는 배열의 길이)
// 객체 안에 포함된 변수의 값을 다른 변수에 복사하는 경우= 객체이름.멤버변수;

// 객체 안에 포함된 변수의 값에 다른 값을 대입하는 경우
객체이름.멤버변수 =;

// 객체 안에 포함된 메서드를 호출하는 경우
객체이름.메서드이름(); // this가 안되는 이유

// 객체 안에 포함된 메서드에 파라미터를 전달하는 경우
객체이름.

<실습 01>

class Student {
	/** 멤버변수의 선언 + 할당 */
	// 문장을 표현할 수 있는 변수형
	String name = "자바학생";
	int age = 19;
}

public class Main01 {
	public static void main(String[] args) {
		/** 객체의 선언과 할당의 분리 */
		Student std;
		std = new Student();
		
		/** 객체의 생성 (일괄지정) */
		// Student std = new Student();
		
		System.out.println("이름: " + std.name); // 객체이름.멤버변수 = 값
		System.out.println("나이: " + std.age);  // 객체이름.멤버변수 = 값
	}
}
  • 클래스를 만든다는건 어떻게 보면 자료형을 만드는 것

<멤버변수와 삽질>

  • 멤버변수에 직접 값을 대입한 경우 모든 객체가 동일한 값을 갖게 된다.
  • 근데 이 값은 사실 임시로 설정된 기본값이다. 객체에다가 틀을 제공하고, 객체에서 멤버변수의 값을 지정해버리면 사라진다. 그렇다! 삽질이다! 똑똑하고 게으른 프로그래머는 삽질을 싫어한다.
  • 그래서 클래스의 멤버변수엔 값을 지정하지 않는다.

<전역변수와 지역변수> - global vs local

  • 변수의 스코프를 배울 때 상위블록은 하위블록(상사는 부하직원에게 간섭할 수 있지만 부하직원이 상사에게 간섭했다간 하극상이라고 한다. 새드..)을 침범할 수 있다는 걸 우린 이미 알았다.
  • 클래스에 선언된 멤버변수는 메서드 안의 변수 바깥에 있고, 이는 메서드 안에서 멤버 변수를 쓸 수 있지만, 메서드 안의 변수는 빠져나갈 수 없다는 것을 의미한다.
  • 멤버변수 (=전역변수) : 클래스에 선언된 변수고, 클래스 범위 안이면 유효하게 쓰일 수 있는 것
  • 지역변수 : 메서드 안에 선언된 변수. 메서드 밖을 빠져나가지 못한다. for문에서 조건식에 i값을 써봤자 main에서 i를 부르면 그게 뭔데? 라고 한다. 이게 지역변수다.

<클래스 = 거푸집 이란건 알겠는데 어떻게 쓴다고?>

  • 붕어빵 장사를 하겠다고 야심차게 틀을 산 붕어빵 덕후. 너무 신나서 슈크림이고 팥이고 다 만들겠다고 재료도 알차게 질렀다. 근데 옆에 돈을 물쓰듯 쓰는 친구가 말한다. 야, 붕어빵 만든다고? 그럼 슈크림용, 팥용, 피자용 등등으로 틀을 다 따로 사야겠다 그치. 재료와 틀에 돈을 탕진한 평범한 수저 붕어빵 덕후친구는 이해를 하지 못한다. 야, 이걸로 그냥 다 만들면 되지 뭘 따로 사냐?
  • 클래스도 붕어빵 틀과 같다. 하나 만들어두면 그냥 이것저것 다 만들어낼 수 있는것이다. 일회용이 아니라 다회용이다.
  • 붕어빵에 들어가는 앙금은 프로퍼티(변수)값이다. 같은 기능을 수행하고, 변수값만 달라진다면 객체는 하나만 생성하는게 바람직하단 말이다.

this

<클래스와 메서드의 변수를 구분해 봅시다.>

  • 파이썬의 self와 비슷하다.
  • 클래스 안의 메서드에다가 변수를 지정하고, 클래스 밖에도 지정하고. 구분을 어떻게 하냐고 물으면 this를 붙이면 무조건 클래스의 멤버변수를 가르킨다고 보면 되겠다.
  • 모든 멤버변수 앞에는 this 키워드를 사용해서 우리 모두 소통을 원활히 하는 멋진 개발자가 되자
  • 메서드는 굳이 this를 붙이지 않아도 괜찮다. 다른클래스의 메서드는 이름만으로 호출할 수 없기 때문이다.

클래스 / 메서드 / 상수의 이름 규칙

  • 공통 규칙 : 영어 + 숫자 + 언더바(_)의 조합이고, 첫 글자는 반드시 영어로 표기한다.
  • 클래스 : 첫글자는 대문자로 시작, 나머지 글자는 소문자로 표시한다. 클래스는 큰 단위니까 멋지게 대문자로 강조해주자. 또한 두개 이상 단어를 조합할 때 새로운 단어는 대문자로 시작한다. (카멜)(낙타혹)
  • 메서드, 변수 : 대부분 소문자로 구성되지만 두개 이상의 단어를 조합할 경우 새로운 단어는 대문자로 표시한다.
  • 상수 : 모두 대문자로 표시한다.

생성자 메서드

  • 복습 필요한 부분
  • new 키워드를 사용하여 객체가 생성될 때 자동으로 실행되는 특수한 형태의 메서드
  • 리턴형을 명시하지 않으며, 메서드 이름은 클래스와 동일하다.
  • 자동으로 실행되는 특성때문에, 객체가 생성되면서 해당 객체의 특성을 초기화 하기 위하여 사용된다. (ex 멤버변수의 초기값을 할당하는 용도)

은닉성

<정의>

  • 멤버변수나 메서드가 객체에 노출되지 않도록 설정하는 기법이다.
  • 객체를 사용하는 측의 실수로 인한 기능의 오작동을 방지하기 위해, 클래스의 일부를 숨기는 처리를 말한다. 클래스 - 객체는 연결되어 있으니까
  • 객체에선 은닉된(private) 변수는 접근할 수 없다.

<적용방법>

  • 변수의 이름이나 메서드 이름 앞에 "접근 한정자"를 지정한다.
  • public : 모든 곳에서 접근 가능하며, 일반적으로 모든 메서드 앞에 명시한다. (why? 메서드는 자주 쓰이는 구문을 한번에 쓰려고 묶은 것이기 때문에, 즉 여러번 쓰이기 때문에)
  • private : 클래스 안에서만 사용 가능하고, 객체를 통해 접근할 수 없다. 일반적으로 모든 멤버변수 앞에 명시한다. (클래스의 변수가 바뀌어버리면 객체 찍어내기가 곤란해짐)
  • protected : 현재 클래스 내의 다른 자원이나 같은 패키지 안에 존재하는 클래스와 하위 클래스에서 접근 가능하다. 잘 사용하지 않는다.
  • default : 접근 한정자를 명시하지 않은 경우, 현재 클래스 내의 다른 자원이나 동일 패키지 내의 다른 클래스에서 접근 가능하다. 잘 사용하지 않는다. (앞전에 배운 10-클래스에서 사용한 예제들이 다 여기에 해당한다.)
  • public, private에만 집중하자!

<getter, setter>

  • 멤버변수가 은닉되면 데이터에 접근할 방법이 사라진다. 이는 프로그램의 근본적인 목적인 데이터를 다루는 것에 위반되므로 메서드를 통해 간접적으로 접근하는 방법이 마련되어야 한다.

  • getter : 은닉된 멤버변수의 값을 리턴하기 위한 메서드 (읽기)

              읽어주는 것이므로 return값의 자료형을 지정해주어야한다.
  • setter : 파라미터로 전달된 값을 멤버변수에 복사하기 위한 메서드 (쓰기)

              써주는 것이므로 void를 사용하고, 파라미터값에 데이터 타입을 지정한다.
  • 상속에서 부모클래스를 상속받는 자식클래스를 다루며 심화로 들어갈 내용이다. 부모클래스의 멤버변수에 자식클래스가 접근하기 위해서 쓰는 값, 유전자 같은 개념으로 보면 되겠다.

<실습 01>

class Student {
	// 은닉된 멤버변수 --> 현재 블록 안에서만 접근 가능함.
	private String name;
	private int age;
	
	// 은닉된 멤버변수에 값을 넣는 방법 --> 메서드를 사용
  // alt + shift + 
	public void setName(String name) {
		this.name = name;
	}
	
	public void setAge(int age) {
		this.age = age;
	}
	
	// 은닉된 멤버변수의 값을 읽는 방법
	public String getName() {
		return name;
	}
	
	public int getAge() {
		return age;
	}
}

public class Main01 {
	public static void main(String[] args) {
		Student s = new Student();
		s.setName("JAVA학생");
		s.setAge(20);
		
		String name = s.getName();
		System.out.println("이름: " + name);
		
		int age = s.getAge();
		System.out.println("나이: " + age);
	}
}

<클래스와 접근 한정자> - 클래스에 접근 한정자를 지정한 경우

  • public : 객체 생성이 가능하다. 서로 다른 소스코드에 정의된 클래스끼리도 객체 생성이 가능하다.
  • private, protected : 클래스에 적용할 수 없다. why? 다양한데 갖다 쓰려고 만드는데 클래스인데 그걸 못쓰게 은닉해버린다? 말이 안된다.
  • default : 동일한 소스코드에 정의된 클래스끼리만 객체로 생성 가능하다. (지금까지의 예제 형태)

<클래스의 분리 - 접근 한정자의 역할>

  • 하나의 소스코드에 프로그램의 모든 기능을 구현한다고 가정해보자. 죽죽죽 스크롤을 아무리 내려도 끝이 없는 팔만대장경 코드.. 보기만해도 머리아프고, 유지보수따윈 꿈도 못꿀것이다.
  • 따라서 기능단위로 소스코드를 분리해야하는데, 이럴 경우에는 public을 클래스에다가 명시해줘야 서로 객체생성이 가능하다.
  • 하나의 소스코드에는 하나의 public 클래스만 존재할 수 있다.

<실습 02> - 회원정보를 표현하는 클래스의 작성

public class Member {
	private String name;
	private int age;
	
	public Member(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}
  • 꿀팁 : toString,생성자, getter, setter 작성 굉장히 귀찮다. 자동완성 단축키 shift + alt + s를 이용하자.
/** 하나의 소스파일에는 하나의 public 클래스만 존재할 수 있기 때문에,
 * Member클래스를 다른 파이롤 나누어 놓았다. */
public class Main02 {
	public static void main(String[] args) {
		Member s = new Member("JAVA학생", 20);
		
		String name = s.getName();
		System.out.println("이름: " + name);
		
		int age = s.getAge();
		System.out.println("나이: " + age);
	}
}

Java Beans

  • 자바 언어에서 사용하는 복합적 데이터 표현의 최소 단위로서, 재사용 가능한 컴포넌트(구성요소)를 생성할 수 있다.
  • 자바빈즈 클래스로 작동하기 위해서 객체 클래스는 명명법, 생성법 그리고 행동에 관련된 일련의 관례를 따라야만 한다. 이러한 관례는 (빌더 형식의) 개발도구(ex 이클립스)에서 자바빈즈와의 연결을 통해 클래스의 사용과 재사용 그리고 클래스의 재배치를 가능하게 한다.
  • 관례 : 클래스는 생성자를 가지고 있어야한다. 클래스의 속성들은 get, set 혹은 표준 명명법을 따르는 메서드들을 사용해 접근할 수 있어야한다.
public class JavaBeans {
		private 자료형 멤버변수명;
		
		public JavaBeans() {} // 생성자
		
		public 자료형 getJavaBeans () { // getter
			return 멤버변수명;
		}

		public void setJavaBeans ( 자료형 멤버변수명 ) { // 지역변수, setter
			this.멤버변수명 = 멤버변수명;
		}

		// toString 포함
		@Override
		public String toString() {
			return "멤버변수명 [ = " + getJavaBeans() + " ] ";
		}
  • private로 멤버변수가 은닉
  • 생성자 포함
  • getter, setter 포함
  • toString 포함
이 포스트는 itpaper.co.kr에서 제공되는 강의자료를 바탕으로 작성되었습니다.
profile
그림 그리는 백엔드 개발자

0개의 댓글