클래스
1. 객체란?
객체 : 쉽게 말해 덩어리. 속성과 동작으로 구성. 사람이 객체라고 치면, 사람은 이름이라는 속성이 있고, 걷다 등의 동작이 가능하다. 있다.
이러한 속성과 동작을 각각 필드(field)와 메소드(method)라고 부른다.
이러한 객체로 소프트웨어를 설계하면 객체지향 프로그래밍이 된다.
특징은 캡슐화, 상속, 다형성이 있음.
- 캡슐화 : 객체의 필드(=데이터)와 메소드(=동작)를 하나로 묶어서 내부를 감추면 외부의 객체는 알 수 없고 객체가 노출하는 필드와 메소드만 사용 가능하다. 이러면 외부에서 잘못사용해도 객체가 망가질 일이 없음.
- 상속 : 부모와 자식으로 역할을 부여해서 자식은 부모의 것을 사용가능하다. 단, 부모는 자식꺼를 사용할 수 없음. 코드를 재사용할 수 있기 때문에 효율적임. 그리고 부모를 수정하면 그 아래 자식들이 자동으로 수정된 걸 쓰기 때문에 편함.
- 다형성 : 사용방법은 동일하지만 실행결과가 다양하게 나온다는 뜻. 객체를 바꾸면 프로그램의 실행 결과가 다르게 나올 수 있다.
2. 인스턴스
클래스로부터 생성된 객체를 해당 클래스의 인스턴스(instance)라고 부른다. 클래스로부터 객체를 만드는 과정을 인스턴스화라고 함. 동일한 클래스로부터 여러 개의 인스턴스를 만들 수 있는데, 동일한 설계도로 여러대의 자동차를 만드는 것과 같다.
클래스 선언은 객체 생성을 위한 설계도를 작성하는 것과 같다.
이 설계도(클래스) 접근 권한을 어떻게 할 것인지는 클래스 앞에 접근권한자를 붙여서 판단한다.
[클래스의 접근제한자]
public - 전부 공개(같은 패키지, 다른 패키지 전부 가능)
(default) - 같은 패키지 ㅇㅋ, 다른패키지는 안됨.
3. new 클래스()
객체 생성 연산자이다. 객체를 만드는데, 걔 이름이 이거라고 변수에 담아준다.
Student s1 = new Student();
//앞에 있는 Student와 뒤에 있는 Student는 같다.
Student라는 class가 있는데 그걸 new로 객체화해준다. 단, s1이라는 이름에 담아서.
이렇게 되면 인스턴스화 되어서 인스턴스가 생성되었으므로 Student 라는 클래스 안에 있는 애들을 s1 이름으로 불러다 쓸 수 있다.
1번 Student 클래스
//Student.java
public class Student{
}
2번 StudentExample 클래스
//StudentExample.java
public class StudentExample {
public static void main(String[] args) {
Student s1 = new Student();
System.out.println("s1 변수가 Student 객체를 참조합니다." + s1);
Student s2 = new Student();
System.out.println("s2 변수가 또다른 Student 객체를 참조합니다.");
}
}
총 2가지의 클래스가 있는데 클래스는 Student 처럼 실행은 못하지만 다른 클래스가 사용할 수 있도록 라이브러리 역할을 하는 클래스가 있고, StudentExample 처럼 main()메소드를 가지고 있어 실행 가능한 클래스가 있다.
4. 클래스 구성 멤버(필드, 생성자, 메소드)
public class 요소 {
//--------- 필드 선언 ------------
String company = "현대자동차";
String 문자1;
String 문자2;
Boolean 불린;
int 숫자;
//--------- 아이템 생성 끝 ------------
//-------- 여러개의 생성자 선언 ------------
/*왜? 생성자가 있어야 쓸 수 있는데(조합 어떻게 갖다씀?)
순서에 따라서 뭘 갖다쓸지 모르니까.*/
//첫번 째 생성자
public 요소(){}
//두번 째 생성자
요소(String company, int 숫자){
this.company = company;
this.숫자 = 숫자; //야 숫자 좀 받아줘
}
//3번 째 생성자
요소(String company, String 문자2, Boolean 불린){
this.company = company;
this.문자2 = 문자2;
this.불린 = 불린;
}
}
main()이 없는 라이브러리형 클래스의 경우, main()이 선언된 실행 클래스에서 가져가기 위해 작업이 필요하다.
1) 필드는 변수 선언과 비슷하다. 쉽게 말해서 값 담는 그릇임.
java는 javascript와 다르게 변수 타입(형)이 굉장히 중요하기 때문에 필드에 알맞는 형(그릇의 재질처럼)을 부여해준다.
이 필드가 외부에서 마음대로 값을 읽고 변경하면 무결성이 깨지므로, getter/setter를 사용해서 들어갔다가 나오는 통로를 만들어준다.
2) 생성자는 쉽게 말해 그릇의 묶음이다.
필드가 값을 담는 그릇이라 다양한 재질(String, Boolean 등)이 선언되어 있는데 main()이 있는 실행클래스에서 뭘 갖다쓸지 모른다. 이 때 생성자를 호출하면 비어있는 껍데기를 템플릿처럼 가져와서 초기화할 수 있음.
위에서는 main()을 가진 실행 클래스가 company와 숫자 묶음을 호출하면 두번째 생성자를 호출할 것이고, company와 문자, 불린 묶음을 호출하면 세번째 생성자를 호출할 것이다. (첫번째꺼는 기본 생성자로 값이 들어있는 company만 가능)
나는 라이브러리형 클래스를 s1으로도 가져오고, 또 s2로도 가져오고 해서 템플릿을 다양하게 가져와서 복사해서 쓸꺼다. 그러면 한개 이상의 생성자를 쓴다면 해당 묶음의 재질(형)에 따라서 다양하게 활용 가능하다. 물론 캡슐화도 가능!
=> 이렇게 매개변수를 달리하는 생성자를 여러개 선언하는 것을 생성자 오버로딩이라고 한다.
중복된 생성자의 경우, this로 묶어서 사용 가능.
//다른거 불러와
Car(String model){
this(model, "은색", 250);
}
//불러와지는 생성자
Car(String model, String color, int maxSpeed){
this.model = model;
this.color= color;
this.maxSpeed = maxSpeed;
}
}
3) 메소드는 객체의 동작이다.
public class Calculator {
void powerOn() {
System.out.println("전원을 켭니다");
};
void powerOff() {
System.out.println("전원을 끕니다");
}
int plus(int x, int y) {
int result = x + y;
return result;
}
double plus(double x, double y) {
double result = x + y;
return result;
}
}
public class CalculatorExample {
public static void main(String[] args) {
Calculator myCalc = new Calculator();
myCalc.powerOn(); //실행됨
int result = myCalc.plus(3,5);
System.out.println(result); //실행하고 찍음
}
}
라이브러리 클래스인 Calculator에는 powerOn, powerOff, plus 와 같은 메소드가 있다.
return 해서 결과값을 전달하거나, 아니면 리턴값이 없는 경우 void를 선언해서 동작만 한다.
뱉는거 아니면 움직이기만 하는거임.
라이브러리 클래스에서 powerOn() 이라는 클래스를 void로 선언했다면 그 동작을 main()이 선언된 실행 클래스에서 가져다 썼을 때 리턴값은 없지만 powerOn 안에 선언된 행동은 한다.(sysout을 뱉는다.)
return 하면 종료의 의미이다. 뒤에 리턴값이 있다면 멈추고 값을 뱉어내는 것.
만약 plus를 부를 때 값을 double 값을 넣고 호출하면 double plus 에 있는 값이 호출된다.
=> 매소드 이름은 같은데 매개변수를 달리하는 메소드를 여러개 선언하는 것을 메소드 오버로딩이라고 한다.
4.) 정적 멤버(static)
필드(그릇)과 메소드(동작)은 기본적으로 객체를 생성해야만 사용할 수 있지만, 그냥 객체 없이도 고정적으로 사용할 수 있는 정적 멤버를 만들 수 있다.
즉, new 하지 않아도 갖다쓸 수 있음. 정적 필드와 정적 메소드가 생길 수 있다.
public class Calculator {
static double PI = 3.141592; //파이는 변경될일 없는 값이라 정적으로 선언
static plus(int x, int y){
~~~
}
}
이렇게 정적인거 갖다쓰는 경우엔 아래와 같다.
double result = 10 * 10 * Calculator.PI;
int result2 = Calculator.plus(10,5);
다만 이건 다른게, 객체생성을 하는 new()로 만든 인스턴스 필드와 인스턴스 메소드를 사용할 수 없다. 즉, static 한 애에게 다른 멤버도 추가해주거나 변경해주고 싶은 경우 new()로 인스턴스로 복사해서 그걸 써야하는 것.
참고로 main()함수도 static이 붙은 정적 메소드이므로, 객체 생성 없이 인스턴스 필드와 인스턴스 메소드 사용이 불가하므로 객체 생성 후에 갖다써야 한다.
5) 값 변경 불가할 땐 final 필드와 상수
final이 붙으면 값 변경 못한다고 보면 된다. static 붙으면 불변의 다 갖다쓸 수 있는 값, 즉, 상수가 되는거임.
6) 필드, 생성자, 메소드의 접근제한자
[클래스의 접근제한자]
public - 전부 공개(같은 패키지, 다른 패키지 전부 가능)
(default) - 같은 패키지 ㅇㅋ, 다른패키지는 안됨.
클래스는 이 두개만 되지만 그 내부의 필드, 생성자, 메소드는 몇개 더 사용 가능하다.
[필드, 생성자, 메소드의 접근제한자]
public - 전부 공개(같은 패키지, 다른 패키지 전부 가능)
protected - 같은 패키지이거나, 자식 객체만 사용 가능
(default) - 같은 패키지 ㅇㅋ, 다른패키지는 안됨.
private - 현재 클래스 내부에서만 사용 가능.
7) 싱글톤 패턴
애플리케이션 전체에서 단 한개의 객체만을 생성해서 사용하는 것. (=private로 생성자 접근제한해서 외부에서 new 못하게 막음) 객체 얻는 것은 getInstance() 메소드 호출로만 가능하다.
상속
프로그램에서는 상속은 자식이 부모를 선택한다.
public class 자식클래스 extends 부모클래스 {
}
부모클래스는 여러개일 수 없다. 단, 각 자식클래스는 동일한 부모클래스를 상속할 수 있다.
자식클래스 변수 = new 자식클래스();
부모 없는 자식 없듯이 이렇게 자식 객체를 생성하면 부모가 자동으로 생성된다. 즉, extends 를 해놓은 부모클래스는 객체 생성 시 함께 생성되는 거다.(물론 눈에 보이지는 않지만)
만약 plus를 부를 때 값을 double 값을 넣고 호출하면 double plus 에 있는 값이 호출된다. => 매소드 이름은 같은데 매개변수를 달리하는 메소드를 여러개 선언하는 것을 메소드 오버로딩이라고 한다. |
위에서 설명한 이 메소드 오버로딩을 자식이 부모한테 쓸 수 있다.
즉, 부모한테 상속받아 가져온 것을 자기가 재정의해서 입맛대로 사용하는 것.
단, Override 라는 어노테이션을 붙여준다.
public class Calculator {
public double areaCircle(double r){
Sysyem.out.println("Calculator 객체 안의 areaCircle 입니다");
return 3.14159 * r * r;
}
public class Computer extends Calculator {
@Override
public double areaCircle(double r){
Sysyem.out.println("Calculator 객체 안의 areaCircle메소드를 오버라이딩");
return Math.PI * r * r;
}
public class ComputerExample {
public static void main(String[] args) {
int r = 10; //원의 반지름
//그냥 기본적인 부모 메소드의 값
Calculator calculator = new Calculator();
System.out.println(calculator.areaCircle(r)); //314.159
//자식이 갖다가 오버라이딩 해서 쓴 메소드 값
Computer computer = new Computer();
System.out.println(computer.areaCircle(r)); //314.15926535...
}
}
이렇게 나온다는 것!
final
동일하게 값을 변경 못하므로 클래스에 붙으면 상속이 불가하고, 메소드에 붙으면 오버라이딩을 못한다.
타입 변환
자식은 부모의 특징과 기능을 상속받았기 때문에 부모와 동일한 취급을 받을 수 있다. 즉, 자동으로 타입이 변환된다.
물론 부모걸로 자동 타입이 변환되면 부모클래스의 필드와 메소드만 접근 가능하다.
하지만 반대로 부모는 자식 타입으로 자동변환 되지 않는다. 이 경우 casting 연산자로 강제 타입 변환을 한다.
Parent parent = new Child(); //자동 타입 변환
Child child = (Child) parent; //강제 타입 변환
추상클래스(abstract)
새, 곤충, 물고기 클래스가 각각 있는데 공통되는 부분이 있어서 그걸 따로 묶고싶다. 그러면 이걸 추상클래스로 만들어서 상속시킨다.
public absract class Phone {
void trunOn(){ //핸드폰들은 공통적으로 켜는 기능을 가짐
...
}
}
봉인된(Sealed) 클래스
Java15 부터 무분별한 자식 클래스 생성을 방지하기 위해서 봉인된(Sealed) 클래스가 도입되었다.
'[ java ]' 카테고리의 다른 글
Spring Boot 란? 스프링과 스프링부트 차이점 (1) | 2024.03.23 |
---|---|
protected void doProcess(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException { 뜯어보기 (1) | 2022.10.17 |
HttpServletRequest 과 HttpServletResponse 메소드 정리 (1) | 2022.10.17 |
jQuery 를 이용하여 id, class, name 의 input value 값 가져오기 (1) | 2022.10.17 |
서블릿/JSP 공부(2) - 웹 서버 프로그램 구조 (2) | 2022.09.27 |
댓글