Java/기초 튜토리얼

간단한 게임 케릭터 공격 프로젝트로 총정리 해보자.

1. 부모클래스가 될 Hero 클래스를 만들어주자. 게임의 케릭터를 담당할 것이다.

  • 이름변수, 공격메소드 + 생성자를 생성해준다.

2. 각 직업인 Warrior, Archer, Wizard 클래스를 만들어준다.

  • 부모클래스에서 받은 필드(name)를 초기화할 생성자를 만들어준다.
  • 각 직업에 해당하는 기술메소드()를 만들고, 출력문에 해당 기술명을 적어주자.

public class Warrior extends Hero {

    public Warrior(String name) {
         super(name);
        
     }
    
     public void groundCutting() {
         System.out.println("대지 가르기!");
     }

}

3.  메인클래스-메인함수를 만들고 구현해보자.

  1. 먼저 각 직업 클래스들을 담을 Hero클래스의 변수 배열을 만들자(다형성 개념으로, 부모클래스에는 자식클래스 어느것이든 담긴다)
    Hero[] heros = new Hero[3];
  2. 배열의 각 원소에다가 해당클래스 객체를 생성해서 참조해주자.
    heros[0] = new Warrior("전사");
    heros[1] = new Archer("궁수");
    heros[2] = new Warrior("마법사");

  3. for문안에 각 원소들에서 attack()메소드를 사용하도록 해보자. 공통적인 부모클래스의 메소드이므로, for문에서 한번에 해결할 수 있다.
  4. 바로 밑에서는 if문 + < instanceof > 를 이용하여, 부모클래스인 Hero변수안에 있지만, 각 직업클래스의 객체인지 판별하여 –> 
    해당 객체가 맞다면, 그 클래스의 참조변수 temp안에다가 형변환을 해서 넣은 뒤-> 특수기술메소드()를 사용할 수 있게 호출해주자.
    *형변환을 하는 이유는 지금 객체가 <부모클래스인 Hero의 참조변수 의 배열 >속에 있기 때문이다.
    for(int i = 0; i< heros.length; i++) {
       
        heros[i].attack();
       
        if(heros[i] instanceof Warrior) {
            Warrior temp = (Warrior) heros[i];
            temp.groundCutting();
        }
       
    }
  5. 마찬가지로 다른 직업도 같은 작업을 해주고, 출력해보자.

자바 (2)

  1. 2019.11.29 21:07

    비밀댓글입니다

  2. 2019.11.29 21:10

    비밀댓글입니다

Obejct클래스는 모든 객체의 조상인 부모클래스이다. 모든 객체가 암시적으로 extends Object를 가지고 있다고 생각하면 된다.

이런 계층구조를 가지는 이유는, 객체를 만들어내는 모든 클래스가 공통으로 포함하고 있어야 하는 기능을 제공하기 위해서이다.


Object가 모든 클래스의 부모이며, 다형성의 개념으로서, 어느 객체든 변수에 받을 수 있을 뿐만 아니라
어느클래스든 자식클래스가 되므로 (형변환)하여 대입할 수 있다는 개념을 이용해서
비교시 하는 메소드()의 파라미터에 해당 객체형이 아닌 Obejct형으로 받아서, 그것을 형변한 해도 비교가 가능하다는 것을 실습해보자.


1. 같은 클래스지만, 다른 필드값을 가질 객체를 생성할 Archer클래스를 만들어보자.

이름과, 공격력 변수를 만들고, 생성자도 만들어, 사용되는 곳에서 초기화 될 수 있게 하자

2. 다형성을 이용해서, 부모클래스의 참조변수 Object안에 들어있는 객체가 Archer객체로서, 필드값(name, power) 같는지 물어보고
   boolean을 반환하는 equals()메소드를 만들어주자.
  ( 비교할 때, 둘 다 Archer객체를 만들어서 비교할 것임)

확인하는 방법은

  1. 들어오는 Object변수를, 확인해줄 Archer클래스로 형변환 한다. 그러면 Object변수는 Archer의 객체가 된다.
    그러면 자동으로, Archer의 필드인 name과 power를 가지고 있게 된다.
    ( 상위계층(Object) –> 하위계층(Archer)에 대입할 때는 형변환 필수)
  2. Object에서 비롯하여 형변환이 된 Archer객체를 –> Archer의 참조변수 temp 에 넣는다.
    이렇게 object변수를 Archer형으로 형변환할 수 있는 이유는 <더 상위계층, 부모클래스>이기 때문에 가능하다.
    넘어온 매개변수 Object는 암시적으로 자신을 상속한 Archer클래스로 형변환이 가능하다.
  3. if문으로 Archer객체가 가지는 name과 < – > temp속 name이 같은지 물어본다. 동시에 power도 같은지 물어본다.
  4. 같으면 true, 그렇지 않다면 false를 return한다.


public class Archer {
    
     String name;
     String power;
    
     public Archer(String name, String power) {
         this.name = name;
         this.power = power;
        
     }
    
    
     public boolean equals(Object obj) {
         Archer temp = (Archer) obj;
         if( name == temp.name && power == temp.power) {           
            
             return true;   
            
         }else {
            
             return false;
            
         }
        
     }
    
    
}


3. 메인클래스-메인함수를 구현해서, Archer클래스의 객체를 생성하고, 사용해보자.

  1. 2개의 객체를 생성하고, 참조변수에 넣어준다.
  2. 이제 2개의 객체를 비교해주기 위해서,  출력문에 archer1 == archer2 객체를 바로 비교한다. 당연히 false가 출력된다.
    필드값들이 서로 다르게 초기화 되었기 때문에 서로 다른 객체이다.
  3. 만약 초기화값을 같게 해줘도, false가 반환된다. 기본적으로 각각 생성된 객체는 hashCode가 다르기 때문에,
    초기화하여 내부 필드들이 갔더라도 완전이 다른 객체이다.
  4. 이제 Archer클래스의 equals()메소드를 이용해서 비교해보자. 초기화 값이 같다면 true가 반환된다.
    equals()라는 메소드는 같은 Archer객체로서, 들어있는 필드값이 같은지를 물어보기 때문이다.

public class Main {

    public static void main(String[] args) {
        
         Archer archer1 = new Archer("궁수1", "상");
         Archer archer2 = new Archer("궁수1", "상");
         System.out.println(archer1 == archer2);
         System.out.println(archer1.equals(archer2));

    }

}

정리

모든 클래스의 조상은 Object클래스이다. 같은 종류의 객체를 비교하는 메소드를 만들 때,  파라미터에 (Object obj)가 들어있어도
호출시에는 자식이되는 모든 클래스들을 넣어도 상관없다. 대신 형변환을 해줘야 같은 객체의 필드값들이 같은지 비교할 수 있다.

다형성이란 객체를 사용할 때, 변수형태를 바꾸어서 여러타입의 객체를 참조할 수 있다.

결과적으로 이러한 다형성의 개념을 적절하게 이용할 때, 소스코드를 유연하게 구성할 수 있다.

다형성은 <부모 클래스 타입의 참조변수><하위 클래스의 객체를 참조할 수 있게 해준다.>

과일 정보 프로젝트를 구현해보자

1. 부모클래스가 될 Fruit 클래스를 생성하자

  1. 과일의 이름과 가격 , 신선도 변수를 선언해주자.
  2. 변수를 출력할 메소드도 만들어주자.

public class Fruit {

    String name;
     int price;
     int fresh;
    
     public void show() {
         System.out.println("이름 : " + name);
         System.out.println("가격 : " + price);
     }
    
}

2. 자식 클래스가 될 Peach클래스를 생성하자.

  1. 부모클래스의 필드를 초기화 해주자
  2. 만약, 부모클래스안에 부모필드를 초기화하는 생성자가 있다면,  자식클래스에서는 super.( , , ); 초기화 하는 것이 맞다.

public class Peach extends Fruit {
    
     public Peach() {
        
         price=1500;
         name = "복숭아";
         fresh = 75;
        
     }

}

3. 메인클래스와 메인함수를 생성한다.

4. 이제 다형성의 개념으로서, 부모클래스의 참조변수 에다가, 자식클래스의 객체를 생성해서 넣어준다.
   비록 부모클래스의 변수에 넣었지만, 필드나 메소드가 작동하는 것은 new뒤에 붙은 자식클래스의 객체가 본질이다.
   show()메소드는 부모클래스에만 존재하기 때문에, 부모의 메소드를 호출한다. 필요하다면 오버라이딩해서 쓰면된다.

public class Main {

    public static void main(String[] args) {
        
         Fruit fruit = new Peach();
         fruit.show();

    }

}

5. 또하나의 자식클래스 Banana를 만들고, 부모 필드를 초기화해주자.

public class Banana extends Fruit {
    
     public Banana() {
     price = 1000;
     name = "바나나";
     fresh = 80;
     }

}

6. 메인 클래스의 메인함수에서 다시 부모클래스 참조변수에 Banana객체를 넣자.

Fruit fruit2 = new Banana();
fruit2.show();


실제로 어떤 것을 구현할 때, 복숭아인지 / 바나나인지 사용자가 임의적으로 선택할 수 있게 한다.

자식클래스의 인스턴스(객체)를   자신의 변수에다가 넣는 것이 바로 다형성이다.


7. 실제적으로 사용되는 코드로 만들어보자.

  1. 스캐너를 생성한 뒤, 복숭아/ 바나나를 숫자로 입력받도록하자.(입력받을땐 println이 아니라 print로 출력)
  2. 담을 변수는 미리 선언해놓고, 넣을 객체만 if문에 따라 new Banana인지 new Peach인지 구분해서 넣어준다.

public class Main {

    public static void main(String[] args) {
        
        
         Scanner sc = new Scanner(System.in);
         System.out.print("복숭아는 1, 바나나는 2를 입력하세요 : ");
         int input = sc.nextInt();
        
         Fruit fruit;
         if(input == 1) {
             fruit = new Peach();
             fruit.show();
         }else if(input ==2) {
             fruit = new Banana();
             fruit.show();
         }
        
     }

}

다형성 정리

다형성을 이용해서 만약 과일이 아니라, 게임케릭터라고 생각해보면, 입력받은 숫자대로 전사/마법사 등등을 선택 할 수 있다

미리 부모클래스를 만들어, 을 만들어놓은 다음

사용자가 입력한 값에 따라서, 선택된 그리고 완전히 다른 인스턴스 (객체)를 쉽게 바꿔서 대입할 수 있다는 측면에서

유동적으로 자기자신의 변수에 불러오므로, 내용도 유동적으로 바꿔줄 수 있다.

자바














추상클래스와 비슷한 개념으로서,  추상같은 경우, 어느 클래스안에 어느 메소드가 사용될지 암시해주는 설계제공이었다.
인터페이스 같은 경우, 더 선호되는 설계기능으로, 비록 같은 추상메소드를 가지고 있으나, 차이점은

  1. 다중 상속이 가능하게 한다.
  2. 사전에 정의된 상수만 가질 수 있다.(추상클래스는 필드를 여러개 가질 수 있고, 상수가 아니어도 된다)
  3. 사전에 정의된 추상메소드만 가질 수 있다.(추상클래스는 추상메소드 이외에 다른 일반 메소드들도 가질 수 있다. 따라서 일반메소드의 body{}도 채워놓을 수 있다.)
    추상클래스보다 요구되는 설계의 기준이 더 높아서 더 체계적이라 할 수 있다.


[1] 인터페이스를 선언하고 메소드를 다루어보자.


1. Dog라는 인터페이스를 만들고, abstract로 추상메소드를 만들자(추상메소드를 만드는 과정은 추상클래스와 동일하다)
public interface Dog {
    
     abstract void crying();
     public void show();
    

}


2. 메인클래스, 메인함수를 만들어서 인터페이스를 상속(implements)해서 구현화해보자.  반드시 구현화할 메소드들을 오버라이딩해야한다.

public class Main implements Dog{

   
     public static void main(String[] args) {

    }

    @Override
     public void crying() {
         // TODO Auto-generated method stub
        
     }

    @Override
     public void show() {
         // TODO Auto-generated method stub
        
     }

}

3. 재정의로 구현화할 메소드 내용을 채워준다.

4. 메인클래스에서 상속했으니, static인 메인메소드에서 사용하려면, Main 객체를 만들어서 사용해야한다.

public class Main implements Dog{

   
     public static void main(String[] args) {
        
         Main main = new Main();
         main.crying();
         main.show();
        

    }

    @Override
     public void crying() {
        
         System.out.println("월 월!");
        
     }

    @Override
     public void show() {
         System.out.println("Hello World!");
     }

}


[2] 인터페이스의 다중 상속을 학습해보자.


  -  2개의 추상클래스를 만들어서, 2개를 모두 상속하면 오류가 난다.

1. Dog 인터페이스를 복사해서 , Cat 인터페이스를 만들어보자.

2. 메인클래스에서 implements Dog, Cat 으로 2개의 인터페이스를 다중상속해보자.

3. 반드시 오버라이딩해야하는 인터페이스의 메소드들을 추가해보자.
   만약 2개의 인터페이스의 추상메소드가 겹칠 경우, 하나만 구현화해줘도 문제가 없다.

public class Main implements Dog, Cat{

   
     public static void main(String[] args) {
        
         Main main = new Main();
         main.crying();
         
         main.dog();
         main.cat();
        

    }

    @Override
     public void crying() {
        
         System.out.println("월 월!");
        
     }


     @Override
     public void cat() {
         System.out.println("고양이 입니다.");
     }

    @Override
     public void dog() {
         System.out.println("개 입니다.");
        
     }

}

절대로 변하지 않는 특정한 것을 사용할 때 final을 사용한다. 변수 메소드 클래스에 모두 사용할 수 있다.
변수의 경우, 변하지 않는 상수
메소드의 경우,  상속한 자식클래스에서 오버라이딩(재정의)가 불가능한 메소드
클래스의 경우, 상속이 불가능한 완전한 클래스가 된다.

[정리] final을 사용해서, 더이상 바뀌지 않게 보호하는 기능을 한다고 할 수 있다.

cf) 인터페이스의 필드는 모두 final이 생략되어있다.

[1] final 상수를 만들어보자.

1. final 변수(상수)를 만들어보자. 이 때, 어떠한 수를 다시 대입하면 오류가 난다.

        final int number = 10;
         System.out.println(number);
        
         number=5; //오류가 난다.

[2] final 메소드를 만들어보자.(1 ~3 까지는 final이 아니라서, 재정의된 오버라이딩 메소드를 호출한다.)

1. Parent클래스를 만들고, 출력문을 가진 메소드 show()를 만들자.

public class Parent {

    public void show() {
        
         System.out.println("Hi from Parent");
        
     }   
    
}

2. 메인클래스에서 Parent 클래스를 상속받고, (STATIC 메인메소드에서도, 재정의한 메소드를 호출할 수 있게)메인클래스 객체를 생성해서,
   부모parent에 있는 show()메소드를 이용해보자.

public class Main extends Parent{
     public static void main(String[] args) {
         Main main = new Main();
         main.show();       
     }
}

3. 이제 부모클래스의 show()를 오버라이딩 해서, 재정의한 show();가 호출되도록 해보자.

public class Main extends Parent{

    @Override
     public void show() {
         System.out.println("hello from Main");
     }

    public static void main(String[] args) {

        Main main = new Main();
         main.show();
        
     }
}

4. 이제 Parent 클래스의 show()메소드에 final을 붙혀보자. -> 자식인 Main클래스에서 오버라이딩 한 곳에서 오류가 난다.
   ***final이 붙은 메소드는, 부모클래스의 것이더라도 자식이 오버라이딩해서 사용 못한다!

    public final void show() {       
         System.out.println("Hi from Parent");       
     }
    


[3] 클래스에 final을 붙혀보자.

1. Parent클래스에 public을 빼고, final class로 바꿔보자.  => 상속한 자식인 Main클래스에서 오류가 난다.
  *** final이 붙은 클래스는, 다른 클래스에서 더이상 상속하지 못한다.

MY) 완전한 설계도이므로, 객체를 생성해서 사용할 수 밖에 없다. 상속할 자식은 더이상 존재할 수 없음.

[정리] final을 사용해서, 더이상 바뀌지 않게 보호하는 기능을 한다고 할 수 있다.

객체지향을 본격적으로 활용하기 위해, 추상(Abstract)와 인터페이스(interface)의 개념이 존재한다.

  • 자바에서는, 일종의 미완성 클래스라고 할 수 있는 추상(Abstract)클래스를 제공하는데, 추상클래스는 직접적으로 객체(인스턴스)를 생성할 수 없지만,
    < 새로운 클래스(추상클래스를 상속하는 자식 클래스) 를 작성하는데 밑바탕이 되는 역할을 해준다는 것>에 의미가 있다.
    예를 들어, 사각형/ 원/ 삼각형 이라는 클래스는 <공통적으로 넓이()와 모양()>이라는 메소드가 필요하다.
    그것을 반드시 구성할 수 있도록 추상 클래스에 설계해놓고 알려준다.
    - 규칙 : 추상클래스 꼭 상속받아야 하며, 모든 추상메소드는 자식클래스에서 구현이 되어야한다.


[1] 추상의 개념을 이용해서, 음악 플레이어 클래스를 구현해보자.

  1. Player클래스를 만들어보자. 그리고 접근제한자 public 대신 <abstract>로 바꿔서 추상클래스로 바꾸자.
  2. 안에 추상메소드를 넣어줘야한다. 인터페이스처럼 body{}가 없다.3가지 매소드를 넣어주자.
    abstract class Player {
       
         abstract void play(String songName);
         abstract void pause();
        abstract void stop();
  3. }


  4. 메인클래스를 만들고 추상클래스를 상속(extends Player)하자.
    *추상 클래스를 상속했다면, 그 안의 추상 메소드들은 모두 implements해야한다. 오류가 뜨는 것을클릭해서 메소드들을 구현하자.
      -> 오버라이딩 되면서, 추상클래스들의 추상 메소드들이 implements된다.


    @Override
     void play(String songName) {
         // TODO Auto-generated method stub
        
     }

    @Override
     void pause() {
         // TODO Auto-generated method stub
        
     }

    @Override
     void stop() {
         // TODO Auto-generated method stub
        
     }

4. 이제 비어있는, 추상 메소드들을  구현화하자.
     @Override
     void play(String songName) {
         System.out.println(songName+"곡을 재생합니다");       
     }

    @Override
     void pause() {
         System.out.println("곡을 일시정지 합니다");       
     }

    @Override
     void stop() {
         System.out.println("곡을 정지합니다");
     }

5. 메인메소드에서 구현화한 추상메소드들을 이용해보자. 이 때, 그냥 평소 호출하듯이 play("");를 호출하면, 오류가 뜬다. 그 이유는 메인메소드가 static이기 때문이다.
     클래스의 것으로, 클래스에서만 사용할 수 있도록 만든 static 메소드 내부에서는 반드시 호출되는 메소드들도 static이어야만 한다.*****
  ==> 이러한 것을 해결하는 방법으로, 호출하는 메소드에 static을 붙혀줄 수 도 있지만,
      ****< 메인클래스의 객체를 생성한 뒤, 객체.를 이용해서 호출하면 static이 붙은 메인메소드안에서 호출이 가능하다.>****

    public static void main(String[] args) {

        Main main = new Main();
         main.play("따르릉 by 홍진영");
        
     }

6. 다른 메소드 2개도 호출하여 실행해본다.


public class Main extends Player {

    public static void main(String[] args) {

        Main main = new Main();
         main.play("따르릉 by 홍진영");
        
         main.pause();
         main.stop();
        
     }

    @Override
     void play(String songName) {

        System.out.println(songName+"곡을 재생합니다");
        
     }

    @Override
     void pause() {

        System.out.println("곡을 일시정지 합니다");
        
     }

    @Override
     void stop() {

        System.out.println("곡을 정지합니다");

    }

}


[2] 추상의 개념을 이용해서 동물 클래스를 구현해보자.


1. Animal클래스를 만들고, 추상클래스로 바꾸자. 그리고  abstract void crying(); 메소드 하나를 넣어주자.

2. Dog클래스를 만들고 Animal 추상클래스를 상속하자. 추상메소드를 가져와서, 개가 우는 방식으로 구현화하자.

public class Dog extends Animal{

    @Override
     void crying() {
         System.out.println("월 ! 월 !");
     }

}

3. 마찬가지로 Cat 클래스를 만들고 추상클래스를 상속해서, 추상메소드를 구현화해주자.

4. 메인클래스에서, dog/cat객체를 만들어서, crying()을 호출해준다.
  - [1]에서는 메인클래스가 직접 추상클래스를 상속해서 이너메소드로 추상메소드가 오버라이딩 되었다. 하지만 이너메소드이면서 static메소드인 메인메소드에서 nonstatic 이너메소드를 호출 할 수없으니, 메인객체를 만들어서, 외부에서 호출하는 것처럼 만들었다.
    하지만, 이번에는 메인클래스에서 추상클래스를 상속한 것이 아니므로, 메인클래스의 객체를 생성할 필요가 없다.

    public static void main(String[] args) {

        Dog dog = new Dog();
         Cat cat = new Cat();
         dog.crying();
         cat.crying();
        
     }



[정리] 이렇게 추상클래스를 상속함으로써, 반드시 구현화해야하는 추상 메소드들을 알려준다. 사용자는 상속한 자식클래스에서 꼭 implements를 통해 오버라이딩해서 구현화해야하니 , 필요한 메소드들을 미리 알림을 받을 수 있고, 설계적인 측면에서 효율적으로 가져오면서, 안빠트리고 사용할 수 있다.

미리 만든 [Person - 이름, 키, 나이, 몸무게 ]클래스를 상속하면, 

아래 자식클래스들[ Student - 학점, 학번, 학년 + (상속)이름, 키,나이,몸무게 ] , [ Teacher - 교직원 번호, 월급, 년차  + (상속)이름, 키,나이,몸무게] 를 만들 때,  상속해서 그 속성을 쓸 수 있다. 불필요한 코드를 줄여서, 효율적으로 코딩할 수 있다.




[1] 부모클래스(4가지 변수)를 만들고 자식클래스(3가지변수 + 부모 4가지 변수)에서 상속해보자.


1. 사람의 특징을 가진 [Person]클래스를 만든다.( private 필드 -> getter/setter -> 생성자 -> 그외 매쏘드 순)

(1) 각각 private로 필드들을 만든다. 

(2) 보안private필드에 접근할 수 있는 get/set()매쏘드를 만든다.(이번에는 한번에 만들 수 있는 자바기능을 이용한다)

  *** 클래스 내부에서 우클릭 > [ Source ]  >  [ Generate getter / setter ]

  cf) 외부에서 꺼내쓰는 int get() / 내부에 입력하는 void set(int x)


(3) 각 필드(private 필드=멤버변수)에 대한 생성자를 만든다.  우클릭 > source > Generate Constructor using Field

 *** 이 때 super()라는 것은, 부모클래스의 생성자를 호출해서 초기화 시키겠다는 의미이다. 

       그러나, Person은 가장 상위 클래스로, 아직 상속한 부모클래스가 없기 때문에, 비어있다.


public class Person {

private String name;

private int age;

private int height;

private int weight;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public int getHeight() {

return height;

}

public void setHeight(int height) {

this.height = height;

}

public int getWeight() {

return weight;

}

public void setWeight(int weight) {

this.weight = weight;

}

public Person(String name, int age, int height, int weight) {

super();

this.name = name;

this.age = age;

this.height = height;

this.weight = weight;

}


}


2. Person이라는 클래스를 상속하는 Student클래스를 만들자.

(1) 클래스를 만들고, extends Person 으로 상속하자.

(2) 학생클래스에 필요한 필드를 만들어주자.

(3) getter/setter를 만들고

(4) 생성자를 만들어주자. 이때, Student클래스에 만든 필드들 이외에, 

    상속한 Person클래스의 필드(name, age, height, weight)들까지 입력되어있다.*******

 - 개발의 편의성을 위해, 자신이 상속한 부모클래스의 필드들까지 한꺼번에 초기화 해주기 위한 것이다.

 - super( 파라미터)부모가 가진 생성자를 호출해서, 초기화 한다는 의미이다.****


public Student(String name, int age, int height, int weight, String studentID, int grade, double gPA) {

super(name, age, height, weight);

this.studentID = studentID;

this.grade = grade;

GPA = gPA;

}

(5) 이제 추가적으로 Student클래스 내부에 show() 라는 매쏘드도 만들어주자. 출력시 get변수명()으로 꺼내쓰자.

  *** 이때 Person클래스 내부 필드인 name을 getName()할 때는, 상속받은 상태이므로 Person.getName()이 아니라 getName()으로 바로 사용할 수 있다.

public void show() {

System.out.println("-----------------------------");

System.out.println("학생 이름 : " + getName());

System.out.println("학생 나이 : " + getAge());

System.out.println("학생 키 : " + getHeight());

System.out.println("학생 몸무게 : " + getWeight());

//위에는 상속받은 Person클래스의 매쏘드를 바로 사용함

System.out.println("학생 학번 : " + getStudentID());

System.out.println("학생 학년 : " + getGrade());

System.out.println("학생 학점 : " + getGPA());

System.out.println("-----------------------------");

}



3. 이제 메인클래스를 만들고 메인함수에서 객체를 만들고, show()매쏘드를 호출해보자.



public class Main {


public static void main(String[] args) {


Student student1 = new Student("조재성", 32, 163, 55, "1111048", 3, 4.1);

Student student2 = new Student("김석영", 31, 167, 47, "1311051", 3, 3.8);

student1.show();

student2.show();

}


}


4. 출력물을 확인해보자.

-----------------------------

학생 이름 : 조재성

학생 나이 : 32

학생 키 : 163

학생 몸무게 : 55

학생 학번 : 1111048

학생 학년 : 3

학생 학점 : 4.1

-----------------------------

-----------------------------

학생 이름 : 김석영

학생 나이 : 31

학생 키 : 167

학생 몸무게 : 47

학생 학번 : 1311051

학생 학년 : 3

학생 학점 : 3.8

-----------------------------


[2] 또하나의 클래스를 만들고 출력해보자. 


1. Teacher클래스를 만들고, extends Person을 해보자.

(1) 3가지 private필드를 만들고 -> 외부에서 접근할 수 있는 public 겟셋터 -> 생성자까지 만들자.

(2) show()함수를 만들어서 3+ 부모4 총 7가지 변수를 출력해주자.


2. 메인클래스 메인함수로 가서, Teacher객체를 만들면서 초기화해주고, 출력도 해보자.

Teacher teacher1 = new Teacher("이숭인", 38, 171, 55, "0000000", 400, 5);

teacher1.show();

------------------

교사 이름 : 이숭인

교사 나이 : 38

교사 키 : 171

교사 몸무게 : 55

교직원 번호 : 0000000

교사 월급 : 400

교사 연차 : 5

------------------



[3]배열을 이용해서 많은 객체들을 관리해보자.


1. Student클래스 객체100개를 배열로 생성하자.

 *** 객체배열을 만들때는, 생성자호출이 아니므로 new Student() [ 배열 ] ; 이 아니라, new Student[ 배열 ]; 로 우항을 적는다.  

     생성자로 초기화하는 것은 따로 해줄 것이다.


Student[] students = new Student[100];


2. for문을 이용해서 100명의 학생들을 모두 [ 생성자를 이용한 초기화 ]를 한번해 해주자. 대신 학번만 다르게 주자.

- 학번은 숫자가 하나의 단어로 인식되는 String 문자열이다. index인 i를 쓰더라도 뒤에 [ + "" ] 로 끝나야한다.( i + "" )


for(int i=0;i<100;i++) {

students[i] = new Student("홍길동 "+i+"번째", 20, 175, 70, i + "" , 1, 4.5 );

students[i].show();

}



3. 출력값 


for(int i=0;i<100;i++) {

students[i] = new Student("홍길동 "+i+"번째", 20, 175, 70, i + "" , 1, 4.5 );

students[i].show();

}



[4] Scanner로 실제 입력을 받아서 객체를 초기화하여, 출력해주자.

1. Scanner 변수를 생성해서, 콘솔창에서 총 학생수를 입력받고, 그 숫자로 배열을 만들자.


Scanner sc = new Scanner(System.in);

System.out.print("총 몇명의 학생이 존재합니까?(배열수) : ");

int number = sc.nextInt();


2. for문을 만들고, 각 학생들마다 입력받을 정보들을 Student클래스의 생성자의 파라미터에서 복사해와서 가져온 뒤

   임시적으로 변수로 선언하여 받은 다음, 생성자로 초기화 해준다.

< 객체 생성시, 생성자로 초기화해줄 파라미터는, 클래스의 생성자부분의 파라미터를 복사해와서 편하게 작성할 수 있다>


(1) Student 클래스에서 생성자의 파라미터에 각 변수들을 복사해오자

public Student(String name, int age, int height, int weight, String studentID, int grade, double gPA)


(2) 그것을 for문 안에 변수선언으로서 수정하자.


for(int i = 0 ; i <number ; i++) {

String name;

int age;

int height;

int weight;

String studentID;

int grade;

double gPA;


}

(3) 출력문 + sc.nextInt() 등 7가지를 만들어서, 각 변수에다 대입해준다.

 *** 이때, String은 sc.nextString()이 아니라 , sc.next()이다! / double 은 sc.nextDouble()


for(int i = 0 ; i <number ; i++) {

String name;

int age;

int height;

int weight;

String studentID;

int grade;

double gPA;


System.out.print("학생의 이름을 입력하세요 : ");

name = sc.next();

System.out.print("학생의 나이를 입력하세요 : ");

age = sc.nextInt();

System.out.print("학생의 키를 입력하세요 : ");

height = sc.nextInt();

System.out.print("학생의 몸무게를 입력하세요 : ");

weight = sc.nextInt();

System.out.print("학생의 학번을 입력하세요 : ");

studentID = sc.next();

System.out.print("학생의 학년을 입력하세요 : ");

grade = sc.nextInt();

System.out.print("학생의 학점을 입력하세요 : ");

gPA = sc.nextDouble();

}


(4) 입력받은 값을 가진 변수들을 이용해서, 생성자를 호출하여 각 배열[i]에 new Student(~ , , , ,~)로 초기화해준다.

*** Student 클래스의 파라미터를 다시 복사해 온 뒤, 자료형을 다 지워주면 쉽게 생성자호출을 할 수 있따.


my) 생성자의 파라미터에 Model타입에 직접 값을 입력해주는 것이 아니라, 생성자의 변수명과 똑같은 변수를 선언한 뒤, 입력받아서 변수에 넣고,

      생성자의 파라미터를 활용해서 자료형만 지워주면, 바로 대입하는 식을 만들 수 있다.


students[i] = new Student(name,age,  height,  weight,  studentID, grade, gPA);



3. 출력도 for문을 써서 show()로 출력해준다.


for(int i = 0; i < number ; i++) {

students[i].show();

}



4. 출력값을 보자.


총 몇명의 학생이 존재합니까?(배열수) : 3

학생의 이름을 입력하세요 : 조재성

학생의 나이를 입력하세요 : 20

학생의 키를 입력하세요 : 19


-----------------------------

학생 이름 : 조재성

학생 나이 : 20

학생 키 : 19

  1. 박카스 2019.02.15 16:10

    혹시 안드로이드에서도 상속이 쓰이나요??

    혹시 안된다면 xml이 없는 class에서도 startactivity를 할 수 있는 지 여쭤 보고 싶어요

    • nittaku 2019.02.15 16:34 신고

      안드에서 상속을 쓰려고 공부했어요. 함수에 추가 기능을 넣을때요. 근데 제가 한 개인프로젝트에서는 못써봤어요!

    • nittaku 2019.02.15 16:36 신고

      안드가 현재는 코틀린으로 가고있으나 자바입니다. 다 가능합니다!

  2. ㅇ_ㅇ 2021.09.03 17:29

    잘 봤습니다. 감사합니다.

단축키) [Alt + Shift + S ] : 겟셋터/생성자 자동생성해주는 메뉴를 띄운다(안드로이드의 [alt + insert ]와 동일)


객체지향 프로그래밍에 있어서 가장 기본이 되는 것으로, 가장 대표적으로 많이 사용되는 것이 Node클래스이다. 이는 하나의 장소나 위치를 의미할 수 도 있으며 자료구조에서 말하는 이진 탐색트리의 하나의 서브 트리가 될 수도 있다. 

개발프로젝트에서는 종종 Student클래스와 같이 < 하나의 처리할 데이터 단위를 명시> 하는데 사용된다.


클래스는 공장, 객체는 물건이라고 생각하면 좋다.  new Car();와 같이 객체를 만드는 과정을 <인스턴스 화>라고 한다.

클래스에서 만들어진, 사용가능한 변수를 <인스턴스>라 한다.

my) 인스턴스 = 객체형 변수


[클래스 구조 정리]

클래스내부에서는 보안관계상 private으로 변수(필드, 멤버변수)들을 선언한다.

외부에서 접근이 바로 안되므로, public getter/setter를 만든다.

기본적으로 외부에서 객체를 만들 때, 초기화하는 생성자도 만든다.
그외 필요한 메소드를 만든다.




[1] 하나의 점을 의미하는 Node 클래스를 생성하자


1. 이번에는 클래스가 2개가 필요하다. 기존에 만든 Main클래스( 메인함수 포함)을 만들고 + Node라는 클래스도 생성한다.


2. Node클래스에서 private 형 변수 2개를 생성한다( 클래스 필드(멤버변수)는 private으로 생성하는 것이 좋다)

 *** 보안을 굉장히 중요시 하므로, private 형태로 만들어서, < 클래스 외부에서 함부로 변형이 안되도록 만든다>

public class Node {

private int x;

private int y;


}


3. public int getX() 라는 매쏘드를 만들고, return x를 통해 외부에 x를 반환해준다. (get은 외부에서 꺼내는 반환형 get - return private 필드)

 *** [ private 변수 ] 는 자체적으로는 외부에서 접근이 안된다. 대신 [ public 반환형 get 매쏘드 ]를 만들어서  클래스 내부의 

      private필드 x --> 외부에서 꺼내는 get하도록 반환 해주는 매쏘드다.


public int getX() {

return x;

}


4. public void setX(int x)라는 매쏘드를 만들고, x를 받아서 클래스의 필드인 [ private int x = this.x ] 에다가 값을 set해준다. 

   (set은 내부에 넣어주는 void set(파라미터) - this.필드 = 파라미터; )

 ***  [private 변수 ] 에 외부에서 받은 값을 대입해주기 위해서 [public void set (int x) 매쏘드]를 만들어서  외부의 x --> 클래스 내부의 x에 set하도록 입력해주느 매쏘드다.


public void setX(int x) {

this.x = x;

}


5. y도 마찬가지로 복사해서 만들어준다.


6. 이제 생성자를 만들어준다. 이때, this.x, this.y에다가 넘어온 값을 넣어줘야한다.( 생성자는 new 객체 만들 때, 필드 초기화해주는 것)


 - 생성자는 메인함수에서 객체(인스턴스)를 만들어줄 때, 직접 클래스필드(멤버변수)에 값을 넣어 초기화해주는 것이다. 

public Node(int x, int y) {

this.x = x;

this.y = y;

}


7. 파라미터로 Node객체를 받아서, 초기화된 필드 x,y를 기준으로 <중앙좌표를 계산>한 뒤 new Node( x좌표, y좌표)를 만듬과 동시에 를 반환하는 , 

  public Node getCenter(Node other)를 만들어준다.


(1) 자신의 x좌표 [ this.x ] 와  넘어오는 다른 곳의 Node other의 x좌표를 의미하는 [ other.getX() ] 를 더한 뒤 2를 나누어서 중앙 x좌표를 구하고

  * other도 Node클래스의 객체이므로, getX()메소드를 사용할 수 있다.

(2) y도 똑같이 구한 다음

(3) new Node( 중앙x, 중앙y); 객체를 생성함과 동시에, 변수에 안넣어주고 바로 return한다.


public Node getCenter(Node other) {

return new Node(  (this.x + other.getX())/2  ,(this.y + other.getY() )/2 );

}


my)

****** priavate 변수로 클래스 필드의 보안을 만들고, public 외부에서 꺼내는 get() / 내부에 넣어주는 set() 매쏘드를 만들어서 소통하는구나!

****** 생성자는, 필요한 곳에서 객체를 만들때, 클래스 필드(멤버변수)에 값을 넣어 초기화 해주는 것이다.

****** new Car(); 는 기본생성자를 이용한 것이다. 만약 기본생성자가 없다면,, 생성안된다.(안드로이드는 컴파일시 자동생성 되기도 한다)



[2] Node클래스를 이용하여 두 점 사이의 중점을 구하는 프로그램을 작성해보자.


1. 메인함수에서 생성자를 이용해서, 새로운 객체를 만들자. -> one이라는 객체는 생성자를 통해 priavte 변수(필드)인 x와 y에 10, 20을 가지고 있다.

Node one = new Node(10, 20);

2.  two라는 객체를 만들자.

3. 이제 one이라는 node를 기준으로해서 two와의 중앙점을 구하자. getCenter()는 기준Node인 one. getCenter ( 비교할 Node); 로 사용하는 것이다. 그것을 Node result에 넣어주자.

Node one = new Node(10, 20);

Node two = new Node(30, 40);

Node result = one.getCenter(two);

4. result라는 Node객체에서 getX(), getY()를 이용해 좌표들을 출력해보자.


System.out.println("중앙 x : "+ result.getX());

System.out.println("중앙 y : "+ result.getY());




5. 전체코드


[Node.java]

public class Node {

private int x;

private int y;


public int getX() {

return x;

}

public void setX(int x) {

this.x = x;

}


public int getY() {

return y;

}

public void setY(int y) {

this.y = y;

}

public Node(int x, int y) {

this.x = x;

this.y = y;

}

public Node getCenter(Node other) {

return new Node(  (this.x + other.getX())/2  ,(this.y + other.getY() )/2 );

}

}




[Main.java]


public class Main {


public static void main(String[] args) {


Node one = new Node(10, 20);

Node two = new Node(30, 40);

Node result = one.getCenter(two);

System.out.println("중앙 x : "+ result.getX() + " 중앙 y : "+ result.getY());

}


}

배열이 배열의 원소에 들어가는 구조로, 이차원 배열은 M X N 구조를 가진다.


a[0][0]  a[0][1] ...

a[1][0]  a[1][1] ...


앞[]에 배열이 , 뒤에 배열[]을 의미한다.

그냥 단순배열 a[]에서는 열을 의미한다고 생각한다.



[1] 50 X 50의 이차원배열에 0~9사이의 랜덤 정수를 생성해서 전체 데이터를 분석해보자


1. 배열크기를 의미하는 상수하나를 선언한다.

int N = 50;


2. int형 2차원 배열을 선언하고, 1에서 만든 상수를 배열의 크기에 집어넣는다.

int[][] array= new int[N][N];


3. 2차원 배열을 처리할 때는, 이중 for문 (중첩 for문)을 사용한다. ( 행 > 열 순서로 for문 순서를 지정한다.) 

    index 0번째 행에서--> 0~49까지 각 열이 채워준다. 그걸 50번 반복


4. 이제 0부터 9까지 사이의 랜덤한 수를 배열에 집어넣는다 ( 랜덤 수 0<= x<1  에 10을 곱해서 0<=x<10 을 정수형으로)


for(int i = 0; i<N ; i++) {

for(int j = 0; j<N ;j++) {

array[i][j] = (int)(Math.random() * 10 );

}

}


5. 이제 집어넣은 50 X 50 2차원 배열을 출력할 때도 이중 FOR문을 써야한다.

 (1) 열에 있어서는, print 로 배열[i][j] + "  "; 로 빈칸을 두도록 출력하고

 (2) 행에 있어서는, println으로 한줄씩 내려가도록 만든다.


** 이차원 배열은 2d게임의 좌표설명 / 미로찾기 등을 개발할 때 많이 사용된다.



public class Main {


public static void main(String[] args) {


int N = 50;

int[][] array= new int[N][N];

for(int i = 0; i<N ; i++) {

for(int j = 0; j<N ;j++) {

array[i][j] = (int)(Math.random() * 10 );

}

}

for(int i = 0; i < N; i++) {

for(int j = 0; j < N ; j++) {

System.out.print(array[i][j] + " ");

}

System.out.println();

}

}


}


참고) [ Ctrl + Shift+ O ] : import 단축키


배열의 선언방법

int[] array  = new int[100]; 

 - 여기서 new라는 것은 어떠한 인스턴스를 만들 때 쓰는 약속 예약어이다.

 - 대괄호[]로 배열을 선언하고, 배열의 크기를 우항에 넣는다.

 - 변수 array에는 100개의 배열을 가진다.

 - index라는 것은 0부터 출발해서, 99까지 차지한다. (크기 -1까지)





[1] 원하는 개수만큼 배열을 생성 하고, 입력한 다음, 최대값을 구하는 프로그램을 작성해보자.


1. 최대값을 반환하는 max()매쏘드를 하나 만들어준다. 

public static int max(int a, int b) {

return a>b?a : b;

}

2. 메인함수 안에 Scanner 를 선언한다. sc를 이용해서 사용자에게 배열의 크기를 입력받을 것이다. print도 찍어준다.

3. sc를 이용해서 정수를 받고, int number에 입력 받는다. 입력받을 변수를 이용해서 배열을 만든다.

4. for문을 통해 0부터 number-1까지 1씩 증가시키면서 index값으로서 사용하며, for문 안에서는 배열을 입력받고 그것을 배열안에 넣어준다.

5. int result =-1;을 선언하고, for문을 돌리면서,  max()함수를 이용해 최대값이 들어가는 result 와  각 배열 index[0]부터 [number-1]까지 최대값을 비교해서 result에 집어넣어준다.

6. 최대값 result를 출력한다.


import java.util.Scanner;


public class Main {


public static int max(int a, int b) {

return a>b?a : b;

}

public static void main(String[] args) {


Scanner sc = new Scanner(System.in);

System.out.print("생성할 배열의 크기를 입력하세요 : ");

int number = sc.nextInt();

int[] array = new int[number];

for(int i = 0; i <number ;i++) {

System.out.print("index["+i+"]"+"번째 배열에 넣을 값을 입력해주세요(양수) : ");

array[i] = sc.nextInt();

}

int result = -1;

for (int i = 0; i<number ; i++) {

result=max(result, array[i]);

}

System.out.println("배열 중 최대값 : "+result);

}


}



7. 출력값

생성할 배열의 크기를 입력하세요 : 5

index[0]번째 배열에 넣을 값을 입력해주세요(양수) : 1

index[1]번째 배열에 넣을 값을 입력해주세요(양수) : 10

index[2]번째 배열에 넣을 값을 입력해주세요(양수) : 54

index[3]번째 배열에 넣을 값을 입력해주세요(양수) : 3

index[4]번째 배열에 넣을 값을 입력해주세요(양수) : 2

배열 중 최대값 : 54



[2] 100개의 랜덤 정수의 평균을 구하는 프로그램을 작성해보자.

1. 100개짜리 배열을 선언하고, for문에는 index만큼 0부터 99까지 돌린다.

2. 각 배열[index]마다, Math.random() * 100 + 1 을 넣어준다. 이 때 double이므로, int로 캐스팅 해야한다. 

   이때, Math.random() 0 <= x < 1 사이의 랜덤한 수를 가져오기 때문에, 100을 곱해서 0<= 100x< 100 가 되고, +1을 더해서 1<=x<101 이게 된다. 이제 형변환으로 정수가 나오게 된다.


3. for문을 돌려서 array[i] 100개를 모두 sum에 넣은 뒤에, 그 값을 출력해준다.


int[] array= new int[100];

for(int i = 0;i<100;i++) {

array[i] = (int) (Math.random() * 100 + 1);

}

int sum = 0;

for(int i = 0; i<100;i++) {

sum+=array[i];

}

System.out.println("100개의 랜덤 정수의 평균 : "+ sum/100);;

}


9. 반복함수와 재귀함수(팩토리얼/ 피보나치 수열) ( Tutorial10, 11)


반복함수는 while 혹은 for문법을 이용해서 문제를 해결하는 함수다.

재귀함수는 자신의 함수 내부에서 자기 자신을 스스로 호출함으로써 재귀적으로 문제를 해결하는 함수다.


(Tutorial10)


[1] 반복함수로 팩토리얼을 구현해보자


1. int를 하나 받아서 int를 반환하는 팩토리얼 함수를 만들자.

 -  로컬 변수 sum에 1을 넣어 초기화 한 다음, for문을 통해서 sum에다가 2부터 i를 받은 숫자까지 곱해서 넣어주자.


//5! = 5 * 4 * 3 * 2 * 1

public static int factorial(int number) {

int sum = 1;

for ( int i = 2; i<=number ; i++) {

sum *= i;

}

return sum;

}


2. 메인함수에서 출력시켜보자

public static void main(String[] args) {

System.out.println("10팩토리얼은 "+factorial(10)+ "입니다.");

}




[2] 재귀함수로 팩토리얼을 구현해보자.


1. 받은 값이 1일 때만 예외적으로, 1을 return해서 마무리 짓는다. 

   왜냐하면, 속에 똑같은 매쏘드()가 들어가는데, -1씩 줄여서 대입할 것이기 때문에, 마지막 1은 바로 1을 리턴하도록 한다

   재귀함수는 계속 자신을 호출하므로, 마지막에는 상수로 마무리대응 해야한다.


2. return할 때, 받은 수 * 매쏘드( 받은수 -1 ); 을 반환해서, 받은수 -1 이 또 한바퀴 돌도록 한다. 1이 되면 그냥 1이 return되서 끝난다.

 - 핵심은 -1 씩 줄여가다가 마지막 종점 1에서는 재귀함수를 호출 안하고, 값 1을 반환해서 끝낸다.

 - 핵심은 5!  =  5 * 4!로 같으며, 팩토리얼 속에 또 -1의 팩토리얼이 담겨져 있으니까, 재귀함수로 표현 가능하다.

   f(5) = 5 * f(4)


//5! = 5 * 4!

public static int factorial(int number) {


if(number ==1)

return 1;

else

return number * factorial(number-1);

}



(Tutorial11)


[3] 반복함수로 피보나치 수열을 구현해보자.

 - 피보나치 수열은 연속된 두 수를 더해서 다음 수가 나오는 수열을 말한다.(// 1, 1, 2, 3, 5, 8, ...)

 - k번 째 수가 무엇인지 알아내는 함수를 만들 것이다.



1. 첫째항 1과 둘째항 1을 미리 선언해준다. 잘못된 경우에 -1을 반환하도록 result도 만들어준다.

  (피보나치수열은 초기항 a1, a2는 기본적으로 주어진다)


2.  if /else if 에다가는 첫째항, 둘째항이 입력되면 바로 반환하도록 만들고 / 나머지 3번째항 이상인 경우 else에다가  아래와 같이 코드를 작성해야한다.

- for문 2 <=  i < number 까지 돌면서,

<2부터 시작하는 이유는, number가 3이라고 가정해보면, i=2일때만 for문이 작동하므로 a1 + a2 만 계산한 a3가 리턴된다

  만약 number가 4라면, i=2일 때 다음계산을 위해, a2가 one에 / a3가 two에 들어가고, 마지막 i=3이면 a2+ a3 가 result에 담겨서 리턴되기 때문>

      i = 2 일때, 초기항 2개를 더해서 result에 넣어준다. (one + two)

      다음계산을 위해서,  one에다가는 오른쪽으로 한칸 옮겨지므로, two를 대입해준다

        two에다가는 오른족으로 한칸 옮겨지므로, one+two값인 result를 넣어준다.


      i = 3 일때, 미리 옮겨진 one 과 two를 더해서 result에 더한다. 그리고 오른쪽으로 한칸씩 one과 two를 옮긴 값을 대입해 놓는다.

 ...

i=number-1 일때, number항 직전 2개의 항인 one과 two를 더해서 result에 넣는다. 그리고 다음계산을 위해 오른쪽으로 옮겨놓지만 사용되진 않음.


public static int fibonacci(int number) {

int one=1;

int two=1;

int result = -1;

if(number == 1)

{

return one;

}

else if(number == 2) {

return two;

}

else{

for(int i = 2; i < number ; i++) {

result = one + two; // number 직전까지 아래에서 one과 two를 옮겨놓았다.

one = two; // 수열 오른쪽으로 한칸씩 다음 계산을 위해, 옮겨놓는 작업.

two = result;

}

return result;

}

}



3. 출력해보자.

public static void main(String[] args) {


System.out.println("피보나치 수열의 10번째 원소는 "+fibonacci(10)+"입니다.");

}



[4] 재귀함수로 피보나치 수열을 구현해보자. 훨씬 간단해진다.

 - 핵심은 n-1씩 해가면서, 줄여가는 것이다. 그리고 마지막 종점지에는 상수를 이용해서 리턴해줘야한다. 

 - 팩토리얼 함수와 차이점은 n-1뿐만 아니라 n-2도 있으니, 2개의 초기항이 존재해야한다. 


1. 첫번째, 2번째 항에 대해서는 if문으로 바로 return1을 해주자.


2. 3번재 이상부터는, return값에 f(n-2) + f(n-1)만 해주면 된다.


public static int fibonacci(int number) {

if(number ==1)

{

return 1;

}else if(number==2) {

return 1;

}else {

return fibonacci(number-2) + fibonacci(number-1) ;

}

}


*** 반복함수와 달리 재귀함수의 피보나치 수열은 f(49) 계산시, f(48) + f(47)이 계산되지만, 

     f(48)계산시에도 f(47) + f(46)이 계산되므로, 반복되는 계산이 기하급수적으로 많아 진다.

     재귀함수를 사용할 때, 훨씬 비효율적인 계산을 하므로, 주의해서 계산해야한다. 실제로 f(50)을 물어보면, 계산값이 안나온다.

(Tutorial8)

사용자 정의 함수는 정해진 특정한 기능을 수행하는 모듈(작은 기능을 수행하는 장치)을 의미한다. 함수를 활용하여 하나의 문제를 잘게 분해할 수 있다.

메인함수에서 모든 알고리즘을 처리하면 매우 비효율적이다. 사용자 정의 함수를 통해 각각의 모듈로서 쉽게 수정되고 보완되는 장점이 있다.



1. 사용자 정의 함수를 만들어보자. ( Main 클래스 안 <-> Main함수 밖)

(1) public static 이후에 함수는 [반환형]을 먼저 써주어야한다.  그 다음 [함수명],  [매개변수] 순이다.

- 매개변수는 함수가 처리할 값을 사전적으로 주어주는 것이다.


// 반환형, 함수명, 매개변수

public static int function(int a, int b, int c) {

return 0;

}


2. 3개의 수 최대 공약수를 찾는 프로그램을 만들어보자.

[ 3개의 수 중 가장 작은 수 찾기 -> 찾은 가장 작은 수를 1씩 줄여가면서(줄여나가다가 나타나면 종료되니 최대가 됨) 

  -> 3개 모두 나눈 나머지가 0인 공약수 찾고 바로 반환하여 함수 종료시키기]


(1) 최대공약수를 찾을 때는 먼저 3개의 값 중 가장 작은 수를 먼저 찾아내야한다.

 - if a와 b를 먼저 비교한 뒤 -> b가 더 작은 경우, if  b와c를 비교해서 둘 중 더 작은 값을 min에 넣어준다.

             -> a가 더 작은 경우, if a와 c를 비교해서 더 작은 값을 min에 넣어준다.


int min;

if(a>b) {

if(b >c ) {

min =c;

} else {

min = b;

}

}else {

if(a>c) {

min = c;

}else {

min = a;

}

}


(2) 가장 작은 수를 for문을 통해, 1이 될 때까지  줄여주면서 그 안에 포함된 모든 수(약수 포함)를 돌면서, 

    if문을 통해 [ 3개의 수를 나눈 나머지가 모두 0이 되는, 공통약수]들을 찾아준다.

     만약 i가 반환되면 바로 [return i;를 통해 값을 반환하면서, 함수를 바로 종료] 시킨다. 그러면 더 최대공약수가 나오자마자 바로 함수가 종료되므로, 2번째 큰 공약수는 무시된다.


for(int i = min; i>0; i--) {

if( a%i==0 && b%i==0 && c%i==0) {

return i;

}

}


(3) 만약 최대공약수가 없는 경우에 -1값을 반환하도록 return -1;을 함수 맨 마지막에 준다.

return -1;


(4) 메인함수에서 사용자정의함수 function()을 사용하여, 문자를 출력해주자.


public static void main(String[] args) {


System.out.println("(400, 300, 750);의 최대공약수 : " + function(400, 300, 750) ); 

}


(5) 출력값 

(400, 300, 750);의 최대공약수 : 50



(Tutorial9)


3. 약수중 K번째로 작은 수를 찾는 프로그램을 작성해보자.

 [ 1부터 숫자까지 증가시키면서, 숫자로 나누어 떨어지는 약수가 k번 발견될 때, 반환한다. 이 때 몇번인지는 k--;로 센다]

(1) 매개변수는 2개를 받자, 약수를 찾을 수number k번째를 의미하는 k이다.


(2) 1부터 약수를 찾을 숫자 number까지 1씩 증가시키면서, 약수를 의미하는 [number % i ==0] 을 찾을 때마다, k를 1씩 뺀다

    그러다가 k가 0이 되는 순간에 그 약수 i를 return시킨다.

    ex> 만약 10번째 약수를 찾기위해, k에 10을 집어 넣었다면, 처음 찾았을 때는 9, 2번째찾으면 8, ... , 10번째 찾으면 0이 된다. 

     이 때, 약수를 찾은 횟수가 k만큼 돌아서 0이 되는 순간에 return시켜서 해당 약수를 반환시키는 것이다. 


(3) for문을 통해 1부터 숫자까지 돌았는데, k번째 약수가 안나타난다면, for문을 빠져나와서 -1을 return하도록 한다.

public static int function(int number, int k) {

for(int i =1; i<=number; i++) {

if(number % i ==0 ) {

k--;

if(k==0) {

return i;

}

}

}

return -1;

}


(4) 메인매쏘드에서 함수를 사용해보자. 3050의 10번째 약수를 받아와 result에 넣어주고, 출력하자.

public static void main(String[] args) {

 

int result = function(3050, 10);

if(result == -1) {

System.out.println("3050의 10번째 약수는 없습니다.");

}else {

System.out.println("3050의 10번째 약수는 "+result+"입니다");


}


}


(5) 출력값

3050의 10번째 약수는 610입니다



4. 문자열의 마지막 단어를 반환하는 함수를 작성해보자.

(1) 문자열 하나를 입력 받아,  문자열의 단어 하나를 반환하는 public static char function(String input) {} 매쏘드를 만들자.

(2) input.charAt()을 이용해서, 문자열 중 하나의 단어만 가져올 수 있다. 이때, 파라미터에 문자열변수.length() -1 을 넣으면 가장 마지막 단어의 index를 의미하게 된다.

    (charAt()의 index는 0부터 세고, length는 1부터 세니까.. length-1하면 마지막 index값을 가져올 수 있다)

     

public static char function(String input) {

return input.charAt(input.length()-1);

}


(3) 매인매쏘드에서 사용해본다.

public static void main(String[] args) {

 

System.out.println("Hellow World의 마지막 단어는 "+function("Hello World") +" 입니다.");


}


(4) 출력값 

Hellow World의 마지막 단어는 d 입니다.


5. max()를 활용해서 최대값을 저장하는 함수를 작성해보자. 

[ 2개 중 최대값삼항연사자로 만든 함수를 만들고, 3개 중 최대값은 2개 최대값 함수를 이용해서, 

  최대값을 뽑아 낸 것을 담아 놓고, 그것을 다시 2개 최대값 함수로 비교하자]


(1) 2개의 정수를 받아 삼항 연산자를 이용해서 큰놈을 return하자.


public static int max(int a, int b) {

return (a>b)? a:b;

}


(2) 3개의 정수를 받아, (1)에서 만든 매쏘드를 활용해서 최대값을 뽑아내자. 

public static int function(int a, int b, int c) {

int result = max(a,b);

result = max(result, c);

return result;

}


(3) 메인매쏘드에서 출력해보자.


  public static void main(String[] args) {

System.out.println("(345, 567, 789)중 가장 큰 값 : "+function(345,567,789));

 

}



java언어의 기본적인 입출력방식을 알아보자.

자바에서는 대표적으로 Scanner클래스를 이용해서 사용자와 상호작용할 수 있다.

일반적으로 Scanner sc = new Scanner(System.in);으로 클래스 객체를 생성한 뒤에, 

sc.nextInt();와 같은 방법으로 int형을 입력받을 수 있다.

입력받은 자료는 내부적으로 처리한 뒤에 다시 사용자에게 그 값을 반환한다.


프로그램이 입출력을 잘 지원한다는 것은 사용자 인터페이스가 뛰어나다는 의미와 같다. 나중에 Gui를 배우게 되면 이러한 입출력을 훨씬 용이하게 할 수 있다.


1. 특정한 정수를 입력 받아서 그대로 출력하는 프로그램을 작성해보자.

(1)Scanner라는 클래스의 인스턴스 변수(객체형 변수, 대문자로 시작, 클래스의 객체)인 sc를 만들고,  우항에는 객체를 생성해 초기화를 해주자. 

   이때, 오류가 나는 것을 클릭해서 java.util라는 자바 기본 라이브러리 것을 import하자.

 -파라미터의 System.in은 console창에 입력하는 데이터들을 의미한다.


(2) int i = sc.nextInt(); 는 사용자가 입력한 정수를 i에 넣는 다는 의미이다.


(3) sc.close();를 통해 입출력하는 변수sc를 닫아 주어, 안정적으로 종료시킨다.


import java.util.Scanner;


public class Main {


public static void main(String[] args) {


Scanner sc = new Scanner(System.in); //콘솔창에서 입력받는데 필요한 스캐너변수

System.out.print("정수를 입력하세요 : ");

int i = sc.nextInt();

System.out.println("입력된 정수는 "+i+"입니다");

sc.close();


}


}



2. 파일에 입력된 정수에 100을 곱해서 출력하는 프로그램을 작성해보자.

[ 파일을 컨트롤 하는 file변수 ->  file의 내용을 읽어올 수 있는 scanner 변수]이용

(1) File클래스를 이용해 file이라는 변수를 만들고, 객체를 생성해 초기화를 할 때, 매개변수에는 "input.txt"라는 파일명을 넣어준다.

    이때, 파일은 프로젝트 창에서 새로 생성할 것이다.

File file = new File("input.txt");


(2) Scanner클래스를 사용하는데, 매개변수에는 콘솔창에 입력되는 데이터(System.in)가 아닌 file변수를 넣어준다. 


(3) 매개변수에 빨간줄이 난 것은 아직 input.txt라는 파일이 없어서 file변수가 비어있거나 등등의 예외처리가 안되어서 나는 것이다.

     빨간줄에 마우스를 갖다대어, try/catch문을 입혀주자. (예외처리문) catch문에서는 println으로 파일 읽는데 오류가 발생했다고 띄워주자.


(4) try문 내부에 계속 코드를 이어가는데, while문의 조건으로 sc.hasNextInt()(반환되는 것이 boolean형)를 통해 

    sc가 읽어오는 file(input.txt)에서 '다음에 읽어올 정수가 존재하는지?'를 조건으로 건다.

    그리고 조건문 안에는 실제로 '다음에 읽어오는 정수 값'인 sc.nextInt()에 100을 곱해서 출력해주자. 


(5) while문을 나와서 다음에 읽어올 정수가 없는 것으로서, 입출력이 끝났으니 스캐너함수를 닫아주는 sc.close()로 닫아주자.


File file = new File("input.txt");

try {

Scanner sc = new Scanner(file);

while(sc.hasNextInt()) {

System.out.println( sc.nextInt() * 100 );

}

sc.close();

} catch (FileNotFoundException e) {

System.out.println("파일을 읽어오는 도중에 오류가 발생했습니다");

}


(6) 이제 file 매개변수인 input.txt파일을 만들어주자. Tutorial7 우클릭 > new > File > file name : [ input.txt ]


(7) txt파일 안에 정수를 임의로 입력하자. space로 간격 or 줄바꿈을 주면 숫자들이 구분된다


170 580 30

2000

30

2080


(8) 출력된 값은  아래와 같다.


17000

58000

3000

200000

3000

208000














(Tutorial5)


*기본 데이터 타입 복습

---

byte - 1byte(8비트) 

short- 2byte(16비트)- 작은 정수를 표현할 때 사용

int - 4byte(32비트)

long-8byte-int보다 더 큰 정수

---

float -4byte -실수

double-8byte- 더 큰 실수

---

boolean- false or true

---

char - 2byte 


*헷갈리는 연산자

i++, ++i는 모두 1씩 증가시키는 것은 같으나, 출력함수 내에서는 역할이 다르다

i++ : 1을 더한 다음 출력 / ++i : 출력 후 1을 더함


i++ 와 i+=1;과 i = i+1은 모두 동일한 표현이라 할 수 있다.


---

100 < x < 200 을 프로그래밍 언어로 표현 하는 것은 잘못된 표현이다

( 100 < x ) &&( x < 200 ) 으로 앤드 연산자를 사용해서 표현해야한다.

---



<조건문>

조건문과 반복문은 프로그래밍에 있어서 논리의 흐름을 정하는 가장 기본적인 기술이다.


1. if문을 이용해서, 특정 문자열을 포함하는지 확인하는 프로그램을 작성해보자.

- String 변수는 비원시적 자료형으로서, 클래스기반으로 생성되어 있으며, 내부적인 함수를 변수. 으로 확인할 수 있다. 이 중에 contains를 이용해보자


public static void main(String[] args) {


String a = "I Love You.";

if(a.contains("Love")) {

System.out.println("Me too.");

}else {

System.out.println("포함하지 않네요");


}

}


(Tutorial6)


2. if-else if-else문을 이용해서, 점수에 따라 다른 메세지를 출력하는 프로그램을 작성해보자.

- 큰 점수를 기준으로해서 >= 크거나 갔다의 조건문을 if - else if  - else(마지막은 else로 끝나자**)순으로 사용하자

(if와 else로 보통 2개 조건만 거는데, 3개 이상의 조건에서 가운데 else if를 삽입한다고 생각)


public static void main(String[] args) {


int score = 95;

if(score >= 90) {

System.out.println("A+입니다.");

}else if(score >= 80){

System.out.println("B+입니다.");


}else if(score >= 70){

System.out.println("C+입니다.");


}else 

System.out.println("D+입니다.");


}

}


3. 문자열과 정수형을 각각 조건문을 이용해 활용해보고 차이점을 알아보자.

(1) 자바에서는 String을 비교할 때, equals() 매쏘드를 사용한다. 그 이유는 타 자료형과는 다른 비원시적 자료형이기 때문이다.

(내부 클래스 기반의 함수를 가짐)

 

String a ="Man";

int b = 0;


if(a.equals("Man")) {

System.out.println("남자입니다.");

}else {

System.out.println("여자입니다.");


}

if(b ==3) {

System.out.println("b는 3입니다.");

}else {

System.out.println("3이 아닙니다.");

}

(2) String의 비교연산자 중에 equalsIgnoreCase()대소문자 상관없이 문자열을 비교할 수 있다.


if(a.equalsIgnoreCase("man") && b == 0) {

System.out.println("a는 대소문자 상관없이 man이고, b는 0입니다.");

}else {

System.out.println("a는 대소문자 상관없이 man이 아니거나 b는 0이 아닙니다.");


}



<반복문>

4. while문을 이용해서 1부터 1000까지 합을 출력해보자.

- while문은 true일 때까지 돌아간다. i는 1부터, 1000까지 총 1000회를 돈다.

- while 안에서  i++;는 대입이 다 끝난 뒤에, 1이 더해진다. 즉,  sum+=i++; 에서 최초의 i값을 대입한 뒤에 i에다가 1이 더해진다.

  (1로 먼저 계산하고, 2가 된다)


int i = 1, sum = 0; //동시에 int형 변수를 선언할 수 있다.

while(i <= 1000) {

sum += i++;    //sum에 sum+ i를 대입하고 난뒤 i가 +1된다.

}

System.out.println("1부터 1000까지의 합은 " + sum + "입니다.");



< FOR문 해석 : ~부터 ~까지 1씩 증가시켜서 반복>

5. 이중 for문을 이용해서 삼각형을 출력해보자. for문의 구성은  for(초기화 부분, 조건부분, 증감연산자) 로 구성된다.

(1) 상수를 클래스 전역변수로 하나 만들자. 


final static int N = 30;


(2) 초기화 부분의 변수 i는 N=30부터 시작해서, i가 0보다 클 때까지 반복한다. 1씩 내려가다가 1까지 계산된 뒤 멈춘다. 출력을 하면, 

* 이 줄바꿈하면서 총 30개가 출력된다. 바깥 for문은 줄바꿈 30줄 하는 것으로 쓰일 것이다. 그리고 j가 30부터 1까지 사용될 것이다.


for(int i = N; i> 0; i --) { 

System.out.println("*");

}


(3) 이중 for문으로서, j는 i부터 시작해서,, j가 0보다 클 때까지, j를 하나씩 빼준다.  이때, println이 아니라 별이 붙어나오도록 ln을 뺀다.

(4) 문자열이 없는 println(); 만 호출해줘서 줄바꿈이 되도록 한다.


for(int i = N; i> 0; i --) { 

// 초기화 부분의 변수 i는 N=30부터 시작해서, i가 0보다 클 때까지 반복한다. 1씩 내려가다가 0이 되면 멈춘다.

for(int j = i; j>0 ; j--) {  //j는 30일때부터 시작해 1까지 가고, 또 29부터 시작해서 1까지 가고.. 를 반복

System.out.print("*");

}

System.out.println();

}


< 해석할 때, i를 먼저 고정시켜서 해석한다. >

i가 30일때, j는 30부터 1까지 총 30번이 반복되면서 30개의 별이 붙어서 출력된다. + 줄바꿈한다.

i가 29라면, j는 29부터 1까지 총 29개의 별을 붙혀 출력된다. +줄바꿈한다.

...

i가 1이라면, j는 1개의 별을 붙혀서 출력한다.+줄바꿈한다.



6. for문을 이용해서 원을 출력하는 프로그램을 작성해보자.

- x^2 + y^2 = r^2 원점을 중심으로 하는 원의 방정식의 원리를 도입할 것이다.

(1) 처음 상수를 15로 선언하자.

final static int N = 15;

(2) for문을 i는 -N부터 시작해서 / N까지/ 1씩 증가시키면서, 줄바꿈을 할 것이다.( 총 (2N + 1) 번의 줄바꿈)

(3) 이중for문에는 j가 -N부터 시작해서 N까지 1씩 증가시키면서 ( 가로도 2N+1 만큼 차지, i 와 무관)

i를 x, j를 y로 생각하고 원의 방정식을 만든다. 만약 반지름 내부라면 "*" 를 출력하고, 반지름 밖이라면 " " 공백을 출력하도록 하자


public class Main {

final static int N = 15;


public static void main(String[] args) {


// x^2 + y^2 = r^2 원점을 중심으로 하는 원의 방정식

for ( int i = -N; i<=N ; i++) {

for( int j = -N ; j <=N ;j++) {

if(i*i + j*j <= N*N) {

System.out.print("*");

}else {

System.out.print(" ");


}

}

System.out.println();


}




}

}


<해석> 

가로, 세로 2n+1의 격자안에서 

i가 -15 일때, +15까지 총 31번을 줄바꿈 할 것이다.(N이 홀수어야 한다?)

i가 -15라면, j는 -15부터 +15까지 31번을 반복하여, 붙혀서 출력하는데, 원의방정식 내부에 *를 출력한다. 원이아니라면 공백을 출력한다.

i가 -14라면, j는 -15부터 +15까지 31번을 반복하여, 붙혀서 출력...

- j는 i에 영향을 안받고, 정해진 N에 의해, 무조건 31개를 붙혀서 출력하며, 원의공식에 따라서 원 내부에 *를 찍는다

- i는 N에 의해 31개부터 1까지 줄어들면서 줄바꿈을 한다.




7. for(;;)  /  white(true)는 무한루프 이며, break로 탈출할 수 있다.

int count = 0;

for(;;)

{

System.out.println("출력");

count++;

if(count==10) {

break;

}

}

연산자는 사칙연산과 %(Modular, 나머지)연산이 있다.  [프로젝트명 : Tutorial4]


1.  초를 입력받아 몇분 몇초인지 계산하는 프로그래밍을 해보자.


(1) 메인클래스 내부에, 메인함수 밖에, 1000초를 의미하는 상수를 선언해보자. 

final static int SECOND = 1000;


(2) 1000초를 몇분 몇초로 만들려면 [ 분 : 1000초/60,  + 초(60초를 넘지않는 나머지수) : 1000초/60의 나머지] 이다. 메인함수안에 변수를 선언해주자.

int minute = SECOND / 60;

int second = SECOND % 60;

(3) 출력해보자

System.out.println(minute + "분 "+ second + "초");



2. 증감연산자(++, --)를 활용하는 프로그래밍 해보자. 


int a= 10;

System.out.println("현재의 a는 " + a +"입니다.");

a++;

System.out.println("현재의 a는 " + a +"입니다.");


(1) 만약 출력하는  내부에 ++를 입력하는 경우는 2가지로 나뉜다

1) a앞에 붙어있는 ++a : 출력시 a+1이 출력된다.

2) a뒤에 붙어있는 a++ :  출력이 된 이후에 +1이 되는 구조-> 출력식에는 +없이 그냥 a가 나온다.


System.out.println("현재의 a는 " + ++a +"입니다.");

System.out.println("현재의 a는 " + a++ +"입니다.");


3. 모듈러 연산자(%)를 실습해보자.

System.out.println(1 % 3);

System.out.println(2 % 3);

System.out.println(3 % 3);

System.out.println(4 % 3);

System.out.println(5 % 3);

System.out.println(6 % 3);

(1) 위 식을 해보면, 3으로 나눈 나머지가 나온다.



4. 다양한 연산자(==, >, <, &&, ||, ! )에 대해서 알아보자.


(1) 출력식에 괄호를 넣어 ==연산자로 비교하면, true  혹은 false값이 나온다.

int a = 50;

int b = 50;

System.out.println("a와 b가 같은가요?" + ( a == b ));


(2) 등호를 넣어보면 false값이 출력된다.

System.out.println("a가 b가 큰가요?" + ( a > b ));


(3) 이제 2가지 연산자를 동시에 사용하기 위해 and연산자( &&) 를 사용해보자.

System.out.println("a가 b와 같으면서 a가 30보다 큰가요?" + ((a == b) && (a > 30) ));


(4) 등호연산자의 식을 괄호로 묶어서, 앞에 null연산자 (!)을 붙혀보자.  (안드로이드에서는 !=,  자바에서도 사용은 가능한 것 같다 출력은 정상)


System.out.println("a가 b와 같으면서 a가 30보다 큰가요?" + !( a==50 ));


5. 삼항연산자( 조건? 참 : 거짓)의 형태를 숙지하고 프로그램을 작성해보자. (삼항연산자를 통해  두 숫자중 최대값 구하기)

 * 함수는 반환형 함수이름 ( 매개변수) {} 로 구성된다


(1) 간단하게 함수를 만들어주자. 메인함수 밖에서 만들어야한다.  int형 a와b를 받아서 둘중에 더 큰 수를 반환하는 함수 int max()만들자.

(2) 내부에는 int형 변수에다가 (조건)? 참 : 거짓;에 해당하는 내용을 넣고, 그 값을 return하자.

int max(int a, int b) {

int result = (a > b)? a: b;

return result;

}

(3) 메인함수에서 만든 함수를 사용해보자.


int x = 50;

int y = 60;

System.out.println("최대값은 " +max(x,y) +"입니다.");


(4) 이때, *static이 붙은 함수는 클래스 내부에서 사용되는 함수다. static인 메인함수에서 max함수를 사용하려면, max함수에도 static이 붙어있어야한다!(static은 nonstatic을 사용할 수없음)


static int max(int a, int b) {


(5) 출력해보면, 최대값이 사용된다.


6. Math라는 클래스속의 거듭제곱 함수pow()를 이용해서 거듭제곱 연산프로그램을 작성해보자.

(1) 계산을 위해 double형(실수형) 변수를 선언하고, 거기다가는 Math클래스.에 있는 pow()매쏘드를 이용해서 3.0의 20.0제곱을 입력하자

(2) 출력시에는 (int)로 형변환하여 정수로 나타내자.


double a = Math.pow(3.0, 20.0);

System.out.println("3의 20제곱은 " + (int) a +"입니다.");

+ Recent posts