학원/수업 기록

생성자, main, 인자, 메소드, void, 반환값, 상속, Object 클래스, 오버라이딩

2022. 6. 22. 11:49

더보기

생성자

기본 생성자

main

인자

파라미터(매개변수)

반환값(리턴값)

void

상속

부모, 자식

Object Class

오버라이딩

오버로딩

메소드

구구단

배열

근사값

객체 생성

 

0 - 값이 없다

void - 공간도 값도 없다

NULL - 공간은 있으나 값이 없다

 

 

< 생성자 >

객체 생성 1회만 호출 가능.
여러 개를 선언할 수 있다.




접근권한 클래스명(인자타입 인자명, ...) {


     내용

}


설명)

... : 여러 개 들어갈 수 있다는 것. 넣고 싶은 대로.
인자 : 실행될 때 받아와야 하는 데이터를 지정해주는 것.
기본생성자 : 인자가 없는 형태의 생성자이며 생략 가능. 안 만들었지만 실행이 된다. 내용이 없어서 생략했을 뿐. 단, 다른 생성자들이 존재하는 경우에는 반드시 선언(생략X)해 주어야 사용할 수 있다.

 

 

 

public class Test10s {
	public String s = "Hi";
	
	public Test10s() {  //추가
		System.out.println("기본생성자 실행");
	}
}

 

public class Test10 {

	public static void main(String[] args) {
		Test10s ts = new Test10s();

		System.out.println(ts.s);
		
		Test10s ts2 = new Test10s();
    
		ts.s = "1번";
		System.out.println(ts.s);
		System.out.println(ts2.s);
		
		ts2 = ts;
		System.out.println(ts2.s);		
	}

}

Test10에서 객체 생성이 될 때마다 내용인 "기본생성자 실행" 출력이 실행된다. 

객체 생성 때에만 한 번 실행되고 이 이후로는 호출할 방법이 없음.

기본 생성자는 생략 가능하다.

 

출력 결과:

기본생성자 실행
Hi
기본생성자 실행
1번
Hi
1번

 

 

 

public class Test10s {
	public String s = "Hi";
	
	private String a = "Oh!";
	
	public Test10s() {
		System.out.println("기본생성자 실행");
	}
	
	public Test10s(String msg) {
		System.out.println(a+" : "+msg);
	}
}

이후 실행할 때, 문자열을 준 적 없으니 안 불러지고 이전과 동일한 결과 출력된다.

문자열 추가해주면 기본 생성자가 아니라 내가 만든 추가적인 생성자가 동작을 한다.

public class Test10 {

	public static void main(String[] args) {
		Test10s ts = new Test10s("안녕?");

		System.out.println(ts.s);
		
		Test10s ts2 = new Test10s();
		
		ts.s = "1번";
		System.out.println(ts.s);
		System.out.println(ts2.s);
		
		ts2 = ts;
		System.out.println(ts2.s);		
	}

}

출력 결과:

Oh! : 안녕?
Hi
기본생성자 실행
1번
Hi
1번

 

 

기본생성자는 생략을 할 수 있기 때문에 주석처리를 하면

public class Test10s {
	public String s = "Hi";
	
	private String a = "Oh!";
	
//	public Test10s() {
//		System.out.println("기본생성자 실행");
//	}
	
	public Test10s(String msg) {
		System.out.println(a+" : "+msg);
	}
}

에러 뜸...

 

이유?

기본 생성자는 아무 생성자가 없을 때만 생략이 가능하고

다른 생성자가 존재할 때에는 생략할 수 없다.

 

 

 

 

쉬어가는 코너

자바에는 main 메소드가 포함된 클래스가 있다.

main 메소드는 객체 생성 없이 사용 가능하며,

프로그램 구동 시 자동적으로 실행되는 메소드이기 때문에 별도의 객체가 생성되지 않는다.

main이 포함된 클래스에서 추가적인 기능을 선언하고 사용하고자 하는 경우에는

반드시 main이 포함된 클래스 객체를 생성하여 사용하여야 한다.

 

메인은 이클립스 안의 메인이라는 메소드 말고 메인이 별도로 따로 존재하고 있다.

그래서 아래 코드에서 TestMain이라는 객체가 만들어진 게 아니라는 것.

tm.main이라고 하면서 부를 수는 있다. tm에 있는 main을 불러달라는 건데 메인도 어차피 메소드라서 불러지긴 한다.

하지만 메인이 메인을 부르고 메인이 메인을 부르므로 기능호출로 인한 무한루프..

-> 재귀함수 : 내가 나를 부르는 함수.

애초에 재귀라는 개념을 안 쓰려고 객체 지향을 만들었다. 비효율적이라서 권하지 않음.

 

예제)

public class TestMain {

 

int a = 10;

 

public static void main(String[] args) {

      TestMain tm = new TestMain();    -> a를 가져다 쓰기 위해 객체 생성

      System.out.println(tm.a);    -> 객체변수 tm에 있는 a를 가져와서 출력

}

 

 

 

 

 

Method

메소드는 기능 또는 동작을 나타내는 것으로 자바에서 모든 동작들은 메소드로 이루어져 있다.

메소드는 클래스 내부에 존재하게 되며, 이렇게 선언된 메소드들은 생성된 객체를 통해 동작시킬 수 있다.

객체 생성이 선언돼야 그 안의 기능들을 동작시킬 수 있다.

 

 

메소드 선언 형태 

접근권한 반환타입 메소드명(인자타입 인자명, ...) {

     내용

     *만약 반환타입이 void가 아니라면
     return 값;

}

접근권한 : public, protected, private

 

 

 

 

 

< 인자 (Argument) >

해당 메소드가 실행될 때 반드시 있어야 하는 기능(필수적인 값)을 선언한 것.

타입도 개수도 제한이 없다.

단, 호출할 경우 받아오는 값들을 차례대로 해당 위치의 인자 변수에 넣어주기 때문에

넣어주는 값과 타입의 개수가 동일해야 한다.

 

 

 

인자 vs 매개변수

선언 시 () 안에 지정되는 내용 : 인자 (Argument)

호출 시 () 안에 넣는 값 : 매개변수 (Parameter)

 

메소드 선언할 때는 인자, 사용할 때는 파라미터라고 인식한다.

 

 

 

 

 

 

< 반환값 >

메소드 선언에서 반환타입 부분은 객체, 일반변수를 가리지 않고 다양하게 사용 가능.

반환 타입의 void는 존재하지 않는다는 의미로, 값 자체가 올 수 없음.

null의 경우 공간은 있으나 값이 없다는 의미이지만 void공간 자체가 없음

return을 통해 값을 반환하게 되는데, 해당 메소드 호출 위치에 반환값을 집어 넣어준다고 생각하면 된다.

따라서 해당 기능을 수행하고 그 결과 값을 받아서 처리할 일이 있는 경우 반환값을 선언해야 한다.

.은 '~에 있는' 이라는 의미!!

 

굳이 뭐 하려는 게 아니면 그냥 아래처럼 출력하면 되는데,
public void printPlus(int a, int b) {
System.out.println(a+b);
}
->
ts.printPlus(1, 2);

리턴값 쓰는 건 그게 활용하기 좋기 때문.

호출 위치에 반환값을 넣어줬으니 출력하려면 통째로 써야 한다.
public int plus(int a, int b) { 
return a + b; 
}
->
System.out.println(ts.plus(1, 2));

                                이게 아예 반환값인 3을 의미함.

메소드를 만들 때, 실행하고 받은 결과를 이래저래 활용하고 싶으면 리턴값을 쓰는 것.
예를 들어 아래처럼 변수 지정해서 쓴다던지?
int b = ts.plus(1, 2);
System.out.println(b);

 

 

 

메소드 호출은 생성된 객체를 통해 간단하게 호출한다.

 

예제)

Test1 클래스

public Test1 {

             public static void main(String[] args) {

                          Test2 t2 = new Test2();

                          t2.test("Hi");     -> Test2 객체에 있는 test 메소드에 Hi를 보내고 실행

            }

}

 

 

 

 

 

 

Life Cycle

전역변수 : 클래스에 선언된 변수. 클래스 내부 어디서든 사용 가능.

지역변수 : 메소드, 생성자 등에서 선언된 변수. 생성된 위치 내부에서만 사용 가능. 변수 선언 시점 ~ 종료 시점

 

둘을 변개로 인식한다. 사용처가 분리되어 있기 때문에 이름이 같다고 해도 마찬가지!

 

 

둘 다 a이지만,

전역변수인 상단 int a는 이 객체가 살아있는 한 계속 쓸 수 있다.

test메소드 안에 있는 int a는 지역변수.

 

그냥 a는 가까이에 있는 걸 가리킴. 당연히 메소드 안에 있는 게 제일 가까우므로 test메소드 안에 있는 지역변수 a를 가리킨다.

 

this = 내가 들어있는 클래스 안에 있는

-> this.a = test 클래스 안에 있는 a

-> this.a = a;  전역변수a에 지역변수 a 값을 넣어주겠다는 것.

 

int i는 for문에 속한 것. 그래서 for문 시작부터 끝까지만 살아 있다.

 

 

 

 

 

 

예제

1. 구구단 만들고 숫자 입력 시 출력하기.

public void gugu(int a) {
    for (int i = 2; i <= 9; i++) {
        System.out.println(a + "*" + i + "=" + a * i);
    }
}

 

 

 

2. 기초데이터 배열이 있고, 그 배열에서 내가 입력한 숫자와 가장 가까운 숫자를 출력하기.

//Test10s 클래스
public void pick(int num) { 
    int[] arr = {45, 17, 22, 1, 98};

    int min = arr[0]; //0을 넣고 시작해도 된다.
    //절댓값 만드는 거 메소드 따로 만들 거임

    //메소드 부르려면 객체 생성을 해야 한다.
    //같은 객체 안에 있는 건 별다른 행위 없이 객체 생성 없이 불러서 쓸 수 있다. 그래서 cha 그냥 부름.
    int cha = cha(num, min);

    for(int i = 1; i<arr.length; i++) {
        //차이가 작을 경우 최솟값 교체
        if(cha > cha(num, arr[i])) {
            min = arr[i];  //현 배열의 값이 앞의 값들보다 가까운 경우 교체
            cha = cha(num, arr[i]);
        }
    }
    System.out.println(min);
}

public int cha(int a, int b) {  //차이의 절댓값을 돌려주는 메소드
    int cha = a-b;

    if(cha<0) {
        cha*= -1; //양수 만들기
    }
    return cha;
}
//Test10 클래스
public class Test10 {

	public static void main(String[] args) {
		Test10s ts = new Test10s();
		
		ts.pick(16);
	}
}

출력 결과 : 17

 

가깝다 = 두 수의 차이가 작다

중요한 건 값을 어떻게 보관하느냐?

 

 

 

 

상속

자바의 상속은 1:1 관계만 성립한다.

권한이 허용하는 모든 것을 물려받는다.

 

- A 클래스 안에 a라는 메소드를 만들었다.

- 그럼 a 메소드를 쓸 수 있는 클래스는 A, B, C, D, E 클래스 모두..

- B 클래스에 b 메소드를 만들면 B, D, E 클래스만 쓸 수 있다.

- A 클래스에 접근권한을 둔 ap 메소드(private)를 만들면 A 클래스만 쓸 수 있다.

- E 클래스가 B, C 클래스 둘 다 부모로 하는 건 불가능. 부모자식 관계(상속)는 일대일이기 때문.

부모는 하나만 둘 수 있게 상속관계 제한을 뒀다.

 

 

public class 자녀클래스 extends 부모클래스 {

     내용

}

 

 

 

 

이클립스에서 클래스를 3개 만들고 하나만 main 넣고 나머지 둘은 각 부모 클래스, 자녀 클래스.

Test12, Test12P, Test12C

public class Test12P {
	public String string = "Parent!";
}
public class Test12C extends Test12P {  //상속
	public void test() {
		System.out.println("C에서 부름 : "+s);  
        //스트링 s를 그냥 쓸 수 있음
	}
}
public class Test12 {

	public static void main(String[] args) {
		Test12C c = new Test12C();
		
		c.test();
        
        System.out.println(c.s);
	}

}

출력 결과 :

C에서 부름 : Parent!
Parent!

 

 

 

public class Test12C extends Test12P {
	public String s = "Child!!!";
	
	public void test() {
		System.out.println("C에서 부름 : "+s);
	}
}

덮어씌워진다. (오버라이딩)

출력 결과:

C에서 부름 : Child!!!
Child!!!

 

되찾을 방법은 없다.

이럴 때는 새끼를 만들어야 함.

public class Test12C extends Test12P {
	public String s = "Child!!!";
	
	public void test() {
		System.out.println("C에서 부름 : "+s);
	}
	
	public void test2() {
		System.out.println("부모꺼 호출 : "+super.s);
	}
}

super : 부모 클래스를 지정하는 용어.

 ex) super.s -> 부모 클래스의 s라는 의미.

이런식으로 간접적으로 우회해서 부를 수 있다.

마우스를 갖다 대면 어디의 s인지 확인 가능..

public class Test12 {

	public static void main(String[] args) {
		Test12C c = new Test12C();
		
		c.test();
		
		System.out.println(c.s);
		
		c.test2();
	}
}

출력 결과:

C에서 부름 : Child!!!
Child!!!
부모꺼 호출 : Parent!

 

 

public class Test12P {
	public String s = "Parent!";
	
	private int a = 10;
}

protected는 못쓴다.

 

변수 뿐 아니라 메소드까지도 호출 가능하다.

p라는 메소드 만들고, Test12에서 호출.

public class Test12P {
	public String s = "Parent!";
	
	private int a = 10;  //protected는 못쓴다
	
	public void p() {
		System.out.println("P메소드");
	}
}
public class Test12 {

	public static void main(String[] args) {
		Test12C c = new Test12C();
		
		c.test();
		
		System.out.println(c.s);
		
		c.test2();
		
		c.p();
	}
}

출력 결과 :

C에서 부름 : Child!!!
Child!!!
부모꺼 호출 : Parent!
P메소드

public class Test12C extends Test12P {
	public String s = "Child!!!";
	
	public void test() {
		System.out.println("C에서 부름 : "+s);
	}
	
	public void test2() {
		System.out.println("부모꺼 호출 : "+super.s);
	}
	
	public void p() {
		System.out.println("갈아엎음");
	}
}

또 덮어씌워짐. (오버라이딩)

출력 결과 :

C에서 부름 : Child!!!
Child!!!
부모꺼 호출 : Parent!
갈아엎음

 

오버라이딩 : 상속관계에서 부모의 내용을 자식에서 재정의 하는 것.

오버로딩 : 이름만 같은 다른 메소드를 여러 개 선언하는 것.

이름만 비슷함..

  오버라이딩
(Overriding)
오버로딩
(Overloading)
위치 상속 관계 한 클래스 내부
하는 일 재정의
(update)
신규
(new)
조건 1. 메소드명, 인자 개수,
    인자 타입, 순서 동일
2. 접근 권한 확장 가능,
    예외 처리 축소 가능.
    ex) protected -> public (O)
          public -> protected (X)
메소드명만 같아야 함.

오버로딩 대표적인 예 : println, 생성자

 

 

형태를 잡고 값을 할당하는 코드인데..

자식 쪽에 더 많은 내용이 들어 있다.

만족시킬 수 있는 만큼의 내용물이 있느냐 없느냐에 따라 객체 생성이 달림.

그래서 마지막 코드가 성립이 안 되는 것.

 

즉, 객체 생성 시 객체 형태를 충족시킬 수 있는 객체여야 한다.

사용할 수 있는 내용을 모두 사용할 수 있는 완성품이어야 함.

public class Test12 {

	public static void main(String[] args) {
		Test12C c = new Test12C();
		
		c.test();
		
		System.out.println(c.s);
		
		c.test2();
		
		c.p();
		
		Test12P p2 = new Test12C();
		//Test12C c2 = new Test12P();  //안 됨
		p2.p(); //아까 오버라이딩 된 거 나옴.
	}
}

출력 결과 :

C에서 부름 : Child!!!
Child!!!
부모꺼 호출 : Parent!
갈아엎음
갈아엎음

 

자식꺼의 주소가 담길 수 있다.

 

 

 

점 찍어보면 내가 만들지 않은 클래스들이 보인다.

자바의 최상위 부모java.lang.Object 클래스

클래스 만들 때 보임.

기본적으로 Object는 별도의 선언을 하지 않더라도 상속을 받기 때문에 생략이 가능하다.

 

C 클래스는 P 클래스를 이미 상속 받은 상황이지만

P 클래스가 Object 를 상속 받았기 때문에 C 클래스 또한 Object를 상속 받고 있다.

즉, Object는 모든 클래스가 상속받고 있다.

 

Object는 어떠한 형태의 객체도 변환이 가능하다. 모두가 다 상속받고 있기 때문.