수업내용/Java

[2022.09.20.화] static, 상속

주니어주니 2022. 9. 20. 23:16

 

1. static

  • static변수와 static메소드는 정적필드와 정적메소드라고 부른다. (클래스변수, 클래스메소드)
  • 정적필드와 정적메소드는 객체(인스턴스)에 소속된 멤버가 아니라 클래스에 고정된 멤버

 

1-1. 정적변수, 정적메소드

 

  • Sample 클래스 - 정적변수, 정적메소드가 포함되어 있는 클래스로 객체 생성하기

 

  1. 설계도 로딩
    - 작성한 Samlple.class 설계도(소스파일)를 클래스 영역에 로딩(컴파일)
    - Sample.class 설계도 안에는 멤버변수, 멤버메소드, 정적변수, 정적메소드가 다 있었어

  2. 정적변수, 정적메소드 할당
    - 클래스 영역에는 정적 변수, 정적 메소드 영역이 있어

    - 수행문에 클래스이름이 등장하는 순간, 클래스 영역 내의 정적변수/메소드 영역에 Sample 전용의 공간이 만들어져 
      ( 선언할 때 X, 메모리가 로딩될 때 O ) ---> ( 선언한 곳은 아직 소스파일이니까 ) 
      ( 객체 생성하든 안하든 메모리에 로딩이 되면 바로 만들어짐 ) 
    - 이 안에 정적변수b, 정적메소드test3, test4 (설계도가 아니라 실제 사용한 상태로 만들어져)가 들어가고
    - Sample.class설계도 안에 있는 정적변수, 정적메소드는 없어져 

  3. 객체 생성 
    - Sample sample1 = new Sample();
    - 객체영역에는 Sample객체 3개가 만들어지고 참조변수가 각각의 주소값을 가지고 있음 
    - 이 Sample객체마다 안에는 멤버변수, 멤버메소드가 들어가 있음
      ( 정적변수, 정적메소드는 이 객체에 포함이 안되어 있음 -> 정적변수,메소드 영역에 들어가있음 )
    * 멤버변수 a는 객체마다 생성이 되지만, 정적변수 b는 메모리에 로딩되는 순간 딱 한번만 생성됨

 


 

 

1-2. 정적변수, 정적메소드의 사용

 

  • SampleApp 클래스(실행클래스) - 정적변수, 정적메소드 사용하기

 

public class SampleApp { 

  public static void main(String[] args) { 

 

     // 클래스변수(정적변수)의 사용 ( 클래스를 통해 클래스변수에 접근 (클래스 내에 있으니까) )

     Sample.b = 100;                                 // 객체 생성없이 사용 가능

                                                                 // 수행문에서 Sample클래스 이름이 나오는 순간, Sample영역이 메모리에 로딩
                                                                 // 정적변수/정적메소드는 사용가능 상태가 됨   

                                                                 // Sample 클래스 안에 가야 b가 있다 => 클래스에 고정

 

     // 객체 생성

     Sample sample1 = new Sample( );    // 위에서 Sample 설계도가 로딩되었기 때문에 다시 로딩되지 않는다.

     Sample sample2 = new Sample( );

     Sample sample3 = new Sample( );

 

     // 멤버변수의 사용 ( 참조변수를 통해 멤버변수에 접근 )

     sample1.a = 10;                                 // 멤버변수, 멤버메소드는 객체 생성 후 사용

     sample2.a = 10;                                 // 참조변수를 통해 사용

     sample3.a = 10;

     

     // 멤버메소드 실행

     sample1.test1( );                               

   

     // 정적메소드 실행

     Sample.test3( );                                  // 객체 생성없이 사용가능

                                                                 // 클래스이름.변수 / 클래스이름.메소드( ) 로 사용 

  }

 

 

 

public void test1( ) { 
   s.o.p(a);    s.o.p(Sample.b);                                   // 멤버변수 사용가능, 클래스변수 사용가능
   test2( );     Sample.test3();      Sample.test4();     // 멤버메소드, 클래스메소드 사용가능
}

=> 멤버 메소드의 블록에서는 멤버변수/ 클래스변수/ 멤버메소드 / 클래스메소드 모두 사용가능 

 

public static void test1( ) {
   s.o.p(a);                             // 멤버변수 사용불가
   s.o.p(Sample.b);               // 클래스변수 사용가능
   test2( );                              // 멤버메소드 사용불가
   Sample.test3();                 // 클래스메소드 사용가능
}

=> 정적 메소드의 블록에서는 정적변수, 정적메소드만 사용가능 (객체 생성 전이니까) 
( ∵ 정적변수,메소드가 먼저 생기고, 그 다음에 객체 생성을 통해서 멤버변수,메소드가 생겨
정적메소드가 사용가능한 시점은 아직 객체가 생성되기 전이니까 멤버변수, 멤버메소드에 접근할 수가 없어 ) 

 

 


 

1-3. 정적변수, 정적메소드의 생성과정 

  •  main메소드가 왜 static이어야 하는가?

 

- main 메소드를 객체생성 없이 바로 사용하기 위해서 static 이 붙는 것

- Hello.main( );  : Hello클래스의 정적변수/메소드 영역에 있는 main메소드 실행

 

 


 

 

1-4. 정적 메소드의 사용 이유 

 

1)  정적변수 (static 변수) 

 

 

  • 인스턴스변수 a와 정적변수 b의 가장 큰 차이점
    a : 객체마다 생성
    b : 딱 한번 생성 



  • 정적변수의 사용 이유
    - 원의 둘레 공식 = 2 * pi * r 
    - 여러개의 객체를 통해서 여러개의 원을 만들건데, 원이 아무리 다양해도 pi는 항상 똑같은 값
    -> 객체의 고유한 속성이 될 수 없어 -> 객체마다 갖고있을 필요가 없어 -> 그럴때 정적변수 (상수)
    -> static final double PI = 3.14;   
    ∴ 원주율값은 변하지 않는 값이므로 정적변수로 정의, 정적변수는 대부분 상수,  상수는 변수명을 전부 대문자


  • 정적변수 : 상수를 정의할 때 사용 


  • 상수 정의하기 (정적변수)
    public class FileUpload { 
       static final String USER_IMAGE_SAVE_DIRECTORY = "resources/images/user";   // 이미지는 항상 여기에 저장
       static final int IMAGE_SIZE = 100*800;                                                                      // 파일크기는 항상 고정
    }


  •  final 키워드 -> 값이 한번 할당되고나서는 그 값이 수정되지 않게 할 때, 정적변수가 아닌 곳에도 붙임
    정적변수      -> 보통 static final

 

 

public class Circle {
	
	private double x;
	private double y;
	private double r;
	public static final double PI = 3.14;	// 정적변수, 상수 정의하기. 상수는 public static final이다.
	
	public Circle(double x, double y, double r) { // 객체생성하면서 초기화하는 생성자
		this.x = x; 
		this.y = y;
		this.r = r;
	}
	
	public double round() {
		return 2*Circle.PI*r;
	}
	
	public double area() { 
		/*
		 * - PI는 Circle 클래스의 정적변수
		 * 		Circle.Pi로 사용
		 * - pow(double a, double b)는 Math클래스의 정적메소드
		 * 		Math.pow(r,2)로 사용 
		 */
		
		return Circle.PI*Math.pow(r, 2);	// Math안에는 전부 정적변수, 정적메소드
	}
	
	public void display() { 
		System.out.println("x좌표: "+x);
		System.out.println("y좌표: "+y);
		System.out.println("둘레길이: "+round());
		System.out.println("원의 넓이: "+area());
	}

}

 

public class CircleApp {

	public static void main(String[] args) {
		Circle c1 = new Circle(10, 10, 5);
		Circle c2 = new Circle(0, 0, 7);
		
		c1.display();
		c2.display();
	}
}

 

x좌표: 10.0
y좌표: 10.0
둘레길이: 31.400000000000002
원의 넓이: 78.5
x좌표: 0.0
y좌표: 0.0
둘레길이: 43.96
원의 넓이: 153.86

 

 

* static 변수, 메소드는 클래스단위로 생기는것 ! 

public class Circle {

  public static final double PI = 3.14;

  public Circle(double x, double y, double r) { ~~ } } 
public class SampleApp {

   int x;                         // 멤버변수
   static int y=2000;    // 정적변수

   public void test1() { ~~ } } 

 

 

 

 

2) 정적메소드 (static 메소드)

 

 

* 인스턴스메소드: 객체 안에서 속성(멤버변수)과 상호작용하며 사용 가능

 

* 정적메소드 :  - 객체의 속성(멤버변수)과 상호작용없이(객체 생성할 필요 X) 직접 값을 전달받아서 수행

                        - 여러 클래스에서 광범위하게 사용가능

 

 

- 인스턴스 메소드, 정적 메소드 내에서 변수들의 사용 가능 여부

public class SampleApp {

	int x; 							// 멤버변수
	static int y=2000;					// 정적변수
	
	public void test1() {
		System.out.println(x);
		System.out.println(SampleApp.y);
		System.out.println(y);				// SampleApp내부에서는 클래스이름을 생략해도 정젹변수, 정적메소드를 사용할 수 있다. 비추천. 
		
	}
	
	public static void main(String[] args) {
		
//		System.out.println(x);				// 컴파일 오류, 정적메소드에서 인스턴스변수에 접근할 수 없다. 
		System.out.println(SampleApp.y);		// 정적메소드에서 정적변수 사용 가능
		
		SampleApp app = new SampleApp();	
		app.x = 100;					// 인스턴스변수는 객체 생성 후, 참조변수를 통해서 접근 가능
		System.out.println(app.x);
	}
}

 

 

 

- 정적메소드의 사용 예시 

import java.text.DecimalFormat;

public class NumberUtils {

	/**
	 * 숫자를 3자리마다 ,가 포함된 문자열로 반환
	 * @param number 숫자
	 * @return ,가 포함된 문자열
	 */
	
	public static String numberToStringWithComma(long number) {
		DecimalFormat format = new DecimalFormat("#,###");
		return format.format(number);
	}
	
	/**
	 * 문자열 "10,000,000"을 정수 10000000로 반환
	 * @param str
	 * @return
	 */
	public static int stringToInt(String str) { 
		
	}
	
	public static long stringToLong(String str) {
		
	}
	
	public static double stringToDouble(String str) {
		
	}
}

 

public class NumberUtilsApp {

	public static void main(String[] args) {
		int amount = 100000000;
		System.out.println("입금액: " + NumberUtils.numberToStringWithComma(amount));
	}
}

 

 


 

1-5. 변수의 종류

 

 

  < 멤버변수, 인스턴스변수, 프로퍼티, 속성 >   
    - 접근제한자를 붙일 수 있다.        
    - 객체 생성 이후에 사용 가능
    - 생성된 객체 내부에 존재 
    - 객체가 존재하는 동안 계속 유지
    - 모든 생성자, 모든 멤버 메소드에서 사용가능 (정적메소드x)

public class Account {

   private String no;                        
   private String owner;           
   private int password;      
   private long balance;      

 

  < 정적변수, 클래스변수 >
    - 접근제한자를 붙일 수 있다.
    - 클래스가 로딩되면 즉시 사용가능
    - 클래스 로딩 후 사용가능해진 정적 변수는 프로그램이 종료될 때까지 유지
    - 모든 생성자, 모든 멤버 메소드, 모든 정적 메소드에서 사용가능

public class Account {

  public static final double DESPOSITE_RATE = 0.031;      

 

< 매개변수 >
   - 메소드 실행시 인자로 전달된 값을 저장하는 변수 
   - 접근제한자, static을 붙일 수 없다.
   - 매개변수에 저장된 값을 메소드 블록에서 사용가능 
   - 메소드 종료되면 즉시 사라짐 

public void expire(String date, int amount) {  

   int amount = (int) (balance*DEPOSITE_RATE) + balance;
   System.out.println("예상 예지금액: " + amount);
   }

 

< 지역변수 >
   - 메소드 안에서 정의된 변수
   - 접근제한자, static을 붙일 수 없다. 
   - 변수가 선언된 다음 행부터 사용가능
   - 그 변수가 선언된 블록이 닫히거나, 메소드가 종료되면 즉시 사라짐

public void expire(String date, int amount) {  

   int amount = (int) (balance*DEPOSITE_RATE) + balance;
   System.out.println("예상 예지금액: " + amount);
   }

 

 


 

 

2. 상속

 

 

2-1. 클래스와 클래스의 관계

 

 

 

has a 관계

- A has a B : A 클래스가 B 클래스를 포함(의존)하는 관계

( ex. Car has a Engine : 자동차는 엔진을 포함하고 있다. 자동차는 엔진없이는 굴러갈 수 없다. (의존관계)

        Book has a review : 책은 리뷰를 포함하고 있다. (포함관계) )

 

is a 관계

- A is a B : A 클래스가 B 클래스의 종류 중 하나. 

( ex. Iphone is a SmartPhone : 아이폰도 스마트폰이다.

        GalaxyFlip is a SmartPhone : 갤럭시플립도 스마트폰이다. )

 

- B 클래스가 동일한 A 클래스들은 비슷한 특징, 기능을 가짐. (제네시스와 모닝)

- B 클래스는 상위 클래스, A 클래스는 하위 클래스(구체적) 

- A 클래스는 B 클래스로부터 공통속성과 공통기능을 상속받고, 자신의 고유속성과 고유기능을 구현 (상속관계) 

  -> 하위클래스를 빨리 만들 수 있음

 

-공통속성,기능을 갖고있으면 같은 타입이라고 봄 = 같은 타입의 변수 안에 담을 수 있다

 

Iphone p1 = new Iphone();                   // Iphone객체는 Iphone타입이고, SmartPhone타입이다. 

Galaxy p2 = new Galaxy();                  // Galaxy객체는 Galaxy타입이고, SmartPhone타입이다. 

 

SmartPhone p3 = new Iphone();         // Iphone객체를 SmartPhone타입의 참조변수로 참조할 수 있다.

SmartPhone p4 = new Galaxy();         // Galaxy객체를 SmartPhone타입의 참조변수로 참조할 수 있다. 

 

 

- 기존 클래스(상위클래스)를 상속받아서 새로운 클래스(하위클래스) 작성할 때는 extends 키워드를 사용
   extends(확장) : 상속은 상위 클래스를 확장시켜서 하위 클래스틀 만드는 것

- 자식한테 상속이 안되는 것
  1) 은닉화된 코드
  2) 생성자 

 

 


 

2-2. 상속 활용 예시 

 

  • 설계도

<설계도>

 

  • Phone 객체
/**
 * 전화기를 표현하는 클래스
 * @author wnnns
 *
 */
public class Phone {
	
	//은닉화된 멤버변수
	private String number; 
	
	/**
	 * 전화번호 반환
	 * @return 전화번호
	 */
	public String getNumber() {
		return number;
	}
	
	/**
	 * 전화번호를 전달받아서 멤버변수 number에 대입
	 * @param number
	 */
	public void setNumber(String number) {
		this.number=number; 
	}
	
	/**
	 * 전화하기 기능 실행
	 */
	public void tel() {
		System.out.println("["+number+"] 전화하기 기능 실행");
	}
	
	/**
	 * 문자하기 기능 실행
	 */
	public void sms() {
		System.out.println("["+number+"] 문자하기 기능 실행");
	}
	

}

 

  • SmartPhone 객체
/**
 * 스마트폰을 표현하는 클래스
 * <p> Phone클래스가 부모클래스
 * @author wnnns
 *
 */
public class SmartPhone extends Phone {

	private String email;
	private String ip;
	
	/**
	 * 이메일 반환
	 * @return 이메일
	 */
	public String getEmail() {
		return email;
	}
	
	/**
	 * 이메일을 전달받아서 멤버변수 email에 대입
	 * @param email 이메일 주소 
	 */
	public void setEmail(String email) {
		this.email = email;
	}
	
	/**
	 * ip주소 반환
	 * @return ip주소
	 */
	public String getIp() { 
		return ip;
	}
	
	/**
	 * ip주소를 전달받아서 멤버변수 ip에 대입
	 * @param ip ip주소
	 */
	public void setIp(String ip) {
		this.ip = ip;
	}
	
	/**
	 * 이메일 보내기 기능 실행
	 */
	public void sendEmail() {
		System.out.println("["+email+"] 이메일 보내기 기능 실행");
	}
	
	
	/**
	 * 인터넷 기능 실행
	 */
	public void internet() {
		System.out.println("["+ip+"] 인터넷 기능 실행");
	}
}

 

 

  • PhoneApp1 객체 (실행) 
public class PhoneApp1 {

	public static void main(String[] args) {
		
		System.out.println("### Phone객체 생성해서 사용하기");
		Phone p1 = new Phone(); 
		p1.setNumber("010-1111-1111");
		p1.tel();
		p1.sms();
		
		System.out.println("### SmartPhone객체 생성해서 사용하기");
		SmartPhone p2 = new SmartPhone();
		p2.setNumber("010-2222-2222");	// Phone 클래스로부터 상속받은 기능
		p2.setEmail("hong@gmail.com");	// SmartPhone 클래스에 정의된 기능
		p2.setIp("192.168.10.100");	// SmartPhone 클래스에 정의된 기능 
		p2.tel();			// Phone 클래스로부터 상속받은 기능
		p2.sms();			// Phone 클래스로부터 상속받은 기능
		p2.sendEmail();			// SmartPhone 클래스에 정의된 기능 
		p2.internet();			// SmartPhone 클래스에 정의된 기능 
	}
}

 

### Phone객체 생성해서 사용하기
[010-1111-1111] 전화하기 기능 실행
[010-1111-1111] 문자하기 기능 실행

### SmartPhone객체 생성해서 사용하기
[010-2222-2222] 전화하기 기능 실행
[010-2222-2222] 문자하기 기능 실행
[hong@gmail.com] 이메일 보내기 기능 실행
[192.168.10.100] 인터넷 기능 실행

 

 

  • Iphone 객체
/**
 * 아이폰을 표현하는 클래스
 * <p>SmartPhone클래스가 부모 클래스
 * @author wnnns
 *
 */
public class Iphone extends SmartPhone{

	/**
	 * 전달받은 금액만큼을 애플페이로 결제
	 * @param amount 결제금액
	 */
	public void applePay(int amount) {
		System.out.println("["+amount+"] 금액을 애플페이로 결제");
	}
	
	/**
	 * 페이스아이디로 잠금 해제
	 */
	public void faceId() {
		System.out.println("페이스아이디 기능 실행해서 잠금 해제");
	}
	
}

 

  • Galaxy 객체
**
 * 갤럭시를 표현하는 클래스
 * <p>SmartPhone클래스가 부모 클래스
 * @author wnnns
 *
 */
public class Galaxy extends SmartPhone {

	/**
	 * 전달받은 금액만큼을 삼성페이로 결제
	 * @param amount 결제금액 
	 */
	public void samsungPay(int amount) {
		System.out.println("["+amount+"] 금액을 삼성페이로 결제");
	}
	
	/**
	 * 전달받은 키워드로 검색
	 * @param keyword 키워드
	 */
	public void bixby(String keyword) {
		System.out.println("빅스비로 ["+keyword+"] 검색어를 검색");
	}
}

 

  • PhoneApp2 객체 (실행) 
public class PhoneApp2 {

	public static void main(String[] args) {
		System.out.println("### Iphone 객체를 생성해서 기능 사용하기");
		Iphone p1 = new Iphone();
		p1.setNumber("010-1111-1111");	// Phone 클래스로부터 상속받은 기능
		p1.setEmail("kang@gmail.com");	// SmartPhone 클래스로부터 상속받은 기능
		p1.setIp("192.168.10.101");	// SmartPhone 클래스로부터 상속받은 기능
		p1.tel();			// Phone 클래스로부터 상속받은 기능
		p1.sms();			// Phone 클래스로부터 상속받은 기능
		p1.sendEmail();			// SmartPhone 클래스로부터 상속받은 기능
		p1.internet();			// SmartPhone 클래스로부터 상속받은 기능
		p1.applePay(10000);		// Iphone 클래스에 정의된 기능
		p1.faceId();			// Iphone 클래스에 정의된 기능
		System.out.println();
		
		System.out.println("### Galaxy 객체를 생성해서 기능 사용하기");
		Galaxy p2 = new Galaxy();
		p2.setNumber("010-1111-2222");	// Phone 클래스로부터 상속받은 기능
		p2.setEmail("kim@gmail.com");	// SmartPhone 클래스로부터 상속받은 기능
		p2.setIp("192.168.10.101");	// SmartPhone 클래스로부터 상속받은 기능
		p2.tel();			// Phone 클래스로부터 상속받은 기능
		p2.sms();			// Phone 클래스로부터 상속받은 기능
		p2.sendEmail();			// SmartPhone 클래스로부터 상속받은 기능
		p2.internet();			// SmartPhone 클래스로부터 상속받은 기능
		p2.samsungPay(250000);		// Galaxy 클래스에 정의된 기능
		p2.bixby("아이폰14");		// Galaxy 클래스에 정의된 기능
	}
}

 

<< Iphone 객체 생성해서 사용하기
[010-3333-3333] 전화하기 기능
[010-3333-3333] 문자하기 기능
[kim@naver.com] 이메일 보내기 기능
[192.168.10.100] 인터넷 기능
[50000] 금액을 애플페이로 결제
페이스아이디로 잠금 해제

<< Galaxy 객체 생성해서 사용하기
[010-4444-4444] 전화하기 기능
[010-4444-4444] 문자하기 기능
[bang@naver.com] 이메일 보내기 기능
[192.168.10.100] 인터넷 기능
[10000] 금액을 삼성페이로 결제
빅스비로 [삼성] 검색

 

 


 

 

2-3. 상속관계인 객체의 생성 및 활용 

 

 

<상속관계의 객체 생성 및 활용>

 

 

* 상속받는 객체(부모클래스)가 저렇게 내부에 만들어지는건 아니고, 부모클래스로부터 확장돼서 자식클래스가 생성된다

 

* 상속받는 Phone객체는 한 객체를 공유하는게 아니라, 매번 새로 만들어짐 !! 

  (Phone객체마다 갖고있는 변수number의 값이 달라) 

 

* 객체를 만들 때 부모객체를 항상 고려 

 

 

 

< 실제 메모리상의 부모객체와 자식객체 연결관계 (내부에 있는게 아니라 원래 이런 모습) >

 

 

* Phone객체에는 변수,메소드랑 this, super도 있음

  this(); : 자기 자신을 가리키는 주소값

  super(); : 부모를 가리키는 주소값 

  Object : 모든 객체의 부모 객체  

 

- p변수가 가리키는 Iphone 객채로 가서 찾아 -> 없네?

-> super가 가리키는 부모객체 SmartPhone 객체로 가서 찾아 -> 없네? 

-> super가 가리키는 부모객체 Phone 객체로 가서 찾아 -> 없네?

-> super가 가리키는 부모객체 Object 객체로 가서 찾아 

-> 최상위 부모객체인 Object에서도 검색되지 않으면 컴파일 오류

 

 


 

2-4. 클래스의 단일 상속

 

클래스의 단일상속

- 단일 상속만 허용 (부모객체를 하나만 가질 수 있음) 

- 자신의 상위 클래스는 오직 하나만 허용