ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TIL #15 -String, 상속, 오버로드/ 오버라이딩
    프로그래밍/TIL(국비과정) 2020. 4. 15. 12:59

     

     

    #String 관련 메소드들

    문자열을 요모조모 건들이는 메소드들을 보자

    아래는 문자열을 입력하고, 특정 문자열을 바꾸고자 하는 문자열로 바꿔주는 클래스이다. 

    여기서 사용할 메소드는 

    toLowerCase() : 소문자로 바꾸어준다

    indexOf() : 특정 글자를 찾아준다

    replace() : 치환해준다

    이다. 

    먼저 indexOf() 

    str 을 aabbaa 로 지정해 놓고 aa를 cc로 바꾼다고 가정한다. 

    aabbaa에 index를 지정해보면 aa는 0부터 총 2개, 4부터 총 2개가 있다. 

    그렇다면 0부터 하나를 찾게 하고, 0부터 시작한 a가 끝나는 시점 2에서 다시 찾으라고 하면 된다. 

    4부터 찾으라고 할 수 없는 것은 문자열이 엄청 긴 경우 정확하게 aa가 시작하는 지점을 다시 알려주는 것은 힘들기 때문에 찾은 index 뒤부터 다시 찾는 것이 좋다. 

    그럼 코드는 아래와 같다. 

    str.indexOf("aa", 0);

    str.indexOf("aa", 2);

    정리해보면 공식은 아래와 같다 

    str.indexOf("aa", index +aa.length())

    aa.length() 는 찾은 문자열의 길이가 되는 것이다. 

     

    그럼 본격적으로 코드를 본다. 

    여기서 우리는 총 3번의 입력을 받는다. 

    import java.util.Scanner;
    
    class StringMain2 {
    	public static void main(String[] args){
    	
    		Scanner scan = new Scanner(System.in);
    		
    		int index = 0; 
    		int count = 0; 
    		String str, find, change; 
    		
    		System.out.print("문자열 입력 : ");
    		str = scan.next();
    		System.out.print("현재 문자열 입력 : ");
    		find = scan.next();
    		System.out.print("바꿀 문자열 입력 : ");
    		change = scan.next();

    str : 전체 문자열 

    find : 현재 문자열 

    change : 바꿀 문자열 

    현재 문자열이 위에서 본 aa가 되는 것이고 이 aa를 바꾸고 싶은 문자열로 바꿔준다. 

    물론 문자열은 맨 처음에 입력했던 전체 문자열에서 찾는다. 

     

    또 코드가 돌아가면서 치환을 몇번했는지도 보여주고 싶기 때문에  count 변수를 따로 설정했다. 

    또한 문자열을 찾을 때 index로 찾기 때문에 index 변수도 선언해주었다. 

     

    입력을 다 받았으면 찾고자하는 문자열 find가 전체 문자열 str 보다 길면 더 수행할 수 없기 때문에 걸러준다. 

    if(str.length() < find.length()){
    			System.out.println("입력한 문자열의 크기가 작습니다");

    위의 조건이 성립하지 않으면 정상적인 문자열이라는 뜻이므로 본격적으로 문자열을 바꿔본다. 

    		}else {
    			str = str.toLowerCase(); // 입력받은 문자열을 소문자로 바꾸기 
    			find = find.toLowerCase(); // 현재 문자열도 소문자로 

    바꾸고자하는 문자열은 모두 소문자로 설정했기 때문에 toLowerCase를 이용해 문자열을 모두 소문자로 바꾼다. 

    (대문자로 바꾸고자 할 때는 toUpperCase이다)

    문자열을 몽땅 소문자로 바꿨다면 그 문자열에서 찾고자하는 문자열의 index를 검색한다. 

    치환 횟수도 계산한다. 

    while((index = str.indexOf(find, index)) != -1){ 
    			
    				index += find.length();
    				
    				count++;
    				
    			} // while 
    

     만약 index가 -1이면 str에  find 문자열이 없으므로 while 문을 나가 코드가 종료된다. 

    -1이 아니면 문자열이 있다는 뜻이므로 indexOf 메소드로 먼저 0부터 글자를 찾는다. 

    index가 0으로 초기화 되었기 때문에 0부터 검색한다. 

    그런 후 글자를 찾았다면 count++를 해주어 치환 횟수를 더하고 

    index를 찾은 문자열의 길이(find.length())로 해주어 여기서부터 다시 문자열을 찾도록 한다. 

     

    다 찾았으면 index가 -1이 되어 while문을 빠져나가고 

    replace 메소드를 통해 find를 찾아 change로 바꾸는 치환이 이루어진다. 

    그런 후 치환 횟수 count가 출력된다. 

     

    # 대망의 상속

    상속이 힘들면 그 뒤에는 따라가기 정말 힘들다는 말을 듣고 지레 겁을 먹고 있었는데 겁먹길 아주 잘했다는 생각이 든다. 여기서부턴 정신을 더더욱 차리고 복습해야할 것 같다. 

    상속이라나 is ~a 와 같은 의미를 가지고 있다. 

    클래스를 재구현하거나 업그레이드를 한다는 것이다. 

    과거에 전화밖에 안되던 핸드폰이 시간이 지나 점점 촬영, 인터넷 등과 같은 기능이 더해짐에도 

    초반에 있었던 전화의 기능은 그대로 유지하는 것과 같다. 

    자식은 부모의 모든 것을 가진다. 

    즉, 자식은 부모를 참조할 수 있다. 

    하지만 거꾸로는 갈 수 없다. 

    부모는 자식을 참조할 수 없다. 

     

    class 자식 extends 부모 클래스 가 된다. 

     

    아래의 코드를 보자.

    ShapeMain 클래스이다.

    삼각형, 사각형, 사다리꼴 클래스도 있다. 

    각 클래스는 shapeTest 클래스의 것들을 상속받는다.

     

    먼저 ShapTest 클래스이다. 

    class ShapeTest{
    	protected double area; 
    
    	public ShapeTest(){
    		System.out.println("ShapeTest 기본생성자");
    		// 얘는 따로 호출한 적 없지만 본 클래스를 상속받는 자식 클래스들에게 불려 같이 생성된다. 
    	}
    
    	public void calcArea(){
    		System.out.println("도형을 계산합니다");
    	}
    	
    	public void dispArea(){
    		System.out.println("도형을 출력합니다");
    	}
    }
    

     본 클래스에는 calcArea, disArea 메소드가 있다. 

    밑에서 다른 클래스도 더 볼 것이지만, 삼각형 사각형 사다리꼴을 계산하고 출력하는 메소드 또한 calcArea, disArea라는 이름을 가진 메소드이다. 

    이와 같이 상속 시에 부모자식간의 한글자도 빠짐없이 같은 메소드가 생성되는 것을 오버라이드 라고 한다.

    잠깐! 앞서 배운 오버로드와는 어떻게 다를까? 

    우선 오버로드는 굳이 비교하자면 형제관계와 같다. 

    같은 클래스 안에서 메소드 이름만 같고 매개변수 형이나 갯수가 다른 꼴이다. 

    반드시 같은 클래스 내에서 이뤄지는 것이다. 

    하지만 오버라이드 는 다른 클래스에서 이루어진다. 

    다른 클래스에서 메소드의 모든 구성요소가 같아야한다. 

    상속 시에 일어난다. 

    말하자면 부모자식관계와 같다. 

     

    밑으로 나올 삼각형, 사각형, 사다리꼴 클래스에도 calcArea, disArea 메소드를 보게 될텐데 위의 ShapeTest 클래스의

    calcArea, disArea메소드와 구성요소가 완벽하게 같을 것이다. 즉 오버라이딩이다. 

    하지만 유의해서 볼 것이 삼, 사, 사다리 꼴 클래스에서이 calcArea, disArea 메소드를 실행할 때 ShapeTest의 

    calcArea, disArea 메소드가 실행되는지 안되는지 이다. 

    결론부터 말하자면 실행되지 않는다. 

    각자가 가지고 있는 calcArea, disArea 메소드가 실행된다. 

    왜냐하면 우선권은 자식에게 있기 때문에 자식의 calcArea, disArea 메소드를 먼저 실행하기 때문이다. 

    만약 자식에게 calcArea, disArea 메소드가 없다면 부모에게 가서 찾는다. 

    기억할 것은 부모보다 자식, 즉 자기자신이 더 우선적이라는 것이다.

     

    삼각형에 대한 계산과 출력을 하는 SamTest 클래스이다. 

    ShapeTest를 상속했다(extends) 

    class SamTest extends ShapeTest{
    	protected int base, height;
    
    	// 생성자 
    	public SamTest(){
    		System.out.println("SamTest 기본생성자");
    
    		Scanner scan = new Scanner(System.in);
    		System.out.print("밑변 : ");
    		base = scan.nextInt();
    		System.out.print("높이 : ");
    		height = scan.nextInt();
    	}
    	
    	@Override
    	public void calcArea(){
    		area = base*height/2.0;
    	}
    	
    	public void dispArea(){
    		System.out.println("삼각형 넓이 = " + area);
    	}
    	
    }

     역시 calcArea와 dispArea 를 오버라이드 했다. 

    메소드 위의 @Override는 어노테이션으로 오버라이드에 대한 오류를 잡아낸다. 

    만약 내가 calcArea를 그냥 calc 라고 적고 실행을 하고 calcArea를 불러내면 오류메세지 없이 자연스럽게 부모에게 있는 calcArea 메소드를 실행시킨다. 

    하지만 어노테이션을 해주면 오버라이드 되지 않았다는 오류 메세지를 출력시켜준다. 

    (이클립스에서 실행시켜주면 자동으로 해준다.)

     

    아래는 사각형에 대한 계산과 출력을 하는 SaTest 클래스이다. 

    class SaTest extends ShapeTest{
    	protected int width, height; 
    
    	public SaTest(){
    		System.out.println("SaTest 기본생성자");
    
    		Scanner scan = new Scanner(System.in);
    		System.out.println("가로 : ");
    		width = scan.nextInt();
    		System.out.println("세로 : ");
    		height = scan.nextInt();
    	}
    
    	@Override
    	public void calcArea(){
    		area = width * height;
    	}
    
    	public void dispArea(){
    		System.out.println("사각형 넓이 = " + area);
    	}
    }

     

    아래는 사다리꼴에 대한 계산과 출력을 하는 SadariTest 클래스이다. 

    class SadariTest extends ShapeTest{
    	protected int top, bottom, height; 
    
    	public SadariTest(){
    		System.out.println("SadariTest 기본생성자");
    
    		Scanner scan = new Scanner(System.in);
    		System.out.println("윗변 : ");
    		top = scan.nextInt();
    		System.out.println("밑변 : ");
    		bottom = scan.nextInt();
    		System.out.println("높이 : ");
    		height = scan.nextInt();
    	}
    
    	@Override
    	public void calcArea(){
    		area = (top+bottom) * height/2.0;
    	}
    
    	public void dispArea(){
    		System.out.println("직사각형 넓이 = " + area);
    	}
    }

     

    위의 클래스들을 메인 클래스에서 부른다. 

    여기서 우리는 마스터키같은 객체를 하나 만든다. 

    마스터키가 없으면 우리는 일일이 열쇠 하나하나를 찾아서 써야하지만 마스터키가 있으면 일일이 찾을 필요없이 바로바로 문을 열고 들어갈 수 있을 것이다. 

    우선 마스터키를 만들기 전의 코드이다. 

    (아래의 코드는 ShapeMain 클래스의 메인 메소드 안에서 이루어진다)

    		SamTest sam = new SamTest(); 
    		sam.calcArea();
    		sam.dispArea();
    		System.out.println();
            
    		SaTest sa = new SaTest();
    		sa.calcArea();
    		sa.dispArea();
    		System.out.println();
            
    		SadariTest sadari = new SadariTest();
    		sadari.calcArea();
    		sadari.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라는 마스터키가 모든 방의 문(SamTest, SaTest, SadariTest)을 연 것이다. 

    이렇게 마스터키를 만들어주는 것을 다형성(Polymorphism)이라고 한다. 

    여러개의 개별적인 각자의 클래스를 하나의 부모클래스(ShapTest)가 통합 관리하는 것이다. 

    다형성을 통해 통합관리를 해주어 중복적인 작업을 줄일 수 있다.

     

     

    댓글

Designed by Tistory.