지구정복
[JAVA] 11/11 | 생성자오버로딩, 정적초기화블럭, final필드, 상수, 객체배열, 상속, 메소드재정의, 부모메소드 호출, fianl클래스와 메소드 본문
[JAVA] 11/11 | 생성자오버로딩, 정적초기화블럭, final필드, 상수, 객체배열, 상속, 메소드재정의, 부모메소드 호출, fianl클래스와 메소드
nooh._.jl 2020. 11. 11. 12:43ㅇ생성자 오버로딩
외부에서 다양한 데이터들을 이용해서 객체를 초기화하려면 생성자도 다양화될 필요가 있다.
또한 혼동을 피하기 위해 생성자의 매개변수명과 필드명을 같이한다음 this. 연산자를 사용해서
매개변수를 필드에 저장한다.
이때 생성자 오버로딩이 많아질 경우 생성자 간에 중복이 발생할 수 있는데 이때는 하나의 생성자 안에
공통되는 필드들을 this. 연산자로 모두 할당하고 다른 생성자에서 this() 연산자로 공통 생성자를 호출한다.
class Car{
//필드
String serial;
String name;
//생성자
Car() {
this("0000", "sonata"); //this()로 맨 밑에 생성자를 호출
}
Car(String name) {
this("0001", name); //this()로 맨 밑에 생성자를 호출
}
//공통으로 생성되는 생성자
Car(String serial, String name) {
this.serial = serial;
this.name = name;
}
}
public class Ex111101 {
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car("grandger");
Car car3 = new Car("0002", "ray");
System.out.println(car1.serial+ car1.name);
System.out.println(car2.serial+ car2.name);
System.out.println(car3.serial+ car3.name);
}
}
ㅇ정적 초기화 블럭
정적(static)은 한 번 값이 정해지면 변하지 않는 경우에 사용한다. static을 사용하면 메모리 할당을 한 번만 하기 때문에
메모리 사용에 이점이 생긴다.
인스턴스 필드는 생성자에서 초기화를 하지만 정적 필드는 객체 생성 없이도 사용해야 하므로 생성자에서 초기화
할 수가 없다. 따라서 정적필드의 초기화를 위해 정적 초기화 블록을 제공한다. 정적블록은 클래스가 메모리로 로딩될 때
자동적으로 실행된다.
또한 초기화블록의 추가적인 기능은 실행문을 사용할 수 있다. 예를 들면 클래스 내부에서는 출력문이 불가능하지만
초기화블록안에서는 가능하다.
class CodeBlock {
String name1;
static String name2;
////초기화 블럭의 목적은 멤버필드의 초기화
//그냥 중괄호만 있으면 인스턴스 초기화 블럭
{
System.out.println("인스턴스 초기화 블럭");
this.name1 = "honggildong";
}
//static이 붙으면 클래스 초기화 블럭
static {
System.out.println("클래스 초기화 블럭");
CodeBlock.name2 = "parkmunsu";
}
}
public class Ex111102 {
public static void main(String[] args) {
System.out.println("시작");
CodeBlock cd = new CodeBlock();
System.out.println(cd.name1);
System.out.println(CodeBlock.name2);
System.out.println("끝");
}
}
밑에 예제는 두 개의 정적필드를 연결해서 새로운 정적 필드에 초기화해주는 것이다.
class Television {
static String company = "Samsung";
static String model = "LCD";
static String info;
static {
info = company+ "-" + model;
}
}
public class TelevisionExample {
public static void main(String[] args) {
System.out.println(Television.info);
}
}
ㅇfinal 필드
final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 중에 수정할 수 없다는 것이다.
class Person {
final String nation = "korea";
final String ssn;
String name;
Person(String ssn, String name) {
this.ssn = ssn;
this.name = name;
}
}
public class Ex111103 {
public static void main(String[] args) {
Person p1 = new Person("123456-1234567", "zzz");
System.out.println(p1.nation);
System.out.println(p1.ssn);
System.out.println(p1.name);
//final 필드는 값 수정 불가
//p1.nation = "usa";
//p1.ssn = "123121-1241554";
p1.name ="dfs";
}
}
ㅇ상수 (static final)
불변의 값을 상수라고 부른다. 불변의 값은 객체마다 저장될 필요가 없는 공용성을 띄며 여러 가지 값으로 초기화될 수
없기 때문에 static fianl 을 사용한다. 상수의 예로는 원주율 파이나 지구의 무게, 둘레 등이 해당한다.
상수는 대문자로 표시하고 사용할 때 객체를 만들지않고 '클래스명.상수명 ' 으로 바로 사용한다.
class Earth {
static final double EARTH_RADIUS = 6400;
static final double EARTH_SURFACE_AREA;
static {
EARTH_SURFACE_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS;
}
}
public class Ex111104 {
public static void main(String[] args) {
System.out.println("지구의 반지름: "+Earth.EARTH_RADIUS);
System.out.println("지구의 표면적: "+Earth.EARTH_SURFACE_AREA);
}
}
이때 다른 메인클래스를 만들어서 Earth 클래스를 사용할 수 있다.
public class Ex111105 {
public static void main(String[] args) {
System.out.println("지구의 반지름: "+Earth.EARTH_RADIUS);
System.out.println("지구의 표면적: "+Earth.EARTH_SURFACE_AREA);
}
}
ㅇ객체 배열 만들기
객체 또한 배열 형태로 생성할 수 있다. 아래 예제를 보면 Book 타입을 가지는 객체배열 arr1을 생성하고
Book 타입을 가지는 3개의 객체를 만든 뒤 객체배열 안에 할당한다.
class Book {
String title;
String author;
int price;
Book(String title, String author, int price) {
this.title = title;
this.author = author;
this.price = price;
}
}
public class Ex111106 {
public static void main(String[] args) {
//Book 타입을 가지는 arr1 객체배열 생성
Book[] arr1 = new Book[3];
Book book1 = new Book("java", "hong", 1000);
Book book2 = new Book("zzz", "asdf", 2000);
Book book3 = new Book("jadf", "qwer", 3000);
//객체배열 안에 객체할당
arr1[0] = book1;
arr1[1] = book2;
arr1[2] = book3;
System.out.println(arr1.length);
for (int i=0; i<3; i++) {
System.out.println(arr1[i].title);
System.out.println(arr1[i].author);
System.out.println(arr1[i].price);
}
//다른 방법으로 출력하기1 : for문 사용
System.out.println("-----------------");
for (int i=0; i<3; i++) {
Book book = arr1[i];
System.out.println(book.title);
System.out.println(book.author);
System.out.println(book.price);
}
//다른 방법으로 출력하기2 : 향상된 for문 사용
System.out.println("-----------------");
for(Book book: arr1) {
System.out.println(book.title);
System.out.println(book.author);
System.out.println(book.price);
}
}
}
ㅇ상속(Ingeritance)
ㅇ두 개의 클래스를 사용하는 방법
1. 클래스를 객체변수로 사용하기(인스턴스)
만약 B에서 A를 사용하고 싶으면 B클래스 안에서 A객체를 생성한다.
has - a 관계 / 부속관계 / 연관관계
class A { }
class B {
A a;
}
2. 클래스를 상속해서 사용하기
is - a 관계 / 상속관계
상속은 이미 개발된 클래스를 재사용해서 새로운 클래스를 만들기 때문에 코드의 중복을 줄여준다.
상속은 부모가 자식을 결정하는 것이 아니라 자식이 부모를 결정한다.
상속을해도 부모 클래스의 모든 필드와 메소드를 물려받는 것은 아니다.
private 접근 제한을 갖는 것은 제외되고, 타 패키지에서 default 접근 제한을 갖는 필드와 메소드도 제외된다.
상속을 받는 자식클래스는 단일 상속밖에 못한다.
즉, 두 개의 부모 클래스를 상속받을 수 없다.
class SubParent {
}
class Parent {
//부속 관계 표현
SubParent sp;
String p;
void viewParent() {
System.out.println("viewParent() 호출");
}
}
class Child1 extends Parent {
String c1;
void viewChild1() {
System.out.println("viewChild1() 호출");
}
}
class Child2 extends Parent {
String c2;
void viewChild2() {
System.out.println("viewChild2() 호출");
}
}
public class Ex111107 {
public static void main(String[] args) {
System.out.println("---자식객체로 부모 메소드 사용---");
Parent p = new Parent();
p.viewParent();
Child1 c1 = new Child1();
Child2 c2 = new Child2();
c1.viewChild1();
c2.viewChild2();
c1.viewParent(); //부모에게 상속받은 부모 메소드를 자식객체에서 사용
c2.viewParent();
//자식객체로 자식과 부모클래스 필드 초기화
c1.c1 = "홍길동";
c2.c2 = "호빵맨";
c1.p = "박문수";
System.out.println("---자식객체로 부모 필드 사용---");
System.out.println("자식 필드: "+c1.c1);
System.out.println("자식 필드: "+c2.c2);
System.out.println("부모 필드: "+c1.p);
}
}
참고
모든 class는 object를 상속한다. 즉, class 클래스명 extends object { }가 있는데 자동적으로 생략된다.
결국 모든 것의 최상위 부모는 object이다.
생성자의 호출로 상속관계를 살펴보자.
c라는 객체변수를 만들면 Child 객체 안에 Parent 객체가 만들어진다.
따라서 Parant의 객체가 먼저 생기고 Child 객체가 생김을 알 수 있다.
또한 this 연산자를 이용해서 참조값을 살펴보면 모두 같음을 알 수 있다.
class Parent {
//디폴트 생성자
Parent() {
System.out.println("Parent 생성자"+this);
}
}
class Child extends Parent {
Child() {
System.out.println("Child 생성자"+this);
}
}
public class Ex111108 {
public static void main(String[] args) {
Child c = new Child();
System.out.println("c: "+c);
}
}
만약 디폴트 생성자가 아닌 다른 생성자를 만들경우를 살펴보자.
오류가 나고 부모객체가 생성이 안되는 것을 알 수 있다. 즉, 상속을 받은 자식 객체를 생성하려면 부모 생성자는
디폴트생성자만 가능하다.
만약 부모클래스의 디폴트생성자가 없을 경우 super()연산자를 통해 부모클래스의 다른 생성자를 불러올 수 있다.
class Parent {
//디폴트 생성자
/*
Parent() {
System.out.println("Parent 생성자"+this);
}
*/
Parent(String data) {
System.out.println("Parent 생성자"+this);
}
}
class Child extends Parent {
Child() {
System.out.println("Child 생성자"+this);
}
}
public class Ex111108 {
public static void main(String[] args) {
Child c = new Child();
System.out.println("c: "+c);
}
}
super연산자로 부모 클래스의 다른 생성자를 불러와보자. 자식클래스의 생성자 첫 줄에 부모클래스 생성자
양식에 맞춰서 입력해야 한다.
아래와 같이 오류가 발생하지 않는 것을 확인할 수 있다.
class Parent {
//디폴트 생성자
/*
Parent() {
System.out.println("Parent 생성자"+this);
}
*/
Parent(String data) {
System.out.println("Parent 생성자"+this);
}
}
class Child extends Parent {
Child() {
super("홍길동"); //여기
System.out.println("Child 생성자"+this);
}
}
public class Ex111108 {
public static void main(String[] args) {
Child c = new Child();
System.out.println("c: "+c);
}
}
다음으로 부모 클래스에 디폴트생성자가 없다고 하고 자식 객체를 이용해서 부모클래스 메소드를 사용해보자.
자식클래스 생성자에서 super() 을 사용할 때 부모생성자의 양식에 맞게 집어넣어야 한다.
class Parent {
Parent(String data) {
System.out.println("Parent 생성자"+this);
}
void viewParent() {
System.out.println("viewParent() 호출");
}
}
class Child extends Parent {
Child(String data) {
super(data); //또는 super("hong"); 아무 문자열 넣으면 된다.
System.out.println("Child 생성자"+this);
}
}
public class Ex111109 {
public static void main(String[] args) {
Child c = new Child("hong");
c.viewParent();
}
}
ㅇ메소드 재정의(@Override)
메소드 오버라이딩은 상속된메소드의 내용이 자식 클래스에 맞지않을 경우, 자식 클래스에서 동일한 메소드를 재정의하는 것을 말한다. 메소드가 오버라이딩되면 부모 객체의 메소드는 숨겨져서 사용할 수 없고, 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.
다음과 같은 규칙이 있다.
- 부모의 메소드와 동일한 시그너처(리턴 타입, 메소드 이름, 매개변수리스트)를 가져야 한다.
- 접근 제한을 더 강하게 오버라이딩 할 수 없다.
- 새로운 예외를 throws 할 수 없다.
여기서 부모 메소드가 public 접근제한을 가지고 있는데 오버라이딩하는 자식 메소드는 default나 private 접근 제한으로 수정할 수 없다는 뜻이다.
또한 오버로딩과 차이점을 알아두자.
오버로딩은 같은 이름의 메서드 여러 개를 가지면서 매개변수의 유형과 개수가 다르도록 하는 것이고
오버라이딩은 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의해서 사용하는 것을 의미한다.
class Parent {
void viewParent() {
System.out.println("viewParent() 호출");
}
}
class Child extends Parent {
void viewChild() {
System.out.println("viewChild() 호출");
}
//부모 메소드인 viewParent()를 재정의 (오버라이딩)
@Overide
void viewParent() {
System.out.println("Child viewParent() 호출");
}
}
public class Ex111110 {
public static void main(String[] args) {
Child c = new Child();
c.viewParent();
}
}
ㅇ부모 메소드 호출(super)
부모 클래스의 메소드는 숨겨지고 오버라이딩된 자식 메소드만 사용된다. 그러나 자식 클래스 내부에서 오버라이딩된 부모 클래스의메소드를 호출해야 하는 상황이 발생한다면 명시적으로 super 키워드를 붙여서 부모 메소드를 호출할 수 있다. super는 부모 객체를 참조하고 있기 때문에 부모 메소드에 직접 접근할 수 있다.
class Parent {
void viewParent() {
System.out.println("viewParent() 호출");
}
}
class Child extends Parent {
void viewChild() {
super.viewParent(); //부모 메소드 호출
System.out.println("viewChild() 호출");
}
//부모 메소드인 viewParent()를 재정의 (오버라이딩)
@Override
void viewParent() {
System.out.println("Child viewParent() 호출");
}
}
public class Ex111110 {
public static void main(String[] args) {
Child c = new Child();
c.viewChild();
}
}
부모 메소드 호출 예제
class Airplane {
void land() {
System.out.println("착륙");
}
void fly() {
System.out.println("비행");
}
void takeOff() {
System.out.println("이륙");
}
}
class SupersonicAirplane extends Airplane {
static final int NORMAL = 1;
static final int SUPERSONIC = 2;
int flyMode = NORMAL;
@Override
void fly() {
if(flyMode == SUPERSONIC) {
System.out.println("초음속 비행");
} else {
//부모클래스의 메소드 호출
super.fly();
}
}
}
public class Ex111111 {
public static void main(String[] args) {
SupersonicAirplane sa = new SupersonicAirplane();
sa.takeOff();
sa.fly();
sa.flyMode = SupersonicAirplane.SUPERSONIC;
sa.fly();
sa.flyMode = SupersonicAirplane.NORMAL;
sa.fly();
sa.land();
}
}
ㅇfinal 클래스와 final 메소드
ㅇ상속할 수 없는 final 클래스
클래스를 선언할 때 final 키워드를 붙이면 이 클래스는 최종적인 클래스이므로 상속할 수 없다.
즉 부모 클래스가 될 수 없어서 자식 클래스들은 이 클래스를 상속할 수 없다. 따라서 상속을 거부시킬 수 있다.
final class Parent {
}
class Child extends Parent {
}
public class Ex111112 {
public static void main(String[] args) {
Child c = new Child();
}
}
ㅇ오버라이딩할 수 없는 final 메소드
메소드를 선언할 때 final 키워드를 붙이게 되면 이 메소드는 최종적인 메소드이므로 오버라이딩할 수 없는 메소드가 된다.
'데이터 엔지니어링 정복 > JAVA & JSP' 카테고리의 다른 글
[JAVA] 11/13 | 기본API, Object클래스, String클래스 (0) | 2020.11.13 |
---|---|
[JAVA] 11/12 | 접근제한자, getter와 setter, 패키지 (0) | 2020.11.12 |
[JAVA] 11/10 | 메소드의 매개변수 배열로 받기, 전역변수와 지역변수, 정적멤버 (0) | 2020.11.10 |
[JAVA] 인터페이스 (0) | 2020.11.04 |
[JAVA] 추상 클래스 (0) | 2020.11.04 |