As the add method changes the current calendar instance, one solution is to create another one, using the clone() method:
// clone it before changing it
GregorianCalendar other = (GregorianCalendar) gc.clone();
gc.add(Calendar.DAY_OF_MONTH, -2);
System.out.println("Karfreitag;" + dateFormat.format(gc.getTime()));
other.add(Calendar.DAY_OF_MONTH, 1);
System.out.println("Ostermontag;" + dateFormat.format(other.getTime()));
Java new Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
As you're dealing with dates (day/month/year), you can use the LocalDate class. In this new API, classes are immutable, so the methods that add a number of days always create another object:
LocalDate easter = LocalDate.parse("2017-04-16");
// minusDays and plusDays return a new LocalDate, keeping the original unchanged
System.out.println("Karfreitag;" + easter.minusDays(2));
System.out.println("Ostermontag;" + easter.plusDays(1));
The output will be:
Karfreitag;2017-04-14
Ostermontag;2017-04-17
If you have the day, month and year as int values, you can create a LocalDate using the of method:
int tag = 16;
int monat = 4;
int jahr = 2017;
// Easter
LocalDate easter = LocalDate.of(jahr, monat, tag);
Convert from/to GregorianCalendar
If you still need to work with GregorianCalendar, you can easily convert it from/to the new API. In Java 8 there are new methods to do the conversion, while in Java 6/7 ThreeTen Backport there's the org.threeten.bp.DateTimeUtils class:
// Java 8: convert calendar to local date
LocalDate dt = gc.toZonedDateTime().toLocalDate();
// Java 7: convert calendar to local date
LocalDate dt = DateTimeUtils.toZonedDateTime(gc).toLocalDate();
To convert the LocalDate back to GregorianCalendar is a little bit tricky. A LocalDate has only the date fields (day, month and year), while a GregorianCalendar represents a "full date": a date and time in a specific timezone.
So, when converting a LocalDate to a GregorianCalendar, you must make some assumptions about the time (hour, minutes, etc) and the timezone. One example is to set the time to midnight, and use the JVM default timezone:
// Java 8: convert local date to calendar (midnight in JVM default timezone)
GregorianCalendar cal = GregorianCalendar.from(dt.atStartOfDay(ZoneId.systemDefault()));
// Java 7: convert local date to calendar (midnight in JVM default timezone)
GregorianCalendar cal = DateTimeUtils.toGregorianCalendar(dt.atStartOfDay(ZoneId.systemDefault()));
You can also convert to any other time of the day and change the timezone to whatever you want:
// set to 10:30 AM in Berlin timezone
dt.atTime(10, 30).atZone(ZoneId.of("Europe/Berlin"));
Or you can use the ZonedDateTime returned by toZonedDateTime() directly, and extract the LocalDate part when printing:
// convert calendar to ZonedDateTime
ZonedDateTime z = gc.toZonedDateTime();
// print just the LocalDate part
System.out.println("Karfreitag;" + z.minusDays(2).toLocalDate());
System.out.println("Ostermontag;" + z.plusDays(1).toLocalDate());
// get the original calendar back
GregorianCalendar cal = GregorianCalendar.from(z);
This new API has lots of new types and allows you to choose the best for each case.