分类: 开发

聊点程序员的日常!这里没有太硬核的技术黑话,主要唠唠开发的事儿~

  • 部分省份无法访问在线服务的解决方案

    周末被反应国内的服务福建省出现无法调用的情况,心中大概知道什么问题。这个问题经常出现,尤其是在福建,江苏,河南等省份。

    下面是简单的解决方案,分别为:HTTPS(SSL),CDN,ipv6,组建集群,客户端指定DNS

    HTTPS

    大部分国内的服务加上证书可以解决很多问题,没错这是真实情况…

    CDN

    最优的解决方案,目前可以查询到上面三个省份均有节点的服务商:

    1. 阿里云
    2. 腾讯云
    3. 七牛云

    其它服务商暂时没有查询,具体可以咨询官网客服。如果是前后端分离的项目,推荐购买接口加速的CDN就可以了。至此这个方案基本可以解决所有国内地区的访问难题!

    ipv6

    现在国内云厂商是提供免费ipv6的,且绝大部分用户实际上设备和网络状况均支持ipv6,如果可以使用ipv6进行访问也可以解决很大程度的DNS污染问题。

    后端集群

    在访问不到的地区购置服务器,组建集群,亦可解决问题,但是依然需要考虑被重复封禁的问题以及客户端因为地理位置解析不精准或者缓存等原因,并未选择最优线路的方案;亦或者选择的最优路线根本就是访问不到的路线。所以这个方案被排除在最后!(这里只是以解决访问问题为例,而非其他原因)

    客户端方案:指定可访问的DNS

    部分运营商会存在白名单/黑名单的情况,当然我们的备案服务和国内IP暂时可以排除黑名单的问题。但是不排除运营商DNS存在污染情况。如果定位不到问题,不妨让客户端多加一点逻辑。首先是检测哪个DNS服务访问最快,以Android端为例,以下用到第三方库版本分别为:OKHttp3(4.13.2),dnsJava(3.5.2),下面为具体逻辑代码:

    
    object DnsLatencyTester {
        // DNS服务器列表
        private val dnsServers = listOf(
            //并非越多越好,下面只是国内流行且高可用的DNS地址,另外还有360的DNS地址,具体情况请按需添加
            "223.5.5.5",    // 阿里公共DNS
            "223.6.6.6",    // 阿里公共DNS
            "119.29.29.29", // 腾讯公共DNS
            "119.28.28.28", // 腾讯公共DNS
            "180.76.76.76", // 百度公共DNS
            "114.114.114.114", // 114DNS纯净版
            "114.114.115.115", // 114DNS纯净版
        )
    
        // 测试API地址,协商你们的任意可访问服务地址
        private const val TEST_URL = ""
        private const val TIMEOUT_MS = 5000L
    
        /**
         * 测试所有DNS服务器的延迟并返回最快的一个
         */
        suspend fun findFastestDns(context: Context): String? = withContext(Dispatchers.IO) {
            // 为每个DNS创建异步测试任务
            val deferredResults = dnsServers.map { dns ->
                async { testDnsLatency(dns) to dns }
            }
    
            // 等待所有测试完成并过滤掉超时的结果
            val results = deferredResults.awaitAll()
                .filter { it.first != -1L }
                .sortedBy { it.first }
    
            // 返回延迟最低的DNS
            results.firstOrNull()?.second
        }
    
        /**
         * 测试单个DNS服务器的延迟
         * @return 延迟时间(毫秒),-1表示超时或失败
         */
        private suspend fun testDnsLatency(dnsServer: String): Long = withContext(Dispatchers.IO) {
            return@withContext try {
                // 创建自定义DNS
                val customDns = object : Dns {
                    override fun lookup(hostname: String): List<InetAddress> {
                        try {
                            // 使用指定DNS服务器解析域名
                            val addresses = InetAddress.getAllByName(hostname)
                            return addresses.toList()
                        } catch (e: UnknownHostException) {
                            // 如果解析失败,尝试直接使用IP连接
                            return listOf(InetAddress.getByName(dnsServer))
                        }
                    }
                }
    
                // 配置OkHttpClient
                val client = OkHttpClient.Builder()
                    .dns(customDns)
                    .connectTimeout(TIMEOUT_MS, TimeUnit.MILLISECONDS)
                    .readTimeout(TIMEOUT_MS, TimeUnit.MILLISECONDS)
                    .writeTimeout(TIMEOUT_MS, TimeUnit.MILLISECONDS)
                    .build()
    
                // 创建请求
                val request = Request.Builder()
                    .url(TEST_URL)
                    .head() // 使用HEAD请求减少数据传输
                    .build()
    
                // 记录开始时间
                val startTime = System.currentTimeMillis()
    
                // 发送请求
                client.newCall(request).execute().close()
    
                // 计算延迟时间
                System.currentTimeMillis() - startTime
            } catch (e: Exception) {
                // 发生异常时返回-1
                -1L
            }
        }
    }

    可以在闪屏页调用(注意实际用户体验):

            lifecycleScope.launch {
                val fastestDns = DnsLatencyTester.findFastestDns(binding.imageViewcontent.context)
                if (fastestDns != null) {
                    Log.i("DNS_TEST", "最快的DNS服务器: $fastestDns")
                    SPUtils.getInstance().put("fastestDns", fastestDns)
                    getInApp()
                } else {
                    Log.e("DNS_TEST", "所有DNS服务器测试失败")
                    SPUtils.getInstance().put("fastestDns", "")
                    getInApp()
                }
            }

    OKHttp3的使用示例:

            var customDns: Dns = Dns.SYSTEM // 默认使用系统 DNS
    
            val fastestDns = SPUtils.getInstance().getString("fastestDns")
    
            if (fastestDns.isNotEmpty()) {
                try {
                    customDns = CustomDns(fastestDns)
                } catch (e: UnknownHostException) {
                    e.printStackTrace()
                    customDns = Dns.SYSTEM
                }
            }
            val okHttpClientBuilder = OkHttpClient().newBuilder().apply {
                connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                dns(customDns)
                addInterceptor(MInterceptor())
                addInterceptor(DataEncryptInterceptor())

    中间存在的CustomDNS代码如下:

    
    class CustomDns(dnsServer: String) : Dns {
        private var resolver: SimpleResolver? = null
    
        init {
            try {
                // 使用自定义的DNS服务器地址
                this.resolver = SimpleResolver(dnsServer)
            } catch (e: UnknownHostException) {
                throw IllegalArgumentException("Invalid DNS server address: $dnsServer", e)
            }
        }
    
        @Throws(UnknownHostException::class)
        override fun lookup(hostname: String): List<InetAddress> {
            try {
                // 使用dnsjava进行解析
                val name = Name("$hostname.")
                val record: Record = Record.newRecord(name, Type.A, DClass.IN)
                val query = Message.newQuery(record)
    
                val response = resolver!!.send(query)
    
                val addresses: MutableList<InetAddress> = ArrayList()
                if (response.rcode == Rcode.NOERROR) {
                    val records: Array<Record> = response.getSectionArray(Section.ANSWER)
                    for (r in records) {
                        if (r is ARecord) {
                            val a = r as ARecord
                            addresses.add(a.address)
                        }
                    }
                }
    
                if (addresses.isEmpty()) {
                    throw UnknownHostException("No address found for $hostname")
                }
    
                return addresses
            } catch (e: Exception) {
                // 捕获所有dnsjava的异常,并转换为UnknownHostException
                throw UnknownHostException("DNS lookup failed for " + hostname + ": " + e.message)
            }
        }
    }

    以上就是我目前可以想到的全部解决方案,综合之后我个人建议的排序为:

    CDN>DNS>集群,(ipv6以及HTTPS为默认配置😄)

  • Android 12的启动白屏简单适配方案

    TL;DR

    1. 导入implementation 'androidx.core:core-splashscreen:1.0.1'
    2. 继承主题 Theme.ScreenSplash,继承的主题可以设置前景和背景以及时间(最大500),类似icon
    3. 使用主题

    在国产大厂的APP上,每一个Feature,每一个模块,每一个按钮,甚至每一个不可交互的地方,它都有出生的意义:广告位。

    闪屏页的战略地位不言而喻😄

    在 Android 12(SDK 31)之前,应用的启动画面(Splash Screen)一直是个令人头疼的问题。开发者们不得不绞尽脑汁,通过自定义 Activity 主题、设置 windowBackground 或者创建一个独立的 Splash Activity 来模拟启动效果。

    不仅需要作为天然的广告位,这个一闪而过页面承载太多东西了。你的所有需要注册的内容,所有内容的完整性检查,服务器判断,DNS解析。当然还有广告的加载以及用户的手有没有抖动…

    虽然目前看来体验不尽人意,适配寥寥无几,不过当初Android12似乎真的想改变这些(当然不止这些,更多的可能是体验统一的问题),所以带来一个原生,更高效的解决方案。抛开这些开发决定不了的内容,下面我简单记录一下自己适配这个SplashScreen,当然这也是我第一次使用这个特性。

    核心概念与 Theme.SplashScreen 属性详解

    Android 12 的启动画面不再是应用自身绘制的 View,而是由系统根据主题配置生成并管理。你的启动 Activity 会首先应用一个继承自 Theme.SplashScreen 的主题,系统会根据该主题的属性来渲染启动画面,并在应用准备就绪后平滑过渡到应用真正的界面。下面是我在网上找到的VerseAPP的动画闪屏页(你也可以下载查看teelgram的动画效果):

    以下是 Theme.SplashScreen 主题及其关键属性的详细解析:

    1. 父主题:Theme.SplashScreen

    • 作用: 这是 Android 12+ 系统提供的启动画面基础主题。你的自定义启动主题必须继承这个主题,才能享受到系统级别的启动画面管理和动画效果。

    2. 背景属性:windowSplashScreenBackground

    类型: Drawable 引用(@drawable/@color/

    作用: 定义启动画面的背景。这是替换传统“白屏”的关键。

    自定义方案:

    • 纯色: 最简单也是最高效的方式,直接引用一个颜色资源,如 <item name="windowSplashScreenBackground">@color/your_brand_color</item>
    • 渐变色: 创建一个 shape Drawable,并在其中定义 gradient 标签,实现平滑的颜色过渡。
    • 图片作为背景(慎用,推荐 LayerList): 虽然可以直接引用一张图片 (@drawable/your_image),但通常不推荐直接将一张大图作为背景,因为它可能导致屏幕适配问题、文件尺寸增大和内存开销。
    • LayerList 组合背景

    3. 图标属性:windowSplashScreenAnimatedIcon

    • 类型: Drawable 引用(@drawable/@mipmap/
    • 作用: 定义在启动画面中心显示的图标或动画。
    • 可选动画类型:
      • Animated Vector Drawable (AVD)
        • 格式: XML (.xml),定义了矢量图的动画。
        • 优点: 官方推荐,文件小巧,可缩放不失真,性能优秀,可以实现复杂的矢量动画。
        • 创建: 复杂动画通常需要手动编写 XML 或借助工具。
      • Animation Drawable:
        • 格式: XML (.xml),引用一系列帧图片。
        • 优点: 简单易懂,适合简单的帧动画。
        • 缺点: 每一帧都是一张图片,文件体积大,内存占用高,缩放可能失真。适用于帧数非常少且简单的动画。
      • 自适应图标 (Adaptive Icon):
        • 格式: 通常是矢量图,由前景层 (@mipmap/ic_launcher_foreground) 和背景层 (@mipmap/ic_launcher_background) 组成。
        • 优点: 系统原生支持,自动适应不同形状的图标蒙版,无需额外动画即可平滑缩放。
        • 最常用方案: 如果没有自定义动画需求,直接使用你的自适应图标前景层即可,例如:<item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher_foreground</item>

    4. 动画持续时间:windowSplashScreenAnimationDuration

    • 类型: 整数(毫秒)
    • 作用: 定义 windowSplashScreenAnimatedIcon 中图标动画的持续时间。系统会在这个时间结束后开始淡出启动画面。
    • 注意: 你的动画设计应尽量在这个持续时间内完成,以避免动画被截断或过早结束。建议值为 200ms 到 1000ms 之间,保持快速且流畅。

    5. 品牌 Logo:windowSplashScreenBrandDrawable (可选)

    • 类型: Drawable 引用
    • 作用: 在启动画面底部显示一个可选的品牌 Logo。
    • 位置: 这个 Logo 会固定显示在启动画面的底部,不会随图标一起动画。
    • 用处: 适合展示公司或产品的额外品牌标识。

    6. 核心属性:postSplashScreenTheme

    • 类型: Style 引用(@style/
    • 作用: 这是最重要的属性!它指定了启动画面结束后,Activity 应该切换到哪个主题来渲染你的应用界面。
    • 重要性: 如果没有正确设置这个属性,你的 Activity 在启动画面消失后,可能会显示错误的样式,甚至出现界面空白或闪烁。务必将其指向你应用正常运行时所使用的主要主题。

    二、应用开发实践:从零开始配置

    让我们通过一个完整的示例,一步步将 Android 12 的启动画面集成到你的应用中。

    1: 确保你的项目兼容 Android 12 (SDK 31+)

    首先,在你的 build.gradle (Module: app) 文件中,确保 compileSdktargetSdk 至少是 31:

    android {
        compileSdk 31 // 或更高版本
        defaultConfig {
            targetSdk 31 // 或更高版本
            // ...
        }
        // ...
    }

    2. 添加 Splash Screen 库依赖

    dependencies {
        // ... 其他依赖
        implementation 'androidx.core:core-splashscreen:1.0.1' 
    }

    3. 定义你的应用主题

    res/values/themes.xml (和 res/values-night/themes.xml,用于深色模式) 中定义应用主主题。这将是 postSplashScreenTheme 指向的主题。

    <resources>
        <style name="Theme.MyAwesomeApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
            <item name="colorPrimary">#6200EE</item>
            <item name="colorPrimaryVariant">#3700B3</item>
            <item name="colorOnPrimary">#FFFFFF</item>
            <item name="colorSecondary">#03DAC6</item>
            <item name="colorSecondaryVariant">#018786</item>
            <item name="colorOnSecondary">#000000</item>
            <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
            <item name="android:navigationBarColor">@color/black</item>
            <item name="android:windowBackground">@color/white</item>
            </style>
    </resources>
    
    <resources>
        <style name="Theme.MyAwesomeApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
            <item name="colorPrimary">#BB86FC</item>
            <item name="colorPrimaryVariant">#3700B3</item>
            <item name="colorOnPrimary">#000000</item>
            <item name="colorSecondary">#03DAC6</item>
            <item name="colorSecondaryVariant">#03DAC6</item>
            <item name="colorOnSecondary">#000000</item>
            <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
            <item name="android:navigationBarColor">@color/black</item>
            <item name="android:windowBackground">@color/dark_gray</item>
        </style>
    </resources>
    

    4. 准备启动画面背景(渐变色和Logo)

    我们创建一个 LayerList 来实现一个包含渐变背景和底部品牌Logo的启动画面。(实际上不推荐 用渐变色,我更推荐telegram或者X的方案,纯色背景+AVD/静态LOGO)

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)">
        <item>
            <shape android:shape="rectangle">
                <gradient
                    android:angle="270"
                    android:startColor="#6200EE"
                    android:endColor="#03DAC6"
                    android:type="linear" />
            </shape>
        </item>
    
        <item android:bottom="32dp"> <bitmap
                android:src="@drawable/ic_my_company_logo"
                android:gravity="bottom|center_horizontal" />
        </item>
    </layer-list>
    

    注意:

    • @drawable/ic_my_company_logo 应该是你的 Logo 图片(Vector Drawable 或 Bitmap)。
    • android:bottom 可以调整 Logo 的位置。
    • android:gravity="bottom|center_horizontal" 将 Logo 放置在底部中央。

    5. 准备启动画面动画图标(AVD 示例)

    假设你已经有一个名为 ic_animated_logo.xml 的 Animated Vector Drawable。

    <?xml version="1.0" encoding="utf-8"?>
    <animated-vector xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
        android:drawable="@drawable/ic_static_logo"> <target android:name="group_name_in_static_logo"> <propertyValuesHolder
                android:propertyName="rotation"
                android:valueFrom="0"
                android:valueTo="360"
                android:valueType="floatType"
                android:duration="1000"
                android:interpolator="@android:interpolator/fast_out_slow_in" />
        </target>
    </animated-vector>
    

    如果不想使用自定义动画,直接使用自适应图标前景部分:@mipmap/ic_launcher_foreground

    6. 定义启动画面主题

    现在,在 res/values/themes.xml (和 res/values-night/themes.xml) 中定义你的启动主题,并引用我们准备好的 Drawable。

    <resources>
        <style name="Theme.MyAwesomeApp.SplashScreen" parent="Theme.SplashScreen">
            <item name="windowSplashScreenBackground">@drawable/splash_layer_bg</item>
    
            <item name="windowSplashScreenAnimatedIcon">@drawable/ic_animated_logo</item>
            <item name="windowSplashScreenAnimationDuration">1000</item> <item name="postSplashScreenTheme">@style/Theme.MyAwesomeApp</item>
        </style>
    </resources>
    
    <resources>
        <style name="Theme.MyAwesomeApp.SplashScreen" parent="Theme.SplashScreen">
            <item name="windowSplashScreenBackground">@color/dark_splash_bg_color</item>
            <item name="windowSplashScreenAnimatedIcon">@drawable/ic_animated_logo_dark</item>
            <item name="windowSplashScreenAnimationDuration">1000</item>
            <item name="postSplashScreenTheme">@style/Theme.MyAwesomeApp</item>
        </style>
    </resources>
    

    7. 在 AndroidManifest.xml 中应用主题

    AndroidManifest.xml 中,将 Theme.MyAwesomeApp.SplashScreen 主题应用到启动 Activity(通常是 MainActivity,也有可能是SplashActivity之类的)。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="[http://schemas.android.com/apk/res/android](http://schemas.android.com/apk/res/android)"
        xmlns:tools="[http://schemas.android.com/tools](http://schemas.android.com/tools)">
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.MyAwesomeApp"> <activity
                android:name=".MainActivity"
                android:exported="true"
                android:theme="@style/Theme.MyAwesomeApp.SplashScreen"> <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <activity android:name=".OtherActivity"
                android:theme="@style/Theme.MyAwesomeApp"/>
    
        </application>
    </manifest>
    

    8. 在 MainActivity 中安装启动画面

    最后,在启动 Activity 的 onCreate() 方法中调用 installSplashScreen()这是启用系统启动画面 API 的核心步骤。

    // MainActivity.kt
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.withContext
    import androidx.lifecycle.lifecycleScope
    import kotlinx.coroutines.launch
    
    class MainActivity : AppCompatActivity() {
    
        private var isContentReady = false // 用于控制启动画面何时消失
    
        override fun onCreate(savedInstanceState: Bundle?) {
            // 1. 调用 installSplashScreen() 必须在 super.onCreate() 之前
            val splashScreen = installSplashScreen()
    
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            // 2. 延迟启动画面消失,直到内容准备就绪
            splashScreen.setKeepOnScreenCondition {
                !isContentReady // 当 isContentReady 为 false 时,启动画面会一直显示
            }
    
            // 3. 模拟数据加载或应用初始化
            loadAppContent()
        }
    
        private fun loadAppContent() {
            lifecycleScope.launch(Dispatchers.IO) {
                // 模拟耗时的初始化操作,例如网络请求、数据库加载等
                delay(3000) // 模拟 3 秒的加载时间
    
                // 数据加载完成
                isContentReady = true
    
                withContext(Dispatchers.Main) {
                    // 在这里可以执行数据加载完成后的 UI 更新或跳转操作
                    // 例如:navigateToHome()
                }
            }
        }
    }
    

    Java 示例(AI转换的代码,应该咩问题):

    // MainActivity.java
    import android.os.Bundle;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.splashscreen.SplashScreen;
    import android.os.Handler;
    import android.os.Looper;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class MainActivity extends AppCompatActivity {
    
        private boolean isContentReady = false; // 用于控制启动画面何时消失
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // 1. 调用 installSplashScreen() 必须在 super.onCreate() 之前
            SplashScreen splashScreen = SplashScreen.Companion.installSplashScreen(this);
    
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // 2. 延迟启动画面消失,直到内容准备就绪
            splashScreen.setKeepOnScreenCondition(() -> !isContentReady);
    
            // 3. 模拟数据加载或应用初始化
            loadAppContent();
        }
    
        private void loadAppContent() {
            ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
            executor.schedule(() -> {
                // 模拟耗时的初始化操作
                isContentReady = true; // 数据加载完成
    
                new Handler(Looper.getMainLooper()).post(() -> {
                    // 在主线程执行数据加载完成后的 UI 更新或跳转操作
                });
            }, 3, TimeUnit.SECONDS); // 模拟 3 秒的加载时间
        }
    }
  • 三十而立,JAVA生日快乐!

    当一款一出生就颇具竞争力,甚至在每个榜单都名列前茅的编程语言持续更新到30周年,它已经不只是一门编程语言,一个学科,一项技术了。它是一个时代的标志。

    Java 技术演进之路与时代回响

    1995 年 5 月 23 日,Sun Microsystems 发布了 Java 语言,谁也没想到这个诞生于小型家电控制项目的编程语言,会在接下来的三十年里重塑整个软件开发生态。从 Web 时代的 Servlet 到移动互联网的 Android,从大数据领域的 Hadoop 到云原生时代的 Kubernetes,Java 始终站在技术变革的潮头。今天,在 Java 迎来三十周年生日之际,让我们回溯那些改变技术史的里程碑时刻。

    一、“一次编写,到处运行” 的传奇起点

    Java 的诞生源于 Sun 公司 “Green Team” 的一个冷门项目 —— 为智能电视、机顶盒等设备开发编程语言。詹姆斯・高斯林(James Gosling)带领团队设计出 Oak 语言(Java 前身),却因市场时机未到陷入沉寂。直到互联网浪潮袭来,Oak 凭借 “Write Once, Run Anywhere”(WORA)的跨平台特性,在浏览器插件领域找到突破口。1995 年发布的 Java 1.0 定义了核心特性:字节码、虚拟机(JVM)、垃圾回收机制,这些设计让 Java 程序能够在不同操作系统上以相同行为运行,彻底改变了软件开发的部署模式。

    1996 年,第一个 Java 开发工具包(JDK 1.0)正式发布,包含 AWT 图形库与 Applet 技术。Applet 允许 Java 程序嵌入网页运行,引发了第一次 Java 热潮。尽管后来因安全问题逐渐被淘汰,但它验证了 Java 在网络应用领域的潜力。

    二、企业级开发的统治时代

    1998 年,Java 2 平台的发布标志着 Java 正式进军企业级应用领域。Java 2 平台分为标准版(J2SE)、企业版(J2EE)和微型版(J2ME),其中 J2EE 通过 EJB(Enterprise JavaBeans)、Servlet、JSP 等技术,为分布式应用开发提供了完整解决方案。IBM、Oracle 等巨头纷纷加入 Java 阵营,Java EE 成为构建银行核心系统、电商平台的事实标准,支撑起全球 80% 以上的企业级应用开发。

    2004 年,Java 5(代号 Tiger)的发布堪称 Java 史上的重大飞跃。泛型、自动装箱 / 拆箱、枚举类型、可变参数等特性的引入,让 Java 从单纯的面向对象语言进化为更现代化的编程语言。同年,Spring 框架 1.0 发布,以轻量级容器和依赖注入理念革新了企业级开发模式,与 Java EE 形成互补,共同奠定了 Java 在企业级市场的统治地位。

    三、移动革命与开源觉醒

    2007 年,苹果发布 iPhone,智能手机时代拉开帷幕。而 Java 早已提前布局,J2ME 曾是功能机时代的主流开发平台。2008 年,谷歌推出基于 Linux 内核和 Java 的 Android 系统,将 Java 推向移动开发的巅峰。虽然 Android 后来改用 Kotlin 作为官方推荐语言,但 Java 至今仍是安卓开发者的重要工具,全球数十亿台设备上运行着 Java 编写的应用。

    2006 年,Sun 宣布 Java 开源,成立 OpenJDK 社区。这一决策打破了商业闭源的限制,激发了全球开发者的创造力。Apache Harmony、IBM J9 等替代 JVM 项目涌现,OpenJDK 逐步成为 Java 的参考实现。2010 年 Oracle 收购 Sun 后,围绕 Java 版权与开源协议的争议不断,但社区力量最终推动 Java 走向更开放的生态。

    四、云原生时代的涅槃重生

    2011 年起,Java 进入快速迭代周期,每半年发布一个新版本。Java 8 引入的 Lambda 表达式与 Stream API,让函数式编程范式融入传统 OOP 体系,极大提升了代码简洁性与并行处理能力;Java 11 成为首个 LTS(长期支持)版本,精简模块化系统并移除过时特性,标志着 Java 向轻量化转型。

    随着容器化与微服务兴起,Java 凭借 Spring Cloud、Quarkus 等框架,以及 GraalVM 即时编译技术,在云原生领域焕发新生。Kubernetes 项目的核心组件 Kubelet、kube-apiserver 均采用 Java 开发,印证了其在分布式系统领域的卓越性能与稳定性。

    五、下一个三十年:Java 的无限可能

    站在三十周年的节点,Java 生态早已超越编程语言本身,成为覆盖开发、部署、运维全生命周期的技术体系。如今,Java 正积极拥抱 AI 与 Web3 领域,通过 Project Loom 虚拟线程优化高并发场景,探索与机器学习框架的深度集成。OpenJDK 社区持续推进 JVM 性能优化,让 Java 在边缘计算、Serverless 等新兴领域保持竞争力。

    从智能家电到数据中心,从桌面应用到云端服务,Java 用三十年时间证明了 “技术生命力在于持续进化”。对于全球 1000 万 Java 开发者而言,这不仅是一门编程语言的生日,更是一个时代技术精神的缩影。未来,随着 Java 22 计划引入结构化并发、虚拟线程成熟化等重磅特性,这个 “永远年轻” 的语言仍将书写新的传奇。


    Java重要版本更新列表

    由GROK.COM生成

    1. Java 1.0(1996 年 1 月)

    • 定义核心特性,如字节码、虚拟机(JVM)、垃圾回收机制,实现 “Write Once, Run Anywhere” 跨平台运行。
    • 发布第一个 Java 开发工具包(JDK 1.0),包含 AWT 图形库与 Applet 技术。

    2. Java 1.1(1997 年 2 月)

    • 引入内部类,增强代码封装与组织性。
    • 加入反射机制,支持运行时检查和操作类、方法、字段。
    • 诞生 JDBC,方便 Java 程序与数据库交互。

    3. Java 2 平台(1998 年)

    • 分为标准版(J2SE)、企业版(J2EE)和微型版(J2ME)。其中 J2EE 通过 EJB、Servlet、JSP 等技术,为分布式应用开发提供完整解决方案。

    4. Java 5(2004 年 9 月)

    • 引入泛型,提升类型安全性与代码复用性。
    • 实现自动装箱 / 拆箱,简化基本数据类型与包装类的转换。
    • 新增枚举类型,方便定义常量集合。
    • 支持可变参数,使方法参数数量更灵活。

    5. Java 7(2011 年 7 月)

    • 引入 try – with – resources 语句,简化资源管理。
    • 钻石操作符(<>)自动推断泛型类型参数。
    • Fork/Join 框架提升并行处理大量数据的效率。
    • NIO 升级到 NIO.2,新增异步 I/O 和文件系统 API。

    6. Java 8(2014 年 3 月)

    • 引入 Lambda 表达式,支持函数式编程范式。
    • 推出 Stream API,提升代码简洁性与并行处理能力。
    • 允许为接口方法添加默认方法体。
    • 引入日期时间应用程序接口(date time API)。

    7. Java 9(2017 年 9 月)

    • 引入模块化系统(Project Jigsaw),解决类路径问题,提高代码可维护性。
    • 提供 Javadoc,支持在 API 文档中进行搜索,输出兼容 HTML5 标准。
    • 在 List、Set、Map 接口中,静态工厂方法可创建不可变实例。
    • 内置轻量级 JSON API。

    8. Java 10(2018 年 3 月)

    • 带来局部变量类型推断(var 关键字),简化代码声明。
    • 提升线程间通信效率的线程本地握手机制。
    • 将 JDK 多个代码仓库合并到一个储存库。

    9. Java 11(2018 年 9 月)

    • 成为首个 LTS(长期支持)版本。
    • 精简模块化系统,移除过时特性。
    • 引入 NestMembers 属性和 NestHost 属性。
    • 支持 TLS 1.3 协议。

    10. Java 17(2021 年 9 月)

    • 引入密封类,限制类的继承层次,增强代码安全性。
    • 恢复浮点语义,为伪随机数发生器(PRNG)提供新接口类型。
    • 增加 macOS AArch64 端口,删除 Applet API、远程方法调用(RMI)激活机制,强封装 JDK 内部。
  • 基于Nextjs的说说程序

    TL;DR

    点这里看效果: https://ss.banzhuanriji.com

    发布,查看


    一代人有一代人的QQ空间要踩 XD ,可是QQ空间怎么少的了说说呢。而且发长文会带动一切RSS订阅用户,偶尔想当个动态/朋友圈发的话,太打扰人了。

    所以我想到了两种方案:

    • RSS避免某个分类的文章,把这个分类当做说说来发。但我想了想,似乎在function里加一些方法直接控制数据更好用一点,而且样式制作可以直接在CSS里修改,不影响其他文章样式内容。所以wordpress的方案又分为两种
      • 不经过RSS的分类
      • 在wordpress的function中创建独立的方法内容
    • 创建一个独立的说说页面

    但是目前wordpress的数据库已经够复杂,冗余过于夸张了。并且之后如果我不希望使用wordpress的话,如何迁移?所以决定使用独立的方案。

    第一想到的就是目前已经存在的有没有可用的。第一个想到的就是面条大佬的BroadcastChannel ,把TG频道做数据来源。实话实说挺好用的。不过样式不可控,可能需要自己修改,且一些TG的内容样式不支持。所以我就采用了Nextjs+neon数据库的方案写了这个程序。

    功能

    • 浏览内容,分页
    • 鉴权登录
    • 发布内容

    忽略了内容修改的功能,因为要做修改的话可能需要在首页做登录判断,然后在布局上做一些按钮预留。想了想暂时不做这个了

    特色

    如果可以成为特色的话…

    • 全部白嫖,白嫖!
    • 结构超级简单
    • Markdown支持

    预览

    下载

    如果你有兴趣可以查看这里: https://ss.banzhuanriji.com

    暂时没有提供下载,等我完善之后在github上发出来XD。比较感兴趣的话也可以联系我,我会发给你一份源码: m#hoytzhang.com 或者在文章下面留言 ;D