JAVA

DAY 9 - static / 상속 / Override / Overload (2024.07.15)

summ.n 2024. 8. 12. 19:09

 

 

 

오늘은 static과 상속에대해서 배웠다. 컨디션이 꽤나 안 좋은 날이었지만,,, 어려운 내용이기에 열심히 들어보았다.

 

static - 클래스변수

1. 메모리 static 영역에 1번만 생성된다. → 초기화 1번만 수행

모든 객체가 공유한다. (공유변수)

2. static 메소드에서는 static 변수만 사용 가능

static 메소드에서는 this를 참조할 수 없다

3. static변수나 메소드는 호출시 클래스명으로 직접 호출 할 수 있다.

객체로도 호출이 가능하다

4. static{ } - 초기화 영역

- 생성자보다도 먼저 수행한다.

 

StaticMain.java

package class__;

class StaticTest {
	int a; //필드(클래스 꺼) -> 초기화 끝남(0값 가지고있음) 
	      // new 생성해서 써야함. / 클래스 소속 o
	static int b; //필드, 초기화 / 그냥 갖다쓰면 됨. 
	// 클래스 소속 x //클래스 변수
	//static - 공유변수(같이 쓰자)
	
	static { //자바가상머신이 시작하자마자 여기 먼저 처리됨.(미리 올라옴)
		    //(main 불리자마자 여기부터 초기화하고 new하면 생성자메소드 초기화)
		System.out.println("static 초기화 영역");
		b = 7;
	}
	
	public StaticTest() {
		System.out.println("default 생성자");
		this.a = 7; //일반변수는 생성자에서 초기화시켜줌.
	}
	public void calc() {
		a++;
		b++;
	}
	public void disp() {
		System.out.println("a = " + this.a + "\t b = " + StaticTest.b); 
		                                   //static 변수는 출신만 밝히면 됨.
	}
	
	public static void output() {
		System.out.println("static method...");
		//System.out.println("a = " + this.a + "\t b = " + StaticTest.b); 
		//static 영역에는 this x
		// error - static 메소드에서는 static 변수만 사용 가능 
		// static 메소드에서는 this를 참조할 수 없다
		 
	}
}
//---------------
public class StaticMain {

	public static void main(String[] args) { //static이므로 this가 없다.
		//StaticTest st = new StaticTest();
		//a, b 접근 가능? o
		//System.out.println("a = " + st.a);
		//System.out.println("b = " + StaticTest.b);
		
		StaticTest aa = new StaticTest(); //클래스에 a잡힘 //static 영역에 b잡힘
		aa.calc();
		aa.disp(); //a = 1	 b = 1
		System.out.println();
		
 		StaticTest bb = new StaticTest(); //클래스에 새로운 a 잡힘 //b 새로 안잡힘.
		bb.calc();
		bb.disp(); //a = 1	 b = 2
		System.out.println();
		
		StaticTest cc = new StaticTest(); //클래스에 또 새로운 a 잡힘 //b 새로 안잡힘.
		cc.calc();
		cc.disp(); //a = 1	 b = 3
		System.out.println();
		
		StaticTest.output(); //클래스명.메소드 (이렇게해도 호출가능)
		aa.output(); // 객체명.메소드
	}

}//class StaticMain
 
static 초기화 영역
default 생성자
a = 8	 b = 8

default 생성자
a = 8	 b = 9

default 생성자
a = 8	 b = 10

static method...
static method...
 

import static

: 간단하게 static 상수 또는 메소드를 호출할 때 사용

 

ImportStatic.java

package class__;

//import static java.lang.Math.random;
//import static java.lang.Math.pow;
import static java.lang.Math.*; //와일드 카드, 모든 것(all)
import static java.lang.System.out;
import static java.util.Arrays.sort;

import java.util.Arrays;

public class ImportStatic {

	public static void main(String[] args) {
		//System.out.println("난수 = " + Math.random());
		System.out.println("난수 = " + random());
		//System.out.println("2의 5승 = " + Math.pow(2,  5));
		System.out.println("2의 5승 = " + pow(2,  5));
		
		int[] ar = {25, 78, 32, 40, 55};
		//Arrays.sort(ar); //return 값 없다.
		sort(ar);
		for(int data : ar) 
			System.out.print(data + "  ");
		//System.out.println();
		out.println();
		
		String[] ar2 = {"apple", "strawberry", "applemango", "pineapple", "tomato"};
		sort(ar2);
		for(String data : ar2) 
			System.out.print(data + "  ");
	}
}
 
난수 = 0.5264059054829953
2의 5승 = 32.0
25  32  40  55  78  
apple  applemango  pineapple  strawberry  tomato  
 

근데 이 방법은 뭐를 참조한지 잘 모르기때문에 굳이 안 쓰는게 좋다고 하셨다.


private(나만) < default(같은 패키지에서만) < protected(나, 내 자식들) < public(전부 다)

 

상속 (inheritance)

: is~a 관계 ~이다

: 클래스의 재 구현

 

자동차 ← 택시(돈 벌 수 있는 수단) 택시는 자동차이다. (택시가 자동차를 상속한다.)

 

1. 상속받는 클래스는 상속해주는 클래스의 생성자와

private를 제외한 모든 것을 상속받는다.

2. Super class : 상속해 주는 클래스(부모)

Sub class : 상속받는 클래스(자식)

3. 접근제한자 protected는 Sub class에서 접근이 가능하다

4. Sub class로 객체를 생성하면 Super class와 자신의 생성자를 모두 호출한다.

5. 다중상속을 할 수 없다.

 

[형식]

class Sub클래스명 extends Super클래스명{ }


Override 메소드

1. Super클래스와 Sub클래스에 똑같은 메소드가 존재

2. 모든 우선권은 Sub클래스가 갖는다.

3. Super, Sub 클래스의 접근제어자(Modifier)는 틀려도 되지만

Super보다 Sub클래스의 접근제어자(Modifier)가 더 커야한다.

 

 

Overload(쌍둥이) Override(부모-자식)

1. 하나의클래스 1. 상속관계

2. 메소드명이 똑같다. 2. 다 똑같아야 함.

3. 인수의 개수, 인수의 형이 틀린 경우. 3. 접근제어자(Modifier)는 틀려도되지만

반드시 자식클래스가 더 커야한다.


this 와 this()

1. this 는 자신의 클래스의 참조값을 갖고 있다.

2. this() 는 Overload된 다른 생성자를 호출 할 수 있다.

3. this()는 생성자의 첫줄에 써야 한다.


super 와 super()

1. super 는 부모클래스의 참조값을 갖고 있다.

2. super() 는 부모클래스의 생성자를 호출 할 수 있다.

3. super() 는 생성자의 첫줄에 써야 한다.

 

Super.java

package inheritance;

public class Super {
	protected double weight, height;
	
	public Super() {
		System.out.println("Super 기본 생성자");
	}
	
	//생성자 하나도 없으면 기본생성자 생성
	public Super(double weight, double height) {
		System.out.println("Super 생성자");
		
		this.weight = weight;
		this.height = height;
	}
	
	public void disp() {
		System.out.println("몸무게 = " + weight);
		System.out.println("키 = " + height);
	}
}
 

 

SubMain.java

package inheritance;

public class SubMain extends Super{//나 자신의 참조값은 this / 부모의 참조값은 super
	private String name;
	protected int age;

	public SubMain() {
		System.out.println("SubMain 기본 생성자");
	}
	public SubMain(String name, int age, double weight, double height) {
		//3. 부모 생성자 호출 가능 (본인도 생성자일 때) -> 생성자는 생성자끼리 호출가능
		super(weight, height); //첫 번째 줄에 적어야함.   -> 부모한테 갔다가 돌아옴
		//super 없으면 기본생성자 부름
		
		System.out.println("SubMain 생성자");
		
		this.name = name;
		this.age = age;
		//1.
		//this.weight = weight;
		//this.height = height;
		
		//2.
		//super.weight = weight;
		//super.height = height;
	}
	
	public void output() {
		System.out.println("이름 = " + name);
		System.out.println("나이 = " + age);
		//System.out.println("몸무게 = " + weight);
		//System.out.println("키 = " + height);
		
		disp(); //호출 super 생략
	}
 
public static void main(String[] args) {
		//생성자 2번 호출 (본인꺼랑 부모꺼 같이 메모리에 잡음)	
		SubMain aa = new SubMain("홍길동", 25, 85.3, 178.6); 
		aa.output();
		System.out.println();
		
		aa.disp(); //내꺼도 내꺼 부모꺼도 내꺼 
		// 먼저 this에서 찾고 없으면 super에 가서 찾음.
		//몸무게 = 85.3
		//키 = 178.6

		System.out.println("---------------------");
		
		//자식꺼만 잡는게 아니라 부모꺼도 같이 잡음
		Super bb = new SubMain("코난", 13, 35.8, 156.7); 
		//bb라는 애는 Super을 가리킴.(Super만 참조함)
		//bb.output() - error / output()함수 참조할 수 x
		bb.disp();
		//몸무게 = 85.3
		//키 = 178.6
	}
}
 

bb.output()이 안 되는 이유

  • bb 변수는 Super 타입이다. 즉, bb는 Super 클래스의 인스턴스를 가리킬 수 있는 참조 변수이다.
  • 그러나 실제로 bb가 가리키는 객체는 SubMain의 인스턴스다.

 

  • bb 변수의 타입은 Super이기 때문에, Super 클래스에 정의된 메소드만 호출할 수 있다.
  • SubMain 클래스의 output() 메소드는 Super 클래스에 정의되지 않았기 때문에, Super 타입의 참조 변수를 사용하여 output() 메소드를 호출하려고 하면 컴파일러는 이를 유효하지 않은 메소드 호출로 간주하고 에러를 발생시킨다.

 

  • 런타임에 실제 객체의 타입이 SubMain이라는 것을 알고 있지만, 컴파일 타임에는 참조 변수의 타입만을 고려하기 때문에 SubMain 클래스의 메소드에 접근할 수 없다.

 

[요약]

  • bb는 Super 타입의 참조 변수이기 때문에 Super 클래스에 정의된 메소드만 호출할 수 있다.
  • SubMain 클래스에 정의된 output() 메소드는 Super 클래스에 정의되어 있지 않기 때문에 Super 타입의 참조 변수를 통해서는 접근할 수 없다.
Super 생성자
SubMain 생성자
이름 = 홍길동
나이 = 25
몸무게 = 85.3
키 = 178.6

몸무게 = 85.3
키 = 178.6
---------------------
Super 생성자
SubMain 생성자
몸무게 = 35.8
키 = 156.7
 

ChildMain.java

package inheritance;

public class ChildMain extends Super {
	private String name;
	protected int age;
	
	public ChildMain() {
		System.out.println("ChildMain 기본 생성자");
	}
	
	public ChildMain(String name, int age, double weight, double height) {
		super(weight, height);
		
		System.out.println("ChildMain 생성자");
		
		this.name = name;
		this.age = age;
		
	}
	
	public void disp() {
		System.out.println("이름 = " + name);
		System.out.println("나이 = " + age);
		
		//disp(); //본인꺼 부르러감
		super.disp();
	}
 

 

밑에 코드가 좀 헷갈리는데 설명을 차근차근 해보자면

오버라이딩은 자식 클래스가 부모 클래스의 메소드를 재정하는 것을 의미한다. 이는 자식 클래스가 부모 클래스의 기본 동작을 변경하거나 확장할 수 있게 한다.

 

오버라이드 (Override): 자식 클래스에서 부모 클래스에 정의된 메소드를 동일한 시그니처(즉, 동일한 이름, 동일한 매개변수, 동일한 반환 타입)로 재정의하는 것을 의미한ㄷ다.

이를 통해 자식 클래스는 부모 클래스의 메소드 대신 자신이 정의한 메소드를 사용할 수 있다.

 

자바에서 메소드 오버라이드를 할 때 지켜야 할 규칙:

  1. 메소드의 이름, 반환 타입, 매개변수 목록이 부모 클래스의 메소드와 동일해야 한다.
  2. 접근 제어자는 부모 클래스의 메소드와 같거나 더 넓은 범위여야 한다 (예: 부모 클래스의 메소드가 protected이면, 자식 클래스의 메소드는 protected나 public이어야 한다).
  3. 오버라이드된 메소드는 부모 클래스의 예외 선언과 호환되어야 한.

 

	public static void main(String[] args) {
		ChildMain aa = new ChildMain("홍길동", 25, 85.3, 178.6);
		
		aa.disp(); //우선은 내꺼에서 먼저 찾음.
		
		System.out.println("---------------------");

        //자식꺼만 잡는게 아니라 부모꺼도 같이 잡음
		Super bb = new ChildMain("코난", 13, 35.8, 156.7); 
		//bb라는 애는 Super을 가리킴.(Super만 참조함)
		bb.disp(); //모든 우선권은 자식에게 있음. ChildMain의 disp()호출
		//오버라이드는 무조건 자식 먼저. 
	}
}
 
Super 생성자
ChildMain 생성자
이름 = 홍길동
나이 = 25
몸무게 = 85.3
키 = 178.6
---------------------
Super 생성자
ChildMain 생성자
이름 = 코난
나이 = 13
몸무게 = 35.8
키 = 156.7
 

Super bb = new ChildMain("코난", 13, 35.8, 156.7);

  • bb는 Super 타입의 참조변수지만, 실제로는 ChildMain 객체를 참조한다.
  • 이 때도 ChildMain의 매개변수 있는 생성자가 호출되어 초기화가 진행된다.

 

bb.disp();

  • bb는 Super 타입이지만 실제 객체는 ChildMain이기 때문에, ChildMain 클래스의 disp() 메소드가 호출된다.
  • 오버라이딩된 메소드 호출에서는 항상 실제 객체 타입의 메소드가 호출된다. 따라서 ChildMain의 disp()가 실행된다.

 

 

[요약]

  • 오버라이드된 메소드부모 클래스 타입의 참조변수를 통해 호출해도 자식 클래스에서 재정의한 메소드가 실행된다.
  • 이는 다형성의 중요한 예로, Super 타입의 변수가 실제로 ChildMain 객체를 참조할 때도, 자식 클래스에서 재정의된 메소드가 호출된다는 것을 보여준다.

ShapeMain.java

package inheritance;

import java.util.Scanner;

class ShapeTest{
	protected double area;
	protected Scanner scan = new Scanner(System.in);
	
	public ShapeTest() {
		System.out.println("ShapeTest 기본생성자");
	}
	public void calcArea() {
		System.out.println("도형을 계산합니다.");
	}
	public void dispArea() {
		System.out.println("도형을 출력합니다.");
	}
}
//------------
class SamTest extends ShapeTest{
	protected int base, height;
	
	public SamTest() {
		System.out.println("SamTest 기본생성자");
		
		System.out.print("밑변 : ");
		base = scan.nextInt();
		
		System.out.print("높이 : ");
		height = scan.nextInt();
	}
	
	@Override //바로 밑에있는 줄에만 먹힘.
	public void calcArea() {
		area = base * height / 2.;
	}
	
	@Override
	public void dispArea() {
		System.out.println("삼각형 넓이 = " + area);
	}
}
//------------
class SaTest extends ShapeTest{
	protected int width, height;
	
	SaTest(){
		System.out.println("SaTest 기본생성자");
		System.out.print("가로 : ");
		width = scan.nextInt();
		
		System.out.print("세로 : ");
		height = scan.nextInt();
	}
	@Override
	public void calcArea() {
		area = width * height;
	}
	@Override
	public void dispArea() {
		System.out.println("사각형 넓이 = " + area);
	}
}
//------------
class SadariTest extends ShapeTest{
	protected int top, bottom, height;
	
	SadariTest(){
		System.out.println("SadariTest 기본생성자");
		System.out.print("윗변 : ");
		top = scan.nextInt();
		
		System.out.print("밑변 : ");
		bottom = scan.nextInt();
		
		System.out.print("높이 : ");
		height = scan.nextInt();
	}
	@Override
	public void calcArea() {
		area = (top + bottom) * height / 2.;
	}
	@Override
	public void dispArea() {
		System.out.println("사다리꼴 넓이 = " + area);
	}
}
//------------
public class ShapeMain {

	public static void main(String[] args) {/*
		//1:1관계, 결합도 100%로 묶임. SamTest()클래스를 부르려면 sam 리모콘 필요
		SamTest sam = new SamTest(); 
		sam.calcArea();
		sam.dispArea();
		System.out.println();
		
		SaTest sa = new SaTest();
		sa.calcArea();
		sa.dispArea();
		System.out.println();
		
		SadariTest sad = new SadariTest();
		sad.calcArea();
		sad.dispArea();
		System.out.println();*/
		
		ShapeTest shape;
		//다형성, 부모는 모든 자식클래스 참조할 수 있다.
		shape = new SamTest(); 
		shape.calcArea();
		shape.dispArea();
		System.out.println();
		
		shape = new SaTest();
		shape.calcArea();
		shape.dispArea();
		System.out.println();
		
		shape = new SadariTest(); 
		shape.calcArea();
		shape.dispArea();
		System.out.println();
	}
}
 
ShapeTest 기본생성자
SamTest 기본생성자
밑변 : 2
높이 : 7
삼각형 넓이 = 7.0

ShapeTest 기본생성자
SaTest 기본생성자
가로 : 5
세로 : 5
사각형 넓이 = 25.0

ShapeTest 기본생성자
SadariTest 기본생성자
윗변 : 6
밑변 : 5
높이 : 4
사다리꼴 넓이 = 22.0