: 부품 객체를 먼저 만들고, 하나씩 조립해 완성된 프로그램을 만드는 기법
객체(object)
객체와 클래스
⇒ 개발자 → (설계) → 클래스 → (인스턴스화) → 인스턴스(객체)
절차지향형 vs 객체지향형
// 객체지향프로그래밍(OOP)
// 모든 사물을 객체(물건)로 추상화(모델링, 설계)하여
// 프로그래밍하는 기법
// 예) 자동차
// 속성(변수, 필드)과 행동(메서드, 함수)으로 구분한다.
// 클래스 선언부
class Car {
// 속성 = 변수(필드) = 멤버 변수
int price = 1000;
// 행동(동작) = 함수(메서드) = 멤버 함수
void run() {
System.out.println("달린다. ");
}
}
public class ex25 {
public static void main(String[] args) {
// 클래스 생성 및 호출부
// 클래스이름 객체이름 = new 클래스이름();
Car objCar = new Car();
// 객체이름 뒤에 점(.)을 찍으면 멤버변수/함수에 접근가능
System.out.println(objCar.price);
objCar.run();
// System.out.println(objCar.run()); // void 타입은 값이 아님!(오류발생)
}
}
this
자신이 속한 클래스의 객체
클래스 이름
클래스 선언과 컴파일
소스 코드 작성 → 컴파일(javac.exe) → 클래스이름.class
new 연산자
new 클래스();
A a = new A();
클래스 구성 멤버
public class ClassName{
// 필드
int fieldName;
// 생성자
ClassName() {...}
// 메소드
void methodName() {...}
}
연습문제
// 연습문제 - 클래스 설계
// 카페를 클래스로 설계해보자
// 클래스이름 : Cafe
// 속성 : coffeeCount 커피갯수는 10개로 초기화
// 행동 : sale 출력값은 "커피를 판다"
// 클래스를 설계하고, 객체를 생성해서 속성값을 출력하고,
// 행동을 실행시켜보자.
// sale함수를 호출하면 coffeeCount가 하나 준다.
// sale함수를 3번 호출후, coffeeCount를 출력하시오.
// 2.
// 당근농장을 클래스로 설계해 보자
// 클래스이름 : Farm
// 속성 : carrot 당근의 갯수 초기값은 0
// 행동 : plant() 호출시마다 당근을 하나씩 생산하고,
// 속성 carrot++를 해준다.
// 호출시 "당근을 1개 생산했습니다." 출력한다.
// 당근을 plant()함수를 이용하여 5개 생산한 후 당근 갯수를
// 출력하시오.
class Cafe {
int coffeeCount = 10; // 커피 갯수
void sale() {
System.out.println("커피를 판다.");
int coffeeCount = 5; // sale 메서드 안에서 사용 가능
// this는 자기 클래스를 의미한다.
this.coffeeCount -= 1; // 멤버변수 의미
}
}
class Farm {
int carrot = 0;
void plant() {
this.carrot += 1;
System.out.println("당근을 1개 생산했습니다.");
}
}
public class ex26 {
public static void main(String[] args) {
Cafe cafe = new Cafe();
System.out.println("처음 커피 갯수>> " + cafe.coffeeCount);
cafe.sale();
cafe.sale();
cafe.sale();
System.out.println("3번 호출 후 커피 갯수>> " + cafe.coffeeCount);
Farm farm = new Farm();
farm.plant();
farm.plant();
farm.plant();
farm.plant();
farm.plant();
System.out.println("당근 갯수>> " + farm.carrot);
}
}
객체의 동작에 해당하는 실행 블록
(접근지정자) (static) 반환타입 메서드이름 (매개변수){
메서드 내용
}
public static int sum(int a, int b){
}
class MyClass {
// 메소드 4가지 패턴
// 선언부
// 매개변수 X 반환형 X
void func1() {
System.out.println("func1");
}
// 매개변수 O 반환형 X
void func2(int x, int y) {
System.out.println("func2");
System.out.println(x + " " + y);
}
// 반환형이 있는 경우, 리턴 값과 타입이 일치해야함
// 매개변수 X 반환형 O
int func3() {
System.out.println("func3");
return 10;
}
// 매개변수 O 반환형 O
int func4(int x, int y) {
System.out.println("func4");
return x + y;
}
}
public class ex28 {
public static void main(String[] args) {
// 호출부
MyClass myClass = new MyClass();
myClass.func1();
myClass.func2(10, 20);
int result = myClass.func3();
System.out.println(result);
result = myClass.func4(10, 20);
System.out.println(result);
}
}
static 키워드: 객체 생성 없이 바로 사용 가능
class A{
int m = 3; // 인스턴스 필드는 객체 생성 후 사용 가능
static int n = 5; // static 필드는 객체 생성없이 사용 가능
}
A a = new A();
System.out.println(a.m); // 3
System.out.println(A.n); // 5
class(code) 영역, static 영역, final 영역, 메서드 영역 | Stack 영역 | Heap 영역 |
---|
// static 변수/함수에 대하여
// static 예약어: 정적 변수(객체)/함수를 지정할 때 사용
// 의미: 프로그램 구동시에 고정된 메모리 번지에 들어감. (자동 new)
// 프로그램 종료시까지 변경되지 않음
// 사용이유: 1. 시작점(Entry Point)를 지정할 때 사용
// 2. 중요한 데이터를 안정적으로 저장할 때 주로 사용
// 3. 자주 사용하는 유틸성 클래스에 지정한다.
// 4. new를 안해도 클래스 함수 사용 가능
class BallFactory {
int ballCount = 100;
void make() {
this.ballCount += 1;
System.out.println("축구공 생산!");
}
}
class FoodFactory {
static int foodCount = 200;
static void make() {
foodCount += 1;
System.out.println("도시락 생산!");
}
}
public class ex27 {
// 전역 변수 또는 중요한 데이터 저장용
public static BallFactory myBallFactory;
public static void main(String[] args) {
BallFactory ballFactory = new BallFactory();
System.out.println(ballFactory.ballCount);
// static 변수/함수는 클래스이름 뒤에 점을 찍어서 접근
System.out.println(FoodFactory.foodCount);
FoodFactory.make();
// 예)
System.out.println(Math.random());
}
}
클래스 및 클래스의 구성 멤버에 대한 접근을 제한하는 역할
접근범위 넓음 | public | 동일 패키지의 모든 클래스 + 다른 패키지의 모든 클래스에서 사용 가능 |
---|---|---|
protected | 동일 패키지의 모든 클래스 + 다른 패키지의 자식 클래스에서 사용 가능 | |
default | 동일 패키지의 모든 클래스에서 사용 가능 | |
접근범위 낮음 | private | 동일 클래스에서 사용 가능 |
// 접근제한자
// : 클래스, 함수, 변수 앞에 위치하여 접근을 제한할 때 사용
// * C언어: 접근제한자가 없어서, 모든 곳에서 접근가능.
// : 변수의 변경을 감지하기 어렵다. 유지보수가 힘들다.
// public: 같은 폴더(패키지)에서, 다른 폴더의 클래스에서 접근 가능
// protected: 같은 폴더 + 상속관계 클래스에서 접근 가능
// default: 같은 폴더
// private: 같은 클래스안에서 접근 가능(캡슐화, 은닉)
// : Getter/Setter 함수를 통해서 접근 가능하도록 허용
class Hong {
String name = "홍길동"; // default: 같은 폴더 + 자기 클래스
public int age = 30; // public: 모든 폴더+상속관계+같은 폴더+자기 클래스
protected int korScore = 80; // protected: 같은 폴더+상속관계 클래스
private int engScore = 90; // private: 자기 클래스에서만 접근 가능
// Getter/Setter 함수를 통해서 접근 가능하도록 한다.
public int getEngScore() {
return engScore;
}
public void setEngScore(int engScore) {
this.engScore = engScore;
}
void printEngScore() {
System.out.println(this.engScore);
}
}
public class ex29 {
public static void main(String[] args) {
Hong hong = new Hong();
// hong.engScore; // 다른 클래스에서 접근 불가 - error 발생
System.out.println(hong.getEngScore());
hong.setEngScore(70);
}
}
동일한 메서드 명의 매개변수 타입과 개수를 다르게 정의하여 함수의 기능을 확장한 것
public class ex31 {
// main 함수가 static이므로, 함수도 static이어야 함.
// static은 미리 공간을 확보하기 때문에, main 함수에서 사용하는 함수 역시 공간을 미리 확보해주어야 함.
static void echo() {
System.out.println("echo");
}
static void echo(int x) {
System.out.println("echo: " + x);
}
static void echo(String msg) {
System.out.println("echo: " + msg);
}
static void calc(int num) {
System.out.println(num);
}
static void calc(int num1, int num2) {
System.out.println(num1 + num2);
}
static void calc(int num1, int num2, int num3) {
int max = Math.max(num1, num2);
max = Math.max(max, num3);
System.out.println(max);
}
public static void main(String[] args) {
// 메소드 오버로딩(Overloading, 과적)
// : 매개변수의 타입과 갯수를 다르게 함으로
// 함수의 기능을 확장하는 것.
// : 같은 이름의 함수를 사용할 수 있다.
// echo();
// echo(10);
// // ex) println
// System.out.println();
// System.out.println(10);
// System.out.println("야호");
// 연습문제 - 오버로딩 연습
// 메소드(함수) 이름 : calc
// 반환형은 없음.
// 1. 매개변수 정수형 1개일때는 그냥 변수값만 출력
// 2. 매개변수 정수형 2개일때는 두 변수의 합계를 출력
// 3. 매개변수 정수형 3개일때는 세 변수중에서
// 최대값을 출력하시오.
calc(1);
calc(1, 2);
calc(1, 2, 3);
}
}
프로그램안에서의 유일한 클래스 객체
// 방법1
class ClassName {
private static ClassName singleton =
new ClassName();
static ClassName getInstance(){
return singleton;
}
}
// 방법2
class ClassName {
private static ClassName singleton;
static ClassName getInstance(){
if(singleton == null) {
singleton = new ClassName();
}
return singleton;
}
}
// 싱글톤(Singleton)
// : 프로그램안에서의 유일한 클래스 객체
// : new 를 통해서 여러개의 객체를 반복적으로 찍어낼 수 있다.
// : 예) 붕어빵1, 붕어빵2, ...
// 절대 붕어빵(유일한 붕어빵)
// 유일한 객체가 필요한 이유
// : 유일한 정보를 저장하기 위해서
// : 데이터를 안정적으로 가지고 있을 수 있다.
class FishBun { // 일반 붕어빵: 일반 객체
int bunNo = 10;
}
class UniqueFishBun { // 절대 붕어빵: 싱글톤
int bunNo = 30;
// 내부적으로 가지고 있음
private static UniqueFishBun singleton =
new UniqueFishBun();
static UniqueFishBun getInstance() {
return singleton;
}
}
class UniqueFishBun2 { // 절대 붕어빵: 싱글톤
int bunNo = 30;
// 내부적으로 가지고 있음
private static UniqueFishBun2 singleton;
static UniqueFishBun2 getInstance() {
// null 일 때만 싱글톤 호출하고, 그 이후로는 호출 X
if (singleton == null) {
singleton = new UniqueFishBun2();
}
return singleton;
}
}
public class ex32 {
public static void main(String[] args) {
// uBun1과 uBun2는 메모리 주소가 같음 -> 번지수가 같음
UniqueFishBun uBun1 = UniqueFishBun.getInstance();
UniqueFishBun uBun2 = UniqueFishBun.getInstance();
System.out.println(uBun1); // 7a81197d
System.out.println(uBun2); // 7a81197d
// bun1, bun2, bun3는 각각 생성되어 메모리 주소가 다름
FishBun bun1 = new FishBun();
FishBun bun2 = new FishBun();
FishBun bun3 = new FishBun();
System.out.println(bun1); // 메모리 주소: 7a81197d
bun1.bunNo = 20;
System.out.println(bun2); // 메모리 주소: 5ca881b5
System.out.println(bun1.bunNo);
System.out.println(bun2.bunNo);
// 일반 객체는 일관된 데이터를 저장할 수 없다.
// dynamic 하다. 객체의 생성과 소멸이 자유롭게 이루어짐.
// GC(Garbage Collector)가 자동으로 메모리를 회수함.
}
}
클래스 객체 생성될 때(new
) 자동으로 호출되는 함수(메소드)
(public) 반환타입생략 클래스이름() { }
// 생성자 함수 - Constructor
// : 클래스 객체가 생성될 때(new) 자동으로 호출되는 함수(메소드)
class Book {
// 속성
int price = 1000;
// 행동
void read() {
System.out.println("책을 읽는다.");
}
// 생성자 함수
// 패턴: public 반환타입생략 클래스이름(){ }
public Book() {
this.price = 2000;
System.out.println("생성자 함수 호출됨.");
}
}
public class ex34 {
public static void main(String[] args) {
Book book = new Book();
System.out.println(book.price); // 2000
}
}
// 생성자 함수 - 메소드 오버로딩 가능
// : 매개변수의 타입과 개수를 다르게함으로 함수를 확장하는 것.
class Robot {
String color = "빨강";
int price = 1000;
// 기본(매개변수가 없는) 생성자함수
public Robot() {
System.out.println("기본 생성자함수");
}
// 매개변수가 있는 생성자함수
public Robot(String color) {
this.color = color;
System.out.println("매개변수가 있는 생성자함수");
}
public Robot(String color, int price) {
this.color = color;
this.price = price;
}
}
public class ex38 {
public static void main(String[] args) {
Robot r1 = new Robot(); // 기본생성자 호출
Robot r2 = new Robot("파랑"); // 매개변수가 color인 생성자 호출
Robot r3 = new Robot("보라", 2000); // 매개변수가 color, price인 생성자 호출
}
}
자기 클래스 내부의 다른 생성자를 호출
반드시 중괄호 이후 첫 줄에 위치해야함
class A {
A() {
System.out.println("첫번째 생성자");
}
A(int a){
this(); // 첫줄에 위치
System.out.println("두번째 생성자");
}
}
public static void main(String[] args) {
A a = new A(3);
// 첫번째 생성자
// 두번째 생성자
}
static 키워드: 객체 생성 없이 사용 가능
class, static, final, 함수 | Stack | Heap |
---|
// static 초기화 블럭
class StaticClass {
int a;
// 프로그램 시작시 초기화됨
static int b; // static int b = 0; // 암묵적으로 0으로 초기화됨
static { // => 클래스 호출 시 처음 한번만 호출됨
b = 5;
System.out.println("static block!");
}
// 생성자 함수 - new를 통해 객체가 생성될 때 호출된다!
StaticClass() {
this.a = 3;
b = 10;
System.out.println("constructor block");
}
}
public class ex35 {
public static void main(String[] args) {
System.out.println(StaticClass.b); // 5
StaticClass s = new StaticClass();
System.out.println(StaticClass.b); // 10
}
}
// 클래스 객체 배열 사용하기
class Snack {
String name = "새우깡";
int price = 1000;
// 필드가 있는 생성자
public Snack(String name, int price) {
this.name = name;
this.price = price;
}
}
public class ex42 {
public static void main(String[] args) {
// 정수형 1차 배열
int[] nums = new int[5];
// 클래스 객체 1차 배열
Snack[] snacks = new Snack[5];
// 객체니까 new 로 객체 생성해서 배열에 할당
snacks[0] = new Snack("짱구", 2000);
snacks[1] = new Snack("새우깡", 3000);
snacks[2] = new Snack("포카칩", 4000);
snacks[3] = new Snack("홈런볼", 3500);
snacks[4] = new Snack("프링글스", 5000);
for (Snack snack : snacks) {
System.out.println(snack.name);
System.out.println(snack.price);
}
}
}