이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.
1. 클래스와 객체
1-1 자바의 객체 지향 특성
캡슐화 – 객체를 캡슐로 싸서 그 내부를 보호하고 볼 수 없게 하는 것으로 객체의 가장 본질적인 특징이다.
상속 – 자바의 상속은 자식클래스가 부모클래스의 속성을 물려받아 부모 클래스에 기능을 확장(extends)하는 개념
장점: 코드의 중복을 방지하고, 코드를 재사용함으로써 코드 작성에 드는 시간과 비용을 줄여준다.
다형성 – 같은 이름의 메소드가 클래스 혹은 객체에 따라 다르게 구현 되는 것
메소드 오버라이딩(overriding) – 서브 클래스에서 자신에 특징에 맞게 동일한 이름으로 다시 구현하는 것
메소드 오버로딩(overloading) – 클래스 내에서 같은 이름의 메소드를 여러 개 만드는 것(인자가 다르게 구성됨)
1-2 객체지향의 목적
- 소프트웨어 생산성 향상
- 실 세계에 대한 쉬운 모델링
1-3 클래스와 객체
클래스 – 클래스는 객체를 만들어 내기 위한 설계 혹은 틀
객체(instance)- 클래스에 선언된 모양 그대로 생성된 실체가 객체이고, 클래스의 인스턴스라고도 부른다.
하나의 클래스에 객체들은 수 없이 많이 생성될 수 있다.
1-4 클래스의 구성
클래스는 class 키워드로 선언 후 클래스 내에 필드와 메소드를 정의한다. 클래스 외부에는 필드나 메소드를 결코 둘 수 없다.
1-5 클래스 멤버
필드 – 객체의 상태 값을 저장할 멤버 변수
메소드 – 실행 가능한 함수이고 객체의 행위를 구현한다.
1-6 접근 지정자
클래스 선언 부 앞에 붙여진 public은 접근 지정자로서, 선언 클래스를 다른 클래스에서 이용할 수 있음을 지정한다.
1-7 객체의 생성 및 활용
public class Circle {
int radius;
String name;
public double getArea() {
return 3.14 * radius * radius;
}
public static void main(String[] args) {
Circle pizza;
pizza = new Circle();
pizza.radius = 10;
pizza.name = "자바피자";
double area = pizza.getArea();
System.out.println(pizza.name + "의 면적은 " + area);
Circle dount = new Circle();
dount.radius = 2;
dount.name = "자바도넛";
area = dount.getArea();
System.out.println(dount.name + "의 면적은 " + area);
}
}
객체의 생성은 레퍼런스 변수 선언 후 꼭 new 연산자로 객체를 생성해야 한다.
New 연산자는 Circle 타입의 크기 만한 메모리를 할당 받아 메모리에 대한 레퍼런스를 리턴 한다.
또한 객체 멤버 접근은 . (점) 연산자를 이용한다.
1-8 객체 배열
자바에서 객체를 원소로 하는 배열을 만들 수 있다.
객체 배열을 만들기 위해서는 3단계를 거치게 된다.
- 배열에 대한 레퍼런스 선언 – Circle [] c; (단, 배열의 개수를 지정하면 안 된다.)
Circle[] c;
- 레퍼런스 배열 생성 – 배열의 원소는 객체가 아니라 Circle 객체에 대한 레퍼런스이다.
c = new Circle[5];
- 객체 생성 – Circle 객체를 생성하여 배열 c[i]의 각 레퍼런스에 대입한다.
for (int i = 0; i < c.length; i++)
c[i] = new Circle(i);
1-9 배열의 원소 객체 접근
배열 c의 i번째 객체를 접근하기 위해서는 c[i] 레퍼런스를 사용한다.
1-10 메소드 형식
- 접근 지정자 – 자바에서는 public, protected, private, 디폴트 접근 지정 4 가지의 접근 지정자가 있다.
- 리턴 타입 – 메소드가 호출자에게 리턴할 값의 타입이다. 메소드가 아무 값도 리턴하지 않으면 void로 선언한다.
1-11 인자 전달
자바의 메소드 호출 시 인자 전달 방식은 '값에 의한 호출(call by value)이다. 실인자의 값이 복사 되어 메소드의 매개 변수에 전달된다.
기본 타입이 전달되는 경우 – call by value
사진 1 인자 전달: 기본 타입의 전달
객체가 인자로 전달되는 경우 – call by reference
사진 2 인자 전달: 객체 전달
배열이 인자로 전달되는 경우 – call by reference
사진 3 인자 전달: 배열 전달
1- 12 메소드 오버로딩
클래스 내에 이름이 같지만 매개 변수의 타입이나 개수가 서로 다른 여러 개의 메소드를 작성하는 것. 메소드 오버로딩은 다형성의 한 종류이다. 메소드 오버로딩의 조건은 다음과 같다.
- 메소드 이름이 동일하여야 한다.
- 메소드 매개 변수의 개수나 타입이 서로 달라야 한다.
메소드의 리턴 타입과, 접근 지정자는 오버로딩의 성공을 판단하는 것과는 무관하다.
1-13 객체 치환 시 주의할 점
객체의 치환은 객체를 복사하는 것이 아니라는 점에 주의 해야 한다. 가리키는 객체가 없을 시에는 가비지가 되어 버린다.
사진 4 객체 치환 시 주의할 점
1-14 객체의 소멸
객체를 생성하는 new 연산자만이 사용가능 한 자바에서는 객체를 소멸시키는 연산자는 없다. 개발자는 마음대로 객체를 소멸 시킬 수 없다. 하지만, 자바에서는 응용프로그램의 사용하지 않는 객체나 배열의 메모리를 JVM이 알아서 수거해 가기 때문에 사용하지 않는 메모리에 대한 생각 또한 하지 않아도 된다.
1-15 가비지
할당 받은 객체나 배열 메모리 중에서 더 이상 사용하지 않게 된 메모리를 가비지라고 부른다.
가비지의 조건은 자바 플랫폼은 참조하는 래퍼런스가 하나도 없는 객체나 배열을 가비지로 판단한다.
public class GarbageEx {
public static void main(String[] args) {
String a = new String("Good"); // 가비지 발생 객체
String b = new String("Bad");
String c = new String("Normal");
String d, e;
a = null;// 가비지 발생 원인
d = c;
c = null;
}
}
.1-16 가비지 컬렉션
가용 메모리 공간이 일정 크기 이하로 줄어들면, 자바 가상 기계는 자동으로 가비지를 회수하여 가용 메모리 공간을 늘린다. 이것을 가비지 컬렉션이라고 부른다.
가비지 컬렉션은 JVM 내에 준비된 가비지 컬렉션 스레드에 의해 처리된다.
1-17 가비지 컬렉션 강제 수행
System 또는 Runtime 객체의 gc() 메소드를 호출하여, 자바 플랫폼에 가비지 컬렉션을 요청할 수 있다.
System.gc();
하지만 강제 호출 한다고 해서 컬렉션이 작동하는 것은 아니다. 그냥 강력한 제안에 불과하다는 것만 알아두자.
어차피 가비지 스레드의 판단 하에 적절히 가비지 컬렉션이 작동한다.
2. 생성자와 접근제어
2-1 생성자
생성자는 객체가 생성될 때 초기화를 위해 실행되는 메소드이다.
- 생성자의 이름은 클래스 이름과 동일하다.
- 생성자를 여러 개 작성할 수 있다. (매개 변수의 개수와 타입만 다르다면)
- 생성자는 객체를 생성할 때 한 번만 호출된다.
- 생성자에 리턴 타입을 지정할 수 없다.
- 생성자의 목적은 객체가 생성될 때 필요한 초기 작업을 위함이다.
2-2 기본 생성자(default constructor)
기본 생성자란 매개 변수가 없고 또한 실행 코드가 없어 아무 일도 하지 않고 단순 리턴 하는 생성자이다.
- 생성자가 자동으로 생성된 경우 – 생성자가 없는 클래스는 존재하지 않으며, 클래스에 생성자가 하나도 선언되지 않은 경우 컴파일러는 기본 생성자를 자동으로 생성한다.
- 생성자가 자동으로 생성되지 않은 경우 – 생성자가 오버로딩 되어 매개 변수를 다르게 사용자가 선언 했을 경우 컴파일러는 기본 생성자를 생성하여 주지 않는다.
2.3 this 레퍼런스
This는 객체 자신에 대한 레퍼런스로서 메소드 안에서 사용된다. This는 컴파일러에 의해 자동으로 관리된다.
2.4 this()로 다른 생성자 호출
This()는 클래스 내에서 생성자가 다른 생성자를 호출할 때 사용하는 자바 코드이다.
- This()는 생성자에서만 사용된다.
- This()는 반드시 같은 클래스의 다른 생성자를 호출할 때 사용된다.
- This()는 반드시 생성자의 첫 번째 문장으로 사용되어야 한다.
public class Book {
String title;
String author;
void Show() {
System.out.println(title + " " + author);
}
public Book() {
this("", "");
System.out.println("생성자 호출됨");
}
public Book(String title) {
this(title, "작자미상");
}
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public static void main(String[] args) {
Book javaBook = new Book("java", "황기태");
Book bible = new Book("Bible");
Book emptyBook = new Book();
bible.Show();
}
}
2-5 자바의 패키지 개념
자바에서는 상호 관련 있는 클래스 파일들을 패키지에 저장하여 관리한다.
패키지는 디렉터리 혹은 폴더와 같은 개념이다.
2-6 접근 지정자
자바에는 다음과 같이 4가지의 접근 지정자가 있다.
- Public, protected, private, 접근 지정자 생략(디폴트 접근 지정)
클래스 접근 지정
- Public 클래스 – 패키지에 상관없이 다른 어떤 클래스에게도 사용이 허용됨.
- 디폴트 클래스 – 오직 같은 패키지 내에 있는 클래스들에게만 사용이 허용됨
멤버 접근 지정
사진 5 멤버 접근 지정
2-7 Static 멤버
Static 지시어로 선언된 멤버는 클래스 당 하나만 생성되는 멤버로서, 동일한 클래스의 모든 객체들이 공유하므로 클래스 멤버라고 부른다(프로그램을 시작할 때나 클래스 로딩 시에 생성)
Non-Static 멤버는 각 객체마다 별도로 생기므로 인스턴스 멤버라고 부른다. (객체가 생성 될 때 함께 생성되고 사라질 때 같이 사라진다.)
사진 6 Static 멤버와 non-Static 멤버의 차이
2-8 Static 활용
Static 메소드는 자바 특성 상 클래스 외부에 필드나 메소드를 선언하지 못하는 이유로 다음과 같이 활용될 수 있다.
- 전역 변수와 전역 함수를 만들 때 활용
- 공유 멤버를 만들고자 할 때 사용
또한 Static 메소드의 제약 조건은 2가지 제약 사항을 가진다.
- Static 메소드는 오직 Static 멤버만 접근 할 수 있다.
- Static 메소드에서는 this를 사용할 수 없다.
2-9 final 키워드
- 클래스에 final 키워드 사용 – 클래스 상속 불가능
- 메소드에 final 키워드 사용 – 메소드 오버라이딩 불가능
- 필드에 final 키워드 사용 – 필드에 사용 시 필드는 상수가 된다. Final 키워드와 public static과 함께 선언하면, 프로그램 전체에서 공유할 수 있는 상수가 된다.
public static final PI=3.14;
3. 배열
3-1 배열 선언 및 생성
자바에서의 배열은 2단계의 과정을 거친다.
- 배열에 대한 레퍼런스 변수 선언
- 배열 생성 – 배열 공간 할당
사진 7 배열에 대한 레퍼런스 변수 선언
사진 8 배열 생성 및 공간할당
배열 선언 시 [] 안에 배열의 크기를 지정해서는 안 된다. 이는 컴파일 오류이다.
3-2 배열 초기화
배열 선언문에서 {} 사이에 원소를 나열하여 초기화된 배열을 생성할 수 있다.
public class input {
public static void main(String[] args) {
int intArray[] = { 4, 3, 2, 1, 9 };
double doubleArray[] = { 0.01, 0.21, 0.312 };
}
}
3-3 배열 인덱스와 원소 접근
배열의 인덱스는 정수만 가능하다. 인덱스는 0부터 시작하며 마지막 원소의 인덱스는 배열 크기 -1 이다.
public class input {
public static void main(String[] args) {
int intArray [] = new int[5];
intArray[0] = 5;
intArray[3] = 6;
int n = intArray[3];
System.out.print(n);
}
}
배열이 생성되어있지 않거나 음수 인덱스를 사용하여 배열을 접근하면 오류가 난다.
3-4 래퍼런스 치환과 배열 공유
public class input {
public static void main(String[] args) {
int intArray [] = new int[5];
int myArray [] = intArray;
}
}
myArray는 intArray의 레퍼런스 값을 가지게 되고, 배열을 공유한다.
3-5 배열의 크기, length 필드
import java.util.Scanner;
public class input {
public static void main(String[] args) {
Scanner = new Scanner(System.in);
System.out.println("5개의 정수를 입력하세요");
int intArray[] = new int[5];
double sum = 0.0;
for (int i = 0; i < intArray.length; i++)
intArray[i] = scanner.nextInt();
for (int i = 0; i < intArray.length; i++)
sum += intArray[i];
System.out.println("평균은 " + sum / intArray.length);
scanner.close();
}
}
3-6 배열과 For-each문
for문을 변형한 for-each문은 배열이나 배열의 크기만큼 루프를 돌면서 각 원소를 순차적으로 접근하는데 매우 유용하다.
import java.util.Scanner;
public class input {
public static void main(String[] args) {
int[] n = { 1, 2, 3, 4, 5 };
int sum = 0;
for (int k : n) {
System.out.print(k + " ");
sum += k;
}
System.out.println("합은 " + sum);
String f[] = { "사과", "배", "바나나", "체리", "딸기", "포도" };
for (String s : f)
System.out.print(s + " ");
}
}
3-7 2차원 배열
public class input {
public static void main(String[] args) {
double score[][] = { { 3.3, 3.4 }, { 3.5, 3.6 }, { 3.7, 4.0 }, { 4.1, 4.2 } };
double sum = 0;
for (int year = 0; year < score.length; year++)
for (int term = 0; term < score[year].length; term++)
sum += score[year][term];
int n = score.length;
int m = score[0].length;
System.out.println("4년 전체 평점 평균은 " + sum / (n * m));
}
}
3-8 배열 리턴
public class input {
static int[] makeArray() {
int temp[] = new int[4];
for (int i = 0; i < temp.length; i++)
temp[i] = i;
return temp;
}
public static void main(String[] args) {
int intArray[];
intArray = makeArray();
for (int i = 0; i < intArray.length; i++)
System.out.print(intArray[i] + " ");
}
}
5. 상속
5-1 상속의 필요성
- 클래스 사이의 멤버 중복 선언 불필요 – 클래스 간결화
- 클래스들의 계층적 분류로 클래스 관리 용이
- 클래스 재사용과 확장을 통한 소프트웨어의 생산성 향상
5-2 상속 선언 및 슈퍼 클래스 접근
자바에서는 extends 키워드로 상속을 선언한다. 상속하는 부모 클래스를 슈퍼 클래스라고 하며, 상속받는 자식 클래스를 서브 클래스라고 한다.
class Point {
private int x, y;
void set(int x, int y) {
this.x = x;
this.y = y;
}
void showPoint() {
System.out.println("(" + x + "," + y + ")");
}
}
class ColorPoint extends Point {
private String color;
void setColor(String color) {
this.color = color;
}
void showColorPoint() {
System.out.print(color);
showPoint();
}
}
public class ColorPointEx {
public static void main(String[] args) {
Point p = new Point();
p.set(1, 2);
p.showPoint();
ColorPoint cp = new ColorPoint();
cp.set(3, 4);
cp.setColor("red");
cp.showColorPoint();
}
}
5-3 자바 상속의 특징
- 자바에서는 클래스의 다중 상속을 지원하지 않는다. 하지만 인터페이스는 다중으로 상속받아 구현 가능하다.
- 자바의 모든 클래스는 자바에서 제공하는 Object 클래스를 자동으로 상속 받도록 컴파일 된다.
5-4 protected 접근 지정
사진 9 슈퍼 클래스에 대한 접근 지정
5-5 protected 멤버
슈퍼 클래스의 protected 멤버는 다음 두 가지 경우에 접근이 허용된다.
- 같은 패키지에 속한 모든 클래스
- 상속되는 서브 클래스(패키지 상관 X)
5-6 서브 클래스의 생성자와 슈퍼 클래스의 생성자
상속 받은 각 클래스의 생성자는 모두 호출된다. 서브 클래스의 생성자가 먼저 호출되면 결국 슈퍼 클래스의 생성자가 먼저 실행되고 서브 클래스의 생성자가 나중에 생성된다.
사진 10 생성자 실행 순서
5-7 서브 클래스에서 슈퍼 클래스 생성자 선택
- 개발자 명시가 없는 서브 클래스는 매개 변수가 없는 기본 생성자일 경우 – 기본 생성자 호출.
- 개발자 명시가 없고 서브 클래스의 매개 변수를 가진 생성자의 경우 - 기본 생성자 호출
- Super()를 이용하여 명시적으로 슈퍼 클래스의 생성자 선택(단, super()는 생성자의 첫 라인에 반드시 위치해야 함.) – 해당 선택된 생성자 호출
5-8 업 캐스팅
서브 클래스 객체가 슈퍼 클래스 타입으로 변환되는 것
Person p;
Student s = new Student();
p = s; // 업 캐스팅
슈퍼 클래스의 레퍼런스 p가 서브 클래스 객체를 가리키도록 치환되는 것이 업 캐스팅이다.
업 캐스팅한 레퍼런스로는 객체 내에 실존하는 모든 데이터를 접근할 수 없고 슈퍼 클래스의 멤버만 접근할 수 있다.
업 캐스팅 된 서브 클래스는 잠시 자신의 객체 속성이 가려져 있는 것이라고 보면 된다.
5-9 다운캐스팅
업 캐스팅 된 것을 다시 원래대로 되돌리는 것을 다운캐스팅이라고 한다.
Student s = (Student)p; //다운 캐스팅
다운캐스팅을 할 때는 반드시 명시적 타입을 지정해 주어야 한다.
5-10 instanceof 연산자
레퍼런스가 가리키는 객체가 어떤 클래스 타입인지 구분하기 위해, 자바에서는 instanceof 연산자를 두고 있다.
class Person{}
class Student extends Person {}
class Researchar extends Person {}
class Professor extends Researchar {}
public class InstanceOfEx {
static void print(Person p){
if(p instanceof Person){
System.out.print("Person ");
}
if(p instanceof Student){
System.out.print("Student ");
}
if(p instanceof Researchar){
System.out.print("Researchar ");
}
if(p instanceof Professor){
System.out.print("Professor ");
}
System.out.println();
}
public static void main(String[] args) {
System.out.print("new Student() -> "); print(new Student());
System.out.print("new Researchar() -> "); print(new Researchar());
System.out.print("new Professor() -> "); print(new Professor());
}
}
5-10 메소드 오버라이딩
메소드 오버라이딩은 서브 클래스에서 슈퍼 클래스에 선언된 메소드를 중복 작성하여 슈퍼 클래스에 작성된 메소드를 무력화시키고, 객체의 주인 노릇을 하는 것이다.
메소드 오버라이딩의 목적은 슈퍼 클래스에 선언된 메소드를, 동일한 이름으로 각 서브 클래스에서 필요한 내용으로 새로 구현하는 데 있다.
5-11 동적 바인딩
동적 바인딩은 실행할 메소드를 컴파일 시에 결정하지 않고 실행 시에 결정하는 것을 말한다. 즉, run time중에 소스 코드 내에서 호출하는 것.
사진 11 동적 바인딩 사례
5-12 오버라이딩과 super 키워드
클래스에서 super 키워드를 이용하면 정적 바인딩을 통해 슈퍼 클래스의 멤버에 접근 할 수 있다.
Super는 자바에서 자동으로 지원되는 것으로 슈퍼 클래스에 대한 레퍼런스이다.
class SuperObject {
protected String name;
public void paint() {
draw();
}
public void draw() {
System.out.println(name);
}
}
public class SubObject extends SuperObject {
protected String name;
public void draw() {
name = "Sub";
super.name = "Super";
super.draw();
System.out.println(name);
}
public static void main(String[] args) {
SuperObject b = new SubObject();
b.paint();
}
}
5-13 오버로딩과 오버라이딩의 차이점
사진 12 오버로딩과 오버라이딩의 차이점
5-14 추상 메소드
abstract 키워드와 함께 원형만 선언되고, 코드는 작성되지 않은 메소드이다.
- 추상 메소드를 최소 한 개 이상 가지고 abstract로 선언된 클래스
- 추상 메소드가 없어도 abstract로 선언한 클래스
추상 메소드를 가지고 있으면 반드시 추상 클래스로 선언되어야 한다.
응용프로그램에서는 추상 클래스의 인스턴스(객체)를 생성할 수 없다.
5-15 추상 클래스 상속과 구현
- 추상 클래스를 상속받으면 자동으로 추상 클래스가 된다.
- 추상클래스 구현이란, 서브 클래스에서 슈퍼 클래스의 모든 추상 메소드를 오버라이딩하여 실행 가능한 코드로 구현하는 것을 말한다.
5-16 추상 클래스의 목적
추상 클래스를 작성하는 목적은 객체를 생성하기 위함이 아니다. 상속을 위한 슈퍼 클래스로 활용하기 위한 것이다.
6. 인터페이스와 다형성
6-1 자바 인터페이스란?
자바의 인터페이스는 오직 추상 메소드와 상수로만 구성되며, interface 키워드를 사용하여 선언한다.
자바의 인터페이스는 상속받을 서브 클래스에게 구현할 메소드들의 원형을 모두 알려주어, 클래스가 자신의 목적에 맞게 메소드를 구현하도록 하는 것이 목적이다.
사진 13 자바 인터페이스
6-2 인터페이스의 특징
- 인터페이스는 상수 필드와 추상 메소드만으로 구성된다.
- 모든 메소드는 추상 메소드로서, abstract public 속성이며 생략 가능하다.
- 상수는 public static final 속성이며, 생략하여 선언할 수 있다.
- 인터페이스를 상속받아 새로운 인터페이스를 만들 수 있다.
- 인터페이스의 객체를 생성 할 수 없다.
6-3 인터페이스 상속
인터페이스는 인터페이스끼리 상속이 가능하다. 인터페이스 상속 시에도 extends 키워드를 사용한다.
interface MobilePhoneInterface extends PhoneInterface {
void sendSMS(); //추상 메소드
void receiveSMS(); //추상 메소드
}
인터페이스는 콤마를 연결하여 2개 이상의 인터페이스를 다중 상속 받을 수도 있다.
interface MobilePhoneInterface extends PhoneInterface, MP3Interface {
void sendSMS(); //추상 메소드
void receiveSMS(); //추상 메소드
}
6-4 인터페이스 구현
인터페이스 구현이란 인터페이스를 상속받고, 추상 메소드를 모두 구현한 클래스를 작성하는 것을 말한다.
Implement 키워드를 이용하여 구현한다.
interface PhoneInterface {
int BUTTONS = 20;
void sendCall();
void receiveCall();
}
interface MobilePhoneInterface extends PhoneInterface {
void sendSMS();
void receiveSMS();
}
interface MP3Interface {
public void play();
public void stop();
}
class PDA {
public int calculate(int x, int y) { return x + y; }
}
class SmartPhone extends PDA implements MobilePhoneInterface, MP3Interface {
public void sendCall() { System.out.println("전화 걸기"); }
public void receiveCall() { System.out.println("전화 받기");}
public void sendSMS() { System.out.println("SMS 보내기");}
public void receiveSMS() {System.out.println("SMS 받기"); }
public void play() { System.out.println("음악 재생"); }
public void stop() { System.out.println("재생 중지"); }
public void schedule() { System.out.println("일정 관리"); }
}
public class InterfaceEx {
public static void main(String[] args) {
SmartPhone p = new SmartPhone();
p.sendCall();
p.play();
System.out.println(p.calculate(3, 5));
p.schedule();
}
}