<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Birgit's Blog]]></title><description><![CDATA[Birgit's Blog]]></description><link>https://blog.birgitkratz.de</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1674332242254/JnEVDZDEM.png</url><title>Birgit&apos;s Blog</title><link>https://blog.birgitkratz.de</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 11 Sep 2026 02:14:51 GMT</lastBuildDate><atom:link href="https://blog.birgitkratz.de/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Stable Values und Immutability in Java: Eine Klarstellung]]></title><description><![CDATA[Im Oktober 2025 hatte ich die Gelegenheit, bei der betterCode Java 2025 einen Vortrag zum Thema "Stable Values - JVM-optimierte Immutability" zu halten. Der Vortrag drehte sich um JEP 502 "Stable Values" und wie dieses Feature die Art und Weise verän...]]></description><link>https://blog.birgitkratz.de/stable-values-und-immutability-in-java-eine-klarstellung</link><guid isPermaLink="true">https://blog.birgitkratz.de/stable-values-und-immutability-in-java-eine-klarstellung</guid><category><![CDATA[JEP]]></category><category><![CDATA[Java]]></category><category><![CDATA[immutability]]></category><dc:creator><![CDATA[Birgit Kratz]]></dc:creator><pubDate>Thu, 20 Nov 2025 21:03:06 GMT</pubDate><content:encoded><![CDATA[<p>Im Oktober 2025 hatte ich die Gelegenheit, bei der <a target="_blank" href="https://java.bettercode.eu/#programm">betterCode Java 2025</a> einen Vortrag zum Thema "Stable Values - JVM-optimierte Immutability" zu halten. Der Vortrag drehte sich um JEP 502 "Stable Values" und wie dieses Feature die Art und Weise verändert, wie wir mit verzögerter Unveränderlichkeit in Java umgehen können.</p>
<h2 id="heading-der-abstract-zum-vortrag">Der Abstract zum Vortrag</h2>
<p>In der Java-Entwicklung kennt man das Dilemma: Daten sollen immutable sein, doch <code>final</code> -Felder erzwingen sofortige Initialisierung, was den Anwendungsstart verlangsamen kann. Nicht- <code>final</code> -Felder erlauben flexibles Timing, erschweren aber die Handhabung bei konkurrierenden Zugriffen und verhindern JVM-Optimierungen. Oft entstehen umständliche Muster.<br /><a target="_blank" href="https://openjdk.org/jeps/502">JEP 502 "Stable Values"</a> bietet eine Lösung für verzögerte Unveränderlichkeit. Ein Stable Value speichert einen Wert, der später und garantiert nur einmal gesetzt wird, oft bei Bedarf. Die Vorteile sind klar: Verbesserter Anwendungsstart durch Lazy Loading. Die JVM kann Stable Values wie <code>final</code> -Felder optimieren, was zu besserer Performance führt. Sie ermöglichen flexibles, sicheres und performantes Coden.<br />Mein Vortrag beleuchtete, wie Stable Values die Lücke schließen, gängige Muster ersetzen können und Code vereinfachen.</p>
<h2 id="heading-das-feedback">Das Feedback</h2>
<p>Nach dem Vortrag erhielt ich konstruktives Feedback, auf das ich gerne näher eingehen möchte:<br />"Immutability und Stable Values (bzw. Lazy Values) sind zwei unterschiedliche Dinge. Stable Values sind ein besseres <code>final</code> , während Immutability wirklich fordert, dass das ganze Objekt nicht veränderbar ist, d.h. da gibt es keine Setter (die neue Time-API macht das schön vor), was optimal für funktionale Programmierung ist (Stichwort: Monaden). Ein Objekt, das irgendwo als Stable Value initialisiert wird, kann lediglich nicht mehr ersetzt werden. Mit einem existierenden Setter ist es aber jederzeit veränderbar, d.h. nichts ist mit Immutability. Hier verfehlt der Vortrag das Thema…"</p>
<h2 id="heading-meine-antwort-und-klarstellung">Meine Antwort und Klarstellung</h2>
<p>Diese Anmerkung bietet eine wertvolle Gelegenheit zur Klärung. Ich möchte dazu ein paar wichtige Punkte herausarbeiten:</p>
<h3 id="heading-das-final-keyword-als-baustein">Das final-Keyword als Baustein</h3>
<p>In meinem Vortrag habe ich versucht deutlich zu machen, dass das <code>final</code>-Keyword in Java ein wichtiger Baustein für Immutability ist – aber eben nur ein Baustein, kein vollständiges Konzept. Das <code>final</code>-Keyword hat zwei wesentliche Einschränkungen:<br />Erstens muss ein als <code>final</code> deklariertes Klassenfeld spätestens im Klassen-Konstruktor initialisiert werden. Es gibt keine Möglichkeit, dies später zu tun.<br />Zweitens sind <code>final</code>-Felder nicht wirklich "final" im Sinne von konstant oder unveränderbar, wenn sie auf Instanzen von mutable Klassen verweisen. Diesen Punkt habe ich in meinem Vortrag ausdrücklich angesprochen.</p>
<h3 id="heading-was-stable-values-bieten">Was Stable Values bieten</h3>
<p>Mit <a target="_blank" href="https://openjdk.org/jeps/502">StableValue</a>, das in Java 25 als Preview eingeführt wurde (und wahrscheinlich ab Java 26 zu <a target="_blank" href="https://openjdk.org/jeps/526">LazyConstant</a> wird), haben wir nun eine neue Möglichkeit: wir können einen Wert speichern, der nicht mehr verändert werden kann, sobald er einmal gesetzt wurde. Was dieser "Wert" ist, ist erstmal nicht entscheidend, es kann sowohl eine Zahl, ein String als auch eine Referenz auf ein Objekt sein. Der entscheidende Unterschied: Der <strong>Zeitpunkt</strong>, wann dieser Wert gesetzt wird, ist flexibel thread-safe und muss nicht zwangsläufig im Konstruktor geschehen. Zudem bieten StableValues auch dieselben JVM-Optimierungsmechanismen wie das <code>final</code>-Keyword (Stichwort: Constant Folding)</p>
<h3 id="heading-der-unterschied-zur-vollen-immutability">Der Unterschied zur vollen Immutability</h3>
<p>Natürlich hat der/die Feedback-Geber/in recht: Wenn man eine Instanz einer mutable Klasse als Wert in einem StableValue speichert, kann man diese Instanz auch wieder auslesen und darauf dann setter-Methoden aufrufen. Insofern ist der Inhalt des im StableValue referenzierten Objekts dann veränderbar. StableValue macht (wie auch <code>final</code>) nur die Referenz immutable. Für wirkliche Immutability braucht man also auch immutable Klassen.</p>
<h3 id="heading-ein-baustein-kein-allheilmittel">Ein Baustein, kein Allheilmittel</h3>
<p>StableValue bietet einen weiteren Baustein, um mit Immutability in Java zu arbeiten. Wenn man konsequent immutable programmieren möchte, wird das in Java immer mit zusätzlichem Aufwand verbunden sein. Aber die Sprache entwickelt sich weiter und bietet immer mehr Hilfsmittel – von Records über Sealed Classes bis hin zu Stable Values – um idiomatischen, sicheren und performanten Code zu schreiben.</p>
<h2 id="heading-fazit">Fazit</h2>
<p>Ich hoffe, ich konnte mit dieser Klarstellung zeigen, dass mein Vortrag nicht den Anspruch hatte, Stable Values als vollständige Lösung für Immutability darzustellen, sondern als wichtigen Baustein, der eine Lücke im bisherigen Java-Ökosystem schließt. Die Diskussion zeigt, wie wichtig es ist, zwischen verschiedenen Ebenen von Immutability zu unterscheiden: die Unveränderlichkeit von Referenzen und die Unveränderlichkeit von Objektzuständen.</p>
<p>Ich freue mich über weitere Anmerkungen und Kommentare zu diesem Thema!</p>
<hr />
<p>English version:  </p>
<h1 id="heading-stable-values-and-immutability-in-java-a-clarification">Stable Values and Immutability in Java: A Clarification</h1>
<p>In October2025 I had the opportunity to give a talk at <a target="_blank" href="https://java.bettercode.eu/#programm">betterCode Java 2025</a> on the topic "Stable Values - JVM-Optimized Immutability." The presentation focused on JEP 502 "Stable Values" and how this feature transforms the way we handle deferred immutability in Java.</p>
<h3 id="heading-the-talk-abstract">The Talk Abstract</h3>
<p>In Java development, we know the dilemma: data should be immutable, but <code>final</code> fields require immediate initialization, which can slow down application startup.<br />Non-<code>final</code> fields allow flexible timing but complicate handling with concurrent access and prevent JVM optimizations. This often leads to cumbersome patterns.<br /><a target="_blank" href="https://openjdk.org/jeps/502">JEP 502 "Stable Values"</a> offers a solution for deferred immutability. A Stable Value stores a value that is set later and guaranteed to be set only once, often on demand.<br />The advantages are clear: improved application startup through lazy loading. The JVM can optimize Stable Values like final fields, resulting in better performance. They enable flexible, safe, and performant coding.<br />My talk examined how Stable Values fill this gap, can replace common patterns, and simplify code.</p>
<h3 id="heading-the-feedback">The Feedback</h3>
<p>After the talk, I received constructive feedback that I'd like to address in more detail:</p>
<p>"Immutability and Stable Values (or Lazy Values) are two different things. Stable Values are a better <code>final</code>, while immutability truly requires that the entire object be unchangeable, meaning there are no setters (the new Time API demonstrates this nicely), which is optimal for functional programming (keyword: monads). An object initialized as a Stable Value can merely not be replaced. However, with an existing setter, it remains mutable at any time, meaning there's nothing immutable about it. Here the talk misses the point..."</p>
<h3 id="heading-my-response-and-clarification">My Response and Clarification</h3>
<p>This observation provides a valuable opportunity for clarification. I'd like to highlight a few important points:</p>
<h4 id="heading-the-final-keyword-as-a-building-block">The final Keyword as a Building Block</h4>
<p>In my talk, I tried to make clear that the <code>final</code> keyword in Java is an important building block for immutability – but just a building block, not a complete concept. The final keyword has two significant limitations:<br />First, a field declared as final must be initialized no later than in the class constructor. There's no way to do this later.<br />Second, final fields aren't truly "final" in the sense of constant or immutable when they reference instances of mutable classes. I explicitly addressed this point in my presentation.</p>
<h4 id="heading-what-stable-values-offer">What Stable Values Offer</h4>
<p>With <a target="_blank" href="https://openjdk.org/jeps/502">StableValue</a>, introduced as a preview in Java 25 (and likely becoming <a target="_blank" href="https://openjdk.org/jeps/526">LazyConstant</a> from Java 26), we now have a new capability: we can store a value that cannot be changed once it's been set. What this "value" is doesn't matter initially – it can be a number, a string, or even a reference to an object. The crucial difference: the <strong>timing</strong> of when this value is set is flexible and thread-safe, and doesn't necessarily have to happen in the constructor.<br />Additionally, StableValues offer the same JVM optimization mechanisms as the <code>final</code> keyword (keyword: Constant Folding).</p>
<h4 id="heading-the-difference-from-full-immutability">The Difference from Full Immutability</h4>
<p>Of course, the feedback provider is correct: if you store an instance of a mutable class as a value in a StableValue, you can retrieve that instance and then call setter methods on it. In this respect, the content of the object referenced in the StableValue is mutable.<br />StableValue makes (like <code>final</code>) only the reference immutable. For true immutability, you also need immutable classes.</p>
<h4 id="heading-a-building-block-not-a-panacea">A Building Block, Not a Panacea</h4>
<p>StableValue offers another building block for working with immutability in Java. If you want to program consistently with immutability, it will always involve additional effort in Java. But the language continues to evolve and offers more and more tools – from Records to Sealed Classes to Stable Values – for writing idiomatic, safe, and performant code.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>I hope this clarification demonstrates that my talk didn't claim Stable Values as a complete solution for immutability, but rather as an important building block that fills a gap in the existing Java ecosystem. The discussion shows how important it is to distinguish between different levels of immutability: the immutability of references and the immutability of object states.</p>
<p>I look forward to further comments and feedback on this topic!</p>
<p> <br /> <br /> </p>
<p>Bild von <a target="_blank" href="https://pixabay.com/de/users/uroburos-325152/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=629984">Bronisław Dróżka</a> auf <a target="_blank" href="https://pixabay.com/de//?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=629984">Pixabay</a></p>
]]></content:encoded></item><item><title><![CDATA[Reading from a file within a JAR]]></title><description><![CDATA[TL;DR:
When reading resources from a file in Java it is better
to use an `InputStream` rather than a `File`.
Using `InputStream` allows you to read content
regardless of where the resource file is located.

Recently in one of the projects I'm working...]]></description><link>https://blog.birgitkratz.de/reading-from-a-file-within-a-jar</link><guid isPermaLink="true">https://blog.birgitkratz.de/reading-from-a-file-within-a-jar</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[Birgit Kratz]]></dc:creator><pubDate>Wed, 25 Jan 2023 21:08:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/oW2Fr25cGbY/upload/41df0aab4e8185bf2d17d691e634ab10.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<pre><code class="lang-plaintext">TL;DR:
When reading resources from a file in Java it is better
to use an `InputStream` rather than a `File`.
Using `InputStream` allows you to read content
regardless of where the resource file is located.
</code></pre>
<p>Recently in one of the projects I'm working on, we had the problem that we wanted to use a CSV file to read some mapping content. The content of the file was a large number of lines with 2 entries per line. The first entry contains a value to map from (lookup value) and the second entry the value to map to.</p>
<p>The idea was to read the content of the file at application startup and then hold it in a hashmap. So whenever there would be changes to the file content necessary, we only had to exchange the file and restart the application.</p>
<p>The file was placed within the /src/main/resources directory of the project. Well, maybe not the first choice of directory for a file you might want to exchange later, but that thought was pushed aside for the moment.</p>
<p>Since we develop using TDD principles, we also wrote tests to see, that the content was correctly read into the hashmap. Since the application is a SpringBoot application, we not only had plain Java tests but also tests annotated with <code>@SpringBootTest</code>. These kinds of tests start the whole Spring application context and run the tests within this context.</p>
<p>Here is the sample code, which reads the content of a file at application startup from a file within the classpath.</p>
<pre><code class="lang-java"><span class="hljs-meta">@SpringBootApplication</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadingFromFileWithinJarApplication</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Logger LOGGER = LoggerFactory.getLogger(ReadingFromFileWithinJarApplication.class);

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> ReadMappingsService service;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ReadingFromFileWithinJarApplication</span><span class="hljs-params">(ReadMappingsService service)</span> <span class="hljs-keyword">throws</span> IOException </span>{
        <span class="hljs-keyword">this</span>.service = service;
        service.readMappingFile();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException </span>{
        SpringApplication.run(ReadingFromFileWithinJarApplication.class, args);
    }

    <span class="hljs-meta">@Service</span>
    <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadMappingsService</span> </span>{
        <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">readMappingFile</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>{
            <span class="hljs-keyword">final</span> <span class="hljs-keyword">var</span> file = ResourceUtils.getFile(<span class="hljs-string">"classpath:mappings.csv"</span>);
            <span class="hljs-keyword">final</span> <span class="hljs-keyword">var</span> fileContent = Files.readAllLines(Path.of(file.getPath()));
            <span class="hljs-keyword">final</span> <span class="hljs-keyword">var</span> fileContentAsString = String.join(<span class="hljs-string">"\n"</span>, fileContent);
            LOGGER.info(fileContentAsString);
            <span class="hljs-keyword">return</span> fileContentAsString;
        }
    }
}
</code></pre>
<p>And here is the test code:</p>
<pre><code class="lang-java"><span class="hljs-meta">@SpringBootTest</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadingFromFileWithinJarApplicationTests</span> </span>{

    <span class="hljs-meta">@Autowired</span>
    ReadingFromFileWithinJarApplication.ReadMappingsService service;

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">contextLoads</span><span class="hljs-params">()</span> </span>{
    }

    <span class="hljs-meta">@Test</span>
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldReadFromMappingsFile</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>{
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">var</span> mappingsFileContent = service.readMappingFile();
        assertThat(mappingsFileContent).isNotBlank();
    }
}
</code></pre>
<p>Using IntelliJ:</p>
<ul>
<li><p>the tests are green</p>
</li>
<li><p>a call to <code>mvn clean verify</code> is also successful, also when called from Command Line</p>
</li>
<li><p>starting the application with the help of IntelliJ's Run Configuration starts it just fine</p>
</li>
</ul>
<p>But starting the application from Command Line with <code>java -jar target/ReadingFromFileWithinJar-0.0.1-SNAPSHOT.jar</code> --&gt; <mark>BOOM</mark>, it crashes.</p>
<p>How can that be?</p>
<p>So what does the error message tell me?<br /><code>FileNotFoundException</code></p>
<pre><code class="lang-bash">Caused by: java.io.FileNotFoundException: class path resource [mappings.csv]
cannot be resolved to absolute file path because it does not reside <span class="hljs-keyword">in</span> the file system: jar:file
&lt;absolutePathToFile&gt;/ReadingFromFileWithinJar/ReadingFromFileWithinJar/target/ReadingFromFileWithinJar-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/mappings.csv
</code></pre>
<p>Navigating to the path shown in the error message, shows, that the file is there. It exists! But somehow it cannot be "found"? It's there! When I can find it, so should the program!</p>
<p>After a bit of research (Stackoverflow is your reliable friend) it dawned on me.<br /><a target="_blank" href="https://stackoverflow.com/questions/25869428/classpath-resource-not-found-when-running-as-jar">https://stackoverflow.com/questions/25869428/classpath-resource-not-found-when-running-as-jar</a></p>
<p>As you can see in the sample code, we used Java's <code>File</code> class to read the content from the file.</p>
<p>And Andy Wilkinson's answer states it very clearly:<br />"<code>resource.getFile()</code> expects the resource itself <strong>to be available on the file system</strong>, i.e. it <strong>can't be nested inside a jar file</strong>. This is why it works when you run your application in STS (Spring Tool Suite) but doesn't work once you've built your application and run it from the executable jar. Rather than using <code>getFile()</code> to access the resource's contents, I'd recommend using <code>getInputStream()</code> instead. That'll allow you to read the resource's content regardless of where it's located."</p>
<p>Java's <code>File</code> class uses the "physical" space of the underlying operating system.</p>
<p>"Instances of this class may or may not denote an actual file-system object such as a file or a directory." (<a target="_blank" href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/File.html">https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/File.html</a>)</p>
<p>But since the file we wanted to read from was part of the application's JAR file (which is a file itself), it is not possible to use a Java <code>File</code> class to read from a file within a JAR file.<br />If the file we want to read from was outside of the application's JAR file, it would be no problem.</p>
<p>So what is the solution to this problem? Well, there are at least two options.</p>
<ul>
<li><p>As I just mentioned - put the file to read from outside the application's JAR file. In this case, Java's <code>File</code> class can be used.</p>
</li>
<li><p>If the previous is not an option, then you are left with using Java's <code>InputStream</code> to read the content from the file.</p>
</li>
</ul>
<p>Once we refactored the source code using an InputStream everything worked like a charm. All the tests were still green, even though I had not touched them.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Service</span>
<span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadMappingsService</span> </span>{
        <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">readMappingFile</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>{
            <span class="hljs-keyword">try</span> (<span class="hljs-keyword">var</span> inputStream = <span class="hljs-keyword">new</span> DefaultResourceLoader().getResource(<span class="hljs-string">"classpath:mappings.csv"</span>).getInputStream();
                 <span class="hljs-keyword">var</span> bufferedReader = <span class="hljs-keyword">new</span> BufferedReader(<span class="hljs-keyword">new</span> InputStreamReader(inputStream))) {
                <span class="hljs-keyword">var</span> fileContentAsString = bufferedReader.lines().collect(Collectors.joining(<span class="hljs-string">"\n"</span>));
                LOGGER.info(fileContentAsString);
                <span class="hljs-keyword">return</span> fileContentAsString;
            }
        }
    }
</code></pre>
<p>Finally, we ended up using a third option. Since the large file would not change as often as presumed, we put the content into a static HashMap inside the program. But this might not be possible in every case.</p>
<p>You can find the (not working) source, as well as the working one on <a target="_blank" href="https://github.com/bkratz/ReadingFromFileWithinJar">Github</a>.</p>
]]></content:encoded></item></channel></rss>