본문 바로가기
Development (국비 복습 )/자바

[2023-03-23] static,접근제한자(private),getter,setter

by Puddingforever 2023. 3. 23.

인스턴스 멤버

Car myCar = new Car();

객체를 불러내는 것이지만, 주 목적은 클라스 안에 있는 멤버를 쓰기 위함이다. 이러한 것들은 인스턴스 멤버라고 한다.

생성자는 인스턴스 멤버가 아님 . 그냥 객체를 만들때 쓰는 코드임 ! 

 

정적멤버 (static)

객체 생성 없이 쓸 수 있는 멤버

클래스에 소속된 멤버로 객체 내부에 존재하지 않고 메소드 영역에 존재한다.

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

생성자는 정적멤버가 아님 ... 그냥 객체를 만들기 위해 쓰는 것 

public String color; //인스턴스 필드 , 객체 각각 다른 값을 가지고 싶음 
public static double pi = 3.14159; // 정적필드 : 값을 고정시키고 싶음, 모든 객체가 공유


//인스턴스 메서드멤버: 정사각형의 넓이 구하는 메서드 , 객체를 생성할 때마다 사용됨 
public  double areaRectangle(double width) {
    System.out.println("double 타입 매개변수 1개 메서드");
    return width * width ;
}

 

jvm -> 내가 만든 객체를 실행메소드 가서 인스턴스화 시킴!!

 

객체가 메모리에 올라가는 순서 

내 소스 -> java compiler (영어소스를  바이트코드로 만들어줌) ->

jvm이 바이트 코드를 기계어로 만들어 줌 -> 

CPU에 기계어로 객체가 등록되서 그 객체는 힙영역.스택영역에 저장된다.

 

출처:programiz

 

JDK는 영어소스가 바이트 코드로 변환될 수 있도록 컴파일러를 가지고 있고, JRE는 JVM이 있어서 바이트코드를 기계어로 번역해주는 기능이 있다.

따라서 JDK가 있어서 개발자가 코드를 쓰고 개발할 수 있는 것이며 , JDK안에 내장되어있는 JRE로 바이트코드를 기계어로 변환해준다. 


 

static으로 되어있는 것들은 객체로 안들어가고 그냥 공유 메모리에 들어가있는 것이다! 

 

 

public String color; //인스턴스 필드 , 객체 각각 다른 값을 가지고 싶음 
public static double pi = 3.14159; // 정적필드 : 값을 고정시키고 싶음, 모든 객체가 공유

//정적 메소드 : 공유 메모리 안으로 들어감 , 객체 고유한 값이 설정되지 않음 . 
//모두 같은 값을 가지고 있음
	public static int plus(int x,int y){	
		return x+y;
	}
	
	//인스턴스 메서드멤버: 정사각형의 넓이 구하는 메서드 , 객체를 생성할 때마다 사용됨 
	public double areaRectangle(double width) {
		System.out.println("double 타입 매개변수 1개 메서드");
		System.out.println("계산기의 색깔: "+this.color);
		return width * width ;
	}

여기서 static이 안들어간 것은 객체 안에 들어가지만 , static 이 들어있는 것은 공유 메모리 안에 들어간다. 

 


Calcualator 객체 

public class Calculator {
	
	public Calculator(String color){
		this.color = color;
	}
	
	//인스턴스 필드:객체의 고유한 값
	public String color;
	
	// 정적(static)필드 : 값을 고정시키고 싶음, 모든 객체가 공유 
	public static double pi = 3.14159;
	
	//정적 메소드 : 공유 메모리 안으로 들어감 , 객체 고유한 값이 사용되지 않음 !! 
	public static int plus(int x,int y){	
		return x+y;
	}
	
	//인스턴스 메서드멤버: 정사각형의 넓이 구하는 메서드 , 객체를 생성할 때마다 사용됨 
	public double areaRectangle(double width) {
		return width * width ;
	}
	
}

 

 

실행메소드 

 

 

Calculator myCalcu = new Calculator("빨간색");
		Calculator yourCalcu = new Calculator("흰색");

		//인스턴스 멤버 사용
		System.out.println("나의 계산기 색깔: "+myCalcu.color); // 빨간색
		System.out.println("나의 사각형 면적: "+myCalcu.areaRectangle(10)); // 100.0
		System.out.println("너의 계산기 색깔: " + yourCalcu.color); // 흰색
		System.out.println("너의 사각형 면적: "+yourCalcu.areaRectangle(20)); // 400.0
		
		//정적 멤버 사용
		System.out.println("나의 pi: "+Calculator.pi);
		System.out.println("너의 pi: "+Calculator.pi); //둘다 3.14159
        
        //변수 멤버를 이용하여 정적멤버를 가져올 수도 있다.
        System.out.println(myCalcu.pi);
		System.out.println(yourCalcu.pi); // 3.14159

하지만 변수 이름으로 정적멤버를 가져오려고 하면 ,

The static field Calculator.pi should be accessed in a static way

이런 경고가 뜬다. 따라서 클라스 이름.정적멤버 로 불러야한다. !

 

 

정적필드 특징 또는 주의점 

Calculator myCalcu = new Calculator("빨간색");
Calculator yourCalcu = new Calculator("흰색");

myCalcu.pi= 3.14; 
	
System.out.println(myCalcu.pi);
System.out.println(yourCalcu.pi); // 3.14

myCalcu 의 pi만 3.14로 변형된 줄 알았는데 yourCalcu의 객체의 변수값도 바끰

 

이는 !!! 객체 안에는 정적(static)멤버가 없고, 공유 메모리 쪽에 있기 때문에 동시에 변형된다. 


정적멤버를 쓰는 것은 대표적으로 Math 클래스를 들 수 있다

Math의 경우 new Math()로 쓰지않고 바로 Math.random()같은걸 사용했다.

즉, 데이터나 메소드가 공유메모리에 있는 static멤버들이기 때문에 굳이 new 객체로 생성해서 쓸 필요가 없다. 

반면에 String의 경우 , new String으로 사용할수 있는데, 이는 안의 필드 멤버들이 static이 아니라 인스턴스 멤버들이기 때문이다. 


정적필드는 공유메모리에 들어가기 때문에 생성자를 따로 설정해줄 필요가 없다.

 

	public Calculator(String color){
		this.color = color; // 에러는 안나지만 , 쓸필요 없다고 표시가 나옴 
	}
	//정적 필드 
	public static String color;

공유 메모리에 있기때문에 this를 할필요도 없다.

	public static double pi = 3.14159;
	
    public double areaCircle(double radius) {
		return radius * radius* this.pi ; // 에러는 안나지만 this 할필요없다고 표시남 
	}

하지만 메소드 자체를 static 멤버로 설정해주면 this에 에러가 간다.

	public static double areaCircle(double radius) {
		return radius * radius* this.pi ; // 메소드 자체가 공유메모리에 들어가서 에러 .
	}

static메소드에서 필드를 사용할 때는  반드시 공유메모리에 있는 static 멤버의 필드만 사용 가능하다.

 

 

https://docs.oracle.com/javase/8/docs/api/ 

 

Java Platform SE 8

 

docs.oracle.com

 

System.out.println 을 생각해보자 

 

System.out.println 도 그냥 가져와서 쓸 수 있는 이유는 공유메모리에 저장되어있기 때문이다,. 

public class System{
public static PrintStream out;
}

out의 타입은 PrintStream 임 . 이 PrintStream의 메소드 중의 하나가 println인거임 

 

 

public String nation; 이걸 가져와서 쓸 때

nation.charAt() 을 쓸 수 있는 것이랑 같은 것임. 왜냐하면 nation의 타입은 String이기 때문에 String객체의 메소드를 사용할 수 있는 것임 

 

 

설명 

 

PrintStream은 static이며, 얘가 가지고 있는 메소드는 여러개가 있다 ! 

따라서 객체 생성없이 System.out.println() 을 마음대로 사용하는 것이다. 


정적 메소드에는 , 인스턴스 필드를 사용할 수 없다. 

public String color; // 인스턴스 
public static double areaCircle(double radius) {
		System.out.println(color); 
        //에러 : Cannot make a static reference to the non-static field color
		return radius * radius*pi ;
	}

static 을 설정하는 경우 

public class Television {
	//240p Television.java 작성
	public static String company = "전자회사"; //정적필드
	public static String model = "LCD";   //정적필드
	public static String info  ;    //정적필드
	
	public int size ; //인스턴스 멤버
	
	
	public Television(String company, String model, String info, int size) {
		Television.company = company ;
		Television.model = model ;
		Television.info = info ;
		//Television.size = size ;//오류: size 는 인스턴스 맴버이므로 클래스이름.필드이름 형식을 사용할 수 없습니다.
		this.size = size ;
		
	}
	//정적 필드를 생성자를 통해서 초기화 시킬 수 있지만, 정적 필드의 값을 객체가 가지고 있지 않으므로
	//객체의 고유성이 확보되지 않음 
	//따라서, 정적필드는 생성자를 통해서는 초기화하지 않습니다.
	
	public Television(int size) {
		if (size < 0) {
			this.size = 24 ;
		} else {
			this.size = size ;	
		}
		
	}
	
	
	//사용자가 원하는 초기값이 지정되지 않은 static 필드에 초기값을 대입하려는 경우, 
	//정적 필드는 생성자를 통해 초기화하는 대신 static { } 을 사용합니다
	//static {  } 블록에는 정적 맴버들만 기술될 수 있습니다.
	static {
		if (Television.info == null ) {
			Television.info = Television.company + "-" + Television.model ;
			//인스턴스 멤버는 static {  } 블록에서 사용할 수 없습니다.
			//this.size = 24 ;   //오류
			//Television.size = 24 ; //오류
		} else if(Television.info.equals("")) {
			info = model + "-" + company ;
			
		} else {
			info = "전 LG 텔레비젼인데요...." ;
		}
	}
	
	static {
		//필요한 경우, 2개 이상의 static 블록을 아래에 나열하면 됩니다.
	}
	

	
}

 

 

public class TelevisionExample {

	public static void main(String[] args) {
		System.out.println("Television.info: " + Television.info);
		System.out.println();
		//showMyMessage() ; //오류정적 메서드의 { }에서 같은 클래스의 인스턴스 메서드를 호출할 수 없습니다. 
		//Cannot make a static reference to the non-static method showMyMessage() 
		//from the type TelevisionExample
		
		showYourMessage();
	}
	
	public void showMyMessage() {
		System.out.println("나는 인스턴스 멤버 메서드 입니다.");
	}
	
	public static void showYourMessage() {
		System.out.println("나는 인스턴스 멤버 메서드 입니다.");
	}
	
	

}

Television.info: 전자회사-LCD

나는 인스턴스 멤버 메서드 입니다.

 


 

final 은 변수값이 변하지 않는 것이다. 메소드에서 필드를 사용할 때 변경을 못하도록 강제함 

public class Person {
	
	public int weight; //인스턴스 필드 
	public static String nation; //정적(static) 필드
	public final String name = "pudding"; //인스턴스 final 필드 
	public static final String company = "삼성자동차"; // static final 필드 

}

메소드에서 name필드를 사용할 때 pudding이 항상 고정된 것이다. 

만약 필드에서 정의해주지 않는 다면 , 생성자를 통해 값을 초기화 시켜준다. 

public class Person {
	public int weight; //인스턴스 필드 
	public static String nation; //정적(static) 필드
	public final String name; //인스턴스 final 필드 
	public static final String company = "삼성자동차"; // static final 필드 
	
	public Person(String name) {
		this.name = name; // 이제 처음 만든 객체의 name값이 계속 사용됨 
	}
}

처음에 이름 설정하고 , 그 다음부터 객체를 생성하면 , Person pudding = new Person(~~~~), name값은 절대 변하지 않음 

 

 

결론 : static 은 인스턴스를 생성하지 않고도 쓸 수 있음 . 값이 모든 인스턴스에 공유됨

반면에 final 은 객체를 생성해야 쓸 수 있긴 하지만 , 값이 변할 수 없음

 

따라서 두개를 합쳐서 static final ! 을 만들어서 영원히 변하지 않는 필드를 만들어버림 

필드에 미리 정의해둠 

 

 


인스턴스 멤버 정적멤버 실행메소드 있는 클래스에서 쓸 때 

public class TelevisionExample {

	public static void main(String[] args) {
		System.out.println("Television.info: " + Television.info);
		System.out.println();
		//showMyMessage() ; //오류정적 메서드의 { }에서 같은 클래스의 인스턴스 메서드를 호출할 수 없습니다. 
		//Cannot make a static reference to the non-static method showMyMessage() 
		//from the type TelevisionExample
		
		showYourMessage();
		
		TelevisionExample t1 = new TelevisionExample();
		t1.showMyMessage();
	}
	
	public void showMyMessage() {
		System.out.println("나는 인스턴스 멤버 메서드 입니다.");
	}
	
	public static void showYourMessage() {
		System.out.println("나는 정적 멤버 메서드 입니다.");
	}
	
	

}

static final 필드는 관례적으로 전부 대문자로 쓴다.

 

Math.PI 

public static final int NOT_FOUND = -1000;

 


접근제한자 (Access Modifier)

 

-클래스 레벨의 접근 제한  ( public,default / package) 

public : 다른 패키지에서 쓸 수 있음 

default(public을 안씀): 같은 패키지에서만 쓸 수 있음 

주의 ! 클래스가 public 으로 안되어있으면 ,  import를 하더라도 쓸 수 없음

 

-멤버 레벨의 접근 제한(public , (default) , private , protected)

public : 다른 패키지에서도 쓸 수 있음  

default(public 을 안씀) : 같은 패키지에서만 쓸 수 있음 

private: 같은 클래스에서만 쓸 수 있음 !

생성자에 private 쓰는 것이 가능하지만 절대 쓰면 안된다.

다른 곳에서 객체를 사용하는 경우가 많으니까 , 생성자를 private 으로 해놓으면 객체지향이 이루어지지 않는다. 

protected: 상속받은 필드만 쓸 수 있음 , 다른 패키지에 있는 클래스에서 사용은 못하지만 , 만약 해당 필드의 클래스에서 상속받은 클래스면 쓸 수 있음 

 

일터에서는 !

클래스는 대부분 public 씀 !

필드 선언 시에 주로 private을 사용 !

protected는 거의 사용되지 않음 (개발자가 만든 클라스들을 이용해 상속 개념을 잘 안씀,

그냥 스프링 API를 상속받아서 쓰기만 함 !) 

 


Getter VS Setter : private 접근제한 필드 사용 ! 

 

 

VO , DTO : 필드만 정의되어있는 객체 , 다른 객체로 값을 보낼 때 사용 

public class DepartmentVO {
	private int department_id;
	private String department_name;
	private int manager_id;
	private int location_id;
	private boolean delFlag; 
	
	//디폴트 생성자 
	public DepartmentVO() {
		
	}
	
    
    //파라메터 생성자
    public DepartmentVO(int depart_id,String departName,int manager_id,
			int location_id) {
		this.depart_id = depart_id;
		this.departName = departName;
		this.manager_id = manager_id;
		this.location_id = location_id;
	}
    
	public String getDepartment_id() {
		return this.department_name;
	}
	
}

DTO 나 VO 객체를 만들 때 디폴트 생성자를 쓰는 이유는 @RequestBody를 DTO로 연결하는 과정에서 기본생성자가 존재하지 않는 다면 정상적으로 연결되지 않기 때문이다.   

 

파라메터 생성자를 쓰는 이유는 , 객체 하나하나마다 고유한 값을 가지고 있는 객체를 만들고 싶기 때문이다. (사용자 마다 정보 다름) 

 

 

 

private으로 설정된 필드는 다른 객체에서 바로 못씀 !

public static void main(String[] args) {
		
		DepartmentVO myDepartmentVO = new DepartmentVO();
		
		myDepartmentVO.변수;//에러

DepartmentVO 객체의 필드가 다 private으로 설정되어 있기 때문에 접근이 불가하다. 

 

getter와 setter를 이용하여 값을 가져오고 수정할 수 있다. 

/* getter : 필드의 값을 반환 */
	public int getDepart_id() {
		return this.depart_id;
	}
	public String getDepartName() {
		return this.departName;
	}
	public int getManager_id() {
		return this.manager_id;
	}
	public int getLocation_id() {
		return this.location_id;
	}
	public boolean getDelFlag() {
		return this.delFlag;
	}
	
	/* setter:값을 수정 */
	
	public void setDepart_id(int departId) {
		this.depart_id=departId;
	}
	
	public void setDepartName(String name) {
		 this.departName=name;
	}
	public void setManager_id(int id) {
		 this.manager_id = id;
	}
	public void setLocation_id(int location) {
		 this.location_id = location;
	}
	public void setDelFlag(boolean delFlag) {
		 this.delFlag = delFlag;
	}

 

다른 객체에서 가져와서 쓸 때 

DepartmentVO myDepartmentVO = new DepartmentVO();

myDepartmentVO.setDepartName("pudding");
System.out.println(myDepartmentVO.getDepartName());

쓰는 이유 ! 

private은 함부로 값을 대입하거나 , 값을 읽어오는 것을 막는 역할임 

하지만 객체지향이기 때문에 getter setter를 사용하는 것이다 ! 


 

댓글