객체 지향 프로그래밍(OOP: Object-Oriented Programming) : 객체를 하나씩 조립해서 완성된 프로그램을 만드는 기법
객체(object) : 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 가지고 있으면서 식별 가능한 것
- 객체는 속성(field)과 동작(method)으로 구성
- 객체 모델링(object modeling) : 현실 세계의 객체를 소프트웨어 객체로 설계하는 것
- 물리적인 객체 : 자동차, 자전거, 책, 사람
- 추상적인 객체 : 학과, 강의, 주문
=> 사람 : 속성(이름, 나이) / 동작(웃다, 먹다)
=> 자동차 : 속성(색깔, 속도) / 동작(달린다, 멈춘다)
객체들은 독립적으로 존재
다른 객체와 서로 상호작용하여 동작(=메서드 호출)
1. 집합 관계 : 하나의 객체는 부품, 하나의 객체는 완성품
=> 부품과 자동차는 집합 관계
2. 사용 관계 : 객체 간의 상호작용
=> 사람과 자동차는 사용 관계
3. 상속 관계 : 부모 객체를 기반으로 자식 객체를 생성하는 관계
=> 기계와 자동차는 상속 관계
설계도 = 클래스
클래스로 만든 객체는 인스턴스
- 클래스의 이름은 다른 클래스와 식별할 목적으로 사용
- 클래스 이름도 대소문자를 구분 , 첫 글자는 보통 대문자로 혼합된 이름을 사용한다면 각 단어의 첫 글자는 대문자로 작성
식별자 작성 규칙
- 하나 이상의 문자로 이루어져야 함
- 첫 글자에는 숫자 불가
- '$', '_' 외의 특수 문자 사용 불가
- 자바 키워드 사용 불가
클래스 선언 예시
public class Car {
}
public class Tire{
}
// 클래스 선언
public class Student {
}
public static void main(String[] args) {
// 객체 생성
Student s1 = new Student();
System.out.println("s1 변수가 Student 객체를 참조합니다.");
// 객체 생성
Student s2 = new Student();
System.out.println("s2 변수가 또 다른 Student 객체를 참조합니다.");
}
1. 필드(Field)
객체의 데이터가 저장되는 곳
생성자와 메서드 전체에서 사용되며 객체가 소멸되지 않는 한 객체와 함께 존재
2. 생성자(Constructor)
객체 생성 시 초기화 역할 담당
new 연산자로 호출되는 특별한 중괄호 {}블록
클래스 이름으로 되어 있고 리턴 타입이 없음
3. 메서드(Method)
객체의 동작에 해당하는 실행 블록
메서드를 호출 하면 중괄호 블록에 있는 모든 코드들아 알괄적으로 실행
객체 간의 데이터를 전달하는 수단
public class ClassName{
// 필드
int fieldName;
// 생성자
ClassName(){
}
// 메서드
void methodName(){
}
}
클래스 중괄호 {} 블록 어디서든 존재
초기값이 지정되지 않은 필드는 객체 생성 시 자동으로 기본 초기값 설정
=> 정수 타입 : 0 , 실수 타입 : 0.0, boolean 타입 : false, 참조 타입 : null
필드 선언 예시
String company = "현대자동차";
String model = "그랜저";
int maxSpeed = 300;
int productionYear;
boolean engineStart;
필드값을 읽고 변경 하는 작업
클래스 내부에서 사용 시 필드 이름으로 읽고 변경, 클래스 외부에서 사용 시 클래스로부터 객체 생성한 뒤 사용 가능
public class Car {
// 필드
String company = "현대자동차";
String model = "그랜저";
String color = "검정";
int maxSpeed = 350;
int speed; // 0, 초기값 지정하지 않았기 때문에 자동으로 기본 초기값 설정
}
public static void main(String[] args) {
// 객체 생성
Car myCar = new Car();
// 필드값 읽기
System.out.println("제작 회사: " + myCar.company);
System.out.println("모델명: " + myCar.model);
System.out.println("색깔: " + myCar.color);
System.out.println("최고속도: " + myCar.maxSpeed);
System.out.println("현재속도: " + myCar.speed);
// 필드값 변경
myCar.speed = 60;
System.out.println("수정된 속도: " + myCar.speed);
}
모든 클래스는 생성자가 반드시 존재
클래스 내부에 생성자 선언을 생략하여도 컴파일러는 기본 생성자를 바이트 코드에 자동 추가
but, 명시적으로 선언한 생성자가 하나라도 있다면 컴파일러는 기본 생성자를 추가하지 않음
public class Car{
// 기본 생성자
// public Car(){ // 이 부분을 생략하고 작성 하여도 기본 생성자는 자동으로 추가 됨
//
// }
}
리턴 타입이 없고 클래스 이름과 동일
매개 변수 선언은 생략도 가능하며 여러 개 선언도 가능
=> 매개 변수는 생성자를 호출할 때 외부의 값을 생성자 블록 내부로 전달하는 역할
public class Car {
// 명시적 생성자
Car(String color, int cc){
System.out.println(color);
System.out.println(cc);
System.out.println(color + "색의 " + cc + "cc 자동차가 생성");
}
}
public class CarExample {
public static void main(String[] args) {
// 생산자를 명시적으로 사용할 경우 기본 생성자 자동으로 설정되지 않는다.
Car myCar = new Car("검정", 3000);
}
}
필드 선언시 초기값을 줌
=> 클래스로 부터 객체가 생성될 때 필드는 기본 초기값으로 자동 설정
생성자에서 초기값을 줌
=> 객체 생성 시 외부에서 제공되는 다양한 값들로 초기화 될 경우 사용
=> 필드와 매개 변수의 이름이 동일한 이름을 사용하는 것이 일반적이다. 헷갈릴 수 있으므로 필드앞에 this를 붙여 매개변수와 구분할 수 있다.
public class Korean {
// Field
String nation = "대한민국"; // 국적, 필드 선언 시 초기값을 줌
String name; // 이름 // 생성자에서 초기값을 줌
String ssn; // 주민번호 // 생성자에서 초기값을 줌
// 생성자
public Korean(String name, String ssn) {
this.name = name; // this.name은 필드, name은 생성자의 매개 변수
this.ssn = ssn;
}
}
public class KoreanExample {
public static void main(String[] args) {
Korean k1 = new Korean("박자바", "011225-1234567");
System.out.println(k1.nation); // 대한민국, 초기값에서 설정
System.out.println(k1.name); // 박자바, 생성자에서 초기값 설정
System.out.println(k1.ssn); // 011225-1234567, 생성자에서 초기값 설정
}
}
매개 변수를 달리하는 생성자를 여러 개 선언하는 것
매개 변수의 타입과 개수 그리고 선언된 순서가 똑같을 경우 매개 변수의 이름만 바꾸는 것은 생성자의 오버로딩이 아님
public class Car(){
Car(){}
Car(String model){}
Car(String model, String color){}
Car(String model, String color, int maxSpeed){}
}
같은 클래스 내에서 다른 생성자를 호출하는 역할
생성자의 첫줄에만 허용
Car(String model){
this(model, null, 0); // this 생성자의 첫 줄에만 허용
}
Car(String model, String color, int maxSpeed) {
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed;
}
메서드 선언은 선언부(=메서드 시그니처)와 실행 블록으로 구성
리턴 타입 : 메서드가 리턴하는 결과의 타입을 표시
메서드 이름 : 기능이 드러나도록 식별자 규칙에 맞게 이름을 지어준다
매개 변수 선언 : 필요한 데이터를 받기 위한 변수 선언
메서드 실행 블록 : 실행할 코드 작성
1. 리턴 타입
void powerOn() {} // void로 리턴값이 없음
powerOn(); // 리턴값이 없으므로 단순히 메서드만 호출
double divided(int x, int y) {} // double이라는 리턴값이 있음
double result = divided(10, 20); // 리턴값이 있으므로 저장할 변수가 필요
2. 메서드 이름
숫자로 시작 x, $와 _를 제외한 특수 문자 사용 x
메서드 이름은 소문자로 작성
서로 다른 단어가 혼합된 이름은 단어의 첫 글자는 대문자로 작성
void run() {}
void startEngine() {}
String getName() {}
3-1. 매개 면수 선언
메서드 실행 시 필요한 데이터를 외부로부터 받기 위해 사용
매개 변수가 필요한 경우도 있고 필요 없는 경우도 있다
매개 변수가 있는 매서드 선언 시 메서드를 호출 시 반드시 매개값을 주어야 함
double divided(int x, int y) {} // 매개 변수가 있는 메서드 선언
double result = divided(10, 20); // 매서드 호출 시 매개값 주어야 함
3-2. 매개 변수의 개수를 모를 경우
1. 배열 타입 방법
int sum1(int[] values) { }
2. 값의 목록만 넘겨주는 방법
int sum2(int ... values) { }
1. 리턴값이 있는 메서드
return값을 지정해야 함
return문이 없다면 컴파일 에러 발생, return문 실행되면 메서드는 즉시 종료
int plus(int x, int y) {
int result = x + y;
return result;
}
2. 리턴값이 없는 메서드 : void
리턴값이 없는 메서드는 void로 작성
void로 선언된 메서드도 return문을 사용 가능
=> 메서드 실행을 강제 종료
void run() {
while (true) { // 무한반복
if (gas > 0) {
System.out.println("달립니다.(gas 잔량:" + gas + ")" );
gas -= 1;
} else {
System.out.println("멈춥니다.(gas 잔량:" + gas + ")" );
return; // 메서드 종료
}
}
}
1. 객체 내부에서 호출
메서드 이름으로 호출
리턴값이 있는 메서드를 호출하고 리턴값을 받고 싶다면 변수를 선언하고 리턴값을 대입
public class ClassName{
void method1(String p1, int p2) {
}
void method2() {
method1("홍길동", 100); // 메서드 이름으로 호출
}
}
2. 객체 외부에서 호출
public class Car {
void keyTurnOn() {
System.out.println("키를 돌립니다.");
}
}
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car(); // 객체 생성
myCar.keyTurnOn(); // 객체 생성으로 메서드 호출 가능
}
}
클래스 내에 같은 이름의 메서드를 여러 개 선언하는 것
매개 변수의 타입, 개수, 순서 중 하나가 달라야 함
public class Calculator {
double areaRectangle(double width) {
return width * width;
}
double areaRectangle(double width, double height) {
return width * height;
}
}
인트턴스 멤버 : 객체마다 가지고 있는 멤버
정적 멤버 : 클래스에 위치시키고 객체들이 공유하는 멤버
1. 인스턴스 멤버 선언
public class Car{
// 인스턴스 필드
int gas;
// 인스턴스 메서드
void setSpeed(int speed) {
}
}
2. this
객체 자신을 가리키는 것
생성자와 메서드의 매개 변수의 이름이 필드와 동일한 경우, 인스턴스 멤버인 필드임을 명시 할 때 사용
public class Car {
// 인스턴스 필드
String model;
int speed;
// 생성자
Car(String model){
this.model = model;
}
// 메서드
void setSpeed(int speed) {
this.speed = speed;
}
}
정적 멤버 : 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메서드
객체마다 가지고 있을 필요가 없는 공용 데이터라면 정적 필드로 선언하는 것이 좋음
1. 정적 멤버 선언 : static 키워드를 사용
public 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;
}
}
2. 정적 멤버 사용
double result1 = 10 * 10 * Calculator.pi; // 정적 필드이기 때문에 클래스로 접근
int result2 = Calculator.plus(10, 5);
int result3 = Calculator.minus(10, 5);
3. 정적 메서드 선언 시 주의할 점
public class Car {
// 인스턴스 필드
int speed;
// 인스턴스 메서드
void run() {
System.out.println(speed + "으로 달립니다.");
}
public static void main(String[] args) {
// static 안에 인스턴스 필드와 메서드를 사용하기 위해 객체 생성 후 접근
Car myCar = new Car();
myCar.speed = 60;
myCar.run();
}
}
1. private 접근 제한자를 붙여준다.
=> 클래스 외부에서 new 연산자로 생성자 호출을 막음
2. 자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성해 초기화한다.
=> 클래스 내부에서는 new 연산자로 생성자 호출이 가능
=> 정적 필드도 private 접근 제한자를 붙여 외부에서 필드값 변경을 막음
3. 외부에서 호출할 수 있는 정적 메서드인 getInstance()선언하고 정적 필드에서 참조하는 자신의 객체를 리턴
public class Singleton {
// 2. 자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성해 초기화한다.
private static Singleton singleton = new Singleton();
//1. private 접근 제한자를 붙여준다.
private Singleton() {}
// 3. 외부에서 호출할 수 있는 정적 메서드인 getInstance()선언하고 정적 필드에서 참조하는 자신의 객체를 리턴
static Singleton getInstance() {
return singleton;
}
}
1. final 필드
- final 필드의 초기값 주는 방법
- 필드 선언 시 초기값 주기
- 생성자에서 초기값 주기
public class Person {
// 필드
final String nation = "Korea"; // 필드 선언 시 초기값 주기
final String ssn;
String name;
public Person(String ssn , String name) {
this.ssn = ssn; //생성자에서 초기값 주기
this.name = name;
}
}
2. 상수(static final)
불변의 값 ex) 원주율 파이, 지구의 무게 및 둘레
static이면서 final이어야 한다
=> static final 필드는 객체마다 존재하지 않고 클래스에만 존재, 변경 불가
상수이름은 대문자로 작성
public class Earth {
static final double EARTH_RADIUS = 6400;
static final double EARTH_AREA = 4 * Math.PI * EARTH_RADIUS;
}
package sec06.exam01.com.mycompany;
public class Car {
}
패키지 이름 규칙
숫자로 시작 x, $와 _ 제외한 특수 문자 사용 x
java로 시작하는 패키지는 사용 x
모두 소문자로 작성
클래스 또는 인터페이스가 다른 패키지에 속한다면 import문을 사용하여 컴파일러에게 알려줘야 한다.
패키지 선언과 클래스 선언 사이에 작성
서로 다른 패키지에 동일한 클래스 이름이 존재할 때 import문을 사용 시 패키지가 포함된 클래스 전체 이름을 기술하여 구분한다.
접근 제한자 종류
- public 접근 제한자 : 외부 클래스가 자유롭게 사용 가능
- protected 접근 제한자 : 같은 패키지 또는 자식 클래스에서 사용 가능
- private 접근 제한자 : 외부에서 사용 불가
- default 접근 제한 : 같은 패키지에 소속된 클래스에서만 사용 가능
Getter : 외부에서 객체의 데이터를 읽을 때 사용
=> 메서드로 필드값을 가공한 후 외부로 전달
Setter : 외부에서 메서드를 통해 필드에 접근하도록 유도
=> 메서드는 매개값을 검증해서 유효한 값만 객체의 필드로 저장하게 함