2023_1_9_TIL

TIL

자바

배열 생성하기

int[] a;// 선언만
int[]a = null;// 선언 + 초기화
// null은 힙 메모리의 주소를 가리키고 있지 않음, 즉 연결된 실제 데이터가 없다!

자바의 메모리 구조

  • 클래스 영역, 정적 영역, 상수 영역, 메서드 영역
  • 스택 영역
    • 기본 자료형, 참조 자료형, 지역변수
  • 힙 영역
    • 객체가 위치하는 공간

배열 자료형 변수에 객체 대입하기

int[] a;
a = new int[3];
// int 자료형 3개를 저장할 수 있는 공간을 힙 메모리에 넣고
// 그 주소를 참조 변수 a에 넣어라

객체의 위치를 참조 변수에 저장하는 이유

  • new 키워드 -> 객체 생성한다는 것(JVM이 힙의 빈 공간에 객체 생성)

참조 변수와 배열 객체의 값 초기화하기

  • 초기화 하지 않을 때의 초깃값
    • 스택 메모리 변수를 초기화X -> 메모리 공간 비어있다
    • 모든 변수는 초기화 이후 출력 O
int a;
int[] b;
System.out.println(a);// 출력 오류
System.out.println(b);// 출력 오류
  • 기본 자료형과 참조 자료형의 초깃값
int a = 0;
int[] b = null;
System.out.println(a);// 0
System.out.println(b);// null

참조 자료형으로서 배열의 특징

  • 기본 자료형
int a = 3;
int b = a;
b = 7;
System.out.println(a);// 3
System.out.println(b);// 7
  • 참조 자료형
    • 참조 자료형 변수를 복사
      - 실제 데이터 복사 X 셀제 데이터 '주소' 복사 O
      - 따라서 복사 후 하나의 참조 변수 수정 -> 같은 '주소'가진 참조 변수도 변경
int[] a = {3, 4, 5};
int[] b = a;
b[0] = 7;
System.out.println(a[0]);// 7
System.out.println(b[0]);// 7

2차원 비정방 행렬

  • 배열 객체의 행 성분부터 생성하고 열 성분 생성하기
    • 행 성분 생성 -> 각각 행에 열 성분 추가
int[][] a = new int[2][];
a[0] = new int[2];// 첫 번째 행의 열의 갯수
a[0][0] = 1;
a[0][1] = 2;
a[1] = new int[3];// 두 번째 행의 열의 갯수
a[1][0] = 3;
a[1][1] = 4;
a[1][2] = 5;

int[][] a = new int[2][];
a[0] = new int[]{1, 2};
a[1] = new int[]{3, 4, 5};

String 클래스의 2가지 특징

  • 한 번 정의된 String은 변경 X
  • String 리터럴로 객체 생성 -> 같은 String 끼리 '공유'

특징1. 객체 안의 값을 변경하면 새로운 객체를 생성

String str1 = new String("안녕);
String str2 = str1;
str1 = "안녕하세요";
System.out.println(str1);// 안녕하세요
System.out.println(str2);// 안녕

String 클래스의 특징과 배열의 특징 비교

  • String -> 값 변경시 항상 새로운 객체 생성
  • Array -> 하나의 변수에서 수정 = 나머지 변수도 같이 수정

특징2. 리터럴을 바로 입력한 데이터는 문자열이 같을 때 하나의 객체를 공유

String str1 = new String("안녕);
String str2 = "안녕";
String str3 = "안녕";
String str4 = new String("안녕);
  • new 생성 -> 무조건 새로운 객체 생성
  • String 리터럴 -> 객체 '공유'

문자열 + 문자열 연산

String str1 = "안녕" + "하세요" + "!";
System.out.println(str1);// 안녕하세요!

String str2 = "반갑";
str2 += "습니다.";
str2 += "!";
System.out.println(str2);// 반갑습니다!
  • String 객체는 변경 불가능 -> 힙 메모리에는 각각 문자열 리터럴 객체를 만듬

문자열 + 기본 자료형, 기본 자료형 + 문자열

System.out.println(1 + "안녕");// 1안녕
System.out.println(1 + "안녕" + 2);// 1안녕2
System.out.println(1 + 2 + "안녕");// 3안녕
System.out.println("안녕" + 1 + 2);// 안녕3

String 클래스의 주요 메소드

String str1 = "Hello World";
String str2 = "안녕하세요! 반갑습니다."

// indexOf()
System.out.println(str1.indexOf('a'));// 7, 앞에서 부터 첫 번째 'a'가 위치한 인덱스
System.out.println(str1.indexOf('a', 8));// 뒤에 매개변수는 검색 시작 위치
// lastIndexOf()
System.out.println(str1.lastIndexOf('a'));// 뒤에서 부터 첫 번째 'a'가 위치한 인덱스
System.out.println(str1.lastIndexOf('a', 8));// 뒤에 매개변수는 검색 시작 위치

// String.valueOf() -> 문자열로 변환해준다

// concat() -> 문자열 변경
String str5 = str3.concat(str4);// str3과 str4를 이어 붙인다

// getBytes() -> 문자열을 byte[]로
byte[] array1 = str1.getBytes();
byte[] array2 = str2.getBytes();

// replace()
str.replace("이것을", "이것으로");

// subString()
str.subString(0, 5);// 0 <= X < 5 까지 가져온다

// split()
String[] strArray = "abc/def-ghi jkl".split("/|-| ");// | | 사이에 있는 것을 구분함
  • == 과 equals() 차이
    • == -> '주소'를 비교
    • equals(), equalsIgnoreCase() -> '값'을 비교

클래스의 개념

  • '변수'가 모여서 배열, '배열'이 모여서 구조체, '구조체'가 모여서 클래스
  • 객체지향 문법 요소
    • 클래스 -> 일반 클래스, 추상 클래스
    • 인터페이스

클래스 구조

  • 클래스 외부
    • 패키지, 임포트, 외부 클래스
  • 클래스 내부
    • 클래스 멤버
      • 필드, 메소드, 이너 클래스
    • 생성자

클래스와 객체 구분

  • 클래스의 생성자로 객체 만듬(인스턴스 화)
  • 인스턴스 화로 만들어진 객체(인스턴스)

객체 생성하기


  • 객체는 new 키워드로 생성
A a = new A();// A() 생성자로 만든 객체를 힙 메모리에 넣고, 주소를 A타입의 참조변수 a에 저장
  • 클래스 A -> 메모리 클래스 영역
  • 참조 변수 -> 스택 영역
  • 생성자로 만들어진 객체 -> 힙 메모리
  • 객체 내부에는 클래스의 멤버 위치
  • 객체 안에서는 메소드 영역의 위치만 가리킴
    • 클래스로 만든 객체는 필드는 달라도 메소드는 동일하다(메소드 공유)

필드와 지역 변수의 구분

  • 2가지의 차이점 -> 메모리의 위치
    • 필드 -> 힙 메모리의 객체 내부
      • 힙 메모리의 객체 안에 저장되는 필드는 객체 사라질 때까지 존재
    • 지역변수 -> 스택 메모리
      • 스택 메모리에 저장되는 변수는 JVM이 알아서 삭제
class A {
	int m = 3;// 필드
    int n = 4;// 필드
    void work1() {
    	int k = 5;// 지역변수
        System.out.println(k);
        work2(3);
    }
    
    void work2(int i) {// 지역변수
    	int j = 4;// 지역변수
        System.out.println(i + j);
    }
}

  • 정의된 메서드 부분을 메모리에서는 'frame'
  • 해당 메서드 종료되면 JVM에서 'frame' 전체 삭제
  • work2() 삭제 -> work1() 삭제

필드와 지역 변수의 초깃값

  • 힙 메모리 -> 빈 공간이 저장 X => 힙 메모리에 위치하는 필드는 강제로 초기화
  • 스택 메모리 -> 강제로 초기화 X => 스택에 저장되는 지역 변수 또한 그 특징 가짐
class A {
 int m;// 강제 초기화
 int n;// 강제 초기화
 void work1() {
 	int k;
    System.out.println(k);// 초깃값 없이 출력시 에러
 }
}
A a = new A();
System.out.println(a.m);// 0
System.out.println(a.n);// 0
a.work1();// 에러

리턴 타입이 void일 때 return 키워드의 의미

  • 리턴 타입이 void인 메소드 안에서 return 키워드 사용
void printMonth(int m ) {
	if (m < 0 || m > 12) {
    	System.out.println("잘못된 입력!");
        return;// 메서드 종료
    }
    System.out.println(m + "월 입니다.");
}

메서드 호출하기

  • 클래스 외부에서 메서드 호출
    • 클래스 외부에서 메서드 호출시 '먼저 객체 생성'
  • 클래스 내부에서 메서드 호출
    • 클래스 내부에 있는 메서드는 호출 안해도 됨
      • static이 붙어있는 메소드 -> static필드, static 메소드

기본 자료형 입력매개변수와 참조 자료형 입력매개변수의 차이

  • 참조 자료형이 파라미터로 넘겨진다 = 실제 객체의 '주소'가 전달
int[] array = {1, 2, 3};
modifyData(array);
printArray(array);

public static void modifyData(int[] a) {
	a[0] = 4;
    a[1] = 5;
    a[2] = 6;
}
public static void printArray(int[] a) {
	System.out.println(Arrays.toString(a));
}

가변 길이 배열 입력매개변수 메서드

  • 오버로딩의 불편함을 줄이기 위해 만들어진 문법
method1(1, 2);
method1(1, 2, 3);
method1();

method2("안녕", "방가");
method2("안녕", "방가", "감사");
method2();

public static void method1(int... values) {// 가변 길이 배열 입력매개변수
	System.out.println("입력매개변수 길이 : " + values.length);
    for (int i = 0; i < values.length; i++)
    	Sytem.out.print(values[i] + " ");
    System.out.println();
}
public static void method2(String... values) {// 가변 길이 배열 입력매개변수
	System.out.println("입력매개변수 길이 : " + values.length);
    for (int i = 0; i < values.length; i++)
    	Sytem.out.print(values[i] + " ");
    System.out.println();
}

생성자와 객체의 생성 방법

  • 생성자도 메서드 처럼 오버로딩 가능
A() {
	System.out.println("첫 번째 생성자");
}
A(int a) {
	System.out.println("두 번째 생성자");
}
A(int a, int b) {
	System.out.println("세 번째 생성자");
}

public class ...
	public static void main(String[] args) {
    	A a1 = new A();
        A a2 = new A(3);
        A a3 = new A(3, 5);
    }

내부 객체 참조 변수명 this 키워드

  • 모든 메소드는 자신이 포함된 클래스의 객체를 가리키는 this 존재

클래스 내 다른 생성자를 호출하는 this() 키워드

  • this() 메소드 -> 생성자 중복 제거(필요 이유)
    • 자신이 속한 클래스 내부의 다른 생성자 호출
    • 생성자의 내부에서만 사용가능
    • 생성자의 첫 줄에 위치
class A {
	int m1, m2, m3, m4;
    A() {
    	m1 = 1;
        m2 = 2;
        m3 = 3;
        m4 = 4;
    }
    A(int a) {
    	this();// m2 ~ m4 A() 생성자 따름
    	m1 = a;
    }
    A(int a, int b) {
    	this(a);// A(int a) 생성자 따름
        m2 = b;
    }

외부 클래스

  • 외부 클래스는 다른 패키지에서 import '불가능'
    • 다른 패키지에서 외부 클래스 import 할려면 (별도의 public 소스파일 분류)
  • public 클래스는 다른 패키지에서 import '가능'

인스턴스 필드와 정적 필드

class A {
	int m = 3;
    static int n = 5;
}

인스턴스 메소드와 정적 메소드

  • 인스턴스 메소드 -> 인스턴스 메소드 영역
  • 정적 메소드 -> 클래스 영역

정적 메소드 안에서 사용할 수 있는 필드와 메소드

  • static은 객체의 생성 없이 사용 가능 / instance는 객체의 생성으로 사용
    • 정적 메소드 안에서 인스턴스 멤버 사용 -> 결국 정적 메소드도 객체 생성 후 사용
  • 따라서 효율성이 떨어지기 때문에 정적 메서드 내부에 정적 멤버만 올 수 있음(특징)

정적 초기화 블록

  • 정적 필드는 생성자 호출 전에 사용가능 = 생성자에서는 정적 필드 초기화 불가능
  • 클래스가 메모리에 로딩될 때 가장 먼저 실행 -> 클래스 로딩 시점에 바로 초기화
class A {
	int a;
    static int b;
    
    static {
    	b = 5;
        System.out.println("클래스가 로딩될 때 static block 실행");
    }
    
    A() {
    	a = 3;// 인스턴스 필드 초기화 위치
    }
}

public class ...
	public static void main(String[] args) {
    	System.out.println(A.b);// 클래스가 로딩될 때 static block 실행 -> 5
    }

상속의 장점

  • 1개의 객체를 여러가지 모양으로 표현 = polymorphism

상속할 때의 메모리 구조

class A {
	int m;
    void abc(){...}
}
class B extends A {
	int n;
	void bcd(){...}
}

B b = new B();
  • 부모 클래스 객체 생성 -> 자식 클래스 객체 생성
  • 이후, 자식 클래스 필드, 메소드 추가
  • 자식 클래스 객체 내부에는 부모 클래스 객체가 포함 -> 부모 클래스의 멤버 사용 가능

생성자의 상속 여부

  • 생성자인지 메소드인지 알 수 없기 때문에 -> 상속 X

메모리로 이해하는 다운캐스팅

  • 선언되 타입 = 실제 객체에서 자신이 선언된 타입의 객체를 가리키는 것

B b = new B();
b.abc();// O
b.bcd();// O

A a = new B();
a.abc();// O
a.bcd();// X

메서드 오버라이딩


오버로딩, 오버라이딩 차이점

class A {
	void print1() {...}
    void print2() {...}
}
class B extends A {
	void print1() {...}// 오버라이딩
    void print2(int a) {...}// 오버로딩
}

메소드 오버라이딩과 접근 지정자

  • 상속관계의 오버라이딩 -> 상속 받은 메소드의 접근 지정자와 동일 or 넓은 접근 지정자

인스턴스 필드의 중복, 정적 필드의 중복

  • 인스턴스 필드 중복
class A {int m = 3;}
class B extends A {int m = 4;}
  • 정적 필드 중복
class A {static int m = 3;}
class B extends A {static int m = 4;}

정적 메소드의 중복

  • 인스턴스 메소드 오버라이딩 이유 -> 동일한 공간에 메소드를 저장해서
  • 정적 메소드는 각자 클래스 내부에 존재 = 다른 공간에 저장
class A {
	static void print() {...}
}
class B extends A {
	static void print() {...}
}

Object 클래스의 주요 메소드

  • toString()
    • 패키지명.클래스명@해시코드
      • 따라서 사용시에 오버라이딩 후 재정의 필요
      • 오버라이딩 X 출력 -> 패키지명.클래스명@해시코드 형태 출력
  • equals(Object obj)
    • 객체의 스택 메모리값을 비교(주소를 비교하는 것)
      • 따라서 사용시에 오버라이딩 후 재정의 필요
public boolean equals(Object obj) {//  주소비교 -> 데이터 값 비교
	if (obj instanceof B) {
    	if (this.name == ((B) obj).name)// 다운 캐스팅
        	return true;
    }
}
  • hashCode()
    • Object의 hashCode()메소드 -> 고윳값
      • hashCode() + equals() -> '오버라이딩' 필요
    • 객체의 위치와 연관된 값
      • 두 객체의 hashCode() 비교
      • hashCode() 동일하면 -> equals() 메소드 호출
class A {
	String name;
    A(String name) {this.name = name;}
    
	@ override
    public boolean equals(Object obj) {
    	if (obj instanceof A) {
        	if (this.name == ((A) obj).name)
            	return true;
        }
        return false;
    }
    
    @ override
    public String toString()
    	return name;
}
class B {
	String name;
    B(String name) {this.name = name;}
    
	@ override
    public boolean equals(Object obj) {
    	if (obj instanceof B) {
        	if (this.name == ((B) obj).name)
            	return true;
        }
        return false;
    }
    
    @ override
    public int hashCode()
    	return name.hashCode();
    
    @ override
    public String toString()
    	return name;
}

public class ...
	public static void main(String[] args) {
    	HashMap<Integer, String> hm1 = new HashMap<>();
        hm1.put(1, "데이터1");
        hm1.put(1, "데이터2");
        hm1.put(2, "데이터3");
        // -> 1 = 데이터2, 2 = 데이터3
        
        HashMap<A, String> hm2 = new HashMap<>();
        hm2.put(new A("첫 번째"), "데이터1");
        hm2.put(new A("첫 번째"), "데이터2");
        hm2.put(new A("두 번째"), "데이터3");
        // -> 첫 번째 = 데이터2, 두 번째 = 데이터3, 첫 번째 = 데이터1
        
        HashMap<B, String> hm3 = new HashMap<>();
        hm3.put(new B("첫 번째"), "데이터1");
        hm3.put(new B("첫 번째"), "데이터2");
        hm3.put(new B("두 번째"), "데이터3");
		// -> 첫 번째 = 데이터2, 두 번째 = 데이터3
    }
}

fianl 제어자

profile
블로그 이전 : https://medium.com/@jaegeunsong97

0개의 댓글