Since this question is tagged 'java', I assume the question is on the FP practices (namely, immutability) in Java.
Today's good practice in Java is to use either a builder:
Employee e = Employee.builder()
                     .surname("Lex")
                     .age(24)
                     .name("Vasili")
                     .salary(2500)
                     .build();
or a static constructor:
Employee e = Employee.of("Vasili", "Lex", 24, 2500);
In both cases, the "classic" constructor should be declared private to ensure that the object can't be instantiated and made available to the client in inconsistent state.
Object mutators should then return the new object:
Employee.of("Vasili", "Lex", 24, 2500)  // creates an object
        .updateName("Sergey")           // returns 1st modified copy
        .updateSalary(3500);            // returns 2nd modified copy based on 1st copy
Following these practices, the need for non-final local references often vanishes.
A very popular example is the Date and Time API.
Now, on using mutable local variables. That's ok, but the code can be shortened and made more expressive using method chaining. Trying to chain the static methods as-is will not look very elegant:
Employee e = setSalary(setAge(setName(new Employee("Lex", 24, 250), "Vasili"), 12), 2500);
As an attempt to emulate a monad, one can wrap the object in some monad-like container which defines a bind method that accepts a function, which will accept the object stored in monad and return some result, which will again be wrapped in a monad. A simple example will look like this:
static class Employee {
    public String name;
    public int age;
    public long salary;
}
static class Monad<T> {
    private final T value;
    private Monad(T value) { this.value = value; }
    public static <T> Monad<T> of(T value) {
        return new Monad<>(value);
    }
    public T getValue() { return value; }
    public Monad<T> bind(UnaryOperator<T> operator){
        return of(operator.apply(value));
    }
}
public static void main(String[] args) {
    Employee value = Monad.of(new Employee())
                          .bind(e -> {e.name = "Lex"; return e; })
                          .bind(e -> {e.age = 24; return e; })
                          .bind(e -> {e.salary = 2500; return e; })
                          .getValue();
}
But this can be done with core Java since version 8 - Stream API can do this and much more:
Stream.of(new Employee())
      .map(e -> {e.name = "Lex"; return e; })
      .map(e -> {e.age = 24; return e; })
      .map(e -> {e.salary = 2500; return e; })
      .findFirst()
      .get();
     
    
e0,e1,e2are all undefined. UsingsetXandsetYas method names here is misleading. Perhaps name themcreateWithXandcreateWithY.new Employee("Lex", 24, 250).withName("Vasili").withAge(12).withSalary(2500), with thewithmethods each returning a newEmployeeobject.final, so they cannot be re-assigned / updated / modified.