Java static keyword — static counter race condition
static int counter++ is not atomic — multiple threads cause duplicate transaction IDs.
20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.
- ✓Solid grasp of fundamentals
- ✓Comfortable reading code examples
- ✓Basic production concepts
- static keyword binds members to the class, not instances
- Static variables: one copy shared across all objects, allocated once at class load
- Static methods: no this reference, cannot access instance variables directly
- Static initializer blocks run once when class loads — perfect for complex config
- Static nested classes: no hidden outer instance reference, safe for Builders
- Biggest myth: static is thread-safe — plain static ints are not atomic
The static keyword in Java declares that a member (field, method, or nested class) belongs to the class itself rather than to any particular instance. This means there is exactly one copy of a static variable in the JVM, shared across all objects of that class, and static methods can be invoked without creating an instance.
The core reason static exists is to model class-level state and behavior — things like configuration constants, utility functions (e.g., Math.max()), or counters that track how many instances have been created. Without static, every object would carry its own copy of such data, wasting memory and making cross-instance coordination impossible without external mechanisms.
In practice, static variables are a common source of race conditions in multithreaded Java applications. When multiple threads concurrently increment a static counter (e.g., static int counter++), the read-modify-write sequence is not atomic — two threads can read the same value, increment it, and write back the same result, losing updates.
This is the classic "lost update" problem. The fix is to use synchronized, AtomicInteger, or volatile depending on the exact semantics needed. Static methods themselves are not inherently thread-safe; they simply lack instance state, but they can still access shared static variables or mutable objects passed as arguments.
Alternatives to static state include dependency injection (e.g., Spring singletons) or instance-level fields with explicit sharing via thread-local storage or concurrent collections. Avoid static mutable state unless you fully understand the concurrency implications — it couples code globally and makes testing harder.
For thread-safe counters, prefer java.util.concurrent.atomic.AtomicInteger over raw static int with synchronized blocks, as it uses CAS instructions for better scalability under contention. The key takeaway: static is for class-level, not thread-level, sharing — you must add explicit synchronization when multiple threads mutate that shared state.
Imagine a school where every classroom has its own whiteboard — that's like a regular instance variable, personal to each room. Now imagine the school has ONE giant scoreboard in the hallway that every classroom shares and can update — that's a static variable. It belongs to the school itself, not to any single classroom. When you change it, everyone sees the change instantly.
Every Java developer hits a point where they slap 'static' in front of something and it works — but they couldn't tell you exactly why. That's a problem, because static is one of those keywords that, misunderstood, leads to subtle bugs that take hours to track down. It shows up in utility classes, constants, counters, singleton patterns, and factory methods. It's everywhere, and interviews love it.
The 'static' keyword exists to solve a simple problem: sometimes data or behavior belongs to the TYPE itself, not to any particular object of that type. Without it, you'd have to create an object just to call a helper method like Math.sqrt() — which would be absurd. Static lets you attach things to the class blueprint rather than the houses built from that blueprint.
By the end of this article you'll know exactly when to reach for static and when to avoid it, the difference between static and instance members, how static initialization blocks work, what static nested classes actually give you, and — most importantly — the real-world patterns where each form of static pulls its weight.
What Java's static Keyword Actually Does
The static keyword in Java binds a member to the class itself, not to any instance. A static field exists exactly once per class loader — a single memory slot shared across all objects. A static method can be called without an instance, but it cannot access instance variables directly because there is no implicit this reference.
Static members are initialized when the class is first loaded, before any object is created. This means static fields are inherently global state within the JVM process. They are not thread-safe by default — concurrent writes from multiple threads produce race conditions unless you synchronize or use atomic types. Static methods also cannot be overridden polymorphically; they are hidden, not overridden.
Use static for constants (final static), utility methods (e.g., Math.max), or shared state that genuinely belongs to the class concept (e.g., a factory registry). Avoid static mutable fields in multi-threaded contexts unless you have explicit synchronization — they are the most common source of heisenbugs in production systems.
Static Variables — One Value Shared Across Every Object
A static variable is declared with the 'static' keyword at the class level. Java allocates memory for it exactly once — when the class is first loaded by the JVM — and that single memory location is shared by every instance of the class.
This is the key insight: if you change a static variable through one object, every other object immediately sees the new value. There's no copy per instance. Compare that to instance variables, where each object gets its own private copy.
The most natural use case is a counter that tracks how many objects of a class have been created. Each object shares the same counter, so incrementing it in the constructor accurately reflects the total across the entire application. Another great use is application-wide constants — think MAX_RETRIES or DEFAULT_TIMEOUT — values that never change and are logically tied to the class, not to any one instance.
Always access static variables through the class name (BankAccount.totalAccounts), not through an object reference. Accessing them via an object reference compiles fine but misleads readers into thinking it's instance state — a trap that trips up even experienced developers.
public class BankAccount { // Static variable — shared across ALL BankAccount instances // Lives in class memory, allocated once when the class is loaded private static int totalAccounts = 0; // Instance variable — each account has its OWN balance private double balance; private String accountHolder; public BankAccount(String accountHolder, double initialDeposit) { this.accountHolder = accountHolder; this.balance = initialDeposit; // Every time a new account is created, the SHARED counter goes up // All accounts see this increment immediately totalAccounts++; } // Static method to read the static variable — no object needed public static int getTotalAccounts() { return totalAccounts; } public String getAccountHolder() { return accountHolder; } public static void main(String[] args) { System.out.println("Accounts before: " + BankAccount.getTotalAccounts()); // 0 BankAccount alice = new BankAccount("Alice", 5000.00); BankAccount bob = new BankAccount("Bob", 3200.50); BankAccount carol = new BankAccount("Carol", 8100.75); // All three objects share the same totalAccounts counter System.out.println("Accounts after: " + BankAccount.getTotalAccounts()); // 3 // Accessing via instance reference — compiles but is misleading, avoid this System.out.println("Via instance ref: " + alice.getTotalAccounts()); // still 3 } }
Static Methods — Behavior That Belongs to the Class, Not the Object
A static method doesn't operate on an instance — it belongs to the class itself. That means you can call it without ever creating an object. This is exactly why Math.abs(-5) and Collections.sort(myList) don't require you to instantiate Math or Collections first.
Static methods have one firm rule: they can only directly access other static members. They have no 'this' reference, because there's no object for 'this' to point to. Trying to access an instance variable from a static method is a compile-time error.
Where do static methods shine in real code? Three places: utility/helper methods (e.g., StringUtils.isBlank), factory methods that construct and return an object (e.g., LocalDate.of(2024, 3, 15)), and methods that operate only on their parameters and don't need object state. If your method doesn't read or write any instance variables, it's a strong signal that it should probably be static.
The factory method pattern deserves special attention. Instead of forcing callers to use 'new', a static factory method can validate inputs, return cached instances, or return a subtype — giving you far more flexibility than a constructor alone.
public class TemperatureConverter { // Private constructor — this class is pure utility, no instances needed private TemperatureConverter() { throw new UnsupportedOperationException("Utility class — do not instantiate"); } // Static method: only uses its parameters, no instance state involved // Call it as TemperatureConverter.celsiusToFahrenheit(100) — no object required public static double celsiusToFahrenheit(double celsius) { return (celsius * 9.0 / 5.0) + 32.0; } public static double fahrenheitToCelsius(double fahrenheit) { return (fahrenheit - 32.0) * 5.0 / 9.0; } // Static factory method pattern — validates before constructing // Returns a formatted string; could return a full object in a real scenario public static String formatReading(double celsius, String location) {\n if (celsius < -273.15) {\n // Absolute zero check — a constructor couldn't do this as cleanly\n throw new IllegalArgumentException(\n \"Temperature below absolute zero is physically impossible: \" + celsius\n );\n }\n double fahrenheit = celsiusToFahrenheit(celsius);\n return String.format(\"%s: %.1f°C / %.1f°F\", location, celsius, fahrenheit);\n }\n\n public static void main(String[] args) {\n // No 'new TemperatureConverter()' needed — call directly on the class\n double boilingCelsius = 100.0;\n double bodyTempFahrenheit = 98.6;\n\n System.out.println(\"Boiling point in °F : \" + TemperatureConverter.celsiusToFahrenheit(boilingCelsius));\n System.out.println(\"Body temp in °C : \" + TemperatureConverter.fahrenheitToCelsius(bodyTempFahrenheit));\n System.out.println(TemperatureConverter.formatReading(36.6, \"Patient Room 4\"));\n }\n}", "output": "Boiling point in °F : 212.0\nBody temp in °C : 37.0\nPatient Room 4: 36.6°C / 97.9°F" }, "callout": { "type": "tip", "title": "Pro Tip:", "text": "If you override a static method in a subclass, it isn't true polymorphism — Java uses method hiding, not dynamic dispatch. The method that runs depends on the compile-time type of the reference, not the runtime type. This distinction trips up nearly every Java interview candidate." }, "production_insight": "In a logging library, a team added a static debug() method and later tried to override it per appender — logging silently broke because the compile-time type determined the call.\nRule: never rely on subclass behavior for static methods. Use instance methods if you need polymorphism.\nDiagnose method hiding by placing a breakpoint on the static method and checking the call stack — you'll see the compile-time type, not the runtime type.", "key_takeaway": "Static methods belong to the class, not objects.\nThey cannot access instance variables and are not polymorphic.\nUse static for utility methods and factories; use instance methods when behavior should vary by object type." }, { "heading": "Static Blocks and Static Nested Classes — Advanced Initialisation and Encapsulation", "content": "A static initializer block runs exactly once when the class is first loaded into the JVM — before any objects are created and before any static method is called. It's the right place for initialization logic that's too complex for a simple field declaration: loading a config file, registering JDBC drivers, building an immutable lookup map, or computing a value that could throw a checked exception.\n\nYou can have multiple static blocks in a class; the JVM runs them top to bottom in source order. If initialization fails and throws an exception, the class enters a broken state and any subsequent attempt to use it throws an ExceptionInInitializerError — a nasty runtime failure that's hard to debug if you've never seen it before.\n\nA static nested class is a class declared inside another class with the static modifier. Unlike an inner (non-static) class, it has no implicit reference to an instance of the outer class. This makes it ideal for logical grouping without creating a hidden memory dependency. The Builder pattern famously uses this: OrderBuilder is logically part of Order, but it doesn't need a pre-existing Order instance to do its job. Static nested classes also appear in the Entry type of Map — Map.Entry is a static nested interface for exactly this reason.", "code": { "language": "java", "filename": "AppConfiguration.java", "code": "import java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class AppConfiguration {\n\n // Static variable that needs complex initialisation\n private static final Map<String, Integer> DEFAULT_TIMEOUTS;\n private static final String ENVIRONMENT;\n\n // Static block runs once when AppConfiguration class is loaded\n // Perfect for setup that can't be done in a single-line field declaration\n static {\n System.out.println(\"[Static block] AppConfiguration class loading...\");\n\n // Build an immutable lookup map — too complex for a field initialiser\n Map<String, Integer> timeouts = new HashMap<>();\n timeouts.put(\"database\", 5000); // 5 seconds\n timeouts.put(\"httpRequest\", 3000); // 3 seconds\n timeouts.put(\"cacheRead\", 500); // 0.5 seconds\n\n // Wrap in unmodifiable so no caller can accidentally mutate shared config\n DEFAULT_TIMEOUTS = Collections.unmodifiableMap(timeouts);\n\n // In real code this might read a system property or environment variable\n String env = System.getProperty(\"app.env\");\n ENVIRONMENT = (env != null && !env.isBlank()) ? env : \"development\";\n\n System.out.println(\"[Static block] Config ready. Environment: \" + ENVIRONMENT);\n }\n\n // Static nested class — logically belongs here but needs no AppConfiguration instance\n // This is the classic Builder pattern use case\n public static class Builder {\n private String serviceName;\n private int customTimeout;\n\n public Builder serviceName(String name) {\n this.serviceName = name;\n return this;\n }\n\n public Builder customTimeout(int milliseconds) {\n this.customTimeout = milliseconds;\n return this;\n }\n\n public AppConfiguration build() {\n return new AppConfiguration(this);\n }\n }\n\n // Private instance fields set by the builder\n private final String serviceName;\n private final int resolvedTimeout;\n\n // Private constructor — only the inner Builder can call this\n private AppConfiguration(Builder builder) {\n this.serviceName = builder.serviceName;\n // Use custom timeout if provided, otherwise fall back to the shared default\n this.resolvedTimeout = builder.customTimeout > 0\n ? builder.customTimeout\n : DEFAULT_TIMEOUTS.getOrDefault(builder.serviceName, 2000);\n }\n\n public static int getDefaultTimeout(String service) {\n return DEFAULT_TIMEOUTS.getOrDefault(service, 2000);\n }\n\n @Override\n public String toString() {\n return String.format(\"AppConfiguration{service='%s', timeout=%dms}\",\n serviceName, resolvedTimeout);\n }\n\n public static void main(String[] args) {\n // First reference to the class triggers the static block\n System.out.println(\"DB default timeout: \" + AppConfiguration.getDefaultTimeout(\"database\") + \"ms\");\n\n // Builder is a static nested class — no AppConfiguration instance needed to use it\n AppConfiguration dbConfig = new AppConfiguration.Builder()\n .serviceName(\"database\")\n .build(); // uses default 5000ms from the map\n\n AppConfiguration customConfig = new AppConfiguration.Builder()\n .serviceName(\"paymentGateway\")\n .customTimeout(8000)\n .build(); // uses explicitly provided 8000ms\n\n System.out.println(dbConfig);\n System.out.println(customConfig);\n }\n}", "output": "[Static block] AppConfiguration class loading...\n[Static block] Config ready. Environment: development\nDB default timeout: 5000ms\nAppConfiguration{service='database', timeout=5000ms}\nAppConfiguration{service='paymentGateway', timeout=8000ms}" }, "callout": { "type": "info", "title": "Interview Gold:", "text": "Interviewers love asking: 'What's the difference between a static nested class and an inner class?' The answer: a static nested class has no hidden reference to the enclosing instance, so it can be instantiated independently and doesn't prevent garbage collection of the outer object. An inner class holds that reference, which can cause memory leaks if the inner class outlives the outer one." }, "production_insight": "A config library used a static block to load a properties file. When the file was missing in one environment, the whole class failed to load and every endpoint returned a 500. The error was an ExceptionInInitializerError wrapped in a NoClassDefFoundError.\nRule: never throw unchecked exceptions in static blocks without a fallback. Log the failure and set a default instead.\nDiagnosis tip: look at the cause chain — the root exception is always nested inside ExceptionInInitializerError.", "key_takeaway": "Static blocks run once at class load — use them for complex initialization.\nA failure in a static block bricks the class for the JVM lifetime.\nStatic nested classes avoid memory leaks because they hold no outer instance reference." }, { "heading": "Static Initialization Order and Class Loading Pitfalls", "content": "The order of static initialization matters. Static fields and static blocks are executed in the order they appear in the source file — top to bottom. This means if you declare a static field after a static block that tries to use it, that field will still have its default value (null, 0, false) at the time the block runs. This is a forward-reference trap.\n\nJava's compiler does warn about forward references in some cases, but not all. It's perfectly legal to write code that compiles but silently produces unexpected values due to ordering. The fix: always declare static fields before any static block that depends on them. If you cannot (e.g., fields are declared across multiple files via inheritance), move the initialization logic into a static method that is called after all fields are declared.\n\nAnother pitfall: static blocks in parent and child classes. When a subclass is loaded, the parent class's static blocks run first. Then the child's static blocks run. This order is safe, but if the parent's static block depends on something that the child's static block sets — that's impossible and will fail.\n\nThe most dangerous scenario: a static block throws a runtime exception. Java wraps it in ExceptionInInitializerError, and the class becomes permanently unusable. Any later attempt to use the class throws NoClassDefFoundError with the original cause buried in the error chain. This is especially nasty when it happens in a class that is used by many parts of the application — entire features become unreachable with a vague 'NoClassDefFoundError' log entry.", "code": { "language": "java", "filename": "ForwardReferenceTrap.java", "code": "public class ForwardReferenceTrap {\n\n // Static block before a static field — TRAP!\n static {\n System.out.println(\"Static block runs first. x = \" + x); // prints 0\n }\n\n private static int x = 42;\n\n public static void main(String[] args) {\n System.out.println(\"Main: x = \" + x); // prints 42\n }\n}", "output": "Static block runs first. x = 0\nMain: x = 42" }
Static in Multithreaded Environments — Thread Safety and Common Pitfalls
Static variables are shared across all threads. Without synchronization, concurrent access leads to race conditions, visibility issues, and tricky bugs. A classic example: a static field used as a counter without synchronization. Under load, increments can be lost because the read-modify-write operation is not atomic.
The Java Memory Model requires explicit happens-before ordering for thread visibility. A plain static int written by one thread may never be seen by another thread. Use volatile for visibility; use AtomicInteger/AtomicLong for atomic updates; use synchronized blocks or locks for compound actions.
Another subtle issue: false sharing. When multiple threads update different static variables that happen to share the same cache line, the CPU cache coherence protocol (MESI) forces cache line invalidations, hurting performance by up to an order of magnitude. Padding static fields with @Contended (JEP 142) or using ThreadLocal can mitigate this.
ThreadLocal provides per-thread copies of a static variable — essential for thread-local counters, request IDs, or database connections that must not be shared. The singleton pattern (public static final Singleton INSTANCE = new Singleton();) is thread-safe because class loading is synchronized by the JVM. However, double-checked locking with a static field requires volatile to be correct.
Utility classes should have a private constructor to prevent instantiation — this is a static method pattern that avoids the accidental creation of objects that have no instance state.
import java.util.concurrent.atomic.AtomicInteger; public class ThreadSafeCounter { // Wrong: plain static int — not thread-safe // private static int counter = 0; // Correct: AtomicInteger provides atomic increment and visibility private static final AtomicInteger counter = new AtomicInteger(0); public static int increment() { return counter.incrementAndGet(); } public static void main(String[] args) throws InterruptedException { Runnable task = () -> { for (int i = 0; i < 1000; i++) { ThreadSafeCounter.increment(); } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final count (expected 2000): " + counter.get()); } }
Static Code Blocks — Expensive Initialization That Can Kill Your Startup
A static block runs once when the class loader first loads the class. That sounds harmless until it blocks your application startup because some resource is unavailable. Static blocks execute before any static method or field is accessed, and crucially, before any instance of the class can be created. If your block throws an unhandled exception, the class becomes unusable — you get ExceptionInInitializerError and no amount of retries fixes it. The WHY: static blocks exist to initialize complex static state that can't be done in a single assignment. Think loading JDBC drivers, reading config files, or warming caches. The HOW: keep static blocks to absolute minimum lines. Prefer private static methods that can be tested independently and handle failures gracefully. Never put blocking I/O, network calls, or heavy computation inside a static block unless you're prepared for the class loading to fail in production and take down your service.
// io.thecodeforge import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Properties; public class DatabaseConfig { private static final Properties props = new Properties(); // Static block — runs ONCE during class loading static { try { var path = Path.of("/etc/app/db.properties"); if (Files.exists(path)) { try (var reader = Files.newBufferedReader(path)) { props.load(reader); } } else { // Fallback: use defaults. props.setProperty("url", "jdbc:postgresql://localhost:5432/app"); } } catch (IOException e) { // Production trap: never silently swallow. throw new RuntimeException("Failed to load DB config", e); } } public static String getUrl() { return props.getProperty("url"); } }
Static Inner Classes — The Only Nested Class You Should Actually Use
A static nested class is a class declared inside another class with the static keyword. Unlike inner classes, it does NOT hold an implicit reference to the enclosing instance. That single fact prevents a whole category of memory leaks. The WHY: every non-static inner class carries a hidden pointer to its outer class. If that inner class object outlives the outer class — common in Android handlers or Swing listeners — the outer class can't be garbage collected. That's a leak. Static nested classes avoid this entirely. The HOW: use static nested classes for helper data structures, builders, or any class that doesn't need access to the outer class's instance variables. Java's Map.Entry is the textbook example. In Spring Boot, you'll see them as DTOs, projection interfaces, or configuration holders. They behave like top-level classes but with the benefit of namespace scoping.
// io.thecodeforge import java.math.BigDecimal; import java.time.LocalDateTime; public class OrderService { // Static nested class — no hidden reference to OrderService public static record OrderSummary( Long orderId, BigDecimal total, LocalDateTime placedAt ) {} public OrderSummary summarize(Long orderId) { // pretend DB call return new OrderSummary(orderId, new BigDecimal("99.99"), LocalDateTime.now()); } // Non-static inner class (AVOID unless you have a very good reason) public class OrderProcessor { // holds implicit 'this$0' reference to OrderService public void process() { System.out.println("Processing..."); } } }
Static Counter Race Condition Brings Down Payment Pipeline
- Static mutable state is shared mutable state — it must be synchronized or thread-safe.
- AtomicInteger, volatile, and synchronized are your tools. AtomicInteger for counters, volatile for visibility, synchronized for compound actions.
- Never assume single-threaded correctness survives production concurrency.
jstack <pid> | grep -A 5 'increment\|update' to see which threads are writing to the field.Use a debugger or add logging to verify the static field's address (System.identityHashCode) stays the same across calls.Add a static { try { ... } catch(Throwable t) { t.printStackTrace(); throw t; } } block to see the stack trace in the logs before it's wrapped.Check if the static block depends on a resource that's not available (file, network, env var).Reorder static fields and blocks so that dependencies are declared before they are used. If that's not possible, move initialization into a static method called after all fields are set.Use a static initializer block at the bottom to set fields that depend on others.| Aspect | Static Member | Instance Member |
|---|---|---|
| Memory allocation | Once, when class is loaded | Each time a new object is created |
| Belongs to | The class itself | Each individual object |
| Accessed via | ClassName.member (preferred) | objectReference.member |
| 'this' keyword available? | No — no object context exists | Yes — refers to current instance |
| Can access instance members? | No — compile-time error | Yes — full access |
| Lifecycle | Lives as long as the class is loaded | Lives as long as the object exists |
| Typical use case | Counters, constants, utility methods, factories | Per-object state and behaviour |
| Overridable (polymorphism)? | No — method hiding, not overriding | Yes — dynamic dispatch applies |
| File | Command / Code | Purpose |
|---|---|---|
| BankAccount.java | public class BankAccount { | Static Variables |
| TemperatureConverter.java | public class TemperatureConverter { | Static Methods |
| ThreadSafeCounter.java | public class ThreadSafeCounter { | Static in Multithreaded Environments |
| DatabaseConfig.java | public class DatabaseConfig { | Static Code Blocks |
| OrderService.java | public class OrderService { | Static Inner Classes |
Key takeaways
Common mistakes to avoid
3 patternsCalling a static method via an object reference
Trying to access an instance variable from a static method
Assuming static variables are thread-safe because they're 'global'
Interview Questions on This Topic
Can you override a static method in Java? What actually happens if you declare a method with the same signature in a subclass?
Why can't a static method access instance variables directly? Walk me through what the JVM is doing at the memory level.
If I have a static variable in a parent class and a subclass both read it, and I modify it through the subclass reference, what value does the parent class reference see — and why?
Frequently Asked Questions
No — static methods can't be overridden in the true polymorphic sense. If you declare a static method with the same signature in a subclass, you're hiding the parent method, not overriding it. Which method runs depends on the compile-time type of the reference, not the runtime type. The @Override annotation will even refuse to compile on a static method.
Static methods belong to the class and have no 'this' reference — they exist before any object is created. Instance variables are part of an object's memory, so they don't exist at the point a static method runs. The fix is either to make the method non-static, or to receive an instance as a parameter and access the variable through that reference.
Static methods are excellent for pure utility logic and factory methods, but over-using them leads to procedural-style code that's hard to test and extend. Static methods can't be mocked in unit tests without special tools like Mockito's mockStatic, and they can't leverage polymorphism. A useful rule: if the logic depends on object state, it should be an instance method. If it operates purely on its inputs, static is fine.
The exception is wrapped in an ExceptionInInitializerError. The class becomes permanently unusable. Any subsequent attempt to use the class throws NoClassDefFoundError. To debug, look at the cause chain of the error — the root exception is nested inside. To prevent this, always handle exceptions inside static blocks and avoid throwing checked or unchecked exceptions.
Yes. Static nested classes are ideal for the Builder pattern because they have no implicit reference to an enclosing instance, so they can be instantiated independently. They also don't prevent garbage collection of the outer object, avoiding memory leaks that inner classes can cause.
20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.
That's OOP Concepts. Mark it forged?
4 min read · try the examples if you haven't