[Java] 4. 자바 꼭 알아야 하는 문법 (2)

2023. 9. 9. 19:55Java언어 공부

프로그램 실행하면, 기계 코드가 메모리에 올라간다.

프로그램이 실행되면, 여러 가지 데이터를 처리하는데, 그 데이터를 처리하기 위한 메모리 공간이 필요하다.

이 공간은 크게 힙과 스택과 코드 영역이 있다.

스택 영역은 함수가 실행될 때마다, 함수를 위해서 필요한 공간들을 만들어 준다.

힙 영역은 나머지들(동적 생성, 전역 변수)이 저장이 된다.

메모리 공간

 

자바에는 전역 변수는 없고 스태틱 변수, 지역 변수, 멤버 변수들이 있다.

 

 

Java의 작동 원리에 대하여 알아보자.

우선 main 함수가 먼저 실행될 것이다. 

 

그럼 스택 공간에 main 함수에 대한 공간이 만들어질 것이다.

그러면 원시타입인 a와 참조타입인 x에 대한 공간이 생길 것이다.

 

 

객체는 stack에 생길 수 없으므로 heap에서 메모리 공간이 할당될 것이고, x가 이를 참조할 것이다.

 

 

다음은 foo() 함수가 실행될 것이다.

아직 main() 함수가 끝나지 않았으므로, 지워지지 않을 것이다.

foo() 함수가 실행되면서, b와 y인 지역변수가 생성된다.

 

foo()함수가 끝나면 반납하게 된다.

그럼 저기 y가 가리키던 객체가 남는데 이는 JVM이 자동으로 반납한다.

 

객체 변수 : 

객체를 생성하면 보통 여러 번 사용해야 하며, 동일 종류의 객체를 여러 개 생성할 수 있다.

이를 구분하기 위해 객체 변수에 객체를 조작하기 위한 정보를 유지한다.

 

-----------------------------------------------------------------------------------------------------

 

콘솔 출력

 

System.out.println(greeting.sayHello());

 

System은 클래스 이름이고, out은 객체 이름이다.

클래스는 항상 대문자로 시작한다.

out은 System 클래스 안에 저장된 무언가이다.

멤버 변수거나 메소드인데 ()가 없으므로 메소드는 아니다.

System에서 생성해 놓은 public 객체이다.

 

println은 메소드 이름임.

 

-----------------------------------------------------------------------------------------------------

 

printf, println, print 메소드

 

콘솔 출력할 때 사용하는 메소드는 위처럼 크게 3가지이다.

 

print와 println은 어떤 하나의 값을 출력할 때 쓰는 것이다.

두 개의 차이는 줄바꿈의 유무이다. (println은 줄바꿈을 함)

 

형식화된 출력을 하기 위한 것은 printf이다.

 

println과 print는 하나의 값을 출력하는 것은 맞고, 다양한 타입의 데이터들을 출력할 수 있다.

다양한 값을 한 함수가 출력하려면, 함수 다중 정의를 해야 한다.

 

함수 다중 정의 (overloading) :  

똑같은 함수의 이름을 여러 개 만드는 것.

=> 컴파일러가 구분을 해야 함

=> 매개 변수 개수와 타입의 차이로 구분해야 한다.

 

ex)

 

다른 타입의 인자를 처리할 때, println처럼 다중 정의를 사용한다.

 

자바는 디폴트 값(기본 인자)이 없다.

기본인자가 필요할 때, 다중  정의를 쓰기도 한다.

기본 인자를 활용하는 함수를 정의 할 때 사용한다.

 

printf():

 

\n 대신에 %n 사용 : %n이 JVM의 환경에 맞게 줄바꿈을 해주기 때문이다.

 %,d를 사용 : 정수를 출력할 때, 3자리마다 쉼표를 출력하여 준다.

인자 색인을 사용 할 수 있다. : 인자 색인은 1부터 시작하고, 색인 번호 다음에 $을 사용함.

 

 

-----------------------------------------------------------------------------------------------------

 

자바에서 키보드 입력 처리

 

아까 시스템 출력은 System.out을 이용해서 출력했다.

그럼 시스템 입력은 System.in을 사용해서 입력을 하는데 그냥 쓰지 않는다.

 

추가적으로 Scanner라는 것을 사용한다.

Scanner을 이용하는 이유는 버퍼를 처리하여 속도를 높이기 위해서이다.

 

Scanner을 만드는 데 생성자에 인자로 System.in을 사용한다.

System.in을 굳이 꼭 써야 하는 것은 아니다.

파일을 주는 방법을 알면, 텍스트 파일로부터 데이터를 가져올 수 있다.

 

in.close()을 사용한다.

파일을 다 쓰면, 닫아야 하기 때문이다.

주의점은, 파일을 닫는다고 in.close()을 쓰면 다시 열 수 없다.

 

그리고 Scanner는 import문을 사용해야한다.

System이랑 String 클래스는 import를 안써도 되는 클래스인데 Scanner는 기본적으로 주어지지 않는다.

import java.util.Scanner;

 

Scanner는 java.util에 있다는 것은 알아두자.

 

in.nextInt() : 정수를 하나 입력 받아서 반환해주는 함수

 

Scanner는 버퍼 입력이다. (C++에서도 썼던 cin도 버퍼입력)

import java.util.Scanner;

public class Test{

	public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("정수 입력: ");
        int n1= in.nextInt();
        System.out.print("정수 입력: ");
        int n2= in.nextInt();
        System.out.printf("%d + %d = %d%n", n1, n2, n1 + n2);
        in.close();
    }
}

 

위 코드를 보자.

실행할 시 정수를 하나씩만 넣으면 되는데 10 20 30 40을 첫번째 줄에다가 넣었다고 가정하자.

네 개의 정수 다 버퍼로 들어갈 것이다.

그러면 in.nextInt() 때문에 10이 먼저 나올 것이고, 버퍼에는 20 30 40이 남을 것이다.

그래서 바로 입력하지도 않고도 2번째 줄을 넘어가서,

nextInt()에 20이 들어갈 것이고 10+20=30이 출력이 된다.

 

in,nextInt()는 원래 버퍼에 가서 확인하고, 취할 것이 없으면 입력이 들어올 때까지 깜빡깜빡 거린다.

 

import java.util.Scanner;

public class Test{

	public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("나이 입력: ");
        int age= in.nextInt();
        System.out.print("이름 입력: ");
        String name= in.nextLine();
        System.out.printf("%s: %d%n", name, age);
        in.close();
    }
}

 

코드를 조금 바꾸어 보았다.

나이를 정수로 입력 받고, 이름은 문자열 받는다.

nextLine()을 써보았다. 

nextLine() : 줄 바꿈 문자를 만날 때까지, 한 줄을 입력 받고 문자열을 반환해준다.

 

위 코드를 실행하고, 나이로 적당한 숫자를 주면 논리적 오류가 난다.

이유는 나이를 주기 위해 숫자를 키보드로 친 뒤에 엔터(\n)을 누를 것이다.

그러면 nextInt()는 나이까지의 정수를 입력을 받고, \n는 버퍼에 남겨놓을 것이다.

 

그래서 다음 in.nextLine()은 버퍼에 바로 \n을 만나 입력 되지않고 넘어가는 것이다.

이걸 해결해주기 위해 줄바꿈 문자를 없애줘야 한다. => 버퍼를 비워주어야함.

 

in.nextLine()을 써서 강제로 버퍼를 비워준다.

 

그런데 in.close()를 쓰면 다시 입력을 받지 못한다.

Scanner를 main() 함수에서 입력을 받았는데, 다른 함수에서도 써야 하는 경우가 있다.

두 가지 방법이 있다.

 

1. Scanner를 그 함수의 인자로 전달하기

2. static Scanner를 사용하면 된다. (전역 변수처럼 사용하면 된다)

=> 자바에서는 전역 변수가 없으니까 클래서 변수로 만들면 되고, 클래스 변수 앞에 static을 붙이면,

전역 변수와 같은 효과를 보인다.

 

-----------------------------------------------------------------------------------------------------

 

BufferedReader

 

고급내용임

 

Scanner보다 빠른 입력을 요구할 때 쓴다.

정수를 3만번 입력 받는 것보다, 문자열 하나를 입력받아 쓰는 것이 더 빠르다.

함수를 호출하는데 드는 비용이 많이 들기 때문이다.

 

Scanner을 사용하여 nextLine()을 하는 방법이 있지만, BuufferedReader가 사용하는 버퍼의 크기가 크기 때문에,

BufferedReader을 사용하는 것이 이득이다.

 

나중에 다시 알아보도록 하자.

 

-----------------------------------------------------------------------------------------------------

 

자바 라이브러리에 있는 클래스 사용

 

하나의 프로그램을 만들 때, 클래스들을 많이 정의하고,

하나의 클래스를 한 파일에 정의한다.

 

여러 개의 클래스와 파일들이 존재할 거고 관리하기가 힘들 것이다.

그래서 나온 개념이 '패키지'이다.

 

우리가 같은 디렉토리에 있을 경우는, C/C++에서는 #include를 사용해야 한다.

자바에서는 같은 패키지에 있으면 import를 사용안해도 된다.

 

module은 패키지를 묶는 개념이고, 패키지는 클래스를 묶는 개념이다.

 

클래스를 어떤 패키지에 포함하고자 하면 소스파일의 첫 문장에 다음과 같은 package문을 포함해야 한다.

ex) package 패키지이름;

 

같은 디렉토리에 포함된 클래스는 package문이 없어도 자동으로 하나의 패키지에 포함됨.

이것을 기본 패키지라고 한다.

 

요약)

어떤 클래스를 만들었는데, 특수 패키지에다 포함하고 싶다 => package문 사용

다른 패키지의 public class를 사용하고 싶다  => import문 사용 ex) java.util.Scanner

 

자바에서 특정 패키지의 위치를 찾는 방법

1. 현재 작업 디렉토리에 있는지 검사함

2. 환경변수 CLASSPATH에 지정된 위치를 검사함

3. java를 실행할 때 "-classpath" 옵션에 지정된 위치를 검사함.