static
- static이란 직역을 하면 고정된 이라는 의미를 갖고 있다.
- 자바에서는 static을 사용 제한자로 이용하며 필드, 메서드에 붙여서 정적 멤버로 만들 때 사용한다.
- 정적 멤버란 객체에 속한 멤버가 아닌 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메서드를 말한다.
- 인스턴스 멤버는 객체에 소속된 멤버로서 객체당 각각 다른 값을 가질 수 있지만 정적 멤버는 클래스에 소속된 멤버이기 때문에 객체간 모든 값을 공유하는 공유데이터가 된다.
- static이 붙은 데이터는 그 static 데이터가 선언된 클래스 이름으로 바로 참조 할 수 있다.
- static은 객체와 무관한 데이터고, 일반 멤버 변수와 혼동할 가능성이 있기 때문에 주소값으로 접근하지 않는다.
정적 초기화 블록
- 인스턴스 필드는 생성자를 통해 초기화할 수 있지만, 정적 필드는 객체 생성 없이 사용하기 때문에 생성자에서 초기화 작업을 하는 것이 올바르지 않다.
- 자바에서 정적 필드 초기화 작업을 위해 정적 블록을 제공하낟. 정적블록은 클래스가 로딩될 때 자동으로 실행된다.
- 정적 메서드 블록과 정적 초기화 블록 내에서는 인스턴스 필드와 메서드를 사용할 수 없고 정적 필드와 정적 메서드만 사용할 수 있다.
package poly.quiz;
public class Product {
public int price;
public String name;
public Product(int price, String name) {
this.price = price;
this.name = name;
}
}
package poly.quiz;
public class Computer extends Product {
public Computer() {
super(1000, "com");
}
}
package poly.quiz;
public class Radio extends Product {
public Radio() {
super(500, "radio");
}
}
package poly.quiz;
public class Tv extends Product {
public Tv() {
super(600, "tv");
}
}
package poly.quiz;
public class MyCart {
private int money;
private Product[] cart = new Product[1]; // 상품을 저장할 배열
private int i = 0; // cart안에 있는 상품의 개수를 알려주는 변수
public MyCart(int money) {
this.money = money;
}
public void buy(Product p) {
if (this.money < p.price) {
System.out.println("금액 부족");
return;
}
this.money -= p.price;
add(p);
}
private void add(Product p) {
if (i >= cart.length) { // 배열에 자리가 부족
// 새로운 장바구니 추가
Product[] temp = new Product[cart.length * 2];
// 기존 배열에 들어 있는 상품을 새로운 배열에 옮기기
for (int n = 0; n < cart.length; n++) {
temp[n] = cart[n];
}
cart = temp;
temp = null;
}
cart[i] = p; // 물건을 배열에 추가
i++; // 개수 증가
info();
}
public void info() {
System.out.println("------------------------");
System.out.println("*** 장바구니 정보 ***");
int total = 0; // 총 상품의 합계를 담아줄 변수
/*
for (int idx = 0; idx < this.i; idx++) {
System.out.print(cart[idx].name + " ");
total += cart[idx].price;
}
*/
for (Product p : cart) {
if (p == null) {
break;
}
System.out.print(p.name + " ");
total += p.price;
}
System.out.println();
System.out.println("구매 금액 합계: " + total + "원");
System.out.println("남은 금액: " + this.money + "원");
}
}
package poly.quiz;
public class MainClass {
public static void main(String[] args) {
MyCart cart = new MyCart(2100);
Tv tv = new Tv();
Radio radio = new Radio();
Computer com = new Computer();
cart.buy(tv);
cart.buy(radio);
cart.buy(com);
}
}
package static_.field;
public class Count {
public int a; // 인스턴스 변수 (멤버 변수)
public static int b; // 정적 변수
}
package static_.field;
public class MainClass {
public static void main(String[] args) {
Count c1 = new Count();
c1.a += 5;
c1.b += 5;
System.out.println("인스턴스 변수 c1.a: " + c1.a);
System.out.println("정적 변수 c1.b: " + c1.b);
System.out.println("------------------------");
Count c2 = new Count();
c2.a += 7;
c2.b += 7;
System.out.println("인스턴스 변수 c2.a: " + c2.a);
System.out.println("정적 변수 c2.b: " + c2.b);
System.out.println("------------------------");
Count c3 = new Count();
c3.a += 8;
c3.b += 8;
System.out.println("인스턴스 변수 c3.a: " + c3.a);
System.out.println("정적 변수 c3.b: " + c3.b);
System.out.println("정적 변수 c1.b: " + c1.b);
System.out.println("정적 변수 c2.b: " + c2.b);
Count.b++;
Count.b++;
System.out.println("정적 변수 Count.b: " + Count.b);
}
}
인스턴스 메서드
- 객체를 생성하여 주소값을 통해 접근 후 호출하는 메서드
- 인스턴스 메서드 안에서는 정적(static) 변수와 인스턴스 변수를 모두 참조할 수 있다.
- static 블록(메서드, 정적 초기화자) 내부에서는 static이 붙은 변수나 메서드만 사용할 수 있다. (this를 사용할 수 없다.)
- static 블록 내부에서 non-static멤버를 사용하려면 반드시 객체를 생성해서 주소값을 통해 참조해야 한다.
package static_.method;
public class Count {
public int a; // 인스턴스 변수
public static int b; // 정적 변수
public int method1() {
return this.a + Count.b;
}
public static int mothod2() {
Count ccc = new Count();
return ccc.a + Count.b;
}
}
- 메서드 내부에서 non-static 데이터를 참조하고 있는 메서드는해당 변수의 정확한 위치(객체의 주소값)를 알려줘야 하기 때문에 정적 메서드로 선언하기가 부적합하다.
- 메서드 내부에서 인스턴스 변수를 사용하지 않고 범용성 있게 사용하는 메서드는 static 키워드를 붙여서 정적 메서드로 선언하는 것이 좋다.
package static_.calc;
public class Calculator {
String color;
static double pi = 3.14159265;
public void paint(String color) {
System.out.println("계산기에 " + color + "색을 칠합니다.");
this.color = color;
}
public static double areaCircle(int r) {
return r * r * pi;
}
}
- 정적 초기화자를 호출하려면 클래스를 로딩하면 된다.
- 클래스 로딩 방법
- 클래스의 이름을 참조하여 static 멤버를 호출.
- 객체를 생성.
- 정적 초기화자는 클래스 로딩 조건 만족 시 최초 1회만 호출.
public class DBManager {
public static String addr;
public static String uid;
public static String upw;
static {
System.out.println("정적 초기화자가 호출됨");
addr = "192.168.0.1:8181/XEPDB1";
uid = "abc1234";
upw = "aaa1111!";
}
public DBManager() {
System.out.println("생성자가 호출됨");
addr = "192.168.0.1:8181/XEPDB1";
uid = "abc1234";
upw = "aaa1111!";
}
}
package static_.init;
public class MainClass {
public static void main(String[] args) {
// DBManager manager = new DBManager();
System.out.println("주소: " + DBManager.addr);
System.out.println("계정명: " + DBManager.uid);
System.out.println("비밀번호: " + DBManager.upw);
}
}
싱글톤 디자인 패턴
- 클래스의 객체를 단 1개로 제한하기 위한 코드 디자인 패턴
- 외부에서 해당 클래스의 객체를 생성하지 못하게 생성자를 단 한개만 선언한 후 private 접근 제한을 붙인다.
- 생성자를 호출할 수 있는 영역은 같은 클래스 내부 뿐이다. 자신의 클래스 내부에서 스스로의 객체를 단 1개만 생성한다.
- 외부에서 해당 클래스의 객체를 요구할 시, 2번에서 미리 만들어 놓은 단 하나의 객체 주소값을 공개된 메서드를 통해 리턴한다.
package static_.singletone;
public class Singleton {
private Singleton() {
System.out.println("객체가 생성됨");
}
private static Singleton s = new Singleton();
public static Singleton getInstance() {
return s;
}
///////////////////////////////////////////
public void method1() {
System.out.println("여러 군데에서 쓰이는 중요한 메서드");
}
public void method2() {
System.out.println("꼭 사용해야 하는 가장 중요한 메서드");
}
}
package static_.singletone;
public class MainClass {
public static void main(String[] args) {
// Singleton s = new Singleton(); (x)
// s.method1();
// s.method2();
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
Singleton s3 = Singleton.getInstance();
Singleton s4 = Singleton.getInstance();
Singleton s5 = Singleton.getInstance();
Singleton s6 = Singleton.getInstance();
System.out.println("주소값: " + s1);
System.out.println("주소값: " + s2);
System.out.println("주소값: " + s3);
System.out.println("주소값: " + s4);
System.out.println("주소값: " + s5);
System.out.println("주소값: " + s6);
// s.method1();
// s.method2();
}
}
final class
- final은 마지막의 라는 뜻을 가진 단어로 자바에서는 클래스, 필드, 메서드에 붙여서 사용할 수 있다.
- 클래스에 final이 붙으면 마지막 클래스라는 의미가 되며, 이것을 상속해서 확장시키지 마라! 라는 의미로 사용된다. final 클래스는 상속이 불가능해진다.
final method
- 메서드에 final이 붙으면 이것은 최종 메서드다. 라는 의미가 된다.
- final 메서드는 상속 시 자식클래스가 재정의(오버라이딩)할 수 없게 된다.
final field
- 필드에 final이 붙으면 최종의 필드라는 뜻이 되므로, 한번 값이 저장되면 그것이 최종본이 된다는 의미가 된다.
- 한번 값을 저장하면 이후로는 절대로 변경이 불가능해진다.
상수(static final)
- 상수는 변수와 달리 불변의 값을 의미한다.
- 불변의 값이란 원주율 파이나 지구의 반지름과 같은 것을 말한다.
- final 필드는 상수라고 볼 수 없으며, 불변의 값이란 값이 변하지 않는 것과 동시에 유일해야 한다.
- final 필드의 경우 값 변경만 되지 않을 뿐, 객체 별로 다른 값을 가질 수 있기 때문에 상수 조건에 어긋난다.
- 자바에서 객체별 유일한 값(static), 값 변경불가(final)을 만족시키기 위해 필드에 static과 final을 함께 붙여 상수로 취급한다.
- final 변수는 반드시 초기화가 필요하며, 초기화는 직접 지정하는 방법과 생성자를 이용하는 방법이 있다.
package final_.field;
public class Person {
final String nation = "대한민국";
final String name;
int age;
public Person(String name) {
this.name = name;
}
}
package final_.field;
public class MainClass {
public static void main(String[] args) {
Person kim = new Person("김철수");
// kim.nation = "미국"; (x)
// kim.name = "김마이클"; (x)
Person park = new Person("박영희");
// park.nation = "영국"; (x)
// park.name = "박영국"; (x)
}
}
package final_.method;
public class Parent {
void method1() {}
void method2() {}
final void method3() {}
}
package final_.method;
public class Child extends Parent {
@Override
void method1() {
super.method1();
}
@Override
void method2() {
super.method2();
}
/*
void method3() {
final메서드는 오버라이딩을 막는다.
}
*/
}
package final_.constant;
public class Earth {
static final double RADIUS = 6400;
static final double SURFACE_AREA;
static {
SURFACE_AREA = RADIUS * RADIUS * Math.PI * 4;
}
}
abstract
- 사전적 의미로 추상(abstract)는 어떤 사물이 실체가 없이 일정한 형태나 성질이 없고 막연하고 일반적인 것을 뜻한다.
- 예를 들면 차량, 동물, 회사 등은 구체적인 대상이 아닌 많은 카테고리들을 모아둔 추상적인 집합적 개념이라는 것이다.
- 호랑이, 고양이, 개와 같은 구체적인 대상들을 하나로 모아서 동물이라고 부르는 것과 같이 동물이라는 것은 추상적 개념이 된다.
- 클래스에도 추상적 개념이 들어간 추상 클래스가 존재한다. 추상적인 것은 실체가 없기 때문에 추상클래스는 객체를 생성할 수 없다.
- 단지 구체적인 실체클래스에게 속성과 기능을 전달하는 매개체가 된다.
- 객체지향 프로그래밍에서는 객체간 공통된 속성과 기능을 상속이라는 개념을 통해 처리한다.
- 상속을 통해 생성된 부모클래스를 추상 클래스로 만들어서 사용하는 이유
- 실체 클래스들의 공통된 필드와 메서드의 이름을 통일을 하기 위함
- 개발 프로젝트에서 설계자와 개발자는 일반적으로 다른사람이고 개발자는 여러명일 가능성이 크다.
- A라는 개발자는 객체의 움직임을 처리하는 기능의 이름을 move()라고 선언하고, B라는 개발자는 walk()라고 선언하면 같은 기능임에도 불구하고 객체마다 사용방법이 달라진다.
- 이 방법을 해결하기 위해 설계자는 추상클래스로 추상메서드 move()를 선언하고 개발자들에게 이 추상클래스를 상속받게함으로써 문제를 해결한다.
- 실체 클래스들의 공통된 필드와 메서드의 이름을 통일을 하기 위함
- 추상 클래스는 실체 클래스가 공통적으로 가져야 할 필드와 메서드들을 정의해 놓은 추상적인 클래스로 실체 클래스의 멤버를 통일화 하는데 목적이 있다.
- 실체 객체들이 모두 동일한 실행 내용을 가지고 있다면 추상클래스에서 해당 내용을 정의하여 상속해주면 되지만, 이름은 같아도 실체 객체들의 실행내용이 다를 경우에는 추상 클래스에서 추상 메서드를 만들어서 오버라이딩을 강제할 수 있다.
- 추상메서드는 일반 메서드와 달리 메서드 블록을 열어 실행내용을 작성하지 않고, 세미콜론으로 닫아 선언만 하여 추상적인 규격만 만들어 둔다.
- 추상클래스의 메서드에 abstract를 붙이면 해당 메서드는 실체가 없는 추상 메서드가 되며, 해당 메서드는 상속을 통해자식 클래스에서 무조건 오버라이딩을 하도록 강요된다.
- 프로그래머의 오버라이딩 실수를 줄일 수 있고, 부모 클래스에서 메서드를 선언할 때 메서드 내부에 어떤 내용을 써야 할지 막연할 때도 구현하기가 편하다.
- 다형성을 적용시켰을 경우에도 안전하게 사용이 가능하다.
- 상속관계에 있는 자식 메서드들의 이름을 통일하기가 편하다.
- 추상메서드는 실체가 존재하지 않는 틀 역할을 하기 때문에 메서드의 바디 ({})부분이 없고, 세미콜론으로 메서드 선언을 마감한다.
- 일반 클래스에서는 추상 메서드를 선언할 수 없다. 추상 메서드가 하나라도 존재한다면 반드시 해당 클래스를 추상 클래스로 선언해야 한다.
- 추상 클래스에 추상 메서드만 선언할 필요는 없다. 일반 멤버변수 및 메서드도 얼마든지 선언이 가능하다.
추상 메서드 선언시 주의사항
- 추상 메서드는 반드시 추상 클래스에서만 선언이 가능하다.
- 추상 메서드를 실체 클래스가 오버라이딩 하지 않을 경우 컴파일 에러가 발생하낟.
- 추상 클래스에는 추상 메서드 이외에 일반적인 인스턴스 메서드, 정적 메서드도 선언이 가능하다.
bad Example
package abs.bad;
//쥬스 프랜차이즈 본사
public class HeadStore {
// 상속을 받는 클래스들에게 오버라이딩을 권유 (강제 x)
public void orderApple() {
System.out.println("0원입니다. 가게에서 직접 가격을 정해주세요.");
}
public void orderBanana() {
System.out.println("0원입니다. 가게에서 직접 가격을 정해주세요.");
}
public void orderGrape() {
System.out.println("0원입니다. 가게에서 직접 가격을 정해주세요.");
}
}
package abs.bad;
public class SeoulStore extends HeadStore {
public void orderApple() {
System.out.println("사과 쥬스의 가격은 3000원 입니다.");
}
public void orderBANANA() {
System.out.println("바나나 쥬스의 가격은 2800원 입니다.");
}
}
package abs.bad;
public class MainClass {
public static void main(String[] args) {
HeadStore store = new SeoulStore();
store.orderApple();
store.orderBanana();
store.orderGrape();
}
}
Good Example
package abs.good;
public abstract class HeadStore {
public int i;
public HeadStore() {
System.out.println("HeadStore의 생성자");
}
public abstract void orderApple();
public abstract void orderBanana();
public abstract void orderGrape();
public void orderWaterMelon() {
System.out.println("수박 쥬스의 가격은 4000원 입니다.");
}
}
package abs.good;
public class SeoulStore extends HeadStore {
public SeoulStore() {
super();
}
@Override
public void orderApple() {
System.out.println("사과 쥬스의 가격은 3000원 입니다.");
}
@Override
public void orderBanana() {
System.out.println("바나나 쥬스의 가격은 2800원 입니다.");
}
@Override
public void orderGrape() {
System.out.println("포도 쥬스의 가격은 3200원 입니다.");
}
}
package abs.good;
public class MainClass {
public static void main(String[] args) {
// 추상클래스는 스스로의 객체를 생성할 수 없다.
// HeadStore h = new HeadStore(); (x)
HeadStore s = new SeoulStore();
s.orderApple();
s.orderBanana();
s.orderGrape();
s.orderWaterMelon();
}
}
Abstract Quiz
- Shape를 상속받는 Circle과 Rect 클래스를 생성
- Circle 객체를 생성할 때, 반지름도 받아서 초기화 할 수 있는 생성자를 선언
Rect 객체를 생성할 때, 이름과 변의 길이를 받도록 처리
- 오버라이딩이 강제되는 메서드에는 각 도형의 넓이를 구하는 로직을 알아서 작성
- MainClass에서 길이가 5인 정사각형, 반지름이 4인 원의 모든 내용을 호출
abstract class Shape 요구사항
1. 도형의 이름을 저장할 수 있는 변수(name)을 private으로 선언.
2. 생성자를 선언 -> 매개값으로 도형의 이름을 받아서 저장할 수 있도록.
3. 자식 클래스에게 오버라이딩을 강제할 수 있는 메서드 getArea() 를 선언 (return type -> double)
4. 도형 이름을 출력할 수 있는 메서드 getName()을 선언. (강제 x)
package abs.quiz; public abstract class Shape { private String name; public Shape(String name) { this.name = name; } public abstract double getArea(); public String getName() { return "도형 이름: " + name; } }
package abs.quiz; public class Circle extends Shape { private int radius; public Circle(String name, int radius) { super(name); this.radius = radius; } @Override public double getArea() { return radius * radius * Math.PI; } }
package abs.quiz; public class Rect extends Shape { private int side; private int width; private int height; public Rect(String name, int side) { super(name); this.side = side; } public Rect(String name, int width, int height) { super(name); this.width = width; this.height = height; } @Override public double getArea() { if (side != 0) { return side * side; } else { return width * height; } } }
package abs.quiz; public class MainClass { public static void main(String[] args) { Shape r = new Rect("정사각형", 5); System.out.println(r.getName()); System.out.println("넓이: " + r.getArea()); System.out.println("---------------------------------"); Shape c = new Circle("원", 4); System.out.println(c.getName()); System.out.printf("넓이: %.2f\n", c.getArea()); } }
'Programming Language > Java' 카테고리의 다른 글
[국비] Java 내용정리 Day15 (0) | 2021.11.25 |
---|---|
[국비] Java 내용정리 Day14 (0) | 2021.11.24 |
[국비] Java 내용정리 Day12 (0) | 2021.11.21 |
[국비] Java 내용정리 Day11 (0) | 2021.11.20 |
[국비] Java 내용정리 Day10 (0) | 2021.11.10 |
댓글