본문 바로가기

프로그래머/Java Managed Programming

[포프 tv 복습] Java 기본 문법, Java와 C,C#의 차이

Ch02. Java 언어의 기본 문법

"Hello POCU" 출력하기

package academy.pocu;

public class HelloPocu {
    public static void main(String[] args){
        System.out.println("Hello POCU");
    }
}
  • Java에서는 언제나 클래스가 필요
  • 한 .java 파일에는 최고 레벨 public 클래스가 하나만 있어야 함
  • 클래스 안에 다른 클래스를 넣을 수 있음
    • 내포(nested) 클래스
    • 내포 클래스는 public 이어도 상관 없음
  • main 함수는 반드시 이 시그내처대로 만들어야 함
  • 매개변수 : String[] args
    • 문자열 배열
    • 커맨드 라인으로부터 받은 인자

Java에도 printf()가 있다

String name = "Mumu";
int score = 64;
System.out.printf("%s's score: %d\n", name, score);
  • print() 대신 format() 메서드를 사용해도 동일하게 동작

올바른 새 줄 문자 추가 방법

String name = "Mumu";
int score = 64;
System.out.printf("%s's score: %d%s", name, score, System.lineSeparator());
  • 플랫폼에 알맞은 새 줄 문자를 반환하는 메서드

가변인자

public PrintStream printf(String format, Object... args);
  • ... 앞에 '자료형'을 반드시 넣어야 함
  • 해장 '자료형'의 데이터만 인자로 전달 가능
  • Object라는 자료형을 쓸 경우 모든 자료형을 넣을 수 있음

package

package academy.pocu;
  • 연관된 클래스들끼리 묶는 기법
  • C#의 namespace와 비슷

패키지 종류

  • 자바 기본(built-in) 패키지
    • 이름이 java로 시작하는 패키지들
    • ex) java.lang, java.util, ...
  • 프로그래머가 직접 만든 패키지(user-defined)

패키지의 목적

  • 이름 충돌 문제를 해결

패키지 이름 짓기

  • 패키지 이름의 중복을 최소화해야 함
  • 보통 회사의 도메인 명을 패키지 이름에 사용(단, 역순으로)

주의: 패키지 이름만 적으면 안 됨!

  • 패키지명과 똑같은 폴더 트리에 .java 파일을 넣어야 함

커맨드라인에서 컴파일하기

hellopocu>javac -d class\ src\academy\pocu\*.java

javac 명령어

javac -d <컴파일 결과물을 저장할 경로> <컴파일할 .java 파일>
  • .java 파일이 무사히 컴파일 되면 .class 파일이 나옴
    • 이름은 .java 파일과 동일
    • 이 .class 파일에는 바이트 코드가 들어있음
  • 옵션: -d
    • .class 파일을 저장할 경로
  • '컴파일 결과물을 저장할 경로' 안에 .java 파일의 패키지 구조와 동일한 폴더가 생김
java -classpath <class 파일 위치> <클래스 이름>
  • 바이트 코드를 실행하는 명령어(*.class)
  • '클래스 이름'
    • 이 .class 파일에는 반드시 main 함수가 들어 있어야 함
    • 클래스 이름 앞에 반드시 패키지 이름을 붙어야 함(academy.pocu.HelloPocu)
jar <option> <jar 파일 이름> <최상위 패키지 경로>
  • .jar 파일을 만드는 명령어
  • 옵션: -cf
    • c: create(생성)
    • f: 만들어질 .jar 파일의 이름을 지정. f 뒤에 파일명이 와야 함

Manifest 파일

  • 자바 애플리케이션의 정보를 담고 있는 메타데이터 파일
  • .jar 파일을 만들 때 이 파일을 같이 넣어줄 수 있음
  • .jar 파일의 시작점(메인 함수)에 대한 정보를 넣어야 함

다시 .jar 파일 만들기

hellopocu\class> jar -cfm ..\lib\hellopocu.jar ..\src\Manifest.txt academy
  • 추가된 옵션: -m
    • .jar 파일을 만들 때 manifest 파일도 함께 넣겠단 의미

import

import java.util.Random;
import java.util.*;

java.lang

  • 기본 패키지
  • 모든 .java 파일에 자동으로 임포트
  • System은 java.lang 안에 있는 클래스 중 하나

부호 있는 자료형만 존재

  • 예외: char는 부호 없는 자료형만
  • 음수 배열 색인과 음수 나이
    • 이런 걸 예방하도록 코드를 방어적으로 작성해야 한다는 의미
  • 다른 언어들은 대부분 부호 없는 자료형을 지원

부호 없는 자료형 부재의 또다른 문제

  • 보통 컴퓨터에서 색상을 저장할 때 8비트를 사용(0~255)
  • Java에서 포현하려면 byte로 불가능
  • 그래서 short를 씀

Integer 클래스

int num = 4294967295;
int num = Integer.parseUnsignedInt("4294967295");
System.out.printIn(num);
String numStr = Integer.toUnsignedString(num);
System.out.printIn(numStr);
  • int의 값을 부호 없는 수처럼 처리할 수 있는 도우미 함수 제공
    • parseUnsignedInt()
    • compareUnsigned()
    • divideUnsigned()
    • toUnsignedString()
    • 등등
  • 그러나 불편함

유니코드지만 16비트인 char

  • Java의 유일한 부호 없는 자료형
  • char로는 모든 유니코드를 표현할 수 없음
  • U+FFFF보다 큰 유니코드를 표현하려면 String을 사용해야 함

불리언형

boolean isStudent = false;

기본 자료형은 모두 '값형'임을 잊지 말자

  • 모든 값형은 복사 가능
  • 참조형은 그렇지 않음

String

  • 클래스는 언제나 참조형
  • String 클래스의 구성
    • 연속된 문자를 저장하는 메모리
    • 위 데이터를 처리할 수 있는 각종 메서드

String과 new 키워드

String name1 = "Nana";
String name2 = new String(name1);
String name3 = new String("Elsa");
  • 클래스형이므로 new를 이용해서 개체 생성 가능

Java의 String은 immutable

String name = "Nana";
name[0] = 'C';  // 컴파일 오류
  • C++에서는 가능
  • 일단 생성된 String 개체는 변경 불가
  • 바꾸고 싶다면 새로운 문자열을 만들어야 함

정수 리터럴

int num1 = 1234;
long num2 = 123467890L;
long num3 = 987654321l;
long num4 = 123456784235435;    // error

int num1 = 0xFFFF;      // 16진수 리터럴
int num2 = 0b11001010;  // 2진수 리터럴
int num3 = 01234;       // 8진수 리터럴

부동 소수점 리터럴

float num1 = 10.0F;
float num2 = 3.14F;
double num3 = 92341.234D;
double num4 = 2314.56d;
double num5 = 43525.91;     // 생략해도 무방

문자, 문자열 리터럴

char ch = 'a';
String helloPocu = "Hello POCU";
String alphabet = "\u03b1\u03b2\u03b3"; //alphabetagamma
String helloWorld = "Hello\nWorld";
  • 문자 리터럴
  • 문자열 리터럴
  • 유니코드 표현
  • 이스케이프 문자 : \로 시작

기타 리터럴

String msg = null;
int num = 12_345_678;
float pi = 3.14_15F;
long hexNum = 0xFF_EC_DE_5E
  • null
    • 참조형에 사용 가능한 리터럴
    • 의미 : 참조하는 대상이 없음(C의 널 포인터)
  • _
    • 큰 숫자의 가독성을 높이기 위해 사용(쉼표 찍듯이)
    • Java7부터 사용 가능

Java의 상수형 변수: final 키워드

final int MAX_STUDENT = 10;
MAX_STUDENT = 30;   // compile error

final 키워드를 붙일 수 있는 곳

  1. 지역 변수
  2. 클래스 멤버 변수
  3. 메서드 매개변수
  4. 클래스와 메서드

final과 멤버 변수

public class StudentManager {
    public final int MAX_STUDENT = 10;
    // ...
}
// 클래스 내부
public static final int MAX_CLASS = 7;

final과 메서드

public int add(final float op1, final float op2){
    op1 += 1;   // compile error
}

final 변수의 초기화1 : 선언과 동시에

public class StudentManager {
    public final int MAX_STUDENT = 10;

    public void printScores() {
        final int MAX_CLASS = 5;
        // ...
    }
    // ...
}

final 변수의 초기화2 : 사용하기 전에만

public void printScores() {
    final int MAX_CLASS;

    MAX_CLASS = 5;
    System.out.prinf("%d", MAX_CLASS);
}
  • C와 C#의 const와는 다르게 final은 선언 후에 초기화 가능
  • 지역 변수일 경우 그 final 변수를 사용하기 전에만 초기화해주면 됨
public class StudentManager {
    final int MAX_STUDENT;

    public void printScores() {
        MAX_STUDENT = 10;   // compile error
    }
}
  • 멤버 변수일 경우
    • 생성자에서 초기화 가능
    • 그 외의 메서드에서 초기화 할 경우 컴파일 오류가 발생
public class StudentManager {
    final int MAX_STUDENT;      // OK

    public void printScores() {
        final int MAX_CLASS;    // OK

        MAX_CLASS = 5;          // OK
        System.out.printf("%d", MAX_CLASS);
    }

    public StudentManager() {
        MAX_STUDENT = 10;       // OK
    }
}

Javadoc 주석

/**
 * <설명>
 * @<태그> <태그>에 대한 설명
 */
  • 클래스, 메서드, 멤버 변수, 인터페이스 위에 위치
  • 이로부터 자동으로 Java API 문서를 새엇ㅇ
  • C#도 ///주석을 통해 비슷한 기능 지원
  • C나 C++에서는 Doxygen 사용
  • @<태그>를 이용하여 설명을 추가할 수 있음
    • @param: 메서드의 매개변수
    • @return: 메서드의 반환값

산술 연산자

  • 기본저긍로 숫자를 표현하는 자료형만 피연산자로 사용 가능
    • 유일한 예외: 문자열 더하기
  • 불리언형에 사용 불가
  • 문자형(char)은 가능
    • 자바에서 char는 정수

문자열은 참조형!

  • 문자열 변수에 저장돼 있는 건 값이 아닌 메모리 주소
  • 따라서 ==은 주소를 비교
    • 그 주소에 있는 실제 문자열을 비교하지 않음

허나 주소를 공유하는 경우가 있음

String name1 = "Nana";
String name2 = "Nana";
  • String을 new로 생성하지 않은 경우

equals() 메서드

public boolean equals(Object obj);

boolean isSame1 = name1.equals(name2);
boolean isSame2 = name1.equals(name3);
boolean isSame3 = name1.equals(name4);
boolean isSame4 = name1.equals("Nana");
  • 두 String을 비교하는 함수

Java는 연산자 오버로딩을 지원 안 함

  • 다행히도 메서드 오버로딩은 지원
  • 그러나 Java에서 딱 하나 지원하는 오버로딩 된 연산자
    • String용 +와 += 연산자

Java의 문자열 비교 베스트 프랙티스

  • 문자열이 참조형이란 사실을 잊지 말 것
  • ==을 쓰지 말 것
  • 그 대신 equals() 메서드를 사용할 것

>>>연산자

  • 부호 없는 비트 연산자
  • 오른쪽으로 이동 후 남는 공간을 '0'으로 채움
  • Java에는 부호 없는 자료형이 없기에 이렇게 연산자를 따로 만듦

case에 사용 가능한 자료형

  • 정수형(char, byte, int...), 열거(enum)형, String
String month = "January";
switch(month){
    case "March":
        System.out.printIn("Spring");
        break;
    case "July":
        System.out.printIn("Summer");
        break;
    default:
        System.out.printIn("Unknown");
        break;
}

case 안에서 break를 빼먹으면?

  • switch 문을 탈출하지 않고 그 아래 있는 코드를 계속 실행
    • fall-through라고 함

continue, break, goto

  • goto는 없음

break와 라벨

break <라벨 이름>;

int[] scores;   // {10, 20, 30, 40}
outer:
for (int i=0; i<scores.length; i++){
    if(scores[i] > 30){
        break outer;
    }
    System.out.print(" " + scores[i]);  // " 10 20 30"
}
  • "라벨 이름"이 달린 코드 블록을 탈출함
  • C의 goto와는 달리 어느 코드로든 점프할 수 있는 게 아님
  • continue 역시 라벨을 사용할 수 있다

foreach 스타일 for문

int[] scores = {10, 20, 30, 40};
for (int score : scores){
    System.out.prinf("%d ", score);
}
  • 어떤 컬렉션이든 순회할 수 있는 반복문
  • C에는 없지만 Java와 C#에는 있음
    • 차이점은 C#은 foreach, Java는 기존의 for 키워드를 이용

함수

public static int add(final int op1, final int op2){
    return op1 + op2;
}

함수 호출과 참조형 인자

  • 참조형을 인자로 넘길 때는 조심해야
  • C에서 포인터를 넘기는 것과 마찬가지
  • 메서드 안에서 참조형 인자의 값을 바꾸면 원본이 바뀜

final 참조형 매개변수

public static Vector add(final Vector v1, final Vector v2){
    // ...
    v1.x += 5432.0f;        // ok. 주소가 가리키는 값은 변경 가능
    v1 = new Vector(20, 30);    // 컴파일 오류. 주소 변경은 불가능
    // ...
}
  • 참조형 변수는 주소를 저장
  • 따라서 final이 붙으면 그 주소만 변경 못 함
  • 그 주소가 가리키는 값은 여전히 바꿀 수 있음

1차원 배열

<자료형>[] <변수명> = new <자료형>[<크기>];
<자료형> <변수명>[] = new <자료형>[<크기>];
int[] nums = new int[5];    // 더 흔한 방법
int scores[] = new int[20];
  • C#처럼 new 키워드를 사용해서 메모리 할당을 해야

다차원 배열

<자료형>[][] <변수명> = new <자료형>[<크기>][<크기>];
<자료형>[][][] <변수명> = new <자료형>[<크기>][<크기>][<크기>];
  • C#의 '배열의 배열'이나 C의 '포인터 배열'과 비슷
  • 즉, 안쪽 배열의 길이가 다 다를 수 있음

Java의 열거형

// 독자적으로 선언
public enum Subject {
    KOREAN,
    ENGLISH,
    MATH
}

// 클래스 내부에 선언
public class SchoolManager {
    public enum Subject {
        KOREAN,
        ENGLISH,
        MATH
    }
    //...
}

Java의 열거형에서 못 하는 것

// 컴파일 오류
public enum Direction
{
    North = 5,
    South = 10,
    East = 15,
    West = East + 10
}
  • 각 원소에 원하는 값을 대입하지 못함

Java의 열거형은 클래스형

public enum Subject {
    KOREAN,
    ENGLISH,
    MATH;

    private int hours;

    public int getHours() {
        return hours;
    }
}
  • 멤버 변수와 메서드를 가질 수 있음
  • 그러나 그냥 데이터로만 사용할 것
  • 멤버 변수 및 메서드도 있을 경우, 맨 마지막 상수 뒤에 ; 추가

열거형과 생성자

public enum Subject {
    KOREAN(2),
    ENGLISH(3),
    MATH(6);

    private int hours;

    public int getHours() {
        return hours;
    }

    Subject(int h){ // 언제나 private
        hours = h;
    }
}
  • 생성자도 추가할 수 있음
  • 단, 생성자는 암시적으로 private
    • public 혹은 protected 불가
  • 각각의 원소(상수)가 만들어질 때 생성자 호출
    • 위 열거형에서는 총 3번 호출됨
  • 따라서, 각 원소들은 생성자가 요구하는 매개변수를 반드시 넘겨줘야 함

열거형과 new 키워드

Subject subject1 = new Subject();   // compile error
Subject subject2;                   // ok
Subject subject3 = Subject.MATH     // ok
  • 열거형 개체를 만들 때는 new를 안 씀

var

var num = 10;
var message = "Hello World!";
var error;      // compile error
  • Java 10 부터 지원
  • 컴파일러가 알아서 자료형 추론
  • 선언과 동시에 값을 대입해야

var 사용 시 주의할 점

var scores = new int[20];           // ok
var number[] = new int[20];         // compile err
var world = new int[100][200][300]; // ok
var names = {"Lulu", "Nana", "Mama"}    // compile err

람다 표현식

() -> System.out.printIn("Hi");
(name) => Systsem.out.printIn("Hi " + name);
  • 이름 없는 함수로 내포(nested) 함수라고도 부름

기존 패키지 시스템의 한계

  • 애플리케이션이 사용하는 클래스 목록을 찾는 공식적인 방법이 없음
    • 누락된 클래스가 있다면 실행 중에 그것을 사용하려 할 때 오류 발생
    • 따라서 사용 중인 패키지에 있는 모든 클래스를 같이 배포하는 게 일반적
    • 문제점
      • Java 버전에 증가함에 따라 Java 자체 제공 라이브러리의 크기가 커짐
      • 안 사용하는 클래스까지 같이 배포할 경우 쓸데 없이 용량이 커짐
    • 패키지 안에 있는 모든 public 클래스를 아무나 사용할 수 있음

모듈

  • Java9부터 지원
  • 패키지보다 상위 개념
    • 패키지를 내포함
  • 장점
    • 정말 필요한 패키지만 포함할 수 있음(경량화)
    • 프로그램 시작 시 누락된 모듈을 확인 가능
    • 어떤 모듈이 사용하는 다른 모듈 목록을 찾기 쉬움
    • 모듈 사용자에게 공개할 클래스를 특정할 수 있음

module-info.java

  • 모듈 간의 의존관계를 정의한 파일
  • 컴파일 과정에서 .class 파일로 바뀜
  • 모든 모듈에 반드시 존재해야 함
  • 이 파일 안에 들어가는 내용
    1. 본 모듈 안에서 사용하는 외부 모듈 목록
    2. 본 모듈 사용자에게 공개할 패키지 목록
module pocu.adademy.core {
    exports pocu.academy.core.math;
    requires java.sql;
}
  • exports : 본 모듈 사용자가 pocu.academy.core.math 패키지를 사용할 수 있게 공개해 줌
    • 아무것도 노출하고 싶지 않다면 exports를 생략
  • requires : 본 모듈에서 사용하는 외부 모듈