세상 모든 것이 객체
세상 모든 것이 객체
실세계 객체의 특징 - 객체마다 고유한 특성과 행동을 가짐
(여기서 고유한 특성은 멤버와 필드 변수, 행동은메소드를 뜻한다.)
필드는 다른 말로 멤버변수나 전역변수라고 불린다. 변수라는 말처럼 필드는 어떠한(객체) 데이터를 저장하기 위한 역할을 담당한다.
다른 객체들과 정보를 주고 받는 등, 상호작용하면서 존재
컴퓨터 프로그램에서 객체 사례
- 테트리스 게임의 각 블록들
- 한글 프로그램의 메뉴나 버튼들
객체 지향 특성 : 캡슐화
캡슐화
외부 인터페이스는 공개되어 있고 알고리즘은 숨어져 있다.
자바의 캡슐화
클래스 : 객체를 선언한 틀(설계도)
메소드(멤버 함수)와 필드(멤버 변수)는 모두 클래스 내에 구현
객체(object) :
class Animal{
String name;//필드
int age;//필드
void eat(){...}//메소드
void speak(){...}//메소드
void love(){...}//메소드
}
위와 같이 클래스를 선언할 수 있다.
위와 같은 클래스를 선언한 뒤
name이 lion이고 age가 4인 Animal 클래스에 속하는 객체
name이 bear이고 age가 8인 ANimal 클래스에 속하는 객체 등을 선언할 수 있다.
객체 지향의 특성 : 상속
자바의 상속
상속(inheritance)
자바는 클래스 다중 상속 없음.
인터페이스 다중 상속 허용. (인터페이스는 나중에 배움)
서브 클래스 객체는 슈퍼 클래스의 멤버와 서브 클래스의 멤버를 모두 가짐.
class Animal {
String name;
int age;
void eat {...}
void sleep {...}
void love {...}
}
//extends 가 상속한다는 뜻
class Human extends Animal {
String hobby;
String job;
void work() {...}
void cry() {...}
void laugh() {...}
}
이렇게 상속하면 Human객체는
hobby, job 멤버 변수 뿐만 아니라, name, age 멤버 변수도 가지고
work, cry, laugh 멤버 메소드 뿐만 아니라, eat, sleep, love 멤버 메소드도 가지게 된다.
객체 지향의 특성 : 다형성
ex. Animal 클래스의 speak() 메소드는 자식 클래스 dog, cat, chicken 에서 다르게 구현될 수 있다. -> 메소드 오버라이딩
(ex. 각각 멍멍, 야옹야옹, 꼬꼬댁 을 출력하도록 구현)
객체 지향 언어의 목적
절차 지향 프로그래밍과 객체 지향 프로그래밍
클래스와 객체
객체들은 클래스에 선언된 동일한 속성을 가지지만, 객체마다 서로 다른 고유한 값으로 구분됨.
클래스 구성
public class Circle{
public int radius;//원의 반지름 필드
public String name;//원의 이름 필드
public Circle() { //원의 생성자 메소드
}
public double getArea() {//원의 면적 계산 메소드
return 3.14 * radius * radius;
}
}
여기서
public 은 접근 권한
class 는 클래스 선언
Circle 은 클래스 이름 을 의미한다.
클래스 구성 설명
객체 생성 및 접근
인스턴스(instance) : 메모리상에 생성된 클래스 객체
레퍼런스 변수 : 메모리 상에 생성된 인스턴스를 가리키는데 사용되는 변수
Circle Pizza; //레퍼런스 변수 선언
pizza = new Circle(); //new 연산자를 이용한 객체 생성
pizza.radius = 30;
pizza.name = "자바피자";//.을 이용한 객체 멤버 변수 접근
double area = pizza.getArea();//.을 이용한 객체 멤버 메소드 호출
예제 4 - 1
반지름과 이름을 가진 Circle 클래스를 작성하고., Circle 클래스의 객체를 생성하라.
public class Circle {
int radius;
String name;
public Circle() {}
public double getArea() {
return 3.14 * radius * radius;
}
}
public static void main(String[] args) {
Circle pizza = new Circle();
pizza.radius = 10;
pizza.name = "해운대 피자";
double area = pizza.getArea();
System.out.println(pizza.name + "의 면적은 " + area);
Circle donut = new Circle();
donut.radius = 2;
donut.name = "크리스피 크림 도넛";
area = donut.getArea();
System.out.println(donut.name+"의 면적은 " + area);
}
예제 4 - 2 Rectangle 클래스 만들기 연습
너비와 높이를 입력받아 사각형의 면적을 출력하는 프로그램을 작성하라.
import java.util.Scanner;
class Rectangel {
int width;
int height;
public int getArea() {
return width * height;
}
}
public class RectApp {
public static void main(String[] args) {
Rectangle rect = new Rectangle();
Scanner scanner = new Scanner(System.in);
System.out.print("사각형의 너비와 높이 입력>> ");
rect.width = scanner.nextInt();
rect.height = scanner.nextInt();
System.out.println("사각형의 면적은 " + rect.getArea());
scanner.close();
}
}
Tip. 메모리 낭비를 막기 위해 scanner.close() 는 필수다.
하나의 .java 파일에는 단 하나의 public class 만 존재할 수 있음.
여러 클래스를 사용할 경우, public 속성을 제외한 클래스 사용 가능.
접근 지정자가 없으면 default로 동일한 패키지 안에서만 접근이 가능함
-> 패키지를 공부한 후에 복습!!!
생성자(Constructor)
예제 4 - 3 두 개의 생성자를 가진 Circle 클래스
public class Circle {
int radius;
String name;
public Circle() {
radius = 1;
name = "";
}
public Circle(int r, String n) {
radius = r;
name = n;
}
public double getArea() {
return 3.14 * radius * radius;
}
public static void main(String[] args) {//static은 객체 선언 없이 사용가능.
Circle pizza;
pizza = new Circle(10, "자바피자");
double area = pizza.getArea();
System.out.println(pizza.name + " 의 면적은 " + area);
Circle donut = new Circle();//매개변수 없이 생성자를 호출했으므로 반지름이 1
donut.name = "도넛피자";
area = donut.getArea();
System.out.println(donut.name + "의 면적은 " + area);
}
}
예제 4 - 4 생성자 선언 및 활용 연습
public class Book {
String title;
String author;
public Book(String t)
{
title = t;
author = "작자미상";
}
public Book(String t, String a)
{
title = t;
author = a;
}
public static void main(String[] args){
Book littlePrince = new Book("어린 왕자", "생텍쥐페리");
Book loveStory = new Book("춘향전");
System.out.println(littlePrince.title + " " + littlePrince.author);
System.out.println(loveStory.title + " " + loveStory.author);
}
}
오버로딩 vs 오버라이딩
ex.
public class Book {
String title;
String author;
public Book(String t)
{
title = t;
author = "Unknown";
}
public Book(String t, String a)
{
title = t;
author = a;
}
}
ex.
class Shape {
void draw(int i, int j)
{
System.out.println("Shape draw");
}
}
class Square extends Shape {
void draw(int i, int j)
{
System.out.println("Square draw");
}
}
Square 클래스에서는 Square 클래스에서 재정의한 draw 함수를 우선시한다.
기본 생성자
ex.
public class Circle {
int radius;
void set(int r)
{
radius = r;
}
double getArea()
{
return 3.14 * radius * radius;
}
public Circle(int r) // 생성자
{
radius = r;
}
}
이렇게 작성하면 Circle donut = new Circle();
이라 하면 컴파일 에러가 난다.
해당하는 생성자가 없기 때문!!
-> 개발자가 클래스에 생성자를 작성하였으므로 기본 생성자가 자동으로 생성되지 않은 것이다.
this 레퍼런스
ex.
public class Circle {
int radius;
public Circle() {
this.radius = 1;
}
public Circle(int radius) {
this.radius = radius; //파라미터의 이름이 클래스의 멤버 변수와 이름이 같은 경우
}
public Circle getMe()
{
return this;//객체 자신의 레퍼런스 리턴
}
}
ex.
public class Circle {
int radius;
public Circle(int radius) {
this.radius = radius;
}
public void set(int radius) {
this.radius = radius;
}
}
public class Circle {
int radius;
public Circle(int radius) {
this.radius = radius;
}
public Circle() {
this(0);//메소드랑은 다른 느낌이라 Circle()은 해서는 안된다!!
//객체를 생성한 후 만드는 다른 메소드랑은 다르기 때문
//나 자신을 만든다는 느낌으로 this()를 생각하면 좋을 듯!!
}
}
this()로 다른 생성자 호출
public class Book1 {
String title;
String author;
void show() {System.out.println(title + " " + author);}
public Book1() {
this("","");//세번째 생성자를 호출한 것이다.
System.out.println("생성자 호출됨");
}
public Book1(String title) {
this(title, "작자미상");
}
public Book1(String title, String author){
this.title = title;
this.author = author;
}
}
객체의 치환
객체의 치환은 객체가 복사되는 것이 아니며 레퍼런스가 복사되는 것이다.
ex.
Circle ob1 = new Circle();
Circle ob2 = new Circle();
가 있고
Circle s = ob2; 라고 하면 메모리 주소가 복사되는 것이다.
객체의 복사는
배열은 copyof, 객체는 clone()을 쓴다.
ex.
import java.util.Arrays;
public class Copy {
public static void main(String[] args) {
int[] array = new int[10];
for(int i = 0; i < 10; i++)
{
array[i] = i;
}
int[] brray = new int[10];
for(int i = 0; i < 10; i++)
{
brray[i] = 2 * i;
}
brray = Arrays.copyOf(array, 10);
for(int i= 0; i < 10; i++)
{
System.out.println(brray[i]);
}
}
}
객체 배열
Circle [] c;
c = new Circle[5]; //이제 레퍼런스들을 받을 준비가 된 것!1 -> C언어와의 가장 큰 차이이다.
for(int i = 0; i < c.length; i++)
{
c[i] = new Circle();//배열의 각 원소 객체 생성
}
ex.
class Circle2
{
int radius;
public Circle2(int radius)
{
this.radius = radius;
}
public double getArea()
{
return 3.14 * radius * radius;
}
}
public class CircleArray {
public static void main(String[] args) {
Circle2[] c = new Circle2[5];
for(int i = 0; i < c.length; i++)
{
c[i] = new Circle2(i);
System.out.println(c[i].getArea());
}
}
}
[출력]
0.0
3.14
12.56
28.259999999999998
50.24
객체 배열 만들기 연습
import java.util.Scanner;
class Book {
String title, author;
public Book(String title, String author)
{
this.title = title;
this.author = author;
}
}
public class BookArray {
public static void main(String[] args) {
Book[] book = new Book[2];
Scanner scanner = new Scanner(System.in);
for(int i = 0; i < book.length; i++)
{
System.out.print("제목 >> ");
String title = scanner.nextLine();
System.out.print("저자 >> ");
String author = scanner.nextLine();
book[i] = new Book(title, author); //배열 원소 객체 생성
}
for(int i = 0; i < book.length; i++)
{
System.out.println("(" + book[i].title + ", " + book[i].author + ")");
}
scanner.close();
}
}
[출력]
메소드 형식
자바의 인자 전달 방식
레퍼런스 전달의 장점 :
큰 객체나 배열의 경우에도 정수 크기인 레퍼런스만 전달
매개변수 전달로 인한 시간이나 메모리 오버헤드가 없음.
//오버헤드 : 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간 · 메모리 등을 말한다.
예제 - 객체 전달 (레퍼런스 전달)
class Circle3
{
int radius;
public Circle3(int radius) {
this.radius = radius;
}
public double getArea() {
return 3.14 * radius * radius;
}
}
public class ReferencePassing {
public static void increase(Circle3 m) {
m.radius++;
}
public static void main(String[] args) {
Circle3 pizza = new Circle3(10);
System.out.println("Before Passing radius: " + pizza.radius);
Increase(pizza);
System.out.println("After Passing radius: " + pizza.radius);
}
}
출력하면 10에서 11로 반지름이 증가한 것을 알 수 있다.
예제 - 참조에 의한 호출
class Pizza {
int size;
String topping;
public Pizza()
{
size = 10;
topping = "기본";
}
public void increaseSize(Pizza pizza)
{
pizza.size += 10;
}
public void addTopping(Pizza pizza)
{
pizza.topping += "치즈, 베이컨";//이렇게 하면 문자열 뒤에 저 문자열이 추가된다.
}
public void printPizza(Pizza pizza)
{
System.out.println("[피자 주문 내역] 크기: " + pizza.size + ", 토핑: " + pizza.topping);
}
}
public class CallByReferenceEx {
public static voud increaseSize(Pizza pizza)
{
pizza.size += 10;
}
public static void addTopping(Pizza pizza)
{
pizza.topping += "치즈, 베이컨";
}
public static void main(String[] args)
{
Pizza pizza1 = new Pizza();
pizza.printPizza(pizza1);
increaseSize(pizza1);
System.out.println("피자 주문 변경");
addTopping(pizza1);
pizza1.printPizza(pizza1);
}
}
[출력]
[피자 주문 내역] 크기: 10, 토핑: 기본
피자 주문 변경
[피자 주문 내역] 크기: 20, 토핑: 기본 + 치즈, 베이컨
인자 전달 : 배열
예제 4 - 8 : 인자로 배열이 전달되는 예
public class ArrayParameterEx {
static void replaceSpace(char a[]) { //배열의 스페이스를 콤마로 바꾸는 함수
for(int i = 0; i < a.length; i++)
{
if(a[i] == ' ') a[i] = ',';
}
}
static void printCharArray(char a[])//배열을 프린트하는 함수
{
for(int i = 0; i < a.length; i++)
{
System.out.print(a[i]);
}
System.out.println();
}
public static void main(String[] args)
{
char c[] = {'T','h','i','s',' ','i','s',' ','a',' ','p','e','n','c','i','l','.'};
printCharArray(c);
replaceSpace(c);
printCharArray(c);
}
}
This is a pencil 이 This,is,a,pencil 로 출력된다.
메소드 오버로딩
//메소드 오버로딩이 성공한 사례
class MethodOverloading{
public int getsum(int i, int j)
{
return i + j;
}
public int getsum(int i, int j, int k)
{
return i + j + k;
}
}
//메소드 오버로딩이 실패한 사례
class MethodOverloading{
public int getsum(int i, int j)
{
return i + j;
}
public double getsum(int i, int j)
{
return (double)(i + j);
}
}
메소드 오버로딩이 성공했을 때,
getsum()함수 호출은 매개 변수의 개수와 매개 변수의 타입에 따라 서로 다른 함수가 호출된다.
객체의 소멸과 가비지 컬렉션
자바의 패키지 개념
패키지
접근 지정자
자바의 접근 지정자는 4가지가 있다.
public, protected, public, default(접근 지정자 생략)
클래스 접근 지정
멤버 접근 지정자
[정리]
ex.
class Sample {
public int a;
private int b;
int c;
}
public class AccessEx {
public static void main(String[] args) {
Sample aClass = new Sample();
aClass.a = 10;
aClass.b = 10;//이건 불가능!!!!
aClass.c = 10;
}
}
static 변수와 non-static 변수
non-static 변수 (인스턴스 변수)의 특성
공간적 특성 - 멤버들은 객체마다 독립적으로 별도 존재
인스턴스 멤버라고도 부름
시간적 특성 - 필드와 메소드는 객체 생성 후 사용 가능
비공유 특성 - 멤버들은 다른 객체에 의해 공유되지 않고 배타적
static 변수 (정적 변수)란?
객체마다 생기는 것이 아님
클래스당 하나만 생성됨
클래스 멤버라고도 부름
객체를 생성하지 않고 사용 가능.
특성
-> 모든 객체들이 공유하는 변수가 static 변수로 클래스들의 전역 변수 개념이다.
class StaticSample {
int n;
void g() {...}
static int m;
static void f() {...}
}
static 멤버는 객체가 생기기 전에도 사용가능
(Math.~~ 이 객체를 생성하지 않고도 사용가능한 이유)
static 멤버를 클래스 이름으로 접근하는 사례
class StaticSample {
public int n;
public void g() {
m = 20;
}
public void h() {
m = 30;
}
public static int m;
public static void f()
{
m = 5;
}
}
public class Ex {
public static void main(String[] args)
{
StaticSample.m = 10;//상수는 아니다.
StaticSample s1;
s1 = new StaticSample();
System.out.println(s1.m);
s1.f();
StaticSample.f();//m은 5호 변경
}
}
static의 활용
static 메소드의 제약 조건
static 메소드는 non - static 멤버에 접근할 수 없음
- 객체가 생성되지 않은 상황에서도 static 메소드는 실행될 수 있기 때문에 non-static 메소드와 필드 사용 불가.
- 반대로 non - static 메소드는 static 멤버 사용 가능.
class StaticMethod {
int n;
void f1(int x) {n = x;}//정상
void f2(int x) {n = x;}//정상
static int m;
static void s1(int x) (n = x;)//컴파일 오류 : static 메소드는 non-static 필드 사용 불가
}
객체가 생성되지 않아도 사용가능하기 때문에 non - static 변수에 접근 불가능하므로, (어떤 객체에 접근하는지 알 수가 없어짐) 당연히 this 역시 사용 불가. -> this는 현재 객체를 가리키는 레퍼런스
final 클래스와 메소드
final 클래스 - 클래스 상속 불가
final 메소드 - overriding 불가 -> 상속해서 수정 불가!!!
final 필드
final 필드 : 상수 선언
상수를 선언할 때 사용
class SharedClass {
public static final double PI = 3.14;
}
상수 필드는 선언 시에 초기값을 지정하여야 한다.
상수 필드는 실행 중에 값을 변경할 수 없다.
내부 클래스(Inner Class)
클래스 안에 클래스를 정의함
형식
class OuterClass {
private int value = 0;
class InnerClass{...}
}
내부 클래스는 외부 클래스의 private 멤버에도 접근할 수 있음 -> 같은 클래스 안에 있잖어~
내부 클래스 예제
class OuterClass {
private int value = 10;
class InnerClass {
public void myMethod() {
System.out.println("OuterClass의 private 변수 값: " + value);
}
}
public OuterClass() {
InnerCLass obj = new InnerClass();
obj.MyMethod;
}
}
위 예제를 통해 외부 클래스의 private 멤버 변수에 접근이 가능하다는 것을 알 수 있다.
private 클래스 선언
최상위 클래스 선언에 private 사용 안함
-접근하지 못하는 클래스를 생성할 이유가 없음.
private 클래스 선언 예
-내부 클래스(inner class) 선언에 사용
public class MyClass {
public MyClass() {
System.out.println("MyClass 생성자 ");
}
private class InnerMyClass {
public InnerMyClass() {
System.out.println("InnerClass 생성자");
}
public String getName(){
return this.getClass().getName();
}
}
public void showInnerClassName() {
InnerMyClass ic = new InnerMyClass();
String icName = ic.getName();
System.out.println(icName);
}
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.showInnerClassName();
}
}