Hayden's Archive

[자바/ Java] 명시적 생성자 / setter & getter / 캡슐화 / 접근 제어자 / 참조변수와 toString() 본문

Study/Java & Kotlin

[자바/ Java] 명시적 생성자 / setter & getter / 캡슐화 / 접근 제어자 / 참조변수와 toString()

_hayden 2020. 4. 13. 23:47

생성자 전반, 기본 생성자 관련 포스팅 : https://hayden-archive.tistory.com/60?category=775409

 

[자바/JAVA] 객체 지향 프로그래밍

클래스(Class) / 필드(Field) / 메소드(Method) - 변수(Variable) : 값이 저장되는 공간. Value(값)와 다름. - 클래스(Class)의 구성요소 / 클래스의 멤버 : 필드(Field), 메소드(Method) - 필드(Field) : 클래스..

hayden-archive.tistory.com

 

 명시적 생성자(Explicit Constructor) 

현실세계에 있는 라운드티, 점퍼, 반팔티를 인스턴스로 만들고 싶다.
-> (타입 : Shirt, 속성: maker : String / longsleeved : boolean / color : char)

package oop.constructor;

public class Shirt {
	public String maker; // 필드로 생성하는 순간 null이라는 기본값을 가짐.(묵시적 초기화)
	public boolean longSleeved;
	public char color;
	
	
	public Shirt(){} // Default Constructor(기본 생성자)
	
	public Shirt(String m, boolean longs, char c){
		// Explicit Constructor(명시적 생성자)
		maker = m; // 명시적 생성자가 하는 일 = 필드 초기화(Field Initialization)
		longSleeved = longs;
		color = c;
	}
	
	public String getDetails() { // Worker Method = 구현부가 있는 메소드. 
		return maker+", "+longSleeved+", "+color;
	}
}

 

Shirt 타입으로 만든다!

Shirt 라운드티 = new Shirt(); 
Shirt 점퍼 = new Shirt();
Shirt 반팔티 = new Shirt();

Clothing 타입으로는 만들 수 있음. 하지만 Animal 타입으로는 만들 수 없음.

Clothing 타입이 shirt 타입보다 더 넓은 범주. 현업에 나가면 더 상세한 Shirt 타입으로 만들게 된다.

Shirt 타입, Clothing 타입 - 이러한 것들이 추상화(Abstraction)에 해당됨. 상속(Inheritance)과 관련됨.

Shirt(); 는 메소드가 아니라 생성자(Constructor)! 메소드는 소문자로 시작한다. 생성자는 메소드는 아니지만 하는 일은 메소드와 같다.

package oop.constructor.test;
/*
 * 객체가 생성될 때 생성자 안에 인자값을 넣어주지 않으면
 * 명시적인 값을 가지지 않는 디폴트 객체로 생성된다.
 * ::
 * 생성자를 통해서 값을 입력 +
 * 객체가 생성됨과 동시에 값을 가지도록 코드를 작성하겠음.
 */
import oop.constructor.Shirt;

public class ShirtTest {

	public static void main(String[] args) {
		
		Shirt sh = new Shirt(); // 기본 생성자로 sh라는 객체 생성
		System.out.println(sh.maker); // null 값이 출력됨.
		
		// 명시적 값으로 할당하겠음.
		Shirt roundT = new Shirt("유니클로", true, 'B'); // Calling. 생성자 호출
		Shirt jumpa = new Shirt("베네똥", true, 'B');
		Shirt banpalT = new Shirt("aaa", false, 'W');
		// 서로 다른 셔츠객체가 만들어졌다면 Heap 영역에 객체 3개가 로드되어 있을 것이고 
		// 각각의 위치는 서로 다르기 때문에 참조변수의 주소값들은 서로 다르게 나와야 한다
		
		System.out.println(roundT);
		System.out.println(jumpa);
		System.out.println(banpalT); 
        // 주소값들이 출력되고 객체가 만들어졌다는 게 확인됨
        
		System.out.println("++++++++++++++++++++++++++++++++++");
        
		System.out.println(roundT.getDetails());
		System.out.println(jumpa.getDetails());
		System.out.println(banpalT.getDetails());
        // 여기서 roundT, jumpa, banpalT는 각각 다른 주소값을 가진
        // 참조변수(Reference Variable)
	}
}
Shirt roundT = new Shirt("유니클로", true, 'B');
roundT.getDetails();
===> 여기에서 roundT를 참조변수(Reference Variable)라고 한다. 참조변수는 생성된 객체의 주소값을 가지고 있다.

 

- 참조변수는 필드 사용은 값 할당으로 하고, 메소드 사용은 메소드 호출(Method Calling)으로 한다.  

roundT.maker="나이키"; //값 할당
System.out.println(roundT.getDetails()); //메소드 호출. METHOD Calling

 

>>> 정리 1 <<<

- 객체 생성할 때 타입이 있어야 하고, new라는 키워드가 있어야 하고, 생성자가 호출되어야 한다.

- 객체를 생성하면 객체의 멤버(필드, 메소드)가 가상 메모리 JVM 안에 있는 Heap이라는 공간에 올라간다. Heap에는 반드시 new라는 키워드를 통해서 생성된 객체만 올라간다.  

- 객체 생성의 결론 -> Stack에 주소값 할당! 저장된 객체의 위치값이 지정된다.

>>> 정리 2 <<<

- 생성자가 하는 일은 필드 초기화(Field Initialization)이다.(X) - null값을 가지는 기본 생성자도 있음.

- ★명시적 생성자가 하는 일필드 초기화(Field Initialization)이다.(O)

- 생성자는 메소드 중에서 set메소드와 기능이 같다.(아래 setter 참고) 애초부터 값을 넣어서 만드는 것.

 


 

 setter()와 getter() 

public class Programmer {
	public String name;
	public int age;
	public float salary;
	public int bonus;
	
	public void setProgrammer(String n, int a, float s, int b) {
		name = n;
		age = a;
		salary = s;
		bonus = b;
	}
    // setPorgrammer()가 setter에 해당. 값을 주입하는 기능
    
	public String getProgrammer() {
		return name + "\t" + age + "\t" + salary + "\t" + bonus;
	}
    // getProgrammer()가 getter에 해당. 값을 가져오는 기능
    
}
public class ProgrammerTest {
	public static void main(String[] args) {
		Programmer pro = new Programmer();
		System.out.println("정보를 출력합니다");
		pro.setProgrammer("Amily", 30, 200.0f, 10);
		System.out.println(pro.getProgrammer());
	}
}

- 무조건 setter와 getter의 이름을 set, get으로 지을 필요는 없음. 내가 작성하려는 주제와 관련하여 set, get보다 더 어울리는 이름이 있다면 그 이름으로 정해주는 게 좋다. 

- 예를 들어 주입할 Setter를 setProducts -> buyProducts로 짓는다면 더 현실세계와 가깝게 직관적으로 지을 수 있다.

- 변수명은 충분히 유추할 수 있는 이름으로. 자세하면서도 심플하게. 둘 중 하나를 선택해야 한다면 자세하게! PM이 하는 가장 중요한 일이 식별자(Identifier를 지정하는 것!

 

- 변수명 앞에 붙이는 this의 쓰임새 ( 생성자 앞에 붙이는 this 관련 포스팅 : https://hayden-archive.tistory.com/66 )

public int month;
public void setMonth(int month){
    month = month; 
    // 필드 이름과 로컬 변수의 이름이 같으면 컴퓨터가 혼동함.
}
public int month;
public void setMonth(int m){
    month = m; 
    // 하지만 그렇다고 이렇게 m으로 쓰면 알아보기 어려움.
}
public int month;
public void setMonth(int month){
    this.month = month; 
    // 그러므로 필드와 로컬변수의 이름이 같을 때 구분하기 위해 this를 붙임.
}

- 로컬변수와 필드의 이름이 같을 때 구분하기 위해 필드 앞에 this를 지정한다.
- this는 해당 클래스 자기 자신을 가리킴. 해당 클래스 주소값을 참조하는 역할. 이 클래스 객체 자체의 레퍼런스값을 가져옴. ===> 따라서 위 코드의 this.month는 이 클래스 안에 있는 필드 month를 가리킴.

 

심화 참고 : https://blog.naver.com/jktk1/221551306370

 

[자바] 객체지향설계 - getter, setter를 사용하지 말라는 건 무슨 뜻일까

** https://qiita.com/Yahagi_pg/items/1bf59fc75d7f17c3b731** 위 글을 번역한 글입니다.​​취지이번에...

blog.naver.com

 


 

 캡슐화 Encapsulation 

public class MyDate {
	public int month;
	public int getMonth() {
		return month;
	}
	public void setMonth(int month) {
		this.month = month;
	}
}
public class MyDateTest {
	public static void main(String[] args) {
		MyDate md = new MyDate();
		md.month = 13;
        // MyDate 클래스의 필드 month에 직접적으로 값 13을 할당.
        // 이러면 잘못된 값이 쉽게 들어갈 수 있음.        
	}
}

 

- 안에 있는 데이터를 보호하기 위해 캡슐을 씌움. 데이터는 하나의 클래스 안에 있음. 이 클래스에 접근하지 못하도록 보호막을 씌움. 바깥에서 필드에 직접적으로 접근하지 못하도록(그러면 에러날 수 있으니까) 하는 게 Encapsulation!

- 그러려면 필드 앞에 private라는 접근 지정자를 붙이면 됨.(pulic private로 바꾸면 됨.)

public class MyDate {
	private int month;
	public int getMonth() {
		return month;
	}
	public void setMonth(int month) {
		this.month = month;
	}
}
public class MyDateTest1 {
	public static void main(String[] args) {
		MyDate md = new MyDate();		
		md.setMonth(13);
        // setter를 통해 값 13을 주입.
		
		md.month = 13; // 이제 이 코드는 에러가 뜨게 된다.
        
		// 그래서 MyDate 클래스에 있는 필드 month 앞에 private 붙여주면 
		// 필드가 not visible하다고 하며 접근 막힘 
	}
}

 

- 중요한 건 필드 앞에만 private을 붙임! 메소드 앞에 붙여버리면 기능을 구현하지 못한다.

- 앞으로 class 안에 들어가는 field 앞에는 무조건 private 붙일 것.

- field에 값을 주입하는 통로는 2개 뿐이다. 생성자와 setter

- 값을 주입하는 setter와 생성자 차이를 굳이 따져본다면...


 

 접근 제어자  / 접근 제한자 (Access Modifier) 

참고 :  https://hunit.tistory.com/162

 

자바(Java)〃접근 제한자 private/ protected/ public / default

이번 포스팅에서는 자바에서 사용되는 필드와 메소드등을 보호하는 보안과 관련된 접근 제한자에 대해 정리할 시간입니다. 접근 제한자는 모든 접근을 허용하는 public부터 시작해서 차례대로 protected, default..

hunit.tistory.com

- 접근 제어자(Access Modifier) : 접근 제어자는 public, private, protected로 총 3가지가 있다. (default는 그냥 아무것도 안 붙이는 것. 엄밀히 따지면 접근제어자는 아니다)

private   현재 클래스에서만 접근
default(아무것도 붙이지 않음)   현재 패키지에서만 접근
protected  같은 패키지에서만 접근(보통 때는 default랑 똑같다), 하지만 상속 관계일 때는 public으로 접근
public   import만 하면 다 접근 가능

 

- 접근 제어자가 아닌 나머지를 Usage Modifier라고 보면 된다. ex) abstract, static, final

- static final로 쓰든 final static으로 쓰든 순서 바꿔도 둘 다 되는 이유 : 둘 다 Usage Modifier에 해당한다.

 


 

 참조변수와 toString() 

package object.test;

class ReferenceVariable{
	private String notAddr = "Getter를 써야 이 값이 출력됨";
	String notAddr2 = "필드 앞에 private를 안 붙였으므로 직접 접근 가능";
	public String getNotAddr() {
		return notAddr;
	}
	
}

public class ReferenceVariableTest {
	public static void main(String[] args) {
		ReferenceVariable rv = new ReferenceVariable();
		System.out.println("1번\n" + rv); // 주소값 출력
		System.out.println("2번\n" + rv.toString()); // 주소값 출력
		System.out.println("3번\n" + rv.getNotAddr());
		System.out.println("4번\n" + rv.notAddr2);
	}
}

1번)

object.test 패키지에 있는 ReferenceVariable 클래스의 Full Name은 object.test.ReferenceVariable이다. 이렇게 패키지명을 포함한 클래스 전체 이름FQCN(Full Qualified Class Name)이라고 하는데, 이렇게 써야 프로그램이 인식한다. 

콘솔창에 출력되는 object.test.ReferenceVariable@7852e922에서 object.test.ReferenceVariable 뒤에 있는 7852e922는 클래스의 주소값이라고 할 수 있다.  따라서 참조변수는 주소값을 가지고 있고, 참조변수를 있는 그대로 출력하면 주소값이 출력된다.

2번)

2번은 1번과 출력 결과가 똑같다. toString()은 주소를 문자열로 반환하는 함수인데, 참조변수 뒤에는 무조건 .toString()이 생략되어 있다. 그러므로 toString으로 호출하든 안 하든 똑같이 주소값이 문자열로 반환된다.

===> 결론1 : 자바에서는 객체의 주소값을 출력하면 문자열로 출력된다. 
===> 결론2 : 객체는 주소값을 가지고 주소값은 참조변수 안에 담겨 있으며 이 주소값은 항상 문자열로 출력되는데 FQCN이 담겨 있다.

3번)

객체의 필드값을 가져오려면 Getter를 이용해서 가져오면 된다.

4번)

필드 앞에 접근 제어자 private에 붙이지 않았다면 현재 같은 패키지 안에 있는 클래스이므로 참조변수.필드명으로 직접 접근할 수 있다.

==> 하지만 필드명 앞에는 항상 private를 붙여서 Encapsulation을 하는 게 좋다.(값을 주입할 때는 생성자나 Setter / 값을 가지고 올 때는 Getter)