클래스의 인스턴스를 얻는 전통적인 수단은 public생서자다
클래스는 생성자와 별도로 정적 팩터리 메서드를 제공할수 있다
정적 팩터리 메서드가 생성자 보다 좋은점
1. 이름을 가질수있다
public class Student {
    private String name;
    private int age;
    private char gender;
    // 디폴트 생성자
    public Student(){
    }
    //매개변수 생성자
    public Student(String name) {
        this.name = name;
    }
    //팩토리메서드를 이용한 초기화
    public static Student createStudentName(String name){
        return new Student(name);
    }
}
2. 호출될때마다 인스턴스를 새로 생성하지 않아도 된다
public final class Boolean implements java.io.Serializable, Comparable<Boolean> {
    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);
    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
    ...
객체 안에 미리 정의된 static final 상수 객체를 반환, 매번 새로운 객체를 만들지 않는다.
3. 반환 타입의 하위 타입의 객체를 반환할 수 있는 능력이 있다.
class Animal {
    public void speak() {
        System.out.println("Animal makes a sound");
    }
}
class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("Dog barks");
    }
    public void fetch() {
        System.out.println("Dog fetches a ball");
    }
}
class Cat extends Animal {
    @Override
    public void speak() {
        System.out.println("Cat meows");
    }
    public void scratch() {
        System.out.println("Cat scratches");
    }
}
public class AnimalTest {
    public static Animal getAnimal(String type) {
        if ("dog".equalsIgnoreCase(type)) {
            return new Dog();
        } else if ("cat".equalsIgnoreCase(type)) {
            return new Cat();
        } else {
            return new Animal(); 
        }
    }
    public static void main(String[] args) {
        Animal animal1 = getAnimal("dog");
        Animal animal2 = getAnimal("cat");
        animal1.speak(); // 출력값: Dog barks
        animal2.speak(); // 출력값: Cat meows
        
        if (animal1 instanceof Dog) {
            ((Dog) animal1).fetch(); // 출력값: Dog fetches a ball
        } else if (animal1 instanceof Cat) {
            ((Cat) animal1).scratch();
        }
        if (animal2 instanceof Dog) {
            ((Dog) animal2).fetch();
        } else if (animal2 instanceof Cat) {
            ((Cat) animal2).scratch(); // 출력값: Cat scratches
        }
    }
}
이 예시는 Animal 클래스와 Dog 및 Cat이라는 두 개의 하위 클래스가 있다.
getAnimal 메소드는 Animal 클래스의 객체를 반환하지만 , Animal의 하위 유형이기 때문에 실제로 Dog 또는 Cat의 인스턴스도 반환할 수 있다.
각각 'Dog'와 'Cat'의 인스턴스인 'animal1'과 'animal2' 모두에서 'speak' 메서드를 호출하여 이를 보여준다.
반환타이의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하던 상관없다
class Shape {
    public void draw(){
        System.out.println("모양을 그리다");
    }
}
class Circle extends Shape{
    @Override
    public void draw() {
        System.out.println("원을 그리다");
    }
}
class Retangle extends Shape
{
    @Override
    public void draw() {
        System.out.println("직사각형을 그리다");
    }
}
public class ShapeFactory{
    public Shape createShape(String shapeType){
        if("circle".equalsIgnoreCase(shapeType)){
            return new Circle();
        } else if ("retangle".equalsIgnoreCase(shapeType)) {
           return new Retangle();
        }else {
            return new Shape();
        }
    }
    public static void main(String[] args) {
        ShapeFactory shapeFactory= new ShapeFactory();
        Shape shape1= shapeFactory.createShape("circle");
        Shape shape2= shapeFactory.createShape("retangle");
        Shape shape3= shapeFactory.createShape("triangle");
        shape1.draw();
        shape2.draw();
        shape3.draw();
    }
}
위 예시는 Shape 기본 클래스와 Circle 및 Rectangle이라는 두 개의 하위 클래스가 있고, ShapeFactory 클래스에는 shapeType 매개변수를 사용하는 createShape 메서드가 있다. shapeType 값에 따라 적절한 클래스(Circle, Rectangle 또는 기본 Shape)의 인스턴스를 반환한다.
다양한 입력 매개변수로 createShape를 호출하면 다양한 클래스의 객체를 반환할 수 있다.
이는 Java에서 메소드 오버로딩을 사용하여 입력 매개변수를 기반으로 다양한 클래스의 객체를 반환하는 방법을 보여주고있다.
5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재 하지 않아도 된다
Java에서는 리플렉션 및 동적 클래스 로딩을 사용하여 컴파일 타임에 존재할 필요가 없는 클래스의 인스턴스를 생성할 수 있다
Shape라는 인터페이스가 있다고 가정할때
public interface Shape {
    void draw();
}
이제 컴파일 타임에 해당 클래스가 알려지지 않은 경우에도 이 'Shape' 인터페이스를 구현하는 클래스의 인스턴스를 반환할 수 있는 정적 팩토리 메서드를 생성.
리플렉션 및 동적 클래스 로딩을 사용하여 구현할수있다.
import java.lang.reflect.InvocationTargetException;
public class ShapeFactory {
    public static Shape createShape(String className) {
        try {
            //리플렉션을 사용하여 클래스를 동적으로 로드
            Class<?> shapeClass = Class.forName(className);
            //클래스의 인스턴스 만들기
            Object instance = shapeClass.getDeclaredConstructor().newInstance();
            // 인스턴스가 Shape 객체인지 체크
            if (instance instanceof Shape) {
                return (Shape) instance;
            } else {
                throw new IllegalArgumentException("\n" + "클래스가 Shape 인터페이스를 구현하지 않습니다");
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                 | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
    }
    public static void main(String[] args) {
        // 팩토리를 이용해 동적으로 생성
        Shape circle = ShapeFactory.createShape("Circle");
        Shape triangle = ShapeFactory.createShape("Triangle");
        if (circle != null) {
            circle.draw();
        } else {
            System.out.println("원을 만들수없다");
        }
        if (triangle != null) {
            triangle.draw();
        } else {
            System.out.println("삼각형을 만들수 없다");
        }
    }
}