객체(Object) : 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 가지고 있고, 다른 것과 식별 가능한 것.
속성 = 필드(field)
동작 = 메소드(method) : 객체들 사이의 상호작용 수단은 메소드
객체 모델링(Object Modeling) : 현실 세계의 객체를 소프트웨어 객체로 설계하는 것
메소드 호출 : 객체가 다른 객체의 기능을 이용하는 것
객체 간의 관계 : 집합 관계, 사용 관계, 상속 관계
[객체 간의 관계]
객체 지향 프로그래밍의 특징
캡슐화(Encapsulation)
객체의 필드, 메소드를 하나로 묶고, 실제 구현 내용을 감추는 것
외부 객체는 객체의 내부 구조를 알 수 없다.
자바 언어는 캡슐화된 멤버를 노출시킬 것인지, 숨길 것인지 접근 제한자(Access Modifier) 사용
상속(Inheritance)
부모가 가지고 있는 재산을 자식에게 물려주는 것
코드의 중복 최소화 가능
유지 보수 시간 최소화
다형성(Polymorphism)
같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질
자바는 다형성을 위해 부모 클래스 또는 인터페이스의 타입 변환을 허용
부모 타입에는 모든 자식 객체 대입 가능
인터페이스 타입에는 모든 구현 객체가 대입 가능
다형성으로 인해 객체는 부품화가 가능
객체와 클래스
현실에서 객체는 설계도를 바탕으로 만들어진다.
자바에서의 설계도는 클래스(class)이다.
클래스(class)
객체를 생성하기 위한 필드와 메소드가 정의되어 있다.
클래스로부터 만들어진 객체를 해당 클래스의 인스턴스(instance)라고 한다.
인스턴스화 : 클래스로부터 객체를 만드는 과정
객체지향 프로그래밍 개발 3단계
클래스 설계
설계된 클래스를 가지고 사용할 객체 생성
생성된 객체 사용
클래스 선언
클래스 이름은 자바의 식별자 작성 규칙을 따른다.
일반적으로 소스 파일당 동일한 이름의 하나의 클래스를 선언한다.
두 개 이상도 가능하지만, 컴파일을 하면 바이트 코드 파일은 클래스를 선언한 개수만큼 생기기 때문에, 파일 이름과 일치하지 않는 클래스 선언에 public 접근 제한자를 붙이면 컴파일 에러가 발생한다.
[자바의 식별자 작성 규칙]
객체 생성과 클래스 변수
new 연산자
클래스로부터 객체를 생성하는 방법.
연산자 뒤에 생성자가 온다.
new 연산자로 생성된 객체는 메모리 힙(heap) 영역에 생성된다.
힙 영역에 객체를 생성시킨 후, 객체의 주소를 리턴한다.
클래스 변수 = new 클래스();
[new 연산자 사용 후 메모리 구조]
클래스의 2가지 용도
라이브러리(API)용 : 다른 클래스에서 이용할 목적으로 설계
실행용 : main() 메소드를 제공하는 역할
프로그램을 하나의 클래스로 구성할 수 있지만, 객체 지향 프로그램은 대부분 라이브러리와 실행 클래스가 분리되어 있다.
public class studnet { // 라이브러리 클래스
}
public class StudentExample { // 실행 클래스
public static void main(String[] args){
...
}
}
클래스의 구성 멤버
필드(Field)
객체의 데이터가 저장되는 곳
변수와 비슷하지만. 변수는 생성자와 메소드 내에서만 사용되고 생성자는 메소드가 실행 종료되면 자동 소멸한다. 하지만 필드는 메소드 전체에서 사용되고, 객체가 소멸되지 않으면 객체와 함께 존재한다.
생성자(Constructor)
객체 생성 시 초기화 역할 담당
생성자는 메소드와 비슷하지만, 클래스 이름으로 되어 있고, 리턴 타입이 없다.
생성자가 성공적으로 실행되면 힙(heap) 영역에 객체가 생성되고 객체의 주소가 리턴된다.
모든 클래스는 생성자가 반드시 존재한다.
기본 생성자
중괄호 블록 내용이 비어있는 기본 생성자. 클래스 내부에 생성자 선언을 생략하면 컴파일러가 자동으로 추가한다.
클래스에 생성자를 선언하지 않아도 new 연산자 뒤에 기본 생성자를 호출해서 객체 생성이 가능하다.
[기본 생성자 자동 생성][기본 생성자로 객체 생성 가능]
메소드(Method)
객체의 동작에 해당하는 실행 블록
객체 간의 데이터 전달 수단
public class ClassName{
int fieldName; // 필드
ClassName() {...} // 생성자
void methodName() {...} // 메소드
}
외부 클래스에서 Car 필드 값 읽기와 변경
public class Car {
// 필드
String company = "현대자동차";
String model = "그랜저";
String color = "검정";
int maxSpeed = 350;
int speed;
}
public class CarExample {
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);
}
}
기본 생성자 대신 명시적 생성자 선언
명시적 생성자의 매개 변수는 new 연산자로 생성자를 호출할 때 외부의 값을 생성자 블록 내부로 전달한다.
public class Car {
// 생성자
Car(String color, int cc) {
}
}
public class CarExample {
public static void main(String[] args){
Car myCar = new Car("검정", 3000);
// Car myCar new Car();
// 생성자가 명시적으로 선언되어 있을 경우 기본 생성자 호출이 불가능
}
}
필드 초기화
클래스로부터 객체가 생성될 때, 필드는 기본 초기 값으로 자동 설정
다른 값으로 초기화하는 2가지 방법
필드를 선언할 때 초기값을 준다.
생성자에서 초기값을 준다. (객체 생성 시점에 외부에서 제공되는 다양한 값들로 초기화될 경우)
public class Korean {
// 필드
String nation = "대한민국"; // 필드에서 초기화
String name;
String ssn;
// 생성자
// 생성자에서 초기화
public Korean(String n, String s) {
name = n;
ssn = s;
}
}
생성자의 매개 변수 이름은 필드와 동일한 이름을 갖는 매개 변수를 사용한다. 그렇게 되면 필드와 매개 변수 이름이 동일하기 때문에 생성자 내부에서 해당 필드에 접근이 불가능하다. (매개 변수가 사용 우선순위가 높기 때문)
public class Car {
// 필드
String company = "현대자동차";
String model;
String color;
int maxSpeed;
// 생성자
Car(){
}
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;
}
}
메소드
메소드 : 객체의 동작에 해당하는 중괄호 블록
리턴타입, 메소드이름, 매개변수선언
시그너처(signature) : 메소드 선언부
메소드 이름은 자바 식별자 규칙에 맞게 사용한다.
소문자로 작성, 단어의 첫머리는 대문자
public class Caculator {
// 메소드
void powerOn(){
System.out.println("전원을 켭니다.");
}
int plus(int x, int y){
int result = x + y;
return result;
}
}
public class CaculratorExample {
public static void main(String[] args){
Caculator myCalc = new Caculator();
myCalc.powerOn(); // 메소드 호출
int result1 = myCalc.plus(5, 6);
System.out.println(result1);
}
}
매개 변수의 수를 모를 경우 : 매개 변수를 배열 타입으로 선언한다.
public class Computer {
// 첫번째 방법 생성자
int sum1(int[] values){
int sum = 0;
for(int i=0; i<values.length; i++){
sum += values[i];
}
return sum;
}
// 두번째 방법 생성자
int sum2(int ... values) {
int sum = 0;
for (int i = 0; i < values.length; i++) {
sum += values[i];
}
return sum;
}
}
public class ComputerExample {
public static void main(String[] args){
Computer myCom = new Computer(); // 객체 생성
int[] values1 = {1, 2, 3};
int result1 = myCom.sum1(values1);
System.out.println(result1);
int result2 = myCom.sum1(new int[] {1,2,3,4,5});
System.out.println(result2);
int result3 = myCom.sum2(1, 2, 3);
System.out.println(result3);
}
}
클래스 로더가 클래스(바이트 코드)를 로딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리된다.
클래스 이름으로 접근할 수 있다. 객체 참조 변수로도 가능하지만, 사용 시 경고 표시가 나타난다.
정적 필드는 필드 선언과 동시에 초기 값을 주는 것이 보통이지만, 계산이 필요한 경우 정적 블록을 사용한다.
public class Television {
static String company = "Samsung"; // 보통 필드에서 초기값 결정
static String model = "LCD";
static String info;
static { // 정적 블록 사용
info = company + "_" + model;
}
}
클래스 내부에서 new 연산자로 생성자 호출이 가능하기 때문에, 정적 필드도 private 접근 제한자를 붙인다.
외부에서 호출할 수 있도록, 정적 메소드인 getInstance()를 선언하고, 정적 필드에서 참조하고 있는 자신의 객체를 리턴해준다.
public class Singleton {
// private + 정적 필드
private static Singleton singleton = new Singleton();
// private + 생성자
private Singleton(){}
// private + 정적 메소드
static Singleton getInstance(){
return singleton;
}
}
// 외부에서 객체를 얻는 유일한 방법
public class SingleExample(){
public static void main(String[] args){
Singleton obj = Singleton.getInstance();
}
}
final 필드와 상수
final 필드
초기 값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중에 수정할 수 없다.
final 필드 초기값 주는 2가지 방법
필드 선언 시에 준다.
생성자에서 준다.
public class Person {
final String nation = "Korea"; // 수정 불가
final String ssn;
String name;
public Person(String ssn, String name){
this.ssn = ssn;
this.name = name;
}
}
상수(static final)
변하지 않는 값
final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 때문에 상수가 될 수 없다.
상수는 static + final
상수 이름은 모두 대문자
public 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;
}
}
패키지(Package)
클래스를 체계적으로 관리하기 위해서 사용
패키지의 물리적 형태는 파일 시스템의 폴더
클래스를 식별해준다.
상위패키지.하위패키지.클래스
package 상위패키지.하위패키지;
모두 소문자로 작성
중복되지 않도록 보통 회사의 도메인 이름으로 패키지 작성
패키지 폴더를 자동으로 생성하려면 javac 명령어 다음 -d 옵션을 추가해 패키지가 생성될 경로를 지정해줘야한다.
javac -d 경로
import문
다른 패키지에 속하는 클래스를 사용할 수 있는 방법
* 은 패키지에 속하는 모든 클래스
import 문으로 지정된 패키지의 하위 패키지는 import 대상이 아니다.
접근 제한자(Access Modifier)
객체 생성을 막기 위해 생성자를 호출하지 못하게 하거나 객체의 특정 데이터를 보호하기 위해서 해당 필드에 접근하지 못하도록 막아준다.
public, protected, default, private
클래스에 적용할 수 있는 접근 제한 : public, default
[접근 제한자 종류]
Getter와 Setter 메소드
객체 지향 프로그래밍에서는 객체의 무결성을 유지하기 위해, 외부의 직접적 접근을 막는다.
메소드를 통해 데이터를 변경하는 방법을 선호한다.
Setter : 검증을 통해 유효한 값만 데이터로 저장 가능 (ex. 음수 검증)
Getter : 객체 외부에서 객체 필드값을 사용하기 부적절한 경우, 메소드에서 필드 값 가공 후 외부로 전달
private 타입 fieldName;
// Getter
public 리턴타입 getFieldName(){
return fieldName;
}
// Setter
public void setFieldName(타입 fieldName){
this.fieldName = fieldName;
}
어노테이션(Annotation)
메타데이터(metadata)라고 볼 수 있다.
애플리케이션에서 처리해야 하는 데이터가 아니라, 컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할 것인지 알려주는 정보
어노테이션의 3가지 용도
컴파일러에게 코드 문법 에러를 체크하도록 정보 제공
대표적인 예시 : @Override (메소드가 오버라이드된 것인지 컴파일러에게 알려주고, 제대로 되지 않았으면 에러 발생)
소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보 제공
실행 시 특정 기능을 실행하도록 정보 제공
어노테이션 타입 정의 방법
public @interface AnnotationName {}
엘리먼트(element)를 멤버로 가질 수 있다.
타입 element() [default 값];
엘리먼트 타입 : int, double, String, 열거, Class 타입 등등
어노테이션 타입 적용 방법
@AnnotationName
어노테이션 적용 대상 : java.lang.annotation.ElementType 열거 상수로 정의