Tipos por valor
Falando em alto nível, sem entrar no mérito da gestão da memória ou detalhes de implementação das linguagens/plataformas, podemos dizer sem medo de errar que uma variável inicializada sempre contém um valor:
int X = 1;
Integer é um tipo por valor. Uma variável de tipo por valor armazena o valor nela própria. No código acima, portanto, a variável X contém o valor 1.
Tipos por referência
Agora considere o código abaixo:
String Y = "1";
String é um tipo por referência. A variável Y, assim como a variável X, também contém um valor, mas o valor da variável Y não é "1"! O valor da variável Y é uma referência para um objeto string.
Outra forma de dizer: a variável Y não armazena o valor atribuído a ela mas sim uma referência para este valor, o qual está em algum outro lugar por aí na memória.
Uma analogia* comum é dizer que uma variável de um tipo por referência contém o endereço do valor ou o endereço do objeto que representa o valor, em vez de conter o próprio valor. Mas este endereço, por si só, também é um valor (em alguns aspectos).
Fonte: https://pt.stackoverflow.com/questions/59437/qual-a-diferen%C3%A7a-entre-passagem-por-valor-e-passagem-por-refer%C3%AAncia