* 공부하면서 알게된 것 1. new 그리고 생성자를 호출한다는 것은? 2. 어떤 식으로 메모리에 인스턴스가 생성이 되는 것이지? 3. 상속관계에서 부모 클래스 역시 새로운 인스턴스로 만들어지는 것인가?
자바 상속의 특징 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다. 즉 기존 클래스의 멤버 변수와 메서드를 사용하면서 새로운 멤버 변수와 메서드를 정의하여 클래스를 만들겠다는 것이다.
interface이긴 하지만, extends의 개념은 비슷해보여 그려보았다.
예를 들어 r (반지름) 멤버변수를 가진 Circle 클래스가 있다고 하자. (또한 기본으로 0, 0에 위치한다고 가정) 추가적인 요구사항이 들어와 Circle에 0, 0이 아닌 cx, cy (위치 정보) 및 위치 정보를 출력하는 기능을 지닌 클래스가 필요하다고 할 때 어떻게 해야 할까? 이 경우 위치 정보를 멤버 변수로 가지는 새로운 클래스를 정의하는 것이 낫다. 또한 반지름 정보가 필요하므로 Circle을 상속받아 해당 멤버 변수와 관련 메서드를 사용할 수 있는 것이다.
멤버 변수 가리기와 메서드 오버라이딩은 다르다!
super 키워드 상속관계에 있는 경우 조상 클래스의 멤버변수가 초기화되어야 하기 때문에 조상 클래스의 생성자가 반드시 호출되어야 한다. 즉 상속관계인경우 부모클래스의 인스턴스 멤버를 포함한 채 인스턴스가 생성되어야 하기 때문이다.
만약, super()가 명시되어있지 않다면 컴파일러에서 해당 값을 추가해준다. 하지만 다음의 경우 부모 클래스에 이미 정의된 생성자가 있기에 default constructor는 없어서 에러가 난 경우이다.
위에서부터 하나씩 코드로 살펴보자. 클래스에는 private과 protected가 쓰일 수 없다. 실제로 작성하면 에러가 나는 것을 볼 수 있다.
그 이유는 무엇일까? 답하기에 앞서 private과 protected이 왜 필요한지부터 생각해볼 필요가 있을 것 같다. private같은 경우는 데이터를 은닉하는 효과의 문법이고 protected 역시 다른 클래스에서 해당 데이터를 은닉하기 위한 효과의 문법이다. 이는 객체지향개념의 캡슐화에 해당하는 내용이다. 캡슐화란 데이터가 유효한 값을 유지하기 위해 혹은 외부에서 수정할 수 없도록 외부로부터 데이터를 제한하는 것이다. 그렇기에 두 문법은 class보다는 data단에 속하는 메서드 및 멤버변수에 적용되는 것이 알맞다.
만약 다른 패키지에서 클래스를 사용하려면 반드시 (default)가 아닌 public을 붙여줘야한다. 그래야 다음 에러가 없어진다!
그리고 클래스는 public이나 메서드는 그렇지 않다면 이 경우도 에러가 발생한다. 그 이유는 인스턴스 생성시 결국 생성자 메서드를 호출하는 것인데 이 경우에도 다른 패키지에서 해당 생성자 메서드를 호출하기 위해선 public 접근제어자가 꼭 필요하다.
위와 같은 간단한 도구(data를 csv파일로 내보내고, 다시 표로 읽어들이는 간단한 도구)를 Java Swing을 통해 만들어보려고 한다. 근데 그 전에 어떻게 UI를 구성하는지, Window Builder 추가, CSV 파일로 저장하고 읽는 방법에 대한 약간의 공부가 필요해서 정리중이다.
그리고 Eclipse 상단 우측 Help -> Install New Software -> Add -> Archive를 선택해 다운로드 받은 zip파일을 추가한다. download progress가 뜨고 Restart 할 것인지 물어야 최종적으로 사용이 가능한 환경이 된다!
Q. this가 어떤 문법이고 왜 필요한 것일까? A. 현재의 객체를 지칭하는 문법이며 인스턴스별로 다른 필드의 값을 구분하기 위해 필요하다. 하나의 클래스에서 만들어진 것이 인스턴스인데, 각 인스턴스의 필드내용은 다른 값을 가진다.
public class Circle {
// An instance field
public double r; // The radius of the circle
Circle (double r) {
this.r = r;
}
}
예를 들어 현재 인스턴스와 다른 인스턴스 필드의 값을 비교하는 메서드를 만든다고 하자. 두 값이 모두 같은 필드를 참조하는데 어떻게 비교를 해야 할까? 이 경우에 this를 사용한다.
public Circle bigger (Circle that) {
if (this.r > that.r) return this;
else return that;
}
혹은 앞서봤듯이 생성자와 같이 지역변수명이 중복이거나 명시적으로 해당 필드의 값을 참조해야 할 필요가 있을때 필요하다.
(다음 내용이 맞는지는 정확한 확인이 필요하다.) 인스턴스 메서드에서의 this의 개념은 크게 2가지로 나뉜다. 바로 컴파일러가 추가해주는 implicit this (보이지 않는) 와 명시적으로 사용하는 eplicitthis (개발자가 직접 사용하는) 이다.
인스턴스 메서드에서 다른 인스턴스 메서드 호출 또는 필드를 참조하고 싶다면, 해당 메서드 이름을 호출하거나 필드명을 작성하면 된다.
조금 더 깊이 들어가보면 컴파일러를 통해 암묵적으로 추가된 this를 통해 현재 객체가 가지고 있는 메서드 호출 혹은 필드를 참조하는 것이다.
public class Circle {
// 클래스 필드
public static final double PI = 3.1459;
// 인스턴스 필드
double r;
// 인스턴스 메서드
public double circumference() { return 2 * PI * r; }
public double radius() {return r;}
// 인스턴스 메서드 - 암묵적/명시적/중복 필드 참조
public double areaWithImplicitThis() {return PI * r * r;}
public double areaWithExplicitlyThis() {
return Circle.PI * this.r * this.r;
}
public double areaWithDouplicatedThis() {
double r = 2.0;
return Circle.PI * r * r;
}
public void printArea() {
System.out.println("Area is ... " + this.areaWithDouplicatedThis());
}
}
How the this Reference Works - The implicit this parameter is not shown in method signatures because it is usually not needed; whenever a Java method accesses the instance fields in its class, it is implicit that it is accessing fields in the object referred to by the this parameter. The same is true when an instance method invokes another instance method in the same class—it’s taken that this means “call the instance method on the current object. -
package myclass;
public class Circle {
static int count;
int serialNo;
static {
double x = 0.2; // 오직 이곳에서만 생성된다.
count = 1;
System.out.printf("1. Static Constructor is called%n");
}
{
double x = 0.1; // 오직 이곳에서만 생성된다.
count++;
serialNo = count;
System.out.printf("2. This instance serialNo = %d%n", serialNo);
}
Circle (double r) {
this.r = r;
System.out.printf("3. Constructor called%n");
}
// A class field
public static final double PI= 3.14159; // A useful constant
// A class method: just compute a value based on the arguments
public static double radiansToDegrees(double radians) {
return radians * 180 / PI;
}
// An instance field
public double r; // The radius of the circle
public static double areaV1(double r) {
return PI * r * r;
}
// Two instance methods: they operate on the instance fields of an object
public double area() { // Compute the area of the circle
return PI * r * r;
}
public double circumference() { // Compute the circumference
// of the circle
return 2 * PI * r;
}
public static Circle bigger(Circle a, Circle b) {
return a.r > b.r ? a : b;
}
public Circle bigger (Circle that) {
if (this.r > that.r) return this;
else return that;
}
}
객체 만드는 방법 (new 키워드 이해하기)
객체란 클래스의 인스턴스를 말하며, new 키워드를 통해 1. heap에 인스턴스 생성 및 2. 생성자가 실행된다.
package myclass;
class Data1 {
int value;
}
class Data2 {
int value;
Data2 (int x) {
value = x;
}
}
public class BasicClass {
public static void main(String[] args) {
Circle circle1 = new Circle(2.0);
Circle circle2 = new Circle(4.0);
Circle bigCircle1 = circle1.bigger(circle2);
System.out.printf("1. Big circle radius is = %.1f\n", bigCircle1.r);
Circle bigCircle2 = Circle.bigger(circle1, circle2);
System.out.printf("2. Big circle radius is = %.1f\n", bigCircle2.r);
Data1 dt1 = new Data1();
// The constructor Data2() is undefined is occured.
// Data2 dt2 = new Data2();
}
}
메소드 정의하는 방법 (Class, Instance 메소드로 구성할 수 있다.)
Class methods
static이 앞에 붙여진 메서드
클래스에 묶인 메서드
내부의 경우 모든 메서드에서 호출할 수 있으나, 클래스 외부에서 호출 시 <클래스명.> 을 붙여서 호출해야 한다.
// A class field
public static final double PI= 3.14159; // A useful constant
// A class method: just compute a value based on the arguments
public static double radiansToDegrees(double radians) {
return radians * 180 / Circle.PI;
}
Instance methods
static이 없는 나머지 메서드
객체에 묶인 메서드
클래스 내부의 경우 인스턴스 메서드에서 호출할 수 있으나, 클래스 외부에서 호출시 <인스턴스명.>을 붙여서 호출해야 한다.
// Two instance methods: they operate on the instance fields of an object
public double area() { // Compute the area of the circle
return PI * r * r;
}
변수 정의하는 방법 (Class, Instance 변수로 구성할 수 있다.)
Class fields
Instance fields
위 두 종류의 필드의 경우 초기값을 명시하지 않는 경우, 디폴트 값으로 셋팅된다.
public class Circle {
// A class field
public static final double PI = 3.14159;
// An instance field
public double r;
...
}
* The use of public static fields that are not final is almost never a good practice—as multiple threads could update the field and cause behavior that is extremely hard to debug. (멀티쓰레드 환경에서는 클래스 필드를 공유하므로 해당 필드가 업데이트 될 수 있기 때문에 꼭 final을 붙여사용하라는 것 같다.)
생성자 정의하는 방법
<클래스명>() {}, <클래스명>() {}
클래스 내부에 생성자가 하나도 정의되지 않은 경우에만 컴파일러가 default constructor를 추가한다.
현재의 클래스명과 동일하다.
리턴타입이 없다. (void라는 키워드사용하지 않는다. 아예 명시하지 않는다.)
생성자의 body는 인스턴스를 초기화할때 사용하며 값을 리턴하지 않는다.
매개변수가 다른 생성자를 선언할 수 있다.
매개변수가 다른 생성자가 선언되어 있을 때, this(x); 를 사용해 다른 선언자를 지칭할 수 있다.
this 키워드 이해하기
현재 객체의 인스턴스 메서드 호출 혹은 인스턴스 필드를 가리킨다는 뜻이다.
클래스 초기화
public class Circle {
static int count;
int serialNo;
static {
count = 1;
System.out.printf("Static Constructor is called%n");
}
{
count++;
serialNo = count;
System.out.printf("This instance serialNo = %d%n", serialNo);
}
Circle (double r) {
this.r = r;
System.out.printf("Constructor called%n");
}
...
}
위와 같은 코드가 있을 경우 초기화가 어떤 식으로 이루어질까? class 변수가 default 값으로 초기화되고 static 초기화 블럭에서 1로 값이 변경된다. 그리고 인스턴스 생성시 인스턴스 초기화 블럭에서 serialNo가 초기화 되고 그 후에 생성자가 실행된다.