객체란?
물리적으로 존재하거나 추상적으로 생각할 수 있는 것중에서 자신의 속성과 동작으로 구성되어 있으며 , 다른 것과 식별 가능한것을 말한다.
- 속성 : 필드
- 동작: 메소드
☆객체의 상호작용
세계에서 일어나는 모든 현상은 객체간의 상호 작용으로 이루어져 있음
- 객체들은 독립적으로 존재
- 다른 객체와 서로 상호작용하며 동작한다.
- 상호작용 수단은 메소드, 객체가 다른 객체의 기능을 이용하는 것이 바로 메소드 호출
객체간의 관계
- 집합관계 :
자동차 객체는 엔진 객체, 타이어 객체, 핸들 객체로 이루어져 있다. - 사용 관계:
사람 객체가 자동차 객체의 메소드를 호출한다. - 상속관계:
상위 부모 객체를 기반으로 하위 객체를 생성하는 관계를 의미한다.
☆객체 지향 프로그래밍의 특징
- 캡슐화
- 상속
- 다형성
캡슐화란?
객체의 필드, 메소드를 하나로 묶고, 실제 구현 내용을 감추는 것을 말한다.
감추는 이유?
외부의 잘못된 사용으로 인해 객체가 손상되지 않도록 하는데 있다.
public class Car{
public int speed = 0;
public boolean ready = false;
public void 차량컨디션확인(){
//상태안좋으면 throw new RuntimeException
//좋으면 통과
}
public void 시동걸기(){
ready = true;
}
public void 출발(){
if(ready){
speed = 10;
}else{
throw new RuntimeException("시동을 걸어주세요");
}
}
public void 엑셀(){
if(speed > 100 ){
throw new RuntimeException("속도를 더이상 올릴 수 없습니다.")
}else{
speed += 5;
}
}
}
public class Drive{
Car car =new Car();
//내가 원하는 방향
car.차량컨디션확인()
car.시동걸기()
car.출발()
car.엑셀()
//현실
car.speed = 120;
//또는
car.엑셀();
car.엑셀();
}
public class Car{
.....
public void 자동시스템(){
차량컨디션확인();
시동걸기();
출발();
}
}
상속
부모가 가지고 있는 역할을 자식이 물려받아 사용할 수 있도록 해주는것
코드의 재사용, 유지보수 최소화 등의 장점이 있다.
public Truck extends Car{
@Override
public void 엑셀(){
if(speed > 100 ){
throw new RuntimeException("속도를 더이상 올릴 수 없습니다.");
}else{
speed += 15;
}
}
}
다형성
같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질을 말함
하나의 타입에 여러 객체를 대입함으로써 다양한 기능을 이용할 수 있도록 해준다.
다형성을 위해 부모클래스 또는 인터페이스의 타입 변환을 허용한다.
부모 타입에는 모든 자식 객체가 대입될 수 있고, 인터페이스 타입에는 모든 구현 객체가 대입될 수 있다.
객체와 클래스
설계도가 class
클래스에는 객체를 생성하기 위한 필드와 메소드가 정의되어있다.
클래스로부터 만들어진 객체를 해당 클래스의 인스턴스라고 한다.
(자동차 객체는 자동차 클래스의 인스턴스이다. 이 작업을 인스턴스화 라고한다)
객체 지향 프로그래밍 개발의 3가지 단계
클래스 설계 → 객체 생성 → 객체 이용
클래스 선언
- 하나 이상의 문자로 이루어져야 한다.
- 첫 번째 글자는 숫자가 올 수 없다.
- $, _ 외의 특수 문자는 사용할 수 없다
- 자바 키워드는 사용할 수 없다
객체 생성과 클래스 변수
클래스로부터 객체를 생성하는 방법은 new연산자를 사용하는것
ex)클래스 변수 = new 클래스();
new 연산자로 생성된 객체는 메모리 힙(heap) 영역에 생성된다.
→ 객체를 생성하고 리턴된 객체의 주소를 변수에 저장한다.
클래스의 구성 멤버
- 필드: 객체의 고유 데이터
- 생성자 : 객체 생성시 초기화를 담당한다
- 메소드 : 객체의 동작에 해당하는 중괄호 블록
public class exampleClass{
//필드
int fieldName;
//생성자
exampleClass(){}
//메소드
void methodName(){
}
}
필드선언
[기본] 정수타입 - byte, char, short, int, long
[기본] 실수타입 - float, double
[기본] 논리타입 - boolean
[참조]타입 - 배열, 클래스, 인터페이스
필드 사용
클래스 내부의 생성자나 메소드에서 사용할 경우 단순히 필드이름으로 읽고 변경하면 되지만,
클래스 외부에서 사용할 경우 우선적으로 클래스로부터 객체를 생성한 뒤 필드를 사용해야한다.
public class externalClass{
Car myCar = new Car();
MyCar.speed = 60;
}
public class Car{
int speed;
Car(){
speed = 0;
}
}
생성자
생성자는 new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당한다.
특징
우리가 클래스 내부에 생성자 선언을 생략했다면 컴파일러는 중괄호 내용이 비어있는 기본 생성자를 만든다
public class Car{ private static final int MIN_CAR_PRICE = 0; private String carName; private int price; public Car(){}//기본 생성자 자동추가 //매개변수가 한개인 생성자 public Car(String carName){ this.carName = carName; this.price = 0; } //매개변수 두개인 생성자 // 값 검증이 가능함 public Car(String carName, int price){ if(price < MIN_CAR_PRICE){ throw new IllegalArgumentException("0보다 작을 수 없습니다."); } this.carName = carName; this.price = price } }
다른 생성자 호출(this())
public class Car{
....// 위와 동일
public Car(String carName){
this(carName, 0)
}
//위에서 아래 호출
public Car(String carName, int price){
if(price < MIN_CAR_PRICE){
throw new IllegalArgumentException("0보다 작을 수 없습니다.");
}
this.carName = carName;
this.price = price
}
}
*적은 숫자에서 많은 숫자로 호출한다
메소드
메소드 선언은 선언부(리턴타입, 메소드 이름, 매개변수선언)
리턴타입 메소드이름 매개변수 선언
public int add (int firstNumber, int secondNumber){
}
리턴타입으로 기본타입과, 참조타입, void(반환값없음)이 올 수 있다.
메소드 이름
- 관례적으로 메소드명은 소문자로 작성
- 숫자로 시작하면 안되고 특수문자 사용금지
- 서로 다른 단어 혼합할때 뒤이어오는 첫글자 대문자로 작성
리턴문
리턴값이 있는 메소드
return 리턴값;
*리턴값은 리턴타입과 맞출것
리턴타입이 없는 메소드
return;
객체 내부에서 호출 &객체 외부에서 호출
내부 [메소드(매개값);]
ex) add(10, 20);
외부 [ 참조변수.메소드(매개값)]
ex) car.엑셀()
메소드 오버로딩
하나의 메소드 이름으로 여러 기능을 담는다 하여 붙여진 이름이며,
매개 변수의 타입, 개수, 순서중 하나가 달라야 한다.
int plus(int x, int y )
double plus(double x, double y)
*단 리턴타입은 오버로딩에 아무런 영향을 주지 않는다.
인스턴스 멤버와 this
객체 내부에서도 인스턴스 멤버에 접근하기 위해 this를 사용할 수 있다.
public class Car{
private String name;
//여기서 this는 Car클래스의 자신 인스턴스를 의미함
public car(String name){
this.name = name;
}
}
정적 멤버와 static
정적 필드와 정적 메소드는 클래스에 고정된 멤버이므로 클래스 로더가 클래스를 로딩해서
메소드 메모리 영역에 적재할 때 클래스별로 관리한다. 따라서 클래스의 로딩이 끝나면 바로 사용할 수 있다.
→ 간단히 빌드될때 메모리에 올라가서 프로그램이 종료될때까지 남아 있는다.
차이점
public class SampleClass{
static int staticNumber = 10;
public int number = 20;
static int add(int a, int b){
return a + b;
}
}
일반 필드
SampleClass sampleClass= new SampleClass();
sampleClass.number
정적 필드
SampleClass.staticNumber
정적메소드
SampleClass.add(10, 20)
정적 초기화 블록
정적 필드는 필드 선언과 동시에 초기값을 주는것이 보통이지만,
계산이 필요한 작업이 있을 수도 있고, 자바에서는 해당 기능을 위해
정적 필드를 위한 초기화 작업을 정적블록(static block)에서 할 수 있도록 만들었다.
정적 블록은 클래스가 메모리에 로딩될 때 자동적으로 실행된다.
public class Television{
static String company = "samsung"
static String model = "LCD";
static String info;
static{
info = company+"-" + model
}
}
정적 메소드와 블록 선언시 주의할 점
객체가 없어도 실행된다는 특징 때문에, 이들 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다. 또한 자신의 참조인 this키워드도 사용이 불가능하다.
public class ClassName{
int field1;
void method1(){}
static int field2;
static void method2(){}
//정적 블록
static{
field1 = 10 (x)
method1() (x)
field2 = 10; (o)
method2(); (o)
}
}
싱글톤
단 하나의 객체만 만들도록 보장해야 할때 사용함
장점: 메모리 낭비를 막을 수 있다.
단점: 멀티쓰레드 환경에서 동기화처리를 안할 경우 인스턴스가 두개가 생성된는 문제점 발생
new로 생성하지 못하도록 막아야 함
public class ExampleClass{
private static ExampleClass exampleClass = new ExampleClass();
private ExampleClass(){}
static ExampleClass getInstance(){
return exampleClass;
}
}
new ExampleClass() -> 생성 불가
ExampleClass.getInstance() 로만 접근이 가능하다
final 필드와 상수
초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중에 수정을 할 수 없다.
초기화 방법 2가지
- 필드 선언 시에 주는 방법
- 생성자에서 주는 방법
public class Person{
final String nation = "Korea";
final String ssn;
String name;
public Person(String ssn, String name){
this.ssn = ssn;
this.name = name;
}
}
상수(static final)
일반적으로 불변의 값을 상수라 부른다.
객체마다 따로 저장할 필요가 없는 공용성을 가지고 있으며, 여러 가지 값으로 초기화될 수 없기 때문이다.
final필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 대문에 상수가 될 수 없다.
public static 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;
}
import문
같은 패키지에 속하는 클래스들은 아무런 조건없이 다른 클래스를 사용할 수 있지만 다른 패키지에 속하는 클래스를 사용하려면
두가지 방법을 사용해야한다.
패키지와 클래스를 모두 기술하는 방법
public class Car{ com.hankook.Tire tire = new com.hankook.Tire(); }
import문으로 선언하는 방법
import com.hankook.Tire;
- 예시
접근 제한자
- 라이브러리 클래스를 설계할 때에는 외부 클래스에서 접근할 수 있는 멤버와
접근 할 수 없는 멤버로 구분해서 필드, 생성자,메소드를 설계하는 것이 바람직하다. - 객체 생성을 막기 위해 생성자를 호출하지 못하게 하거나
객체의 특정 데이터를 보호하기 위해 해당 필드에 접근하지 못하도록 막아야 한다.
종류
- public : 누구나 접근가능
적용대상: 클래스, 필드, 생성자, 메소드 - protected :자식 클래스까지 접근가능
적용대상: 필드, 생성자, 메소드 - default : 같은 패키지에 있는 클래스까지 접근가능
적용대상: 클래스, 필드, 생성자, 메소드 - private : 클래스 내부에서만 접근가능
적용대상: 필드, 생성자, 메소드
Getter Setter메소드
객체지향의 프로그래밍에서 객체의 데이터는 객체 외부에서 직접적으로 접근하는 것을 막는다.
객체의 데이터를 외부에서 마음대로 읽고 변경할 경우 객체의 무결성이 깨질 수 있기 때문이다.
어노테이션
어노테이션은 메타데이터라고 볼수 있다. 메타 데이터란 애플리케이션이 처리해야 할 데이터가 아니라,
컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할 것인지를 알려주는 정보이다.
어노테이션은 세가지 용도로 사용된다.
- 컴파일러에게 코드 문법 에러를 체크하도록 제공
- 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공
- 실행 시 특정 기능을 실행하도록 정보를 제공
대표적인 예로 @override 어노테이션으로, 메소드가 오버라이드된 것임을 컴파일러에게 알려주어
컴파일러가 오버라이드 검사를 하도록 해준다.
어노테이션이 적용될 대상을 지정할 때에는 @Target 어노테이션을 사용한다.
@Target({ElementType.METHOD})
public @interface sampleAnnotaion {
String name();
String describe();
}
IDE툴에서 사용 예
어노테이션 유지 정책
사용 용도에 따라 @AnnotationName을 어느 범위까지 유지할 것인지 지정해야 한다.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface sampleAnnotaion {
String name();
String describe();
}
런타임 시 어노테이션 정보 사용하기
어노테이션 자체를 아무런 동작을 가지고 있지 않는 단지 표식일 뿐이지만, 리플랙션을 이용해서 어노테이션의 적용 여부와 엘리먼트 값을 읽고
적절히 처리 할 수 있다.
클래스에 적용된 어노테이션 정보를 얻으려면 java.lang.Class를 이용하면 되지만, 필드 생성자, 메소드에 적용된 어노테이션 정보를 얻으려면 Class의
다음 메소드를 통해서 java.lang.reflect 패키지의 Field, Constructor, Method 타입의 배열을 얻어야 한다.
그런다음 Class , Field, Constructor, Method가 가지고 있는 다음 메소드를 호출해서 적용된 어노테이션 정보를 얻을 수 있다.
리플랙션 사용 예제
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAnnotation {
String value() default "-";
int number() default 15;
}
public class PrintAnnotationUseService {
@PrintAnnotation
public void method(){
System.out.println("실행 내용");
}
@PrintAnnotation("*")
public void method2(){
System.out.println("실행 내용2");
}
@PrintAnnotation(value = "#", number = 20)
public void method3(){
System.out.println("실행내용 3");
}
public static void main(String[] args) {
Method[] declaredMethods = PrintAnnotationUseService.class.getDeclaredMethods();
for (Method method: declaredMethods) {
if(method.isAnnotationPresent(PrintAnnotation.class)){
PrintAnnotation annotation = method.getAnnotation(PrintAnnotation.class);
System.out.println("메소드 이름 출력:" + method.getName());
System.out.println("annotation값 출력:" + annotation.value());
System.out.println("annotation값 출력:" + annotation.number());
System.out.println("################################");
}
try{
method.invoke(new PrintAnnotationUseService());
}catch (Exception e){
e.printStackTrace();
}
}
}
}
'책 > 이것이 자바다' 카테고리의 다른 글
[11장]기본 API클래스 (0) | 2022.02.14 |
---|---|
[10장]예외처리 (0) | 2022.02.14 |
[9장]중첩 클래스와 중첩 인터페이스 (0) | 2022.02.14 |
[8장]인터페이스 (0) | 2022.02.14 |
[7장] 상속 (0) | 2022.02.14 |