패스트캠퍼스의 국비지원교육인 Java & Spring 웹 개발 종합반의 2주차는 Java 제어문과 객체지향 개념을 학습한다. 제어문은 시스템의 흐름을 제어하는 구문으로 조건문과 반복문으로 나뉘어 진다. 여기서는 제어문은 간단히 복습하고 객체지향 개념을 조금 더 깊게 학습하는 것을 목표로 하였다.
if
if (조건식) {
// 조건식이 참일 경우 실행될 문장
}
기본적인 문법. 괄호 안의 조건식이 참일 경우 블럭 내의 문장이 수행된다. 조건식이 거짓이라면 수행되지 않고 다음 줄로 넘어간다.
if - else
if (조건식) {
// 조건식이 참일 경우 실행될 문장
} else {
// 조건식이 거짓일 경우 실행될 문장
}
else 문을 붙여 참일 경우와 거짓일 경우 수행될 문장을 각각 구별한다.
참일 경우 else 문의 블럭 내의 문장은 수행되지 않고 건너 뛴다.
if - else if
if (조건식1) {
// 조건식1이 참일 경우 실행될 문장
} else if (조건식2) {
// 조건식1은 거짓이고 조건식2이 참일 경우 실행될 문장
} else {
// 조건식1과 2 모두 거짓일 경우 실행될 문장
// 생략 가능
}
조건이 여러개일 경우 else if 문을 사용한다. 위에서부터 순서대로 검사한 뒤 참인 문장을 수행하며 참인 문장이 없는 경우 else 블럭 내의 문장이 수행된다. else 문은 생략할 수 있다.
switch
switch (변수) {
case 값1:
// 변수가 값1 과 같은 값일 때 수행될 문장
break;
case 값2:
// 변수가 값2 와 같은 값일 때 수행될 문장
break;
default:
// 변수의 값과 일치하는 case 문이 없는 경우 수행될 문장
}
int i = 10;
switch (i) {
case 10:
System.out.println("10을 받았을 때 수행될 문장");
break;
case 11:
System.out.println("11을 받았을 때 수행될 문장");
break;
default:
System.out.println("10과 11이 아닐 떄 수행될 문장");
}
만일 조건이 여러개라면 스위치문을 통해 간략하게 구현할 수 있다.
스위치문에 조건으로 주어진 변수 내 데이터와 케이스문에 기재된 값을 비교하여 일치하는 케이스문을 찾아가 실행된다. 다만 조건에 맞는 케이스문만 실행하는것이 아닌 찾아간 케이스문부터 아래로 순차적으로 실행되므로 break 문으로 switch - case 문을 빠져나와야 한다.
while
while (조건식) {
// 조건식이 참일동안 수행될 문장
}
do - while
do {
// 조건식이 참일 동안 수행될 문장
} while (조건식);
while 문은 조식이 참일동안 블럭을 벗어나지 않고 끝을 만나면 처음으로 돌아가 계속하여 반복한다.
일반적인 while 문은 조건식이 거짓일 경우에는 한번도 수행하지 않게 되므로 조건식의 결과와는 관계 없이 한번은 반드시 수행되어야 하는 경우 do-while 문을 사용한다. while 문의 끝에는 ;
을 작성하지 않지만 do-while 문에는 ;
을 반드시 작성해야 하므로 주의한다.
for
for (초기화식; 조건식; 증감식) {
// 반복할 문장
}
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
for문은 초기화식을 먼저 실행한 다음, 조건에 맞는지를 확인한다. 참일 경우 블럭 내 문장을 수행한 뒤 증감식을 진행한 다음 다시 조건식을 검사하여 반복 여부를 결정한다.
위 예제의 경우에는 int i = 0;
이 실행되어 변수 i 가 생성되었고, i < 10;
의 조건이 만족하므로 System.out.println(i);
문장이 실행된다. 그 다음 i++
가 실행되어 i는 1이 되고 다시 조건식을 확인하여 수행된다. 즉 실행결과는 0 ~ 9 까지 10번 반복하여 콘솔에 출력된다.
for 문의 경우 초기화식, 조건식, 증감식 모두 생략이 가능하며 전부 생략한 경우 무한 반복문이 된다.
주로 for 문의 경우 몇번 반복해야 하는지를 정확히 알고 있는 경우 사용하며 while 문과 for문은 언제든 변환할 수 있다.
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
// 위 for 문과 아래 while 문은 완벽하게 동일하다.
int i = 0;
while (i < 10) {
System.out.println(i);
i++;
}
중첩 for문
for (int i = 1; i < 10; i++) {
// 반복문 1
for (int j = 1; j < 10; j++) {
// 반복문 2
System.out.println(i + "*" + j + "=" + i * j);
}
}
반복문 안에 또 반복문을 사용할 수 있다. 식에 따르면 반복문 1 안에서 반복문 2가 9번 실행되니 반복문 문장이 총 81번 수행되어 99단이 출력된다고 볼 수 있다.
반복을 하다 특정 조건을 만족하는 경우 반복문을 빠져 나오거나 다음 반복으로 넘어가는 경우 사용한다. break 문은 switch 문에서도 사용한다.
int num = 0;
while (true) { // 조건식이 참이므로 무한 반복문이 된다.
num++; // 숫자를 1증가시킴
if (num != 10) { // 숫자가 10이 아니라면
System.out.println("10이 아님");
continue; // 다음 반복으로 넘어감
}
System.out.println("10임");
break; // 반복문을 종료함
}
제임스 고슬랭이 작성한 자바 언어 설명서에는 객체란 클래스의 인스턴스나 배열을 말한다고 정의되어 있다. 객체지향 프로그래밍에서 클래스를 기반으로한 변수를 클래스의 인스턴스라고 하며 클래스는 제품 설계도, 인스턴스는 그 제품 설계도를 기반으로 생성한 제품
이라고 생각하면 쉽다.
우리가 지금 사용중인 컴퓨터를 예시로 들어보자
public Computer { // 컴퓨터
// 컴퓨터의 구성요소들
String cpu;
int ram;
int hdd;
}
이렇게 작성된 클래스를 이용하여 인스턴스를 생성할 수 있으며 이를 클래스의 인스턴스화 라고 한다.
Computer com = new Computer();
com.cpu = "intel 12th i5";
com.ram = 16;
com.hdd = 500;
new 연산자를 이용하여 객체를 생성한 뒤 Computer 타입의 참조변수 com에 참조한다. 이 때 com 이 객체, 즉 인스턴스이다.
위 예제에서는 인스턴스를 생성한 뒤에 각 속성에 접근하여 초기화를 진행했다. 생성자를 사용하면 인스턴스의 생성과 함께 초기화가 가능하다.
public Computer { // 컴퓨터
// 컴퓨터의 구성요소들
String cpu;
int ram;
int hdd;
// 생성자
public Computer() {
this("intel 12th i5", 16, 500);
}
public Computer(String cpu, int ram, int hdd) {
this.cpu = cpu;
this.ram = ram;
this.hdd = hdd;
}
// 매개변수로 받은 cpu와 객체 자신의 cpu를 구분하기 위해 this 사용
public staitc void main (String args[]) {
Computer com1 = new Computer("intel 12th i5", 16, 500);
Computer com2 = new Computer();
}
}
위 코드에서는 2개의 생성자를 확인할 수 있다.
public Computer() {
this("intel 12th i5", 16, 500);
}
이렇게 매개변수를 가지지 않는 생성자를 기본 생성자라고 한다. 컴파일러가 자동으로 추가해주어 여태까지는 작성하지 않아도 됐었지만 이 기본 생성자에서는 this를 사용하여 오버로드한 다른 생성자를 호출하였다. 기본 생성자는 다른 생성자가 있는 경우 반드시 직접 작성해야 한다.
public Computer(String cpu, int ram, int hdd) {
this.cpu = cpu;
this.ram = ram;
this.hdd = hdd;
}
매개변수 3개를 가진 생성자이다. 매개변수로 받아온 값을 객체의 속성값에 대입한다. 위에서 본 기본 생성자는 매개변수 3개를 가진 생성자에게 그 매개변수 값을 담아 넘겨주었다. 오버로딩은 뒤에서 자세히 다룬다.
변수나 메서드의 사용 권한은 다음과 같은 접근 제어자를 사용하여 설정할수 있다.
접근 제어자는 private -> default -> protected -> public 순으로 보다 많은 접근을 허용한다.
접근제어자가 private으로 설정되었다면 private 이 붙은 변수, 메서드는 해당 클래스에서만 접근이 가능하다.
접근 제어자를 별도로 설정하지 않는다면 접근 제어자가 없는 변수, 메서드는 default 접근 제어자가 되어 해당 패키지 내에서만 접근이 가능하다.
protected
접근제어자가 protected로 설정되었다면 protected가 붙은 변수, 메서드는 동일 패키지의 클래스 또는 해당 클래스를 상속받은 다른 패키지의 클래스에서만 접근이 가능하다.
public
접근제어자가 public으로 설정되었다면 public 접근제어자가 붙은 변수, 메서드는 어떤 클래스에서라도 접근이 가능하다.
변수나 메서드 외에도 클래스의 접근 역시 해당 접근 제어자를 따른다.
객체지향의 특징 중 하나이다. 객체의 속성(변수)과 기능(메서드)을 하나로 묶고, 실제 구현 내용 일부를 외부에 감추어 은닉한다. 데이터와, 데이터를 처리하는 행위를 묶고, 외부에는 그 행위를 보여주지 않는 것이다.
캡슐화를 통해 우리가 얻을 수 있는 이점중 가장 큰것은 코드의 중복을 피할 수 있다는 점과, 데이터를 처리하는 동작 방식을 외부에서 알 필요가 없다는 점이다.
중복을 피할 수 있다는 것은 상당히 중요하다. 만일 쇼핑몰의 아이템이 10만개라고 했을 때 10만개 모두 10% 할인 할 경우, 금액을 모두 수정하는 것 보다 10% 할인 기능을 구현하여 하나를 10만개에 적용하는게 더 간단할 것이다. 그리고 10%를 20%로 늘린다고 할 경우 숫자 하나만 고치면 될 것이다.
People클래스에 age필드와 setAge( ) 메서드가 다음과 같이 선언되어 있다.
public class People{
//필드
int age;
//메서드
void setAge(int age){
this.age = a;
}
}
위와같이 age에 외부의 값을 입력받을 수 있는 setAge( )라는 Setter함수를 선언하였다.
this를 사용하면, 메서드의 인수나 변수에 필드와 같은 이름을 붙여도 그것들을 구분하여 사용할 수 있다. this는 주로 생성자와 메서드의 매개변수 이름이 필드와 동일한 경우, 인스턴스 멤버인 필드임을 명시하고자 할때 사용한다.
여기서 this.age는 필드인 age를 의미합니다. 즉, this의 뒤의 변수명은 인수가 아닌 멤버변수(필드)를 의미하는 것이 된다.