Dropped support for Java 7, to simplify build. Now minimum is Java 8.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 95403ad..a40850e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,12 +35,6 @@
       - name: Checkout
         uses: actions/checkout@v4
 
-      - name: Set up JDK 7
-        uses: actions/setup-java@v3
-        with:
-          java-version: 7
-          distribution: zulu
-
       - name: Set up JDK 8
         uses: actions/setup-java@v3
         with:
@@ -56,7 +50,6 @@
       - name: Prepare build.properties
         shell: bash
         run: >-
-          echo "boot.classpath.j2se1.7=${JAVA_HOME_7_X64}/jre/lib/rt.jar" >> build.properties;
           echo "boot.classpath.j2se1.8=${JAVA_HOME_8_X64}/jre/lib/rt.jar" >> build.properties;
           echo "mvnCommand=$(which mvn)" >> build.properties;
           echo "gpgCommand=$(which gpg)" >> build.properties;
diff --git a/README.md b/README.md
index 84d04cd..9626e8c 100644
--- a/README.md
+++ b/README.md
@@ -83,7 +83,7 @@
 included in OpenJDK anymore. It's not needed on Oracle Java 9,
 or if FreeMarker is configured to use Jaxen for XPath.
 
-The minimum required Java version is currently Java SE 7. (The presence
+The minimum required Java version is currently Java SE 8. (The presence
 of a later version may be detected on runtime and utilized by
 FreeMarker.)
 
@@ -194,14 +194,12 @@
    - Press "Finish"
 - Eclipse will indicate many errors at this point; it's expected, read on.
 - Project -> Properties -> Java Compiler
-  - Set "Compiler Compliance Level" to "1.7" (you will have to uncheck
-    "Use compliance from execution environment" for that)
   - In Errors/Warnings, check in "Enable project specific settings", then set
     "Forbidden reference (access rules)" from "Error" to "Warning".
 - You will still have errors on these java files (because different java
   files depend on different versions of the same library, and Eclipse can't
   handle that). Exclude those java files from the Build Path (in the Package
-  Explorer, right click on the problematic file -> "Build Path" -> "Exclude"):
+  Explorer, right-click on the problematic file -> "Build Path" -> "Exclude"):
     _Jython20*.java,
     _Jython22*.java,
     _FreeMarkerPageContext2.java,
diff --git a/build.properties.sample b/build.properties.sample
index d5e31cc..dd796c3 100644
--- a/build.properties.sample
+++ b/build.properties.sample
@@ -16,8 +16,6 @@
 # under the License.
 
 # Copy this file to "build.properties" before editing!
-# These propeties should point to the rt.jar-s of the respective J2SE versions:
-boot.classpath.j2se1.7=c:/Program Files/Java/jre7/lib/rt.jar
 boot.classpath.j2se1.8=C:/Program Files/Java/jdk1.8.0_66/jre/lib/rt.jar
 mvnCommand=C:/Program Files (x86)/maven3/bin/mvn.bat
 gpgCommand=C:/Program Files (x86)/GNU/GnuPG/pub/gpg.exe
\ No newline at end of file
diff --git a/build.xml b/build.xml
index 38c2c6e..8dc7033 100644
--- a/build.xml
+++ b/build.xml
@@ -44,15 +44,11 @@
   <property name="server.ivy.repo.root" value="${basedir}/build/dummy-server-ivy-repo" />
 
   <property file="build.properties"/>
-  <condition property="has.explicit.boot.classpath.j2se1.7">
-    <isset property="boot.classpath.j2se1.7"/>
-  </condition>
   <condition property="has.explicit.boot.classpath.j2se1.8">
     <isset property="boot.classpath.j2se1.8"/>
   </condition>
   <condition property="has.all.explicit.boot.classpaths">
     <and>
-      <isset property="has.explicit.boot.classpath.j2se1.7"/>
       <isset property="has.explicit.boot.classpath.j2se1.8"/>
     </and>
   </condition>
@@ -60,14 +56,9 @@
 
   <!-- When boot.classpath.j* is missing, these will be the defaults: -->
   <!-- Note: Target "dist" doesn't allow using these. -->
-  <property name="boot.classpath.j2se1.7" value="${sun.boot.class.path}" />
   <property name="boot.classpath.j2se1.8" value="${sun.boot.class.path}" />
 
   <!-- For checking the correctness of the boot.classpath.j2se* -->
-  <available classpath="${boot.classpath.j2se1.7}"
-    classname="java.nio.file.Path" ignoresystemclasses="true"
-    property="boot.classpath.j2se1.7.correct"
-  />
   <available classpath="${boot.classpath.j2se1.8}"
     classname="java.time.Instant" ignoresystemclasses="true"
     property="boot.classpath.j2se1.8.correct"
@@ -193,12 +184,6 @@
   </target>
 
   <target name="compile" depends="javacc">
-    <fail unless="boot.classpath.j2se1.7.correct"><!--
-      -->The "boot.classpath.j2se1.7" property value (${boot.classpath.j2se1.7}) <!--
-      -->seems to be an incorrect boot classpath. Please fix it in <!--
-      -->the &lt;projectDir>/build.properties file, or wherever you <!--
-      -->set it.<!--
-    --></fail>
     <fail unless="boot.classpath.j2se1.8.correct"><!--
       -->The "boot.classpath.j2se1.8" property value (${boot.classpath.j2se1.8}) <!--
       -->seems to be an incorrect boot classpath. Please fix it in <!--
@@ -207,8 +192,8 @@
     --></fail>
     <echo level="info"><!--
       -->Using boot classpaths: <!--
-      -->Java 7: ${boot.classpath.j2se1.7};<!--
       -->Java 8: ${boot.classpath.j2se1.8}<!--
+      -->Java 16: Current JDK classpath<!--
     --></echo>
 
     <!-- Comment out @SuppressFBWarnings, as it causes compilation warnings in dependent Gradle projects -->
@@ -235,10 +220,10 @@
     <!-- Note: the "build.base" conf doesn't include optional FreeMarker dependencies. -->
     <ivy:cachepath conf="build.base" pathid="ivy.dep" />
     <javac destdir="build/classes" deprecation="off"
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       excludes="
         freemarker/core/_Java?*Impl.java,
         freemarker/ext/jsp/**,
@@ -257,15 +242,6 @@
 
     <ivy:cachepath conf="build.base" pathid="ivy.dep" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off"
-      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
-      includeantruntime="false"
-      classpathref="ivy.dep"
-      bootclasspath="${boot.classpath.j2se1.8}"
-      includes="freemarker/core/_Java8Impl.java"
-    />
-
-    <ivy:cachepath conf="build.base" pathid="ivy.dep" />
-    <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off"
            debug="on" optimize="off" release="16" encoding="utf-8"
            includeantruntime="false"
            classpathref="ivy.dep"
@@ -274,10 +250,10 @@
 
     <ivy:cachepath conf="build.jsp2.0" pathid="ivy.dep.jsp2.0" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off"
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jsp2.0"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/jsp/**,
         freemarker/ext/servlet/**,
@@ -292,10 +268,10 @@
 
     <ivy:cachepath conf="build.jsp2.1" pathid="ivy.dep.jsp2.1" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off"
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jsp2.1"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/jsp/_FreeMarkerPageContext21.java,
         freemarker/ext/jsp/FreeMarkerJspFactory21.java,
@@ -304,10 +280,10 @@
 
     <ivy:cachepath conf="build.jython2.0" pathid="ivy.dep.jython2.0" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off"
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jython2.0"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/ant/**,
         freemarker/template/utility/JythonRuntime.java,
@@ -319,20 +295,20 @@
 
     <ivy:cachepath conf="build.jython2.2" pathid="ivy.dep.jython2.2" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off"
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jython2.2"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/jython/_Jython22VersionAdapter.java"
     />
 
     <ivy:cachepath conf="build.jython2.5" pathid="ivy.dep.jython2.5" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off"
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jython2.5"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/jython/_Jython25VersionAdapter.java"
     />
diff --git a/osgi.bnd b/osgi.bnd
index 9345879..491eb84 100644
--- a/osgi.bnd
+++ b/osgi.bnd
@@ -49,10 +49,10 @@
 # This is needed for "a.class.from.another.Bundle"?new() to work.
 DynamicImport-Package: *
 
-# The required minimum is 1.7, but we utilize versions up to 16 if available.
-# See also: http://wiki.eclipse.org/Execution_Environments, "Compiling
-# against more than is required"
-Bundle-RequiredExecutionEnvironment: JavaSE-16, JavaSE-1.7
+# List whole version range between minimum up to where we use new features
+# if available. See also: http://wiki.eclipse.org/Execution_Environments,
+# "Compiling against more than is required"
+Bundle-RequiredExecutionEnvironment: JavaSE-16, JavaSE-15, JavaSE-14, JavaSE-13, JavaSE-12, JavaSE-11, JavaSE-10, JavaSE-9, JavaSE-1.8
 
 # Non-OSGi meta:
 Main-Class: freemarker.core.CommandLine
diff --git a/src/main/java/freemarker/core/_Java8.java b/src/main/java/freemarker/core/_Java8.java
deleted file mode 100644
index e1a3954..0000000
--- a/src/main/java/freemarker/core/_Java8.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package freemarker.core;
-
-import java.lang.reflect.Method;
-
-/**
- * Used internally only, might change without notice!
- * Used for accessing functionality that's only present in Java 8 or later.
- */
-public interface _Java8 {
-
-    /**
-     * Returns if it's a Java 8 "default method".
-     */
-    boolean isDefaultMethod(Method method);
-    
-}
diff --git a/src/main/java/freemarker/core/_Java8Impl.java b/src/main/java/freemarker/core/_Java8Impl.java
deleted file mode 100644
index e4d14c0..0000000
--- a/src/main/java/freemarker/core/_Java8Impl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package freemarker.core;
-
-import java.lang.reflect.Method;
-
-/**
- * Used internally only, might change without notice!
- * Used for accessing functionality that's only present in Java 8 or later.
- */
-// Compile this against Java 8
-@SuppressWarnings("Since15") // For IntelliJ inspection
-public class _Java8Impl implements _Java8 {
-    
-    public static final _Java8 INSTANCE = new _Java8Impl();
-
-    private _Java8Impl() {
-        // Not meant to be instantiated
-    }    
-
-    @Override
-    public boolean isDefaultMethod(Method method) {
-        return method.isDefault();
-    }
-
-}
diff --git a/src/main/java/freemarker/core/_JavaVersions.java b/src/main/java/freemarker/core/_JavaVersions.java
index 9023b64..9530e59 100644
--- a/src/main/java/freemarker/core/_JavaVersions.java
+++ b/src/main/java/freemarker/core/_JavaVersions.java
@@ -31,31 +31,6 @@
         // Not meant to be instantiated
     }
 
-    private static final boolean IS_AT_LEAST_8 = isAtLeast(8, "java.time.Instant");
-
-    /**
-     * {@code null} if Java 8 is not available, otherwise the object through with the Java 8 operations are available.
-     */
-    static public final _Java8 JAVA_8;
-    static {
-        _Java8 java8;
-        if (IS_AT_LEAST_8) {
-            try {
-                java8 = (_Java8) Class.forName("freemarker.core._Java8Impl").getField("INSTANCE").get(null);
-            } catch (Exception e) {
-                try {
-                    Logger.getLogger("freemarker.runtime").error("Failed to access Java 8 functionality", e);
-                } catch (Exception e2) {
-                    // Suppressed
-                }
-                java8 = null;
-            }
-        } else {
-            java8 = null;
-        }
-        JAVA_8 = java8;
-    }
-
     private static final boolean IS_AT_LEAST_16 = isAtLeast(16, "java.net.UnixDomainSocketAddress");
 
     /**
diff --git a/src/main/java/freemarker/ext/beans/ClassIntrospector.java b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index 9ef5109..bf86781 100644
--- a/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -48,7 +48,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 import freemarker.core.BugException;
-import freemarker.core._JavaVersions;
 import freemarker.ext.beans.BeansWrapper.MethodAppearanceDecision;
 import freemarker.ext.beans.BeansWrapper.MethodAppearanceDecisionInput;
 import freemarker.ext.util.ModelCache;
@@ -413,7 +412,7 @@
         List<PropertyDescriptor> introspectorPDs = introspectorPDsArray != null ? Arrays.asList(introspectorPDsArray)
                 : Collections.<PropertyDescriptor>emptyList();
         
-        if (!treatDefaultMethodsAsBeanMembers || _JavaVersions.JAVA_8 == null) {
+        if (!treatDefaultMethodsAsBeanMembers) {
             // java.beans.Introspector was good enough then.
             return introspectorPDs;
         }
@@ -434,7 +433,7 @@
         // (Note that java.beans.Introspector discovers non-accessible public methods, and to emulate that behavior
         // here, we don't utilize the accessibleMethods Map, which we might already have at this point.)
         for (Method method : clazz.getMethods()) {
-            if (_JavaVersions.JAVA_8.isDefaultMethod(method) && method.getReturnType() != void.class
+            if (method.isDefault() && method.getReturnType() != void.class
                     && !method.isBridge()) {
                 Class<?>[] paramTypes = method.getParameterTypes();
                 if (paramTypes.length == 0
@@ -607,14 +606,14 @@
         List<MethodDescriptor> introspectionMDs = introspectorMDArray != null && introspectorMDArray.length != 0
                 ? Arrays.asList(introspectorMDArray) : Collections.<MethodDescriptor>emptyList();
 
-        if (!treatDefaultMethodsAsBeanMembers || _JavaVersions.JAVA_8 == null) {
+        if (!treatDefaultMethodsAsBeanMembers) {
             // java.beans.Introspector was good enough then.
             return introspectionMDs;
         }
 
         Map<String, List<Method>> defaultMethodsToAddByName = null;
         for (Method method : clazz.getMethods()) {
-            if (_JavaVersions.JAVA_8.isDefaultMethod(method) && !method.isBridge()) {
+            if (method.isDefault() && !method.isBridge()) {
                 if (defaultMethodsToAddByName == null) {
                     defaultMethodsToAddByName = new HashMap<>();
                 }
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 4b4dc4b..ab7368c 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -30062,6 +30062,9 @@
 
         <para>Release date: [TODO]</para>
 
+        <para>Please note that with this version the minimum required Java
+        version was increased from Java 7 to Java 8.</para>
+
         <section>
           <title>Changes on the FTL side</title>
 
diff --git a/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java b/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
index c3ff772..ac7a7b7 100644
--- a/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
+++ b/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
@@ -29,7 +29,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import freemarker.core._JavaVersions;
 import freemarker.template.Configuration;
 import freemarker.template.TemplateBooleanModel;
 import freemarker.template.TemplateHashModel;
@@ -101,7 +100,6 @@
 
     @Test
     public void java8InaccessibleIndexedAccessibleNonIndexedReadMethodTest() throws TemplateModelException {
-        assertTrue("This test case must be ran on Java 8 or later", _JavaVersions.JAVA_8 != null);
         assertFalse(Modifier.isPublic(BeanWithInaccessibleIndexedProperty.class.getModifiers()));
         
         for (Version ici : new Version[] { Configuration.VERSION_2_3_26, Configuration.VERSION_2_3_27 }) {