본문 바로가기

Java/알고리즘

[자바 알고리즘 인터뷰] 2장 자바

1. 자바의 주요 특징 

 

1.1 객체 지향 프로그래밍

 

1) 캡슐화 

 

- 속성과 행위를 외부와 분리하는 개념

- 속성을 변수에, 행위를 메소드에, 캡슐을 클래스에 빗댈 수 있다.

- 외부 코드가 데이터에 직접 접근하지 못하도록 방어막 역할을 하고 내부 상태로는 private으로, 외부에는 public 메소드를 노출하여 정보를 은닉(Information Hiding)할 수 있다.

 

ex) Setter/Getter 메소드를 이용한 캡슐화 

- name 변수에 직접 접근하지 않고 반드시 setName() 메소드로 변경을, getName() 메소드로는 조회를 진행하는 방식 

public class Encapsulation
{
    private String name;
    
    public String getName() {
    	return name;
    }
    public void setName(String name) {
    	this.name = name;
    }
}

 

 

2) 추상화

 

- 복잡한 시스템에서 핵심적인 개념 또는 기능을 간추려내는 것

- 수행하는 방법이 아니라 수행하는 일 자체에 집중해 복잡도를 낮추는 것

 

 

3) 상속

 

- 한 객체를 또 다른 객체가 이어받는 것

- 유사하게 부모 코드를 자식 코드가 재사용할 수 있다.

 

4) 다형성

 

- 경우에 따라 객체가 다르게 동작하거나 또는 어떤 동작을 다른 방법으로 동작하게 하는 개념

- 메소드 오버로딩 

  -> 이름은 같지만 파라미터가 다른 메소드 

 

- 메소드 오버라이딩

 -> 상속을 받아서 메소드를 재정의하는 것

 

 

1.2 제네릭

 

제네릭(Generic)

 

 - 클래스나 메소드에서 사용할 자료형을 컴파일 타임에 미리 지정하는 방식

 - 이를 통해 컴파일 시점에 타입 검사를 할 수 있게 되어 객체의 타입 안정성을 높일 수 있으며, 타입 변환 시에도 자동으로 검사를 수행할 수 있다.

 

ex)

- 제네릭의 등장으로 정확한 타입을 사용했는지 여부를 컴파일 시점에 확인할 수 있게 되었다.

- 자료형을 명확히 선언할 수 있어 가독성이 높아졌다.

List<String> listNames = new ArrayList<String>();
listnames.add("카리나");
listnames.add("윈터");
listnames.add("지젤");
listnames.add("닝닝");
listnames.add(34); //에러 발생

 

 

- 제네릭을 사용하여 타입을 명시에 편리하게 반복문 구성 

for(String name : listNames)
{
	System.out.println(name);
}

 

 

1.3 함수형 프로그래밍 언어 

 

- 자바 8버전이 등장하면서 함수형 프로그래밍이 도입되었다.

- 함수형 언어 개념은 코딩 테스트에도 매우 유용하다. 

 

1) 람다 표현식

 

- 함수형 인터페이스(Functional Interface)를 매우 적은 코딩으로 간결하게 구현할 수 있다. 

- 람다 표현식의 기본 문법

(파라미터) -> 내용

 

- 함수형 인터페이스 

 -> 추상 메소드를 하나만 갖는 인터페이스

 

ex)

@FunctionalInterface
interface MathInterface {
    double getPiValue();
}

 

- 이 인터페이스를 활용하려면 기존에는 다음과 같이 익명 클래스(Anonymous Class)를 생성해 메소드명을 기입하고 사용했다. 

MathInterface math = new MathInterface() {
   @Override
    public double getPiValue() {
    	return 3.141592;
    }
};

System.out.println("Pi: " + math.getPiValue());

 

- 그러나 람다 표현식을 활용하면 다음과 같이 메소드명을 기입하지 않고도 ( )->3.141592 와 같은 하나의 식으로 간단하게 구현할 수 있다.

MathInterface math = () -> 3.141592;
System.out.println("Pi: " + math.getPiValue());

 

 

- 이와 같이 람다표현식은 특히 정렬을 구현할 때 유용하다. 

 

ex)

- Member라는 엔티티 클래스가 있고 여기에 다음과 같이 데이터를 삽입했다고 가정 

 

class Member {
	String name;
    int age;
    
    public Memeber(String name, int age) {
    	this.name = name;
        this.age = age;
    }
}

//리스트에 데이터 삽입
List<Member> members = new ArrayList<>();
members.add(new Member("윈터",23));
members.add(new Member("카리나", 24));
members.add(new Member("닝닝", 23));

 

- 나이를 추출해 역순으로 정렬하는 코드 

 -> 익명 클래스로 정렬 기준을 구현

//정렬을 익명 클래스로 구현
Collections.sort(members, new Comparator<Member>() {
    @Override
    public int compare(Member o1, Member o2) {
    	reutrn o2.age - o1.age;
    }
});

 

- Compartor는 함수형 인터페이스이며 다음과 같이 람다표현식으로 표현 가능 

//정렬을 람다 표현식으로 구현
members.sort((o1,o2) -> o2.age - o1.age);

 

 

2) 스트림 API

 

- 번거로운 반복 작업을 단 몇줄의 코드로 간결하게 처리할 수 있게 해주기 때문에 코딩 테스트에서도 매우 유용하다.

 

ex)

- 나이가 24세인 멤버의 이름을 정렬해 출력 

//스트림 API를 사용하지 않는 예전 방식
List<String> chosenMembers = new ArrayList<>();

//나이가 24세인 멤버의 이름을 chosenMembers 리스트에 삽입
for(Member member : members) {
	if(member.age == 24)
    	chosenMembers.add(member.name);
}

//chosenMembers 정렬
Collections.sort(chosenMembers);
//chosenMembers 출력
for(String name : chosenMembers) {
	System.out.println(name);
}

 

 

- 스트림 API를 이용해 위 코드를 함수형으로 변경

 -> 스트림을 시작(stream())한 다음, 나이가 24세인 값을 필터링(filter())하고, 이름 필드를 가져온 후(map())  정렬(sorted())을 거쳐 마지막 과정으로 리스트로 취합(collect())까지 한 번의 파이프라인으로 처리 가능

//스트림 API 사용방식
members.stream() //스트림 API
       .filter(m->m.age == 24) //24세 필터링
       .map(m->m.name)	//이름 매핑
       .sorted()	//정렬
       .collect(Collections.toList())	//리스트로 취합
       .forEach(System.out::println);	//메소드 참조로 출력

 

 

2. 자바의 도구 

 

JShell

 

- 자바 9버전에서 등장하면서 REPL 환경을 사용 할 수 있게 되었다.

- REPL (Read-Eval-Print Loop)

  -> 입력하고 실행하고 출력하는 과정을 반복하면서 결과를 실시간으로 확인할 수 있는 상호작용 환경 

- 터미널에 jshell만 입력하면 간단히 실행