1. 객체 지향 언어의 특성
객체 지향(Object-Oriented)이란, 실세계 개체를 속성과 메서드를 통한 객체로 모형화하고, 객체를 조립하는 기법이다.
- 캡슐화
캡슐화(Encapsulation)란 객체를 캡슐로 감싸서 내부를 보호하고 볼 수 없도록 하는 것이다.
객체의 세부 내용을 보호하여, 정보 은닉을 통해 외부에서의 변경에 대한 영향을 적게 받도록 한다.
인터페이스가 단순해지며, 결합도를 낮추어 종속성을 줄일 수 있고, 재사용성을 높인다.
- 상속
상속(Inheritance)이란, 상위 개체의 속성을 하위 개체가 그대로 물려받는 것이다.
자식 클래스(sub class)가 부모 클래스(super class)의 속성을 물려받고,
기능을 추가하며 확장(extends)하는 개념이다.
- 다형성
다형성(Polymorphism)이란, 동일한 이름의 메서드가 클래스 혹은 객체에 따라 다르게 동작하도록 구현하는 것이다.
메서드 오버라이딩(overriding)과 메서드 오버로딩(overloading)이 있다.
- 메서드 오버라이딩 : 슈퍼 클래스에서의 메서드를 자식 클래스에서 재구현
- 메서드 오버로딩 : 클래스 내에서 동일한 이름이지만, 다르게 동작하는 메서드 구현
2. 클래스와 객체
클래스(Class)란, 공통의 속성과 메서드를 갖는 객체의 집합이다.
붕어빵 틀처럼, 클래스를 통해 모두 동일한 객체를 생성한다.
객체(Object)란, 데이터와 데이터를 처리하는 함수를 묶어두고 캡슐화한 모듈이다.
클래스의 인스턴스(instance)라고도 불리며, 클래스에 따라 동일한 속성을 가지지만, 고유한 값을 가짐으로써 구분된다.
- 클래스의 구성
- class 키워드를 통해 클래스를 선언한다.
- n과 m의 두 필드가 있으며, add라는 메서드는 함수이자 객체의 행동을 구현한다.
- 접근 지정자(access specifier)인 public을 통해 다른 클래스에서 접근할 수 있음을 선언한다.
- 생성자(consturctor) : 클래스의 이름과 동일한 특수 메서드이다. 객체가 생성될 때 호출된다.
- 레퍼런스 변수와 객체 생성
Sample sample;
객체를 생성할 때, 객체를 가리킬 레퍼런스 변수는 위와 같이 선언한다.
아직 객체가 생성된 것이 아니며, 레퍼런스를 가지는 변수일 뿐이자, 객체 자체가 아니다.
sample = new Sample();
new 연산자를 사용하여 객체가 생성되며, 이 객체의 주소를 sample에 대입한다.
보통 레퍼런스 변수 선언과 동시에 객체를 생성하여 주소를 대입한다.
3. 생성자
생성자(constructor)는 객체가 생성될 때, 실행되는 특수한 메서드이다.
생성자의 이름은 클래스 이름과 반드시 동일하게 작성해야 한다.
생성자는 여러 개 작성하여 오버로딩할 수 있다.
생성자는 객체를 생성할 때 한 번만 호출된다.
생성자에 리턴 타입을 지정할 수 없다.
기본 생성자(default constructor)란, 매개변수가 없고, 실행 코드도 없이 아무 일도 하지 않는 생성자이다.
개발자가 생성자를 만들어두지 않은 채 객체를 생성하게 된다면, 자바 컴파일러가 기본 생성자를 자동으로 생성하여 컴파일 오류가 발생하지 않는다.
단, 개발자가 생성자를 하나라도 만들어준 클래스에서는 컴파일러가 기본 생성자를 자동으로 생성해주지 않는다. 개발자의 의도대로 객체를 초기화해줄 수 있도록 하기 위함이다.
4. this vs. this( )
this는 현재 객체의 레퍼런스이다.
public Sample(int n) {
n = n;
}
매개변수의 이름을 멤버 변수와 동일하게 붙이고자 하는 경우에, 둘 다 매개변수로 접근하기 때문에, 멤버 변수를 변경시키지 못한다. 이럴 경우에 this를 사용해주도록 한다.
public Sample(int n) {
this.n = n;
}
this( )는 클래스 내에서 다른 생성자를 호출할 때 사용한다.
this( )는 반드시 같은 클래스 내 다른 생성자를 호출할 때 사용된다.
this( )는 생성자 코드에서만 호출할 수 있다.
this( )는 생성자의 첫 번째 문장이 되어야 한다.
5. 메서드 활용
- 객체 & 배열 인자 전달
static void ex1(Sample sample) {
sample.n = 10;
}
static void ex2(int[] array) {
array[0] = 10;
}
객체와 배열이 메서드에 전달되는 경우도 객체가 전달되는 것이 아닌, 레퍼런스 값이 전달된다.
아무리 큰 객체나 배열도, 하나의 정수 크기인 레퍼런스 값만 전달되므로, 전달에 걸리는 시간과 메모리 오버헤드가 없다는 장점이 있다. 다만, 이러한 장점은 필드나 배열의 값을 변경시키는 단점이 될 수도 있으니 주의해야 한다.
- 오버로딩
메서드 오버로딩이란, 한 클래스 내에 이름이 동일하고, 매개변수의 타입이나 개수가 다른 메서드를 여러 개 작성하는 것이다.
메서드의 이름이 동일해야 한다.
매개변수의 개수나 타입이 달라야 한다.
메서드의 리턴 타입이나 접근 지정자는 관계없다.
class OverloadingExample {
public int getSum(int x, int y) {
return x + y;
}
public int getSum(int x, int y, int z) {
return x + y + z;
}
}
동일한 이름의 getSum 매소드 두 개는 서로의 매개변수 개수가 다르기 때문에, 오버로딩이 정상적으로 이루어진다.
class OverloadingExample {
public int getSum(int x, int y) {
return x + y;
}
public double getSum(int x, int y) {
return x + y;
}
}
매개변수의 개수와 타입이 모두 같으므로, 컴파일 오류가 발생한다.
- 오버라이딩
6. 접근 지정자
패키지(package)란, 관련 있는 클래스를 분산 관리하기 위한 것으로 디렉터리 혹은 폴더와 같은 개념이다.
- 클래스 접근 지정
public 클래스
패키지에 상관없이 모든 클래스에서 접근이 허용된다.
디폴트 클래스
접근 지정자를 생략한 클래스인 경우, 같은 패키지 내의 클래스에서만 접근이 허용된다.
- public 클래스인 B는 패키지에 상관없이 모두 접근 가능하다.
- 접근 지정자가 없는 디폴트 클래스인 C는 패키지가 다른 A 클래스가 접근할 수 없다.
- 그러나, 같은 패키지 내에 있는 D 클래스는 C 클래스에 접근할 수 있다.
- 멤버 접근 지정
private → 디폴트 → protected → public 순으로 접근 허용 범위가 넓어진다.
멤버에 접근하는 클래스 | public | protected | default | private |
같은 패키지의 클래스 | O | O | O | X |
다른 패키지의 클래스 | O | X | X | X |
접근 가능 영역 | 모든 클래스 | 동일 패키지와 자식 클래스 | 동일 패키지 내 | 클래스 내 |
• public 멤버
public 멤버는 패키지에 상관없이 모든 클래스에서 접근이 가능하다.
• protected 멤버
protected 멤버는 같은 패키지 내의 모든 클래스에 접근이 허용된다.
단, 다른 패키지에 있더라도 자식 클래스의 경우 접근이 허용된다.
• default 멤버
접근 지정자가 생략된 디폴트 멤버는 동일한 패키지 내에 있는 클래스에만 접근을 허용한다.
• private멤버
private 접근 지정자는 클래스 내의 멤버에게만 접근을 허용한다.
캡슐화의 원칙을 지켜지도록 하기 위해 가능한 접근 범위를 작게 하여 접근 지정자를 선정하는 것이 좋다.
특히 필드에 대해서는 특별한 이유가 없는 한 public보다 private로 선언한다.
그 후 public 메소드를 통해 private 필드를 조작한다.
7. static
static 멤버는 객체를 생성하지 않아도 사용할 수 있는 멤버이다.
main( ) 메서드가 실행되기 전에 이미 생성되기 때문이다.
(객체 내부가 아닌 별도의 공간(클래스 코드가 적재되는 메모리에 생성된다.)
클래스당 하나만 생성되며, 동일한 클래스의 모든 객체들이 공유한다.
non - static 멤버는 객체가 생성될 때마다 생기며, 다른 객체들과 공유하지 않는다.
static 멤버는 클래스 멤버라고도 부르며, non-static 멤버는 인스턴스 멤버라고 부른다.
non-static 멤버 | static 멤버 |
객체마다 생성되어 인스턴스 멤버 | 클래스당 하나 생성되어 클레스 멤버 |
객체 생성 시 생성됨 - 객체 생성 후 사용 가능 - 객체 소멸 후 같이 소멸됨 | 클래스 로딩 시에 생성됨 - 객체 생성 전 생성되어, 사용 가능 - 객체 소멸 후에도 사용 가능 - 프로그램이 종료될 때 소멸됨 |
객체 내에 각각 유지되어 공유되지 않음 | 동일한 클래스 내의 모든 객체들의 의해 공유됨 |
- static 활용
전역 변수(global variable)와 전역 함수(global function)에 활용할 수 있다.
대표적인 클래스로 java.lang.Math를 예로 들 수 있다.
public class Math {
public static int abs(int a);
public static double cos(double a);
public static int max(int a, int b);
public static double random();
...
}
static 멤버는 클래스당 하나만 있기 때문에, 클래스 이름으로 바로 접근이 가능하다.
int n = Math.abs(-10);
- static 메서드
static 메서드는 static 멤버만 접근할 수 있다.
static 메서드는 this를 사용할 수 없다.
class StaticMethod {
int n;
void f1(int x) { n = x; }
void f2(int x) { m = x; }
static int m;
static void s1(int x) { n = x;} // 오류
static void s2(int x) { f1(3);} // 오류
static void s3(int x) { m = x; }
static void s4(int x) { s3(3); }
static void s5(int x) { this.n = x; } // 오류
static void s6(int x) { this.m = x; } // 오류
}
- static 메서드는 non-static 필드를 사용할 수 없다.
- static 메서드는 non-static 메서드를 사용할 수 없다.
- static 메서드는 this를 사용할 수 없다.
8. final
- final 클래스
final을 클래스 앞에 사용하면, 상속받을 수 없음을 지정한다.
- final 메서드
final로 메서드를 선언하면, 오버라이딩할 수 없음을 지정한다.
- final 필드
final로 필드를 선언하면, 한 번 초기화되면 값을 변경할 수 없는 상수가 된다.
실습문제
package Practice;
import java.util.Dictionary;
import java.util.Scanner;
class Ex01 {
String manufacturer;
int year;
int size;
public Ex01(String manufacturer, int year, int size) {
this.manufacturer = manufacturer;
this.year = year;
this.size = size;
}
void show() {
System.out.println(manufacturer + "에서 만든 " + year + "년형 " + size + "인치 TV");
}
}
class Ex02 {
int math;
int science;
int english;
public Ex02(int math, int science, int english) {
this.math = math;
this.science = science;
this.english = english;
}
int average() {
return (math + science + english) / 3;
}
}
class Ex03 {
String title;
String artist;
int year;
String country;
public Ex03() {
}
public Ex03(String title, String artist, int year, String country) {
this.title = title;
this.artist = artist;
this.year = year;
this.country = country;
}
void show() {
System.out.println(year + "년 " + country + "국적의 " + artist + "가 부른 " + title);
}
}
class Ex04 {
int x, y, width, height;
public Ex04(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void show() {
System.out.println("(" + x + ", " + y + ")에서 크기가 " + width + "X" + height + "인 사각형");
}
public int squre() {
return width * height;
}
public boolean contains(Ex04 ex04) {
if (x < ex04.x && y < ex04.y &&
(width + x) > (ex04.width + ex04.x) && (height + y) > (ex04.height + ex04.y)) {
return true;
} else {
return false;
}
}
}
class Ex05 {
private double x, y;
private int radius;
public Ex05(double x, double y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
public void show() {
System.out.println("(" + x + "," + y + ")" + radius);
}
public double getRadius() {
return radius;
}
}
class Ex07_1 {
private String work;
public void set(String work) {
this.work = work;
}
public String get() {
return work;
}
public void show() {
if (work == null) System.out.println("없습니다.");
else System.out.println(work + "입니다.");
}
}
class Ex07_2 {
Scanner scanner = new Scanner(System.in);
Ex07_1[] ex07_1s;
public Ex07_2(int num) {
ex07_1s = new Ex07_1[num];
for (int i = 0; i < num; i++) {
ex07_1s[i] = new Ex07_1();
}
}
public void run() {
System.out.println("이번달 스케쥴 관리 프로그램");
while (true) {
System.out.print("할일( 입력 : 1, 보기 : 2, 끝내기 3 ) >> ");
int cmd = scanner.nextInt();
if (cmd == 1) {
System.out.print("날짜(1~30)? ");
int day = scanner.nextInt();
System.out.print("할일(빈칸없이입력)? ");
String work = scanner.next();
ex07_1s[day - 1].set(work);
} else if (cmd == 2) {
System.out.print("날짜(1~30)? ");
int day = scanner.nextInt();
System.out.print(day + "일의 할 일은 ");
ex07_1s[day - 1].show();
} else if (cmd == 3) {
System.out.println("프로그램을 종료합니다.");
break;
}
}
}
}
class Ex08 {
String name;
String tel;
public Ex08(String name, String tel) {
this.name = name;
this.tel = tel;
}
}
class Ex09 {
public static int[] concat(int[] a, int[] b) {
int[] array = new int[a.length + b.length];
for (int i = 0; i < a.length; i++) {
array[i] = a[i];
}
for (int i = a.length; i < (a.length + b.length); i++) {
array[i] = b[i - a.length];
}
return array;
}
public static void print(int[] a) {
System.out.print("[ ");
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.print("]");
}
}
class Ex10 {
private static String[] kor = {"사랑", "아기", "돈", "미래", "희망"};
private static String[] eng = {"love", "baby", "money", "future", "hope"};
public static String kor2Eng(String word) {
for (int i = 0; i < kor.length; i++) {
if (kor[i].equals(word)) {
return eng[i];
}
}
return "";
}
}
class Ex11_1 {
private int a;
private int b;
public void setValue(int a, int b) {
this.a = a;
this.b = b;
}
public int calculate() {
return a + b;
}
}
class Ex11_2 {
private int a;
private int b;
public void setValue(int a, int b) {
this.a = a;
this.b = b;
}
public int calculate() {
return a - b;
}
}
class Ex11_3 {
private int a;
private int b;
public void setValue(int a, int b) {
this.a = a;
this.b = b;
}
public int calculate() {
return a * b;
}
}
class Ex11_4 {
private int a;
private int b;
public void setValue(int a, int b) {
this.a = a;
this.b = b;
}
public int calculate() {
return a / b;
}
}
class Ex12 {
private String[][] seats;
Ex12() {
seats = new String[3][10];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 10; j++) {
seats[i][j] = "---";
}
}
}
public void booking(int n) {
Scanner scanner = new Scanner(System.in);
print_seat(n);
System.out.print("이름 >> ");
String name = scanner.next();
System.out.print("번호 >> ");
int num = scanner.nextInt();
if (n > 0 && n < 4) {
seats[n - 1][num - 1] = name;
}else {
System.out.print("1부터 3까지 입력해야 합니다.");
}
}
private void print_seat(int n) {
if (n == 1) {
System.out.print("S>> ");
} else if (n == 2) {
System.out.print("A>> ");
} else if (n == 3) {
System.out.print("b>> ");
} else {
System.out.println("1부터 3까지 입력해야 합니다.");
return;
}
for (int j = 0; j < 10; j++) {
System.out.print(seats[n - 1][j] + " ");
}
System.out.println();
}
public void inquiry() {
for (int i = 0; i < 3; i++) {
if (i == 0) {
System.out.print("S>> ");
} else if (i == 1) {
System.out.print("A>> ");
} else if (i == 2) {
System.out.print("b>> ");
} else {
System.out.println("1부터 3까지 입력해야 합니다.");
}
for (int j = 0; j < 10; j++) {
System.out.print(seats[i][j] + " ");
}
System.out.println();
}
System.out.println("<<<조회를 완료하였습니다>>>");
}
public void cancel(int n) {
Scanner scanner = new Scanner(System.in);
print_seat(n);
System.out.print("이름 >> ");
String name = scanner.next();
for (int i = 0; i < 10; i++) {
if (seats[n-1][i].equals(name)) {
seats[n-1][i] = "---";
break;
}
}
}
}
public class Class_Object_Examples {
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("---------[Ex01]---------");
Ex01 ex01 = new Ex01("LG", 2017, 32);
ex01.show();
System.out.println("---------[Ex02]---------");
System.out.print("수학, 과학, 영어 순으로 3개의 점수 입력 >>");
Ex02 ex02 = new Ex02(scanner.nextInt(), scanner.nextInt(), scanner.nextInt());
System.out.println("평균은 " + ex02.average());
System.out.println("---------[Ex03]---------");
Ex03 ex03 = new Ex03("Dancing Queen", "ABBA", 1978, "스웨덴");
ex03.show();
System.out.println("---------[Ex04]---------");
Ex04 ex04_1 = new Ex04(2, 2, 8, 7);
Ex04 ex04_2 = new Ex04(5, 5, 6, 6);
Ex04 ex04_3 = new Ex04(1, 1, 10, 10);
ex04_1.show();
System.out.println("s의 면적은 " + ex04_2.squre());
if (ex04_3.contains(ex04_1)) System.out.println("ex04_3은 ex04_1을 포함합니다");
if (ex04_3.contains(ex04_2)) System.out.println("ex04_3은 ex04_2을 포함합니다");
System.out.println("---------[Ex05]---------");
Ex05[] ex05s = new Ex05[3];
for (int i = 0; i < ex05s.length; i++) {
System.out.print("x, y, radius >>");
double x = scanner.nextDouble();
double y = scanner.nextDouble();
int radius = scanner.nextInt();
ex05s[i] = new Ex05(x, y, radius);
}
for (int i = 0; i < ex05s.length; i++) {
ex05s[i].show();
}
System.out.println("---------[Ex06]---------");
double max = 0;
int index = 0;
for (int i = 0; i < 3; i++) {
if (ex05s[i].getRadius() > max) {
max = ex05s[i].getRadius();
index = i;
}
}
System.out.print("가장 면적이 큰 원은 ");
ex05s[index].show();
System.out.println("---------[Ex07]---------");
Ex07_2 ex07_2 = new Ex07_2(30);
ex07_2.run();
System.out.println("---------[Ex08]---------");
System.out.print("인원수>> ");
int num = scanner.nextInt();
Ex08[] ex08s = new Ex08[num];
for (int i = 0; i < num; i++) {
System.out.print("이름과 전화번호(이름과 번호는 빈 칸없이 입력) >> ");
String name = scanner.next();
String tel = scanner.next();
ex08s[i] = new Ex08(name, tel);
}
System.out.println("저장되었습니다...");
while(true){
System.out.print("검색할 이름 >> ");
String name = scanner.next();
int check = 0;
if (name.equals("그만")) {
break;
}
for(Ex08 ex08 : ex08s){
if (ex08.name.equals(name)) {
System.out.println(name + "의 번호는 " + ex08.tel + "입니다.");
check += 1;
break;
}
}
if (check == 0) {
System.out.println(name + "이/가 없습니다.");
}
}
System.out.println("---------[Ex09]---------");
int array1[] = {1, 5, 7, 9};
int array2[] = {3, 6, -1, 100, 77};
int array3[] = Ex09.concat(array1, array2);
Ex09.print(array3);
System.out.println("---------[Ex10]---------");
System.out.println("한영 단어 검색 프로그램입니다.");
while (true) {
System.out.print("한글 단어? ");
String kor = scanner.next();
if (kor.equals("그만")) {
break;
}
String s = Ex10.kor2Eng(kor);
if (s.equals("")) {
System.out.println(kor + "은/는 저의 사전에 없습니다.");
} else {
System.out.println(kor + "은/는 " + s);
}
}
System.out.println("---------[Ex11]---------");
System.out.print("두 정수와 연사자를 입력하시오 >> ");
int x = scanner.nextInt();
int y = scanner.nextInt();
String p = scanner.next();
switch (p) {
case "+":
Ex11_1 ex11_1 = new Ex11_1();
ex11_1.setValue(x, y);
System.out.println(ex11_1.calculate());
break;
case "-":
Ex11_2 ex11_2 = new Ex11_2();
ex11_2.setValue(x, y);
System.out.println(ex11_2.calculate());
break;
case "*":
Ex11_3 ex11_3 = new Ex11_3();
ex11_3.setValue(x, y);
System.out.println(ex11_3.calculate());
break;
case "/":
Ex11_4 ex11_4 = new Ex11_4();
ex11_4.setValue(x, y);
System.out.println(ex11_4.calculate());
break;
}
System.out.println("---------[Ex12]---------");
System.out.println("명품콘서트홀 예약 시스템입니다.");
Ex12 ex12 = new Ex12();
while (true) {
System.out.print("예약 : 1, 조회 : 2, 취소 : 3, 끝내기 : 4 >> ");
int cmd = scanner.nextInt();
if (cmd == 1) {
System.out.print("좌석 구분 S(1), A(2), B(3) >> ");
ex12.booking(scanner.nextInt());
} else if (cmd == 2) {
ex12.inquiry();
} else if (cmd == 3) {
System.out.print("좌석 구분 S(1), A(2), B(3) >> ");
ex12.cancel(scanner.nextInt());
} else if (cmd == 4) {
break;
}
}
}
}
참고) 명품 자바 프로그래밍 4장 + 실습 문제