조건문
조건문은 연산자를 통해 boolean을 도출해내고 참/거짓으로 실행 여부를 결정한다.
- if
if( boolean1 ) {
// 만약 boolean1을 연산한 결과가 true이면 중괄호 내용을 실행해라...
중괄호 내용;
} else if( boolean2 ) {
// 만약 boolean1이 false이고
// boolean2를 연산한 결과가 true이면 중괄호 내용을 실행해라...
중괄호 내용;
} else {
// 위의 모든 조건이 false일 경우 실행해라...
중괄호 내용;
}
- switch
- 비교 대상 변수가 주어졌을 때, case와 비교하여 같은 경우 처리 문장이 실행된다.
- break가 없을 경우 다음 case가 이어서 실행되기 때문에 break를 작성해주어야 한다.
- default는 case에 해당하는 값이 없을 경우 실행된다.
- 비교 대상 변수는 Long을 제외한 기본 자료형과 Enum과 몇몇 참조 자료형으로 사용이 가능하다
switch(비교대상변수){
case 점검값1:
처리문장;
break;
case 점검값2:
처리문장;
break;
default:
처리문장;
break;
}
반복문
반복문은 연산자를 통해 도출된 결과 값이 조건이 만족할 때까지 반복하는 문장이다.
- for
for( 초기화 값 ; 종료조건 ; 증감 값 ) {
반복 문장;
}
- while
while( 종료조건 ) {
// for문과 달리 while문에는 숫자 종료조건에 대한 증감 여부가 없으니
// 이를 명시하지 않고 사용할 경우 무한루프에 걸릴 수 있다.
반복 문장;
// 조건문++;
}
do {
// do가 적혀있듯 조건이 어떻게 되든 먼저 실행부터 하는 반복문
반복 문장;
} while ( 조건문 )
조건문과 반복문에 사용할 수 있는 명령어
- continue : 반복문에서 사용 가능하다.
→ 문장을 건너뛰어라 즉, continue가 실행되었을 경우 뒤에 있는 문장은 건너 뛰고 조건 점검 부분으로 다시 가라 - break : 조건문과 반복문에서 사용 가능하다
→ 모든 행위를 중지하고 해당 조건 또는 반복문에서 나가라
배열
여러 개의 데이터를 저장하기 위해 사용하는 참조 자료형 객체이다.
- 배열의 선언은 앞에서 배웠던 클래스의 인스턴스화와 같이 객체를 생성하기 위해 new를 통해 선언해주면 된다.
- 배열의 길이 값은 0부터 시작한다.
ex) new int[3]; → 3개의 방을 가진 객체를 만들었다는 의미이고, 이 객체의 첫번째 방은 int[0] 이다. - 배열은 해당되는 타입의 데이터만 넣을 수 있다.
- 배열을 사용하기 위해서는 반드시 배열의 길이를 지정해 주어야 한다.
그렇기에 이후 추가, 수정은 불가능하다는 단점이 존재한다
이것을 보완하기 위해 자바에서는 Collection이 존재한다.
배열의 선언 방법
1. int[] number = new int[1];
2. int[] number = { 1, 2, 3, 4, 5 };
String[] str = new String[5];
System.out.println(str[0]);
// 출력 : null
// 초기화를 하지 않을경우 기본값이 저장되어 있다.
System.out.println(str);
// 출력 : [Ljava.lang.String;@6f75e721
// [ = 배열
// L = 해당 배열은 참조 자료형
// java.lang.String = 배열의 타입
// @6f75e721 = 객체의 고유번호, 주소값 hash코드로서 16진수로 되어 있다.
System.out.println(str.hashCode());
// str 객체의 주소값
// Hashcode : 1869997857
str[0] = "test";
System.out.println(str[0].hashCode());
// str 객체 [0] 번째의 주소값
// Hashcode : 3556498
- 타입별 명칭
- byteArray : [B
- shortArray : [S
- intArray : [I
- longArray : [J
- floatArray : [F
- doubleArray : [D
- charArray : [C
- booleanArray : [Z
2차원 배열
- 일반 배열은 1차원 이였고, 2차원 이상의 배열을 선언할 수 있다.
- 다만, 2차원 이상의 배열은 만들 때는 이해가 가도 이후 다시 보거나 타인이 보게 될 경우 효율이 떨어진다. 고로 2차원 이상의 배열은 지양하는 것이 좋다.
2차원 배열의 선언방법
1. int [][]number = new int[3][4];
2. int [][]number = {{1,2,3}{4,5,6}};
// 1차원 배열은 number[0] 일 경우
// 바로 int 값이지만
// 2차원 배열에서는 아래와 같다
// number[0] = int 배열
// number[0][0] = int 값
- 배열의 길이를 알아내자
1차원 배열 객체의 길이 값 확인
number.length; // 3
2차원 배열 객체의 길이 값은 아래와 같이 확인할 수 있다.
number.length; // 3
number[0].length; // 4
배열을 위한 for 루프
- for문을 이용해 배열 객체의 데이터를 간편하게 가져올 수 있다.
- 단점으로는 for 루프의 index 위치를 알 수 없기에 index 번호가 필요할 경우 따로 선언을 해 주어야 한다.
기본 구조
for( 타입이름 임시변수명 : 반복대상객체 ) {
// 중간내용
}
int num = { 1, 2, 3, 4, 5};
int index = 1;
for( int data : num ){
System.out.println( index + "번째 = " + num );
index++;
}
// 1번째 1
// 2번째 2
// 3번째 3
// 4번째 4
// 5번째 5
참조자료형
참조 자료형이란?
자바에는 기본 자료형과 참조 자료형이 있다. 기본 자료형은 앞에서 기록한 8가지의 타입이고 이 외에는 모두 참조 자료형에 속한다.
- 참조 자료형은 new 선언으로 객체 형태로 생성이 가능하다.
- 예외로는 String은 new를 사용하지 않고 사용이 가능하다
DTO 클래스
자바 패턴 중의 하나인 DTO 클래스는 어떤 속성을 갖는 클래스를 만들고, 그 속성들을 기본적으로 [ get, set, 생성자 ] 등을 이용해 쉽게 전달하기 위해서 생성한다.
public class MemberDTO{
private String name;
private String phone;
private String email;
public MemberDTO(){};
public MemberDTO(String name){
// 이름만 알고 있을때
this.name = name;
}
public MemberDTO(String name, String phone){
// 이름과 전화번호를 알고 있을때
this.name = name;
this.phone = phone;
}
public MemberDTO(String name, String phone, String email){
// 모든 정보를 알고 있을때
this.name = name;
this.phone = phone;
this.email = email;
}
}
- this를 사용하는 파라미터로 들어온 변수와 해당 객체에서 사용하는 변수의 명칭이 동일할 때 구분하기 위해서 이다
메소드 오버로딩(Overloading)
Overloading이란?
클래스의 생성자 매개변수에 따라 다양한 종류의 생성자를 작성할 수 있었던 것과 같다. 메소드 오버로딩 또한 메소드의 매개 변수의 종류와 개수, 순서에 따라 같은 이름으로 생성이 가능하다
String name;
int age;
public void print(){
}
public void print(String name){
}
public void print(int age){
}
public void print(String name, int age){
}
public void print(int age, String name){
}
- 그렇다면 이러한 메소드 오버로딩을 왜 사용할까?
같은 기능(역할)을 하는 메소드는 같은 메소드 이름을 가져야 한다
왜냐? 타입의 차이로 인해 매번 다른 이름으로 메소드를 실행해야 한다면 매우 비효율적일 것이다.
메모리 영역
메모리 영역에는 Code, Data(Static), Heap, Stack 의 4가지 영역이 존재한다
// Data 영역에 할당하고 클래스의 인스턴스화 할 경우 Heap 영역에 할당됨
public class DataExample{
//Instance Variable - Heap 영역에 할당될시 Stack영역에 참조값으로 할당
String instanceVariable = "객체의 전역변수";
//Class Filed(Variable) - Data 영역에 할당
static int staticVariable = 100;
static String classVariable = "클래스의 전역변수";
//Class Method - Data 영역에 할당
static void printMinMax(){
System.out.println(staticVariable);
System.out.println("instanceVariable은 static 메소드에서 사용 할 수 없다.");
}
// Instance Method - 메소드 호출시 매개변수와 지역 변수를 Stack 영역에 할당
void stackExample(int parameterVariable){ // ParameterVariable - Stack 영역에 할당
int localVariable = 10; // LocalVariable - Stack 영역에 할당
}
}
- [Code] 영역
- 실행할 프로그램의 코드가 저장되는 영역
- 프로그램 명령이 위치하는 곳으로서 기계어로 제어되는 메모리 영역
- [Data(Static)] 영역
- 프로그램의 시작과 함께 할당되며, 프로그램이 종료되면 소멸한다
- Class 또한 Static 영역에 소속되어 있고 클래스가 static 메모리에 할당 될 때 static 변수, 메소드도 자동으로 생성된다.
- 정적인 영역에 선언되어 있어 new 선언 없이 사용이 가능하며 공통으로 사용할 수 있다.
- GC ( Garbage Collector ) 에 관리되지 않는다
- Class Method, Class Field(Variable) 두 가지가 static 영역에 해당된다
- [Heap] 영역
- '사용자에 의해 관리되는 영역'
- new 를 통해 객체를 생성하여 Heap영역의 메모리를 할당 받아 사용한다
- 각각의 인스턴스로 생성된 객체들은 독립적인 값을 유지한다
- GC ( Garbage Collector )에 관리되어 사용되지 않는 객체는 자동으로 제거한다
- 선입선출(FIFO, First-In First-Out) 방식으로 가장 먼저 들어온 데이터가 가장 먼저 인출
- Heap 영역의 인스턴스들은 Stack의 지역변수가 참조하고 있는 실제의 값들을 가지고 있다.
- [Stack] 영역
- 함수의 호출시 생성되는 지역변수와 매개변수가 저장되는 영역
- 함수의 호출과 함께 할당되며 함수의 종료와 함께 소멸
- 후입선출(LIFO, Last-IN Fist-Out) 방식으로 가장 나중에 들어온 데이터가 가장 먼저 인출
-
static void m1(int number) { m2(++number); System.out.println("m1 = " + number); } static void m2(int number) { m3(++number); System.out.println("m2 = " + number); } static void m3(int number) { ++number; System.out.println("m3 = " + number); } public static void main(String[] args){ int number = 1; m1(number); System.out.println("m0 = " + number); } // 출력 m3 = 4; m2 = 3; m1 = 2; m0 = 1;
Static Block
딱 한번만 수행되는 코드를 선언하는 블록으로서 객체가 생성되기 전에 한 번만 호출되고, 그 이후에는 호출하려 해도 호출 할 수 없으며 클래스 내에서만 생성될 수 있다.
즉, 생성자 보다도 우선적으로 호출된다.
public class StaticBlock{
static int data = 3;
static {
System.out.println("1번째 실행됨");
data =1;
}
public StaticBlock(){
System.out.println("2번째 실행됨");
}
}
기본 자료형과 참조 자료형의 매개변수로서의 차이점
기본 자료형은 매개변수로 넘길 경우 해당 데이터의 값을 넘겨 새로운 변수값이 해당 메소드에서 호출에서 시작되고 메소드가 끝남에 따라 제거된다. 하지만 참조 자료형을 매개변수로 넘길 경우 해당 참조 주소값으로 넘기기 때문에 해당 메소드에서 참조 자료형의 변화가 있을 경우 참조 자료형의 참조값의 실제 값은 대입되어 최신화 되고 없어지지 않는다.
public class testClass{
static void 기본자료형(int number) {
number = 1;
System.out.println(number); // 1번째 출력
}
static void 참조자료형(MemberDTO dto) {
dto.setNumber(3);
System.out.println(dto.getNumber()); // 3번째 출력
}
public static void main(String[] args) {
int number = 5; // 기본 값 number = 5 설정
기본자료형(number);
System.out.println(number); // 2번째 출력
MemberDTO dto = new MemberDTO();
dto.setNumber(number);
참조자료형(dto);
System.out.println(dto.getNumber()); // 4번째 출력
}
}
class MemberDTO {
private int number;
private int age;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
/**
출력
1
5
3
3
*/
패키지(package)
자바에서는 패키지를 클래스들을 구분 짓는 폴더와 비슷한 개념으로서 트리 형태의 구조로 나타낼 수 있다.
- 패키지는 소스의 가장 첫 줄에 위치해야 한다
- 패키지 선언은 소스 하나에 하나만 있어야 한다
- 패키지 이름과 위치한 폴더 이름이 같아야만 한다
- 패키지 이름은 java로 시작하면 안된다.
- 패키지 이름은 모두 소문자로 지정해야 한다
- 예약어를 사용하면 안된다
package org.example;
public class test{
public test(){
}
public static void main(String[] args){
}
}
임폴트(import)
다른 패키지에 위치한 클래스를 인스턴스화 하기 위해 import를 하는데, 패키지의 위치와 파일의 이름을 알려준다. 호출하는 클래스와 같은 패키지에 위치한 클래스는 import할 필요가 없다
- 해당 패키지 내의 모든 클래스를 이용하고자 할 때는
import org.example.donwn.*;
즉 * 별표를 사용하면 모든 클래스를 import 하는 것을 뜻한다.
주의사항 : *는 해당 패키지만을 뜻하며 그 하위 패키지의 클래스는 import 하지 않는다 - import static을 사용할 경우 해당 클래스의 static 변수 또는 메소드를 객체 생성 없이 사용이 가능하다
- import한 클래스의 변수 또는 메소드가 중복일 경우 호출한 클래스의 변수 메소드명이 우선시 된다.
package org.example;
import org.example.down.DownClass;
import static org.example.down.DownClass.MAX;
public class Test{
public Test(){
}
public static void main(String[] args){
DownClass dp = new DownClass();
System.out.println(MAX);
}
}
package org.example.down;
public class DownClass{
public final static int MAX = 100;
public DownPackage(){
}
}
접근 제어자 ( Access modifier )
접근 제어자란 무엇일까? 말 그대로 접근을 명시한 제어자에 따라 제어하는 것인데 왜 사용하는 것일까? 그 이유는 용도에 따른 이유가 있겠다. 나는 이 클래스를 메소드를 변수를 이 클래스에서 또는 다른 클래스에서 또는 다른 패키지에서 사용하지 못하도록 하고 싶을때 사용한다.
좀 더 큰 이유라면 보안성에 있겠다. 이 변수를 다른 클래스에서는 보지도 접근하지도 못하도록 지정하여 이 데이터가 어떤 데이터인지? 데이터의 값은 무엇인지 알 수 없도록 할 수 있다.
접근제어자의 종류 4가지
// 누구나 접근 가능
public void test(){}
// 같은 패키지 내에 있거나 "상속"받은 경우에만 접근 가능
protected void test(){}
// 아무것도 적지 않으면 default로 설정되며
// 같은 패키지 내에서만 접근 가능
(default) void test(){}
// 해당 클래스에서만 접근 가능
private void test(){}
클래스의 경우 해당 파일 이름과 클래스 이름이 같아야 한다.
ex) [Example.java](http://example.java) ↔ public class Example{}
위와 같은 조건이 있는 상태에서 파일 안에 여러 개의 클래스가 존재할 수 있도록 할 수 있다.
다만 조건이 있다.
사용 가능(O)
public class Example{}
class Example2{}
사용 가능(O)
class Example{}
class Example2{}
사용 불가능(X)
class Example{}
public class Example2{}
사용 불가능(X)
public class Example{}
public class Example2{}
Example2가 public이 될 경우 에러가 발생한다.
파일 안에 여러개의 class가 있을 경우 접근제어자가 public인 클래스는 파일과 동일한 이름을 가지고 있어야 한다.
상속 ( extends )
상속이란 Parent Class와 Child Class가 있을 때, Parent Class의 기능을 확장 즉 부모 클래스가 가지고 있는 기능을 자식 클래스가 사용할 수 있도록 하는 것을 뜻한다.
그렇다면 왜 상속을 하는가?
부모에게 만들어져 있는 기능을 자식이 상속 받아 사용하면 자식 입장에서는 굳이 따로 배우지(만들지) 않아도 활용이 가능하기 때문이다
- 위에서 배운 접근 제어자에서 상속 받은 경우에만 사용이 가능하다 적은 부분이 있다.
이와 같이 상속이 가능한 접근 제어자가 있는데, public과 protected로 선언된 클래스만이 상속이 가능하다. - 자바는 다중 상속이 불가능하기 때문에 단일 상속을 사용한다.
그렇다면 상속 받지 못한 많은 기능들은 어떻게 추가할까?
이는 인터페이스(interface)를 사용해 보완을 한다
class Parent{
public Parent(){
System.out.println("parent()");
}
public void printName(){
System.out.println("printName()");
}
}
class Child extends Parent{
public Child(){
System.out.println("child");
}
}
public class Test{
public static void main(String[] args){
Child child = new Child();
child.printname();
}
}
- super() 란?
부모 클래스의 기능을 확장 받은 자식 클래스에서는 super();라는 예약어가 자동으로 생성된다. 이것은 부모 클래스의 기본 생성자를 실행한다 라는 뜻으로서 만약, 부모 클래스에 기본 생성자가 없고 매개변수가 있는 생성자만 있다면 super();는 에러가 발생하기 때문에 super(매개변수); 를 명시해 주어야 한다. - super()는 반드시 자식 클래스의 "생성자에서" "가장 첫줄에" 선언되어야 한다.
- 위와 같은 이유 때문에라도 기본 생성자는 반드시 생성하는 것을 추천한다
class Parent {
int number = 10;
// public Parent() {
// System.out.println("parent()");
// }
public Parent(String name) {
System.out.println(name);
}
public void printName() {
System.out.println("4.printName()");
}
}
class Child extends Parent {
public Child() {
super("1.parentSuper");
System.out.println("2.childMethod");
System.out.println("3." + super.number);
}
}
public class TestClass{
public static void main(String[] args) {
// 객체 생성시 기본생성자를 바로 호출한다
// 1, 2, 3 실행됨
Child child = new Child();
// 기본 생성자에서 작성된 명령이 수행된 후 실행된다
// 4 실행됨
child.printName();
}
}
/**
출력
1.parentSuper
2.childMethod
3.10
4.printName()
*/
메소드 오버라이딩
위에서 메소드 오버로딩을 배웠다.
- 잠시 복습하고 가자면 현재 클래스 내에 메소드를 선언할 때 매개변수 타입 또는 매개변수 순서의 다양한 조합으로 동일한 기능을 수행하는 메소드를 다양한 매개변수를 받아 사용하도록 만들 때, 동일한 이름으로 생성해 사용 할 수 있게 하는 매우 효율적인 방법이었다.
그렇다면 메소드 오버라이딩이란?
- 부모 클래스의 메소드 시그니처(접근 제어자, 리턴 타입, 메소드 이름, 매개 변수 타입 및 개수 등)를 복제해서 자식 클래스에서 새로운 것을 만들어 내는 것
- 자식 클래스에서 부모 클래스에 있는 메소드와 동일하게 선언하는 것을 의미한다
- 접근 제어자, 리턴 타입, 메소드 이름, 매개 변수 타입 및 개수가 모두 동일해야만 메소드 오버라이딩이라 부른다
- 사전적 의미로는 "보다 더 중요한", "최우선시 되는" 이라는 뜻이 있듯이 메소드 오버라이딩은 부모 클래스와 자식 클래스에 동일한 메소드가 있을 때 호출시 과연 어떤 메소드를 사용할 것인가?에 대한 이슈가 있을 때, 자식 클래스의 메소드가 우선이 되는 것을 뜻한다.
class Parent {
int number = 10;
public Parent() {
System.out.println("parent()");
}
public void printName() {
System.out.println("ParentPrintName()");
}
}
class Child extends Parent {
public Child() {
super();
System.out.println("child()");
}
public void printName() {
System.out.println("ChildPrintName()");
}
}
public class TestClass{
public static void main(String[] args) {
Child child = new Child();
child.printName();
}
}
/**
출력
parent()
child()
ChildPrintName()
*/
참조 자료형의 형변환이 가능할까?
형변환이란 무엇일까?
그릇에 물을 가득 담아 놓았다.
작은 그릇에서 큰 그릇으로 물을 넣는 것은 큰 그릇 안에 모두 들어갈 수 있어 문제가 없다
하지만 큰 그릇에서 작은 그릇으로 넘어가는 것은 담지 못하고 흘러 넘친다
이와 같은 현상과 동일하게 Child 객체를 Parent 객체로 형 변환하는 것은 가능하지만 Parent 객체에서 Child 객체로 형 변환 하는 것은 프로그래머가 넘치게 담지 않을 것이란 장담이 있어도 컴퓨터는 불가능 하다 인식한다.
class Parent{}
class Child extends Parent {}
public static void main(String[] args) {
// Child -> Parent 가능
Parent pc = new Child();
// Parent -> Child 불가능
Child cp = new Parent();
}
그렇다면 어떻게 Parent 를 Child로 형 변환 생성이 가능할까?
class Parent {
int parentData = 10;
public Parent() {}
void printName() { System.out.println("ParentPrintName()");}
}
class Child extends Parent {
int childData = 5;
public Child() {super();}
public void printName() { System.out.println("ChildPrintName()");}
}
public static void main(String[] args) {
// 첫번째 Child -> Child
Child child = new Child();
// 두번째 Child -> Parent
Parent pc = child;
// 세번째 Parent -> Child
Child cp = (Child)pc;
pc.printName(); // 두번째에 선언된 pc는 Parent 기능만 사용 가능하다
System.out.println(pc.parentData);
cp.printName(); // 세번째에 선언된 cp는 Parent, Child의 기능 모두 사용 가능하다
System.out.println(cp.childData);
System.out.println(cp.parentData);
}
첫번째 Child 객체를 먼저 생성한다.
두번째 Child 객체를 Parent에 대입해 생성했다.
세번째 Child에 Parent를 형 변환해 대입한다.
그렇다면 이것은 Child와 Parent의 기능을 모두 갖고 있는 것일까? 위와 같이 하는 이유 또는 의미는 무엇일까?
아니다, 두번째를 보면 Child를 통해 Parent 객체를 생성한다.
위와 같이 하면 Child가 가지고 있는 Parent의 기능만을 뽑아내 Parent 객체를 생성하는 것이다
즉 Child를 통해 Parent 객체를 생성시 Parent 객체의 기능만 사용하도록 제한하기 위해 형 변환한다.
이게 가능한 이유는 Child는 Parent를 상속 받고 있기 때문에 Parent의 모든 기능이 사용 가능하기에 가능하다
세번째를 하는 이유는 무엇일까? Child 객체의 기능이 제한되어 있는 Parent를 Child 객체로 생성함으로써 다시금 Child와 Parent 둘 모두의 기능을 사용 가능하도록 하는 방법이다.
instanceof 연산자
instanceof란?
다형성으로 인해 현재 실제 참조하고 있는 객체의 형(타입)이 무엇인지 알아내어 런타임 에러를 방지가 가능하게 해주는 연산자이다. 즉, 어떤 클래스나 인터페이스로부터 생성되었는지를 판별해주는 역할을 한다.
// 객체 instanceof 클래스
Child child = new Child();
Parent parent = new Parent();
System.out.println(child instanceof Child); // true
System.out.println(parent instanceof Child); // false
System.out.println(child instanceof Parent); // true
System.out.println(parent instanceof Parent); // true
if(parent instanceof Child){
System.out.println(parent.parentData);
} else if(child instanceof Parent){
System.out.println(child.childData);
System.out.println(child.parentData);
}
- instanceof로 타입을 확인 할 때 주의사항으로 부모 타입도 true라는 결과를 제공한다.
결론, 상속 관계에 있는 자식 객체를
" 부모 객체의 기능만 사용하고자 하는가? "
또는
" 부모 객체의 기능만 사용하다가.... 다시 두 객체의 모든 기능을 사용 하고자 하는가? "
에 따라 제한 사항을 두어 용도에 맞는 기능만 사용하기 위해 객체의 형 변환을 한다고 생각하자
다형성 (Polymorphism)
뜻 그대로 풀이하자면 형태가 다양하다는 말이다. 무엇이 다양할까?
하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미한다. 자바에서는 이러한 다형성을 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하여 구현하고 있다
다형성을 구현한 것이 오버로딩과 오버라이딩 이다.
오버로딩 | 오버라이딩 | |
---|---|---|
메서드명 | 동일 | 동일 |
매개변수 및 타입 | 다름 | 동일 |
리턴 타입 | 관계 없음 | 동일 |
'Programming > Backend' 카테고리의 다른 글
자바 기초 다지기 - 6 (Thread, Syncronized, Serializable, IO, NIO) (0) | 2021.09.13 |
---|---|
자바 기초 다지기 - 5 ( Generic, Collection, Map ) (0) | 2021.09.06 |
자바 기초 다지기 - 4 (내부 클래스, 어노테이션, 자바) (0) | 2021.08.29 |
자바 기초 다지기 - 3 (API, Object, 추상, final, Enum, 예외, String) (0) | 2021.08.23 |
자바 기초 다지기 - 1 (클래스, 메소드, 변수, 컴파일, 연산자) (0) | 2021.08.16 |