본문 바로가기
뒷북 정리 (국비 교육)/java

[Java] step04. Class

by 규글 2022. 4. 13.

step04. Class

- 01

/*
 *  [ 클래스의 용도 ]
 *  
 *  1. 객체의 설계도 역활을 한다.
 *  2. 데이터 type 의 역활을 한다.
 *  3. static 필드나 static 메소드를 가지고 있는 역할을 한다.
 */

public class Car {
	//자동차의 이름을 저장할 필드 
	public String name;
	
	//달리는 메소드
	public void drive() {
		System.out.println("부릉 부릉 달려요~");
	}

 오늘은 class가 객체의 설계도 역할과 data type의 역할을 한다는 것만 언급한다. static field와 static method를 가지는 역할은 내일 언급할 예정이다. 여기서 local variable이 아닌 field이기 때문에 해당 변수를 만들고서 null을 대입하지 않아도 만들어진다.

 

 앞선 내용 중에서 heap 영역에 만들어지는 무언가를 가리키는 것을 객체라고 언급했다. 이미지의 1번에서처럼 class는 'new' 를 사용해서 생성하는데, 이미지 2번에서처럼 debugging 과정에서 확인해보면 class도 id를 가지는 것을 확인할 수 있다. 그리고 이 class를 만들어서 해당 객체를 변수에 담고자 한다면 data type을 지정해주어야 하는데, 이 data type이 class 자체가 된다. 이 때문에 class를 만들어 사용하는 만큼 reference data type의 개수가 늘기 때문에 무한이 된다. 이때 선언하는 변수 또한 숫자로 시작하면 안되고, 예약 변수(ex- public, class, static, etc...)를 사용해도 안된다.

 마지막으로 이미지 3번에서처럼 만들어둔 class를 import해야만 이 class를 사용할 수 있다.

 

 만약 만드는 class에 대한 정보를 알고 싶다면 ctrl을 누르고 해당 class의 이름을 클릭하면 class를 열어볼 수 있다. 이때 method의 안에 있는 variable을 local variable, method의 밖이지만 class 안에 있는 variable을 field variable이라고 한다.

 

 사실 이 class는 선언하는 것이 아니다. 한 번 만들어두고 계속해서 사용가능한 설계도라고 할 수 있다. 해당 class를 객체로 만드는데에 사용하는 것이다. 그리고 이 같은 설계도를 이용해서 여러 변수에 객체를 지정하는 경우도 많을 것이다. 게임을 예로 들 수 있을 것이다. 전통 놀이인 스타크래프트의 protoss 종족 gateway에서 유닛을 소환한다고 생각해보자. 질럿 세 기를 뽑는다면 이 세 기는 모두 각각의 고유 개체일 것이다. 하지만 이 세 기가 같은 개체 정보를 동시에 공유한다면 한 기가 적 저글링에게 공격 받았을 때, 세 기의 체력이 모두 감소하게 될 것이다. 이런 상황에서 필요에 따라 여러 객체를 만들기도 하는 것이라고 했다.

 

		Car c1 = new Car();
		Car c2 = new Car();
		c1.name="소나타";
		c2.name="그랜저";

 class 설계 시 reference data 값이 없으면 null로 초기화된다고 한다. 그냥 Car class의 name을 console 창에 출력해보면 null이 나온다.

 

- 02

public class MyUtil {
	// static field
	public static String version;
	
	// static method
	public static void send() {
		System.out.println("전송 합니다.");
	}
}
		//MyUtil 클래스의 static 메소드 호출하기
		MyUtil.send();
		//MyUtil 클래스의 static 필드에 값 대입하기 
		MyUtil.version="1.0";

 class 객체를 만들어서 참조값을 대입하고, 그것에 '.' 을 찍어서 참조할 수도 있다. 하지만 class 이름 자체에 '.' 을 찍어서 참조할 수도 있다. 그렇다면 class의 이름에 '.' 을 찍어서 참조하는 것의 의미와 이유는 무엇일까?

 이전 예제와는 다른 점이 있는데, class를 만들 때 'static' 이라는 친구가 더해진다는 점이다. java에서 static은 'only one' 을 의미한다. 즉, 단 하나뿐이라는 말이다. 이 class 내에 static으로 만든 친구가 없다면 new 로 실체화시킨 후 사용할 수 있다고 하지만, static으로 만든 친구가 있다면 class의 이름을 써서 사용이 가능하다고 했다. 다음을 보자.

 

 이전에 여러 상황에서 언급했던 stack 영역과 heap 영역이 있었다. 그것 말고도 static 영역이 있는 것인데, static으로 만든 것들은 이곳에 위치하게 된다고 했다. 이렇게 static 영역에 만들어진 field나 method는 그 이름 자체로도 구분이 가능한 것이라고 한다. 예시를 보면 MyUtil에는 static으로 만들어진 field와 method가 있다. 때문에 class의 이름 자체로도 static field나 method를 사용할 수 있는 것이다.

 

	PrintStream a=System.out;
	a.println("해골");
	a.println("원숭이");
		
	long current=System.currentTimeMillis();
	a.println(current);
		
	String str = "안녕하세요";
    
    --------------------------
    
    	class System {
    		public static PrintStream out {
        	
        	}
    	}

 console 창에 원하는 내용을 출력하는 System.out.println("xxx"); 에 대한 예시이다. 이 System이라는 친구는 class이다. 이 class에 . 을 찍어 참조하면 여러 내용이 뜨는데, 이 중에 out이라는 친구를 사용하게 되는 것이다. 이 목록에서 보이는 녹색 공백 원은 field(저장소)를, 녹색으로 찬 원은 method(기능)를 의미한다. 이전에도 field는 괄호를 동반하지 않고, method는 괄호를 동반한다고 했었다. 형태를 보면 구분이 가능하다. 그리고 원 위에 적힌 S는 static을, F는 field를 의미한다.

 이어서 out을 쓴다는 것은 PrintStream type의 id에 . 을 찍어 println() method를 사용하는 것이다. 이를 도식화하면 다음과 같다.

 

 System이라는 이름의 class에는 static 영역에 만들어진 out이라는 이름으로 만들어진 객체가 있을 것이다. 이 안에는 PrintStream type의 out이라는 이름으로 만들어진 객체가 있는데, 그렇다면 이 객체는 heap 영역에 만들어져있을 것이다. System class에 . 을 찍어 참조하고 이어서 out의 객체에 .을 찍어 println() 이라는 method를 사용하게 되는 것이다.

 

 만약 이 PrintStream type을 변수로 만들고자 한다면 어떻게 될까? 해당 변수의 data type을 PrintStream으로 해주어야 한다. 하지만 그냥 PrintStream을 type으로 작성하게 되면 붉은 색으로 오류가 발생했음을 알려준다. 이에 대한 import를 해주지 않았기 때문이다. 쉽게 import 하기 위해서는 저 PrintStream type 뒤에 커서를 두고 ctrl + space 를 하면 자동으로 import를 완성해준다. 이 PrintStream은 java의 io package에 있는 것이라 할 수 있다.

 그런데 이전에 System에 . 을 찍어 확인했을 때 java.lang 을 본 적이 있다. 이 java.lang package 안의 것들은 따로 import하지 않아도 사용이 가능하다고 한다.

 

 이어서 System class의 다른 method인 currentTimeMillis() 는 1970년 1월 1일 자정 기준으로 현재까지의 초의 값을 return 해준다. 또한 String의 경우 사실 new String("xxx"); 의 방식으로 객체를 만드는 것이 맞지만, 문자열은 자주 쓰는 data type이기 때문에 단순하게 String type으로 변수로 간단히 해도 문자열을 만들어준다.

 

- 03

		//Car 객체를 생성해서 drive() 메소드 호출하기
		new Car().drive();
		
		//Car 객체를 생성해서 참조값을 car1 이라는 지역변수에 담기 
		Car car1=new Car();
		//car1 안에 들어 있는 참조값을 이용해서 drive() 메소드 호출하기 
		car1.drive();
		//Car 객체를 생성해서 참조값을 car2 라는 지역변수에 담기 
		Car car2=new Car();
		//car2 안에 들어 있는 참조값을 이용해서 drive() 메소드 호출하기 
		car2.drive();
		
		// MyUtil 클래스의 send() 라는 static 메소드 호출하기 
		MyUtil.send();
		// YourUtil 클래스의 up() 이라는 static 메소드 호출하기 
		YourUtil.up();

 보통은 class를 만들어두고, 해당 class를 이용해서 객체를 만들어 method를 호출하는 과정을 거친다. 하지만 class 내에서 static으로 만들어진 field나 method의 경우는 해당 class의 이름에 . 을 찍어 바로 이용할 수 있다.

 

- 04

public class Member {
	// non static field 정의하기
	public int num;
	public String name;
	public String addr;
	
	// non static method 정의하기
	public void showInfo() {
		System.out.println(this.num+" | "+this.name+" | "+this.addr);
	}
}
		Member mem1=new Member();
		mem1.num=1;
		mem1.name="김구라";
		mem1.addr="노량진";
		
		// 2, 해골, 행신동 도 새로운 Member 객체를  생성해서 담아 보세요.
		Member mem2=new Member();
		mem2.num=2;
		mem2.name="해골";
		mem2.addr="행신동";
		
		// Member 객체의 메소드 호출하기
		mem1.showInfo();
		mem2.showInfo();

 this는 javascript에서의 this와 유사하다. class의 method 안에서 같은 class 내에 만들어진 다른 field를 참조하려고 한다고 생각해보자. 아직 해당 class는 객체로 만들어지지 않았으니 참조값도 없을텐데 어떤 식으로 참조할 수 있을까? 아직 설계중일 뿐인데 말이다. 그럴 때 사용하는 것이 this이다.이 this는 바로 그 해당 객체의 참조값을 의미하게 된다. 굉장히 중요한 부분이니 기억해야한다. 이 this는 혼돈의 여지가 없다면 생략도 가능하다. 예를 들어 동일한 객체 안의 것이라면 같은 공간에 있는 것이기 때문에 상관이 없을 것이다. 그렇다면 이 this는 왜 필요한 것일까? 바로 local variable과는 겹치면 안되기 때문이다. 만약에 겹친다면 this로 명확하게 해당 variable과는 구분을 해야할 것이다.

 그렇다면 이 this의 type은 무엇일까? 아무런 type 없이 변수에 넣으면 당연히 오류가 난다. 해당 변수의 type은 바로 그 class의 이름이 된다. 만약에 참조값을 전달해야한다면 해당 class를 type으로 해서 this 자체를 변수로 만들어두면 된다.

 

- 05

public class Rect {
	// non static field
	public int width; // 폭
	public int height; // 높이
	
	// 사각형의 면적을 return해주는 non static method 
	public int getArea() {
		int area=this.width * this.height;
		return area;
	}
}
		// Rect 객체 생성해서 참조값을 r1 이라는 지역 변수에 담기 
		Rect r1= new Rect();
        
		// Rect 객체의 field에 값 대입하기
		r1.width=10;
		r1.height=20;
        
		// Rect 객체의 method를 호출해서 return되는 int 값을 a 라는 지역변수에 담기 
		int a=r1.getArea();
		
		Rect r2=new Rect();
		r2.width=5;
		r2.height=10;
		int b=r2.getArea();

 앞선 - 04 에서와의 차이가 무엇일까? 바로 method를 만들 때 사용했던 void와 data type이다. void로 만든 method는 해당 method를 호출한 자리에 아무런 값도 return 하지 않는다. 때문에 해당 method를 변수에 넣을 일도 없다. 하지만 data type을 정해주면 method를 호출한 자리에 해당 type으로 값을 되돌려주기 때문에 필요하다면 사용할 수 있다. 이때 주의해야 할 점은 method를 만들 때 return이 없으면 문법이 성립하지 않는다는 것이다. 해당 자리에 값을 돌려주는데 return이 없다면 돌려주는 것이 아니기 때문이다. 이처럼 java는 문법이 엄격하다.

 

- 06

public class Box {
	// static field 
	public static Member mem=null; // null  로 초기화된 static field 
	public static Rect rect; // null 로 초기화된 static field
	public static Car car=new Car(); // 참조값을 넣어준 static field 
}
		// new Box().
		Member a=Box.mem; // null
		Rect b=Box.rect; // null
		
		Car c=Box.car; // 참조값이 들어 있다. 
		c.drive(); // method 호출가능!
		
		// 아래와 같이 사용할수도 있다. 
		Box.car.drive();
		
		// a.showInfo(); // a 가 null 이므로 NullPointerException 발생
		// b.getArea();  // b 가 null 이므로 NullPointerException 발생

 class를 만들 때 field를 선언만 하면 null로 초기화되지만, 그냥 명시적으로 작성만 해도 된다. 넣어주지 않으면 null로 초기화될 뿐이다. 또한 다른 객체를 new로 생성하는 것도 가능하다. 나중에는 class 안에 class를 넣어주는 것도 수업할 예정이라고 했다.

 

 더하여 null pointer exception 이라는 것이 있다. null 로 인한 오류가 발생했기 때문에 표기하는 예외표시이다. 생각해보자. 어떤 type이나 class의 field나 method를 사용함에 있어서 해당 변수에 들어있는 값이 null이라면 당연히 오류가 날 것이다. 예를 들어 어떤 String type에 아무런 값이 없는데, 그 해당 문자열의 길이를 구하기 위해 length() method를 사용했을 때 문제가 발생하는 것은 당연하다. 해당 data 값이 null이기 때문에 field나 method를 사용하지 못하는 것이다. 이럴 때 발생하는 것이 null pointer exception이다.

 

- 07

public class Marine {
	// 공격력을 저장하고 있는 static field 
	public static int attackPower=6;
	// 에너지를 저장하고 있는 non static field
	public int energy=40;
	
	// 움직이는 method
	public void move() {
		System.out.println("마린이 움직여요");
	}
	// 공격하는 method
	public void attack() {
		System.out.println(Marine.attackPower+" 의 공격력으로 공격을 해요");
	}
	// 현재 에너지를 출력하는 method
	public void showEnergy() {
		System.out.println("현재 에너지:"+this.energy);
	}
}
		/*
		 *  Marine 객체를 생성해서 참조값을 m1 이라는 지역 변수에 담아 보세요.
		 */
		Marine m1=new Marine();
		m1.move();
		m1.attack();
		m1.showEnergy();
		System.out.println("--- m1 ---");
		
		Marine m2=new Marine();
		m2.move();
		m2.attack();
		m2.showEnergy();
		System.out.println("--- m2 ---");
		
		//Marine 의 공격력을 올려보자 (모든 Marine 객체의 공격력에 영향을 미친다)
		Marine.attackPower += 2;
		m1.attack();
		m2.attack();
		
		//m1 의 에너지 감소 시키기 (m2 의 에너지에는 영향을 주지 않는다)
		m1.energy -= 10;
		m1.showEnergy();
		m2.showEnergy();

 예시

 

 

+) 다음 날 Review

 class를 만드는 이유는 무엇일까? class에는 field와 method가 만들어질 수 있는데, field는 data의 저장소이고 method는 기능이다. 이렇게 만들어둔 설계도를 이용해서 특정 시점에 일괄 사용, 수행할 수 있게 하기 위해서 class를 만드는 것이다. 이중에 method는 class에 설계할 때 중괄호 { } 안에 특정 시점에 일괄 실행할 java code를 넣는 것이다. javascript에서는 단독으로 그냥 했으나, java에서는 class 안에 넣어야 한다.이 field와 method는 static과 non-static으로 나뉜다.

 먼저 non-static부터 보자. non-static은 단지 설계도에 불과하다. 설계도인 class 자체로는 실체가 존재하는 것이 아니다. 이 설계도를 사용해서 heap 영역에 객체로 실체화시키고, 실체화된 해당 객체를 참조해야 사용할 수 있게 된다. 객체를 참조해서 값을 수정하거나, 다른 값에 넣어주거나 하는 작업들이 가능해진다. 객체마다 고유한 값을 가지게 하려는 목적이라면 static으로 만들면 안된다.

 반면에 static은 heap 영역에 있는 객체와는 상관 없이 바로 만들어진다. 이때 오직 딱 하나만 만들어지고, 계속해서 만들어지는 것도 아니게 된다. 이렇게 static으로 만든 것은 class 이름에 점을 찍어서 바로 접근할 수 있다.

 만들어진 객체 각각은 각 객체의 field에 있는 값을 사용해서 동작하는 경우가 많다. 그리고 field는 method를 사용하는 부품인 경우가 많다.

'뒷북 정리 (국비 교육) > java' 카테고리의 다른 글

[Java] step06. Constructor  (0) 2022.04.17
[Java] step05. Method  (0) 2022.04.13
[Java] step03. Operator  (0) 2022.04.11
[Java] step02. Data type  (0) 2022.04.11
[Java] step01. Hello  (0) 2022.04.05

댓글