본문 바로가기
JAVA

[Java] 자바 '공부' 해보자(입문)

by five-sun 2022. 6. 22.
728x90

학교를 다니며 Java 프로그래밍 강의를 수강했었다. 하지만 여전히 Java에 대한 이해와 스킬이 부족하다고 생각하여 기본적인 이론을 학습하고 직접 코딩해보며 공부하기로 했다. 다음 사이트를 참고하였다.

 

*참고자료: 자바 튜토리얼 총 59회 - 스무디코딩 (smoothiecoding.kr)

 

자바 튜토리얼 총 59회 - 스무디코딩

프로그래밍 언어인 자바 튜토리얼의 목차입니다.

smoothiecoding.kr

- 자바란?

자바는 선마이크로소프트웨어에서 개발한 객체지향 프로그래밍 언어이다.

현재는 오라클사 라이센스를 받아서 개발하고 유지보수하고 있다.

한국정부가 자바 스프링 프레임워크를 전자정부의 표준으로 지정한 후에 우리나라에서는 더 많이 사용하고 있다.

자바를 사용하기 위해선 자바 JDK와 IDE를 선택하여 설치해야한다.

 

- 자바 JDK?

JDK는 Java Development Kit의 약자로 자바 개발 키드이다.

자바의 컴파일러와 JRE(Java Runtime Environment)를 포함하며 자바 프로그램을 개발하고 실행시키기 위한 필수 프로그램이다.

JDK를 별도로 설치하면 환경변수가 제대로 설치되었는지 확인해줘야 한다.


- 자바의 변수와 자료형

자바는 C언어와 같이 정적인(Static) 언어입니다. 변수의 데이형에 엄격하다.

 

1. 정수 자료형

바이트(byte):

1바이트는 8비트를 말한다. 저장가능한 숫자의 범위는 2의 8승 256개입니다. (-128~127까지 표현범위)

 

쇼트(short):

쇼트형은 16비트(2바이트)이다. 저장가능한 숫자의 범위는 2의 16승 65336개입니다. (-32768~32767까지 표현범위)

자바 컴파일러는 자료형을 초과하는 변수할당에 대하여 오류를 발생한다.

 

인트(int):

인트형은 가장 많이 사용하는 자료형이다. 32비트(4바이트)로 쇼트형의 2배이다.

4,294,967,296개 범위의 정수를 저장할 수 있다.(-2의 31승 ~ 2의 31승 -1까지 표현범위)

 

롱(long):

롱형은 64비트(8바이트) 중수형이다.(-2의 63승 ~ 2의 63승 -1까지 표현범위)

식별자인 L이나 l을 숫자의 끝에 붙여준다.

 

2. 문자 자료형: char

컴퓨터는 수를 다루지만 인간은 문자를 다룬다. 컴퓨터의 명령을 실행시키기 위해서는 모든 문자나 표현방식을 숫자로 바꿔야 한다.

encoding (컴퓨터가 읽을 수 있는 숫자가 된다.) -> decoding(숫자는 다시 문자)

여러가지 변환 규칙이 있으면 서로 호환이 안되는 문제가 발생해 ASCII라는 규칙을 만든다.

 

ASCII:

7비트 128개의 문자이며 0~31과 127은 컨트롤 문자이고 32~126까지가 출력을 위한 문자이다.

한글코드는 한국에 컴퓨터가 도입되고 한글을 표현해야 하니까 독자적인 문자 코드체계가 만들어졌다.

 

문자형(char):

16비트(2바이트)이다. (0 ~ 65535까지 범위)

음수를 할당할 수 없고 char 형에 숫자를 입력하고 표준출력함수로 출력해보면 각종 언어의 문자를 출력한다.

자바는 2바이트 유니코드를 사용한다. 한글의 유니코드표를 보고 변환하면 정확하게 출려된다.

 

타입캐스팅 char:

숫자에서 문자로 변화하는 디코딩 과정은 println 함수가 자동으로 해주는 일이기 때문에 크게 신경 쓸 일을 잘 없다.

정수 i를 받아서 (cast) 로 println 에 전달하면 아스키 문자 또는 유니코드 문자를 출력합니다.

 

 

3. 실수 자료형(부동소수점): float, double

실수형은 지수와 가수를 나눈 부동 소수점 방식으로 표현한다. 1.0 * 10의 -2승 -> 0.01

컴퓨터 내부에 실수를 저장하기 위해서는 0과 1의 바이너리로 표현해야하는데 IEEE 754에는 32비트 단정도와 64비트 배정도를 사용해서 변환한다는 정더로 이해해 두면 좋다.

부동소수점은 정수에 비해 연산 시간이 길다는 단점이 있는 반면 과학적 표기법을 사용해서 표현범위가 넓다는 장점이 있다.

 

실수 자료형은 Java의 문제가 아닌 부동 소수점인 double과 float 저장방식의 근본적인 결함때문에 정밀도에서 근본적인 문제가 있는 자료형이다. 따라서 Java 에서는 화폐를 다루는 별로의 클래스와 API가 있다.

 

실수 자료형은 64비트(8바이트) double형이 기준이므로 32비트(4바이트) float형을 쓰기 위해선 접미사 F를 붙여주어야 한다.

 

4. 논리형: boolean

 


- Java의 Flow Control

프로그램의 흐름을 제어하는 문법

 

1. if 조건분기 (if branch)

상황이 발생하면 A를 하고 발생하지 않으면 B를 한다. 조건문을 일반적인 언어로 표현한 예이다.

 

if문:

    public static void main(String[] args) {
        boolean situation = true;

        if(situation == true) {
            System.out.println("A");
        }
        if(situation == false) {
            System.out.println("B");
        }
    }

if-else문:

	public static void main(String [] args) throws IOException {
		boolean situation = true;
		
		if(situation == true) {
			System.out.println("A");
		}
		else {
			System.out.println("B");
		}
	}

if- else if-else문:

	public static void main(String [] args) throws IOException {
		char situation = 'A';
		
		if(situation == 'A') {
			System.out.println("A");
		}
		else if(situation == 'B'){
			System.out.println("B");
		}
		else {
			System.out.println("C");
		}
	}

switch-case문:

	public static void main(String [] args) throws IOException {
		char situation = 'A';
		
		switch(situation) {
		case 'A':
			System.out.println("A");
			break;
		case 'B':
			System.out.println("B");
			break;
		default:
			System.out.println("C");
			break;
		}
	}

 

2. for 문 (for loop)

프로그램의 동작을 정의할 때 반복하는 일들이 많다. 컴퓨터는 똑똑할 것 같지만 알아듣는 것은 0과 1밖에 없다.

for문의 형식은 C언어에서 유래했다.

for문의 장점은 한줄안에 어떠한 조건으로 얼마만큼 실행할 수 있는지 파악할 수 있다.

for(초기화; 조건식; 증감식){
	실행할 코드;
}

초기화 -> 조건식 -> 실행할 코드 -> 증감식 -> 조건식 -> 실행할 코드 순으로 반복하여 진행된다.

중첩하여 사용할 수 있다.

 

3. while 문 (while loop)

for문이 정해진 횟수를 반복하는데 주로 사용한다면 while문은 '조건식이 참일 동안 반복 수행' 하는데 사용한다.

while(조건식){
	실행할코드;
}

어떠한 조건에 도달할 때까지 반복한다는 의미이다.

 

do-while 문:

while문과 같지만 차이점은 while 문은 처음 조건식을 검사했을 때 false가 되면 while 문 안의 코드가 한번도 실행되지 않고 빠져나가지만, do while 문은 최초 1번은 실행된다.

do {
	실행될 코드;
} while(조건식);

- Java의 객체지향 프로그래밍 소개

자바 객체지향 프로그래밍이란?

 

객체란?

객체의 뜻은 어떤 대상, 사람이나 사물을 말한다. 눈에 보이는 모든 것들과 보이지 않는 것들도 모두 객체이다.

예를 들어 TV는 객체이다. TV객체의는 고유의 속성과 행동이 있을 것이다. 여기서 TV의 속성은 클래스의 속성이라고 하고 행동을 메소드라고 한다.

 

1. 클래스(Class)

클래스 사용의 기본은 인스턴스를 만들어서 사용한다. 클래스가 설계도라면 인스턴스는 제품이다.

 

클래스 생성:

접근제어자 class 클래스이름 {
	멤버변수;
    	메소드;
}

접근제어자를 사용해 클래스의 사용가능 범위를 조절할 수 있다.

클래스 안에서 멤버변수나 메소드에도 각각 접근제어자를 붙일 수 있다.

객체간에 어떠한 관계를 맺고 있는지도 생각하면서 프로그래밍해야 한다.

 

패키지: 

패키지는 클래스 파일의 묶음이다. 프로그래밍을 하다보면 여러개의 클래스가 계층구조를 가지고 사용하게 된다.

라이브러리도 마찬가지다.

 

2. 메소드(Method)

클래스가 사용할 수 있는 함수이다.

int function(int a, int b) 라면 a와 b를 투입해서 int 형 자료를 반환한다.

 

기본 메소드(main):

프로그램의 시작지점.

public static void main(String[] args) main 함수는 항상 이 형식을 유지해야한다. 자바 표준이다.

이들도 클래스 안에 있다. 자바는 모든 것이 객체지향이기 때문에 혼자 독립해있는 함수는 존재할 수 없다.

 

사용자 메소드 정의:

자바 메소드는 클래스 안에 정의한다. 클래스를 생성하고 그 안에 메소드를 만든다.

클래스는 사용하기 전에 인스턴스를 생성해야 하지만 static 키워드를 쓰면 인스턴스를 생성하지 않고 바로 함수를 사욯라 수 있다.

 

void 함수:

void 함수는 반환값이 없는 함수이다. 제일 처음 사용하는 void main, 자바 메인함수도 자신을 호출한 시스템에 딱히 반환할 값이 없다는 것이다.(JVM에게)

 

return 예약어:

예약어 혹은 키워드 라고 하는 단어는 컴파일러가 사용자에 앞서 미리 선점한 단어를 말한다.

함수 진행도중 return을 만나면 함수는 종료한다.

 

3. 인스턴스(Instance)

인스턴스는 클래스와 연관지어 이해하는 개념이다.

클래스의 설계를 바탕으로 메모리에 구현한 상태를 인스턴스라고 한다. 이 인스턴스를 객체라고 한다.

 

인스턴스의 개수:

인스턴스의 개수에는 제한이 없다. 메모리와 컴퓨터 자원이 허용하는 안에서 얼마든지 늘릴 수 있다.

최근 클라우드 마케팅에서는 쓴만큼 돈을 지불하는 시스템이기 때문에 대량의 인스턴스를 사용하기 전에는 미리 계산할 필요가 있다.

 

인스턴스 생성:

인스턴스를 생성하기 위해서는 new 예약어를 사용합니다.

C에서는 동적 메모리할당하는 방식의 함수이었고 자바에서는 동적 메모리할당 + 인스턴스생성으로 의미가 확장되었다.

new 키워드가 등장하면 힙메모리에 인스턴스가 생성되었다는 것을 알 수 있다.

new 없이 사용하는 클래스들은 static 클래스이다.

 

참조변수:

참조변수는 이 힙메모리에 생성된 인스턴스이다.

참조변수를 출력하면 패키지.클래스이름과 일종의 주소가 나온다. 여기서 주소값은 해시코드이다. JVM에 해시를 주면 실제 주소로 연결된다. 해시코드가 가상주소라고 할 수 있다.

 

4. 생성자(Constructor)

자바 생성자는 인스턴스를 만들 때 초기화하는 일을 한다. 

생성자의 특징은 마치 함수처럼 생겼는데 반환값이 없다. void나 마찬가지다.

자바는 소스코드에 생성자가 있는지 확인 후 없으면 기본 생성자를 만들어 준다.

 

기본생성자:

Person(){}

 

매개변수가 있는 생성자 오버로드:

public Person(String name){
	this.name = name;
    System.out.pritnln(this.name);
}

생성자는 매개변수를 사용하여 데이터를 입력받을 수 있다.

 

this 예약어:

예를 들어 인스턴스 Example을 생성했다. Example.name 이건 밖에서 쓰는 이름이고 클래스 내부에서는 this.name이라고 한다.

 

5. 접근제어자

자바 클래스에는 접근제어자 라는 것이 있다. 클래스, 멤버변수, 메소드에 모두 적용할 수 있는 키워드다.

종류는 public, private, protected, default 가 있다.

접근제어자 접근범위
public 제한 없음
private 클래스 내부에서만
protected 같은 패키지 내부와 상속 관계 클래스
default 같은 패키지 내부에서만

 

private 접근제어자:

private은 클래스 안에서만 사용 가능하다.

private 변수는 외부 클래스에서 값을 직접 사용하려고 하면 사용할 수 없다 나온다.

접근제어자를 활용해 보호해야할 데이터, 숨겨야할 데이터를 다루는 기술을 정보 은닉이라고 하며 객체 지향 프로그래밍의 중요한 특징이다.

 

getter 와 setter:

private 멤버변수를 다룰 때 이들에게 접근하기 위해 사용하는 메소드다.

예를 들면,

public String getPassword() {
	return password;
}

public void setPassword(String password) {
	this.password = password;
}

직접 코딩해도 되지만 이클립스나 인텔리제이에는 코드 generator가 있어서 클릭 한번이면 만들어진다.

이렇게 하면 바깥에서 password라는 private 멤버변수에 접근할 수 있다. 그냥 멤버변수를 직접 사용하는 것보다 메소드 안에서 사용하기 때문에 생길 수 있는 오류들을 미리 사전에 방지할 수 있다.

 

6. this 의미

this란?

자바에서 인스턴스 자신을 가르키는 키워드이다.

클래스 안에서 볼 수 있는데 클래스 메소드의 매개변수와 멤버변수의 식별자가 겹치는 경우가 발생할 때 유용하게 쓰인다.

this는 문맥에 따라 인스턴스를 가르키기도 하고 클래스르 가르키기도 한다.

 

7. Static 변수

클래스에 static 변수라고 하는 변수를 하나 만들어서 공유하면 관리도 쉽고 자원도 낭비하지 않게 한다.

static 변수는 말그대로 정적인 변수이기 때문에 프로그램이 최조 실행되었을 때 이미 메모리에 생성이 된다.

 

인스턴스에서도 사용할 수 있다. static 변수는 모든 인스턴스와 클래스가 공유한다.

 

static 변수 메모리 배치:

메모리에는 여러개의 영역이 있다. 대표적인 영역이 스택과 힙이다. 코드 영역, 데이트 영역도 있다. 

static 변수는 데이터 영역에 있고 클래스와 인스턴스가 모두 데이터 영역 안의 static 변수를 가르키고 있다.

 

인스턴스의 참조변수는 스택메모리에 있고 스택 메모리에 있는 것은 해시값이다. 이들의 실제 값은 힙 메모리 영역에 있다. 메모리 영역이 다르면 변수의 수명이 다르다는 점이 조금 복잡하다. 데이터 영역은 프로그램 시작부터 끝까지 남아있다. 스택 영역과 힙 영역은 GC(가비 컬렉션)이 더이상 쓸모 없다고 판단하면 해제한다. GC는 메모리 유출을 방지하기 위해 힙영역을 먼저 해제해야 한다.

 

8. Static 메소드

인스턴스를 생성하지 않아도 static 메소드를 사용할 수 있다. 인스턴스가 없으니 클래스 이름을 사용한다.

 

private static 변수와 사용:

static 변수가 private 일 때의 사용방식이기도 한다.

private static 변수라면 getter setter를 사용해야 한다. 인스턴스를 생성하지 않아도 클래스에서 사용할 수 있다.

 

인스턴스 변수:

클래스 메서드 내부에서 인스턴스 변수를 사용할 수 없다.

멤버 변수는 인스턴수가 생성되어야 사용할 수 있다. static 메소드는 클래스가 선언하는 시점, 즉 프로그램이 메모리에 로드되는 시점에 사용가능하게 되지만 인스턴스는 컴파일러가 언제 실행시킬지 알 수 없기 때문에 멤버변수의 사용은 불가능하다.

 

9. 변수 scope

어떤 변수라도 범위와 수명이라는 개념이 있다.

변수의 범위를 크게 2가지로 나누면 지역변수와 전역변수로 나눌 수 있따.

 

변수의 범위를 왜 제한하는가?

프로그램의 성능, 안전성, 노동의 능률 등 요소를 고려한다. 지역변수라는 개념이 프로그래머들의 실수를 줄이고 프로그램의 품질을 높이는데 기여했다. 프로그래머는 하나의 함수를 만들 때 웬만하면 그 안에서 모든 것을 해결하는 게 좋다. 범위를 좁힐수록 프로그램 품질이 좋아진다.

 

지역변수(Local Variable):

자바에서 { }의 안에서 변수를 선언하면 지역변수로 인식한다.

처음부터 변수의 범위를 제대로 설계해두는 게 좋다.

 

전역변수(Global Variable):

객체지향프로그래밍인 자바에는 전역변수라는 개념이 없다. 

자바의 변수 범위는 크게 지역변수와 인스턴스 변수, static 변수 3rkwldlek.


- 자바 배열 기초

 

1.자바 배열

배열 선언과 초괴화:

 

예시1:

int [] arr1 = new arr[] {10,20,30};

예시2:

int [] arr2 = {10,20,30,40,50};

예시3:

int [] arr3;
arr3 = new int[] {10,20,30};

예시4:

int [] arr4 = new int [10];

예시5:

int [] arr5 = new int[2];
arr5[0] = 10;
arr5[1] = 20;

 

배열을 사용한 향상된 for문:

for(String item: list){
	System.out.println(item);
}

 

2. 객체 배열

자바는 객체지향프로그래밍으로 모든 프로그램을 객체를 중심으로 작성한다. 객체 역시 하나의 자료형이다.

클래스를 만들고 이 클래스를 담는 객체 배열을 만들 수 있다.

객체 배열에는 각 객체의 해시값이 할당된다. 참조변수를 통해 실제 인스턴스가 저장된 메모리에 가서 데이터를 가져온다.

public class Main {
    public static void main(String[] args) {
        myObject[] arrayObj = new myObject[3];
//        사용할 수 없다
//        arrayObj[0].id = 10;
        arrayObj[0] = new myObject(101, "first  array, John");
        arrayObj[1] = new myObject(102, "second array, Mary");
        arrayObj[2] = new myObject(103, "third  array, Smith");
        for (int i = 0; i < arrayObj.length; i++) {
            System.out.println("arrayObj["+i+"] = " + arrayObj[i]);
        }
        
        arrayObj[0].showInfo();
        arrayObj[1].showInfo();
        arrayObj[2].showInfo();
    }
}
class myObject{
    int id;
    String description;
    myObject(){
    }
    public myObject(int id, String description) {
        this.id = id;
        this.description = description;
    }
    public void showInfo(){
        System.out.println("(id) : " + id);
        System.out.println("(description) : " + description);
    }
}
[실행값]
arrayObj[0] = com.kay.myObject@12edcd21
arrayObj[1] = com.kay.myObject@34c45dca
arrayObj[2] = com.kay.myObject@52cc8049
(id) : 101
(description) : first  array, John
(id) : 102
(description) : second array, Mary
(id) : 103
(description) : third  array, Smith

 

3. 배열 복사

자바에서 new 키워드로 생성한 배열을 복사한다는 개념을 명확히 해야한다.

참조변수를 복사하는 것과 실제 배열의 값을 복사하는 것은 차이가 있다.

참조변수만 복사하는 것을 얕은 복사(shallow copy), 인스턴스까지 복사하는 것을 깊은 복사(deep copy)라고 한다.

 

System.arraycopy 함수:

System.arraycopy(원본, 복사 시작점, 복사본, 복사될 시작점, 복사가 끝나는점)

새로운 배열을 new를 사용해서 메모리를 받아 놓고 다음 함수를 사용해 복사할 수 있다.

 

객체 배열의 얕은복사(Shallow Copy):

실제 값을 복사하는 것이 아닌 참조를 복사하는 것

 

객체 배열의 깊은 복사(Deep Copy):

객체배열은 System.arraycopy를 사용해서 깊은 복사를 할 수 없다.

일일이 new 키워드를 사용해서 메모리를 할당하고 각각의 요소들을 복사한다.

 

자바에는 객체를 deep copy 해주는 내장된 유틸 클래스가 없다. 필요에 따라 직접 만들어야 한다.

 

4. ArrayList 클래스

자바는 객체지향 프로그래밍 언어로  class라는 복잡한 자료형을 중심으로 코딩 해야한다.

배열의 관점에서 class는 하나의 자료형이다.

배열에 새로운 객체를 추가하거나 기존 객체를 삭제해야할 때 이미 크기가 정해져 있는 배열의 경우 자유룝지 못하다. 그래서 자바에서는 배열을 더 유용하게 사용하기 위해서 JDK에 ArrayList 클래스를 제공하고 있다.

ArrayList 는 자바의 Collections Framework에 속한 클래스로 자바 프로그래머들은 자료구조를 개발하지 않아도 빠르고 안정된 응용프로그램을 개발할 수 있다.

 

리스트 자료형:

리스트는 일반적으로 연결리스트(Linked List)를 의미한다.

고정된 크기에 인덱스로 정렬된 배열보다는 탐색속도는 느리지만 쇠사슬처럼 연결되어 있어서 중간에 삽입하거나 삭제가 가능한 자료형이다.

 

ArrayList 는 탐색속도가 빠른 배열과 자료의 추가, 삭제가 쉬운 리스트의 장점을 결합한 클래스입니다.

 

성:

import java.util.ArrayList;

public class Main{
	public static void main(String[] args) {
    	ArrayList<class명> arrayList = new ArrayList<>();

- 객체지향 프로그래밍

1. 싱글톤 패턴 소개(Singleton)

싱글톤 패턴은 디자인패턴의 하나로 디자인 패턴은 일반적 객체지향프로그래밍의 설계이론이다. 싱글톤 패턴은 간단하면서도 쓸모가 많아서 실무에서도 사용됩니다.

 

굳이 여러 개의 인스턴스를 사용할 필요가 없는 경우 싱글톤으로 사용한다.

싱글톤은 new 라는 키워드를 사용할 원천 자체를 차단해버리므로, 클래스의 사용자는 훨씬 편하게 프로그래밍을 할 수 있다.

 

2. 자바 상속(Inheritance)

Extends:

class myClassA{
	private int id;
    pirvate String name;
    
    public myClassA(int id, String name) {
    	this.id = id;
        this.name = name;
    }

class myClassB extends myClassA {
	private String color;
    
    myClassB() {
    	super(100,"Test");
    }
}

클래스 B가 클래스 A를 상속한다. 상속받은 클래스 B의 인스턴스는 클래스 A의 모든 것을 물려받는다.

물론 이때도 상위 클래스 A에서 private 변수에는 접근할 수 없다.

 

B의 생성자에는 super()라는 새로운 것이 나왔다. 이는 this를 생각하면 쉽다.

super() 는 상속도에서 상위클래스의 생성자를 호출하는 키워드이다.

상위 클래스의 public과 protected에 접근하려면 super()를 사용한다.

 

예시 코드:

public class Main {
    public static void main(String[] args) {
    // write your code here
        System.out.println("\n-------- creating base base ---------");
        baseClass myDCs = new baseClass();
        myDCs.showData();
        baseClass myCs = new baseClass(101, "base Class");
        myCs.showData();
        System.out.println("\n-------- sub extends base ---------");
        subClass mySb = new subClass(201, "sub Class-1",
                "this is sub Class");
        mySb.showData();
        mySb.showProtected();
        System.out.println("\n-------- casting to base --------");
//        base class 로 형변환
        baseClass base1 = new subClass(301, "sub Class cast",
                "casting example");
        base1.showData();
        System.out.println("\n-------- casting to sub explilcit --------");
//        접근이 안됨
//        base1.showProtected();
//        명시적 캐스팅(형변환)
        subClass sub1 = (subClass) base1;
        sub1.showProtected();
    }
}
class baseClass{
    private int id;
    private String name;
    protected String text1;
    public baseClass(){
        id = 100;
        name = "null";
        System.out.println("[0- default base Class Created...]");
    }
    public baseClass(int id, String name) {
        this.id = id;
        this.name = name;
        this.text1 = "protected one";
        System.out.println("[1- base Class Created...]");
    }
    public void showData(){
        System.out.println("*id = " + id);
        System.out.println("*name = " + name);
    }
}
class subClass extends baseClass{
    private String description;
    public subClass(){
        super();
        description = "no contents";
    }
    public subClass(int id, String name, String description) {
        super(id, name);
        this.description = description;
        System.out.println("[2- sub Class Created...]");
    }
    public void showData(){
        super.showData();
        System.out.println("#description = " + this.description);
    }
    public void showProtected() {
        System.out.println("#super.text1 = " + super.text1);
    }
//    super의 private 에 접근 불가
//    public void tryPrivate(){
//        System.out.println("id = " + id);
//    }
}

base 클래스의 생성:

base 클래스는 일반형 클래스이다. 여러개의 sub 클래스로 확장을 위한 클래스이다. 최소한의 기능만 갖도록 설계했다. 이게 심화된 것이 abstract(추상화) 클래스이다.

Java의 API는 abstract 계층과 implementation 계층을 분리해서 만드는 방식을 사용한다. 자바가 100% 객체지향을 추구한다는 말은 Java API를 살펴보면 알 수 있다.

 

super와 this는 현재 클래스의 참조를 의미한다. 이는 어떤 대상이 아니라 인스턴스에 접근하는 해시키값이다.

 

형변환(Casting):

다형성을 다루기 위해 알아야 하는 부분이다.

 

base와 sub의 형변환에서 base는 sub의 부분집합이므로 base 데이터형에 sub 인스턴스를 선언하는 것은 가능하지만 반대는 불가능하다.

 

3. 메소드 오버라이딩(Method Overriding)

메소드 오버라이드:

상속받은 것들 중에 바꾸고 싶은 것들은 메소드 오버라이드를 한다.

base에서 확장한 메소드를 밀어버리고 새로 깔아버리는 의미이다. 기존의 코드를 포함시키고 싶을 때 super를 사용하면 base의 public과 protected를 사용할 수 있다.

 

애노테이션 표시 @Overrride 는 컴파일러에게 오버라이딩이라는 것을 적극적으로 알려준다. 오버로딩에 혼동할 수 있기 때문에 표시해주는게 좋다.

 

4. 가상 메소드(Virtual Method)

자바 가상 메서드에 대해서 먼저 메모리 구조에 눈을 떠야 한다. 자바는 프로그래머에게 직접 메모리 엑세스 권한을 주지 않는다.

프로그램을 크게 두 부분으로 나누면 코드와 데이터가 된다. 메소드는 코드이므로 메모리상의 코드세그먼트에 저장된다. 같은 클래스의 메소드는 한번만 저장하면 충분하고 인스턴스는 여러개가 될 수 있으니 여러개의 인스턴스는 하나의 메소드를 가르키는 구조다.

인스턴스와 메소드를 연결하기 위해서 클래스의 메타 데이터에는 가상 메서드 테이블이 있다. 이것으로 인스턴스를 메소드에 연결 시켜준다. (메타 데이터에 가상으로 메서드 테이블을 집어 넣어놓고 이걸로 연결한다.)

 

5. 다형성(polymorphism)

객체지향 프로그래밍의 중요한 원리로 하나의 코드를 다양한 자료형으로 실행하는 것을 뜻한다.

 

상속의 관계 HAS-A 와 IS-A:

상속이 다형성의 다양한 설계 유연성을 제공함에도 불구하고 무분별한 상속의 사용은 권장하지 않는다. 객체 지향 프로그래밍에서는 HAS-A와 IS-A 관계를 나눈다.

'직원은 월급을 받는다'와 같은 HAS-A와 '사람은 동물이다' 와 같은 IS-A가 있다.

기술적으로 보면 HAS-A는 멤버 변수의 관계고, IS-A는 생성자부터 메소드까지 포괄해서 확장하는 개념이다.

 

Instanceof 키워드:

다형성을 사용하다 보면 base 클래스 참조변수인데 어느 sub클래스의 인스턴스인지 기억이 안나거나 헷갈릴 수 있다.

이때 instanceof 키워드를 사용하여 boolean으로 검증할 수 있다.

포함관계를 생각하자. 다음과 같이 사용.

System.out.println(bc1 instanceof BaseA);
System.out.println(bc1 instanceof SubA);

6. 추상 클래스(Abstract Class)

추상 클래스는 인스턴스가 불가능한 클래스입니다. 객체 지향 프로그래밍의 설계 방식이다.

추상 클래스는 그 자체로는 사용이 안되고 sub 클래스가 상속을 받아 구현해야 한다.

추상 클래스와 비슷하게는 인터페이스가 있는데 둘 다 직접 구현이 아닌 sub 클래스를 통한 확장(extends)과 구현(implement)으로 사용한다는 공통점이 있다.

 

추상 클래스는 abstract 키워드를 붙여서 만든다. 이걸 붙이면 인스턴스를 만들 수 없다. 추상 클래스의 메소드는 abstract를 붙여서 구현하지 않아도 된다.

 

추상 클래스도 결국 인스턴스를 만들지 못할 뿐이지 메모리에 로드하는 것은 같으므로 static 메소드를 넣으면 인스턴스 없이도 메소드를 사용할 수 있다. 핵심 원리는 인스턴스를 생성할 수 없고, sub 클래스가 확장시 모든 메소드를 구현해야 한다는 점이다.

 

7. 템플릿 메소드(Template Method)

템플릿 메소드는 프로그래밍 작성 시에 활용할 수 있는 일종의 틀, 디자인 패턴의 하나로 프레임워크 등의 개발 시 유용하다. 이걸 사용하면 여러 개의 복잡한 함수를 하나의 기능으로 통합할 수 있다.

 

템플릿 메소드 예시:

    final public void run(){
        initializing();
        start();
        doSomething();
        end();
    }

 

8. final 키워드

final 키워드를 사용하면 상수를 만들 수 있다. 상수 선언시 초기화를 해줘야 하고 변수와 달리 변하지 않는 수이기 때문에 초기화 후 값을 대입하려고 하면 컴파일러가 오류를 발생시킵니다.

 

final 클래스:

클래스 앞에 final을 붙이면 그 클래스는 확장 불가능하게 된다.

 

final 메소드:

final 메소드는 오버라이드가 불가능하다. 더이상 메소드를 변형시킬 수 없다.

 

9. 인터페이스 기초

자바 인터페이스는 추상 클래스와 비슷하기도 하고 다르기도 하다. 

 

인터페이스 정의:

인터페이스는 키워드 interface 로 정의합니다.

인터페이스에서 정의한 변수는 컴파일러가 public static final 키워드를 붙여서 모두 상수가 된다. 즉 인터페이스에 변수는 정의할 수 없다.

또한 인터페이스에는 메소드 헤드만 선언할 수 있다(default 키워드 예외)

 

인터페이스와 추상 클래스의 가장 큰 차이는 클래스는 base가 하나만 가능하지만 인터페이스는 여러개를 구현할 수 있다. 즉 다중상속(여러 개의 인터페이스에서 상속받는 것)이 가능하다.

 

default 메소드:

인터페이스는 메소드의 body를 정의할 수 없지만 default 키워드를 사용하면 인터페이스에도 메소드를 정의할 수 있다.

 

public static 메소드:

static 메소드를 인터페이스에도 정의할 수 있다. static은 자바에서 영역을 넘나드는 모습을 볼 수 있다.

 

private 메소드:

인터페이스 내부에서 private 메소드는 default 메소드에서 호출 가능하고 private static 메소드는 static 메소드에서 호출할 수 있다.

 

10. 인터페이스 다중 구현

인터페이스 A1과 B1을 클래스가 구현한다. 당연히 두 인터페이스의 메소드를 오버라이드 해야하고 default 메소드가 겹치는 경우가 생기면 어느 쪽을 구현할지 알 수 없으므로 default 메소드도 구현해야 한다.(@Override)

 

11. 인터페이스 다중 상속

인터페이스 간의 다중 상속도 가능하다.

 

상속 및 구현을 동시에 사용:

하나의 sub 클래스에서 base 클래스를 상속하고 인터페이스를 다중으로 구현할 수 있다.

예시:

class Brotherhood extends A1 implements MyBrotehr

 

12. 내부 클래스

클래스 안에 클래스를 만드는 것을 내부 클래스라고 한다. 클래스 안에서 생성자를 사용해서 인스턴스를 생성할 수 있다.

클래스 안에 클래스를 넣는다는 것이 코드를 복잡하게 하므로 필요한 경우에만 사용하는 게 좋다.

 

Runnable 내부 클래스:

Runnable 내부 클래스는 클래스 안에서 메소드의 내부에 클래스를 정의해서 사용하는 것을 말한다. 지역 내부 클래스라고도 한다.

Runnable 인터페이스를 구현한 인스턴스를 반환받아서 사용할 수 있다.

예시:

public class Main {
    public static void main(String[] args) {
        BaseC bc = new BaseC();
        Runnable myRun = bc.getRun("(Test String)");
        myRun.run();
    }
}
class BaseC{
    Runnable getRun(String str1){
        class MyRunnable implements Runnable{
            @Override
            public void run() {
                System.out.println("-- Runnable Run : " + str1);
            }
        }
        return new MyRunnable();
    }
}

Runnable 익명 클래스:

클래스를 한 번 사용하기 위해서 새로운 클래스를 만들다 보면 효율성이 떨어진다. 그럴 때 익명 클래스를 사용할 수 있다. 

 

- java.lang.* 패키지 클래스

java.lang 패키지는 import하지 않아도 기본으로 사용가능한 패키지이다.

1. Object 클래스

모든 자바 앱의 클래스는 Object 클래스로부터 시작한다. Object 클래스가 모든 클래스의 조상 혹인 base라고 할 수 있다. 주로 운영체제와 JVM 사이의 관리를 위한 역할을 한다.

int, float과 같은 자바의 코어 데이터 타입은 객체는 아니라고 한다. 대신 기본 데이터 형을 클래스로 만들기 위해 Wrapper 클래스를 별도로 사용할 수 있다.

모든 클래스는 묵시적으로 extends Object를 한다.

 

2. String 클래스

String 클래스는 문자열 처리와 관련된 클래스이다.

 

3. Wrapper 클래스

기본 자료형을 클래스로 만들어주는 클래스이다. 모든것이 객체인 자바에서 객체로 주고받아야하는 경우 Object로 형변환할 수 있는 Integer클래스가 더 유용하다.

 

4. Class 클래스

자바의 소스코드 파일의 확장자는 .java 이다. 자바 컴파일러를 거치면 .class 파일이 된다.

Class 클래스는 클래스 자체에 대한 설명을 하는 메타 클래스라고 볼 수 있다.

쉽게 말하면 자바 프로그래밍 언어 자기 자신에 대한 정보를 파악하고 조작할 수 있는 클래스이다.

 

클래스 정보 가져오기:

java.lang.reflect 패키지의 클래스를 사용하여 Class 클래스로 선택한 클래스의 생성자, 메소드, 필드 정보를 가져올 수 있다.(접근 제한자가 public 인 경우만)

 

동적 로딩과 인스턴스 생성:

Class 의 정보를 가져올 수 있다는 것은 그 정보를 토대로 동적 로딩 방식으로 인스턴스를 생성할 수 있다는 의미이다.

동적 로딩으로 기존의 앱이 작동하는 상태에서 새로운 클래스를 개발하여 갖다 붙일 수 있기 때문에 유연한 방식으로 클래스를 개발할 수 있다.

 

- 컬렉션 프레임워크(Collection)(예제 따라 코딩해보며 학습)

1. 자바 제네릭(Generic)

제네릭 프로그래밍에서는 필요에 따라 자료형을 다양하게 바꿀 수 있다.

제네릭 클래스는 < > 다이아몬드 연산자를 사용한다. 이름은 별로 중요하지 않다. 예를 들어 <Sample> 을 했다면 컴파일 과정에서 Sample은 Object로 변형된다.

콜렉션 프레임워크에서 제네릭을 지원하지 않았다면 프로그래머가 각각 자료형을 따로 만들어야 하지만 제네릭을 사용하면 그냥 < > 다이아몬드에 넣기만 하면 되기 때문에 상당히 유용하다.

제네릭도 결국 다형성을 위해 사용하는 것이다.

 

2. 컬렉션 프레임워크

추상 데이터 타입, 쉽게 말하면 자바에서 사용하는 자료구조이다.

 

주요 클래스:

컬렉션스 자료구조에는 리스트 인터페이스를 구현한 ArrayList, LinkedList, Vector 등이 있고 Set을 구현한 HashSet, TreeSet이 있다. Map은 키와 값의 쌍으로 관리하는 자료구조이다.

 

3. ArrayList

ArrayList는 Array 배열과 List 리스트를 합친 자료구조이다. 자료구조의 기본 기능은 삽입, 검색, 삭제 세 가지이다.

 

ArrayList에 추가하는데 add 메소드가 필요하고 remove 는 삭제할 수 있다. 삭제할 대상을 찾기 위해서는 index 를 사용할 수 있다. index는 데이터가 붙어서 나열 되기 때문에 for 루프만으로도 검색 대상을 찾아낼 수 있다. add는 인덱스를 이용해서 요소를 삽입하는 역할을 한다.

 

4. LinkedList

Linked List는 컴퓨터 구조에 있어서 상당히 중요한 자료형이다. 

컬렉션 프레임워크로 Linked List를 구현하는 것은 쉽다.

head — data/next — data/next —의 구조라고 생각하면 쉽다. 

 

Linked List 추가는 add, addFirst 등이 있고 삭제는 remove, removeLast 등의 메소드가 있다.

 

5. Stack 클래스

LIFO(Last in first out)의 자료구조이다.

컬렉션 프레임워크의 Stack 클래스가 있다. push,pop,peek가 대표적인 메소드이다.

 

6. Queue 컬렉션

FIFO(First in first out)의 자료구조이다.

ArrayList나 LinkedList 등의 자료구조에 Queue 인터페이스를 함께 사용할 수 있다.Queue가 사용하는 대표적인 메소드는 순서대로 데이터를 집어 넣어주는 offfer, 가장 처음에 넣은 자료를 가져오는 poll, 맨 앞에 있는 자료를 보여주는 peek이 있다.

 

7. Iterator 구현

Iteration은 같은 프로시저를 여러번 반복하는 것을 의미한다.Ilterator는 인덱스가 없는 컬렉션에 사용한다. 즉 for문에서 i 인덱스를 돌릴 수 없는 Set 등에 사용할 수 있다. 물론 인덱스가 있는 컬렉션도 사용 가능하다.사용 예시:

Iterator<MyMemo> irt = memList.iterator();
System.out.println(irt.hasNext());
while(irt.hasNext()){
        MyMemo m1 = irt.next();
        m1.showMemo();
	}

8. HashSet 클래스

집합 SET의 자료구조이다. 집합은 중복된 요소를 갖지 않는 특성이 있다. Hash는 일반적으로 해시함수를 의미합니다. 해시는 랜덤한 길이의 데이터를 고정된 데이터 길이로 매핑하는 함수로 예를 들어 10Byte 문자열을 32비트 해시 값으로 매칭할 수 있다. 랜던함 길이의 데이터를 입력이라고 하면 고정된 길이의 데이터는 출력이다. 입력에 따라 출력이 나오고 출력을 안다고 입력을 알 수 없으므로 암호화에 사용되는 기술이다.

 

입력, 삭제 메소드 등이 있다.

 

9. TreeSet 클래스

TreeSet 클래스는 이진 검색 트리를 구현한 자료형이다. 역시 Set 인터페이스의 특징으로 중복이 제거된다. binary tree에 따른 자동정렬과 unique element가 이 자료구조의 특성이다. 이진 검색 방식은 데이터가 쌓여있어도 가운데를 퉁쳐서 검색하기 때문에 순차적인 자료구조에 비해 속도가 빠르다.

 

10. Comparable 인터페이스

Comparable 인터페이스는 콜렉션 프레임워크에서 비교와 정렬에 사용된다. 자료구조에 따라 사용법이 약간씩 차이가 있다.

 

Comparable 인터페이스의 compareTo 메소드를 오버라이드 하여 오름차순 비교를 할 수 있는데 정렬을 실행하는 것은 main 함수에서 Collections.sort()메소드로 할 수 있다. sort 메소드를 사용하는 기준으로 compareTo 메소드를 사용한다.

예시:

class MyMemo implements Comparable<MyMemo> {
	private int memoId;
    private String memoText;
    
    public Mymemo(int memoId, String memoText) {
    	this.memoId = memoId;
        this.memoText = memoText'
    }
    
    @Override
    public int compareTo(MyMemo o) {
    	return rhis.memoText.length() - o.memoText.length();
    }
}

 

Comparator 인터페이스

Comparable 인터페이스와  Comparator 인터페이스는 같은 비교 기능을 수행한다. 차이점은 오버라이드 메소드 compare에서 매개변수로 두 개의 객체를 받는 다는 것이다.

 

ArrayList 나 TreeSet 등의 컬렉션 프레임워크의 자료구조와 함께 String, Wrapper 클래스 그 밖에 객체를 비교하는데 사용할 수 있다.

 

11. HashMap

Map이란 영단어로 '지도'이다 지도는 실제 위치를 도면 위에 대응시키는 일이다.

HashMap은 해쉬 규칙에 따라 대응한 자료구조이다. 데이터는 key와 value 관계로 put 메소드로 입력할 수 있다.

HashMap도 Iterator 사용이 가능하고 containsKey 메소드는 key로 value를 찾을 수 있게 한다.

 

예시:

public class Main {
    public static void main(String[] args) {
        HashMap<Integer,String> hashMap = new HashMap<Integer, String>();
        hashMap.put(101, "HashMap Test");
        hashMap.put(123, "password");
        hashMap.put(203, "apartment number");
        hashMap.put(501, "address");
        hashMap.put(777, "slot machine");
        hashMap.put(999, "last number");
        System.out.println("[------- Iterating HashMap -------]");
        for(Map.Entry m1 : hashMap.entrySet()){
            System.out.println("m1.getKey() = " + m1.getKey() +
                            " | m1.getValue() = " + m1.getValue());
        }
        System.out.println("[------- Remove using key -------]");
        int keyToRemove = 123;
        if(hashMap.containsKey(keyToRemove)){
            hashMap.remove(keyToRemove);
        }
        System.out.println("[------- Iterator HashMap -------]");
        Iterator<Integer> irt = hashMap.keySet().iterator();
        while(irt.hasNext()){
            int key = irt.next();
            String str1 = hashMap.get(key);
            System.out.println("- " + str1);
        }
    }
}

 

12. TreeMap 클래스

TreeMap은 이진트리와 Map을 합친 것으로 입력과 동시에 오름차순 정렬을 한다. (Key값을 기준으로)

for 문으로 TreeMap의 요소를 가져올 때, Map.Entry객체로 가져오면 메소드를 사용해서 key와 value를 각각 가져올 수 있다. 항상 Map은 2개의 대응관계 key-value가 성립하므로 별도로 가져올 수 없다.

 

- 람다 표현식(Lamda)

람다식(Lamda Expression)은 자바에서 함수형 프로그래밍을 지원하는 기능이다. 변수에 어떤 값이 들어있는 것은 식별자로 변수의 메모리 주소에 접근할 수 있다는 뜻이다. 배열과 객체에서는 뜻이 더 확장된다.

함수에도 메모리 주소가 할당되어 있다. 람다식은 함수(실행 코드)의 주소를 사용하여 C언어 같은 함수 스타일의 프로그래밍을 작성할 수 있다.

람다식을 구현하기 위해서 우선 함수형 인터페이스를 만들고 추상 메소드를 만들어 준다. main 함수에서 람다식을 정의하고 함수형 인터페이스에서는 메소드가 하나만 허용된다.

 

람다식은 매개변수만 사용하여 만드는 함수이기 때문에 사용법이 직관적이다.

예시:

public class Main {
    public static void main(String[] args) {
        String str1 = "My String";
        String str2 = "plus String";
        StringCon con1 = (a, b) -> System.out.println("(" + a + "), (" + b + ")");
        con1.myString(str1, str2);
    }
}
interface StringCon{
    void myString(String str1, String str2);
}

 

- 예외 처리

자바에서 다루는 예외는 Throwable 클래스의 서브클래스로 Exception 클래스에서 처리한다.

예외를 언어의 오류 측면에서 보면 자바의 오류는 Compile 오류와 Runtime 오류가 있다. Compile 오류는 소스코드를 컴파일하는 단계에서 걸러지기 때문에 이를 위한 벼로의 코드를 만들 필요가 없다. 그런데 실행 도중 오류인 Runtime 오류는 돌발상황이 생기기 쉽다. 많은 실수와 오류가 언제나 일어날 수 있기 때문에 예외 처리가 필요하다.

 

처리 방식:

try {
	예외가 발생할 것 같은 블록
} catch (처리할 에러) {
	예외 발생 후의 처리 과정
}

finally:

finally는 try-catch문의 마지막에 항상 실행되는 코드를 넣는다.

try {
	예외가 발생할 것 같은 블록
} catch() {
	예외 발생 후의 처리 과정
} finally {
	무조건 실행되는 코드
}

 

AutoCloseable 인터페이스:

AutoCloaseable 인터페이스는 finally에서 매번 반환해야 하는 고정적인 자원들을 명시적으로 반환하지 않아도 알아서 반환하도록 구현한다. close 메소드에 실제로 자원을 반환하는 코드를 구현하면 된다. 

class AutoClose implements AutoCloseable{
    @Override
    public void close() throws Exception {
        System.out.println("자원을 클로즈 합니다");
    }
}

 

강제 예외(Throw):

throw로 강제 예외 발생을 시킨다. 예외 클래스의 base가 Throwable 이라는 것을 생각하면 에러나 오류가 나면 던진다는 아이디어가 있다. 정상적인 루틴에서는 예외를 처리할 수 없으니까 이것을 처리할 수 있는 클래스에게 던진다. new 키워드를 사용하는 것으로 보면 예외처리를 위해 Exception 인스턴스를 생성한다는 것을 알 수 있다.

        try(AutoClose obj = new AutoClose()){
            throw new Exception();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("[예외 표시]");
        }

 

예외처리 지연 throws 키워드

예외 처리를 하는 방법은 여러가지가 있다. 그 중 throws 키워드를 main 함수나 class 헤드에 정의하면 예외처리를 지연시킬 수 있다. 이 말은 어떤 클래스나 메소드는 예외처리 없이 사용할 수 없다. try-catch로 잡아줘야 한다. 다시 말하면 Exception 처리를 안하면 컴파일이 불가능한 것들이 있다. try-catch를 사용해서 정교한 오류처리를 하는 것이 바람직한 방법이다.

 

- 스트림(Stream)

데이터를 바이트로 추상화해서 다루는 스트림이다. 스트림은 파일이 될 수도 키보드 모니터 등 어떤 입출력장치가 될 수도 있다. 또한 데이터 자체를 스트림으로 바꿀 수도 있다.

 

1. File 클래스

프로그래밍에서 파일은 여러가지 의미를 가지고 있다. 우리가 알고 있는 내 문서 폴더의 '파일'이 이 파일입니다만 입출력 장치를 비롯해 여러가지를 추상화 시키는데 파일을 사용한다.

 

예제 File 클래스 테스트

File 클래는 IOException과 함께 사용한다.

import java.io.File;
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException {
        FileEx fe = new FileEx("aFile.txt");
        File myFile = fe.getMyFile();
        
        System.out.println("-- is File? " + myFile.isFile());
        System.out.println("-- is Directory? " + myFile.isDirectory());
        System.out.println("-- Name? " + myFile.getName());
        System.out.println("-- AbsolutePath : " + myFile.getAbsolutePath());
        System.out.println("-- Path : " + myFile.getPath());
        myFile.deleteOnExit();
    }
}
class FileEx{
    File myFile;
    FileEx(String fileName) throws IOException {
        myFile = new File(fileName);
        myFile.createNewFile();
    }
    public File getMyFile() {
        return myFile;
    }
}

aFile.txt는 delete 호출로 삭제할 수 있다.

 

자바 RandomAccessFile

RandomAccessFile 클래스는 파일 포인터로 임의의 장소에 접근이 가능하다.

RandomAccessFile의 경우 매개변수를 넘겨주면 자동으로 파일을 생성한다. 여기서 인스턴스에는 파일포인터가 들어있다. 파일포인터를 바이트 단위로 이동시켜서 파일에 접근할 수 있다. writeInt 메소드는 텍스트가 아닌 실제 숫자값을 파일에 쓴다. 실제 값은 메모장에서 열어보면 깨진 글자처럼 보인다.

예제:

import java.io.IOException;
import java.io.RandomAccessFile;
public class Main {
    public static void main(String[] args) throws IOException {
        RandomAccessFile randomF = new RandomAccessFile("a.txt", "rw");
        System.out.println("4바이트 정수값 입력");
        randomF.writeInt(777);
        System.out.println("File Pointer : " +  randomF.getFilePointer());
        System.out.println("seek 메소드로 초기화");
        randomF.seek(0);
        System.out.println("File Pointer : " +  randomF.getFilePointer());
        int myInt = randomF.readInt();
        System.out.println("* - 저장된 수를 가져온다 : " + myInt);
        System.out.println("File Pointer : " +  randomF.getFilePointer());
        System.out.println("* - UTF 사용하기");
        randomF.writeUTF("UTF Hello World!");
        System.out.println("File Pointer : " +  randomF.getFilePointer());
        System.out.println("* - 파일 포인터를 옮겨서 출력한다");
        randomF.seek(4);
        String myUTF = randomF.readUTF();
        System.out.println(myUTF);
    }
}

 

2. 자바 스트림

여기서 말하는 스트림은 콜렉션 프레임워크와 함께 사용하는 스트림이다.

예제:

import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        int[] myArray = {2,4,6,8,10};
        System.out.println("[--------- Stream foreach ---------]");
        Arrays.stream(myArray).forEach(a -> System.out.print(a + ", "));
        System.out.println("\n[-------- Stream sum, count -------]");
        System.out.println("-- sum   : " + Arrays.stream(myArray).sum());
        System.out.println("-- count : " + Arrays.stream(myArray).count());
        System.out.println("[-------- Stream max, min -------]");
        System.out.println("-- max : " + Arrays.stream(myArray).max());
        System.out.println("-- min : " + Arrays.stream(myArray).min());
    }
}

forEach(), sum(), count() 메소드 등을 배열과 함께 사용할 수 있다. 원래 문법대로 같은 기능을 구현하려면 더 긴 코드가 필요하지만 스트림 기능을 활용하면 한번에 해결된다. 콜렉션의 Stream은 유용하다. 스트림의 메소드 체이닝(연달아 메소드 호출)을 사용하면 원래는 여러 줄이 걸리는 복잡한 메소드도 한줄에 연결시킬 수 있다.

예제:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Main {
    public static void main(String[] args) {
        List<String> myList = new ArrayList<String>();
        myList.add("apple");
        myList.add("kiwi");
        myList.add("mango");
        myList.add("banana");
        myList.add("watermelon");
        myList.add("lime");
        myList.add("orange");
        myList.add("strawberry");
        Stream<String> myStream = myList.stream();
        myStream.forEach(str -> System.out.print(str + ", "));
        System.out.println();
        myList.stream().sorted().forEach(str-> System.out.println(str));
    }
}

 

3. FileWrite 클래스

FileWriter 클래서는 문자열을 파일에 출력할 때 사용한다. 문자열을 파일에 출력한다는 말은 Text를 Text 파일에 저장한다는 말과 같다.

 

FileWrite로 그대로 쓰는 방식과 BufferedWriter로 출력내용을 버퍼에 받아서 쓰는 방식

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Main {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("file_writer.txt");
        fw.write("Hello this is file writer");
        fw.close();
        BufferedWriter bw = new BufferedWriter(
                new FileWriter("D:\\Programming\\JavaPRJ\\smoothiejava\\b.txt"));
        bw.write("Hello\n");
        bw.write("using Buffered Writer");
        bw.write("777");
        bw.close();
    }
}

new FileWriter도 파일을 생성한다. 기존 파일이 있다면 덮어쓰기 한다. close 메소드를 꼭 사용해야 한다.

BufferedWriter는 출력기로써 받는 매개변수 객체 종류에 따라 콘솔에 출력할 수 있다.

 

FileReader로 읽어오기(BufferdReader)

파일을 쓸 수 있다면 읽을 수도 있다. while 문에서 null이 될 때까지 readline 메소드를 사용해서 한줄씩 읽어서 출력한다. 

import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("b.txt"));
        String str;
        while((str = br.readLine()) != null){
            System.out.println(str);
        }
        br.close();
    }
}

 

텍스트 파일 복사하기

FileWriter와 FileReader는 파일에서 읽고 쓰는 기능을 한다.

복사도 가능하다. 원본 파일을 읽어서 복사 파일에 쓰면 된다.

import java.io.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(
                new FileWriter("copied_b.txt"));
        BufferedReader br = new BufferedReader(
                new FileReader("b.txt"));
        String str;
        while((str = br.readLine()) != null){
            System.out.println(str);
            bw.write(str + "\n");
        }
        br.close();
        bw.close();
    }
}

 

4. 자바 입출력 스트림(I/O Stream)

System.in 입력받기

System.in은 표준 입력 객체이다. 자바를 처음 배우기 시작한 날 부터 System.out 객체의 println메소드를 사용해왔다. System.in과 out은 표준 입출력 대칭하는 객체이다. 이들은 인스턴스가 필요없는 static 객체이다. 지금까지 main 함수에서 인스턴스 생성하지 않고 사용해왔다.

 

Scanner 클래스

Scanner 클래스는 세 개의 소스에서 입력을 받을 수 있다. 스트링, 파일, System.in(표준입력)에서 받을 수 있다.

import java.io.*;
import java.util.Scanner;
public class Main {
    public static void main(String[] args) throws FileNotFoundException {
//        다양한 입력소스에서 받을 수 있는 Scanner 클래스
//        1. String source
        System.out.println("[-------- String Source ---------]");
        Scanner sc = new Scanner("Hello Scanner!");
        String myStr1 = sc.nextLine();
        System.out.println("-> 1번 소스 String : " + myStr1);
        sc.reset();
        sc.close();
//        2. File 에서 받음
        System.out.println("[-------- File Source ---------]");
        File myFile = new File("b.txt");
        if(myFile.exists()){
            Scanner sc1 = new Scanner(myFile);
            while(sc1.hasNext()){
                System.out.println(sc1.nextLine());
            }
            sc1.reset();
            sc1.close();
        }
//        3. System.in 에서 받음
        System.out.println("[-------- Standard Input Source ---------]");
        System.out.println("-> 이름을 입력하시오.");
        Scanner sc2 = new Scanner(System.in);
        String text = sc2.nextLine();
        System.out.println("당신의 이름은 " + text + " 입니다");
    }
}

 

FileInputStream 사용해서 입력받기

FileInputStream은 스트림으로 데이터를 입력받는다. 스트림이란 추상적인 데이터의 관리를 하기 위한 방식이다. while 문에서 보면 바이트 단위로 읽어온다. 파일에 저장된 순서대로 처음부터 끝까지(바이트 -1을 만날 때까지) 출력한다.

 

배열 단위로 입력 받기

바이트 단위로 가져오면 아무래도 시간이 오래 걸린다. 속도를 위해서 배열에 한꺼번에 담는 것이 좋다. 배열에 담아놓고 사용한다는 부분에서 보조스트림인 버퍼 방식과도 유사하지만 다른 방식이다.

 

FileOutputStream 사용하기

Input 스트림의 반대는 OutputStream이다. 출력이라는 개념은 파일에 데이터를 저장한다. 파일을 쓴다라고 생각하면 된다.

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
    public static void main(String[] args) {
        try {
            FileOutputStream fos = new FileOutputStream("myOutput.txt");
            for (int i = 65; i < 91; i++) {
                fos.write(i);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

FileOutputStream도 배열을 사용하거나 보조스트림을 사용해 출력할 수 있다.

 

5. 자바 직렬화

자바의 직렬화는 인스턴스를 다루는 기법이다. 객체와 바이트 스트림 간의 변환을 직렬화라고 한다.

인스턴스라 함은 객체를 메모리에 생성하여 사용하는 것을 말한다. 그런데 런타임 도중에 상태를 유지하던 인스턴스가 프로그램이 종료하면 메모리에서 해제되기 때문에 보존이 안된다.

자바 직렬화 기능을 사용하면 객체를 사용하는 확장성이 늘어난다. 프로그램의 종료시에도 객체의 상태를 저장할 수 있고 네트워크 전송하기 적합한 바이트 스트림으로 변환할 수 있다.

 

- 자바 API

1. 자바 랜덤함수(Random)

자바는 모든 라이브러리가 클래스로 만들어져 있다.

 

java.lang.Math 클래스

Math 클래스는 java.lang 패키지에 포함된다. java.lang 패키지는 모든 클래스의 상위 클래스인 java.lang.Object부터 자바 프로그래밍의 기본이 되는 각종 클래스들을 포함한다.

import 문을 딱히 명시되지 않아도 java.lang 패키지는 import 되어있다.

 

random함수는 의사 난수를 생성한다. static으로 인스턴스 없이 사용할 수 있다. 반환값은 double형 0.0 ~ 0.1의 범위에서 생성한 난수이다.

 

java.util.Random 클래스

이쪽은 java.lang.Math보다 난수생성에 향상된 Random 클래스이다. 난수의 결과를 정수형 int, float, boolean 형 등 다양한 자료형으로 리턴할 수 있다. 범위를 지정하려면 nextInt 에 인수로 범위(bound)를 전달합니다. (0부터9까지 난수생성)

 

ThreadLocalRandom 클래스

java.util.concurrent.ThreadRandom은 스레드 별로 독립적인 랜덤 클래스이다. 아무래도 스레드가 각자 격리된 상태에서 사용하면 Random 클래스보다 고품질의 난수가 생성될 것을 기대할 수 있다.

 

Random 클래스에는 이외에도 다양한 메소드와 멤버변수가 있다.

 

*참고자료의 내용은 여기까지이다. 추후 Java를 이용해 코딩 공부를 해나가며 공부한 내용을 추가로 정리하겠다.

728x90