본문 바로가기

Java-Study

Java Study : 4주차 정리 - 배열과 ArrayList, 상속과 다형성

배열 

배열을 사용하면 자료형이 같은 자료 여러개를 한 번에 관리할 수 있다.

 

배열 선언과 초기화

배열 선언

배열을 사용하려면 선언을 먼저 해야 하는데 변수와 마찬가지로 자료형을 함께 선언한다.

배열을 이루는 각각의 자료를 배열 요소라 하며, 배열 요소는 자료형이 모두 같다.

  • 자료형[] 배열 이름 = new 자료형[개수];
    int[] studentIDs = new int[10]; //int형 요소가 10개인 배열 선언

배열 초기화하기

자바에서 배열을 선언하면 선언과 동시에 특정 값으로 초기화할 수도 있다.

배열이 초기화 요소의 개수만큼 생성 되므로  [ ] 안의 개수는 생략한다.

  • int[] studentIDs = new int[] {101, 102, 103}; //개수는 생략
  • new int[]도 생략 가능하지만 배열의 자료형을 먼저 선언하고 초기화 하는 경우에는 불가능하다.

 

배열 사용하기

선언한 배열의 각 요소에 값을 넣거나 가져올 때는 [ ]를 사용한다.

배열 길이를 n이라고 하면 배열 순서는 0부터 n-1까지이고 0번 요소가 배열의 첫 번째 요소이다.

  • studentIDs[0] = 10; //배열의 첫 번째 요소에 값 10을 저장

인덱스 연산자[ ]

배열 이름에 [ ]를 사용하는 것을 인덱스 연산자라 한다.

인덱스 연산자의 기능은 배열 요소가 저장된 메모리의 위치를 찾아주는 역할이다.

 

객체 배열 사용하기

참조 자료형 변수도 여러개를 배열로 사용이 가능하다. 책 제목과 저자를 출력하는 Book 클래스가 있다고 가정해보면 Book[] library = new Book[5]; 이렇게 배열을 생성할 수 있다. 여기서 중요한 점은 Book 인스턴스 5개가 바로 생성된 것이

아니라 그 인스턴스를 가르키는 주소 값을 담은 공간이 생성된 것이다.

예시

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

배열 복사하기

기존 배열과 자료형 및 배열 크기가 똑같은 배열을 새로 만들거나 배열의 모든 요소가 자료가 꽉 차서 더 큰 배열을 만들어

기존 배열에 저장된 자료를 가져오려 할 때 배열을 복사한다. System.arraycopy() 메서드를 사용한다.

객체 배열도 복사 가능하다.

  • System.arraycopy(src, srcPos, dest, destPos, length)
  • src : 복사할 배열 이름
  • srcPos : 복사할 배열의 첫 번째 위치
  • dest : 복사해서 붙여 넣을 대상 배열 이름
  • destPost : 복사해서 대상 배열에 붙여 넣기를 시작할 첫 번째 위치
  • length : src에서 dest로 자료를 복사할 요소 개수

얕은 복사

만약 객체 배열을 복사한 다음에 복사할 배열의 요소 값 하나를 변경을 하게 되면 복사해서 붙여 넣은 배열의 요소 값도 변경되어서 출력이 된다. 이는 객체 배열 요소에 저장된 값은 인스턴스의 주소 값이기 때문이다. 

 

깊은 복사

객체 배열을 복사하면 항상 인스턴스 주소가 복사가 되기 때문에 인스턴스를 따로 관리하고 싶다면 인스터스를 만들고

값을 복사해야 한다. 이를 깊은 복사라고 한다.

복사할 배열에 인스터스를 따로 생성한 후 요소 값을 복사하면 복사한 배열의 요소는 기존 요소가 서로 다른 인스턴스를

가리키고 있기 때문에 영향을 받지 않는다.

 

향상된 for문과 배열

향상된 for문은 배열의 처음에서 끝가지 모든 요소를 참조할 때 사용하면 편리한 반복문이다.

배열 요소 값을 순서대로 하나씩 가져와서 변수에 대입을 하고, 따로 초기화와 조건이 없기 때문에 배열의 시작 요소 부터 끝 요소까지 실행한다.

  • for(변수 : 배열) {
        반복 실행문;
    }

 

다차원 배열

여태까지 배운 행 하나로 이루어진 일차원 배열이 아닌 이차원 이상으로 구변한 배열을 다차원 배열이라고 한다.

이차원 배열

 

ArrayList

기존 배열은 항상 길이를 정하고 시작하기 때문에 길이가 넘어가게 되는 경우 복잡해 지게 된다.

이 때문에 자바에서는 객체 배열을 좀 더 쉽게 사용할 수 있도록 객체 배열 클래스 ArrayList를 제공한다.

 

주요 메서드

  • boolean add(E e) : 요소 하나를 배열에 추가한다. E는 요소의 자료형을 의미한다.
  • int size() : 배열에 추가된 요소 전체 개수를 반환한다.
  • E get(int index) : 배열의 index 위치에 있는 요소 값을 반환한다.
  • E remove(int index) : 배열의 index위치에 있는 요소 값을 제거하고 그 값을 반환한다.
  • boolean isEmpty() : 배열이 비어 있는지 확인한다.

 

클래스 활용하기

  • ArrayList<E> 배열 이름 = new ArrayList<E>();
  • 배열을 선언하는 부분의 <> 안에 사용할 객체의 자료형(E)를 쓰면 된다.
  • 예시로 Book 클래스형을 자료형을 사용하여 배열을 생성하면 아래처럼 된다.
    ArrayList<Book> library = new ArrayList<Book>();
  • ArrayList를 사용하려면 자바 클래스를 선언하기 전에 import java.util.ArrayList 를 반드시 써줘야 한다.

 

ArrayList를 활용한 학생 성적 출력 프로그램

package arrayList;

import java.util.ArrayList;

public class Student {

    int studentID;
    String studentName;
    ArrayList<Subject> subjectList; //arrayList 선언하기

    public Student(int studentID, String studentName){
        this.studentID = studentID;
        this.studentName = studentName;
        subjectList = new ArrayList<Subject>(); //arrayList 생성하기
    }

    public void addSubject(String name, int score){
        Subject subject = new Subject();    //Subject 생성하기
        subject.setName(name);  //과목 이름 추가하기
        subject.setScorePoint(score);   //점수 추가하기
        subjectList.add(subject);   //배열에 저장하기
    }

    public void showStudentInfo()
    {
        int total = 0;
        for(Subject s : subjectList){
            total += s.getScorePoint(); //총점 더하기
            System.out.println("학생 " + studentName + "의 " + s.getName() + " 과목 성적은 " +
                    s.getScorePoint() + "입니다.");
        }
        System.out.println("학생 " + studentName + "의 총점은 " + total + " 입니다.");
    }
}
package arrayList;

public class Subject {

    private String name;    //과목 이름
    private int scorePoint; //과목 점수

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getScorePoint() {
        return scorePoint;
    }
    public void setScorePoint(int scorePoint) {
        this.scorePoint = scorePoint;
    }
}
package arrayList;

public class StudentTest {

    public static void main(String[] args) {
        Student studentLee = new Student(1001, "Lee");

        studentLee.addSubject("국어", 100);
        studentLee.addSubject("수학", 50);

        Student studentKim = new Student(1002, "Kim");

        studentKim.addSubject("국어", 70);
        studentKim.addSubject("수학", 85);
        studentKim.addSubject("영어", 100);

        studentLee.showStudentInfo();
        System.out.println("======================================");
        studentKim.showStudentInfo();
    }
}

상속

객체 지향 프로그램은 유지보수하기 편하고 프로그램을 수정하고너 새로운 내용을 추가하는 것이 유연한데, 그 기반이 되는 기술이 상속이다. 예를 들어 B 클래스가 A클래스를 상속 받으면 B클래스는 A 클래스의 멤버 변수와 메서드를 사용할 수 있게 된다.

 

클래스의 상속

자바 문법으로 상속을 구현할 때는 extends 예약어를 사용한다.

  • class B extends A { }

 

상속을 사용하여 고객 관리 프로그램 구현하기

package inheritance;

public class Customer {

    protected int customerID;   //고객 아이디
    //VIPCostomer에서 Customer를 상속받아서 사용하려면 private인 경우 사용이 불가능하기 때문에 protected를 사용
    //protected는 상속 받은 하위 클래스에서는 public 처럼 사용이 가능하도록 한다
    protected String customerName;  //고객 이름
    protected String customerGrade; //고객 등급
    int bonusPoint; //보너스 포인트
    double bonusRatio;  //적립 비율

    public Customer()   //디폴트 생성사
    {
        customerGrade = "SILVER";   //기본 등급
        bonusRatio = 0.01;  //보너스 포인트 적립 비율
    }

    public Customer(int customerID, String customerName){
        this.customerID = customerID;
        this.customerName = customerName;
        customerGrade = "SILVER";
        bonusRatio = 0.01;
    }

    public int calcPrice(int price){    //보너스 포인트 적립, 지불 가격 계산 메서드
        bonusPoint += price * bonusRatio;   //보너스 포인트 계산
        return price;
    }

    public String showCustomerInfo(){   //고객 정보를 반환하는 메서드
        return customerName + " 님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.";
    }


    //protected 예약어로 선언한 변수를 외부에서 사용할 수 있도록 get(),set() 메서드들을 추가
    public int getCustomerID() {
        return customerID;
    }

    public void setCustomerID(int customerID) {
        this.customerID = customerID;
    }

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    public String getCustomerGrade() {
        return customerGrade;
    }

    public void setCustomerGrade(String customerGrade) {
        this.customerGrade = customerGrade;
    }

}

 

package inheritance;

public class VIPCustomer extends Customer{

    private int agentID;
    double saleRatio;

    public VIPCustomer()
    {
        customerGrade = "VIP";
        bonusRatio = 0.05;
        saleRatio = 0.1;
    }

    public VIPCustomer(int customerID, String customerName, int agentID){
        super(customerID, customerName);
        customerGrade = "VIP";
        bonusRatio = 0.05;
        saleRatio = 0.1;
        this.agentID = agentID;
    }
	
    //오버라이드
    public int calcPrice(int price){
        bonusPoint += price * bonusRatio;
        return price - (int)(price * saleRatio);
    }

    public int getAgentID(){
        return agentID;
    }


}
package inheritance;

public class CustomerTest {

    public static void main(String[] args) {
        Customer customerLee = new Customer();
        customerLee.setCustomerID(10010);
        //customerID, customerName은 protected 변수이므로 set() 메서드 호출
        customerLee.setCustomerName("이순신");
        customerLee.bonusPoint = 1000;
        System.out.println(customerLee.showCustomerInfo());

        VIPCustomer customerKim = new VIPCustomer();
        customerKim.setCustomerID(10020);
        customerKim.setCustomerName("김유신");
        customerKim.bonusPoint = 10000;
        System.out.println(customerKim.showCustomerInfo());
    }
}

 

부모를 부르는 예약어, super

super 예약어는 하위 클래스에서 상위 클래스로 접근할 때 사용한다.

하위 클래스는 상위 클래스의 주소, 즉 참조 값을 알고 있고 이 참조 값을 가지고 있는 예약어가 바로 super이다.

super 예약어로 매개변수가 있는 생성자를 호출할수도 있고 상위 클래스의 멤버 변수나 메서드를 참조하는 것도 가능하다.

 

상위 클래스로 묵시적 클래스 형 변환

개념 면에서는 Customer가 VIPCustomer보다 상위클래스 이지만 기능 면에서 보면 VIPCustomer가 상속을 받아서 상위 클래스의 기능을 모두 사용할 수 있기 때문에 기능이 더 많다. 따라서 VIPCustomer는 VIPCustomer형이면서 동시에 Customer형 이기도 하다. 즉 VIPCustomer 클래스로 인스턴스를 생성할 때 자료형을 Customer형으로 형 변환하여 선언이 가능하다.

  • Customer vc = new VIPCustomer();

상위 클래스 메서드 재정의하기

코드를 보면 Customer에도 calcPrice() 메서드가 있고, VIPCustomer에도 calcPrice() 메서드가 있는 것을 볼 수 있는데 두 메서드의 코드가 다른 것을 알 수 있다. 이처럼 상위 클래스에서 정의한 메서드를 하위 클래스에서 구현할 내용에 맞게 재정의가 가능한데 이를 메서드 오버라이딩 이라고 한다.

  • 묵시적 형 변환으로 VIPCustomer가 Customer로 형 변환이 된 상태에서 calcPrice() 메서드를 호출하면 어느쪽이 호출이 될까?
    • 멤버 변수와 메서드는 선언한 클래스형에 따라 호출이 되지만 상속에서는 상위 클래스와 하위 클래스에 같은 이름의 메서드가 존재할 경우 호출되는 메서드 인스턴스에 따라 결정이 된다. 따라서 선언한 클래스형이 아닌 인스턴스의 메서드를 호출하게 된다. -> VIPCustomer의 calcPrice() 호출
      이렇게 인스턴스가 호출되는 기술을 가상 메서드 라고 한다.

 

다형성

다형성이란 하나의 코드가 여러 자료형으로 구현되어 실행되는 것을 말한다.

package polymorphism;

import java.util.ArrayList;

class Animal{
	public void move()
	{
		System.out.println("동물이 움직입니다.");
	}
}

class Human extends Animal{
	public void move()
	{
		System.out.println("사람이 두 발로 걷습니다. ");
	}
	
	public void readBook()
	{
		System.out.println("사람이 책을 읽습니다. ");
	}
}

class Tiger extends Animal{
	public void move()
	{
		System.out.println("호랑이가 네 발로 뜁니다. ");
	}
	
	public void hunting() 
	{
		System.out.println("호랑이가 사냥을 합니다. ");
	}
}

class Eagle extends Animal{
	public void move()
	{
		System.out.println("독수리가 하늘을 납니다 ");
	}
	
	public void flying() 
	{
		System.out.print("독수리가 날개를 쭉 펴고 멀리 날아갑니다");
	}
}

public class AnimalTest1 {
	
	 public static void main(String[] args) {
		  AnimalTest1 aTest = new AnimalTest1();
		  aTest.moveAnimal(new Human());
		  aTest.moveAnimal(new Tiger());
		  aTest.moveAnimal(new Eagle());
	 }

	 public void moveAnimal(Animal animal) { //매개 변수의 자료형이 상위 클래스
		  animal.move();                     //재정의 된 메서드 호출
	 }   

}

moveAnimal() 메서드를 보면 어떤 인스턴스가 매개변수로 넘어와도  모두 Animal 형으로 변환되어 이 메서드를 호출할 수 있다. 이것이 바로 다형성이다. 여기에 다른 동물이 추가 되더라도 Animal 클래스를 상속받아서 구현하면 쉽게 관리가 가능할 것이다. 이처럼 다형성을 잘 활용하면 확장성 있고 유지보수 하기 좋은 프로그램 개발이 가능하다는 장점이 있다. 

 

 

다형성 활용하기

일반 고객과 VIP 고객의 중간 등급 만들기

package witharraylist;

public class GoldCustomer extends Customer{

    double saleRatio;

    public GoldCustomer(int customerID, String customerName){
        super(customerID, customerName);

        customerGrade = "GOLD";
        bonusRatio = 0.02;
        saleRatio = 0.1;

    }

    public int calcPrice(int price){    //재정의
        bonusPoint += price * bonusRatio;
        return price - (int)(price * saleRatio);
    }
}
package witharraylist;

import java.util.ArrayList;

public class CustomerTest {

    public static void main(String[] args) {

        ArrayList<Customer> customerList = new ArrayList<Customer>();

        //묵시적 형 변환을 위해 Customer형으로 자료형 선언
        Customer customerLee = new Customer(10010, "이순신");
        Customer customerShin = new Customer(10020, "신사임당");
        Customer customerHong = new GoldCustomer(10030, "홍길동");
        Customer customerYul = new GoldCustomer(10040, "이율곡");
        Customer customerKim = new VIPCustomer(10050, "김유신", 12345);

        customerList.add(customerLee);
        customerList.add(customerShin);
        customerList.add(customerHong);
        customerList.add(customerYul);
        customerList.add(customerKim);

        System.out.println("====== 고객 정보 출력 =======");

        for( Customer customer : customerList){
            System.out.println(customer.showCustomerInfo());
        }
        
        System.out.println("====== 할인율과 보너스 포인트 계산 =======");
        
        int price = 10000;
        //다형성 구현
        for( Customer customer : customerList){
            int cost = customer.calcPrice(price);
            System.out.println(customer.getCustomerName() +" 님이 " +  + cost + "원 지불하셨습니다.");
            System.out.println(customer.getCustomerName() +" 님의 현재 보너스 포인트는 " + customer.bonusPoint + "점입니다.");
        }
    }
}

 

다운 캐스팅

하위 클래스에서 상위 클래스의 자료형으로 선언을 하게 되면 상위 클래스에서 선언한 메서드와 멤버 변수만 사용이 가능하다. 때문에 필요에 따라 다시 원래 인스턴스의 자료형으로 되돌아가는 경우가 있는데 이처럼 상위 클래스로 형 변환되었던 하위 클래스를 다시 원래 자료형으로 형 변환하는 것을 다운 캐스팅 이라고 한다.

 

instanceof

다운 캐스팅을 하기 전에 상위 클래스로 형 변환된 인스턴스의 원래 자료형을 확인해야 변환할 때 오류를 막을 수 있다.

이를 확인하는 예약어가 바로 instanceof 이다.

  • Customer customer = new VIPCustomer();
    if(vc instanceof VIPCustomer) {
        VIPCustomer vcustomer = (VIPCustomer)customer;
    }

'Java-Study' 카테고리의 다른 글

Java Study : 5주차 정리 - 추상 클래스  (1) 2022.12.02
Java Study : 4주차 - 백준 문제풀이  (0) 2022.11.26
Java Study : 3주차 - 문제 풀이  (1) 2022.11.19
Java Study : 3주차 정리  (0) 2022.11.18
Java Study : 2주차 정리  (0) 2022.11.07