제네릭을 엄격한 타입검사를 통해 안전성을 높여준다.(컴파일 시에 미리 타입을 강하게 체크해서 에러 사전 방지)
타입 파라미터(= 매개변수 타입)
일반적으로 대문자 알파벳 한 문자로 표현
실핸클래스에서 타입 파라미터 자리에 구체적인 타입을 지정해 주어야 한다.
타입매개변수로 전달되는 타입의 종류를 제한하고 싶은 경우가 있다. 특정한 종류의 객체들만을 받게 하고 싶은 경우 사용하는 것이 한정된 타입매개변수이다. 이 기능을 사용하기 위해서는 extends나 super 키워드를 사용한다.
[<타입 매개변수 extends 슈퍼 클래스명> : 슈퍼 클래스를 상속받은 하위 객체들만 가능하다.
<타입 매개변수 extends 인터페이스명> : 인터페이스로부터 구현 클래스 객체들만 가능하다.]
제네릭 타입을 매개변수의 타입이나 리턴타입으로 사용할 때 와일드 카드로 명시할 수도 있다.
와일드 카드는 ?로 표현 : 어떤타입 매개변수도 지정할 수 있다는 의미. 현재 객체의 타입 매개변수와 같지 않은 타입의 인자로 받을 수 있다.
[<?> 제한없음
<? extends 슈퍼 클래스> : 슈퍼 클래스이거나 슈퍼클래스로부터 상속받은 하위 객체만 타입으로 지정
<? super 서브 클래스> : 서브 클래스이거나 서브 클래스가 상속하는 상위 객체만 타입으로 지정]
제네릭 메서드 : 매개변수 타입과 리턴 타입으로 타입 매개변수를 갖는 메소드, 타입 매개변수를 리턴 타입과 매개변수에 사용
[public <타입매개변수,...> 리턴타입 메서드명(매개변수,...){ }]
호출
명시적으로 타입 인자를 지정
리턴타입 변수 = <타입 인자> 메서드명(인자값);
인자값을 보고 타입 인자를 추정
리턴타입 변수 = 메서드명(인자값);
프로그램 : 데이터를 표현하고 데이터를 처리하는 것
자료구조 : 데이터를 효율적으로 사용되도록 구조적으로 저장하고 관리하는 것 , 데이터를 추가, 수정, 삭제, 검색이 효율적으로 함(정형화하거 있는 데이터 저장방식 : 배열, 리스트, 스택, 트리, 큐, 해시 정리)
프로그램 구성 = 코드 + 데이터(데이터 관리 기술 = 자료구조)
알고리즘 : 자료구조가 데이터의표현돠 저장에 대한 방법이라면 알고리즘은 표현되거나 저장된 데이터를 대상으로 '문제를 해결하는 방법'이다. 알고리즘과 자료구조는 밀접하게 관련되어 있다.(정렬, 탐색 등으로 정리)
컬렉션 : 자료구조와 알고리즘을 인터페이스,클래스로 구현하려 제공하는 자바 API, 컬렉션 괸련 클래스들은 많은 양의 인스턴스를 다양한 형태로 저장하는 기능을 제공한다. 다양하고 효율적으로 인스턴스의 저장이 가능. 기본적으로 객체들의 집합을 의미. 객체 단위의 연산을 제공,
배열의 문제점 : 불특정 다수의 객체를 저장하기에는 문제, 객체를 저장하려면 어디가 비어있는지 확인해야함
배열과 컬렉션의 차이점
배열은 크기를 지정해랴만 하고 한번 지정된 크리를 변경할 수 없지만 컬렉션은 크기에 제약이 없다.
배열은 중간에 값을 추가 해준다거나 삭제 또는 정렬을 하고자 할 때 개발자가 직접 코드로써 로직을 작성해야 하지만 컬렉션에서는 이미 자료구조적인 알고리즘이 메소드로 정의되어 있기 때문에 메서드 호출만으로 방대한 데이터를 효율적으로, 구조적으로 관리할 수 있다.
배열은 한가지 타입의 여러 개의 데이터를 보관할 수 있다면 컬렉션은 별도의 제네릭 설정을 하지 않을 경우 여러가지 타입의 여러 개의 데이터를 보관할 수 있다.
컬렉션 프레임워크(컬레션 인터페이스, 컬렉션 클래스) : 자료 구조에 사용하는 인터페이스, 인터페이스로 구현된 클래스, 괸련된 알고리즘(java.util 패키지에 존재), 구체적이고 체계화된 API(프로그래밍 접근 방식)을 제공한다는 의미.
프레임워크 : 자료구조에 상관없이 추가하려면 add(), 삭제하려면 remove() 같이 표준화된 접근 받식을 제공하는 것. 사용 방법을 미리 정해 놓은 라이브러리를 말한다.
인터페이스 및 클래스
설명
인터페이스
컬렉션에서 공통으로 제공해야 하는 연산들을 추상적으로 정의하고, 구체적인 구현은 구현 클래스에 위임한다.
구현 클래스
컬렉션을 실제로는 어떤 자료구조를 적용해 구현하느냐에 따라 컬렉션의 종류가 달라진다.
알고리즘
컬렉션마다 유용하게 사용할 수 있는 메서드를 의미한다.
인터페이스
설명
Collection
모든 자료 구조의 부모 인터페이스로서 객체의 모임을 나타낸다
Set
집합(중복된 원소를 가지지 않는)을 나타내는 자료구조
List
순서가 있는 자료 구조로 중복된 원소를 가질 수 있다.
Map
키와 값들이 연관되어 있는 사전과 같은 자료구조
Queue
들어온 순서대로 나가는 FIFO(First - In-First-Out) 자료구조
Collection은 거의 모든 컬렉션 인터페이스의 부모 인터페이스에 해당. 모든 컬렉션 클래스들이 Collection 인터페이스를 구현하고 있기 때문에 Collection에 들어 있는 메소드들은 거의 대부분 컬렉션 클래스(구현 클래스)에서 사용할 수 있다.
기능
메서드
설명
객체추가
boolean add(E e)
주어진 객체 맨 끝에 추가
객체비우기
void clear()
컬렉션의 모든 요소 비우기
여부판단
boolean isEmpty()
컬렉셩의 요소 존재 여부 판단
객체삭제
boolean remove(Object o)
매개변수로 전달받은 객체를 컬렉션에서 삭제
요소 수 반환
int size
컬렉션의 요수 개수
리스트 : 순서를 가지는 원소들의 모듬으로 중복된 원소를 가질 수 있다. 위치를 사용하여 원소에 접근한다.
ArrayList클래스 : List 인터페이스의 구현 클래스. 선형구조(데이터를 구조화하는 기본 표현 방식) 배열을 이용해 자료를 관리한다. 순차구조방식, 연결 구조 방식이 있다.기본 생성자로 ArrayList 객체를 생성하면 내부에 10개의 객체를 저장할 수 있는 초기 용량(capacity)을 가지게 된다. 저장되는 객체 수가 늘어나면 용량이 자동 증가. 동일한 데이터도 여러번 저장될 수 있으므로 맨 처음에 있는 데이터의 위치가 반환된다.
순차구조 : 항목 간의 논리적인 순서와 메모리에 저장되는 물리적 순서가 같은 구조(배열의 원리와 동일)
indexOf(): 특정한 데이터가 저장된 위치를 알 수 있다.
lastIndexOf (): 검색을 반대 방향으로 할 때 사용.
배열: 배열명.length
문자열: 문자열참조변수 또는 "문자".length()
리스트 구현 클래스 : 리스트명.size()
ArrayList에 있는 원소에 접근하는 하나의 방법은 반복자를 사용하는 것
반복자: 특별한 타입의 객체로 컬렉션의 원소들에 접근하는 것이 목적으로 객체 집합의 항목들에 차례대로 접근하는데 하용하는 객체
Iterator : 컬렉션에 저장된 데이터의 위치정보를 포함한 커서가 있어서 인덱스 등을 사용하지 않고 쉽게 데이터에 접근할 수 있다.
Collection view : 컬렉션에 저장된 모든 데이터를 순차으로 접근하여 사용할 목적으로 사용하는 Iterator
String 객체와 다르게 StringBuffer 객체는 +연산자를 통해 매번 새로운 String 객체를 생성하지 않고, append()메서드를 통해 String 객체 하나만으로도 문자열을 다룰 수 있다.
버퍼(buffer: 데이더를 임시로 저장하는 메모리, 문자열을 처리하기 위해 확보한 넉넉한 메모리 공간)에 문자열 저장,
버퍼 내부에서 추가, 수정, 삭제 작업 가능
버퍼에서 문자열을 수정하므로 String처럼 새로운 문자열을 만들 필요가 없다.
차이 : 생성된 인스턴스를 동시에 여러 곳에서 접근하여 사용할 때
StringBuffer클래스는 동시에 접근하는 상황에 대처할 수 있고, StringBuilder는 대처할 수 없다.
다만 일반적으로 StringBuilder가 더 효율적
capacity() : 버퍼의 크기
length() : 문자열의 길이 수
StringBuffer만이 제공하는 가장 중요한 메서드
StringBuffer append(String s) : 매개변수로 전달받은 값을 추가한다. 문자열 버퍼의 끝에 여러 종류의 데이터를 추가하는 메서드
StringBuffer delete(int start, int end) : 매개변수로 전달받은 인덱스 사이의 문자열 삭제한다.
StringBuffer insert(int offset, String str) : 특정 위치에 여러 종류의 데이터를 문자열로 바꾸어 삽입하게 된다.
Math 클래스 : 모든 메서드는 정적 메서드이므로, 객체를 생성하지 않고도 바로 사용할 수 있다.
Math 클래스의 클래스필드
Math.E : 오일러 수, 자연로그의 밑 값
Math.PI : 원주율
Wrapper(기본형을 참조형으로 추상화하는 과정) 클래스 : 기본형과 참조형간의 형변환을 가능하게 해주는 클래스가 래퍼 클래스. 메소드의 인수로 객체 타입만이 요구되면, 기본타입의 데이터를 그대로 사용할 수는 없다. 이때에는 기본 타입의 데이터를 먼저 객체로 변환한 후 작업을 수행해야 한다.
Wrapper 객체 : 기본 타입 값을 내부에 두고 포장하는 객체
Wrapper 클래스는 Wrapper라는 이름의 클래스가 존재하는 것이 아니라 java.lang 패키지에 있는 클래스 중 자바의 기본 데이터 타입과 매핑되는 클래스들을 의미
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double
char
Character
boolean
Boolean
자료형에서 값을 다루는 기본형과 주소를 다루는 참조형간위 형변환을 박싱(boxing)과 언박싱(unboxing)
박싱(valueOf())
값을 다루는 기본형 데이터를 잠조형 데이터로 변환하는 것을 의미
메모리에서 변환관점으로 보면 스택에 저장된 값을 힙으로 복사하는 과정 묵시적(자동)으로 이루어진다.
자동박싱(auto-boxing) : 포장 클래스 타입에 기본값이 대입될 경우 발생
Integer iValue;
iValue = 10;
//정수를 자동으로 Integer 객체로 포장
언박싱(기본타입명 + Value())
반대로 힙에 저장된 값을 스택에 복사하는 과정이다.
힙의 데이터를 기본형으로 변환할 때 언박싱이 일어난다.
자동언박싱(auto-unboxing) : 기본 타입에 포장 객체가 대입될 경우 발생
System.out.println(iValue+1);
//iValue는 자동으로 int형으로 변환
#문자열을 기본 타입 값으로 변환 : parse + 기본타입 명 -> 정적 메소드
int num = Integer.parseInt("10") //문자열을 int형으로 반환
double num = Double.parseDouble("1.2") //문자열을 double형으로 반환
문자열 분리 방법 : String의 split() 메서드 사용(정규표현식을 구분자로 해서 부분 문자열 분리, 배열에 저장하고 리턴), java.util.StringTokenizer클래스 이용
문자열을 구분자를 사용해 나누어 준다. 나누어진 문자열을 토큰이라고 한다. 구분자를 지정하지 않으면 공백이 구분자가 된다.
StringTokenizer클래스
생성자
설명
StringTokenizer(String str)
주어진 문자열을 위한 StringTokenizer 객체를 생성한다.
StringTokenizer(String str, String delim)
주어진 문자열을 위한 위한 StringTokenizer 객체를 샹성한다. 분리자로 delim을 사용한다.
추상(abstract) : " 여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용."
추상 클래스 : 클래스 선언 시 앞에 abstract,
추상클래스 = 필드 + 일반 메서드 + 추상 메서드
메서드 선언 시 본문을 구현하지 않으면 abstract로 선언(abstract 선언 메서드는 본문X)
추상 메서드를 포함하는 클래스는 선언부에 abstract 명시(추상메서드를 하나라도 포함하면 추상클래스)
실체 클래스(객체를 직접 생성할 수 있는 클래스)들의 공통되는 필드와 메서드 정의한 클래스(부모 클래스 역할 : 단독 객체 X)
기능이 무엇인지만을 정의하고 어떻게 구현되는지는 정의 하지 않는다.(메소드가 미완성이므로 추상클래스로는 객체(인스턴스)를 생성할 수 없다.)
그래서 하나의 추상클래스에 정의된 기능을 여러 개의 하위클래스에서 서로 다른 형태로 구현하여 사용할 수 있다.(반드시 재정의 해 주어야 한다.)
설계와 구현을 분리한다.(슈퍼 클래스에서는 개념 정의, 각 서브 클래스에서 구체적 행위 구현)
계층적 상속 관계를 갖는 클래스 구조를 만들 때
[접근제어자 abstract class 클래스이름{
접근제어자 abstract 반환형 추상메서드명();
}]
용도
실체클래스의 공통된 필드와 메소드의 이름 동일할 목적
실체 클래스를 작성할 때 시간 절약(추가적인 필드와 메소드만 선언)
실체 클래스 설계 규격을 만들고자 할 때(필드와 메소드를 추상 클래스에 미리 정의, 추상클래스를 무조건 상속 받아 작성)
추상메소드(몸체가 없어 항상 세미콜론으로 종료)와 오버라이딩
메소드 이름은 동일하지만, 실행내용이 실체 클래스마다 다른 메소드.
선언되어 있으나 구현되어 있지 않은 메소드 서브클래스에서 오버라이딩하여 구현해야함.
추상클래스에는 메소드의 선언부만 작성(추상메서드)
실체 클래스에서 메서드의 실행 내용 작성(오버라이딩)
추상클래스 종류
추상 메서드를 하나라도 가진 클래스 클래스 앞에 abstract
추상 메서드가 하나도 없지만 abstract로 선언된 클래스
다형성 : 동일한 부모 클래스에서 상속된 서브 클래스의 객체들을 하나의 타입으로 취급할 수 있게 해준다. 따라서 서로 다른 타입들을 받아서 하나의 코드로 처리할 수 있다. 상속관계에서는 부모 타입의 변수에 모든 자식 객체를 대입해 이용할 수 있다. 하나의 타입에 여러 객체를 대입할 수 있다는 의미.(다양한 실행 결과를 얻는 것) 다형성은 코드 구조가 향상되고 가독성을 증가시키며, 가능한 프로그램을 작성하는 객체지향의 중요한 개념 중의 하나. 메소드의 매개변수로 사용(유지보수 용이)
구현기술 : 상속 또는 인터페이스의 자동 타입 변환, 오버라이딩
#업캐스팅
[슈퍼클래스참조변수 = new 서브클래스생성자(); ]
상위클래스로 형변환
컴파일러에 의해 자동으로 형변환된다.
업캐스팅이 된 이후 상위 클래스형의 참조변수는 상위 클래스로부터 상속받은 멤버만 호출할 수 있다.
업캐스팅이 되더라도 하위 클래스에서 재정의된 메서드는 참조할 수 있고. 상위 클래스로부터 상속받은 메서드는 은닉된다.
#다운캐스팅(강제타입변환) : 부모타입을 자식 타입으로 변환하는 것, 서브 클래스 참조 변수로 슈퍼 클래스 객체를 참조하는 것으로 본다면 컴파일 오류가 발생. //자식 타입을 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때
[자식클래스참조변수 = (자식클래스)부모클래스타입; ]
하위클래스 = 상위클래스
하위 클래스로 형변환
컴파일러에 의해 자동으로 형변환 X
참조 가능한 영역이 확대되는 의리를 갖는다.
package practice;
class A{
int a = 10;
void b() {
System.out.println("A");
}
}
class AA extends A{
int a = 20;
@Override
void b() {
System.out.println("AA");
}
void c () {
System.out.println("C");
}
}
class BB extends A{
int a = 30;
@Override
void b() {
System.out.println("BB");
}
void d() {
System.out.println("D");
}
}
public class ClassTest {
public static void main(String args[]) {
A z = new AA(); //AA객체를 생성해서 A 타입의 레퍼런스에 할당한다. 업캐스팅(자식클래스를 부모클래스로)
System.out.println(z.a); //슈퍼 클래스의 필드 접근
z.b();//슈퍼 클래스의 메소드 접근. 단 서브클래스에서 메서드 오버라이딩되면 오버라이딩 메서드가 우선접근.
//z.c(); A에는 c 메소드가 없음.
((AA)z).c();//서브클래스의 메서드 접근
System.out.println(((AA)z).a);//서브클래스의 필드 접근
AA a1 = (AA)z;
System.out.println(a1.a);//서브클래스의 필드 접근
a1.c();
z= new BB(); //업캐스팅. 슈퍼클래스참조변수= new 서브클래스의 생성자()
System.out.println(z.a);
z.b();
((BB)z).d();
System.out.println(((BB)z).a);
BB b1 = (BB)z; //다운 캐스팅
b1.d();
System.out.println(b1.a);
}
}
package practice;
class Shape{
public void draw() {
System.out.println("Shape");
}
}
class Line extends Shape{
@Override
public void draw() {
System.out.println("Line");
}
}
public class MethodOverridingExample {
public static void paint(Shape p) {
p.draw();
}
public static void main(String args[]) {
Line line = new Line();
paint(line);//Line.draw
paint(new Shape());
paint(new Line());
}
}
객체의 타입을 알아내는 방법 : 객체가 특정 클래스로부터 생성된(상속된) 객체인지를 판별하기 위해 사용할 수 있는 연산자가 instanceof. 반환값. true, false
[참조변수 instanceof 클래스명]
#인터페이스: 여러 프로그램네서 사용할 멤버(필드, 메서드)를 일관되게 하기 위한 기술 명세, 표준규격을 나타냄 인터페이스 변경시키려면 이 인터페이스를 구현하였던 모든 클래스가 동작하지 않게 된다. 이런 경우를 대비하여서 인터페이스도 상속을 받아서 확장시킬 수 있도록 한다.
[public interface 인터페이스명{
( public static final) 자료형 변수명 = 값 ; //인터페이스에 선언된 필드는 모두 상수이기 때문에 생략가능
(public abstract) 반환형 메서드명1(); //추상메서드만 정의할 수 있기에 생략가능
public abstract 반환형 메서드명2();
}
접근제어자 interface 인터페이스명{
(접근제어자 제어자 ) 자료형 변수명 = 값 ; //인터페이스에 선언된 필드는 모두 상수이기 때문에 생략가능
(접근제어자 제어자) 반환형 메서드명1(); //추상메서드만 정의할 수 있기에 생략가능
public abstract 반환형 메서드명2();
}
]
추상메소드와 상수(객체를 생성할 수 없는 인터페이스에서 필드를 선언한다는 것은 객체 생성과 관계없이 사용할 수 있는 static으로 선언 데이터 저장하지 않음)로 이루어짐. 상수명은 대문자로 작성.선언과 동시에 초기값 지정
메서드를 미리 선언(어떻게 입력 받고 어떤 결과값을 반환할 것이라는 사용법)
구현은 비워 놓은 추상 메서드로 만든다. 메서드의 헤더를 선언.
인터페이스에는 필드(멤버변수) 선언불가
#구현 클래스 선언 : 메서드의 선언부가 정확히 일치 해야한다.
[public class 클래스명 implements 인터페이스명{
반환형 추상메서드1(){//재정의
}
반환형 추상메서드2(){//재정의
}
}]
#디폴트 메소드 선언 : 인터페이스를 구현하는 클래스에 자동 상속되는 메소드, default 키워드를 반드시 붙여야한다. 기본적으로 public 접근제한
[public default 리턴타입 메서드명(매개변수,...){...}]
#정적 메서드 선언 : 인스턴스 생성과 상관없이 바로 사용할 수 있다. 인타페이스를 구현하고 있는 모든 객체에서 자주 사용하는 유용한 기능을 제공하는데 있다.
[public static 리턴타입 메서드명(매개변수, ...){...}]
#private 메소드 선언 : 외부접근 제한, default 메서드에서만 사용할 목적으로 본문을 구현
[private 리턴타입 메서드명(매개변수,...){...}]
일반 클래스
추상 클래스
인터페이스
매서드의 재정의
선택적
강제적
강제적
상속, 구현시 예약어
extends
extends
implements
필드 선언 가능 여부
가능
가능
불가능
상수 선언 가능 여부
가능
가능
가능
생성자 선언 여부
가능
가능
불가능
추상 메서드 포함 여부
불포함
포함
포함
객체 생성 가능 여부
가능
불가능
불가능
다중 상속 가능 여부
불가능
불가능
가능
내부클래스: 클래스 멤버로 선언된 클래스<클래스 생성 시 바이트 코드 따로 생성>
멤버 클래스
인스턴스 멤버 클래스
class outer{ class inner{...} }
outer객체를 생성해야만 사용할 수 있는 inner 내부 클래스
멤버 클래스
정적 멤버 클래스
class outer{ static class inner{...} }
outer 클래스로 바로 접근할 수 있는 inner 내부 클래스
멤버 클래스
바이트코드 파일의 이름
외부 클래스명$내부클래스.class
로컬 클래스
class outer{ void method(){ class inner{...} } }
method()가 실행할 때만 사용할 수 있는 inner 중첩 클래스
로컬 클래스
바이트코드 파일의 이름
외부 클래스명$1내부클래스.class
클래스가 여러 클래스와 관계를 맺는 경우에는 독립적으로 선언하는 것이 좋으나, 특정(하나의) 클래스와 관계를 맺을 경우에는 관계 클래스를 클래스 내부에 선언하는 것이 좋다.
내부 클래스를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있다.
외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.(하나의 장소에서만 사용되는 클래스들을 한 곳에 모을 수 있다.)
#인스턴스 멤버 클래스(non-static 내부 클래스) : 내부 클래스의 멤버들은 static이 될수 없다.
[접근제어자 class 외부클래스명{
[public | private: 외부 클래스 내부에서 접근가능] class 내부클래스명{
}
}]
#정적 멤버 클래스(static 내부 클래스): 내부 클래스는 외부 클래스의 일반멤버 변수는 참조할 수 없다. 정적멤버 클래스는 주로 default or public 접근 제한을 가짐
[접근제어자 class 외부클래스명{
[public |private: 외부 클래스 내부에서 접근가능] static class 내부클래스명{
}
}]
지역 내부 클래스(로컬클래스): 외부 클래스의 메서드에서 사용하기 위해 메서드 내에 선언하는 내부 클래스
생성자 또는 메소드 내부에 선언된 클래스를 늬미하며 생성자와 메소드가 실행될 동안에만 객체를 생성할 수 있다.
보통 메소드 내부에 선언하여 사용하며, 메소드의 실행이 끝나는 순간 메소드의 모든 멤버가 사라진다.
로컬 클래스 내부에는 필드, 생성자, 메서드 선언이 올 수 있으며 정적 필드와 정적 메서드는 JDK17부터 선언이 가능하다.
내부 인터페이스: 클래스 멤버로 선언된 인터페이스
#무명(익명) 클래스 : 클래스 몸체는 정의되지만 이름이 없는 클래스, 정의부와 생성부가 하나로 묶여 있는 클래스로 선언과 동시에 객체가 생성된다. 한번만 사용하며 재참조가 불가능, 하나의 객체만을 생성하는 일회용 클래스
[new 상위클래스(일반클래스,추상클래스, 인터페이스 모두가능)명(생성자 매개변수){ }]
#명시적인 구현 클래스 작성 생략하고 바로 구현 객체를 얻는 방법(이름 없는 구현 클래스 선언과 동시에 객체 샹성)
객체지향 언어의 장점은 유지 보수성이 높은 시스템을 개발할 수 있다는 것이다. 유지보수성을 높이는 가장 기본적인 원칙은 " 중복을 제거하는 것"(공통 멤버를 가지는 클래스들 간의 공통점이 있는지, is-a 관계 성립)
상속: 기존에 존재하는 유사한 클래스로부터 속성괴 동작을 그대로 가져다 사용하면서 자신이 필요한 기능을 추가하는 기법, 자식 클래스가 부모클래스의 멤버를 쿨려받는 것(is - a 관계: ~은 ~이다.)
[클래스 정의 다음에 extends(자식 클래스가 상속할 부모 클래스를 지정하는 키워드)를 써주고 수퍼 클래스 이름]
이미 존재하는 클래스(상속하는 클래스, 내용이 정의된 클래스): 슈퍼클래스, 부모 클래스, 베이스 클래스
상속을 받는 클래스(가져다 사용하는 클래스): 서브 클래스, 자식 클래스, 파생 클래스
기존의 클래스를 재사용해서 새로운 클래스를 작성하는 것
하위클래스는 상위 클래스의 멤버개수보다 적을 수 없다.(같거나 많다)
공통 부분은 부모클래스에서 관리하고 개별부분은 자식클래스에서 관리한다.
상속 효과
부모 클래스 재사용해 자식 클래스 빨리 개발 가능
반복된 코드 중복 줄임
유지 보수 편리성 제공
객체 다형성 구현 가능
상속 대상 제한
부모클래스의 private 접근 갖는 필드와 메소드 제외
부모 클래스가 다른 패키지에 있을 경우, default 접든 갖는 필드와 메서드도 제외
#클래스 상속
[접근 제한자 class 클래스명 extends 부모 클래스명{ }]
자바는 단일 상속 - 부모 클래스 나열 불가
class 자식 클래스 extends 부모 클래스1, 부모 클래스2 { }(X)
대개 슈퍼클래스는 추상적이고 서브 클래스는 구체적이다.
슈퍼 클래스를 변경하면 서브 클래스에 영향을 준다.
super: 하위클래스에서 상위 클래스로부터 상속받은 멤버를 참조할 때 사용하는 참조변수. 객체 자신을 참조하는 참조변수인 this처럼 보든 하위 클래스의 객체에는 상위 클래스를 참조하는 참조변수 super가 있다.
super.필드명;
상위 클래스의 클래스 변수나 접근제어에 의해 접근이 가능한 필드에 접근한다.
super.메서드();
상위 클래스의 메서드에 접근한다.
super();
상위 클래스의 생성자에 접근한다.
접근제어자 class 자식클래스명(this) extends 부모클래스명(super){
}
재정의 (오버라이딩)
메서드 오버라이딩 : 소브 클래스가 필요에 따라 상속된 메소드를 다시 정의하여 사용하는 것. 상속 받은 메소드들을 자기 자신의 필요에 맞추어 변경할 수 있다. 부모클래스에서 정의된 메서드를 자식 클래스에서 재정의해서 사용할 수 있는데 이것을 자식 클래스에서 부모 클래스의 메서드를 오버라이딩한다고 표현. 상위클래스로부터 상속받은 메서드의 내용을 상속받는 클래스에 맞게 변경하는 것을 메서드 오버라이딩이라고 한다.
메소드의 헤더부분은 수퍼 클래스의 헤더와 동일하다. 수퍼 클래스의 메소드와 동일한 시그니처를 가져야 한다(메소드의 이름, 반환형, 매개변수의 개수와 데이터 타입이 일치하여야 한다.)
접근 지정자의 경우 슈퍼클리스의 메소드보다 더 좁은 범위로 변경할 수 없다. (public을 default나 private로 수정 불가, 반대로 default는 public 으로 수정가능)
private.static.final 메소드는 재정의할 수 없다.
메서드 호출 순위는 오버라이딩한 메서드가 부모 클래스의 메서드보다 높다.
어노테이션 : 프로그램에게 추가적인 정보를 제공해주는 메타데이터
컴파일러에게 코드 작성 문법 에러 체크하도록 정보 제공
소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동 생성하게 정보 제공
실행 시 특정 기능 실행하도록 정보 제공
메타데이터 : 다른 데이터를 설명해 주는 데이터
@Override 어노테이션
컴파일러에게 부모 클래스의 메소드 선언부와 동일한지 검사 지시
메서드 오버로딩
메서드 오버라이딩
선언
같은 클래스에서 동일한 이름의 메서드 중복 작성
서브 클래스에서 슈퍼 클래스에 있는 메서드와 동일한 이름의 메서드 재정의
괸계
동일한 클래스
상속 관계
목적
이름이 같은 여러 개의 메서드를 중복 작성하여 사용의 편의성 향상.
슈퍼 클래스에 구현된 메서드를 무시하고 서브 클래스에서 새로운 기능의 메서드를 재정의하고자 함
조건
메서드 이름은 반드시 동일하고 매개변수 타입이나 개수가 달라야 함
메서드의 이름, 매개변수 타입과 개수, 리턴 차입이 모두 동일하여야 성립
바인딩
정적 바인딩. 호출될 메서드는 컴파일 시에 결정
동적 바인딩. 실행 시간에 오버라이딩된 메서드 찾아 호출
super()문도 생성자에서만 사용할 수 있으며 자신이 아닌 부모 생성자를 호출할 때 사용한다.
모든 클래스는 생성자가 반드시 존재한다.(컴파일러가 비어있는 기본 생성자를 생성)
자식 클래스의 모든 생성자는 첫 번째 줄에 super()문이 자동 추가(생성자에서 super()문을 사용하지 않았을 때만 자동 추가)
new 다음에 선언된 생성자를 호출(메서드 호출해 객체를 사용할 수 있도록 준비하는 역할 수행)
#기본 생성자
[public 클래스( ){ }]
모든 클래스는 생성자가 반드시 존재, 하나 이상 가질 수 있음
생성자 선언을 생략하면 컴파일러는 기본 생성자 추가
#생성자 오버로딩(Overloading) : 매개변수의 타입, 개수가 다른 생성자 여러 개 선언(단지, 순서가 바뀐 것은 오버로딩이 아님)
[public 클래스([매개변수]){
...
}
public 클래스([매개변수, 매개변수,...]{
...
})]
다른 생성자 호출( this() ) : 현재 실행중인 인스턴스의 주솟값을 나타냄.this는 사용되는 곳에 따라 값이 달라짐
생성자 오버로딩되면 생성자 간의 중복된 코드가 발생
초기화 내용을 한 생성자에 몰아 작성
다른 생성자는 초기화 내용을 작성한 생성자를 this()로 호출
예약어 this.
클래스의 멤버를 가리킬 때
생성자를 호출 할 때
자신의 주솟값을 전달하고 싶을 때
정적 멤버(static member) : 클래스의 특정 인스턴스만 사용하는 게 아니라 해당클래스로 생성된 모든 인스턴스가 공유하는 멤버이다. 필드와 메서드를 공유 멤버로 선언할 수 있으며 static 키워드 사용한다. 프로그램이 시작될 때 딱 한번 메모리의 코드 영역에 할당되며 프로그램이 종료될 때까지 유지되는 특징을 가진다.
클래스에 소속된 멤버
객체 내부에 존재하지 않고,메소드 영역에 존재
객체를 생성하지 않고 클래스로 바로 접근해 사용
#정적 멤버 선언 : 필드 또는 메소드 선언 할 때 static 붙임
[public class 클래스{
//정적 필드
static 자료형 필드;
//정적 메서드
publid static 리턴타입 메서드(매개변수)
}]
인스턴스 변수: 객체마다 가지고 있어야 할 데이터
정적 변수 : 공용적인 데이터
구분
메모리 할당 시점
메모리 할당 위치
메모리 해제 시점
인스턴스 필드
인스턴스 생성시
힙 메모리
인스턴스 소멸시
클래스 필드
프로그램 시작시
코드 메모리
프로그램 종료시
클래스 필드를 사용할 때는 참조변수가 필요 없다. main() 메서드가 실향되기 전에 이미 메모리에 할당되었으므로 바로 사용할 수 있다.
정적 멤버는 클래스의 인스턴스를 만들지 않고 사용 가능. 객체가 없으므로 클래스 이름을 객체처럼 사용하려 접근. 프로그램이 종료될 때까지 유지
#클래스 필드 사용
[클래스명.필드명]
정적 메소드(static method) : 클래스 메서드는 선언부에 static 키워드가 선언된 메서드. 변수든 메서드든 static이 선언하면 무조건 main()메서드가 실행되기 전에 코드 메모리 영역에 생성되어 사용 준비를 완료한다. 인스턴스 생성과 무관하게 사용하는 메서드는 static으로 선언. 객체가 생성되지 않는 상태에서 호출되는 메소드.
인스턴스 변수와 인스턴스 메소드는 사용할 수 없다.
정적변수와 지역 변수만을 사용할 수 있다.
정적 메소드에서 정적 메소드를 호출하는 것은 가능하다.
정적 메소드는 this 키워드를 사용할 수 없다.(this가 참조할 인스턴스가 없기 때문에)
#클래스 메서드 사용
[클래스명.메서드명(); ]
package practice;
class Count2{
private static int totalCount;
private static int count;
public Count2() {
totalCount++;
count++;
}
public static void display() {
System.out.println("count: "+count);
System.out.println("totalCount: "+totalCount);
}
}
public class Count1 {
public static void main(String args[]) {
Count2.display();
}
}
non-static 멤버
static 멤버
공간적 특성
멤버는 객체마다 별도 존재 인스턴스 멤버라고 부름
멤버는 클래스당 하나 생성 멤버는 객체 내부가 아니라 별도의 공간(클래스 코드가 적재되는 메모리)에 생성 클래스 멤버라고 부름
시간적 특성
객체 생성시에 멤버 생성됨 객체가 생길 때 멤버도 생성 객체 생성 후 멤버 사용 가능 객체가 사라지면 멤버도 사라짐
클래스 로딩 시에 멤버 생성 객체가 생기기전에 이미 생성 객체가 생기기 전에도 사용가능 객체가 사라져도 멤버는 사라지지 않음 멤버는 프로그램이 종료될 때 사라짐
공유의 특성
공유되지 않음 멤버는 객체 내에 각각 공간 유지
동일한 클래스의 모든 객체들에 의해 공유됨
메모리를 할당하고 나면 반드시 초기화를 해야한다.
변수의 초기화 : 메모리에 처음으로 값을 저장하는 것
멤버변수의 초기화 = 생략가능
지역변수의 초기화 = 필수
멤버변수의 초기화
명시적 초기화
생성자를 이용한 초기화
초기화 블록을 이용한 초기화
#초기화 블록(클래스 초기화 믈록, 인스턴스 초기화 블록) : 초기화 블록내에는 메서드의 몸체처럼 다양한 명령문을 함깨 사용할 수 있다. 그래서 복잡한 초기화에 사용한다.
[public class 클래스{
static{클래스 변수의 초기화}
{인스턴스 변수의 초기화}
}]
생성자에서 초기화를 하지 않는 정적 필드(클래스 필드) : 객체 생성 없이도 사용할 수 있기 때문에 생성자에서 초기화 작업을 하지 않는다. 생성자는 객체 생성 후 실행되기 때문이다.
final필드 선언
[final 자료형 필드[= 초기값] ]
final 필드에 초기값을 주는 방법
필드 선언 시에 초기값 대입(고정된 값일 경우 제일 간단한 방법)
생성자에서 초기값 대입(복잡한 초기화 코드가 필요하거나 객체 생성 시에 외부에서 전달된 값으로 초기화한다면 생성자에서 해야한다.)
클래스와 클래스 간의 관계
포함관계(has-a) : ~을 가지고 있다.
싱글톤 : 애플리케이션 전체에서 단 한개의 객체만 생성해서 사용하고 싶다면 싱글톤 패턴을 적용할 수 있다.
싱글톤 만들기
외부에서 new 연산자로 생성자를 호출할 수 없도록 막기(private 접근제어자를 생성자 앞에 붙임)
클래스 자신의 타입으로 정적 필드 선언(자신의 객체 생성해 초기화, private 접근제어자를 외부에서 필드 값 변경 불가하게)
외부에서 호출할 수 있는 정적 메소드인 getInstance() 선언(정적 필드에서 참조하고 있는 자신의 객체 리턴)
싱글톤 패턴이 제공하는 정적 메서드를 통해 간접적으로 객체를 얻을 수 있다.
싱글톤 얻는 방법
클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();
package practice;
public class Singleton {
private static Singleton singleton = null;
private Singleton() {}
public static Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
package practice;
public class SingletonExample {
public static void main(String args[]) {
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
if(obj1==obj2) {
System.out.println("두 객체는 같은 Singleton");
}else {
System.out.println("다른 Singleton");
}
}
}