새소식

인기 검색어

TL

20/09/07 TL. JAVA - 객체, 제네릭

  • -

오늘 할 일

NLP 실습 강의, 코드 분석 -> Day 4, 5

강의 수강 -> 컴퓨터 프로그래밍2, 진로설계2

확인 후 과제 제출 -> 선형대수, 컴파일러 개론

 

 

내일 할 일

NLP 실습 강의, 코드 분석 -> Day 5

강의 수강 -> 컴퓨터 프로그래밍2, 기계학습, 컴퓨터 네트워크

 

매일 할 일

매일 데이터 수집

매일 태깅

태깅 피드백

매일 독서 - 더블린 사람들

매일 영단어

 

시간 날 때 해야하는 일

코드 최적화 정리

리액트 클론 코딩 - 영화 리뷰 앱

java -> UML, class 분할, this, implements, extends, interface, super 공부

lovit.github.io/machine%20learning/2019/03/17/attention_in_nlp/

intelligence.korea.ac.kr/members/wschoi/nlp/deeplearning/paperreview/Contextualized-Word-Embedding/

ebbnflow.tistory.com/151

정독


기초 변수 : int, float, char 등 기초 자료형을 가지는 변수
참조 변수 : 객체의 주소가 들어 있어 객체가 있는 곳을 가리킨다.
(배열, 클래스, 인터페이스 )

수행하려면 객체를 생성해야 하고, 객체 생성은 new()를 통해서만 가능하다.

객체 내의 필드는 메소드를 통해서 간접적으로 접근하는 것이 바람직하다.
-> 캡슐화를 위해. 접근자 accessor, 설정자 mutator를 이용해서 접근.

메소드 : 클래스 안에 정의된 함수

argument 인수 : 호출하는 곳에서 메소드 호출시 전달하는 값
parameter 매개변수 : 메소드에서 값을 받을 때 사용하는 변수

객체는 힙이라는 메모리 공간에서 만들어진다.

여러 개의 참조 변수가 하나의 객체를 가리킬 수 있다.

garbage collector가 실행 환경이 끝난 객체를 스스로 판단해 그 객체가 가지고 있던 자원을 시스템에 반납함.
-> 객체를 가리키는 참조 변수가 하나도 남아 있지 않다면 객체의 사용이 끝났다고 볼 수 있다. (참조 변수가 유효범위를 벗어나거나, null로 설정될 경우 등)

Car car1 = new Car();
Car car2 = new Car();

car2 = car1; // car2가 가리키던 객체는 가비지 컬렉터에 의해 수거

car1 = null; // car2가 아직 참조하기 때문에 car1가 참조하던 객체가 소멸되진 않음.

String은 클래스이다. 즉, 기초 자료형이 아니다. 문자열은 문자들의 나열이다. 객체임을 잘 기억!!

s1 = "abcde"
s2 = "가나다라마"

s2 = s1;

했을 때, s1에 들어가는건, s1에 들어있던 주소값이 들어가는 것이다!

String s = new String("Hello World!");
꼭 이게 아니라
String s = "Hllo World!";
로도 선언할 수 있는 이유 (new 없이 사용할 수 있는 이유)
""만 사용한 곳에서는 객체를 생성하지 않고, 메모리 풀에 해당 문자열이 있는지 살펴본다. 만약 있다면 그 메모리 주소를 s에 할당한다. 없다면 새로 생성한 후 반환한다.
1.객체의 주소가 달라지고, 2.전자는 객체를 무조건 생성하므로 더 느려진다.

==연산자는 주소를 비교하는 연산자이고, .equals는 내용을 비교할 때 사용한다. 이를 기억하자!

필드 : 클래스 안에서 선언되는 멤버 변수. 인스턴스 변수라고도 한다.
지역 변수 : 메소드나 블록 안에서 선언되는 변수
매개변수 : 메소드 선언에서의 변수

접근자 accessor: 필드값을 반환
설정자 :mutator 필드값을 설정
-> 캡슐화를 위해서.

자바에서는 항상 call by value를 한다. 주소가 아니라 값만 복사된다!
참조형의 경우에는 참조값이 복사되어서 전달되는 것이지, 객체 자체는 전달되지 않는다!

가변 길이 인수 : 메소드에 가변 개수의 인수들을 전달할 수 있다.

 

package java_playground;

class Test {
	void sub(int... v) {
		System.out.println("Length of v :" + v.length);
		
		for(int x : v) {
			System.out.print(x + " ");
		}
		System.out.println();
	}
}

public class playground {
	public static void main(String args[]) {		
		Test c = new Test();
		c.sub(1);
		c.sub(1, 2, 3, 4, 5);
		c.sub();
	}
}

 

 


각각의 객체(인스턴스)들은 자신만의 필드를 가진다. 이들 필드는 별도로 생성되기 때문에 인스턴스 변수라고도 한다. 이들은 별도의 기억공간을 가진다.

모든 객체의 공통인 변수가 필요한 경우도 있는데, 이를 정적 변수 static variable이라 한다. 정적 변수는 클래스 변수라고도 한다. 당연히 클래스 1개당 하나만 존재한다. static을 붙여 정의한다.

정적 메소드는 static을 붙인 메소드이다. static 메소드는 객체 선언 없이 사용할 수 있다. 예를 들어 main문에서 같은 클래스 내에 있는 메소드를 사용할 때 사용 가능하다.

main 메소드에 static이 붙어있는 이유는 JVM(자바 가상 머신)에서 객체 생성 없이 main()메소드를 호출하여 실행할 수 있도록 하기 위해서이다.


main 메소드는 정적 메소드로 사용하는데, JVM에서 객체를 생성할 필요 없이 해서 사용할 수 있게 하기 위해서 정적 메소드를 사용한다고 합니다. (power java, 2판, p. 207)

 

그래서 main() 메소드를 static이 아닌 상태로 실행할 수 있을까? 꼭 main 메소드가 아니여도 함수를 수행할 수 있을까? 하는 의문이 들어서 여러가지 실험을 했습니다.

 

확실하지는 않지만, static으로 선언된 메소드는 시작하자마자 실행된다고 이해하고 그렇게 가정을 했습니다. 그래서 자기 클래스의 객체를 선언하고 메소드 내에서 자기 자신을 호출했습니다.

 

제 생각에는 main 메소드가 static으로 선언됐기 때문에 우선 실행되어서 다른 메소드들을 실행하는 기능을 담당한다고 생각했습니다. 그래서 main문이 아니라도 다른 메소드에 static이나 파라미터로 String args[]로 준다면 main 메소드와 같은 기능을 할 것이라고 판단해서 실험을 몇가지 해보았습니다.

 

실험 결과로는 main함수가 아니면 동작하지 않는 듯 합니다. 아마 run을 할 때 static이면 실행하는 것이 아니라, 애초에 main()의 정해진 양식을 따라가는 것으로 보입니다.

-> 답변

static은 객체를 '생성'하지 않고, JVM이 실행될 때 공유 메모리에 해당 객체 / 메소드 등을 생성하여 같은 JVM 위에서 실행되는 코드 블락이 공유합니다.

생성 시기를 보자면 JVM이 생성되고, 바이트 코드를 읽어들이는 시점이지요.

따라서 static이 달려있지 않으면 해당 메소드를 실행하기 위하여 객체를 생성해야합니다. static이 달려있다면, 이미 JVM 생성시 초기화 되어 접근 가능해지므로 객체 생성없이 사용이 가능하지요.

static main은 비슷하면서도 특별한 기능을 하는데, 자바 소스코드의 launcher/java.c 코드를 보면 이런 문장이 있습니다.

mainID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V");

생성되어있는 Static메소드의 ID를 가져오는 것입니다.


제네릭 프로그램 : 클래스를 포함해 다양한 타입의 객체를 하나의 코드로 처리하는 기법. -> 동일한 코드로 정수형이든 사용자가 정의한 클래스이든 처리 가능. 컬렉션 라이브러리에 많이 사용.

 

// 제네릭 클래스
class Box<T>
{...}

// String Box
class Box<String>
{...}

// Integer Box
class Box<Integer>
{...}

 

 

Object type을 파라미터로 받는 것과 비슷하지만 또 다르다.
->Object로 저장하면 다른 타입일 때 컴파일러가 인지하지 못한다.

제네릭 클래스는 타입 매개변수를 사용하는데, 이는 객체 생성시 프로그래머가 결정한다.

 

class name<T1, T2, ..., Tn> { ... }

public class Box<T> {
   private T data;
   public void set(T data) { this.data = data; }
   public T get() { return data; }
}		// 이런 식으로 클래스를 선언하고,

Box<String> b = Box<String>();
Box<Integer> b = Box<Integer>();		// 이런 식으로 객체를 선언

 

타입 매개변수로 int는 사용할 수 없고, 대신 Int를 사용한다. Integer는 정수형 클래스이고, Int는 기초 자료형이다.

타입 매개변수의 표기는 영문 대문자로 표기한다.
E - Element (요소 : 자바 컬렉션 라이브러리에서 많이 사용)
K - Key
N - Number
T - Type
V - Value
S, U, V 등 - 2번째, 3번째, 4번째 타입

최신 자바 버전부터는 제네릭 클래스 생성 위한 생성자 호출할 때, 생성자에는 타입 매개변수 주지 않아도 된다. 생성되는 객체에 해당하는 클래스에 명시돼있어, 컴파일러가 문맥에서 추측한다.

 

Box <String> Box = new Box<>();		//이래도 된다는 소리.

 

다수의 타입 매개변수를 가질 수도 있다.

 

public interface Pair<K, V> {
   public K getKey();
   public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {
   private K key;
   private V value;

   public OrderedPair(K key, V value) {
      this.key = key;
      this.value = value;
   }

   public K getKey() { return key; }
   public V getValue() { return value; }
}

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<"Hello","World!">

 

raw 타입 : 매개변수가 없는 제네릭 클래스.
Box rawBox = new Box();
-> 무조건 Object 타입으로 간주. 제네릭이 없던 이전 코드와 호환성을 위해 도입.

제너릭 메소드의 경우, 타입 매개변수의 범위는 메소드 내부로 제한된다.
제네릭 메소드를 호출할 땐, 실제 타입을 <> 안에 넣어준다.

 

String[] languages = {"C++", "C#", "JAVA"};
String last = Array.<String>getLast(languages); 		//이런식. 여기서 <String>을 생략도 가능. 컴파일러가 알고 있으니까

 

Integer는 Object의 자손 클래스이므로 이런게 가능하다.

 

Object obj = new Object();
Integer i = new Integer(10);
obj = i;

 

Integer와 Double 모두 Number의 자식 클래스이므로 이것 또한 가능하다.

 

Box<Number> box = new Box<Number>();
box.add(new Integer(10));
box.add(new Double(10.1));

 

그러나,

 

 public void sub(Box<Number> n) { ... }

 

의 경우, Integer는 Number의 자식이지만, Box<Integer>는 Box<Number>의 자식이 아니다. 따라서, 매개변수로 Box<Integer>는 받을 수 없다.

 

자식으로 쓰고 싶으면 implements, extends로 써라.
아니면 와일드카드 <?>을 써라!

 

public static void printList(List<?> list) {...}

 

이런 식이다.

 


제네릭 코드에서 ?는 와일드 카드라고 불리우며, 어떠한 타입과도 일치한다.

super를 사용하여 같은 타입의 값을 갖는 모든 객체에 와일드 카드를 적용할 수 있다.

 

public static void addNumbers(List<? super Integer> list) {
	for(int i = 1; i < 10; i++) {
		list.add(i);
	}
}

 

이런식으로 적용 가능하다. List<Integer>, List<Number>, List<Object>와 같은 Integer 값을 가지고 있는 모든 객체에 대하여 작동시킬 수 있다. 

'TL' 카테고리의 다른 글

20/09/09 TL.  (0) 2020.09.09
20/09/08 TL.  (0) 2020.09.08
20/09/06 TL. react - state, lifecycle  (0) 2020.09.06
20/09/05 TL. 선형대수학 입문  (2) 2020.09.05
20/09/03 TL. 컴퓨터 그래픽스 기초  (0) 2020.09.03
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.