C6

0

이것이 자바다

목록 보기
4/18
post-thumbnail

6.1 객체 지향 프로그래밍

소프트웨어를 개발할 때에 부품에 해당하는 객체들을 먼저 만들고, 이 객체들을 조립해서 완성된 프로그램을 만드는 기법을 객체 지향 프로그래밍(Object Oriented Programming) 이라고 한다.

객체란?

물리적으로 존재하거나 개념적인 것 중에서다른 것과 식별 가능한 것을 말한다. 객체는 속성과 동작으로 구성된다. 사람은 이름, 나이 등의 속성과 웃다, 걷다, 뛰다 등의 동작이 있다. 이러한 속성과 동작을 자바에서는 각각 필드와 메소드라고 부른다.

현실 세계의 객체를 소프트웨어 객체로 설계하는 것을 객체 모델링이라고 하며 현실 세계 객체의 대표 속성과 동작을 추려내어 소프트웨어 객체의 필드와 메소드로 정의하는 과정이라고 볼 수 있다.

객체의 상호작용

사람은 전자계산기의 기능을 이용하고, 전자계산기는 계산 결과를 사람에게 리턴하는 상호작용을 한다.

객체들 사이의 상호작용 수단은 메소드이며 객체가 다른 객체의 기능을 이용할 떄 이 메소드를 호출한다.

			메소드(매개값1, 매개값2, ...);
이러한 형태를 가지고 있고 메소드 호출을 통해 객체들은 데이터를 서로 주고받는다. 함께 전달하고자 하는 데이터를 괄호()안에 기술하는데, 이러한 데이터를 매개값이라고 한다. 매개값은 메소드가 실행할 때 필요한 값이며 리턴값은 메소드의 실행의 결과이며, 호출한 곳으로 돌려주는 값이다.
		int result = add(20, 10);
       // 리턴한 값을 int 변수에 저장한다.

객체 간의 관계

객체는 단독으로 존재 가능하지만 대부분 다른 객체와 관계를 맺고 있다. 관계의 종류로는 집합 , 상속 ,사용 관계가 있다.

집합 관계

완성품과 부품의 관계를 말하며 자동차는 자동차의 엔진, 타이어, 핸들 등으로 구성되므로 집합 관계라고 볼 수 있다.

사용 관계

다른 객체의 필드를 읽고 변경하거나 메소드를 호출하는 관계를 말한다. 예를 들어 사람이 자동차에게 달린다, 멈춘다 등의 메소드를 호출하면 사용 관계라고 볼 수 있다.

상속 관계

부모와 자식 관계를 말하며 자동차가 기계의 특징(필드, 메소드)을 물려받는다면 기계와 자동차는 상속 관계에 있다고 볼 수 있다.

객체 지향 프로그래밍의 특징

캡슐화

캡슐화란 객체의 데이터(필드) , 동작(메소드)을 하나로 묶고 실제 구현 내용을 외부에 감추는 것을 말한다. 외부 객체는 객체 내부의 구조를 알지 못하므로 객체가 노출해서 제공하는 필드와 메소드만 이용할 수 있다 (잘못된 사용으로 인한 객체 손상을 우려해 접근 제한자를 사용)

상속

객체 지향 프로그래밍에서는 부모 역할의 상위 객체와 자식 역할의 하위 객체가 있다. 부모 객체는 자기가 가지고 있는 필드와 메소드를 자식에게 물려주어 자식 객체가 사용 가능하게 한다. 따라서 코드의 재사용성을 높여주며 유지 보수 시간을 단축 시켜준다.

다형성

사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질을 말하며 자동차의 부품(엔진, 타이어 등) 을 교환하면 성능이 다르게 나오듯이 프로그래밍을 구성하는 객체를 바꾸면 프로그램의 실행 성능이 다르게 나올 수 있다.

6.2 객체와 클래스

객체를 생성할 때는 설계도가 필요한데 객체 지향 프로그래밍에서도 객체를 생성하려면 설계도에 해당하는 클래스가 필요하다. 클래스로부터 생성된 객체를 해당 클래스의 인스턴스(instance)라고 부르며 클래스로 부터 객체를 생성하는 과정을 인스턴스화라고 부른다. 동일한 클래스로부터 여러 개의 인스턴스를 만들 수 있다.

6.3 클래스 선언

클래스 선언은 객체 생성을 위한 설계도를 작성하는 작업이며 어떻게 객체를 생성하고 객체가 가져야 할 데이터가 무엇이며 객체의 동작은 무엇인지를 정의하는 내용이 포함된다.

[클래스명.java]
		public class 클래스명 {
        }

public은 접근 제한자 중 하나이며 공개 클래스를 선언한다는 뜻이다. 클래스명에는 숫자를 포함할 수 있지만 첫 문자가 숫자일 수 없고 특수 문자 중 $,_를 포함할 수 있다.

public class SportsCar {
}
class Tire {
}
복수 개의 클래스 선언이 포함된 소스 파일을 컴파일하면 바이트 코드 파일(.class)은 클래스의 선언 수만큼 생긴다. (하나의 클래스만 public 클래스가 가능하다. 특별한 이유가 없다면 소스파일 하나당 클래스 하나를 선언하는 것이 좋다.)

6.4 객체 생성과 클래스 변수

클래스로부터 객체를 생성하려면 new(객체 생성 연산자)가 필요하다. new 연산자 뒤에는 생성자 호출 코드가 오는데, 클래스 () 형태를 가진다.

		new 클래스()
        
        클래스 변수 = new 클래스();
public class Student {
}
public class StudentExample {
    public static void main(String[] args) {

        Student s1 = new Student();
        System.out.println("s1 변수가 Student 객체를 참조합니다.");

        Student s2 = new Student();
        System.out.println("s2 변수가 Student 객체를 참조합니다.");
    }
}

6.5 클래스의 구성 멤버

클래스 선언에는 객체 초기화 역할을 담당하는 생성자와 객체에 포함될 필드와 메소드를 선언하는 코드가 포함된다. 그래서 생성자 ,필드 , 메소드를 클래스의 구성 멤버라고 한다.

public class  ClassName;
    
    //필드 선언
    int fieldName;
    
    //생성자 선언
    ClassName() { ... }
    
    //메소드 선언
    int methodName() { ... }

필드

객체의 데이터를 저장하는 역할을 한다. 선언 형태는 변수 선언과 비슷하지만 쓰임새는 다르다.

생성자

new 연산자로 객체를 생성할 떄 객체의 초기화를 담당한다. 선언 형태는 메소드와 비슷하지만, 리턴 타입이 없고 이름은 클래스 이름과 동일하다.

메소드

객체가 수행할 동작이다. 다른 프로그램 언어에서는 함수라고 하기도 하는데 , 객체 내부의 함수는 메소드라고 부른다. 객체와 객체간의 상호 작용을 위해서 호출된다.

6.6 필드 선언과 사용

자동차 객체를 예로 들면 제작회사, 모델, 색깔 ,최고속도는 고유 데이터. 현재속도, 엔진 회전 수는 상태 데이터에 해당되며 차체, 엔진, 타이어는 부품에 해당된다.

필드 선언

클래스 블록 내에서 선언되어야만 필드 선언이 된다.

public class Car {
    //타입 필드명[ =초기값 ] ;
    Stirng model = "그랜저"
    int speed = 300;
    boolean start = true; 
    Tire tire = new Tire();
}
초기값 미제공시 기본값으로 초기화된다.
public class Car {
    //필드 선언
    String model;
    boolean start;
    int speed;
}
public class CarExample {
    public static void main(String[] args) {
        //Car 객체를 생성
        Car myCar = new Car();

        //Car 객체의 필드값 읽기
        System.out.println("모델 명 : " + myCar.model);
        System.out.println("시동여부 : "  + myCar.start);
        System.out.println("현재속도 :" + myCar.speed);
    }
}

필드 사용

필드를 사용한다는 것은 필드값을 읽고 변경한다는 것이다. 클래스에서 필드를 선언했다고 바로 사용할 수 있는 것은 아니며, 필드는 객체의 데이터 이기 때문에 객체가 존재하지 않으면 필드도 존재하지 않는다. 클래스로부터 객체가 생성된 후에 필드를 사용할 수 있다. 필드느 객체 내부의 생성자와 메소드 내부에서 사용할 수 있고, 객체 외부에서도 접근해서 사용할 수 있다.

객체 내부에서는 필드명을 읽고 변경 가능하지만 외부 객체에서는 참조 변수와 도트연산자를 이용해서 필드를 읽고 변경해야 한다. 도트는 객체 접근 연산자로 , 객체가 가지고 있는 필드나 메소드에 접근하고자 할 떄 참조 변수 뒤에 붙인다.

public class Car {

    //필드 선언
    String model = "그랜져";
    String company ="현다이";
    String collor ="핫핑크";

    int speed = 200;
    int maxSpeed = 250;
}
public class CarExample {
    public static void main(String[] args) {
        //Car 객체를 생성
        Car myCar = new Car();

        //객체 필드 값 읽기
        System.out.println("회사명 : " + myCar.company);
        System.out.println("모델명 : " + myCar.model);
        System.out.println("색깔 : " + myCar.collor);
        System.out.println("현재 속도: " + myCar.speed);
        System.out.println("최고 속도 : " + myCar.maxSpeed);

        //Car 객체의 필드값 변경
        myCar.speed = 130;
        System.out.println("변경된 현재 속도 : " + myCar.speed);
    }
}

6.7 생성자 선언과 호출

new 연산자는 객체를 생성한 후 연이어 생성자를 호출해서 객체를 초기화하는 것을 역할을 한다. 객체 초기화란 필드 초기화를 하거나 메소드를 호출해서 객체를 사용할 준비를 하는 것을 말한다.

Tire myTire = new Tire();
클래스 변수 = new 클래스();

생성자가 성공적으로 실행이 끝나면 new 연산자는 객체의 주소를 리턴하며 리턴된 주소는 클래스 변수에 대입되어 객체의 필드나 메소드의 접근할 때 이용한다.

기본 생성자

모든 클래스는 생성자가 존재하며, 하나 이상을 가질 수 있다. 클래스에 생성자 선언이 없으면 컴파일러는 다음과 같은 기본 생성자를 바이트코드 파일에 자동 추가한다.

소스파일(Car.java)

public class Car {
}
class Motor {
}

바이트코드 파일(Car.class)

public class Car {
public Car() {  }
} //자동 추가

바이트코드 파일(Motor.class)

class Motor { 
Motor() {  }
}

그렇기 때문에 new 연산자 뒤에 기본 생성자를 호출 가능하다. 그러나 개발자가 명시적으로 선언한 생성자가 있다면 컴파이러는 기본 생성자를 추가하지 않는다. 개발자가 생성자를 선언하는 이유는 객체를 초기화하기 위해서이다.

생성자 선언

객체를 다양하게 초기화하기 위해 개발자는 생성자를 직접 선언 할 수 있다.

생성자는 메소드와 비슷한 모양을 가지고 있지만, 리턴 타입이 없고 클래스 이름과 동일하다. 매개 변수는 new 연산자로 생성자를 호출할 때 매개값을 생성자 블록 내부로 전달하는 역할을 한다.
Car myCar = new Car("그랜져", "깜장" , 300);
위 3개의 매개값을 순서대로 매개변수로 대입 받기 위해서는 다음과 같이 생성자가 선언되어야 한다.
public class Car { 
//생성자 선언
Car(String model, String collor, int speed) { ... } //매개변수 타입은 매개값에 맞게 작성.
} 
public class CarExample {
    public static void main(String[] args) {

        Car myCar = new Car("그랜져", "깜장", 300);
        //Car myCar = new Car(); // 기본 생성자 호출 불가
    }
}

필드 초기화

객체마다 동일한 값을 갖고 있다면 필드 선언 시 초기값을 대입하는 것이 좋고, 객체마다 다른 값을 가져야 한다면 생성자에서 필드를 초기화하는 것이 좋다. 예로 Korean 클래스를 선언한다면 한국이므로 nation에 대한민국으로 동일 값을 가지지만 , 이름과 주민번호는 다르므로 생성자에서 초기화하는 것이 좋다.

public class Korean {
    //필드 선언
    String nation = "대한민국";
    String name;
    String ssn;

    // 생성자 선언
    public Korean(String n, String s) {
        name = n;
        ssn = s;
        // this.name = name; 코드 가독성을 위해 짧은 매개변수명은 지양
        // this.ssn = ssn;
    }
}
public class KoreanExample {
    public static void main(String[] args) {
        //Korean 객체 생성
        Korean k1 = new Korean("박자바", "010101-1234567");
        System.out.println("k1.nation : " + k1.nation);
        System.out.println("k1.name : " + k1.name);
        System.out.println("k1.ssn :" + k1.ssn);

        //또 다른 Korean 객체 생성
        Korean k2 = new Korean("김자바", "020202-1324567");
        System.out.println("k2.nation : " + k2.nation);
        System.out.println("k2.name : " + k2.name);
        System.out.println("k2.ssn : " + k2.ssn);
    }
}

생성자 오버로딩

매개값으로 객체의 필드를 다양하게 초기화하려면 생성자 오버로딩이필요하다. 매개변수를 달리하는 생성자를 여러 개 선언하는 것을 오버로딩이라고 한다.

public class Car { 
	Car() {...}
    Car(String model)  {...}
    Car(String model, String collor) {...}
    Car(String model, String collor, int speed)  {...}
}
매개변수 이름만 바꾸는 것은 생산자 오버로딩이 아니다
	Car(String model ,String collor)  { ... }
    Car(String collor, String model) { ... } //오버 로딩x
생성자가 오버로딩되어 있을 경우, new 연산자로 생산자를 호출할 떄 제공되는 매개값의 타입과 수에 따라 실행될 생성자가 결정된다.
public class Car {
    // 필드 선언
    String company = "현대";
    String model;
    String color;
    int maxSpeed;

    // 생성자 선언
    Car() {}
    Car (String model) {
        this.model = model;
    }
    Car (String model, String color) {
        this.model = model;
        this.color = color;
    }
    Car (String model, String color, int maxSpeed) {
        this.model = model;
        this.color = color;
        this.maxSpeed = maxSpeed;
    }
}
public class CarExample {
    public static void main(String[] args) {
        Car car1 = new Car();
        System.out.println("car1.company : " + car1.company);
        System.out.println();

        Car car2 = new Car("자가용");
        System.out.println("car2.company : " + car2.company);
        System.out.println("car2.model : " + car2.model);
        System.out.println();

        Car car3 = new Car("자가용", "검정");
        System.out.println("car3.company : " + car3.company);
        System.out.println("car3.model : " + car3.model);
        System.out.println("car3.color : " + car3.color);
        System.out.println();

        Car car4 = new Car("자가용", "검정", 300);
        System.out.println("car4.company : " + car4.company );
        System.out.println("car4.model : " + car4.model);
        System.out.println("car4.collor : "  + car4.color);
        System.out.println("car4.maxSpeed : " + car4.maxSpeed);
    }
}

다른 생성자 호출

생성자 오버로딩이 많아질 경우 생성자 간의 중복된 코드가 발생할 수 있습니다. 매개변수의 수만 달리 하고 필드 초기화 내용이 비슷한 생성자에서 이러한 중복 코드를 많이 볼 수 있다. ( 위 예제)

public class Car {
    String company = "현대자동차";
    String model;
    String color;
    int maxSpeed;

    Car(String model) {
        //20라인 생성자를 호출
        this(model, "은색", 250);
    }
    Car(String model, String color) {
        this(model,color,250);
    }
    Car(String model, String color, int maxSpeed) {
        this.model = model;
        this.color = color;
        this.maxSpeed = maxSpeed;
    }
}
public class CarExample {
    public static void main(String[] args) {
        Car car1 = new Car("자가용"); //생성자 호출
        System.out.println("car1.company : " + car1.company);
        System.out.println("car1.model : " + car1.model);
        System.out.println();

        Car car2 = new Car("자가용", "빨강");
        System.out.println("car2.company : " + car2.company);
        System.out.println("car2.model : " + car2.model);
        System.out.println("car2.color : " + car2.color);
        System.out.println();

        Car car3 = new Car("택시", "검정", 200);
        System.out.println("car4.company : " + car3.company );
        System.out.println("car4.model : " + car3.model);
        System.out.println("car4.color : "  + car3.color);
        System.out.println("car4.maxSpeed : " + car3.maxSpeed);
        }
    }

6.8 메소드 선언과 호출

메소드 선언은 객체의 동작을 실행 블록으로 정의하는 것을 말하고 ,메소드 호출은 실행 블록을 실제로 실행하는 것을 말한다. 메소드는 객체 내부에서도 호출되지만 다른 객체에서도 호출될 수 있기 떄문에 객체간의 상호작용하는 방법을 정의하는 것이라고도 볼 수 있다.

메소드 선언

리턴 타입

리턴 타입은 메소드가 실행한 후 호출한 곳으로 전달하는 결과값의 타입을 말한다. 리턴값이 없는 메소드는 void로 작성한다.
	void powerOn() { ... } // 리턴값이 없는 메소드 선언
    double devide(int x , int y ) { ... } // double 타입 값을 리턴하는 메소드 작성

메소드명

첫 문자는 소문자로, 캐멀 스타일로 작성한다.
	void run() { ... }
    void setSpeed(int speed) { ... }
    String getName() { ... } 

매개변수

매개변수는 메소드를 호출할 때 전달한 매개값을 받기 위해 사용된다. 전달할 매개값이 없다면 매개변수는 생략할 수 있다.

실행블록

메소드 호출시 실행되는 부분이다.
powerOn() , plus(), devide(), powerOff() 메소드를 선언하는 예제.
public class Calculator {
    // 리턴값이 없는 메소드 선언
    void powerOn() {
        System.out.println("전원을 킵니다.");
    }
    void powerOff() {
        System.out.println("전원을 끕니다.");
    }

    //호출 시 두 정수 값을 전달받고,
    //호출한 곳으로 결과값 int를 리턴하는 메소드 선언
    int plus(int x, int y) {
        int result = x + y;
        return  result;
    }

    int divide(int x, int y) {
        int result = x /y;
        return result;
    }
}

메소드 호출

메소드를 호출한다는 것은 메소드 블록을 실행하는 것을 말하며 클래스에서 메소드 선언시 바로 호출할 수 있는 것은 아니다. 메소드는 객체의 동작이므로 객체가 없다면 메소드 호출이 불가능하다. 클래스로부터 객체가 생성된 후에 메소드는 생성자와 다른 메소드 내부에서 호출될 수 있고, 객체 외부에서도 호출 가능하다. 객체 내부에서는 메소드명으로 호출되지만, 외부 객체에서는 도트 연산자를 이용하여 호출한다. 또한 메소드가 매개변수를 가지고 있을 때는 호출할 때 매개변수의 타입과 수에 맞게 매개값을 제공해야한다.

public class CalculatorExample {
    public static void main(String[] args) {
        //Calculator 객체 생성
        Calculator myCalc = new Calculator();

        // 리턴갑이 없는 메소드 호출
        myCalc.powerOn();

        //plus 메소드 호출 시 5와 6을 매개값으로 제공하고,
        // 덧셈 결과를 리턴받아 result1 변수에 대입
        int result1 = myCalc.plus(5,6);
        System.out.println("result1 : " + result1 );

        //divide 메소드 호출시 10과 4를 매개값을 제공하고,
        // 덧셈 결과를 리턴받아 result2 변수에 대입
        int x = 10;
        int y = 4;
        double result2 = myCalc.divide(x,y);
        System.out.println("result2 : " + result2);

        myCalc.powerOff();
    }
}

가변길이 매개변수

메소드를 호출할 때에는 매개변수의 개수에 맞게 매개값을 제공해야 한다. 만약 메소드가 가변길이 매개변수를 가지고 있다면 매개변수의 개수와 상관없이 매개값을 줄 수 있다.

public class Computer {
    //가변길이 매개변수를 갖는 메소드 선언
    int sum(int... values) {
        //sum 변수 선언
        int sum = 0;

        //values는 배열 타입의 변수처럼 사용
        for (int i = 0; i < values.length; i++) {
            sum += values[i];
        }

        return sum;
    }
}
public class ComputerExmaple {
    public static void main(String[] args) {
        //Computer 객체 생성
        Computer myCom = new Computer();

        //sum() 메소드 호출 시 매개값 1, 2, 3을 제공하고
        //합산 결과를 리턴 받아 result1 변수에 대입
        int result1 = myCom.sum(1,2,3);
        System.out.println("result1 :" + result1);

        //sum() 메소드 호출 시 (1,2,3,4,5 를 제공하고
        //합산 결과를 리턴 받아 result2 변수에 대입
        int result2 = myCom.sum(1,2,3,4,5);
        System.out.println("result2 :" + result2);

        //sum() 메소드 호출 시 배열을 제공하고
        //합산 결과를 리턴 받아 result3 변수에 대입;
        int[] values = {1,2,3,4,5};
        int result3 = myCom.sum(values);
        System.out.println("result 3: " + result3);

        //sum() 메소드 호출 시 배열을 제공
        //합산 결과를 리턴 받아 result4 변수에 대입
        int result4 = myCom.sum((new int[] {1,2,3,4,5 }));
        System.out.println("result4 : " + result4);
    }
}

return 문

return 문은 메소드의 실행을 강제 종료 후 호출한 곳을 돌아가라는 의미이며 메소드 선언에 리턴 타입이 있을 경우에는 return 문 뒤에 리턴값을 추가로 지정해야한다.

메소드 오버로딩

메소드 오버로딩은 메소드 이름은 같디 매개변수의 타입, 개수, 순서가 다른 메소드를 여러 개 선언하는 것을 말한다.

class 클래스 { 
	int plus(int x, int y){ ... }
    double plus(double x, double y) { ... }

리턴타입은 무관 메소드이름은 동일 타입, 개수,순서가 달라야 한다

public class Calculator1 {
    //정사각형의 넓이
    double areaRectangle(double width) {
        return width * width;
    }

    //직사각형의 넓이
    double areaRectangle(double width, double height) {
        return width * height;
    }
}
public class CalculatorExample {
    public static void main(String[] args) {
        //객체생성
        Calculator1 myCalcu = new Calculator1();

        //정사각형의 넓이 구하기
        double result1 = myCalcu.areaRectangle(10);

        //직사각형의 넓이 구하기
        double result2 = myCalcu.areaRectangle(10,20);

        System.out.println("정사각형 넓이 = " + result1);
        System.out.println("정사각형 넓이 = " + result2);
    }
}

6.9 인스턴스

필드와 메소드는 선언 방법에ㄸ 따라 인스턴스 멤버와 정적 멤버로 분류할 수 있다. 인스턴스 멤버로 선언되면 객체 생성 후 사용할 수 있고, 정적 멤버로 선언되면 객체 생성 없이도 사용가능하다

구분설명
인스턴스 멤버객체에 소속된 멤버 ( 객체를 생성해야만 사용가능한 멤버)
정적 멤버클래스에 고정된 멤버 ( 객체 없이도 사용가능한 멤버)

인스턴스 멤버 선언 및 사용

지금까지 우리가 선언한 필드와 메소드는 인스턴스 멤버이다.

public class Car { 
//인스턴스 필드 선언
int gas;

//인스턴스 메소드 선언
void setSpeed(int speed ) { ... }
}
gas필드와 setSpeed() 메소드를 외부 클래스에서 사용하기 위해서는 Car 객체를 생성 후 참조 변수로 접근하여 사용해야 한다.
Car myCar = new Car();
myCar.gas = 10;
myCar.setSpeed(60);
메소드를 객체마다 저장한다면 중복 저장으로 메모리 효율이 떨어지므로 메소드 영역에 두되 공유해서 사용하고, 객체 없이는 사용하지 못하도록 제한을 걸어둔다.

6.10 정적 멤버

자바는 클래스 로더를 이용해서 클래스를 메소드 영역에 저장 후 사용한다. 메소드 영역의 클래스에 고정적으로 위치하는 것을 정적 멤버라고 한다. 객체 생성 할 필요 없이 클래스를 통해 바로 사용이 가능하다.

정적 멤버 선언

static 키워드를 추가하면 된다.

public class Calc {
	String color; // 인스턴스 필드
    void setColor(String color) { this.color = color; } // 인스턴스 메소드
    static int plus(int x , int y) { return x + y; } // 정적 메소드
    static int minus(int x , int y) { return x - y; } // 정적 메소드

정적 멤버 사용

클래스가 메모리로 로딩되면 정적 멤버를 사용할 수 있는데, 클래스 이름과 함께 도트 연산자로 접근하면 된다. 정적 요소는 클래스 이름으로 접근하는 것이 정석이며 정적 필드와 정적 메소드는 객체 참조 변수로도 접근이 가능하다.

 class Calculator {
        static double pi = 3.14159;

        static int plus ( int x , int y) {
            return x + y;
        }
        static int minus (int x, int y) {
            return x - y;
        }
}
public class CalcuatorExample {
    public static void main(String[] args) {
        double result1 = 10*10 * Calculator.pi;
        int result2 = Calculator.plus(10,5);
        int result3 = Calculator.minus(10, 5);

        System.out.println(result1);
        System.out.println(result2);
        System.out.println(result3);
    }
}

정적 블록

정적 필드는 필드 선언과 동시에 초기값을 주는게 일반적이고 복잡한 초기화 작업이 필요하다면 정적 블록을 이용해야한다. 정적 블록은 클래스가 메모리로 로딩될 때 자동적으로 실행되며 클래스 내부에 여러개가 선언 되었을 경우에는 선언된 순서대로 실행된다.

 class Television {
     static String company = "MyCompany";
     static String model = "LCD";
     static String info;

     static  {
         info = company + "-" + model; // info는 정적 블록에서 company, model을 서로 연결하여 초기값 줌
     }
}
public class TelevisionExample {
    public static void main(String[] args) {
        System.out.println(Television.info);
    }
}

인스턴스 멤버 사용 불가

정적 메소드와 정적 블록은 객체가 없어도 실행된다는 특징 때문에 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다. 객체 자신의 참조인 this 또한 사용 불가.

정적 메소소드와 정적 블록에서 인스턴스 멤버를 사용하고 싶다면 객체를 먼저 생성하고 참조 변수로 접근해야 한다. main() 메소드도 정적 메소드이므로 동일한 규칙이 적용된다.
 class Car {
    //인스턴스 필드 선언
     int speed;
     //인스턴스 메소드 선언
     void run () {
         System.out.println(speed + "으로 달립니다.");
     }

     static void simulate() {
         //객체 생성
         Car myCar = new Car();
         //인스턴스 멤버 사용
         myCar.speed = 200;
         myCar.run();
     }

     public static void main(String[] args) {
         //정적 메소드 호출
         simulate();

         //객체 생성
         Car myCar = new Car();
         //인스턴스 멤버 사용
         myCar.speed = 60;
         myCar.run();
     }
}

6.11 final 필드와 상수

인스턴스 필드와 정적 필드는 언제든지 값을 변경할 수 있다. 그러나 경우에 따라서는 값을 변경하는 것을 막고 읽기만 허용해야 할 때가 있다. 이 때 사용한다.

final 필드 선언

final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중 변경이 불가능하다.

	final 타입 필드 = 초기값 ;
필드 선언시 초기값을 대입하던가 생성자에서 초기값을 대입해야만 한다.
public class Korean {
    //인스턴스 final 필드 선언
    final String nation = "대한민국" ;
    final String ssn;

    //인스턴스 필드 선언
    String name;

    //생성자 선언
    public Korean(String ssn , String name) {
        this.ssn = ssn;
        this.name = name;
    }
}
public class KoreanExample {
    public static void main(String[] args) {
        //객체 생성시 주민등록번호와 이름 전달

        Korean k1 = new Korean("111111-1234567" , "김자바");

        //필드값 읽기
        System.out.println(k1.nation);
        System.out.println(k1.name);
        System.out.println(k1.ssn);

        k1.name = "김자바";

    }
}

상수 선언

불변의 값. 원주율 파이나 지구의 무게(근데 사실 헬륨은 210만년에 걸쳐서 빠져나간다고 함 그래서 무게는 상수가 아님 사실) 및 둘레 등이 해당된다. 이런 불변의 값을 저장하는 필드를 자바에서는 상수라고 부른다.

상수는 객체마다 저장할 필요가 없고, 여러 개의 값을 가져도 안되기 때문에 static이면서 final인 특성을 가져야 한다.

static final 타입 상수 = 초기값;

static final 타입 상수;
static {
	상수 = 초기값;
}
class Earth {
    //상수 선언 및 초기화
    static final double EARTH_RADIUS = 6400;

    //상수 선언
    static final double EARTH_SURFACE_AREA;

    // 정적 블록에서 상수 초기화
    static {
        EARTH_SURFACE_AREA = 4 * Math.PI*EARTH_RADIUS*EARTH_RADIUS;
    }
}
public class EarthExample {
    public static void main(String[] args) {
        //상수 읽기
        System.out.println("지구의 반지름: " + Earth.EARTH_RADIUS + "km");
        System.out.println("지구의 표면적: " + Earth.EARTH_SURFACE_AREA + "km^2");
    }
}

6.12 패키지

패키지는 클래스의 일부분이며, 클래스를 식별하는 용도로 사용된다. 패키지는 주로 개발 회사의 도메인의 이름의 역순으로 만든다. (이렇게 하면 두 회사에서 개발한 Car 클래스가 있을 경우 다음과 같이 관리할 수 있다.

패키지 선언

패키지 디렉토리는 클래스 파일을 컴파일하는 과정에서 자동으로 생성된다.

6.13 접근 제한자

객체의 필드를 외부에서 변경하거나 메소드를 호출하지 못하도록 막아야할 경우가 있는데, 중요한 필드와 메소드가 외부로 노출되지 않도록 해 객체의 무결성을 유지하기 위해서 접근 제한자를 사용한다.

접근 제한자제한 대상제한 범위
public클래스, 필드, 생성자, 메소드없음
protected필드, 생성자, 메소드같은 패키지
(default)클래스, 필드, 생성자, 메소드같은 패키지
private필드, 생성자, 메소드객체 내부

클래스의 접근 제한

클래스를 선언할 때 public 접근제한자 생략시 default 접근 제한을 가지고 같은 패키지 내에서만 사용가능하다.

생성자의 접근 제한

객체 생성을 위해 어디에서나 호출할 수 있는 것은 아니다. 생성자가 어떤 접근 제한을 갖느냐에 따라 호출 가능 여부가 결정된다.

접근 제한자생성자설명
public클래스(...)모든 패키지에서 생성자를 호출 가능 = 모든 패키지에서 객체를 생성 가능
클래스(...)같은 패키지에서 생성자를 호출 가능 = 같은 패키지에서만 객체를 생성할 수 있다.
private클래스(...)클래스 내부에서만 생성자를 호출할 수 있다.

필드와 메소드이 접근 제한

필드와 메소드도 마찬가지로 어떤 접근 제한을 갖느냐에 따라 호출 여부가 결정된다 .

접근 제한자생성자설명
public클래스(...)모든 패키지에서 필드를 읽고 변경 가능하며 메소드를 호출 가능
클래스(...)같은 패키지에서만 필드를 읽고 변경 가능하며 메소드를 호출 가능
private클래스(...)클래스 내부에서만 필드를 읽고 변경 가능하며 메소드를 호출할 수 있다.

6.14 Getter 와 Setter

객체의 필드를 외부에서 마음대로 읽거나 변경할 경우 객체의 무결성이 꺠질 수 있는데 이러한 문제점 때문에 객체 지향 프로그래밍에서는 직접적인 외부에서의 필드 접근을 막고 대신 메소드를 통해 필드에 접근하는 것을 선호한다. 메소드를 통해서 데이터를 검증 후 유효한 값만 필드에 저장할 수 있기 때문이고, 그 역할을 하는 메소드가 Setter이다. ( 제공된 변경값을 if 문으로 검증 후 음수일 경우 0을 필드값으로 저장한다.

외부에서 객체의 필드를 읽을 떄에도 메소드가 필요한 경우가 있는데 필드값이 객체 외부에서 사용하기에 부적절한 경우, 메소드로 적절한 값으로 변환해서 리턴할 수 있기 때문이다. 이러한 역할을 하는게 Getter이다.

필드 타입이 boolean일 경우에는 Getter 는 get으로 시작하지 않고 is로 시작하는것 이 관례이다.

싱글톤 패턴

애플리케이션 전체에서 단 하나의 객체만 생성, 사용하고 싶다면 싱글톤 패턴을 적용할 수 잇으며 핵심은 생성자를 private 접근 제한해서 외부에서 new 연산자로 생성자를 호출할 수 없도록 막는 것이다.

확인 문제

1번. 3

2번. 4

3번. 2 > 4 : 클래스는 필드와 메소드를 반드시 가질 필요는 없다.

4번. 3

5번. 1

6번. 4

7번. 2

8번. 2

9번. 2

10. 4

11. 2

12. 필드, 생성자, 메소드

13.

public class Member {
    String name;
    String id;
    String password;
    int age;
}

14.

public class Member {
    String name;
    String id;
    String password;
    int age;
    
    Member(String name, String id) {
    this.name = name;
    this.id = id;
}

15.

public class MemberService {
    boolean login(String id, String password) {
        if (id.equals("hong") && password.equals("12345")) {
            return true;
        } else {
            return false;
        }
    }
    void logout (String id) {
        System.out.println(id + "님이 로그아웃 되었습니다.");
    }
}

16.

public class Printer {

    void println(int a) {
        System.out.println(a);
    }
    void println(boolean a) {
        System.out.println(a);
    }
    void println(double a) {
        System.out.println(a);
    }
    void println(String a) {
        System.out.println(a);
    }
}

17.

public class Printer1 {
    static void println(int a) {
        System.out.println(a);
    }
    static void println(boolean a) {
        System.out.println(a);
    }
    static void println(double a) {
        System.out.println(a);
    }
    static void println(String a) {
        System.out.println(a);
    }
}

18.

public class ShopService {
    private static  ShopService singleton = new ShopService();

    private ShopService()  {}

    static ShopService getInstance() {
        return singleton;
    }
}

19.

public class Account {
    public static final int MIN_BALANCE = 0;
    public static final int MAX_BALANCE = 1000000;
    private int balance;

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        if (balance < Account.MIN_BALANCE || balance > Account.MAX_BALANCE) {
            return;
        }
        this.balance = balance;
    }
}

20.

public class Account {
    private String ano;
    private String owner;
    private int balance;

    public Account(String ano, String owner, int balance) {
        this.ano = ano;
        this.owner = owner;
        this.balance = balance;
    }

    public String getAno() {
        return ano;
    }

    public void setAno(String ano) {
        this.ano = ano;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }
}
import java.util.Scanner;

public class BankApplication {
    private static Account[] accountArray = new Account[100];
    private static Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        boolean run = true;
        while (run) {
            System.out.println("----------------------------------------------------------");
            System.out.println("1.계좌생성 | 2.계좌목록 | 3.예금 | 4.출금 | 5.종료");
            System.out.println("----------------------------------------------------------");
            System.out.print("선택> ");

            int selectNo = scanner.nextInt();
            if (selectNo == 1) {
                createAccount();
            } else if (selectNo == 2) {
                accountList();
            } else if (selectNo == 3) {
                deposit();
            } else if (selectNo == 4) {
                withdraw();
            } else if (selectNo == 5) {
                run = false;
            }
        }
        System.out.println("프로그램 종료");
    }

    //계좌생성하기
    private static void createAccount() {
        System.out.println("--------------");
        System.out.println("계좌생성");
        System.out.println("--------------");

        System.out.print("계좌번호: ");
        String ano = scanner.next();

        System.out.print("계좌주: ");
        String owner = scanner.next();

        System.out.print("초기입금액: ");
        int balance = scanner.nextInt();

        Account newAccount = new Account(ano, owner, balance);
        for (int i = 0; i < accountArray.length; i++) {
            if (accountArray[i] == null) {
                accountArray[i] = newAccount;
                System.out.println("결과: 계좌가 생성되었습니다.");
                break;
            }
        }
    }

    //계좌목록보기
    private static void accountList() {
        System.out.println("--------------");
        System.out.println("계좌목록");
        System.out.println("--------------");
        for (int i = 0; i < accountArray.length; i++) {
            Account account = accountArray[i];
            if (account != null) {
                System.out.print(account.getAno());
                System.out.print(" ");
                System.out.print(account.getOwner());
                System.out.print(" ");
                System.out.print(account.getBalance());
                System.out.println();
            }
        }

    }

    //예금하기
    private static void deposit() {
        System.out.println("--------------");
        System.out.println("예금");
        System.out.println("--------------");
        System.out.print("계좌번호: ");
        String ano = scanner.next();
        System.out.print("예금액: ");
        int money = scanner.nextInt();
        Account account = findAccount(ano);
        if (account == null) {
            System.out.println("결과: 계좌가 없습니다.");
            return;
        }
        account.setBalance(account.getBalance() + money);
        System.out.println("결과: 예금이 성공되었습니다.");
    }

    //출금하기
    private static void withdraw() {
        System.out.println("--------------");
        System.out.println("출금");
        System.out.println("--------------");
        System.out.print("계좌번호: ");
        String ano = scanner.next();
        System.out.print("출금액: ");
        int money = scanner.nextInt();
        Account account = findAccount(ano);
        if (account == null) {
            System.out.println("결과: 계좌가 없습니다.");
            return;
        }
        account.setBalance(account.getBalance() - money);
        System.out.println("결과: 출금이 성공되었습니다.");
    }

    //Account 배열에서 ano와 동일한 Account 객체 찾기
    private static Account findAccount(String ano) {
        Account account = null;
        for (int i = 0; i < accountArray.length; i++) {
            if (accountArray[i] != null) {
                String dbAno = accountArray[i].getAno();
                if (dbAno.equals(ano)) {
                    account = accountArray[i];
                    break;
                }
            }
        }
        return account;
    }
}

0개의 댓글

관련 채용 정보