[이것이 자바다] 책을 기반으로 Part 01: "자바 언어 기초"를 정리한 내용입니다.
이 글에서는 Chapter02 ~ Chapter04인 조건문과 반복문까지로 구성되어져 있습니다.
01. Java란?
Write once, run anywhere (한 번 쓰면, 어디서든 실행된다.)
- JVM(자바 가상 머신)을 사용하기 때문에 플랫폼에 독립적인 언어입니다.
- 즉, 리눅스던, 윈도우던, 맥이던 JVM만 설치되어 있다면 어디서든 돌아갑니다.
- JVM은 Garbage Collector를 통해 사용하지 않는 객체를 식별하고 메모리를 해제하여 메모리 관리 부담을 줄여줍니다. (이때 완전히 메모리 누수가 사라지는건 아닙니다)
- 대표적인 객체 지향 언어로 알려져 있습니다.
객체 지향 프로그래밍(OOP)
Java는 대표적인 객체지향 프로그래밍 언어로, 다음과 같은 특징을 가지고 있습니다.
- 캡슐화 : 데이터를 숨기고 접근 제어자를 사용하여 외부에서의 접근을 제한함으로써 객체의 데이터 무결성을 보장합니다.
- 상속 : 기존 클래스를 재사용하여 새로운 클래스를 생성할 수 있습니다. 이를 통해 코드의 재사용성과 유지보수성을 높일 수 있습니다.
- 다형성 : 같은 동작을 다른 방식으로 구현할 수 있는 특징입니다. 이는 메서드 오버로딩과 오버라이딩으로 구현됩니다.
- 추상화 : 중요한 정보만 노출하고 불필요한 세부 사항은 숨깁니다. 이를 통해 복잡한 시스템을 간단하게 표현할 수 있습니다.
캡슐화
은행에서 ATM 기기를 사용할 때, 내 계좌의 잔액은 아무나 볼 수 없고, 반드시 내가 비밀번호를 입력해야만 확인할 수 있습니다.
여기서 잔액은 비밀번호로 보호되어 외부에서 직접 접근할 수 없는 은닉된 데이터 입니다. 예를들어, ATM 화면에서 잔액을 보여주는 버튼이 있다고 해서 아무나 누를 수 있는게 아닌, 비밀번호를 입력해야 그 버튼이 작동합니다.
이는 비밀번호를 통한 인증 과정을 거쳐야 데이터(잔액을)를 확인할 수 있도록 보장하는 '캡슐화'의 예시입니다.
public class BankAccount {
private int balance = 1000; // 은닉된 데이터
private String password = "1234"; // 비밀번호
// 비밀번호 확인 후 잔액을 반환하는 메서드
public int checkBalance(String inputPassword) {
if (inputPassword.equals(password)) {
return balance;
} else {
System.out.println("비밀번호가 틀렸습니다.");
return -1;
}
}
}
상속
자동차를 운송수단의 기본적인 형태라고 생각해봅시다.
자동차는 엔진, 바퀴, 핸들 같은 기본적인 구성 요소가 있어 어디든 이동할 수 있는 공통적인 기능을 가지고 있습니다.
이제 이 자동차로 택시와 버스라는 두가지 운송수단을 만들어야 합니다.
- 택시는 승객을 태우고, 그 승객이 원하는 목적지로 이동하는 기능이 필요합니다.
- 버스는 여러 승객을 태우고 정해진 노선을 따라 운행하는 기능이 필요합니다.
이처럼, 택시와 버스는 기본적인 자동차의 특성을 그대로 물려받으면서,
각각의 목적에 맞게 추가적인 기능을 더하거나 동작을 다르게 만듭니다.
쉽게말해, 택시와 버스는 '자동차'라는 큰 틀 안에 속하면서도 각각의 고유한 역할을 가지는 것입니다.
상속이란 이렇게 기본적인 구조(자동차)를 다른 구조(택시, 버스)가 그대로 받아 새로운 기능을 더하거나 수정하여 사용하는 것을 말합니다.
// 부모 클래스
public class Car {
public void drive() {
System.out.println("자동차가 달립니다.");
}
}
// 자식 클래스
public class Taxi extends Car {
@Override
public void drive() {
System.out.println("택시가 승객을 태우고 달립니다.");
}
}
public class Bus extends Car {
@Override
public void drive() {
System.out.println("버스가 정류장을 지나며 승객을 태우고 달립니다.");
}
}
다형성
여러 종류의 펜(볼펜, 만년필, 형광펜)을 사용한다고 생각해봅시다. 이들은 모두 "글씨를 쓴다"라는 공통적인 동작을 합니다.
하지만 볼펜은 일반적인 잉크로 부드럽게 쓰고, 만년필은 고급스러운 필체를 제공하며, 형광펜은 문서의 중요한 부분을 강조하기 위해 색상을 추가합니다.
즉 "글씨를 쓴다"는 공통 동작은 같지만. 사용하는 펜의 종류에 따라 구현 방식이 달라집니다.
이런 방식이 바로 다형성입니다.
이 덕분에 펜이 어떤 종류인지 일일이 확인하지 않아도, "글씨를 쓴다"는 공통적인 명령만으로 다양한 펜의 기능을 사용할 수 있습니다.
// 부모 클래스
public class Pen {
public void write() {
System.out.println("글씨를 씁니다.");
}
}
// 자식 클래스
public class BallPen extends Pen {
@Override
public void write() {
System.out.println("볼펜으로 글씨를 씁니다.");
}
}
public class FountainPen extends Pen {
@Override
public void write() {
System.out.println("만년필로 글씨를 씁니다.");
}
}
// 실행 예시
Pen pen1 = new BallPen();
Pen pen2 = new FountainPen();
pen1.write(); // "볼펜으로 글씨를 씁니다."
pen2.write(); // "만년필로 글씨를 씁니다."
추상화
우리는 매일 전자제품을 사용합니다. 예를 들어, 세탁기를 사용할 때 전원을 켜고 "세탁"버튼만 누르면 세탁이 시작됩니다.
하지만 세탁기의 내부에서는 복잡한 기계 작동과 프로그램 실행이 이루어지게 됩니다.
이때 우리는 버튼 을 누르기만 하면 되지, 내부에서 어떤 센서가 동작하고, 어떤 밸브가 열리며, 물과 세제가 어떻게 섞이는지 알 필요가 없습니다.
이처럼 사용자가 필요한 정보(버튼과 기능)만 노출하고, 내부의 복잡한 세부 사항은 숨기는 것이 바로 추상화입니다 .
// 추상 클래스
abstract class Appliance {
abstract void turnOn();
abstract void turnOff();
}
// 구체적인 클래스
class WashingMachine extends Appliance {
@Override
void turnOn() {
System.out.println("세탁기가 켜졌습니다.");
}
@Override
void turnOff() {
System.out.println("세탁기가 꺼졌습니다.");
}
}
class Refrigerator extends Appliance {
@Override
void turnOn() {
System.out.println("냉장고가 켜졌습니다.");
}
@Override
void turnOff() {
System.out.println("냉장고가 꺼졌습니다.");
}
}
왜 객체지향 프로그래밍이 중요할까?
- 현실 세계의 문제를 쉽게 모델링
- 객체(Object)를 사용해 현실 세계의 사물이나 개념을 프로그램으로 표현할 수 있습니다.
- 예를 들어, 자동차(Car), 학생(Student), 은행 계좌(Account)와 같은 개념을 코드로 직관적이고 명확하게 나타낼 수 있습니다. 이러한 접근 방식은 복잡한 문제를 단순화하고, 프로그래밍 과정에서 이해하기 쉽게 만들어 줍니다.
- 코드 재사용성과 유지보수성 향상
- 상속(Inheritance): 기존 코드를 재사용하여 새로운 코드를 작성할 수 있어 개발 시간을 줄이고 중복을 방지할 수 있습니다.
- 캡슐화(Encapsulation): 데이터와 메서드를 하나로 묶고 외부에서 접근을 제한하여, 의도하지 않은 오류를 줄이고 데이터의 무결성을 보장합니다.
- 변경 사항이 생기더라도 코드가 구조화되어 있어 유지보수가 간편합니다.
- 코드의 확장성
- 다형성(Polymorphism): 같은 메서드 이름으로 다양한 동작을 구현할 수 있어, 유연하고 확장 가능한 코드를 작성할 수 있습니다.
- 새로운 기능을 추가해야 할 때 기존 코드를 최소한으로 수정하며 확장성을 확보할 수 있습니다.
- 협업에 용이
- 객체는 독립적이고 모듈화되어 있어, 여러 개발자가 동시에 작업하기에 적합합니다.
예를 들어, UI 개발자와 데이터베이스 개발자가 각각 객체를 중심으로 역할을 분리하여 작업할 수 있습니다. 이로 인해 협업 과정에서의 충돌이 줄어들고 생산성이 높아집니다.
- 객체는 독립적이고 모듈화되어 있어, 여러 개발자가 동시에 작업하기에 적합합니다.
JVM
JVM(Java Virtual Machine)은 자바 프로그램을 실행하기 위한 가상 기계입니다.
운영체제(OS)에 종속되지 않도록 설계되어, CPU가 Java를 인식하고 실행할 수 있게 하는 가상의 컴퓨터 역할을 합니다.
JVM은 아래와 같이 동작됩니다.
- .java 파일은 원시 코드로 작성되며, CPU가 직접 이해할 수 없습니다.
따라서, 기계어로 변환하는 과정이 필요합니다. - 자바 컴파일러는 .java 파일을 Java bytecode(.class 파일)로 변환합니다.
이때, Java bytecode는 JVM이 이해할 수 있는 형식입니다. - JVM은 Java bytecode를 실행 환경에 맞게 기계어로 변환하여 프로그램을 실행합니다.
이러한 과정을 통해 자바는 운영체제와 관계없이 동일한 프로그램을 실행할 수 있는 플랫폼 독립성을 제공합니다.
주석 다는법
자바에서는 주석을 통해 코드의 내용을 설명할 수 있습니다. 주석은 다른 개발자와의 협업 및 코드 유지보수를 위해 유용하게 사용됩니다.
/**
* @author jiny
* 이 클래스는 "Hello World"를 출력하는 간단한 Java 프로그램입니다.
* - 프로그램 실행 진입점: main 메서드
* - 메서드 설명: 콘솔에 메시지를 출력합니다.
*
* 작성일: 2025년 1월 6일
* 주석 작성 권장 사항:
* 1. @author 태그를 사용하여 작성자 정보를 명시합니다.
* 2. 파라미터와 메서드 사용법을 기술하여 코드 이해를 돕습니다.
* 3. 함수 위에 커서를 올리면 주석 내용이 표시되므로 유지보수에 유리합니다.
*/
public class Hello {
/**
* 프로그램 실행의 진입점이며, 콘솔에 "Hello World"를 출력합니다.
*
* @param args 실행 시 전달되는 인수 (현재 사용되지 않음)
*/
public static void main(String[] args) {
// 콘솔에 "Hello World!" 출력
System.out.println("Hello World!");
}
}
02. 변수와 타입
변수 작성시
- 클래스명: 클래스 이름은 대문자로 시작하는 것이 관례입니다.
- Week.java
- MemberGrade.java
- ProductKind.java
- 변수명: 변수 이름은 소문자로 시작하며, 카멜 케이스(Camel Case)를 사용하는 것이 권장됩니다.
- score
- mathScore
- sportsCar
Java 자료형
Java는 정적 타입 검사를 통해 컴파일 시점에 오류를 발견할 수 있습니다.
이는 코드의 안정성과 유지보수성을 높이는 데 큰 도움을 줍니다.
1. 기본 자료형 (Primitive Types)
기본 자료형은 메모리에 값 자체를 저장하며, Java에서 가장 기본적으로 사용하는 데이터 타입입니다.
타입 | 크기(byte) | 기본값 | 설명 |
byte | 1 | 0 | 정수 |
short | 2 | 0 | 정수 |
int | 4 | 0 | 정수 |
long | 8 | 0L | 정수 |
float | 4 | 0.0f | 실수 |
double | 8 | 0.0d | 실수 |
char | 2 | ‘\u0000’ | 유니코드 문자 |
boolean | 1bit | false | 참/거짓 |
2. 참조 자료형 (Reference Types)
참조 자료형은 데이터가 위치한 메모리 주소를 저장합니다. 이는 객체나 데이터 구조를 관리하는 데 사용되며,
C++의 포인터와 유사한 개념이지만 더 안전하게 동작합니다.
타입 | 설명 |
String | 문자열 데이터를 저장하기 위한 클래스 |
Array | 동일한 타입의 데이터 모음을 저장하는 배열 |
Class | 객체를 생성하기 위한 틀 |
Interface | 클래스의 동작을 정의하는 메커니증 |
Enum | 상수 값을 모아놓은 집합 |
3. 기본 자료형과 참조 자료형의 차이
구분 | 기본 자료형 | 참조 자료형 |
저장 방식 | 값 자체를 저장 | 데이터의 메모리 주소를 저장 |
사용 목적 | 간단한 데이터 저장 (정수, 실수, 문자, 논리값 등) | 객체 및 데이터 구조 관리 |
초기화 방식 | 각 타입의 기본값으로 초기화 | null로 초기화 |
String 예제
public class StringExample {
public static void main(String[] args) {
// 문자열 생성
String greeting = "Hello, World!";
System.out.println(greeting);
// 문자열 변경
String newGreeting = greeting.replace("World", "Java");
System.out.println(newGreeting); // "Hello, Java!"
System.out.println(greeting); // "Hello, World!" (원본 불변)
}
}
Array 예제
public class ArrayExample {
public static void main(String[] args) {
// 배열 생성
int[] numbers = {1, 2, 3, 4, 5};
// 배열 접근 및 수정
numbers[2] = 10;
// 배열 출력
for (int number : numbers) {
System.out.println(number);
}
}
}
Class 예제
public class Car {
String brand;
int speed;
// 생성자 (Car 객체 생성시 초기값 설정)
public Car(String brand, int speed) {
this.brand = brand;
this.speed = speed;
}
// 메서드 (객체의 동작을 정의)
public void drive() {
System.out.println(brand + " is driving at " + speed + " km/h");
}
}
public class ClassExample {
public static void main(String[] args) {
//Car라는 객체를 생성
Car myCar = new Car("Tesla", 120);
//drive 메소드 호출하여 자동차가 움직이는 동작 출력
myCar.drive(); // Tesla is driving at 120 km/h
}
}
Interface 예제
//동물들의 공통 동작 정의
interface Animal {
//추상메소드 (내용은 없고 선언만 해둠)
void makeSound();
}
//Dog 클래스: Animal interface를 통해 구현
class Dog implements Animal {
public void makeSound() {
System.out.println("왈왈!!!");
}
}
//Cat 클래스: Animal interface를 통해 구현
class Cat implements Animal {
public void makeSound() {
System.out.println("야옹~");
}
}
public class InterfaceExample {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
dog.makeSound(); // 왈왈!!!
cat.makeSound(); // 야옹~
}
}
Enum 예제
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumExample {
public static void main(String[] args) {
Day today = Day.FRIDAY;
switch (today) {
case MONDAY:
System.out.println("집에가고싶다");
break;
case FRIDAY:
System.out.println("불금이다");
break;
case SUNDAY:
System.out.println("내일 학교간다..");
break;
default:
System.out.println("빨리 토요일 됐으면..");
}
}
}
타입 변환 (Type Casting)
- 자동 타입 변환 (Implicit): 크기가 작은 타입 → 큰 타입
int num = 10;
double result = num; // int → double
System.out.println(result); // 출력: 10.0
- 강제 타입 변환 (Explicit): 크기가 큰 타입 → 작은 타입
double pi = 3.14;
int integerPi = (int) pi; // double → int
System.out.println(integerPi); // 출력: 3
Wrapper 클래스
Wrapper 클래스는 기본 자료형(Primitive Type)을 객체(Object)로 다룰 수 있게 해주는 클래스입니다.
이는 Java의 컬렉션 프레임워크(예: ArrayList, HashMap)처럼 객체만 지원하는 기능에서 유용하게 사용됩니다.
Wrapper 클래스란?
- 기본 자료형(primitive type)을 객체(object) 형태로 변환해주는 클래스
- 주요 목적:
- 객체로서의 사용
기본 자료형은 Java의 컬렉션(ArrayList, HashMap 등)에 직접 저장할 수 없습니다.
Wrapper 클래스를 사용하면 기본 자료형을 객체로 변환하여 저장할 수 있습니다. - 유틸리티 기능 제공: Wrapper 클래스는 숫자 변환, 문자열 파싱 등 다양한 유틸리티 메서드를 제공합니다.
- 자동 박싱(Auto-boxing) 및 언박싱(Unboxing): Java는 기본 자료형과 Wrapper 클래스 간의 변환을 자동으로 처리합니다.
- 객체로서의 사용
박싱(boxing) : 기본 자료형 → 객체(Wrapper 클래스)
언박싱(Unboxing) : 객체(Wrapper 클래스) → 기본 자료형
Integer num = 10; // 박싱
int value = num; // 언박싱
주요 Wrapper 클래스와 메서드
기본 자료형 | Wrapper 클래스 | 주요 메서드 |
int | Integer | [parseInt] [toString] [valueof] [compareTo] |
double | Double | [parseDouble] [isNaN] [toString] [compare] |
boolean | Boolean | [parseBoolean] [toString] [valueOf] |
char | Character | [isDigit] [isLetter] [isWhitespace] [toUpperCase] |
float | Float | [parseFloat] [isNaN] [compare] |
long | Long | [parseLong] [toString] [valueOf] |
short | Short | [parseShort] [toString] [valueOf] |
byte | Byte | [parseByte] [toString] [valueOf] |
Wrapper 클래스의 동작 원리
Wrapper 클래스는 기본 자료형에 "옷을 입혀주는 옷장"과 같습니다.
- 기본 자료형(int) 자체로는 단순히 값을 저장하지만, Wrapper 클래스(Integer)는 데이터를 저장할 뿐 아니라 다양한 메서드와 기능을 추가로 제공합니다.
int num = 10; // 기본 자료형
Integer wrapperNum = num; // 자동 박싱
// Wrapper 클래스 메서드 사용
System.out.println(wrapperNum.toString()); // "10"
System.out.println(Integer.compare(10, 20)); // -1
Java vs C++ vs Python
기능 | Java | C++ | Python |
숫자 변환 | Integer.parseInt("123") | stoi("123") | int("123") |
컬렉션에서 숫자 사용 | ArrayList<Integer> | std::vector<int> | [1, 2, 3] |
문자 판별 | Character.isDigit('5') | isdigit('5') | '5'.isdigit() |
숫자/문자 구분 | 기본 타입 + 객체 구분 | 기본 타입(객체 없음) | 모든 것이 객체 |
사용예시
기본 자료형 객체로 변환
public class WrapperExample {
public static void main(String[] args) {
// 박싱: 기본 자료형 -> 객체
int num = 10;
Integer wrappedNum = Integer.valueOf(num); // 명시적 박싱
Integer autoBoxed = num; // 자동 박싱
// 언박싱: 객체 -> 기본 자료형
int unboxed = wrappedNum.intValue(); // 명시적 언박싱
int autoUnboxed = wrappedNum; // 자동 언박싱
System.out.println("박싱된 값: " + wrappedNum);
System.out.println("언박싱된 값: " + unboxed);
}
}
문자열 → 숫자 변환 (문자열 값 숫자로 변환하거나, 기본 자료형 값을 문자열로 변환 가능)
public class ParseExample {
public static void main(String[] args) {
// 문자열 -> 숫자 변환
String strNum = "123";
int parsedNum = Integer.parseInt(strNum);
// 숫자 -> 문자열 변환
int num = 456;
String str = Integer.toString(num);
System.out.println("문자열에서 변환된 숫자: " + parsedNum);
System.out.println("숫자에서 변환된 문자열: " + str);
}
}
Boolean과 Character 활용
public class WrapperMethods {
public static void main(String[] args) {
// Boolean 클래스
String isStudent = "true";
boolean studentStatus = Boolean.parseBoolean(isStudent);
System.out.println("학생 여부: " + studentStatus);
// Character 클래스
char letter = 'A';
boolean isDigit = Character.isDigit(letter);
boolean isLetter = Character.isLetter(letter);
System.out.println("문자인가? " + isLetter);
System.out.println("숫자인가? " + isDigit);
}
}
Wrapper클래스와 컬렉션 활용
Java의 컬렉션 프레임워크는 기본 자료형을 직접 지원하지 않으므로, Wrapper 클래스를 사용해 데이터를 저장합니다.
컬렉션 프레임워크란?
- 데이터를 효율적으로 저장하고 관리하기 위한 자료구조(리스트, 맵 등)와 알고리즘(정렬, 탐색 등)을 제공하는 라이브러리입니다.
- Python의 리스트(list), 딕셔너리(dict)와 유사한 데이터 저장 도구입니다.
import java.util.ArrayList;
public class WrapperWithCollections {
public static void main(String[] args) {
// ArrayList는 기본 자료형을 지원하지 않음 -> Wrapper 클래스를 사용
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10); // 자동 박싱
numbers.add(20);
numbers.add(30);
int firstNumber = numbers.get(0); // 자동 언박싱
System.out.println("첫 번째 숫자: " + firstNumber);
System.out.println("전체 숫자: " + numbers);
}
}
Wrapper클래스의 단점
1. NullPointerException 발생 가능
Wrapper 클래스는 객체이므로 null 값을 가질 수 있습니다.
기본 자료형으로 언박싱하려고 할 때 NullPointerException이 발생할 수 있습니다.
Integer num = null;
// int unboxedNum = num; // NullPointerException 발생!
2. 성능 저하
- Wrapper 클래스는 기본 자료형보다 메모리를 더 많이 사용하며,
박싱/언박싱 과정에서 추가 연산이 발생하여 성능이 떨어질 수 있습니다. - 성능이 중요한 작업에서는 기본 자료형을 사용하는 것이 권장됩니다.
배열 (Array)
배열은 동일한 타입의 데이터를 저장하며, 고정 크기를 가집니다.
public class ArrayExample {
public static void main(String[] args) {
// 배열 선언 및 초기화
int[] numbers = {1, 2, 3, 4, 5};
// 배열 요소 접근
System.out.println("첫 번째 값: " + numbers[0]); // 1
System.out.println("세 번째 값: " + numbers[2]); // 3
// 배열 수정
numbers[2] = 10; // 세 번째 값 수정
System.out.println("수정된 세 번째 값: " + numbers[2]); // 10
// 배열 순회
for (int num : numbers) {
System.out.print(num + " "); // 1 2 10 4 5
}
}
}
Java의 Null값
null이란?
- Java에서 참조 자료형은 기본적으로 null 값을 가질 수 있습니다.
null은 데이터가 없는 상태를 표현하는 일종의 "빈 상자"라고 생각할 수 있습니다.
String str = null; // str은 아무 데이터도 참조하지 않는 상태
null 사용 시 주의점
- 참조 변수가 null인 상태에서 메서드 호출이나 필드 접근을 시도하면 NullPointerException이 발생합니다.
String str = null;
System.out.println(str.length()); // NullPointerException 발생!
따라서, 사용하기 전에 반드시 null 여부를 확인하거나 기본 값을 초기화해야 합니다.
Optional 클래스 활용
- Java는 Optional 클래스를 통해 null 값을 다루는 방식을 간소화하고, null 처리를 명시적으로 표현할 수 있습니다.
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
String str = null;
// Null 처리 없이 Optional 사용
Optional<String> optionalStr = Optional.ofNullable(str);
// 값이 없을 경우 대체 값 사용
System.out.println(optionalStr.orElse("기본값")); // 출력: 기본값
}
}
- null 여부를 확인하지 않아도 안전하게 값에 접근할 수 있습니다.
- 코드 가독성을 높이고, NullPointerException을 방지합니다.
Kotlin
- Kotlin에서는 Nullable과 Non-Nullable 타입을 명확히 구분합니다.
- ?를 사용해 nullable 타입을 정의하고, 안전 호출(?.) 또는 엘비스 연산자(?:)를 사용해 null 처리를 간결하게 표현합니다.
var nullableStr: String? = null
println(nullableStr?.length ?: "기본값") // 출력: 기본값
Java
- @Nullable 또는 @NonNull 어노테이션: 코드에서 명시적으로 참조 변수의 null 가능성을 나타냅니다.
- Objects.requireNonNull(): null 확인 및 처리 로직을 간소화합니다.
import java.util.Objects;
String str = null;
Objects.requireNonNull(str, "값이 null일 수 없습니다.");
////////////////////
String nullableStr = null;
System.out.println(nullableStr != null ? nullableStr.length() : "기본값"); // 출력: 기본값
출력 방법
public class Hello {
public static void main(String[] args){
int d = 10;
String s = "jiny";
//행 안바뀜
System.out.print("행을 바꾸지 않습니다"); //괄호안의 내용 출력 후 행 안바꿈
System.out.printf("%d %s", d,s); //형식 문자열에 맞추어 뒤의 값 출력
System.out.printf("%2$d %1$s", s,d); //순번 바꾸기도 가능
//행바꿈
System.out.println("반갑습니다."); //괄호안의 내용 출력 후 행 바꿈
}
}
키보드값 입력 방법
public class ScannerExample {
public static void main(String[] args) throws Exception {
//Scanner 타입 변수 선언 후 생성된 Scanner 변수에 대입
Scanner scanner = new Scanner(System.in);
System.out.print("x 값 입력: ");
//읽은 문자열을 String 변수에 저장 -- 엔터 누르면 입력된 문자열 읽음
String strX = scanner.nextLine();
int x = Integer.parseInt(strX);
System.out.print("y 값 입력: ");
//읽은 문자열을 String 변수에 저장
String strY = scanner.nextLine();
int y = Integer.parseInt(strY);
int result = x + y;
System.out.println("x + y: " +result);
System.out.println();
while(true){
System.out.print("입력 문자열: ");
//읽은 문자열을 String 변수에 저장
String data = scanner.nextLine();
if(data.equals("q")) {
break;
}
System.out.println("출력 문자열: " + data);
System.out.println();
}
System.out.println("종료");
}
}
Scanner 사용시 예외 상황
import java.util.Scanner;
public class ExceptionHandlingExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("숫자를 입력하세요: ");
int number = scanner.nextInt();
System.out.println("입력한 숫자: " + number);
} catch (Exception e) {
System.out.println("잘못된 입력입니다. 숫자를 입력해주세요!");
} finally {
scanner.close();
}
}
}
03. 연산자
산술 연산자
연산자 | 설명 | 비유 | 예제 |
+ | 덧셈 | 두 숫자를 더하는 계산기 | 3 + 2 → 5 |
- | 뺄셈 | 두 숫자의 차이를 계산 | 5 - 3 → 2 |
* | 곱셈 | 두 숫자를 곱하는 계산기 | 3 * 2 → 6 |
/ | 나눗셈 (정수는 소수 버림) | 피자를 나누듯 숫자를 나눔 | 5 / 2 → 2 (정수), 5.0 / 2 → 2.5 |
% | 나머지 | 피자를 나눌 때 남은 조각 계산 | 5 % 2 → 1 |
관계 연산자
연산자 | 설명 | 비유 | 예제 |
> | 크다 | "x가 y보다 크면 참" | 5 > 3 → true |
< | 작다 | "x가 y보다 작으면 참" | 2 < 4 → true |
>= | 크거나 같다 | "x가 y보다 크거나 같으면 참" | 5 >= 5 → true |
<= | 작거나 같다 | "x가 y보다 작거나 같으면 참" | 3 <= 5 → true |
== | 같다 | "x와 y가 같으면 참" | 5 == 5 → true |
!= | 같지 않다 | "x와 y가 다르면 참" | 5 != 3 → true |
비트 연산자
연산자 | 비유 | 설명 |
& | 비트 AND | 각 비트를 비교해 둘 다 1이면 1 |
` | ` | 비트 OR |
^ | 비트 XOR | 각 비트를 비교해 다르면 1 |
~ | 비트 NOT | 각 비트를 반전 |
<< | 왼쪽 시프트 | 모든 비트를 왼쪽으로 이동 |
>> | 오른쪽 시프트 (부호 유지) | 모든 비트를 오른쪽으로 이동 (부호 유지) |
>>> | 오른쪽 시프트 (부호 제거) | 모든 비트를 오른쪽으로 이동 (부호 제거) |
04. 조건문과 반복문
조건문
if 문
public class IfExample {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.print("숫자 입력: ");
String input = scanner.nextLine();
int num = Integer.parseInt(input);
if(num > 90){
System.out.println("점수가 90보다 큽니다");
System.out.println("등급은 A 입니다");
}
else if(num < 90 && num > 70){
System.out.println("점수가 70보다 크고 90보다 작습니다.");
System.out.println("등급은 B 입니다");
}
else {
System.out.println("당신은 F 입니다");
}
}
}
switch 문
Ex 1)
public class SwitchExample {
public static void main(String[] args){
int num = (int)(Math.random()*6) + 1;
switch (num){
case 1:
System.out.println("1번이 나왔네요");
break;
case 2:
System.out.println("2번이 나왔네요");
break;
case 3:
System.out.println("3번이 나왔네요");
break;
case 4:
System.out.println("4번이 나왔네요");
break;
case 5:
System.out.println("5번이 나왔네요");
break;
default:
System.out.println("6번이 나왔네요");
}
}
}
Ex 2)
public class SwitchExample2 {
public static void main(String[] args){
char grade1 = 'B';
char grade2 = 'A';
switch (grade1){
case 'A':
case 'a':
System.out.println("우수 회원이에요!");
break;
case 'B':
case 'b':
System.out.println("일반 회원이에요!");
break;
default:
System.out.println("손님이에요!");
}
//Java 버전 12 이후부터 Expression 식으로 표현 가능
switch (grade2){
case 'A', 'a' -> System.out.println("우수 회원이에요!");
case 'B', 'b' -> System.out.println("일반 회원이에요!");
default -> System.out.println("손님이에요!");
}
}
}
반복문
for 문
public class SumFromTo100Example {
public static void main(String[] args){
int sum = 0;
int i;
for(i=0; i<=100; i++){
sum += i;
}
System.out.println("1~" + (i-1) + " 합 " + sum);
}
}
이중 for 문
public class MultiplicationTableExample {
public static void main(String[] args){
for(int m=2; m<=9; m++){
System.out.println("\\n*** " + m + "단 ***");
for(int n=1; n<=9; n++){
System.out.println(m + " x " + n + " = " + (m*n));
}
}
}
}
while 문
public class SumFrom1To100Example {
public static void main(String[] args){
int sum = 0;
int i = 1;
while(i<=100){
sum+=i;
i++;
}
System.out.println("1~" + (i-1) + "합 :" + sum);
}
}
do-while 문
public class DoWhileExample {
public static void main(String[] args){
System.out.println("메세지를 입력하세요.");
System.out.println("프로그램을 종료하려면 q를 입력하세요.");
Scanner scanner = new Scanner(System.in);
String inputString;
do{
System.out.print("> ");
inputString = scanner.nextLine();
System.out.println(inputString);
} while(!inputString.equals("q"));
System.out.println();
System.out.println("프로그램 종료");
}
}
break
public class BreakExample {
public static void main(String[] args){
while(true) {
int num = (int)(Math.random()*6) + 1;
System.out.println(num);
if(num == 6) break;
}
System.out.println("프로그램 종료");
}
}
중첩된 반복문에서 바깥쪽 반복문까지 종료하려면 레이블(Label)을 사용해야 합니다.
public class BreakOutterExample {
public static void main(String[] args) throws Exception {
// 바깥쪽 반복문에 Label 설정
Outter: for (char upper = 'A'; upper <= 'Z'; upper++) {
for (char lower = 'a'; lower <= 'z'; lower++) {
System.out.println(upper + "-" + lower);
if (lower == 'g') { // 'g'까지 출력한 후 반복문 종료
break Outter; // Label이 붙은 바깥쪽 반복문까지 종료
}
}
}
}
}
continue
public class ContinueExample {
public static void main(String[] args) throws Exception {
for (int i = 1; i <= 10; i++) {
if (i % 2 != 0) {
continue;
}
System.out.println(i + " ");
}
}
}
질문
하나의 Java 파일에 여러 클래스를 작성할 수 있는가? O
- 하나의 Java 파일에는 여러 클래스를 작성할 수 있습니다.
다만, public 키워드를 가진 클래스는 반드시 하나만 작성해야 하며, 그 클래스의 이름은 파일 이름과 동일해야 합니다.
하지만 일반적으로 하나의 파일에 하나의 클래스를 작성하는게 관례입니다.
Ex)
// File: MainClass.java
public class MainClass {
public static void main(String[] args) {
System.out.println("MainClass 실행");
}
}
class HelperClass {
void assist() {
System.out.println("HelperClass 실행");
}
}
class AnotherClass {
void doSomething() {
System.out.println("AnotherClass 실행");
}
}
위 파일 컴파일시 3개의 class파일이 생성됩니다.
- MainClass.java
- HelperClass.java
- AnotherClass.java
Java의 컴파일 및 실행 방식
- Java는 컴파일 언어와 인터프리터 언어의 특성을 혼합한 구조입니다.
- 컴파일 단계:
- Java 소스 코드(.java)는 Java Compiler에 의해 바이트코드(.class)로 변환됩니다.
- 실행 단계:
- 바이트코드는 JVM(Java Virtual Machine)에 의해 해석(인터프리트)되거나,
JIT(Just-In-Time) 컴파일러에 의해 네이티브 코드로 변환되어 실행됩니다.
- 바이트코드는 JVM(Java Virtual Machine)에 의해 해석(인터프리트)되거나,
- 컴파일 단계:
배열과 동적 구조의 처리:
- Java에서는 배열의 크기를 동적으로 할당한 뒤 반복문으로 처리할 수 있습니다.
- 이는 Java의 JVM과 JIT 컴파일러가 동적 처리를 지원하기 때문입니다.
Java에서 사용 가능한 for문 종류
기본 for문 (Traditional for Loop)
for (초기화; 조건식; 증감식) {
// 반복 실행 코드
}
public class BasicForExample {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
System.out.println("i = " + i);
}
}
}
향상된 for문 (Enhanced for Loop, or for-each Loop)
for (데이터타입 변수 : 배열이나 컬렉션) {
// 반복 실행 코드
}
public class EnhancedForExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println("num = " + num);
}
}
}
중첩 for문 (Nested for Loop)
for (초기화1; 조건식1; 증감식1) {
for (초기화2; 조건식2; 증감식2) {
// 반복 실행 코드
}
}
public class NestedForExample {
public static void main(String[] args) {
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
System.out.println("i = " + i + ", j = " + j);
}
}
}
}
라벨(Label)을 사용한 for문
라벨이름: for (조건식) {
for (조건식) {
// 특정 조건에서 바깥쪽 반복문까지 탈출
break 라벨이름;
}
}
public class LabelForExample {
public static void main(String[] args) {
outer: for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j) break outer; // 바깥쪽 반복문 탈출
System.out.println("i = " + i + ", j = " + j);
}
}
}
}
for문과 스트림 API (Java 8 이상)
- Java 8부터 추가된 Stream API는 내부 반복을 활용하여 데이터를 처리.
- 전통적인 for문 대신 람다 표현식을 사용하는 방식.
import java.util.Arrays;
public class StreamForExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
Arrays.stream(numbers).forEach(num -> System.out.println("num = " + num));
}
}
무한 for문
for (;;) {
// 조건 없이 계속 실행
}
public class InfiniteForExample {
public static void main(String[] args) {
for (;;) {
System.out.println("무한 루프 실행 중...");
break; // 조건을 넣어서 탈출
}
}
}