자바는 객체 지향 프로그래밍 언어이다.
객체 지향에는 클래스, 객체, 인스턴스, 상속, 인터페이스, 다형성, 추상화
등의 많은 개념들이 존재함.
객체 지향 프로그래밍이란?
계산기에 숫자 3을 입력하고 + 기호를 입력한 후 4를 입력하면 결괏값 7을 보여준다.
다시 한번 + 기호를 입력한 후 3을 입력하면
기존 결괏값 7에 3을 더해 보여줌.
즉, 계산기는 이전에 계산한 결괏값을 항상 메모리 어딘가에 저장하고 있어야함.
자바로 구현해보기.
class Calculator {
static int result = 0;
static int add(int num) {
result += num;
return result;
}
}
public class Sample {
public static void main(String[] args) {
System.out.println(Calculator.add(3));
System.out.println(Calculator.add(4));
}
}
add 메서드는 매개변수 num으로 받은 값을 이전에 계산한 결괏값에 더한 후 돌려주는 메서드
결과 값을 유지하기 위해 return 전역 변수(static 변수)를 사용함.
- 만약 2대의 계산기가 필요하다면?
클래스를 새로 만들어야함. 각각 따로
class Calculator1 {
static int result = 0;
static int add(int num) {
result += num;
return result;
}
}
class Calculator2 {
static int result = 0;
static int add(int num) {
result += num;
return result;
}
}
public class Sample {
public static void main(String[] args) {
System.out.println(Calculator1.add(3));
System.out.println(Calculator1.add(4));
System.out.println(Calculator2.add(3));
System.out.println(Calculator2.add(7));
}
}
Calcuator1의 결괏값이 Calculator2에 아무 영향을 끼치지 않음을 확인할 수 있다.
하지만 계산기가 3개, 5개, 10개로 점점 더 많이 필요해진다면?
클래스를 그 때마다 추가할 것인가? 또한, 기능을 추가하게 된다면 상황이 더 안좋게 된다.
이를 객체로 간단하게 해결해보자
class Calculato{
int result = 0;
int add(int num) {
result += num;
return result;
}
}
}
public class Sample {
public static void main(String[] args) {
Calculator cal1 = new Calculator(); // 계산기1 객체를 생성
Calculator cal2 = new Calculator(); // 계산기2 객체를 생성
System.out.println(Calculator1.add(3));
System.out.println(Calculator1.add(4));
System.out.println(Calculator2.add(3));
System.out.println(Calculator2.add(7));
}
}
static : 메서드에 static 키워드가 붙으면 클래스 메서드가 되어 객체를 만들지 않아도
‘클래스명.메서드명’ 형태로 호출할 수 있음
객체를 생성했기 때문에 static을 삭제함.
Calculator 클래스로 만든 별개의 계산기 cal1, cal2 이것을 객체라고 부른다. 이들은 각각의 역할을 수행한다.
결괏값 역시 독립적인 값을 유지한다.
객체를 사용하면 계산기 수가 늘어나더라도 객체를 생성하기만 하면 되기 때문에 아프이 경우와는 달라 매우 간단해진다.
빼기 기능을 추가하려면 sub 메서드를 추가해보자
class Calculator {
int result = 0;
int add(int num) {
result += num;
return result;
}
int sub(int num) {
result -= num;
return result;
}
}
클래스
객체가 무엇인지를 알기 위해서 먼저 Animal 클래스를 다음과 같이 Sample.java 파일에 작성하자
class Animal {
}
public class Sample {
public static void main(String[] args) {
}
}
🔴 Animal 클래스를 Sample.java파일에 작성한 것은 설명을 위한 것.
보통 클래스의 경우는 특별한 경우가 아니라면 파일 단위로 하나씩 작성함.
Animal 클래스는 가장 간단한 형태의 클래스.
현재는 선언만 있고 내용이 없는 껍데기 뿐인 클래스.
하지만 이 클래스도 아주 중요한 기능을 가지고 있다. 바로 객체(Object)를 만드는 기능이다.
객체는 다음과 같이 만들 수 있음.
class Animal {
}
public class Sample {
public static void main(String[] args) {
Animal cat = new Animal();
}
}
이렇게하면 Animal 클래스의 인스턴스(instance)인 cat.
즉, Animal의 객체가 만들어진다.
🔴 객체와 인스턴스의 차이?
Animal cat = new Animal() 이렇게 만들어진 cat은 객체임.
그리고 cat이라는 객체는 Animal의 인스턴스임.
인스턴스(instance)라는 말은 특정 객체가 어떤 클래스의 객체인지를 관계 위주로 설명할 때 사용됨.
즉, ‘cat은 인스턴스’보다는 ‘cat은 객체’라는 표현이
‘cat은 Animal의 객체’보다는 ‘cat은 Animal의 인스턴스’ 라는 표현이 잘어울린다.
그렇다면 다음과 같이 무수히 많은 동물 객체를 Animal 클래스로 만들 수 있다.
Animal cat = new Animal();
Animal dog = new Animal();
Animal horse = new Animal();
...
- 객체 변수란?
Animal 클래스를 발전시켜보기.
동물 객체 이름을 지어보자.
class Animal {
Stirng name;
}
public class Sampe{
public static void main(String[] args) {
Animal cat = new Animal();
}
}
이렇게 클래스에 선언된 변수를 객체 변수(instance variable)이라고 함.
클래스에 의해 생성되는 것은 객체,
클래스에 선언된 변수는 객체 변수.
객체 변수는 인스턴스 변수, 멤버 변수, 속성
객체 변수를 만들었으니 사용하기
객체.객체변수
cat.name
class Animal {
String name;
}
public class Sample {
public static void main(String[] args) {
Animal cat = new Animal();
System.out.println(cat.name); // null출력
}
}
객체 변수로 name을 선언했지만 아무런 값도 대입하지 않았기 때문에 null
- 메서드란?
객체 변수에 접근하는 방법에 대해 알아보았음.
객체 변수에 이번에 값을 대입해보자.
대입하는 방법은 여러가지 있지만 메서드를 이용해보자.
클래스에는 객체 변수와 함께 메서드가 있다.
메서드(method)는 클래스 내에 구현된 함수를 말한다.
class Animal {
String name;
public void setName(String name) {
this.name = name;
}
}
public class Sample {
public static void main(String[] args) {
Animal cat = new Animal();
System.out.println(cat.name);
}
}
입력 : String name
출력 : void (’리턴값 없음’을 의미)
메서드 내부를 살펴보면
this.name = name;
객체 변수에 접근하기 위해 도트연산자(.)로 접근할 수 있던 것과 마찬가지로
메서드를 호출하기 위해서는 객체.메서드로 호출해야한다.
우리가 만든 setName 메서드를 호출하려면 다음과 같이 호출함
cat.setName("boby");
setName메서드를 호출할 수 있도록 main 메서드를 다음과 같이 수정해보자
class Animal {
String name;
public void setName(String name) {
this.name = name;
}
}
public class Sample {
public static void main(String[] args) {
Animal cat = new Animal();
cat.setName("boby");
System.out.println(cat.name);
}
}
🔴 this 이해하기
this.name = name;
main 메서드에서 cat.setName(”boby”) 는 “boby”를 입력값으로 하여 setName 메서드를 호출했기 때문에 메서드 입력항목 name → setName(String name) 에는 “boby”라는 문자열이 전달됨.
그렇다면 다음과 같은 뜻이 됨.
this.name = "boby";
cat.setName(”boby”) 와 같이 cat 객체를 사용하여 setName 메서드를 호출하면
⭐ setName 메서드 내부에 선언된 this는 바로 cat 객체를 지칭한다. ⭐
따라서 this.name = “boby”;는 다음과 같이 해석된다.
// this.name
cat.name = "boby";
**객체.객체변수 = 값**
[cat.name](<http://cat.name>) 은 이제 값이 들어 있으니 이제 null이 아님
- 객체 변수는 공유되지 않는다.
class Animal {
String name;
public void setName(String name) {
this.name = name;
}
}
public class Sample {
public static void main(String[] args) {
Animal cat = new Animal();
cat.setName("boby");
Animal dog = new Animal();
dog.setName("happy");
}
}
setName 메서드에 의해 다음과 같이 문장이 두 번 실행될 것임.
cat.name = "boby";
dog.name = "happy";
출력을 해보자.
class Animal {
String name;
public void setName(String name) {
this.name = name;
}
}
public class Sample {
public static void main(String[] args) {
Animal cat = new Animal();
cat.setName("boby"); // 메서드 호출
Animal dog = new Animal();
dog.setName("happy");
System.out.println(cat.name);
System.out.println(dog.name);
}
}
//boby
//happy
클래스에서 가장 중요한 부분
객체 변수의 값이 독립적으로 유지된다는 것이다.
사실 이 점이 바로 클래스의 존재이유 이다.
객체 지향적(object oriented)이라는 말의 의미도 곱씹어보면
이 객체 변수의 값이 독립적으로 유지되기 때문에 가능한 것.
메서드 더 살펴보기
자바는 클래스를 떠나는 존재는 있을 수 없기 때문에 자바의 함수는 따로 존재하지 않고 클래스 내 존재한다. 자바는 이러한 클래스 내의 함수를 메서드(method)라고 함.
- 메서드를 사용하는 이유
프로그래밍을 하다 보면 똑같은 내용을 반복해서 적을 때가 있음.
이럴 때 바로 메서드가 필요하다.
가장 간단하지만 많은 것을 설명해주는 다음 메서드를 살펴보자.
int sum(int a, int b) {
return a+b;
}
/* “sum 메서드는 입력값으로 두개의 값(int 자료형 a, int 자료형 b)을 받으며
리턴값은 두 개의 입력값을 더한 값(int 자료형)이다.” */
여기서 return은 메서드의 결과값을 돌려주는 명령어이다.
- 매개 변수와 인수
매개 변수(parameter)와 인수(arguments)는 혼용되는 헷갈리는 용어이다.
잘 기억해주기.
매개변수 : 메서드에 전달된 입력값을 저장하는 변수
인수 : 메서드를 호출할 때 전달하는 입력값
매개변수 - 메서드 다음에 씀
인수 - 객체.메서드 다음에 씀
- 메서드의 입력값과 리턴값
메서드는 입력값을 가지고 어떤 처리를 하여 적절한 리턴값을 돌려주는 블랙박스와 같음
왜냐하면 메서드의 내부 동작이 복잡하더라도 외부에서는 그저 입력값을 넣으면
리턴값이 나오는 과정만 관심있음.
- 메서드의 구조
리턴자료형 메서드명(입력자료형1 매개변수1, 입력자료형2 매개변수2, ...) {
...
return 리턴값; // 리턴자료형이 void 인 경우에는 return 문이 필요없다.
}
결괏값을 반환하기 위해 return이라는 키워드를 사용함.
입출력 유무에 따라 다음과 같이 4가지로 분류함
- 입력과 출력이 모두 있는 메서드
- 입력과 출력이 모두 없는 메서드
- 입력은 없고 출력은 있는 메서드
- 입력은 있고 출력은 없는 메서드
1) 입력값과 리턴값이 모두 있는 메서드
int sum(int a, int b) {
return a+b;
}
sum 메서드의 입출력 자료형은 다음과 같음
입력값 : int 자료형 a, int 자료형 b
출력값 : int 자료형
리턴값을 받을 변수 = 객체.메서드명(입력인수1, 입력인수2, ...)
사용예시
Sample sample = new Sample();
int result = sample.sum(3, 4);
return 값을 저장하는 result의 자료형은 int로 해야함
sum 메서드의 return의 자료형이 int로 되어 있기 때문.
2) 입력값이 없는 메서드
String say() {
return "Hi";
}
입출력 자료형 :
입력값 : 없음
리턴값 : String 자료형
입력 인수를 나타내는 괄호 안이 비어있음.
이렇듯 입력 인수가 없을 경우에는 괄호 안을 비워놓으면 됨.
public class Sample {
String say() {
return "Hi";
}
public static void main(String[] args) {
Sample sample = new Sample();
String a = sample.say();
System.out.println(a); // "Hi" 출력
}
}
3) 리턴 값이 없는 메서드
void sum(int a, int b) {
System.out.println(a+"과 "+b+"의 합은 "+(a+b)+"입니다.");
}
입출력 자료형은 다음과 같음
입력값 : int 자료형 a, int 자료형 b
리턴값 : void (리턴값 없음)
리턴 값이 없는 메서드는 명시적으로 리턴 자료형 부분에 void라고 표기한다.
public class Sample {
void sum(int a, int b) {
System.out.println(a+"과 "+b+"의 합은 "+(a+b)+"입니다.");
}
public static void main(String[] args) {
Sample sample = new Sample();
sample.sum(3, 4);
}
}
위와 같이 사용할 수 있음 Animal에서 setName도 같은 종류의 메서드였다.
그런데 7이라는 문장을 출력했는데 왜 리턴값이 없나?
System.out.println 문은 메서드 내에서 사용되는 문장일뿐 리턴값이 없다.
리턴값은 오직 return 명령어로만 반환이 가능하다.
4) 둘 다 없는 메서드
입력값과 리턴값이 모두 없는 메서드도 존재함.
void say() {
System.out.println("Hi");
}
입력값 : 없음
리턴값 : void
이 메서드를 사용하는 방법은 한가지이다.
public class Sample {
void say() {
System.out.println("Hi");
}
public static void main(String[] args) {
Sample sample = new Sample();
sample.say();
}
}
- return의 또 다른 쓰임
특별한 경우 메서드를 빠져나가고 싶다면 return을 단독으로 사용하여 메서드를 즉시 빠져나갈 수 있다.
public class Sample {
void sayNick(String nick) {
if("바보".equals(nick)) {
return;
}
System.out.println("나의 별명은 " + nick + " 입니다");
}
public static void main(String[] args) {
Sample sample = new Sample();
sample.sayNick("야호");
sample.sayNick("바보");
}
}
// 나의 별명은 야호 입니다.
이 메서드는 입력값으로 ‘바보’라는 값이 들어오면 문자열을 출력하지 않고 메서드를 즉시 빠져나간다.
이 방법은 메서드의 리턴자료형이 void일 때만 가능하다.
리턴 자료형이 명시되어 있는 메서드에서 return문만 작성하면 컴파일오류가 발생함.
- 메서드 내에서 선언된 변수의 효력 범위
변수의 이름을 메서드 밖에서 사용한 이름과 동일하게 사용하면 어떻게 될까?
public class Sample {
void varTest(int a) {
a++;
}
public static void main(String[] args) {
int a = 1;
Sample sample = new Sample();
sample.varTest(a);
System.out.println(a);
}
}
// 1출력
main 메서드를 순서대로 분석해보기
- main 메서드에서 a라는 int 자료형의 변수를 생성하고 a에 1을 대입했다.
- varTest 메서드를 입력값으로 a를 주어 호출했다.
- a의 값을 출력하게 했다.
🔴varTest 메서드에서 a의 값을 1증가시켰으니 2가 출력되어야 할 것 같지만 프로그램을 실행해보면
1을 출력했다 왜 일까?
→ 메서드에서 사용한 매개변수는 메서드 안에서만 쓰이는 변수이기 때문
즉 void varTest(int a)라는 문장에서 매개 변수 a는 메서드 안에서만 쓰이는 변수이지 메서드 밖의 변수 a가 아니라는 말임.
앞서 매개 변수 이름을 a로 사용한 varTest 메서드는 다음처럼 매개 변수 이름을 b로 사용한 varTest와 기능적으로 완전히 동일함
public void varTest(int b) {
b++;
}
다시 말해 메서드에서 쓰이는 매개변수와 메서드 밖의 변수이름은 같더라도 서로 전혀 영향 X
🔴 그렇다면 a를 증가시키는 방법은?
public class Sample {
int varTest(int a) {
a++;
return a;
}
public static void main(String[] args) {
int a = 1;
Sample sample = new Sample();
a = sample.varTest(a);
System.out.println(a);
}
}
이처럼 return문을 이용하면 됨.
따라서 a = sample.varTest(a)처럼 작성하면 a의 값은 다시 varTest 메서드의 리턴값으로 대입된다(즉, 1만큼 증가된 값으로 a의 값이 변경된다.).
varTest에 입력값이 int 자료형이 아닌 객체인 경우 살펴보기.
메서드에 값을 전달하는 것과 객체를 전달하는 것에는 큰 차이가 있음
객체를 메서드의 입력으로 넘기고 메서드가 객체의 속성값을 변경한다면 메서드 수행 이후에도 객체는 변경된 속성값을 유지함.
public class Sample {
int a; // 객체변수 a
void varTest(Sample sample) {
sample.a++;
}
public static void main(String[] args) {
Sample sample = new Sample();
sample.a = 1;
sample.varTest(sample);
System.out.println(sample.a);
}
}
이번에는 int 자료형인 변수 a를 Sample 클래스의 객체 변수로 선언했다.
그리고 varTest 메서드는 Sample 클래스의 객체를 입력받아 해당 객체의 a 값을 1만큼 증가시키도록 했다.
그리고 main 메서드에서는 varTest 메서드 호출 시 Sample 클래스의 객체인 sample을 전달하도록 수정했다.
이렇게 수정하고 프로그램을 실행해보면 sample 객체의 객체 변수 a의 값이 원래는 1이었는데 varTest 메서드 실행 후 1만큼 증가되어 2가 출력되는 것을 확인할 수 있다.
varTest 메서드의 입력 파라미터 값이 아닌 Sample 클래스의 객체
이렇게 메서드가 객체를 전달받으면 메서드 내의 객체는 전달받은 객체 그 자체로 수행됨.
따라서 입력으로 전달받은 sample 객체의 객체 변수 a의 값이 증가하게 되는 것.
메서드의 입력 항목이 값인지 객체인지 구별하는 기준?
입력 항목의 자료형이 원시 자료형인지 아닌지에 따라 나뉨
int 자료형과 같은 원시 자료형인 경우 값이 전달되고 그 외의 경우는 객체가 전달됨.
Integer와 같은 원시 자료형의 Wrapper 클래스와 String도 값이 전달된다.
- this 활용하기
sample.varTest(sample);
생각해보니까 객체를 전달할 필요가 없음.
this 키워드를 이용하면 객체에 접근할 수 있음.
public class Sample {
int a; // 객체변수 a
void varTest() {
this.a++;
}
public static void main(String[] args) {
Sample sample = new Sample();
sample.a = 1;
sample.varTest();
System.out.println(sample.a);
}
}
값에 의한 호출과 객체에 의한 호출
메서드에 값(원시 자료형)을 전달하는 것과 객체를 전달하는 것에는 큰 차이가 있다.
매우 중요하기 때문에 다시 알아보자.
결론 : 메서드에 객체를 전달할 경우 메서드에서 객체 변수의 값을 변경할 수 있음
class Updater {
void update(int count) {
count++;
}
}
class Counter {
int count = 0;
}
public class Sample {
public static void main(String[] args){
Counter myCounter = new Counter();
System.out.println("before update:" + myCounter.count);
Updater myUpdater = new Updater();
myUpdater.update(myCounter.count);
System.out.println("after update:" + myCounter.count);
}
}
Updater 클래스는 전달받은 숫자를 1증가
Counter 클래스는 count라는 객체 변수를 가지고 있음.
Sample 클래스의 main 메서드는 Counter 클래스에 의해 생성된 myCounter 객체의
객체 변수인 count값을 Updater 클래스를 이용하여 증가시키고자 함.
하지만 출력은
before update:0
after update:0
객체 변수 count의 값을 update메서드에 넘겨서 변경시키더라도 값에 변화가 없음.
이유 : update 메서드는 값(int 자료형)을 전달받았기 때문
class Updater {
void update(Counter counter) {
counter.count++;
}
}
class Counter {
int count = 0; // 객체변수
}
public class Sample {
public static void main(String[] args) {
Counter myCounter = new Counter();
System.out.println("before update:"+myCounter.count);
Updater myUpdater = new Updater();
myUpdater.update(myCounter);
System.out.println("after update:"+myCounter.count);
}
}
이전 예제와 차이점은 update 메서드의 입력 항목
이전에는 int count와 같이 값을 전달받았다면 지금은 Counter conter와 같이 객체를 전달받도록 변경함.
update 메서드를 호출하는 부분도 다음처럼 바뀜
myUpdater.update(myCounter);
before update:0
after update:1
이렇게 메서드의 입력으로 객체를 전달하면
메서드가 입력받은 객체를 그대로 사용하기 때문에
메서드가 객체의 속성값을 변경하면 메서드 수행 후에도 객체의 변경된 속성값이 유지된다.
참고자료 :
위키독스 점프 투 자바 https://wikidocs.net/book/31
'Java' 카테고리의 다른 글
[점프 투 자바] 6장 자바의 입출력 (1) | 2024.01.01 |
---|---|
[점프 투 자바] 5장 객체지향 프로그래밍2 (1) | 2024.01.01 |
[점프 투 자바] 4장 제어문 이해하기 (0) | 2024.01.01 |
[점프 투 자바] 3장 완결 리스트, 맵, 집합, 형변환, fianl 등 (1) | 2024.01.01 |
[점프 투 자바] 3장 자료형 (String 내장 메서드 등) 배열까지. (0) | 2024.01.01 |