일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- mco
- 자바
- 그리디 알고리즘
- Java
- 주식
- 인플레이션
- 백준
- 오버라이딩
- 제태크
- etf
- S&P500
- XLF
- 무디스
- 배당성장
- javascript
- 접근제어자
- 주린이
- 미국주식
- 금리인상
- FCF
- object
- 현금흐름표
- StringBuffer
- 알고리즘
- 금리인하
- 기업분석
- 다형성
- 객체지향
- 잉여현금흐름
- 프로그래머스
- Today
- Total
오늘의하루
[Java] 자바의 정석 객체지향 part 복습 본문
객체지향언어의 특징
코드의 재사용성이 높고 코드의 관리가 쉽고 제어자를 통해 데이터를 보호 및 코드의 중복을 제거하여 신뢰성 높은 프로그램 개발이 가능하다.
클래스와 객체
클래스란? 객체를 정의해 높은 것이다.
- 클래스는 데이터와 함수의 결합(구조체 + 함수)이다.
객체란? 속성(변수)와 기능(메서드)를 가진 개념
- 객체는 인스턴스를 포함하는 일반적인 의미이다.
- 인스턴스화는 클래스로부터 인스턴스를 생성하는 것이다.
객체의 구성요소
객체는 변수(속성)와 메서드(기능)으로 이루어져 있다.
class Tv{
// 변수
String color;
boolean power;
int channel;
// 메서드
void power(){power = !power;}
void channelUp(){channel = channel + 1;}
void channelDown(){channel = channel - 1;}
}
인스턴스의 생성과 사용
인스턴스의 생성 방법
- 클래스명 참조변수명;
- 객체를 다루기 위한 참조변수 선언
- 참조변수명 = new 클래스명();
- 객체 생성 후 생성된 객체의 주소를 참조변수에 저장
- 클래스명 참조변수명 = new 클래스명();
Tv t1;
t1 = new Tv();
Tv t2 = new Tv();
인스턴스의 사용 방법
- 참조변수명.변수 = 값;
- 참조변수명.메서드(매개변수 값);
Tv t = new Tv(); // 객체 생성 후 참조변수 t에 주소 저장
t.channel = 7;
t.channelDown();
System.out.println(t.channel); // 결과 값 : 6
한개의 객체는 여러개의 참조변수가 주소를 공유 할 수 있다.
- 한개의 참조변수는 여러개의 객체의 주소를 저장할 수 없다.
Tv t1 = new Tv();
Tv t2 = new Tv();
// 지금은 서로 다른 주소를 가지고 있다.
t1.channel = 10;
t1.channelUp();
t2 = t1;
// 이제 참조변수 t2에는 t1에 저장되어있는 객체의 주소를 저장한다.
System.out.println(t1.channel); // 결과값 : 11
System.out.println(t2.channel); // 결과값 : 11
참조변수에 클래스 배열을 저장하기
Tv[] t1 = new Tv[3];
t1[0] = new Tv();
t1[1] = new Tv();
t2[2] = new Tv();
t1[0].channel = 1;
t1[1].channel = 2;
t1[2].channel = 3;
t1[0].channelUp();
t2[1].channelDown();
for(int i = 0; i < 3; i++){
System.out.println(t1[i].channel);
}
// 결과 2 1 3
변수와 메서드
변수(Variable)
선언 위치에 따라 변수는 종류와 범위(scope)가 결정된다.
class Variables{
int iv; // 인스턴스 변수
static int cv; // 클래스 변수
void method(){
int lv = 0; // 지역변수
}
}
변수의 종류 | 선언위치 | 생성시기 |
클래스변수 | 클래스 영역 | 클래스가 메모리에 올라갈때 |
인스턴스 변수 | 인스턴스 생성시 | |
지역변수 | 메서드 영역 | 메서드 수행시 |
인스턴스 변수(instance variable)
- 각각의 인스턴스의 개별적인 저장공간
- 객체 생성 후 참조변수가 없으면 가비지 컬렉터에 의해 자동 제거
- 인스턴스 생성 후 "참조변수명.인스턴스변수명"으로 접근 가능
클래스 변수(class variable)
- 같은 클래스의 모든 인스턴스들이 공유하는 변수
- 인스턴스 생성 없이 "클래스명.클래스변수명"으로 접근 가능
- 클래스가 메모리에 올라갈때 생성 되고 프로그램 종료될때 소멸
지역 변수(local variable)
- 메서드의 종료와 함께 소멸
- 조건문, 반복문의 블럭{ } 내에 선언된 지역변수는 블럭을 벗어나면 소멸
메서드(method)
작업을 수행하기 위한 명령문의 집합이며, 하나의 메서드는 하나의 기능만 수행하는 것이 좋다.
리턴타입 메서드명 (타입 변수명, 타입 변수명 ...) // 선언부
{ // 구현부
// 메서드 호출시 수행할 코드
}
int add(int x, int y){
int result = x + y;
return result;
}
return문
메서드가 정상적으로 종료되는 경우 현재 실행중인 메서드를 종료하고 호출한 곳으로 돌아간다.
1. 반환값이 없는 경우
- return;
- 메서드의 타입이 void일 경우는 return을 작성하지 않아도 된다.
2. 반환값이 있는 경우
- 반환값이 있을 때는 모든 경우에 return문이 있어야 한다.
- return 반환값;
메서드의 호출
"참조변수명.메서드이름(매개변수)"로 호출이 가능하며 매개변수가 없는 경우는 "()"으로 호출한다.
JVM의 메모리 구조
- 메서드 영역(Method Area)
- 클래스 정보와 클래스 변수가 저장되는 곳이다.
- 호출 스택(Call Stack)
- 메서드의 작업공간으로 메서드가 호출되면 메서드 수행에 필요한 메모리 공간을 할당받고 메서드가 종료되면 사용하던 메모리를 반환한다.
- Last In Fast Out 구조이다.
- 힙(Heap)
- 인스턴스가 생성되는 공간으로 new 연산자에 의해서 생선되는 배열과 객체는 모두 여기에 생성된다.
기본형 매개변수와 참조형 매개변수
기본형 매개변수의 경우는 변수의 값을 읽기만 가능하지만 참조형 매개변수는 변수의 값을 읽고 수정할 수 있다.
public class Test{
public static void main(String[] args){
Data d1 = new Data();
d1.x = 10;
System.out.println("main() : x = " + d1.x); // 결과 10
change(d1.x); // 기본형 매개변수
System.out.println("1. After main() : x = " + d1.x); // 결과 10
change2(d1); // 참조형 매개변수
System.out.println("2. After main() : x = " + d1.x); // 결과 1000
}
public static void change(int x){ // 기본형 매개변수
x = 100;
System.out.println("change() : x = " + x); // 결과 100
}
public static void change2(Data d){
d.x = 1000;
System.out.println("chagne2() : x = " + x); // 결과 1000
}
}
class Data{
int x;
}
인스턴스 메서드와 클래스 메서드
인스턴스 메서드(Instance Method)
- 인스턴스 생성 후 "참조변수명.메서드명()"으로 호출
- 메서드 내에서 인스턴스 멤버, 클래스 멤버 사용 가능
클래스 메서드(static Method)
- 인스턴스 생성 없이 "클래스명.메서드명()"으로 호출
- 메서드 내에서 인스턴스 멤버 사용 불가능하지만 클래스 멤버는 사용 가능
class Test{
int iv = 1;
static int cv = 2;
void instanceMethod(){
staticMethod(); // 클래스 메서드 호출 가능
System.out.println(cv); // 클래스 변수 사용가능
System.out.println(iv); // 인스턴스 변수 사용가능
instanceMethod2(); // 다른 인스턴스 메서드 호출 가능
}
static void staticMethod(){
System.out.println(cv); // 클래스 변수 사용가능
instanceMethod(); // Error! 인스턴스 멤버 사용 불가
}
void instanceMethod2(){}
}
메서드 오버로딩
하나의 클래스에 같은 이름의 메서드를 여러개 정의하는 것을 말한다.
오버로딩의 조건
- 메서드의 이름이 같아야한다.
- 매개변수의 개수 또는 타입이 달라야 한다.
- 리턴타입만 다른 경우는 오버로딩이 성립되지 않는다.
- 리턴타입은 오버로딩에 아무런 영향을 주지 못한다.
class Test{
void show();
void show(int x);
void show(boolean x);
}
생성자(Constructor)
인스턴스가 생성될 때마다 호출되는 "인스턴스 초기화 메서드"를 말한다.
- 모든 클래스에는 반드시 하나 이상의 생성자가 있어야 한다.
Test t = new Test();
1. new 연산자에 의해 메모리(Heap)에 Test클래스의 인스턴스가 생성된다.
2. 생성자 Test()가 호출되어 수행된다.
3. new 연산자의 결과로 생성된 Test객체의 주소가 반환되어 참조변수 t에 저장된다.
생성자는 클래스명과 동일해야 하며 리턴값이 없다.
- 리턴값이 없지만 void를 쓰지는 않는다.
class Card{
String Cardkind;
int Cardnumber;
Card(){ // 매개변수가 없는 기본생성자
}
Card(String kind, int number){ // 매개변수가 있는 생성자
Cardkind = kind;
Cardnumber = number;
}
}
기본생성자는 클래스에 생성자가 하나도 없는 경우 컴파일러가 자동으로 추가해준다.
- 생성자가 하나라도 있다면 컴파일러가 기본생성자를 추가해주지 않는다.
class Car{
String color;
String gearType;
int door;
Car(){} // 기본생성자
// 생략 시 컴파일러가 추가해준다.
}
Car c = new Car();
c.color = "white";
c.gearType = "auto";
c.door = 4;
매개변수가 있는 생성자를 사용하는 방법
class Car{
String color;
String gearType;
int door;
car(){}
car(String c, String g, int d){
color = c;
gearType = g;
door = d;
}
}
Car c = new Car("white", "auto", 4);
System.out.println(c.color); // 결과 white
System.out.println(c.gearType); // 결과 auto
System.out.println(c.door); // 결과 4
생성자 this()
같은 클래스의 다른 생성자를 호출할때 사용한다.
- 다른 생성자 호출은 생성자의 첫줄에서만 가능하다.
class Car{
String color;
String gearType;
int door;
Car(){
this("white", "auto", 4);
// 같은 클래스에 있는 매개변수 3개를 가진 생성자를 호출한다.
}
car(String c, String g, int d){
color = c;
gearType = g;
door = d;
}
}
Car c = new Car();
System.out.println(c.color); // white
System.out.println(c.gearType); // auto
System.out.println(c.door); // 4
참조변수 this
인스턴스 자신을 가리키는 참조변수이다.
class Car{
String color;
String gearType;
int door;
Car(){
this("white","auto",4);
}
Car(String color, String gearType, String door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
생성자를 이용한 인스턴스 복사
똑같은 변수값을 가진 독립적인 인스턴스를 복사해서 만들 수 있다.
class Car{
String color;
String gearType;
int door;
Car(){this("white","auto",4);}
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
Car(Car c){
color = c.color;
gearType = c.gearType;
door = c.door;
}
}
Car c1 = new Car("red","auto",6);
Car c2 = new Car(c1); // c1의 변수값을 갖는 인스턴스를 복사하여 c2에 주소를 저장한다.
System.out.println(c2.color); // 결과 red
System.out.println(c2.gearType); // 결과 auto
System.out.println(c2.door); // 결과 6
변수의 초기화
변수를 선언하고 처음으로 값을 저장하는 것을 말하며 멤버변수와 배열은 각 타입의 기본값으로 자동 초기화 되므로 초기화를 생략할 수 있다.
- 지역변수의 경우는 사용전 꼭 초기화를 해주어야 한다.
자료형 | 기본값 |
boolean | false |
char | '\u0000' = 공백 = ' ' |
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0 or 0.0d (생략 가능) |
참조형 변수 | null |
멤버 변수의 초기화 방법
1. 명시적 초기화(Explicit initialization)
class Car{
int door = 4; // 기본형 변수의 초기화
Engine e = new Engine(); // 참조형 변수의 초기화
}
2. 생성자(Constructor)
class Car{
String color;
String gearType;
int door;
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
3. 초기화 블럭(Initialization block)
// 인스턴스 초기화 블럭
{ }
// 클래스 초기화 블럭
static { }
클래스 초기화 블럭은 클래스 변수의 복잡한 초기화에 사용되며 클래스가 로딩 될때 실행된다.
인스턴스 초기화 블럭은 생성자에서 공통적으로 수행되는 작업에 사용되며 인스턴스가 생성될 때 마다 생성자보다 먼저 실행된다.
- 인스턴스 초기화 블럭에서는 클래스 변수도 초기화 할수 있다.
// 사용 예시
class StaticBlocakTest{
static int[] arr = new int[10];
static { // 클래스 초기화 블럭
for(int i = 0; i < arr.length; i++){
arr[i] = (int)(Math.random() * 10) + 1;
// 배열 arr에 1~10사이의 값으로 채운다.
}
}
}
멤버변수의 초기화 시기와 순서 알아보기
class InitTest{
static int cv = 1; // 명시적 초기화
int iv = 2; // 명시적 초기화
static { // 클래스 초기화 블럭
cv = 3;
}
{ iv = 4; } // 인스턴스 초기화 블럭
InitTest(){ // 생성자
iv = 5;
}
}
- 클래스 초기화 cv 기본값으로 cv = 0
- 클래스 초기화 cv 명시적 초기화 cv = 1
- 클래스 초기화 cv 클래스 초기화 블럭 cv = 3
- 인스턴스 초기화 iv 기본값으로 iv = 0 / cv = 3
- 인스턴스 초기화 iv 명시적 초기화 iv = 2 / cv = 3
- 인스턴스 초기화 iv 인스턴스 초기화 블럭 iv = 4 / cv = 3
- 인스턴스 초기화 iv 생성자 iv = 5 / cv = 3
멤버변수의 초기화 시기와 순서 (예제)
class Product{
static int count = 0; // 클래스 변수
int serialNo;
{ // 인스턴스 초기화 블럭
++count;
serialNo = count;
}
Product(){}
}
class ProductTest(){
public static void main(String[] args){
Product p1 = new Product();
Product p2 = new Product();
Product p3 = new Product();
System.out.println("p1의 serial no = " + p1.serialNo); // 결과 1
System.out.println("p2의 serial no = " + p2.serialNo); // 결과 2
System.out.println("p3의 serial no = " + p3.serialNo); // 결과 3
System.out.println("생상된 제품의 수는 모두 " + Product.count + "개 입니다."); 결과 3
}
}
클래스 변수 초기화 순서
- 클래스 변수 초기화 count 기본값 count = 0
- 클래스 변수 초기화 count 명시적 초기화 count = 0
p1 인스턴스 생성시 초기화 순서(클래스 변수 count = 0)
- 인스턴스 변수 초기화 serialNo 기본값 serialNo = 0
- 인스턴스 변수 초기화 serialNo 초기화 블럭 count = 1, serialNo = 1
- 인스턴스 변수 초기화 serialNo 기본 생성자
p2 인스턴스 생성시 초기화 순서 (클래스 변수 count = 1)
- 인스턴스 변수 초기화 serialNo 기본값 serialNo = 0
- 인스턴스 변수 초기화 serialNo 초기화 블럭 count = 2, serialNo = 2
- 인스턴스 변수 초기화 serialNo 기본 생성자
p3 인스턴스 생성시 초기화 순서(클래스 변수 count = 2)
- 인스턴스 변수 초기화 serialNo 기본값 serialNo = 0
- 인스턴스 변수 초기화 serialNo 초기화 블럭 count = 3, serialNo = 3
- 인스턴스 변수 초기화 serialNo 기본 생성자
상속(Inheritance)
기존의 클래스를 재사용하여 새로운클래스를 작성하는 것을 말하며, 기존의 클래스는 부모가 되고 새로운 클래스는 자식이되며 자식 클래스는 부모의 모든 멤버를 상속받는다.
- 상속 시 생성자 및 초기화 블럭은 상속받지 못한다.
- 부모 클래스의 변경은 자식 클래스에게 영향을 미치지만 자식 클래스의 변경은 부모 클래스에게 아무런 영향을 미치지 못한다.
- 공통 부분은 부모 클래스에서 관리하고 개별 부분의 경우는 자식 클래스에서 관리한다.
- 자바는 단일 상속을 원칙으로 한다.
class Parents{
int x;
int y;
}
class Child extends Parents{
int z;
}
Child c = new Child();
c.x = 1;
c.y = 2;
c.z = 3;
System.out.println(c.x); // 결과 1
System.out.println(c.y); // 결과 2
System.out.println(c.z); // 결과 3
포함(Composite)
한개의 클래스의 멤버변수로 다른 클래스를 선언하는 것을 말한다.
- 2개 이상의 클래스를 상속 받고 싶을 때는 한개는 상속으로 나머지는 포함을 하면 사용 가능하다.
class Point{
int x;
int y;
}
class Circle{
Point p = new Point();
int r;
}
Circle c = new Circle();
c.r = 10;
c.p.x = 20; // 포함된 참조변수에 있는 x값을 지정
c.p.y = 30; // 포함된 참조변수에 있는 y값을 지정
System.out.println(c.r); // 결과 10
System.out.println(c.p.x); // 결과 20
System.out.println(c.p.y); // 결과 30
상속과 포함은 어떻게 결정하는 걸까?
상속관계 = "~은 ~ 이다"
포함관계 = "~은 ~ 을 가지고 있다"
위에 작성되있는 Point와 Circle을 예시로 확인해 보자
- 원은 점이다. (상속)
- 원은 점을 가지고 있다. (포함)
확인해보면 상속보다 포함이 더 올바르다는 것을 알 수 있다.
클래스간의 관계 (예제)
class Shape{
String color = "blue";
void draw(){}
}
class Point{
int x;
int y;
Point(){this(0,0);}
Point(int x, int y){
this.x = x;
this.y = y;
}
}
class Circle extends Shape{
Point center;
int r;
Circle(){this(new Point(0,0), 100);}
Circle(Point center, int r){
this.center = center;
this.r = r;
}
}
class Triangle extends Shape{
Point[] p;
Triangle(Point[] p){
this.p = p;
}
Triangle(Point p1, Point p2, Point p3){
p = new Point[]{p1,p2,p3};
}
}
Circle c1 = new Circle();
Circle c2 = new Circle(new Point(150,150), 50);
Point[] p = {new Point(100,100), new Point(140,50), new Point(200,100)};
Triangle t1 = new Triangle(p);
Object 클래스
Object 클래스는 모든 클래스의 최고 조상이며 부모가 없는 클래스는 자동적으로 Object 클래스를 상속 받게 된다.
- 이 때문에 모든 클래스는 Object 클래스에 정의된 11개의 메서드를 상속받는다.
- 메서드 종류 : toString(), equals(Object obj), hashCode() 등등
오버라이딩(Overriding)
부모 클래스로 부터 상속받은 메서드의 내용을 상속받는 자식 클래스에 맞게 변경하는 것을 말한다.
class Point{
int x;
int y;
String getLocation(){
return "x = " + x + " y = " + y;
}
}
class Point3D extends Point{
int r;
String getLocation(){ // 오버라이딩
return "x = " + x + " y = " + y + " r = " + r;
// super 참조변수를 사용할 경우
// return super.getLocation() + " r = " + r;
}
}
오버라이딩의 조건
- 메서드의 선언부가 같아야 한다.
- 선언부 = 메서드명, 매개변수, 리턴타입
- 접근제어자를 좁은 범위로 변경할 수 없다.
- 접근 제어자 범위 = private < (default) < protected < public
- 부모 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
class Parent{
void parentMethod() throws IOException, SQLException{} // 예외 2개
}
class Child extends Parent{
void parentMethod() throws IOException{} // 예외 1개
}
class Child2 extends Parent{
void parentMethod() throws Exception{} // 예외 1개
}
class Child3 extends Parent{
private void parentMethod(){} // Error! 접근제어자가 좁은 범위로 변경 불가
}
class Child4 extends Parent{
public void parentMethod() throws Exception{} // 예외 1개, 접근제어자 넓은 범위로 변경 가능
}
오버로딩과 오버라이딩은 아무런 관련이 없다.
오버로딩(over loading) = 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이딩(overriding) = 상속받은 메서드의 내용을 변경하는 것(change)
class Parent{
void parentMethod(){}
}
class Child{
void parentMethod(){System.out.println("overriding");}
void parentMethod(int i){System.out.println("over loading");}
void childMethod(){}
void childMethod(int i){System.out.println("over loading");}
void childMethod(){} // Error! 중복정의
}
참조변수 super
참조변수 super는 부모의 멤버와 자신의 멤버를 구별하는데 사용된다.
class Parent{
int x = 10;
}
class Child extends Parent{
int x = 20;
void method(){
System.out.println("x = " + x); // 가장 가까운 x의 값 20
System.out.println("this.x = " + this.x); // 자기 자신의 x의 값 20
System.out.println("super.x = " + super.x); // 부모의 x의 값 10
}
}
class Child2 extends Parent{
void method(){
System.out.println("x = " + x); // 가장 가까운 x의 값 10
// 부모의 x만 있기 때문에 가까운것은 10이다.
System.out.println("this.x = " + this.x); // 자기 자신의 x의 값 10
// 부모의 x를 상속받기 때문에 자기 자신 것으로 할수 있다.
System.out.println("super.x = " + super.x); // 부모의 x의 값 10
}
}
생성자 super()
상속시 생성자 및 초기화 블럭은 상속되지 않는데 부모의 멤버들도 초기화되어야 하기 때문에 자식 생성자의 첫문장에서 부모의 생성자를 호출할때 사용된다.
class Point{
int x;
int y;
Point(){this(0,0);}
Point(int x, int y){
super(); // Object() 최고조상인 Object 클래스의 기본생성자 호출
this.x = x;
this.y = y;
}
String getLocation(){
return "x = " + x + " y = " + y;
}
}
class Point3D extends Point{
int z;
Point3D(int x, int y, int z){
super(x,y); // 부모의 생성자 Point(x,y)호출
this.z = z;
}
String getLocation(){ // 오버라이딩
return "x = " + x + " y = " + y + " z = " + z;
}
}
제어자
제어자는 크게 접근제어자와 그외 제어자로 나뉘며 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미를 부여하는데 사용된다.
접근 제어자 = private, (default), protected, public
그외 제어자 = static, final, abstract, native, transient, synchronized, volatile, strictfp
그 외 제어자 알아보기
static - 공통적인
static이 사용되는 곳은 멤버변수, 메서드, 초기화 블럭
멤버 변수에 사용될 경우는 클래스 변수가 되며 메서드에 사용될 경우 클래스 메서드(static 메서드)가 된다.
class StaticTest{
static int width = 200;
static int height = 120;
static {
height = 240;
}
static int max(int a, int b){
return a > b ? a : b;
}
}
System.out.println(StaticTest.max(StaticTest.width, StaticTest.height)); // 결과 240
final - 마지막의, 변경할 수 없는
final이 사용되는 곳은 클래스, 메서드, 멤버변수, 지역변수
대상 | 의미 |
클래스 | 누군가의 부모가 될 수 없는 클래스가 된다. |
메서드 | 오버라이딩을 통해 재정의(change)할 수 없는 메서드가 된다. |
멤버 변수 | 값을 변경할 수 없는 상수가 된다. 보통 선언과 초기화를 동시에 한다. |
지역 변수 |
final class FinalTest{
final int MAX_SIZE = 10; // 멤버변수 (상수)
final int NUMBER;
FinalTest(int NUMBER){
this.NUMBER = NUMBER; // 생성자를 이용한 final 변수 초기화
}
final void getMaxSize(){
final int LV = MAX_SIZE; // 지역변수 (상수)
System.out.println(MAX_SIZE);
}
}
class Child extneds FinalTest{
// void getMaxSize(){} // 오버라이딩 Error! final 메서드는 오버라이딩 할 수 없다.
}
abstract - 추상의
abstract가 사용되는 곳은 클래스, 메서드
대상 | 의미 |
클래스 | 클래스 내에 추상메서드가 선언되어 있음을 의미한다. |
메서드 | 선언부만 작성하고 구현부는 작성하지 않은 추상 메서드임을 알린다. |
abstract class AbstractTest{ // 추상 클래스
abstract void move(); // 추상 메서드
}
접근 제어자(Access modifier)
멤버 또는 클래스에 사용되며 외부로 부터의 접근을 제한한다.
종류 | 의미 |
private | 같은 클래스 내에서만 접근이 가능하다. (멤버) |
default | 같은 패키지 내에서만 접근이 가능하다. (클래스, 멤버) |
protected | 같은 패키지 내에서, 그리고 다른 패키지의 자식 클래스에서 접근이 가능하다. (멤버) |
public | 접근 제한이 없다. (클래스, 멤버) |
public /*or default*/ class AccessModifierTest{
protected int iv;
private static int cv;
/*default*/void method(){}
}
접근 제어자를 이용한 캡슐화 (예제)
class Time{
private int hour;
Time(int hour){
setHour(hour);
}
public int getHour(){ return hour; }
public void setHour(int hour){
if(hour < 0 || hour > 23){
return;
}
this.hour = hour;
}
public String toString(){
return hour + "시 입니다.";
}
}
Time t = new Time(10);
System.out.println(t.hour); // Error!
// 접근제어자가 private이기 때문에 같은 클래스에서만 접근이 가능하다.
System.out.println(t.getHour()); // 결과 10
System.out.println(t); // 결과 10시 입니다.
// System.out.println(t.toString());에서 to String()은 생략이 가능하다.
t.setHour(t.getHour() + 1);
System.out.println(t.getHour()) // 결과 11
System.out.println(t); // 결과 11시 입니다.
생성자의 접근제어자
일반적으로 생성자의 접근제어자는 클래스의 접근제어자와 일치한다.
- 생성자에 접근제어자를 통해 인스턴스 생성을 제한할 수 있다.
class Singleton{
private static Singleton s;
// private static Singleton s = new Singletion();
private Singleton(){} // 생성자에 private을 사용
public static Singleton getInstance(){
if(s == null){
s = new Singleton();
}
return s;
}
}
Singleton s = new Singleton(); // Error! 생성자를 사용할 수 없다.
Singleton s1 = Singleton.getInstance();
제어자의 조합
대상 | 사용가능한 제어자 |
클래스 | public, (default), final, abstract |
메서드 | 모든 접근제어자, final, abstract, static |
멤버 변수 | 모든 접근제어자, final, static |
지역 변수 | final |
- 메서드에 static과 abstract를 함께 사용할 수 없다.
- static 메서드는 구현부가 있는 메서드에만 사용할 수 있다.
- 클래스에 abstract와 final을 동시에 사용할 수 없다.
- abstract는 혼자 사용될 수 없고 부모 클래스가 되야 하는데 final은 부모 클래스가 되는것 을 제한한다.
- abstract 메서드의 접근제어자가 private이 될 수 없다.
- abstract 메서드는 자식 클래스에서 구현부를 완성해야하는데 private은 다른 클래스에서의 접근을 제한한다.
- 메서드에 private과 final을 같이 사용할 필요가 없다.
- 두 제어자 모두 오버라이딩을 할 수 없게 만들기 때문이다.
다형성(polymorphism)
부모타입의 참조변수로 자식타입의 객체를 다룰 수 있는것을 말한다.
- 반대의 경우는 불가능하다.
class Tv{
boolean power;
int channel;
void power(){power = !power;}
void channelUp(){channel = channel + 1;}
}
class CaptionTv{
String text;
void caption(){System.out.println(txext);}
}
Tv t = new Tv();
CaptionTv c = new CaptionTv();
// 다형성 활용
Tv t = new CaptionTv();
CaptionTv는 Tv의 자식 클래스이기 때문에 더 많은 멤버를 가지고 있다. 다형성으로 Tv 타입의 참조변수로 CaptionTv의 객체를 가리키면 만들어진 참조변수에서는 Tv에 있는 멤버만 사용이 가능하다.
참조변수의 형변환
서로 상속관계에 있는 타입간의 형변환만 가능하다.
자식타입에서 부모타입으로 형변환 (Up-casting) = 형변환 생략 가능
부모타입에서 자식타입으로 형변황(Down-casting) = 형변환 생략 불가능
class Car{
String color;
int door;
void drive(){System.out.println("drice Brrrr~");}
}
class FireEngine extends Car{
void water(){System.out.println("water!!!");}
}
Car car; // 기본값 null
FireEngine fe1 = new FireEngine();
FireEngine fe2; // 기본값 null
fe1.water(); // 결과 water!!!
car = fe1; // car = (car)fe1; 자식에서 부모타입으로 변환 (형변환 생략가능)
car.water(); // Error! Car클래스에는 water 멤버가 없다.
fe2 = (FireEngine)car; // 부모에서 자식타입으로 변환 (형변환 생략 불가능)
fe2.water(); // 결과 water!!!
instanceof 연산자
참조변수가 참조하는 인스턴스의 실제 타입을 체크하는데 사용된다.
- instanceof 연산결과가 true이면 해당 타입은 형변환이 가능하다.
- A instanceof B는 A가 가리키는 실제 객체의 타입이 B 또는 B의 자식 타입인지 확인한다.
class Car{}
class FireEngine extends Car{}
FireEngine fe = new FireEngine();
if(fe instanceof FireEngine){
System.out.println("This is a FireEngine instance");
}
if(fe instanceof Car){
System.out.println("This is a Car instance");
}
if(fe instanceof Object){
System.out.println("This is an Object instance");
}
참조변수와 인스턴스변수의 연결
멤버 변수가 중복 정의된 경우 참조변수의 타입에 따라 연결되는 멤버변수가 달라진다.
- 멤버변수는 참조변수타입에 영향을 받는다.
메서드가 중복정의된 경우 참조변수의 타입에 관계없이 항상 실제 인스턴스의 타입에 정의된 메서가 호출된다.
- 메서드는 참조변수타입에 영향을 받지 않는다.
class Parent{
int x = 10;
void method(){System.out.println("Parent Method");}
}
class Child{
int x = 20;
void method(){System.out.println("Child Method");}
}
Parent p1 = new Child();
Child c1 = new Child();
System.out.println(p1.x); // 결과 10
p1.method(); // 결과 Child Method
System.out.println(c1.x); // 결과 20
c1.method(); // 결과 Child Method
//========================================
class Parent{
int x = 30;
void method(){Systetm.out.println("Parent Method");}
}
class Child extends Parent{}
Parent p2 = new Child();
Child c2 = new Child();
System.out.println(p2.x); // 결과 30
p2.method(); // 결과 Parent Method
System.out.println(c2.x); // 결과 30
c2.method(); // 결과 Parent Method
매개변수의 다형성
참조형 매개변수는 메서드 호출시 자신과 같은 타입 또는 자식 타입의 인스턴스를 넘겨줄 수 있다.
- 코드의 중복성을 줄일 수 있다.
class Product{
int price;
}
class Tv extneds Product{}
class Audio extneds Product{}
class Buyer{
int money = 1000;
// 매개변수의 다형성 사용 안했을 때
void Buy(Tv t){
money = money - t.price;
}
void Buy(Audio a){ // 오버로딩
money = money - a.price;
}
// 매개변수의 다형성을 활용 했을 때
void Buy(Product p){ // 다형성으로 자식 클래스를 가져올 수 있다.
money = money - p.price;
}
}
Buyer b = new Buyer();
Product p1 = new Tv();
p1.price = 100;
b.buy(p1); // Product p = new Tv();
Product p2 = new Audio();
p2.price = 20;
b.buy(p2); // Product p = new Audio();
여러 종류의 객체를 하나의 배열로 다루기
부모 타입의 배열에 자식들의 객체를 담을 수 있다.
class Product{}
class Tv extends Product{}
class Audio extends Product{}
class Computer extends Product{}
Product[] p = new Product[3];
p[0] = new Tv();
p[1] = new Audio();
p[2] = new Computer();
// 매개변수 다형성 예시에 있는 Buyer 클래스에 배열 넣기
class Buyer{
int money = 1000;
Product[] cart = new Product[10];
int i = 0;
void Buy(Product p){
if(money < p.price){
System.out.println("잔액 부족");
return;
}
money = money - p.price;
cart[i++] = p;
}
}
추상클래스 - abstract
클래스가 설계도라면 추상클래스는 "미완성 설계도"이며 추상메서드를 포함하고 있는 클래스이다.
abstract class Player{
int currentPos;
Player(){ // 추상 클래스도 생성자가 있어야 한다.
currentPos = 0;
}
abstract void play(int pos); // 추상 메서드
abstract void stop(); // 추상 메서드
void play(){ // 일반 메서드안에서 추상 메서드를 사용할 수 있다.
play(currentPos); // 추상메서드
}
}
- 일반 메서드가 추상 메서드를 호출할 수 있다.
- 호출할 때 필요한 건 선언부이다.
- 추상 클래스는 인스턴스를 생성할 수 없다.
- 다른 클래스를 작성하는데 도움을 줄 목적으로 작성한다.
추상메서드 (abstract method)
선언부만 있고 구현부가 없는 메서드이며 추상클래스는 꼭 필요하지만 자식 클래스마다 다르게 구현될 것으로 예상되는 경우에 사용한다.
- 추상클래스를 상속받는 자식클래스에서 추상 메서드의 구현부를 완성해야 사용 가능하다.
abstract class Player{
abstract void play(int pos);
public abstract void stop();
}
class AudioPlayer extends Player{
void play(int pos){}
void stop(){} // Error! 접근제어자를 좁은 범위로 할 수 없다.
public void stop(){} // OK
}
abstract class AbstractPlayer extends Player{
void paly(int pos){}
// 추상 메서드 하나를 구현하지 않았기 때문에 아직 추상클래스이다.
}
추상 메서드의 구현부를 완성하는 행위는 오버라이딩이기 때문에 오버라이딩 규칙을 지켜줘야한다.
인터페이스 (interface)
인터페이스는 일종의 추상 클래스이며 실제 구현된 것이 전혀 없는 기본 설계도이다.
- 디폴트메서드, static메서드 및 추상메서드와 상수만을 멤버로 가질 수 있다.
- 디폴트메소드는 인터페이스에서 사용할 수 있는 일반 메서드를 말한다.
- 인터페이스는 생성할 수 없으며 클래스 작성에 도움을 줄 목적으로 사용된다.
interface MyInterface{
int MAX = 1; // 상수
void method(); // 추상메서드
default void newMethod(){} // 디폴트 메서드
}
디폴트 메서드가 기존의 메서드와 충돌하게 된다면?
- 여러 인터페이스의 디폴트 간의 충돌
- 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩 해야한다.
- 디폴트 메서드와 부모클래스의 메서드 간의 충돌
- 부모 클래스의 메서드를 상속받고 디폴트 메서드는 무시된다.
인터페이스의 장점
- 개발시간을 단축시킬 수 있다.
- 표준화가 가능하다.
- 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
- 독립적인 프로그래밍이 가능하다.
인터페이스 작성
class 대신 interface를 사용한다는 것 외에는 클래스와 동일하다.
interface interfaceName{
public static abstract method(/*매개변수*/);
/* public static final 타입 상수이름 = 값 */
public static final int MAX = 1;
int HEART = 2; // public static final 생략
String getCardKind(); // public stataic abstract 생략
}
- 인터페이스의 모든 멤버변수는 public static final이기 때문에 생략이 가능하다.
- 인터페이스의 모든 메서드는 public static abstract이기 때문에 생략이 가능하다.
인터페이스 상속
인터페이스도 클래스처럼 상속이 가능하다.
- 다중 상속 허용
- 인터페이스는 Object클래스와 같은 최고 조상이 없다.
interface Movable{
void move(int x, int y); // public static abstract 생략
}
interface Attackable{
void attack(Unit u);
}
interface Fightable extends Movable, Attackable{}
인터페이스 구현
인터페이스를 구현하는 것은 클래스를 상속받는것과 같다.
- extends 대신 implements를 사용한다.
- 인터페이스를 상속받은 클래스는 추상 메서드를 완성해야 사용가능하다.
- 클래스 상속과 인터페이스 구현이 동시에 가능하다.
interface Fightable {
void move(int x, int y);
void attack(Unit u);
}
abstract class Unit{
int x;
int y;
void set();
}
class Fighter implements Fightable{
public void move(){}
public void attack(){}
}
abstract class Fighter2 implements Fightable{
public void move(){}
// 하나의 추상 메서드를 구현하지 않았기 때문에 추상 클래스가 된다.
}
// 상속과 구현이 동시에 가능하다.
class Fighter3 extends Unit implements Fightable{
public void move(int x, int y){}
public void attack(Unit u){}
public void set(){}
}
인터페이스를 이용한 다형성
인터페이스 타입의 변수로 인터페이스를 구현한 클래스의 인스턴스를 참조할 수 있다.
- 인터페이스를 메서드의 매개변수 타입으로 지정할 수 있다.
- 인터페이스를 메서드의 리턴타입으로 지정할 수 있다.
class Fighter extends Unit implements Fightable{
public void move(int x, int y) {}
public void attack(Unit u) {}
}
Fighter f1 = new Fighter();
Fightable f2 = new Fighter();
// ===============================================
void attack(Fightable f){}
// Fightable 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 받는 메서드
//================================================
Fightable method(){ // Fightable 인터페이스를 구현한 클래스의 인스턴스를 반환한다.
return new Fightable();
}
인터페이스의 장점 (예제)
interface Repairable{}
class Unit{
int hitPoint;
final int MAX_HP;
Unit(int hp){
MAX_HP = hp;
}
}
class GroundUnit extends Unit{
GroundUnit(int hp){
super(hp); // Unit(int hp) 생성자 호출
}
}
class Tank extends GroundUnit implements Repairable{
Tank(){
super(150);
hitPoint = MAX_HP;
}
public String toString(){
return "Tank";
}
}
class Marine extends GroundUnit{
Marine(){
super(40);
hitPoint = MAX_HP;
}
}
class SCV extends GroundUnit implements Repairable{
SCV(){
super(60);
hitPoint = MAX_HP;
}
void repair(Repairable r){
if(r instanceof Unit){
Unit u = (Unit)r;
while(u.hitPoint != u.MAX_HP){
u.hitPoint++;
}
}
}
}
Tank tank = new Tank();
Marine marine = new Marine();
SCV scv = new SCV();
scv.repair(tank); // OK
scv.repair(marine); // Error! Marine 클래스는 Repairable을 구현하지 않았다.
'JAVA' 카테고리의 다른 글
[자바의 정석] 변수와 메소드 & 메소드 오버로딩 & JVM 구조 (0) | 2022.10.14 |
---|---|
[자바의 정석] 객체지향 언어 & 클래스와 객체 (0) | 2022.10.12 |
[프로그래머스 2단계 - JAVA] 게임 맵 최단거리 (0) | 2022.09.29 |
[프로그래머스 1단계 - JAVA] 하샤드 수 (1) | 2022.09.16 |
[프로그래머스 1단계 - JAVA] 정수 내림차순으로 배치하기 (0) | 2022.09.16 |