부품: 객체
조립한 완성품: 프로그램
객체
물리적으로 존재하거나 개념적인 것 중에서 다른것과 식별가능한 것
(자동차, 자전거, 책, 사람, 학과, 강의, 주문...)
객체는 속성과 동작으로 이루어짐
- 객체: 사람(Person)
- 속성(필드): 이름, 나이
- 동작(메소드): 웃다, 걷다
객체 모델링
현실 세계의 객체를 소프트웨어 객체로 설계하는것
현실 세계 객체의 대표 속성과 동작을 추려내어 소프트웨어 객체의 필드와 메소드로 정의하는 과정
객체와 객체 간의 상호작용으로 현실세계에서 일어나는 모든 현상을 설명할수 있다.
사람, 계산기 라는 객체
사람은 계산기의 더하기 기능
이용,
계산기는 사람에게 결과 리턴
이라는 상호작용을 한다.
메소드
객체들 사이의 상호작용 수단, 객체가 다른 객체의 기능을 이용할때 메소드 호출, 메소드 이름과 함께 전달하고자하는 데이터(매개값)를 괄호 안에 전달한다.
- 형태 :
메소드명(매개값1, 매개값2, ...);
- 매개값 : 메소드 실행시 필요한 값
- 리턴값 : 메소드 실행의 결과, 호출한 곳으로 돌려주는 값
객체는 단독 존재 가능하지만 대부분 다른 객체와 관계(집합, 사용, 상속)를 맺고 있음
완성품(자동차)과 부품(엔진, 타이어, 핸들) 관계
다른 객체의 필드를 읽고/변경하거나 메소드 호출하는 관계
사람이 자동차의 메소드(달린다, 멈춘다)를 호출
부모/자식 관계
자동차가 기계의 특징(필드, 메소드)을 물려받음
부모(기계), 자식(자동차)
객체의 데이터(필드), 동작(메소드)을 하나로 묶고 실제 구현 내용을 외부에 감추는것
캡슐화 장점
외부의 잘못된 사용으로 인한 객체 손상을 막는다.
외부 객체는 객체 내부 구조를 모른다.
객체가 노출해서 제공하는 필드와 메소드만 이용 가능
자바는 접근제한자
를 이용해 노출 여부를 결정할 수 있다.
부모역할의 상위 객체: 가진 필드/메소드 를 자식 객체에게 물려줌
자식역할의 하위 객체: 부모 객체의 필드/메소드를 사용
상속 장점
- 코드 재사용성 높여줌 : 부모 객체 필드/메소드 자식이 사용가능해서 중복 코딩 할 필요없음
- 유지 보수 시간 줄여줌 : 부모 객체의 필드/메소드를 수정하면 모든 자식 객체들은 수정된 필드/메소드를 사용 가능
하나의 객체가 여러 가지 타입을 가질 수 있는 것
같은 자료형에 여러 가지 객체를 대입하여 다양한 결과를 얻어내는 성질
자바에서는 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하여 구현
클래스의 상속이나 인터페이스를 구현하는 자식 클래스에서 메서드를 재정의(오버라이딩) 하고 자식 클래스를 부모 타입으로 업캐스팅한다. 그리고 부모 타입의 객체에서 자식 멤버를 참조하여 다형성을 구현
참조 변수의 다형성을 보여주는 예제
class Parent { ... } class Child extends Parent { ... } ... Parent pa = new Parent(); // 허용 Child ch = new Child(); // 허용 Parent pc = new Child(); // 허용 Child cp = new Parent(); // 오류 발생.
다형성을 구현하기 위해서는 자동 타입 변환
과 재정의(오버라이딩)
기술이 필요 => 상속/인터페이스 구현을 통해 이뤄짐
클래스 Class
객체 생성시 설계도면
객체 Instance
클래스로부터 생성된 객체, 해당 클래스의 인스턴스라고 부른다.
클래스 -> 객체 의 과정을 인스턴스화 라고 한다.
하나의 설계도로 여러 결과물을 만들듯이 하나의 클래스로 여러 객체 만들수 있음
클래스 선언
객체 생성을 위한 설계도를 작성하는 작업
- 생성자: 어떻게 객체를 생성할것인지 정의
- 필드: 어떤 데이터를 가질것인지 정의
- 메소드: 객체의 동작이 무엇인지 정의
클래스명
- 첫문자 대문자
- 캐멀 케이스로 작성
- 첫문자가 숫자가 될수 없음
- 특수 문자는 $, _ (달러, 언더바) 만 가능
하나의 소스파일에 복수의 클래스 선언 가능
(컴파일시 클래스 개수만큼 바이트 코드 파일 생김)
복수의 클래스 선언시 주의할점은 그중 소스파일명과 동일한 클래스만 공개(public) 클래스로 선언 가능 하다는점이다.
일단 특별한 이유 없으면 소스파일당 하나의 클래스로 선언하는게 좋다.
클래스로 부터 객체 생성하려면 객체생성 연산자인 new
가 필요하다.
클래스 변수 = new 클래스();
new 연산자 뒤에 생성자 호출 코드()가 붙는다.
new연산자 는 객체 생성 후 객체의 주소를 리턴
한다.
// 라이브러리 클래스
package ch06.sec04;
public class Student {
}
// 실행 클래스
package ch06.sec04;
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("s1변수가 또 다른 Student 객체를 참조합니다. ");
}
}
클래스의 두가지 용도
- 라이브러리 클래스 : 실행할 수 없으며 다른 클래스에서 이용하는 클래스
- 실행 클래스 : main()메소드를 가진 실행가능 클래스
클래스구성 멤버
- 생성자 : 객체 초기화 역할을 담당
- 필드/메소드 : 객체에 포함될 것들
객체의 데이터 저장하는 역할
new 연산자로 객체 생성시 객체의 초기화 역할 담당
선언 형태가 메소드와 비슷하게 생겼지만 리턴 타입 없음
,클래스 이름과 이름이 동일
객체가 수행할 동작
다른 언어에서는 함수라고도 하지만, 자바에서 객체 내부에 있는 함수는 메소드라고 한다.
객체와 객체간의 상호작용을 위해 호출된다.
~~객체의 데이터에는 고유 데이터, 현재 상태 데이터, 부품 데이터가 있다. ~~
필드 선언 방법은 변수 선언과 동일하다.
반드시 클래스 블록 안에서 선언되어야 필드 선언이다.
타입 필드명 [= 초기값];
필드
클래스 블록에서 선언
객체 내부에서 존재하고 객체 내•외부에서 사용 가능
(로컬)변수
생성자와 메소드 블록에서 선언
생성자와 메소드 호출시에만 생성/사용
초기값을 제공하지 않을 경우 필드는 객체 생성시 자동으로 기본값으로 초기화된다.
정수 타입 필드는 0, 실수는 0.0 , boolean 필드는 false로 초기화, 참조 타입은 객체를 참조하지 않은 상태인 null로 초기화
package ch06.sec06.exam01;
public class Car {
String model;
boolean isStart;
int speed;
}
package ch06.sec06.exam01;
public class CarExample {
public static void main(String[] args) {
// Car 객체 생성
Car myCar = new Car();
//Car 객체의 필드값 읽기
System.out.println("모델명 : " + myCar.model); // null
System.out.println("시동 여부 : " + myCar.isStart); // false
System.out.println("현재 속도 : " + myCar.speed); // 0
}
}
필드는 객체의 데이터이므로 객체가 존재하지 않으면 필드도 존재하지 않기때문에 클래스로부터 객체 생성이 된 다음에야 필드 사용 가능
필드는 생성자에서, 메소드 안에서, 객체 외부에서도 접근해서 사용가능
객체 외부에서 접근하려면 참조변수
, 도트 연산자(객체 접근연산자)
를 이용해야 필드 읽기/변경 가능
package ch06.sec06.exam02;
public class Car {
// 필드 선언
String company = "현대자동차";
String model = "그랜저";
String color = "검정";
int maxSpeed = 350;
int speed;
}
package ch06.sec06.exam02;
public class CarExample {
public static void main(String[] args) {
// Car 객체 생성
Car myCar = new Car();
// Car 객체의 필드값 읽기
System.out.println("제작회사 : " + myCar.company); // 현대자동차
System.out.println("모델명 : " + myCar.model); // 그랜저
System.out.println("색깔 : " + myCar.color); // 검정
System.out.println("최고 속도 : " + myCar.maxSpeed); // 350
System.out.println("현재 속도 : " + myCar.speed); // 0
// Car 객체의 필드값 변경
myCar.speed = 60;
System.out.println("수정된 속도 : " + myCar.speed); // 60
}
}
new 연산자는 객체를 생성한 후 연이어 생성자를 호출해서 객체를 초기화하는 역할을 한다.
클래스 변수 = new 클래스()
에서 '클래스()' 부분이 생성자를 호출하는 부분이다.모든 클래스에는 생성자 존재하고, 하나 이상 가질 수 있음
클래스에 생성자 없다면 컴파일러가 자동으로 기본 생성자를 바이트코드 파일에 추가해준다.
하지만 개발자가 명시적으로 선언한 생성자가 존재하면 컴파일러는 기본 생성자 추가해주지 않음
객체 다양하게 초기화 하기 위해서 개발자가 직접 생성자 선언할수 있음
생성자는 리턴타입이 없고, 클래스 이름과 동일하다.
매개변수는 new연산자로 생성자 호출시에 매개값을 생성자 블록 내부로 전달하는 역할
생성자 선언
클래스(매개변수,...) { // 객체의 초기화 코드 }
Car myCar = new Car("그랜저", "검정", 300);
//----------------------------------------
public class Car {
// 생성자 선언
Car(String model, String color, int maxSpeed) {...}
}
객체마다 동일한 값을 갖고있다면 필드 선언시 초기값을 대입하는게 좋다.
객체마다 다른값을 가진다면 생성자에서 필드를 초기화하는것이 좋다.
package ch06.sec07.exam02;
public class Korean {
// 필드 선언
String nation = "대한민국";
String name;
String ssn;
// 생성자 선언
public Korean(String n, String s) {
name = n;
ssn = s;
}
}
package ch06.sec07.exam02;
public class KoreanExample {
public static void main(String[] args) {
// Korean 객체 생성
Korean k1 = new Korean("박자바", "011255-1234567");
// Korean 객체 데이터 읽기
System.out.println("k1.nation : " + k1.nation);
System.out.println("k1.name : " + k1.name);
System.out.println("k1.ssn : " + k1.ssn);
System.out.println(); // 대한민국, 박자바, 011255-1234567
// 또 다른 Korean 객체 생성
Korean k2 = new Korean("김자바", "923255-6847456");
// 또 다른 Korean 객체 데이터 읽기
System.out.println("k2.nation : " + k2.nation);
System.out.println("k2.name : " + k2.name);
System.out.println("k2.ssn : " + k2.ssn);
// 대한민국, 김자바, 923255-6847456
}
}
매개변수 이름을 필드명과 동일하게 해도 되는데, 그러면 필드임을 구분하기 위해 필드는 this.필드명 으로 작성해주어야한다. this는 현재 객체, this.name은 현재 객체의 데이터(필드)로서의 name을 뜻한다.
public class Korean {
String nation = "대한민국";
String name;
String ssn;
public Korean(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
}
이클립스, 인텔리제이에서 필드와 매개변수 색깔을 구별해주므로 그것으로 알아볼 수도 있다.
생성자 오버로딩
매개값으로 객체의 필드를 다양하게 초기화하는것
변수를 달리하는 생성자를 여러개 선언한다.
매개변수의 타입, 개수, 순서가 다르게 여러개의 생성자 선언
생성자가 오버로딩 되어있을경우, new 연산자로 생성자를 호출할때 제공되는 매개값의 타입과 수에 따라 실행될 생성자가 결정된다.
package ch06.sec07.exam04;
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;
}
}
package ch06.sec07.exam04;
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("택시", "검정", 200);
System.out.println("car4.company : " + car4.company);
System.out.println("car4.model : " + car4.model);
System.out.println("car4.color : " + car4.color);
System.out.println("car4.maxSpeed : " + car4.maxSpeed);
System.out.println();
// car1.company : 현대자동차
//
// car2.company : 현대자동차
// car2.model : 자가용
//
// car3.company : 현대자동차
// car3.model : 자가용
// car3.color : 빨강
//
// car4.company : 현대자동차
// car4.model : 택시
// car4.color : 검정
// car4.maxSpeed : 200
}
}
생성자 오버로딩이 많아질 경우 생성자 간 중복 코드가 발생할수 있다.
매개변수의 수만 달리하고 필드 초기화 내용이 비슷한 생성자에서 이런 중복 코드를 많이 볼 수 있다.
공통 코드를 한 생성자에 집중 작성하고, 나머지 생성자는 this(매개값, ...)를 사용해서 공통코드를 갖고있는 생성자를 호출하는 방법으로 개선할수있음
// 맨밑의 공통초기화 코드가 있는 생성자를 호출하는 두 생성자
Car(String model) {
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;
}
this(매개값, ...)는 생성자의 첫줄에 작성되며, 다른 생성자를 호출하는 역할을 한다.
호출하고 싶은 생성자의 매개변수에 맞게 매개값을 제공하면 된다.
package ch06.sec07.exam05;
public class Car {
// 필드 선언
String company = "현대자동차";
String model;
String color;
int maxSpeed;
// 생성자 선언
Car(String model) {
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;
}
}
package ch06.sec07.exam05;
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("car3.company : " + car3.company);
System.out.println("car3.model : " + car3.model);
System.out.println("car3.color : " + car3.color);
System.out.println("car3.maxSpeed : " + car3.maxSpeed);
System.out.println();
// car1.company : 현대자동차
// car1.model : 자가용
//
// car2.company : 현대자동차
// car2.model : 자가용
// car2.color : 빨강
//
// car3.company : 현대자동차
// car3.model : 자가용
// car3.color : 검정
// car3.maxSpeed : 200
}
}
메소드 선언
객체의 동작을 실행블록으로 정의하는것
메소드 호출
실행 블록을 실제로 실행하는것
메소드는 객체 내부에서도 호출되지만 다른 객체에서도 호출될 수 있기때문에 객체간의 상호작용하는 방법을 정의하는것이라고 볼수 있다.
메소드 선언
- 리턴 타입 : 메소드가 리턴하는 값의 타입, 리턴값이 없는 메소드는
void
로 작성- 메소드명 : 첫문자는 소문자, 캐멀 케이스로 작성
- 매개변수 : 메소드가 실행할때 필요한 매개값을 전달받기 위한 변수
- 선언부 :
리턴타입 메소드명 (매개변수, ....)
- 실행블록 : { } 중괄호 안에 실행할 코드를 작성하는 곳
package ch06.sec08.exam01;
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;
}
// 호출시 두 정수 값을 전달받고 호출한 곳으로 double 값을 리턴하는 메소드 선언
double divide(int x, int y) {
double result = (double) x / (double) y;
return result;
}
}
메소드 호출 == 메소드 블록 실행
클래스로부터 객체가 생성된 후여야 생성자나 다른 메소드 내부에서 메소드명으로 메소드 호출 가능
객체 외부에서는 외부 객체에서 참조변수와 도트 연산자로 호출한다.
메소드가 매개변수 가지고있다면 호출시 매개변수의 타입고 수에 맞게 매개값을 제공해야한다.
메소드가 리턴값이 있을경우에는 대입 연산자를 사용해서 다음과 같이 리턴값을 변수에 저장할 수 있다.
이때 변수 타입은 메소드의 리턴타입과 동일하거나 자동 타입변환 가능해야한다.
타입 변수 = 메소드();
package ch06.sec08.exam01;
public class CalculatorExample {
public static void main(String[] args) {
// Calculator 객체 생성
Calculator myCalc = new Calculator();
// 리턴값이 없는 powerOn() 메소드 호출
myCalc.powerOn(); // 전원을 켭니다.
// plus 메소드 호출시 5와 6을 매개값으로 제공하고, 덧셈 결과를 리턴받아 result1 변수에 대입
int result1 = myCalc.plus(5,6);
System.out.println("result1 : " + result1); // result1 : 11
int x = 10;
int y = 4;
// devide() 메소드 호출시 변수 x와 y의 값을 매개값으로 제공하고, 나눗셈 결과를 리턴받아 result2 변수에 대입
double result2 = myCalc.divide(x, y);
System.out.println("result2 : " + result2); // result2 : 2.5
// 리턴값이 없는 powerOff() 메소드 호출
myCalc.powerOff(); // 전원을 끕니다.
}
}
// 가변길이 매개변수
int sum(int ... values) {
}
// 매개값들은 자동으로 배열항목으로 변환되어 메소드에서 사용된다.
int result = sum(1,2,3);
int result = sum(1,2,3,4,5);
// 메소드 호출시 직접 배열을 매개값으로 제공해도 된다.
int[] values = {1,2,3};
int result = sum(values);
int reusult = sum(new int[] {1,2,3});
메소드 호출할때 매개변수의 개수에 맞게 매개값을 제공해야한다.
하지만 만약 메소드가 가변길이 매개변수를 갖고있다면 매개변수의 개수와 상관없이 매개값을 줄 수 있다.
package ch06.sec08.exam02;
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;
}
}
package ch06.sec08.exam02;
public class ComputerExample {
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);//result1 : 6
// sum() 메소드 호출시 매개값 1,2,3,4,5 를 제공하고 합산결과를 리턴받아 result2 변수에 대입
int result2 = myCom.sum(1,2,3,4,5);
System.out.println("result2 : " + result2); // result2 : 15
// sum() 메소드 호출시 배열을 제공하고, 합산결과를 리턴받아 result3 변수에 대입
int[] values = {1,2,3,4,5};
int result3 = myCom.sum(values);
System.out.println("result3 : " + result3); // result3 : 15
// sum() 메소드 호출시 배열을 제공하고, 합산결과를 리턴받아 result4 변수에 대입
int result4 = myCom.sum(new int[] {1,2,3,4,5});
System.out.println("result4 : " + result4); // result4 : 15
}
}
return문
메소드의 실행을 강제 종료하고 호출한 곳으로 돌아간다는 의미
메소드 선언에 리턴 타입이 있을 경우 return문 뒤에 리턴값을 추가 지정해야한다.
return [리턴값];
return문 이후에 실행문 작성해도 실행되지 않으므로 'Unreachable'라는 컴파일 에러 발생
package ch06.sec08.exam03;
public class Car {
// 필드 선언
int gas;
// 리턴값이 없는 메소드로 매개값을 받아서 gas 필드값을 변경
void setGas(int gas) {
this.gas = gas;
}
boolean ifLeftGas() {
if(gas == 0) {
System.out.println("gas 없음");
return false;
}
System.out.println("gas 있음");
return true;
}
// return값이 없는 메소드로 gas 필드값이 0이면 return 문으로 메소드를 종료
void run() {
while (true) {
if(gas > 0) {
System.out.println("달립니다. (gas잔량: " + gas + ")");
gas -= 1;
} else {
System.out.println("멈춥니다. (gas잔량: " + gas + ")");
return; //메소드 종료
}
}
}
}
package ch06.sec08.exam03;
public class CarExample {
public static void main(String[] args) {
// Car 객체 생성
Car myCar = new Car();
// 리턴값이 없는 setGas() 메소드를 호출
myCar.setGas(5);
// ifLeftGas() 메소드를 호출해서 받은 리턴값이 true일 경우 if 블록 실행
if(myCar.ifLeftGas()) {
System.out.println("출발합니다.");
//리턴값이 없는 run() 메소드 호출
myCar.run();
}
System.out.println("gas를 주입하시오");
// gas 있음
// 출발합니다.
// 달립니다. (gas잔량: 5)
// 달립니다. (gas잔량: 4)
// 달립니다. (gas잔량: 3)
// 달립니다. (gas잔량: 2)
// 달립니다. (gas잔량: 1)
// 멈춥니다. (gas잔량: 0)
// gas를 주입하시오
}
}
메소드 오버로딩
다양한 매개값을 처리하기 위해 메소드 이름은 같지만, 매개변수의 타입,개수,순서가 다른 메소드를 여러개 선언하는것
package ch06.sec08.exam04;
public class Calculator {
// 정사각형의 넓이
double areaRectangle(double width) {
return width * width;
}
// 직사각형의 넓이
double areaRectangle(double width, double height) {
return width * height;
}
}
package ch06.sec08.exam04;
public class CalculatorExample {
public static void main(String[] args) {
// 객체 생성
Calculator myCalcu = new Calculator();
// 정사각형의 넓이 구하기
double result1 = myCalcu.areaRectangle(10);
// 직사각형의 넓이 구하기
double result2 = myCalcu.areaRectangle(10, 20);
System.out.println("정사각형 넓이: " + result1);
System.out.println("직사각형 넓이: " + result2);
}
}
출처: 이것이 자바다 (개정판) : JAVA 프로그래밍의 기본서 - 신용권, 임경균 저