둘의 차이는 새로운 인스턴스를 만들어내느냐에 있다.
String str = new String("string");
이 코드는 실행될 때마다 String 인스턴스를 새로 만든다.
반복문이나 자주 호출되는 메서드 안에 있다면 쓸데없는 String 인스턴스가 수천 개 이상 만들어질 수 있다.
이를 개선한 것이 ""로 선언하는 것이다.
Stirng st = "string";
이 코드는 새로운 인스턴스를 매번 만드는 대신 하나의 String 인스턴스를 사용한다.
이 방식을 사용하면 같은 가상 머신 안에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재사용함이 보장된다.

자바의 String은 다른 참조형과는 다르게 변하지 않는다는 특징이 있다.
한 번 저장된 String 객체의 값은 변하지 않는다. s3 = s1 + s2;를 실행했을 때 "CatCat"의 값을 가지는 String 객체를 새로 생성하고, s3는 이 객체를 참조하게 된다.
String 객체들의 연산이 이루어지면, 새로운 객체를 계속 만들어내기 때문에 메모리 관리 측면에서 상당히 비효율적이다.
이러한 이유로 Heap 안에 String Constant Pool이 있다. 여기에 기존에 만들어진 문자열 값이 저장되어 있고, s1과 s2처럼 리터럴로 생성된 같은 값을 가지는 객체는 같은 래퍼런스를 가지게 된다.

위와 같이 new String 연산자를 사용할 경우 매번 새로운 객체가 생성된다.