프로그래밍언어/JAVA

[JAVA] Call-By-Reference vs Call-By-Value / 인스턴스멤버 vs 정적멤버 / 자바 접근제한자

angelatto 2021. 2. 22. 20:57

※ 메소드 오버로딩(Overloading)

- 클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것을 말한다. 

- 하나의 메소드 이름으로 다양한 매개값을 받기 위해 메소드를 오버로딩한다.

- 오버로딩의 조건 : 매개변수 타입, 개수, 순서가 달라야 한다. 

 


※ Call By Value 

: 값을 복사해서 사용하기 때문에 한 쪽에서 값을 변경하더라도 영향을 받지 않는다.

Changer changer = new Changer();
		
// Call by value (값을 복사해서 호출)
int v1 = 1;
changer.change(v1); 

// void change(int value) { value = 10; }
// int value = 1 -> v1이 가지고 있는 "값"을 value 변수에 저장 한다. 

System.out.println(v1);

 


※ Call By Reference

: 객체의 번지를 복사하기 때문에 값을 변경하면 영향을 받는다. 

(예외). String 객체는 불변이다. 한번 String 객체가 만들어지면 그 안에 값은 바꿀 수 없다. 

// Call by Reference (번지를 복사해서 호출)

String v3 = "A";
changer.change(v3);
System.out.println(v3); // "A" 출력 
		
/*
void change(String value) { // 참조타입 
	value = "B";
}
*/

// Call by Reference (번지를 복사해서 호출)

int[] arr1 = {0, 0, 0};
changer.change(arr1);
System.out.println(arr1[0]); // 2 출력 

String[] arr3 = {"A", "B", "C"};
changer.change(arr3);
System.out.println(arr3[0]); // "B" 출력 

/*

void change(String[] arr) { // 참조타입 
	arr[0] = "B";
}

void change(String[] arr) { // 참조타입 
	arr[0] = "B";
}

*/


※ 인스턴스 멤버와 this

▶ 인스턴스 멤버란 객체(인스턴스)마다 가지고 있는 필드와 메소드를 말한다. 

- 인스턴스 멤버(인스턴스 필드, 인스턴스 메소드)는 객체에 소속된 멤버이기 때문에 객체가 없이는 사용할 수 없다. 

▶ 주의: 생성자는 인스턴스 멤버가 될 수 없다. 왜? 생성자는 객체가 생성될 때 쓰이고, 그 이후에는 사용되지 않는다. 

 

public class Car {

	// Field
    int gas;
    
    // Method
    void setSpeed(int speed) {
    
    }
}


Car myCar = new Car();
myCar.gas = 10;
myCar.setSpeed(60);

Car yourCar = new Car();
yourCar.gas = 20;
yourCar.setSpeed(80);

 

 

- 인스턴스 필드는 객체마다 각각 그 내부에 존재한다.

하지만 메소드는 코드 덩어리로 객체마다 존재하면 비효율적이기 때문에

메소드 영역에 따로 만들어 놓고 공유해서 사용한다. 

 

- 메소드는 엄연히 객체가 가진 멤버이기는 하다. 

 

▶this란 객체 자신의 참조를 가지고 있는 키워드이다. 


 ※ 메모리 사용 영역

: JVM은 OS에서 할당받은 메모리 영역(Runtime Data Area)를 세 영역으로 구분한다.

 

1. 메소드 영역

  • JVM을 시작할 때 생성
  • 로딩된 클래스 바이트 코드 내용을 분석 후 저장
  • 모든 스레드가 공유

2. 힙 영역 

  • JVM을 시작할 때 생성
  • 객체/배열 저장
  • 사용되지 않는 객체는 GC가 자동 제거

3. JVM 스택 

  • 스레드별로 생성
  • 메소드를 호출할 때마다 프레임을 스택에 추가(push)
  • 메소드가 종료하면 프레임을 제거(pop)

 

 정적 필드 (static field) : 값이 바뀌지 않고 정적인 것.

정적 필드메소드 영역(여러 객체가 공유하는 영역)에 만들어지고, 인스턴스 필드힙 영역의 객체안에 만들어진다. 

 

 

 인스턴스 멤버와 정적 멤버 선언의 기준 

객체안의 데이터를 이용해야하는 메소드가 있고, 반면에 객체없이도 실행가능한 메소드가 있다. 

=> 이 둘을 구분하는 것이 중요하다. 

 

주의) static 메소드에다가 객체의 데이터 (즉, 인스턴스필드)를 사용할 수 없다. 

또한, static 메소드에 this 키워드를 사용할 수 없다.

- 정적 멤버를 클래스에 고정된 필드와 메소드이다.

- 정적 멤버는 객체를 생성하지 않고, 클래스로 바로 접근해서 사용한다. 

- 정적 필드를 선언할 때 보통 초기 값을 준다. (고정값을 가지기 때문에)

- 인스턴스 필드는 객체마다 값이 다르기 때문에 초기값을 주지 않는다.

- 정적 멤버는 메소드 영역에 저장된다. 

 

[실습]

package pr06.exam11;

public class User { // 클래스는 객체를 만들기 위한 설계도이다. 

	// Field -> 인스턴스 필드를 선언한다. 
	String id; 
	String name; 
	String password;
	static String nation = "한국";
	// static 키워드를 붙였다는 것은 객체를 만들지 않아도 사용할 수 있다는 의미이다. 
	// 즉, nation은 힙 영역의 객체안이 아니라 메소드 영역에 저장한다. 
	
	// Constructor -> 생성자는 인스턴스의 멤버가 될 수 없다. 
	User(String name) {
		this.name = name;
	}
	
	// Method -> 인스턴스 메소드안에서는 인스턴스 필드를 얼마든지 쓸 수 있다. 
	void login() {
		// 인스턴스 필드를 사용하고 있다. 
		System.out.println(name + "님이 로그인합니다. ");
	}
	
	void logout() {
		System.out.println(name + "님이 로그아웃 합니다. ");
	}
	
	static void info() {
		// 이 메소드는 객체가 꼭 있어야 하나? NO 
		// 객체가 없어도 사용가능한 메소드도 있다. 
		System.out.println("모든 user는 한국인입니다.");
	}
}

 

package pr06.exam11;

public class Example {

	// 메인 메소드는 객체와 무관하게 실행되기 때문에 static이 붙은 것이다. 
	public static void main(String[] args) {

		// 인스턴스 멤버(필드, 메소)는 반드시 객체 참조 변수를 통해서 접근 
		User user1 = new User("이채정");
	
		System.out.println(user1.name);
		user1.login();
		user1.logout();
		
		// 정적 멤버 (필드, 메소드)는 객체가 없이도 사용 가능하다. 
		// how? 클래스로 접근하면 된다. 
		System.out.println(User.nation); // User는 클래스이다. 
		User.info(); //참조변수가 아니라 클래스로 접근 한다. 
		
	}

}

 싱글톤 (Singleton) 패턴 

- Factory() 메소드중에서 똑같은 객체가 리턴되도록 하는 것이 싱글톤 디자인 패턴이다. 

package pr06.exam13;

public class Example {

	public static void main(String[] args) {
		
		// 생성자를 이용해서 객체 얻기 
		// Singleton result1 = new Singleton();
		// 생성자가 private으로 제한되면 밖에서 new로 객체 생성할 수 없다. 
		
		// Factory Method
		Singleton result1 = Singleton.getInstance();
		Singleton result2 = Singleton.getInstance();
		
		if(result1 == result2) {
			System.out.println("같은 객체를 참조");
		}else {
			System.out.println("다른 객체를 참조");
		}
	}
}


/*

public class Singleton { // 클래스를 디자인할 때 디자인 패턴 
	
	// Field
	private static Singleton sinleton = new Singleton(); 
	// static -> 객체와 무관하게 생성 
	
	// Constructor
	private Singleton() {} // 외부에서 new 로 객체 생성 못한다. 
	
	// Method
	static Singleton getInstance() {
		return sinleton;
	}
	
	
}

*/

※ final 필드와 상수(static final)

- final 필드 

: 값을 변경할 수 없는 필드 

public class Korean {

	// Field
	static final String NATION = "한국";
	final String ssn; 
	
	/*
	 * static은 객체마다 부여되는 것이 아니라는 의미이고,
	 * final은 값이 변하지 않는다는 의미이다. 
	 * 
	 * static final은 대문자로 선언하는게 관례이다. 
	 * 
	 * final만 붙어있다면 객체마다 갖는 값이고, 한 번만 값을 줘야하는데
	 * 처음 생성자를 호출할 때 값을 준다. 
	 * 
	 */
	
	// Constructor
	Korean(String ssn) {
		this.ssn = ssn;
	}
	
	// Method
	
	
}

/*

public class Example {

	public static void main(String[] args) {
		System.out.println(Korean.NATION);
		
		Korean k1 = new Korean("123456-1234567");
		// k1.ssn = "111231-1341414"; 불가능한 코드 
	}

}

*/

※ 패키지 

패키지는 클래스의 일부이다. 

전체 클래스 이름 = 상위패키지.하위패키지.클래스 

- 주의) 패키지에 속한 클래스를 컴파일할 때는 javac의 옵션으로 -d를 주어야 한다. 

- 패키지이름은 소문자로 하는 것이 관례이다. 

- 자바 도큐먼트에 있는 패키지이름과 똑같이 만들면 안된다.

 

프로젝트의 산출물은 .jar 이고, 이 안에는 .class 파일들이 들어있다.

최종 산출물을 만들겠다. == Export 


※ import 문 

-  패키지가 다른 클래스를 사용해야 할 경우에 사용. 

-  패키지명을 전체 다 써주어도 되고 or 패키지를 import 한 뒤에 클래스명만 써도 된다. 


  접근 제한자 

접근 제한  적용 대상  접근할 수 없는 클래스 
public 클래스, 필드, 생성자, 메서드  없음 
protected 필드, 생성자, 메서드  자식 클래스가 아닌 다른 패키지에 소속된 클래스 (상속과 관련 있음 )
default 클래스, 필드, 생성자, 메서드  다른 패키지에 소속된 클래스 (즉, 같은 패키지에서만 쓸 수 있다.)
private 필드, 생성자, 메서드  모든 외부 클래스는 접근할 수 없다. (즉, 같은 클래스에서만 쓸 수 있다.)

주의) 클래스 앞에는 private을 붙일 수 없다.

무조건 클래스 앞에는 public 아니면 디폴트만 가능하다. 


  getter & setter

package pr06.exam17;

public class User {
	// Field
	private String id;
	private String name;
	private String password;
	private int age;
	private boolean adult;
	
	
	// Method
	public void setAdult(boolean adult) {
		this.adult = adult;
	}
	
	public Boolean isAdult() { // boolean getter는 is로 시작한다. 
		return adult;
	}
	
	
	// Setter
	public void setAge(int age) {
		if(age < 0) {
			this.age = 0;
		}else {
			this.age = age;
		}
	}
	
	// getter
	public int getAge() {
		return age;
	}
	
	public void setPassword(String password) { // 쓰기전용필드 
		this.password = password;
	}


}