January 02, 2021
다양한 데이터 타입을 출력하려는 함수를 만들 때, 데이터 타입마다 메소드 이름을 달리하면 코드의 중복이 많아지고 재사용성도 저하된다.
이때 매개변수만 달리하여 매개변수에 따라 올바른 함수가 작동하도록 하는 Overloading을 사용한다면, 데이터 타입이 바뀌더라도 메소드를 호출하는 코드를 수정할 필요가 없다.
∴ 하나의 클래스는 매개변수의 유형과 개수를 달리해서 여러 개의 생성자를 갖도록 한다.
클래스로부터 객체를 생성할 때, 필요한 변수들만 적절히 초기화하기 위해서
this()
의미 & 사용법this 예약어 |
this() 생성자 |
---|---|
생성자나 메서드의 매개변수 이름이 객체 변수의 이름과 같은 경우 | 같은 클래스 내의 오버로딩 된 다른 생성자 메소드를 호출할 때 사용 |
this()
를 사용하여 중복되는 초기화 코드를 최소화한 경우int age;
int salary;
public Employee(){
this(0, "Anonymity", 0, 0);
}
public Employee(int employeeNo, String name){
this.employeeNo = employeeNo;
this.name = name;
}
//중복
public Employee(int employeeNo, String name, int age){
this.employeeNo = employeeNo;
this.name = name;
this.age = age;
}
//this()사용하여 중복을 없앤 예
public Employee(int employeeNo, String name, int age){
this(employeeNo, name);
this.age = age;
}
메소드 호출 시, 객체 간 메세지가 전달될 때 부가 정보가 필요한 경우 매개변수로 전달된다.
유지보수의 용이성을 위해, 매개변수 개수가 지정되어야만 하는 제한을 극복하기 위해 제공된다.
public static int intSum(int... num){
}
//... : 매개변수가 가변적이라는 것을 의미한다.
가변적 매개변수는 여러 개의 매개변수와 함께 사용가능하나 함께 사용할 때에는 마지막에서 한 번만 사용가능하다.
//오류
int add(int... i, String s){}
//오류
int add(String... s, int... i){}
//적합
int add(String s, int... i){}
extends
를 사용한다.상속된 부모 클래스 객체가 생성될 때 부모 클래스의 생성자도 자동으로 호출되어 수행되면서 객체 초기화가 이뤄지도록 한다.
super()
생성자this() 생성자 호출 |
super() 생성자 호출 |
---|---|
클래스 안에서 오버라이딩 된 또 다른 생성자를 호출하기 위해 사용 | 부모 클래스의 생성자를 명시적으로 호출할 때 사용 부모 클래스의 생성자가 오버로딩되어 여러 개 존재하는 경우 특정 생성자를 호출하기 위해 사용 |
부모 클래스의 생성자를 호출하는 super()
는 반드시 자식 클래스 생성자의 첫 번째 라인에 위치해야한다. ∵ 부모 클래스의 생성자가 항상 자식 클래스의 생성자보다 먼저 수행되어야 하기 때문이다.
private
로 정의된 변수는 상속 불가this 예약어 |
super 예약어 |
---|---|
생성된 객체 자신에 대한 참조를 의미 멤버 변수와 메서드 매개변수의 이름이 같을 경우, 두 변수를 구분하기 위해 사용 |
부모 객체에 접근할 수 있는 참조변수로 사용 |
👉 super
예약어 사용
class Camera{
String name;
int sheets;
public void takePicture(){
System.out.println("print 1");
}
}
class PolaroidCamera extends Camera{
int batteryGage;
public void takePicture(){
super.takePicture();
System.out.println("print 2");
System.out.println("print 3");
}
}
final
예약어와 오버라이딩final
예약어 + 메소드는 오버라이딩 금지를 의미한다.
❗️오버라이딩을 금지가 필요한 이유는 때때로 부모 클래스 메소드를 자식 클래스 메소드가 잘못 정의하는 일을 막기 위해서이다.
abstract
예약어를 사용하여 정의된 메소드로, 메소드의 시그니처만 정의된 메소드이다.
추상 메소드는 의미가 없지만, 자식 클래스에서 오버라이딩을 했을 때 의미가 있다. 즉, 상속과 관련이 깊은 예약어이다.
abstract
예약어를 사용하여 정의된 클래스로, 일반적으로 하나 이상의 추상 메소드를 포함하지만 추상 메소드가 없는 클래스도 추상 클래스로 선언 가능. 하지만 추상 메소드를 포함하고 있다면 무조건 추상 클래스로 선언되어야 한다.👉 추상 클래스를 사용하면 유지보수의 편의성을 높일 수 있다.
내부 클래스는 정의되는 위치에 따라 멤버 클래스와 지역 클래스로 나뉜다.
멤버 클래스
static
멤버와 instance
멤버로 나뉜다.지역 클래스
멤버 변수나 메소드와 동일한 위치에서 선언되었기 때문에 다른 외부의 클래스에서도 사용 가능
class Outside{
public class Inside{
}
}
public class InnerClassTest{
public static void main(String args[]){
Outside outer = new Outside();
Ouside.Inside inner = outer.new Inside();
}
}
static
예약어를 통해 내부 클래스를 생성하면 외부 클래스 객체를 생성하지 않아도 내부 클래스 객체를 생성할 수 있다.
class Outside{
public static class StaticInner{
}
}
public class InnerClassTest{
public static void main(String args[]){
Ouside.StaticInner sinner = Ouside.StaticInner();
}
}
클래스의 이름이 명시되는 클래스이다.
class Animal{
void eatFood(){
//지역변수와 동일한 범위를 가지기 때문에
//선언된 메소드 블록 내에서만 사용 가능
class snack{
}
}
}
public class InnerClassTest{
public static void main(String args[]){
Animal outer = new Animal();
}
}
new
예약어 뒤에 명시된 클래스가 기존 클래스인 경우 자동으로 이 클래스의 자식 클래스가 된다.추상 클래스는 추상 메소드를 포함하고 있기 때문에 객체를 생성할 수 없다. 따라서 간단하게 이름이 없는 지역 내부 클래스로 만들어 사용하는 것이 편리할 수있다.
abstract class TV{
public abstract void powerOn();
public abstract void powerOff();
}
public class AnonymousTest{
public static void watchTV(TV tv){
tv.powerOn();
tv.powerOff();
}
public static void main(String args[]){
// 이름 없는 지역 내부 클래스 객체 생성
// TV라는 추상 클래스의 객체를 내부 클래스 형태로 생성했기 때문에, 실제로는 TV클래스를 상속한 내부 클래스가 만들어진다.
watchTV(new TV(){
public void powerOn(){
System.out.println("ON");
}
public void powerOff(){
System.out.println("OFF");
}
});
}
}
예) leftObjRef = rightObjRef
leftObjRef
: 부모 클래스rightObjRef
: 자식 클래스자식 클래스에서 부모 클래스로의 할당/형변환은 가능하지만 반대의 경우에는 명시적 형변환을 해야한다.
따라서 내부 특정 클래스 형이 다른 클래스 형으로 변환될 수 있는지의 여부를 수시로 판단해야하는데, 이를 위해 instanceof
연산자를 사용한다.
<생성된 객체 참조 변수> instanceof <class 또는 interface 명>
형변환에 참여한, 서로 상속관계에 있는 두 클래스 간에는 동일한 이름의 변수가 존재하거나 메소드가 오버라이딩 되어있을 수 있다. 이때 생성된 객체 변수를 통해 멤버에 접근할 때 주의해야 한다.
class Parent{
int num = 10;
void printNum(){
System.out.println(num);
}
}
class Child extends Parent{
int num = 20;
void printNum(){
System.out.println(num);
}
}
public class ObjectCastTest{
public static void main(String args[]){
//case 1
//Output is
// 10
// 10
Parent p = new Parent();
p.printNum();
System.out.println(p.num);
//case 2
//Output is
// 20
// 20
Child p = new Child();
p.printNum();
System.out.println(p.num);
//case 3
//Output is
// 20
// 10
Parent p = new Child();
p.printNum();
System.out.println(p.num);
}
}
case3
의 결과가 다른 이유output 20
: 변수에 대한 접근은 객체의 유형에 의해 결정output 10
: 메서드 호출은 할당되는 인스턴스에 의해 결정👉 객체 참조 변수가 변수나 메소드를 참조하는 경우, 참조 관계를 결정하는 시간이 다르기 때문에 나타나는 차이