자바 기초

Seung jun Cha·2022년 9월 20일
0

1. 자료 표현하기

  • 진법으로 숫자를 표현하면 진수라고 하며 우리가 알아야하는 진법은 2진법, 8진법, 16진법이 있다
  • n진법은 0에서 n-1까지의 숫자를 사용해서 수를 표현하는 방법으로 높은 자릿수를 1씩 증가시켜 다음 수를 표현한다.

1-1 2진수

  • 0~1까지의 숫자를 사용해 수를 표현

1-2 8진수

  • 0~7까지의 숫자를 사용해서 수를 표현

1-3 16진수

  • 0~9 ,문자 A~F까지 16개로 수를 표현

1-4 10진수로 변환

  • 인터넷에 검색하면 쉽게 변환해주므로 여기서는 그림으로 간단하게 알아본다

1-5 printf

  • int

    System.out.printf("%d\n", i);
       System.out.printf("%10d\n", i); // 자릿수를 지정하고(10의자리) 오른쪽으로 정렬(%)
       System.out.printf("%-10d\n", i); // 자릿수를 지정하고 왼쪽으로 정렬 (%- : 왼쪽 정렬)
       System.out.printf("%010d\n", i); // 자릿수를 지정하는데 비어있는 자릿수는 0으로 표시
       System.out.printf("%o\n", i);
       System.out.printf("%x\n", i);
       System.out.printf("%X\n", i);
       System.out.printf("%n"); // 줄 바꿈(\n)
       System.out.println();
- float

 System.out.printf("%f\n", f);
        System.out.printf("%.2f\n", f);  // 소숫점 n번째 자리까지 표시
        System.out.printf("%.8f\n", f); // 부동 소수점의 오차(8번째 자리까지) 
        System.out.printf("%5.2f\n", f); // 전체 5자리 중 2자리까지 표시 
        									(%전체자리.소수점자리)
        System.out.printf("%-5.2f\n", f);
        System.out.printf("%e\n", f);
        System.out.printf("%E\n", f);
        System.out.printf("%30.3e\n", f);
        System.out.printf("%30.3E\n", f);

2. 변수, 상수

2-1 변수

  1. 자료형 + 변수 이름

  2. 변수는 변수를 저장할 수 있는 공간을 의미

  3. 영문자, 숫자, 특수문자를 사용할 수 있지만 변수의 이름은 숫자로 시작할 수 없으며, 특수문자는 $, _ 만 사용가능하다.

  4. 변수이름의 길이가 길어져서 복합어가 될 경우 camel case로 표기한다
    camel case : 첫문자는 소문자로 시작하고, 이어지는 각 단어의 첫글자를 대문자로 표기 ex avegPeopleHeight

2-2 상수(final)

  1. 변수를 한 번 상수로 선언하면 다른 곳에서 값을 바꿀 수 없음
    값을 선언한 부분에서만 변경할 수 있다.

  2. 상수의 변수명은 모두 대문자로 한다.

  3. 변하지 않는 값을 반복해서 사용할 때, 문자로 인식하기 쉽다

3. 자료형

3-1 기본자료형

  • 기본자료형은 stack 메모리 공간을 사용

  • 정수는 int, 실수는 double이 기본이기 때문에, long은 L 식별자 필수 , float는 f 식별자 필수

  • 문자인 char = ' ' 는 딱 1개의 문자만 받을 수 있고, 변수 안에 해당 값이 들어가있다. 아스키코드로 변환되어 숫자와 알파벳으로 인코딩, 디코딩이 가능하다

3-1-1 형변환

  • 서로 다른 자료형 간에 연산 등 수행을 위해 하나의 자료형으로 통일하는 것인데 캐스팅을 명확하게 표현함.

  • 작은 자료형에서 큰 자료형으로, 덜 정밀한 자료형에서 더 정밀한 자료형으로의 형변환은 자동으로 이루어지고 손실이 없음

  • 서로 타입이 다른 것끼리 연산할 경우 작은 피연산자가 큰 피연산자로 자동 형변환된다.

int a = 4; 
double b = a;  (묵시적 형변환)

int a = 4;
double b = (double)a;  (명시적 형변환)

int a = 4;
String b = "b"
sout("a + b = ")  => 4b

int a = 4;
double b = 10.0;
a(4.0) + b = 14.0

3-2 참조자료형

  • 참조자료형으로는 배열, 객체, 함수, String, 열거형이 있다.
    heap 메모리 공간을 사용한다. (유연하게 관리) , 객체의 주소값을 가지고 있는 참조변수는 stack에 들어감

  • 기본자료형은 변수에 값을 저장하지만, 참조자료형은 값은 다른 메모리 공간에 값을 저장하고 변수에는 해당하는 메모리 주소값을 저장한다.

  • 기본 자료형은 사용하는 메모리 크기가 정해져있지만, 참조 자료형은 클래스에 따라 다르다.

3-2-1 equals() , == 연산자

  • == 연산자는 두 객체의 메모리 주소를 비교하여 같은 객체인지 여부를 판단합니다.
String str1 = new String("Hello");
String str2 = str1;

boolean sameObject = (str1 == str2); // true, 같은 객체를 가리킴
  • 기본적으로 equals() 메서드는 == 연산자와 동일한 동작을 수행하지만 많은 클래스들은 equals() 메서드를 오버라이딩하여 객체의 내부 상태를 비교하도록 구현합니다. 예를들면, Java의 String 클래스는 equals() 메서드를 오버라이딩하여 두 String 객체의 내부 문자열 값을 비교하도록 재정의하였습니다.

3-2-2 hashcode()

  • 객체의 해시 코드를 반환합니다. 해시코드는 객체의 주소값에 해시함수를 적용하여 생성한 객체의 고유한 정수값이다. 해시 코드를 사용하는 이유는 객체가 같은 내용을 가진 경우에도 다른 메모리 위치에 저장될 수 있기 때문입니다. hashCode()는 객체가 동등하다고 판단될 때 동일한 값을 반환해야 합니다.
String str1 = new String("Hello");
String str2 = new String("Hello");

boolean sameHashCode = (str1.hashCode() == str2.hashCode()); 
// true (동일한 내용이므로 동일한 해시 코드)

3-2-3 equals()와 hashCode()는 왜 같이 사용하는가?

  • 해시를 사용한 자료 구조는 Key를 결정할 때 hashCode() 를 사용하기 때문이다. 즉, 객체가 동일한지 비교하기 전에, 두 객체의 해시 코드가 같은지 비교하고 그 후 두 객체가 동등한지 판단한다. 이때, hashCode() 가 재정의되어 있지 않다면 Object의 hashCode()를 사용하므로 각 객체가 저장된 메모리 주소가 반환된다. 따라서 해시 자료 구조를 사용하는 경우를 위해 equals() 외에 hashCode() 도 재정의해 주는 것이 좋다.


=> hash자료구조의 key에 해시함수를 적용하여 나온 해시코드에 value를 매핑한다. equals()로 값을 비교하기 전에 hashcode()가 작동해서 해시코드를 먼저 비교한다. 해시코드가 같아야 equals()로 넘어간다.

======
두 객체의 값이 같은지 비교하기 위해 equals()와 hashcode() 메서드를 사용한다.
equals() 를 오버라이딩 해서 값을 비교하도록 오버라이딩해도 hashcode() 를 오버라이딩하지 않아 두 객체의 주소값을 비교하면 동등성이 성립하지 않는다. hashcode()를 오버라이딩하여 값이 같으면 같은 해시코드를 반환하도록 해야한다.

  • StringBuilder
StringBuilder sb = new StringBuilder("hello")
sb.replace("h", "H")   
String s = sb.toString  // 이때 문자열 할당, 이전까지는 자유롭게 수정가능

4. 조건문

4-1 switch-case문

  1. 비교 조건이 특정 값이거나 문자열인 경우에 주로 사용
  2. switch와 case값을 비교하여 출력하고, 모두 해당되지 않는 경우는 default에 설정된 값을 출력
  3. 조건식을 계산해서 일치하는 case문으로 이동하고, break문이나 switch문이 끝나면 switch문 전체를 빠져나간다. break문이 빠지면 switch문 전체를 수행하게 되니 주의할 것
  4. 조건식의 결과는 정수외 문자열만 가능하며 중복되지 않아야 한다. (변수X)
switch(조건식)
	case 1: case 3: day=31
    	break;
    case 2: day=30
    	break;
    default day=28

4-2 if문 , 삼항연산자

  • if문을 사용해야 하는 상황이라면 삼항연산자 사용이 가능한지 생각해보자

5. 반복문

5-1 (do-)while

  1. while에서 주어진 조건이 참이라면 지정된 수행문을 반복적으로 수행한다. for문과 완전 동일하므로 초기화나 증감식이 필요하지 않은 경우라면 while문이 더 적합하다
  2. do에서 선언된 수행문은 무조건 1번 실행한 후, while의 조건문에 따라 진행 (정답을 입력받을 때?)
do{
	수행문
}while(조건문){
	수행문
}

5-2 이름이 붙은 반복문

  1. 반복문이 중첩되는 경우 한번에 둘 이상의 반복문을 벗어나고 싶을 떄, 이름을 사용
Loop1 : for(){
			for(){
         }
       }
       continue Loop1;

5-2 continue문

  1. 반복문 내에서만 사용할 수 있으며, 진행중에 continue를 만나면 자신이 속한 반복문의 끝으로 이동하여 다음 반복으로 넘어간다. 그래서 continue문 이후의 문장들을 수행하지 않는다.
System.out.println("반복문 시작");
    
    for(int index = 1 ; index <= 5 ; index++) {
    	
        if(index == 3) {
            continue;
            System.out.println(index);
        }
    }
    
    System.out.println("반복문 종료");
    
}

//=====실행결과======
//반복문 시작
//1
//2
//4
//5
//반복문 종료

5-3 break

  • 자신이 포함된 가장 가까운 반복문을 벗어난다.
 System.out.println("반복문 시작");
    
    for(int index = 1 ; index <= 5 ; index++) {
    	
        if(index == 3) {
            break;
            System.out.println(index);
        }
    }
    
    System.out.println("반복문 종료");
    
}

//=====실행결과======
//반복문 시작
//1
//2
//반복문 종료 (index가 3이 되었을 때, 뒤의 System.out.println(index);는 
수행되지 않고 반복문을 탈출

5-4 for문

for(int i = 0; i<= 10; i+=2 / i *= 3) // 다양한 연산자로 증감식 가능
for(int i = 0, j=10; i<= 10; i++, j--) // 두 문장 이상을 하나로 연결하는 것 가능
for(;;) // 무조건 참으로 반복

6. 입력메서드

  • 사용자가 콘솔을 통해 입력한 데이터를 저장
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();

7. return문

  • 현재 실행중인 메서드를 종료하고 호출한 메서드로 돌아간다
    반환타입이 void인 경우 컴파일러가 자동으로 return을 추가해준다.

8. 예외처리

8-1 try-catch문

  1. try문에 예외가 발생할 가능성이 있는 문장을 넣고 예외가 발생하면 발생한 예외와 일치하는 catch문을 실행한다. 이후의 catch문은 실행하지 않으며 해당 예외를 처리하는 catch문이 없으면 예외가 처리되지 못하고 종료된다.
    try문에서 예외가 발생하지 않으면 전체 try-catch문을 벗어나고 다음 문장이 실행된다.

  2. 멀티 catch블럭 : | 를 를 사용해서 예외클래스를 연결할 수 있으며 개수에 제한이 없다. 하지만 연결된 예외 클래스가 조상과 자손의 관계에 있다면 컴파일 에러가 발생한다.

try{

} catch(Exception a | Exception B e){

}

8-2 throw, throws

  • throw : 예외를 고의로 발생시킴
try{
	throw new ...
} catch(){

}
  • throws : 해당하는 메서드에서 발생가능한 예외를 선언. 발생한 예외를 try-catch로 발생한 곳에서 처리하지 않으면, 자신이 처리하지 않고 상위로 전달한다. throws가 선언되어 있으면 checked예외의 경우에는 예외가 선언만 되어있으도 컴파일에러가 발생하지 않는다.

8-3 checked, unchecked

9. equals(), hashCode()

  1. Object.equals : 객체의 주소값을 비교

  2. String, Integer, Double, Boolean.equals() : 내부적으로 오버라이딩 된 equals를 사용하기때문에 주소값이 달라도 문자열이 같으면 true를 반환한다.

  3. hashCode() : 객체의 주소값을 이용해서 해시코드를 만들어 반환하므로 서로 다른 두 객체는 절대 같은 해시코드를 가질 수 없다.
    인스턴스 변수의 값으로 객체가 같은 것인지 아닌지 판단하기 위해서는 equals()뿐만아니라 hashCode() 역시 오버라이딩 해야한다.
    String 클래스는 문자열의 내용이 같으면 동일한 해시코드를 반환하도록 오버라이딩되어 있다.

10. 리플렉션

  • 리플렉션은 컴파일 시점에는 알 수 없는 클래스의 정보를 동적으로 알아낼 수 있게 해주는 기능이다. 사용자 입력이나 외부 리소스에 따라 프로그램의 동작이 결정되는 경우, 클래스 정보를 컴파일 시점에는 알 수 없다. 이런 경우 리플렉션은 런타임 시점에서 컴파일된 클래스의 메타데이터를 조작할 수 있는 기능을 제공한다. 프로그램의 유연성과 확장성을 높이는데 도움이 되지만 타입 안정성이 떨어진다.

  • 단점

    • 접근 제한자를 무시하고 private 멤버에도 접근할 수 있어서 보안문제 발생
    • 런타임 시점에 코드가 실행되어 컴파일러가 타입 체크를 수행할 수 없는 상황이 발생
    • 동적으로 클래스 정보를 조사하고 호출하는 과정이 추가되기 때문에 속도가 느림
  • 메타데이터는 클래스의 정보로 예를 들어, 클래스의 이름, 접근 제어자, 필드정보, 메서드정보, 생성자정보 등을 클래스의 메타데이터로 간주된다

컴파일 시점은 소스 코드가 컴파일러에 의해 기계어로 변환되는 시점으로 타입 체크, 문법 검사 등이 이루어진다.
런타임 시점메모리를 할당하거나 객체를 생성하고, 코드가 실행되는 시점이다. 컴파일 시점에 확인되지 않은 오류나 예외 처리, 동적인 데이터 처리 등이 이루어진다.

11. 직렬화, 역직렬화

  • Java에서 입출력할 때는 스트림이라는 데이터를 바이트 흐름으로 처리하는 데이터 통로를 사용한다. 객체는 스트림을 통해 전송이 불가능*해서 바이트 배열로 변환하는데 이것을 직렬화라고 한다.

  • 사용하는 경우

    • 객체를 파일이나 데이터베이스에 저장하고 나중에 다시 읽어와 사용하는 경우
    • 네트워크를 통해 객체를 전송, 수신하는 경우

12. JVM

  • JVM은 Java Virtual Machine(자바 가상 머신)의 약자로, 자바 프로그램을 실행하기 위한 가상 컴퓨터이다. 자바 어플리케이션은 JVM 위에서 실행되기 때문에, 특정 운영체제에 종속되지 않고, 각 운영체제에 맞게 실행된다.

  • 자바 컴파일 과정

  1. Java 프로그램이 실행되면 JVM은 OS로부터 프로그램이 필요로 하는 메모리를 할당받는다.

  2. Java 컴파일러(javac)가 Java 소스(.java) 코드를 읽어 자바 바이트 코드(.class)로 변환한다. (컴퓨터는 읽을 수 없고 JVM만 읽을 수 있는 상태이다. 이때 바이트코드는 OS의 독립적이게 된다. 그렇기 때문에 JVM만 있으면 어디서든 실행할 수 있다.)

  3. 변환된 바이트 코드 파일들을 클래스 로더(Class Loader)가 JVM 메모리 (Runtime Data Area) 영역으로 로딩한다. (클래스 로딩시점)

  4. 로딩된 파일들을 실행 엔진(Execution Engine)이 각 OS에 맞는 기계어(바이너리 코드)로 해석해서 읽는다.
    이때 실행엔진은 인터프리터 방식과 JIT 컴파일러 방식을 사용한다

(메모리 할당 - 컴파일러 - 클래스 로더 - 실행엔진)

0개의 댓글