1023
내부클래스(= 중첩클래스 = 클래스 내부에 선언한 클래스) :: 클래스가 여러 클래스와 관계를 맺는 경우에는 독립적으로 선언하는 것이 좋지만 특정 클래스와 관계를 맺는 경우에는 클래스 내부에 선언하는 것이 좋음
내부클래스에서 외부클래스의 멤버를 쉽게 접근할 수 있음/ 코드의 복잡성을 줄일 수 있음
내부클래스는 선언 위치에 따라서 두가지로 분류되는데 클래스의 멤버로 선언되는 클래스를 멤버 클래스라고 하고 메소드 내부에 선언되는 클래스를 로컬클래스라고 함
멤버클래스는 클래스나 객체가 사용 중이라면 재사용이 가능하다 / 로컬클래스는 메소드를 실행할때만 사용되고 종료되면 사라짐
멤버클래스 (인스턴스 멤버 클래스 / 정적 멤버 클래스)
인스턴스 멤버 클래스는 인스턴스 필드와 인스턴스 메소드만 선언 가능하고 정적필드와 메소드는 선언할 수 없음
정적 멤버 클래스는 모든 종류의 필드와 메소드를 선언할 수 있음
메소드 내에서도 선언하는 클래스를 로컬클래스라고 하고 로컬클래스는 접근제한자 및 static을 붙일 수 없음
익명객체(= 익명 내부 클래스) :: 익명 객체는 이름없는 객체를 말하고 익명객체를 만들려면 어떤 클래스를 상속하거나 인터페이스를 구현해야만 함 / 클래스 변수는 이름없는 객체를 참조하고 인터페이스변수는 이름없는 구현 객체를 참조하게 됨 / 이름이 없기 때문에 생성자도 가질 수 없음
> 익명클래스는 하나의 실행문이라 ; 세미콜론을 붙여주어야 함
Inner 클래스에서 Outer 클래스멤버 접근하는 예제
package inner;
class Outer1{
private int x = 100;
private static int y = 200;
public void outerMethod1() {
System.out.println("public 바깥클래스 메소드");
}
public void outerMethod2() {
System.out.println("public 바깥클래스 메소드");
}
// 인스턴스 멤버클래스
class Inner1 {
private int x = 300;
private int y = 300;
// private static int z = 400;
// 인스턴스 멤버클래스에서 정적(static)는 만들 수 없다
public void disp() {
System.out.println("Outer x :: " + x);
System.out.println("Inner y :: " + y);
System.out.println("Outer y :: " + Outer1.y);
outerMethod1();
outerMethod2();
}
}
}
public class Ex01 {
public static void main(String[] args) {
Outer1 outer1 = new Outer1();
Outer1.Inner1 inner = outer1.new Inner1();
inner.disp();
}
}
Outer클래스에서 Inner클래스 멤버 접근예제
class Outer{
private int x= 100;
public void disp() {
System.out.println(x);
// System.out.println(y);
Inner2 inner2 = new Inner2();
System.out.println(inner2.y);
inner2.innerMethod1();
inner2.innerMethod2();
}
class Inner2{
private int y = 200;
public void innerMethod1() {
System.out.println("public 안쪽클래스 메소드");
}
private void innerMethod2() {
System.out.println("private 안쪽클래스 메소드");
}
}
}
public class Ex02 {
public static void main(String[] args) {
}
}
Outer 클래스의 static 멤버 클래스
class Outer3{
private int x = 100;
private static int y = 200;
// 정적 멤버 클래스
static class Inner3{
private int z = 300;
private static int zz = 400;
public void disp() {
System.out.println(y);
System.out.println(new Outer3().x);
}
}
}
public class Ex03 {
public static void main(String[] args) {
Outer3.Inner3 inner3 = new Outer3.Inner3();
inner3.disp();
}
}
로컬클래스 > 어떤 메소드 안에서 기능을 할때 특정 클래스가 필요할때 만듬 (잘 사용은 안함)
class LocalEx{
public void method() {
class InnerLocal{
int x = 100;
public void disp() {
System.out.println("methodInner x ::" + x);
}
}
InnerLocal innerLocal = new InnerLocal();
innerLocal.disp();
}
}
public class Ex04 {
public static void main(String[] args) {
LocalEx ex = new LocalEx();
ex.method();
}
}
class Outer5 {
public void aaa() {
System.out.println("a");
}
public void bbb() {
System.out.println("b");
}
public void ccc() {
System.out.println("c");
}
}
public class Ex05 {
public static void main(String[] args) {
Outer5 outer5 = new Outer5() {
public void bbb() {
System.out.println("bbbb");
}
}; // 익명클래스(=익명내부클래스)도 하나의 실행문이므로 세미콜론 필수
// 클래스의 메소드를 가져와서 수정하기 위함
outer5.aaa();
outer5.bbb();
outer5.ccc();
}
}
=================================================1교시 끝
java상속 :: 부모클래스의 멤버를 자식 클래스에게 물려줄 수 있음/ 프로그램에서는 부모클래스를 상위클래스라고 부르고 자식클래스를 하위 클래스 라고 부름 / 현실에서 상속은 부모가 자식을 선택해서 물려주지만 프로그램에서는 자식이 부모를 선택해서 부모의 멤버를 물려받음
상속을 쓰는 이유 :: 반복적으로 들어가는 공통 부분을 부모클래스에 작성하면 상속을 받는 자식클래스들은 부모의 멤버를 상속받아 그래도 사용할 수 있게 됨 / 이미 마련되어 있던 부모클래스의 기능들을 재사용하기 때문에 효율적으로 개발 시간을 줄여줌 / 수정을 해도 공통 부분만 수정해주면 되기 때문에 유지보수가 쉬워짐
자바에서의 상속 특징
여러개의 부모클래스를 상속할 수 없기때문에 extends뒤에는 단 하나의 부모 클래스만 와야함
부모 클래스에서 private 접근 제한을 갖는 필드와 메소드는 상속대상에서 제외됨
object 클래스
자바의 모든 클래스는 오브젝트 클래스의 모든 필드와 메소드를 자동으로 상속받게 됨 > 오브젝트 클래스의 모든 멤버를 자유롭게 사용할 수 있음
자바의 모든 객체에서 toString()과 같은 메소드를 바로 사용할 수 있는 이유가 오브젝트 클래스의 메소드 이기 때문
toString() 메소드 :: 해당 객체에 대한 정보를 문자열로 반환함 ex ) Test@123a439b (객체@16진수 해시코드)
super키워드(부모클래스의 기본 생성자를 호출시킴) :: 부모 클래스로 부터 상속받은 멤버를 자식 클래스에서 참조하는데 사용되는 참조변수이다
> this키워드와 비슷함
부모클래스의 생성자 호출( super( ) )은 자식 생성자의 맨 첫줄에서 호출이 됨
부모클래스의 특정 메소드는 자식클래스가 사용하기에 적합하지 않을 수 있음
이 경우 상속된 일부 메소드는 자식 클래스가 다시 수정해야하기 때문에 메소드 재정의 기능을 제공함(오버라이딩)
상속 실습
package inheritance;
class Parent {
int parentMoney = 1000;
public void parentMethod() {
System.out.println("부모클래스 입니다");
}
}
class Child extends Parent{
int childMoney = 500;
public void childMethod() {
System.out.println("자식클래스 입니다");
}
}
public class Ex01 {
public static void main(String[] args) {
Child child = new Child();
System.out.println(child.parentMoney);
child.parentMethod();
}
}
부모생성자 호출과 자식 생성자 호출
class Sedan{
String color;
public Sedan(String color) {
System.out.println("sedan 생성자입니다");
this.color = color;
}
}
class Sonata extends Sedan{
public Sonata(String color) {
// super(); // 부모 생성자 호출 && 첫줄에만 올 수 있음 (해당 키워드가 없으면 컴파일러가 만들어줌)
super(color);
System.out.println("sonata 생성자입니다");
}
}
public class Ex02 {
public static void main(String[] args) {
Sonata sonata = new Sonata("빨강");
System.out.println(sonata.color);
}
}
상속 관계에서의 오버라이딩
- 부모클래스의 private 접근제한을 갖는 필드와 메소드는 자식이 물려받을 수 없다
- 부모와 자식클래스가 서로 다른 패키지에 있다면 부모의 default 접근제한을 갖는 멤버도 자식이 물려받을 수 없다
- 그외 경우는 모두 상속의 대상이 된다
class Suv{
private String color;
int speed = 100;
public void run() {
System.out.println("SUV 차량이 달립니다");
}
}
class Santafe extends Suv{
int speed = 120;
@Override
public void run() {
System.out.println("싼타페 차량이 달립니다");
// super.run();
}
public void disp() {
System.out.println("부모" + super.speed);
super.run();
System.out.println("자식" + speed);
run();
}
// 부모클래스에 상속받은 멤버필드의 이름과 자식클래스에서 만든 멤버필드의 이름이 같을 경우
// 부모 클래스의 멤버를 사용하기 위해서는 super라는 키워드를 사용해야 한다
// 자식클래스는 this가 생략되어 있어서 그냥 사용하면 자식클래스의 멤버가 사용된다
}
public class Ex03 {
public static void main(String[] args) {
Santafe santafe = new Santafe();
// santafe.run();
santafe.disp();
}
}
=================================================2교시 끝
어노테이션 :: 소스코드에 메타 코드를 주는 것
메타코드 :: 메타 정보를 주는 것
메타 정보 :: 주요 정보가 아닌 부가 정보
런타임시(실행시) 특정 기능을 실행하도록 정보 제공
@Override 어노테이션 :: 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공한다
> 메소드 선언시 사용되며 메서드 오버라이드 된 것 임을 컴파일러에게 알려주어 컴파일러가 체크
> 체크했을 때 오버라이드가 되지 않았다면 컴파일러는 에러를 발생시킨다(부모와 동일한 메서드 시그니처를 가져왔는지 확인)
부모클래스 메소드 오버라이딩 실습
class Car{
int speed;
public void upSpeed(int speed) {
this.speed += speed;
System.out.println("현재속도 car 클래스 " + this.speed);
}
}
class Genesis extends Car{
@Override
public void upSpeed(int speed) {
super.speed += speed;
if(super.speed > 200) {
super.speed = 200;
}
System.out.println("현재속도 car 클래스 " + this.speed);
}
}
public class Ex04 {
public static void main(String[] args) {
Car car = new Car();
car.upSpeed(300);
Genesis genesis = new Genesis();
genesis.upSpeed(300);
}
}
// console
현재속도 car 클래스 300
현재속도 car 클래스 200
java 다형성 :: 하나 이상의 형태를 뜻한다(= 여러개의 형태를 갖는다) 자바에서의 다형성이란 동일한 이름을 사용하지만 다양한 객체를 이용해서 다양한 실행결과가 나오도록 하는 성질이다. 하나의 이름으로 실행결과가 여러개가 나온다는 말
부모타입으로 자식객체를 사용하는 것 / 다형성을 구현하려면 메소드 재정의(오버라이딩)와 타입변환이 필요하다
자식의 객체를 부모타입의 변수에 담을 수 있음 > >>> 다형성 *** 부모는 자식을 품을 수 있다
자식은 부모타입으로 자동 타입 변환이 가능함 / 부모타입변수 = 자식객체 [자동타입변환 (upcasting)]
** 제약사항 :: 부모타입으로 자동 타입변환 된 이후에는 부모클래스에 선언된 필드와 메소드만 접근 가능하다.
변수는 자식 객체를 참조하지만 변수로 접근 가능한 멤버는 부모클래스 멤버로만 한정됨/ 하지만 메소드가 자식 클래스에서 재정의 되었다면 자식클래스의 메소드가 대신 호출됨
강제타입변환[down casting] >> 자식 타입이 부모타입으로 자동 타입 변환 후 다시 자식타입으로 변환할 때 강제 타입변환을 시킬 수 있음 >> 자식에만 선언된 멤버만을 사용해야 할 때
***어떤 객체가 어떤 클래스의 인스턴스인지 확인하기 위해 instanceof 연산자를 사용함
Boolean result = 좌항(객체) instanceof 우항(타입) >>> 좌항의 객체가 우항의 타입으로 생성된 객체라면 true 아니면 false
**** 자동타입변환은 주로 메소드 호출할때 많이 발생함
매개변수의 타입을 부모타입으로 선언하면 매개값을 자식타입으로 받을 수 있음 /// ? 무슨말인지 다시 정리하기
class Parent{
int parentMoney = 1000;
}
class Child extends Parent{
int childMoney = 500;
}
public class Ex01 {
public static void main(String[] args) {
Parent poly = new Child(); // 자동타입변환 upcasting
// 다형성은 부모타입의 변수에 자식객체를 담는 것(부모 멤버에만 접근가능)
// 부모 위주이다 >> 객체는 자식객체이지만 타입은 부모타입임
System.out.println(poly.parentMoney);
// System.out.println(poly.childMoney); // 자식멤버 접근 안됨
System.out.println();
Child poly2 = (Child)poly; // 다운캐스팅(강제 타입변환)
System.out.println(poly2.childMoney); // 부모필드와 자식필드 모두 쓸 수 있게 됨
System.out.println(poly2.parentMoney);
Parent poly3 = poly2; // 자동타입변환 upcasting
System.out.println(poly3.parentMoney);
}
// console
1000
500
1000
1000
=================================================3교시 끝
// 다형성 부모클래스 타입의 변수에 자식객체를 담는것
// 부모클래스가 가지고 있는 모든 멤버들에 접근할 수 있다
// 단 자식 클래스에서 메소드 오버라이딩을 했다면 오버라이딩된 자식의 메소드가 호출됨
// 자식 클래스만 있는 멤버는 오버라이딩 된 자식 클래스의 메소드에 의해서만 접근할 수 있다
class Run{
int speed = 100;
public void run() {
System.out.println("부모 클래스 " + speed);
}
}
class Person extends Run{
String name = "홍길동";
public void walk() {
System.out.println("person 클래스 " + name);
}
@Override
public void run() {
System.out.println(name + "이 " + speed + "의 속도록 달린다");
walk();
}
}
class KiaCar extends Run{
String model = "k5";
public void parking() {
System.out.println("kiacar 클래스 " + model);
}
@Override
public void run() {
System.out.println("kiacar 클래스 " + model + "speed 는" + speed);
parking();
}
}
public class Ex02 {
public static void main(String[] args) {
Run run = new Person();
System.out.println(run.speed);
run.run(); // 자식 멤버필드와 멤버 메서드에 접근이 안됨
System.out.println();
run = new KiaCar();
run.run();
}
}
// console
100
홍길동이 100의 속도록 달린다
person 클래스 홍길동
kiacar 클래스 k5speed 는100
kiacar 클래스 k5
필드의 다형성
class Car{
Tire tire = new Tire();
}
class Tire{
public String tireName;
public Tire() {
tireName = "정품타이어";
}
public String getTireName() {
return tireName;
}
}
class hakTire extends Tire{
public hakTire() {
tireName = "한국타이어";
}
}
class khkTire extends Tire{
public khkTire() {
tireName = "금호타이어";
}
}
public class Ex03 {
public static void main(String[] args) {
Car car = new Car();
System.out.println(car.tire.getTireName());
car.tire = new hakTire();
// 필드의 다형성
System.out.println(car.tire.getTireName());
car.tire = new khkTire();
System.out.println(car.tire.getTireName());
// 이런식으로 객체를 부품처럼 조립하는 형태로 프로그래밍 하기 위함
// 필드의 타입을 부모타입으로 선언해두면 다양한 자식 객체들이 저장될 수 있고
// 이 때문에 필드 사용 결과가 달라질 수 있다
}
}
// console
정품타이어
한국타이어
금호타이어
매개변수의 다형성
package poly;
class Vehicle{
public void run() {
System.out.println("차량이 달립니다");
}
}
class Driver{
public void drive(Vehicle vehicle) { // 매개변수가 부모의 타입으로 되어 있으면 자식 객체를 받을 수 있음
if(vehicle instanceof Bus) { // 객체 타입인하여 메서드 사용
Bus bus = (Bus)vehicle;
bus.checkFare(); // 강제 타입변환 안하면 부모멤버만 쓸수있으니까 사용 못함
}
System.out.println("드라이브를 시작합니다");
vehicle.run();
}
}
class Bus extends Vehicle{
@Override
public void run() {
System.out.println("버스가 달립니다");
}
public void checkFare() {
System.out.println("요금확인");
}
}
class Taxie extends Vehicle{
@Override
public void run() {
System.out.println("택시가 달립니다");
}
}
public class Ex04 {
public static void main(String[] args) {
Driver driver = new Driver();
Bus bus = new Bus();
Taxie taxie = new Taxie();
driver.drive(taxie);
System.out.println();
driver.drive(bus);
System.out.println();
driver.drive(taxie); // 캐스팅 안됨
// 매개변수가 부모의 타입으로 되어 있으면 자식 객체를 받을 수 있음
}
}
// cosole
드라이브를 시작합니다
택시가 달립니다
요금확인
드라이브를 시작합니다
버스가 달립니다
드라이브를 시작합니다
택시가 달립니다
=================================================4교시 끝
**오늘 추상화 까지 나가고 인터페이스는 다음시간에 나갈 예정
객체를 직접 생성할 수 있는 클래스를 실체클래스라고 한다면 이 클래슫들의 공통 특성을 추출해서 선언한 클래스를 추상클래스라고 함
추상 클래스가 부모이고 실체 클래스가 자식 클래스라고 보면 됨 / 실체클래스는 추상클래스의 모든 특성을 물려받고 추가적인 특성을 가질 수 있음
실체클래스를 설계하는 사람이 여러 사람일 경우, 실체 클래스마다 필드와 메소드가 제각기 다른 이름을 가질 수 있다
추상클래스는 실체클래스의 필드와 메소드 이름을 통일할 용도로 사용한다
실체클래스가 가져야 할 필드와 메소드를 미리 추상클래스에 정의하므로 실체클래스 설계 규격을 만들고자할때 사용함
** 즉 , 추상클래스는 직접 사용하는 클래스가 아님/ 객체를 생성하는 용도가 아님
abstract 키워드를(추상클래스) 붙이면 new연산자를 이용해서 객체를 못만들고 상속을 통해서 자식 클래스로 만듬
**자식클래스는 반드시 추상메소드를 재정의해서 실행내용을 작성해야 함 >>> 실행블록을 가질수가 없고 선언부만 있음
abstract class Animal {
// abstract 키워드를 붙이면 추상클래스가 됨
// 추상클래스가 되면 객체를 직접 생성할수가 없음
public void eat() {
System.out.println("냠");
}
abstract void sound();
// 추상메소드 :: 실제 구현은 자식클래스에서 함 (선언부만 있고 실행블록 가지지 못함)
// 만약, 클래스 내부에 추상 메소드가 하나라도 있으면 그 클래스는 추상 클래스가 됨
// 즉 일반 클래스에서는 추상 메소드를 만들 수 없다는 뜻
}
class Dog extends Animal{
// 추상메소드 오버라이딩 안하면 오류남
// 자식클래스에서 부모의 추상메소드는 강제 오버라이딩해야함
@Override
void sound() {
System.out.println("멍멍");
}
}
class Cat extends Animal{
// 추상메소드 오버라이딩 안하면 오류남
// 자식클래스에서 부모의 추상메소드는 강제 오버라이딩해야함
@Override
void sound() {
System.out.println("야옹");
}
}
public class Ex01 {
public static void main(String[] args) {
// Animal animal = new Animal(); // 추상클래스는 객체를 직접 생성하지 못함
Dog dog = new Dog();
Cat cat = new Cat();
dog.sound();
cat.sound();
}
}
// console
멍멍
야옹
추상클래스 연습
package abstraction;
abstract class Worker{
String name;
public Worker(String name) {
this.name = name;
}
public abstract void work();
public abstract void eat();
}
// worker클래스 상속받는 TaxiDriver 클래스 만들고 추상메소드 오버라이딩 해서 재정의
// worker클래스 상속받는 Cleaner 클래스 만들고 추상메소드 오버라이딩 해서 재정의
class TaxiDriver extends Worker{
public TaxiDriver(String name) {
super(name);
}
@Override
public void work() {
System.out.println("일해라" + name);
}
@Override
public void eat() {
System.out.println("먹어라 " + name);
}
}
class Cleaner extends Worker{
public Cleaner(String name) {
super(name);
}
@Override
public void work() {
System.out.println("청소해라 " + name);
}
@Override
public void eat() {
System.out.println("구내식당가라 " + name);
}
}
public class Ex02 {
public static void main(String[] args) {
TaxiDriver driver = new TaxiDriver("홍길동");
driver.work();
driver.eat();
System.out.println();
Cleaner cleaner = new Cleaner("성춘향");
cleaner.work();
cleaner.eat();
}
}
// console
일해라홍길동
먹어라 홍길동
청소해라 성춘향
구내식당가라 성춘향
싱글톤 디자인 패턴 :: 객체를 하나만 생성하는 것을 보장
(외부에서 new 연산자를 통해 객체를 생성하지 못하도록 하는 것)
package singleton;
public class Singleton {
private Singleton() {
}
private static Singleton instance = new Singleton();
// 자기 자신을 갖는 정적 필드를 만듬
// 위의 정적 필드를 반환해주는 정적 메소드를 만들어준다
public static Singleton getInstance() {
return instance;
}
// new 연산자를 한번 사용했기때문에 싱글톤으로 리턴받는 객체는 같은 객체임
// 이렇게 싱글톤은 객체를 하나만 생성하도록 보장하는 패턴임
}
=================================================================================
package singleton;
public class SingletonMain {
public static void main(String[] args) {
// Singleton s1 = new Singleton(); 싱글톤 사용하면 이렇게 객체 여러개 못만듬
// Singleton s2 = new Singleton();
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
if(s1 == s2 ) {
System.out.println("같은 객체");
}else {
System.out.println("다른 객체");
}
}
}
다음시간에 인터페이스랑 기본 API 진도나가고
마지막 날에 collection framework 랑 IO 클래스까지 해서 수업 마무리 예정
'Java' 카테고리의 다른 글
클래스 상속과 인터페이스 사용 목적 정리 (1) | 2022.11.23 |
---|---|
다형성_추상클래스 / 인터페이스 정리 (0) | 2022.11.23 |
새싹체험_java_1022 (0) | 2022.10.22 |
새싹체험_java_1016 (0) | 2022.10.17 |
새싹체험_java_1015 (0) | 2022.10.15 |