• [技术干货] Spring IoC和DI
    IoC什么是IoC?像在类上⾯添加 @RestController 和@Controller 注解, 就是把这个对象交给Spring管理, Spring 框架启动时就会加载该类. 把对象交给Spring管理, 就是IoC思想.IoC:Inversion of Control (控制反转), 也就是说 Spring 是⼀个"控制反转"的容器.什么是控制反转呢? 也就是控制权反转. 什么的控制权发⽣了反转? 获得依赖对象的过程被反转了也就是说, 当需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊ (Dependency Injection,DI)就可以了.这个容器称为:IoC容器. Spring是⼀个IoC容器, 所以有时Spring 也称为Spring 容器.引入传统实现思路我们的实现思路是这样的:先设计轮⼦(Tire),然后根据轮⼦的⼤⼩设计底盘(Bottom),接着根据底盘设计⻋⾝(Framework),最后根据⻋⾝设计好整个汽⻋(Car)。这⾥就出现了⼀个"依赖"关系:汽⻋依赖⻋⾝,⻋⾝依赖底盘,底盘依赖轮⼦.  代码实现public class Main {    public static void main(String[] args) {        Car car = new Car(21);        car.run();         Car car2 = new Car(17);        car2.run();    }} //汽车class Car {    private Framework framework;     public Car(int size) {        framework = new Framework(size);        System.out.println("framework init...");    }     public void run() {        System.out.println("car run...");    }} //车身class Framework {    private Bottom bottom;     public Framework(int size) {        bottom =  new Bottom(size);        System.out.println("bottom init....");    }} //底盘class Bottom {    private Tire tire;     public Bottom(int size) {        tire = new Tire(size);        System.out.println("tire init...");    }} //轮胎class Tire {    private int size;     public Tire(int size) {        System.out.println("tire size:"+size);    }}AI运行代码java上面这样的设计看起来没问题,但是可维护性却很低,因为需求可能会越来越多,比如增加轮胎颜色,修改后的代码如下:我们可以看到,修改后的代码会报错,并且需要我们继续修改完整代码如下:public class Main {    public static void main(String[] args) {        Car car = new Car(21,"aaa");        car.run();         Car car2 = new Car(17,"bbb");        car2.run();    }} //汽车class Car {    private Framework framework;     public Car(int size,String color) {        framework = new Framework(size,color);        System.out.println("framework init...");    }     public void run() {        System.out.println("car run...");    }} //车身class Framework {    private Bottom bottom;     public Framework(int size,String color) {        bottom =  new Bottom(size,color);        System.out.println("bottom init....");    }} //底盘class Bottom {    private Tire tire;     public Bottom(int size,String color) {        tire = new Tire(size,color);        System.out.println("tire init...");    }} //轮胎class Tire {    private int size;    private String color;     public Tire(int size,String color) {        System.out.println("tire size:"+size);    }}AI运行代码java从以上代码可以看出,以上程序的问题是:当最底层代码改动之后,整个调⽤链上的所有代码都需要修改.程序的耦合度⾮常⾼(修改⼀处代码, 影响其他处的代码修改)。解决方案我们尝试改变实现方式:轮⼦依赖底盘, 底盘依赖⻋⾝,⻋⾝依赖汽⻋。 基于以上思路,我们把调⽤汽⻋的程序⽰例改造⼀下,把创建⼦类的⽅式,改为注⼊传递的⽅式。完整代码如下:class Main {    public static void main(String[] args) {        Tire tire = new Tire(17, "red");        Bottom bottom = new Bottom(tire);        Framework framework = new Framework(bottom);        Car car = new Car(framework);        car.run();    }} //汽车class Car {    private Framework framework;     public Car(Framework framework) {        this.framework = framework;        System.out.println("framework init...");    }     public void run() {        System.out.println("car run...");    }} //车身class Framework {    private Bottom bottom;     public Framework(Bottom bottom) {        this.bottom = bottom;        System.out.println("bottom init....");    }} //底盘class Bottom {    private Tire tire;     public Bottom(Tire tire) {        this.tire=tire;        System.out.println("tire init...");    }} //轮胎class Tire {    private int size;    private String color;     public Tire(int size, String color) {        System.out.println("tire size:"+size+",color:"+color);    }}AI运行代码java代码经过以上调整,⽆论底层类如何变化,整个调⽤链是不⽤做任何改变的,这样就完成了代码之间的解耦,从⽽实现了更加灵活、通⽤的程序设计了。IoC的优势在传统的代码中对象创建顺序是:Car -> Framework -> Bottom -> Tire改进之后解耦的代码的对象创建顺序是:Tire -> Bottom -> Framework -> Car我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了Framework,Framework 创建并创建了 Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制了.这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想。到这⾥, 我们⼤概就知道了什么是控制反转了, 那什么是控制反转容器呢, 也就是IoC容器。这部分代码, 就是IoC容器做的⼯作.从上⾯也可以看出来, IoC容器具备以下优点:资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度。1. 资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了2. 我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度.Spring 就是⼀种IoC容器, 帮助我们来做了这些资源管理. DIDI: Dependency Injection(依赖注⼊)容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。程序运⾏时需要某个资源,此时容器就为其提供这个资源.从这点来看, 依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。前面代码中, 是通过构造函数的⽅式, 把依赖对象注⼊到需要使⽤的对象中的。 IoC 是⼀种思想,也是"⽬标", ⽽思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI 就属于具体的实现。所以也可以说, DI 是IoC的⼀种实现.————————————————原文链接:https://blog.csdn.net/wmh_1234567/article/details/141640761
  • [技术干货] C++-类型转换
    1. C语言中的类型转换        在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与 接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型 转换和显式类型转换。1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败2. 显式类型转化:需要用户自己处理void Test (){     int i = 1;     // 隐式类型转换     double d = i;     printf("%d, %.2f\n" , i, d);     int* p = &i;     // 显示的强制类型转换     int address = (int) p;     printf("%x, %d\n" , p, address);}AI运行代码cpp缺陷: 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换2. 为什么C++需要四种类型转换C风格的转换格式很简单,但是有不少缺点的:1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失2. 显式类型转换将所有情况混合在一起,代码不够清晰 因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。3. C++强制类型转换标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:static_castreinterpret_castconst_castdynamic_cast3.1 static_cast        static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用 static_cast,但它不能用于两个不相关的类型进行转换特点:在编译时进行类型检查不能移除const属性用于基本数据类型之间的转换用于有继承关系的类指针或引用之间的转换int i = 10;double d = static_cast<double>(i); // 基本类型转换 class Base {};class Derived : public Base {};Base* base = new Derived;Derived* derived = static_cast<Derived*>(base); // 向下转型AI运行代码cpp3.2 reinterpret_cast        reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换 为另一种不同的类型特点:不进行任何类型检查可以将指针转换为整数,反之亦然用于函数指针之间的转换应尽量避免使用,除非明确知道自己在做什么int main(){ double d = 12.34; int a = static_cast<int>(d); cout << a << endl; // 这里使用static_cast会报错,应该使用reinterpret_cast //int *p = static_cast<int*>(a); int *p = reinterpret_cast<int*>(a); return 0;}AI运行代码cpp3.3 const_castconst_cast最常用的用途就是删除变量的const属性,方便赋值特点:只能改变const或volatile属性不能改变基本类型常用于调用历史遗留的未使用const正确性的APIconst int a = 10;int* b = const_cast<int*>(&a); // 移除const属性*b = 20; // 未定义行为,实际不应修改原const变量的值 void print(char* str); // 历史遗留函数const char* c = "hello";print(const_cast<char*>(c)); // 合法使用AI运行代码cpp3.4 dynamic_castdynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)注意:1. dynamic_cast只能用于父类含有虚函数的类2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0特点:运行时进行类型检查需要类有虚函数(多态类型)转换失败时返回nullptr(指针)或抛出异常(引用)相比static_cast更安全但性能开销更大class Base { virtual void dummy() {} };class Derived : public Base {}; Base* base = new Derived;Derived* derived = dynamic_cast<Derived*>(base); // 安全向下转型if (derived) {    // 转换成功}AI运行代码cpp注意:        强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是 否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用 域,以减少发生错误的机会。强烈建议:避免使用强制类型转换3.5 总结转换类型    主要应用场景static_cast    基本类型转换、非多态类型转换、向上转型dynamic_cast    多态类型的安全向下转型const_cast    移除或添加const/volatile属性reinterpret_cast    低级别的不安全转换,如指针与整数互转4. RTTIRTTI:Run-time Type identification的简称,即:运行时类型识别。C++通过以下方式来支持RTTI:1. typeid运算符2. dynamic_cast运算符3. decltype注意事项:需要编译器支持RTTI可能带来一定的性能开销过度使用可能表明设计存在问题5. 常见面试题1. C++中的4中类型转化分别是:_________、_________、_________、_________答案:static_cast、reinterpret_cast、const_cast、dynamic_cast2. 说说4中类型转化的应用场景。static_cast:当需要基本数据类型转换,或者类层次结构中的向上转型(子类指针/引用转为父类指针/引用)时使用。dynamic_cast:当需要在多态类型(含有虚函数的类)中进行安全的向下转型(父类指针/引用转为子类指针/引用)时使用。const_cast:当需要移除或添加const或volatile属性时使用,常用于与历史遗留代码交互。reinterpret_cast:当需要进行低级别的、不安全的类型转换时使用,如指针与整数之间的转换、函数指针之间的转换等。6. 总结        C++的四种类型转换操作符提供了比C风格转换更安全、更明确的类型转换方式。理解每种转换的适用场景和限制条件,能够帮助开发者编写更安全、更易维护的代码。在实际开发中,应根据具体需求选择合适的类型转换方式,并遵循最小权限原则,尽可能减少不必要的类型转换。————————————————原文链接:https://blog.csdn.net/h_number_1/article/details/150288042
  • [技术干货] 新手小白学习java从0到1(超详细),java系统学习
    本文以及后续的系列,将从0到1分享java学习心得,不管你是从未接触过计算机的小白,还是在校为java学习感到头疼的大学生。亦或者是行业中的大佬,都可以看一下这篇文章,学习都是如此,每次回过头来看,都有不一样的体验和收获。一、Java介绍在学习之前,首先介绍一下什么是java,java和python ,c++ ,C语言一样,是最火热的几个主流编程语言之一。由于java的发展历史内容过于多,这里就不一一介绍。当然了,java之父还是要介绍一下的,请看下图上面这个秃头的外国老爷爷就是大家公认的java之父——詹姆斯・高斯林(James Gosling),他在 1995 年主导开发了 Java 语言,是 Java 生态的核心奠基人。。二、Java环境配置熟悉电脑的或者说计算机专业的同学肯定都或多或少接触过环境配置。那么什么是环境配置,为什么要环境配置。环境配置就是运行java编程语言所需要的环境,配置这个环境,目的是你在写编解java代码的时候,要让你的计算机找到运行java所需要的环境。举个例子:我(用户)需要扫地(运行 Java 程序),扫把(JDK / 运行环境)是必需工具;如果不知道扫把在哪(电脑找不到 JDK 路径),就没法完成扫地(运行程序)。” 包括大家使用的其它的,比如说python, mysql等等。大多数都是要配置环境。原理都是一样的。在安装jdk之前,我们要先介绍一下什么是jdk,JDK(Java Development Kit,Java 开发工具包)是 Java 开发的核心工具集,简单说,它是 “程序员写 Java 代码、编译代码、运行代码的全套工具包”。JDK里面有什么,JDK 和 JRE、JVM 的关系?这里只是简单点的介绍,等你从小白蜕变后,你想往一个公司中的绝对的技术支柱。那会你就可以解锁新的学习方向。深入学习jvm等等。这里我就用一句话:JDK 包含 JRE,JRE 包含 JVM。JDK的安装首先,我们要打开甲骨文公司的官网,没使用过的兄弟要注册个账号。甲骨文历代版本jdk网址:Java Archive | Oracle这里我们可以根据不同的需求来选择不同的jdk版本。这里我选择的是javase21然后选择windos系列的,如果想学mac和linux怎么安装大家可以自行搜索。本文主要讲的是windows系统。下载.zip和exe都可以,安装一路点确认就可以,但是要记住自己的安装路径,后续要用到给大家看一下我这里放在的是d盘,这个因人而异自己选择。然后就是最关键的一步。打开我们计算机配置环境变量:此电脑 -  属性 - 高级系统设置在系统变量里面新建 这里路径大家自行更换,这里是我自己装的路径JAVA_HOME      值为:D:\JDK21\jdk-21.0.8新建完上面的后再新建一个  CLASSPATH  值为下面,注意不要漏掉不然容易失败.;%JAVA_HOME%\bin;%JAVA_HOME%\lib;%JAVA_HOME%\jre\lib然后我把具体的内容给截图下来了具体的步骤就是上面添加好这两个配置后你的电脑就可以找到你配置的java运行环境啦。如果不成功还需要在PATH变量中添加:%JAVA_HOME%\bin双击path,添加完之后上移到最上面。然后打开终端,输入java -version ,javac -version 。看到有输出,你就成功验证完你的java配置环境了。可以看到我的jdk虽然安装在d盘,但是我在c盘查看java版本,也是可以看得到的。三、第一个程序,Hello World!使用记事本和命令行编译对于理解底层过程非常有帮助,但对于真正的项目开发来说效率太低。为了让大家更专注于代码本身,我们直接使用目前最智能、最高效的Java集成开发环境(IDE)—— IntelliJ IDEA。”建议大家使用idea的时候使用全英文版本,我这里为了方便大家直接理解,临时调整成了中文。首先左上角新建项目,命名大家可以自己随意取创建好后右上角设置,设置完之后选择自己使用的sdk版本。新建一个HelloWorld类然后手敲下面的代码后,点击右上角绿色箭头,编译后运行,查看终端控制台输出的内容! public class HelloWorld {    public static void main(String[] args) {        System.out.println("Hello World");    }}至此,恭喜你已经完成了你的第一个程序!下一篇,将从代码上逐个“刨析”上面代码每一个具体是什么意思,帮助大家更好的理解java。声明:本文以学习交流为主,无商用目的,请勿搬运,引用请标明出处。欢迎大家补充指正不足和错误的地方。相互学习,共同进步————————————————原文链接:https://blog.csdn.net/zdwxbc/article/details/151081925
  • [技术干货] 解锁JDK 17:从下载到安装的全攻略
    一、为什么选择 JDK 17在 Java 开发的世界里,JDK 的版本选择至关重要,而 JDK 17 无疑是一个极具吸引力的选择。它于 2021 年 9 月发布,作为长期支持(LTS)版本,为开发者带来了众多令人瞩目的优势。在性能方面,JDK 17 进行了深度优化。以垃圾回收为例,对 G1 垃圾回收器(Garbage - First Garbage Collector)的优化使得内存管理更加高效。通过引入新的 GC 算法,垃圾回收的停顿时间显著减少,这对于那些对响应速度要求极高的应用程序来说,无疑是一个巨大的福音。比如在高并发的电商系统中,大量的对象创建和销毁是常态,JDK 17 的垃圾回收优化能够让系统在高负载下保持稳定且快速的响应,提升用户购物的流畅体验 。在编译器优化上,JDK 17 也下足了功夫,更高效的字节码生成和内部库编译优化,使得代码编译速度更快,运行时性能更优,开发效率与程序执行效率都得到了极大提升。安全性是 JDK 17 的又一亮点。在如今网络攻击手段层出不穷的环境下,应用程序的安全性至关重要。JDK 17 引入了新的加密算法和安全协议,例如 TLSv1.3 的默认启用,极大地增强了 HTTPS 协议的安全性,有效防止数据在传输过程中被窃取或篡改。就像在线支付系统,使用 JDK 17 能够为用户的支付信息保驾护航,让用户放心交易。它还增强了内存管理和垃圾回收算法,减少了内存泄漏的风险,进一步提高了应用的安全性和稳定性。JDK 17 为开发者带来了更出色的开发体验。各大 IDE(如 IntelliJ IDEA、Eclipse)对 JDK 17 的支持更加完善,代码提示、智能补全等功能更加智能和高效,能够帮助开发者更快地编写代码,减少错误。构建工具(如 Maven 和 Gradle)的兼容性和性能也得到了改进,项目的构建过程更加顺畅,减少了因版本不兼容导致的各种问题。它还引入了许多新的 API 和功能,在并发、网络、文件系统等操作上都有改进,为开发者提供了更高效、更安全的编程方式。JDK 17 支持新的编程语言和框架,紧跟技术发展的潮流。比如对 JavaFX 和 Java EE 的更新,让开发者能够更方便地构建桌面应用程序、Web 应用程序和企业级应用程序,满足不断变化的市场需求 。它还引入了外部函数和内存 API(孵化器),这一创新功能允许 Java 程序与 Java 运行时之外的代码和数据进行互操作,为跨语言开发打开了新的大门,让开发者能够充分利用不同语言的优势,拓展 Java 的应用边界。二、下载前的准备在下载 JDK 17 之前,有几个关键的准备工作需要完成,以确保下载和后续安装过程顺利进行。首先要确认你的电脑操作系统版本。常见的操作系统有 Windows、Mac OS 和 Linux。不同的操作系统需要下载对应的 JDK 17 安装包。如果你使用的是 Windows 系统,在下载时就要选择 Windows 版本的 JDK 17;若是 Mac OS 系统,就选择 Mac 版本;Linux 系统同样如此,需要依据具体的 Linux 发行版(如 Ubuntu、CentOS 等)来选择合适的 JDK 17 安装包 。确定电脑系统是 32 位还是 64 位也至关重要。32 位系统和 64 位系统在内存寻址能力、软件兼容性等方面存在差异。一般来说,如今的电脑大多为 64 位系统,但仍有部分旧设备可能是 32 位。对于 JDK 17,从 Java 9 版本开始,就不再提供 32 位版本的安装包了,所以如果你的电脑是 32 位系统,可能就无法安装 JDK 17 。以 Windows 系统为例,判断系统是 32 位还是 64 位有多种方法。较为简单直观的是通过系统属性查看,右键单击 “此电脑”,选择 “属性”,在打开的窗口中,“系统类型” 处会显示你的电脑是 32 位操作系统还是 64 位操作系统。使用 “dxdiag” 命令也能查看,按下 “Win+R” 键,打开 “运行” 对话框,输入 “dxdiag” 并按下回车键,在弹出的 DirectX 诊断工具窗口中,查看 “操作系统” 一栏即可得知系统位数 。在 Mac OS 系统中,打开终端程序,输入 “uname -a” 命令并回车,如果输出 “X86_64”,则表示是 64 位系统,反之则可能是 32 位系统 。而在 Linux 系统下,在终端输入 “uname -m” 命令,若返回结果是 “x86_64”,表示系统是 64 位;若返回 “i686” 或 “i386”,则表示系统是 32 位 。除了确认操作系统版本和系统位数外,还需要保证你的电脑有足够的磁盘空间来安装 JDK 17。JDK 17 的安装包大小在几百 MB 左右,安装完成后占用的磁盘空间可能会达到 1GB 甚至更多,所以在下载前,要检查一下磁盘剩余空间是否充足。另外,由于下载过程需要联网,还需要确保网络连接稳定,避免因网络问题导致下载中断,浪费时间。三、JDK 17 下载指南3.1 官网下载Oracle 官网是下载 JDK 17 最权威、最安全的渠道,其下载地址为:https://www.oracle.com/java/technologies/javase-jdk17-downloads.html 。打开上述链接,进入 JDK 17 下载页面。在页面中,你会看到不同操作系统对应的下载选项。根据你的操作系统进行选择。如果你使用的是 Windows 系统,并且是 64 位的,就点击 “Windows x64 Installer”;若是 Mac OS 系统,根据系统版本选择对应的 “Mac OS X Installer” ;Linux 系统则依据具体情况选择 “Linux x64 Compressed Archive”(压缩归档文件,需要手动解压和配置)或 “Linux x64 Installer”(安装程序,安装过程相对简单)。点击下载选项后,会弹出 Oracle 账户登录或注册界面。如果你已有 Oracle 账户,直接登录;若没有,可以选择注册一个新账户。若不想注册登录,也可以在页面下方找到 “Accept License Agreement” 选项,勾选它,然后再次点击下载选项,即可开始下载。3.2 其他可靠下载源(如有)除了 Oracle 官网,Adoptium(Home | Adoptium )也是一个非常可靠的第三方下载渠道,它提供的 OpenJDK 构建与 Oracle JDK 在功能上基本相同,并且完全免费和开源。Adoptium 的下载速度在一些地区可能会比 Oracle 官网更快,这得益于它分布在全球各地的镜像站点,能根据用户的地理位置自动选择最优的下载镜像,大大节省下载时间。它还提供了多种不同的 Java 虚拟机实现,如 Eclipse OpenJ9,开发者可以根据项目的性能需求和内存管理要求进行选择。华为开源镜像站(https://mirrors.huaweicloud.com/ )也提供 JDK 17 的下载。国内用户从华为开源镜像站下载 JDK 17,速度优势明显,能有效避免因网络问题导致的下载缓慢或中断。华为对镜像内容的完整性和安全性进行严格校验,确保下载的 JDK 17 安装包没有被篡改,让用户下载得安心 。在华为开源镜像站下载 JDK 17,操作也十分简单,在网站中搜索 “JDK 17”,就能找到对应的下载链接,按照提示即可完成下载。在选择第三方下载源时,一定要确保其可靠性和安全性,避免从一些不明来源的网站下载,以免下载到包含恶意软件或被篡改的安装包,给电脑和项目带来安全风险 。四、安装 JDK 17 进行时4.1 Windows 系统安装步骤找到下载好的 JDK 17 安装文件,通常是一个.exe 后缀的文件,文件名类似于 “jdk-17.0.XX_os - windows - x64_bin.exe” 。双击该安装文件,启动安装程序。安装程序启动后,会弹出安装向导界面,点击 “下一步” 继续。在 “目标文件夹” 界面,选择 JDK 的安装路径。这里强烈建议不要安装在 C 盘,因为 C 盘通常是系统盘,安装过多软件可能导致系统盘空间不足,影响系统性能。比如可以选择安装在 D 盘的 “Java\jdk17” 文件夹下(需提前创建好该文件夹)。选择好路径后,点击 “下一步”。接下来安装程序会开始复制文件并进行安装,这个过程可能需要一些时间,耐心等待即可。安装进度条会显示安装的进度。安装完成后,会出现安装完成的界面,点击 “关闭” 按钮,完成 JDK 17 在 Windows 系统上的安装。4.2 Mac 系统安装步骤下载完成 JDK 17 的.dmg 格式安装包后,双击该安装包,会弹出一个安装向导窗口。在安装向导窗口中,按照提示一步步操作,点击 “继续” 按钮。阅读软件许可协议,若同意协议内容,点击 “同意”。选择安装位置,一般默认即可,若有特殊需求,也可选择其他磁盘位置,然后点击 “安装”。安装过程中可能需要输入电脑的管理员密码进行授权。安装完成后,打开终端程序,输入 “java -version” 命令,如果显示 JDK 17 的版本信息,说明安装成功。如果想要配置环境变量,可编辑~/.bash_profile 或~/.zshrc 文件(根据你使用的终端类型),添加如下内容:export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk - 17.jdk/Contents/Homeexport PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar添加完成后,保存文件,并在终端输入 “source ~/.bash_profile” 或 “source ~/.zshrc” 使配置生效 。4.3 Linux 系统安装步骤通过包管理器安装(以 Ubuntu 为例)打开终端,输入以下命令更新系统软件包列表:sudo apt update输入以下命令安装 OpenJDK 17:sudo apt install openjdk - 17 - jdk安装完成后,输入 “java -version” 命令,若显示 JDK 17 的版本信息,则安装成功。手动解压安装包安装下载 JDK 17 的压缩包(.tar.gz 格式),并将其上传到 Linux 服务器的指定目录,比如 “/opt” 目录。在终端中切换到存放压缩包的目录,使用以下命令解压压缩包:sudo tar -zxvf jdk - 17_linux - x64_bin.tar.gz解压完成后,为了方便管理,可将解压后的文件夹重命名,比如重命名为 “jdk17”:sudo mv jdk - 17* jdk17配置环境变量,编辑 /etc/profile 文件,在文件末尾添加以下内容:export JAVA_HOME=/opt/jdk17export PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar保存文件后,在终端输入 “source /etc/profile” 使环境变量生效。最后,输入 “java -version” 命令验证安装是否成功 。五、配置环境变量,让 JDK 17 畅行无阻安装好 JDK 17 后,配置环境变量是让 Java 程序能够顺利运行的关键一步。环境变量就像是 Java 程序的 “导航地图”,它能让系统找到 Java 的各种工具和库,确保 Java 程序在运行时能够顺利调用所需的资源。5.1 Windows 系统环境变量配置回到 “此电脑”,右键选择 “属性”,在弹出的窗口中点击 “高级系统设置”。在 “系统属性” 窗口中,点击下方的 “环境变量” 按钮 。在 “环境变量” 窗口的 “系统变量” 区域,点击 “新建” 按钮。在弹出的 “新建系统变量” 窗口中,“变量名” 处输入 “JAVA_HOME”,“变量值” 处填写你之前安装 JDK 17 的路径,比如 “D:\Java\jdk17” ,然后点击 “确定”。在 “系统变量” 中找到 “Path” 变量,点击 “编辑”。在弹出的 “编辑环境变量” 窗口中,点击 “新建”,然后输入 “% JAVA_HOME%\bin” ,点击 “确定”。这一步是将 JDK 17 的 bin 目录添加到系统的 Path 变量中,这样系统就能找到 Java 的各种可执行命令,如 javac(Java 编译器)、java(Java 运行命令)等 。连续点击 “确定” 关闭所有窗口,完成环境变量的配置。5.2 Mac 和 Linux 系统环境变量配置(如有)Mac 系统环境变量配置:Mac 系统常用的终端配置文件有.bash_profile 和.zshrc,具体取决于你使用的终端。如果你使用的是默认的 bash 终端,通常编辑.bash_profile 文件;若使用的是 zsh 终端,则编辑.zshrc 文件 。打开终端,输入以下命令编辑配置文件(以.zshrc 为例):open -e ~/.zshrc在打开的文件中添加以下内容:export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk - 17.jdk/Contents/Homeexport PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar保存文件后,在终端输入以下命令使配置生效:source ~/.zshrcLinux 系统环境变量配置:以 Ubuntu 系统为例,在终端中输入以下命令编辑 /etc/profile 文件:sudo nano /etc/profile在文件末尾添加以下内容:export JAVA_HOME=/usr/local/jdk17 # 根据实际安装路径修改export PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar保存文件后,在终端输入以下命令使环境变量生效:source /etc/profile完成环境变量配置后,打开终端,输入 “java -version” 命令,如果显示 JDK 17 的版本信息,说明环境变量配置成功,你已经可以在系统中使用 JDK 17 来开发和运行 Java 程序了 。六、验证安装成果当你完成 JDK 17 的安装和环境变量配置后,就可以进行安装成果的验证了。这一步非常关键,它能确保你之前的努力没有白费,JDK 17 已经可以在你的系统中正常使用。在 Windows 系统中,你可以按下快捷键 win + R,调出 “运行” 对话框,在对话框中输入 “cmd”,然后回车,这样就打开了命令行窗口。在 Mac 系统和 Linux 系统中,直接打开终端应用程序即可。在打开的命令行窗口或终端中,输入 “java -version” 命令,然后回车。如果安装和配置都正确无误,你将会看到类似下面这样的输出:java version "17" 2021-09-14 LTSJava(TM) SE Runtime Environment (build 17+35-LTS-2724)Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing)这就表明你的 JDK 17 已经成功安装,系统能够正确识别并显示 JDK 17 的版本信息。 除了 “java -version” 命令外,你还可以进一步输入 “java” 和 “javac” 命令来查看相关的命令提示。输入 “java” 命令后,会显示 Java 命令的使用方法和各种参数选项,这表明 Java 运行时环境已经正确配置。输入 “javac” 命令,如果显示 Java 编译器的相关信息和使用说明,说明 Java 编译器也已经可以正常使用 。这些进一步的验证步骤可以让你更加确定 JDK 17 的安装和配置是完全正确的,为你后续的 Java 开发工作打下坚实的基础。七、常见问题及解决办法在下载和安装 JDK 17 的过程中,可能会遇到一些问题,下面为大家整理了常见问题及对应的解决办法。7.1 下载失败下载失败可能由多种原因导致。网络问题是常见原因之一,如果网络不稳定或者网速过慢,就容易出现下载中断的情况。可以尝试切换到其他网络,比如从移动数据切换到 WiFi,或者从 WiFi 切换到手机热点,看看下载是否能顺利进行。如果是公司网络或者公共网络,可能存在网络限制,这时可以联系网络管理员,咨询是否可以解除限制,以保证正常下载 。浏览器兼容性问题也可能导致下载失败。不同浏览器对网页的解析和下载功能支持有所差异,有些浏览器可能无法正确下载 JDK 17 安装包。如果遇到这种情况,可以尝试更换浏览器,比如从 Chrome 浏览器切换到 Firefox 浏览器,或者使用 Edge 浏览器进行下载 。还有一种可能是下载链接失效,比如官方网站更新了下载链接,而你使用的还是旧链接。这时候需要前往 JDK 17 的官方下载页面,确认最新的下载链接,再重新进行下载 。7.2 安装报错安装过程中出现报错也是大家经常会遇到的问题。权限不足是导致安装报错的常见原因之一。当你没有足够的权限来修改系统文件和目录时,安装程序就无法正常写入文件,从而报错。以 Windows 系统为例,如果你不是以管理员身份运行安装程序,就可能出现权限不足的问题。解决办法是右键点击安装程序,选择 “以管理员身份运行”,这样就能获取足够的权限来完成安装 。依赖缺失也可能导致安装报错。JDK 17 的安装可能依赖一些其他的库或者软件,如果这些依赖没有安装,安装过程就会出错。比如在 Linux 系统中安装 JDK 17 时,可能需要先安装一些依赖包,如 libc6-dev、libx11-dev 等。你可以通过系统的包管理器来安装这些依赖,在 Ubuntu 系统中,使用 “sudo apt-get install libc6-dev libx11-dev” 命令即可安装 。如果安装包本身损坏,也会导致安装报错。这时候需要重新下载安装包,可以从官方网站或者可靠的第三方下载源重新获取安装包,然后再进行安装 。7.3 环境变量配置无效配置环境变量后命令无法识别,这也是一个让很多人头疼的问题。配置错误是导致环境变量无效的常见原因,比如变量名拼写错误、变量值路径填写错误等。要仔细检查配置的变量名和变量值是否正确,确保 “JAVA_HOME” 变量的值是 JDK 17 的安装路径,“Path” 变量中添加的 “% JAVA_HOME%\bin” 路径没有拼写错误 。没有重启命令行窗口也可能导致环境变量配置无效。在修改环境变量后,命令行窗口可能不会立即读取新的环境变量配置,需要重新打开命令行窗口才能生效。如果重新打开命令行窗口后还是不行,可以尝试重启电脑,让系统重新加载环境变量 。在 Windows 系统中,之前安装的 JDK 可能会残留一些配置,影响新 JDK 17 环境变量的生效。比如之前安装的 JDK 可能在系统的 “Path” 变量中添加了旧的 JDK 路径,导致系统优先读取旧路径。这时需要检查 “Path” 变量,删除与旧 JDK 相关的路径,确保系统能够正确读取 JDK 17 的环境变量 。八、总结回顾至此,我们已经完成了 JDK 17 从下载、安装到环境变量配置以及验证安装成果的全部过程。在下载时,务必选择可靠的下载源,官网是首选,若官网下载不便,也可考虑 Adoptium、华为开源镜像站等可靠第三方渠道 。安装过程中,要注意根据不同操作系统选择正确的安装步骤,尤其在 Windows 系统中,尽量避免将 JDK 安装在系统盘;Mac 系统安装完成后,记得配置环境变量;Linux 系统则可根据自身情况选择包管理器安装或手动解压安装 。配置环境变量是关键环节,任何一个小的错误都可能导致 JDK 无法正常使用,所以一定要仔细检查变量名和变量值是否正确 。现在,你已经成功安装并配置好 JDK 17,可以开启 Java 开发的新征程啦!在使用 JDK 17 进行开发时,你会发现它的性能提升、安全性增强以及丰富的新特性都能为你的项目带来极大的便利 。如果你在安装或使用过程中遇到任何问题,欢迎在评论区留言交流,大家一起探讨解决办法,共同进步————————————————原文链接:https://blog.csdn.net/xiaoyingxixi1989/article/details/148750799
  • [技术干货] JavaScript--js基础(详细 全面)
    前言:JavaScript是一种广泛应用于网页开发的脚本语言,它能够使网页变得更加动态和交互性。作为一种客户端脚本语言,JavaScript可以被嵌入到HTML中,并且可以被所有现代的网页浏览器支持。 JavaScript的强大之处在于它能够让开发者们通过编写一些简单的代码,就能够实现许多复杂的功能。无论是实现页面元素的动态变化,响应用户的交互操作,还是处理表单提交数据,JavaScript都能够胜任。 本文旨在介绍和学习JavaScript的基础知识。通过本文的阅读,读者将能够了解JavaScript在网页开发中的重要性和作用,掌握其基础语法和概念。不论是初学者还是有一定经验的开发者,都可以通过本文来加深对JavaScript的理解,并提升自己的开发能力。希望读者能够从中获得有益的知识,为日后的学习和实践打下坚实基础。愿读者在学习JavaScript的过程中不断成长,探索更多可能性 作者建议:学习知识在于深度理解,多动手、多动脑,总能更快地领悟。不要仅仅停留在阅读代码的层面,亲自动手敲打一遍,会带来更丰富的收获。通过实践,我们能够更深入地理解知识,掌握技能,并且在解决问题时更加得心应手。相信自己的能力,坚持不懈地实践,你将会取得更大的进步和成就。让学习成为一种习惯,让动手实践成为你提升的捷径,加油!你是最棒的! JavaScript 是什么?JavaScript 简介JavaScript(简称Js)是当前最流行,应用最广泛的客户端(网页)脚本语言,用来在网页中添加动态效果与交互功能,在web开发中拥有举足轻重的地位. JavaScript,HTML,CSS共同构成网页 HTML:用来定义网页内容,例如:标题,正文,图像等(HTML是网页制作的基础语言---->跳转学习:HTML);CSS:用来修饰网页组件得外观,例如:颜色,大小,位置,背景等等(CSS是网页制作的修饰语言--->跳转学习:CSS)JavaScript:用来实时更新网页内容,例如:从服务器中获取数据并更新到网页中,修改某些标签的样式内容,可以让网页变得生动.1.JavaScript历史JavaScript原名:LiveScript 是由美国网景(Netscape Communications Corporation)开发的一种用于对网页操作的脚本语言,LiveScript也是面向对象的.后来sun公司与网景合作更名为JavaScript. 脚本语言:无需编译,可以由某种解释器直接执行 (sql python html css JavaScript) 直接由某种解释器(引擎)解释执行,逐行从上到下解释执行. JavaScript和java完全是二种不同的语言 区别 JavaScript是嵌入在网页中,对网页进行各种操作的,是一种脚本语言java是一种后端的高级语言,是需要编译的2.JavaScript 具有以下特点1) 解释型脚本语言JavaScript 是一种解释型脚本语言,与 C、C++ 等语言需要先编译再运行不同,使用 JavaScript 编写的代码不需要编译,可以直接运行。2) 面向对象JavaScript 是一种面向对象语言,使用 JavaScript 不仅可以创建对象,也能操作使用已有的对象。3) 弱类型JavaScript 是一种弱类型的编程语言,对使用的数据类型没有严格的要求,例如您可以将一个变量初始化为任意类型,也可以随时改变这个变量的类型。4) 动态性JavaScript 是一种采用事件驱动的脚本语言,它不需要借助 Web 服务器就可以对用户的输入做出响应,例如我们在访问一个网页时,通过鼠标在网页中进行点击或滚动窗口时,通过 JavaScript 可以直接对这些事件做出响应。5) 跨平台JavaScript 不依赖操作系统,在浏览器中就可以运行。因此一个 JavaScript 脚本在编写完成后可以在任意系统上运行,只需要系统上的浏览器支持 JavaScript 即可。 第一个JavaScript程序JavaScript程序不能独立运行,只能在宿主环境中执行.一般情况下可以吧JavaScript代码放在网页中,借助浏览器环境来运行. 在HTML文档嵌入JavaScript代码 在HTML页面中嵌入JavaScript脚本需要使用<script>标签,我们可以在<script>标签中编写JavaScript代码,具体步骤如下: 新建HTML文档(一般都直接使用编译工具直接生成HTML文档--->推荐使用:HBuilderX文档)在<head>标签或者<body>标签中插入<script>标签在<script>标签中写JavaScript代码示例: <!DOCTYPE html><html><head><meta charset="utf-8"><title></title><script>// JavaScript代码写的地方alert("你好 JavaScript")</script></head><body></body></html>运行本项目html 注:alert(‘welcome!’); 提示对话框 效果图:   1.在脚本文件中编写JavaScript代码JavaScript代码不仅可以放在HTML文档中也可以放在JavaScript脚本文件中.JavaScript脚本文件,扩展名是.js,使用任何文本编辑器都可以编辑(本博客以HBuilderX文档为示例) 具体步骤如下: 1.新建.js文本.   2.打开文本编写JavaScript文本 alert("你好"); 3.保存JavaScript文件,并连接HTML文档 <script type="text/javascript" src="test.js"></script>运行本项目html4.运行HTML文档   注:定义 src 属性的<script><script> 标签不应再包含 JavaScript 代码。如果嵌入了代码,则只会下载并执行外部 JavaScript 文件,嵌入代码将被忽略。 2.JavaScript代码执行顺序JavaScript代码可以写在HTML文档<head>标签与<body>标签中,写在不同标签中的执行顺序不同 示例 <!DOCTYPE html><html><head><meta charset="utf-8"><title></title><script>alert("写在<head>标签中");</script></head><body><script>alert("写在<body>标签中");</script></body></html>运行本项目html 效果图   由效果图我们就可以看出,在HTML文档中先执行在<head>标签中的JavaScript代码后执行<body>标签中的代码   基本语法1.变量声明变量用var关键字      var  name; 声明变量的同时对其赋值     var  name = "小明"; 2.数据类型数值型(number):其中包括整型数和浮点型数.布尔型(boolean):逻辑值,true或false字符串型:有单个或者多个字符组成的.字符串使用单引号或者双引号来说明的.undefined类型:变量没用赋值的情况下默认类型,值为undefined.Object类型3.算术运算符算数运算符用来执行常见的数学运算,例如加法、减法、乘法、除法等,下表中列举了 JavaScript 中支持的算术运算符:   var x = 10;var y = 4;console.log("x + y =", x + y);  // 输出:x + y = 14console.log("x - y =", x - y);  // 输出:x - y = 6console.log("x * y =", x * y);  // 输出:x * y = 40console.log("x / y =", x / y);  // 输出:x / y = 2.5console.log("x % y =", x % y);  // 输出:x % y = 2 运行本项目html注:在JavaScript中的不同 var a = "10";var b = 5;var c = 10;var d = "a"; // + 字符串连接   加法运算 // alert(c-b);  减法// alert(a-b); // 5  "字符串(数字)" - 数值  = 数值  会把表达式中的数字 字符串尝试类型转换// alert(d-c);  //  NaN  字符串不能转换为数值返回NaN // alert(a==c); ture  只比较值是否相等// alert(a===c); false 全等 比较值和类型运行本项目html  4.赋值运算赋值运算符用来为变量赋值,下表中列举了 JavaScript 中支持的赋值运算符:   代码示例: var x = 10;x += 20;console.log(x);  // 输出:30var x = 12,    y = 7;x -= y;console.log(x);  // 输出:5x = 5;x *= 25;console.log(x);  // 输出:125x = 50;x /= 10;console.log(x);  // 输出:5x = 100;x %= 15;console.log(x);  // 输出:10运行本项目html 5.字符串运算符JavaScript 中的 + 和 += 运算符除了可以进行数学运算外,还可以用来拼接字符串,其中: +运算符表示将运算符左右两侧的字符串拼接到一起;+=运算符表示将字符串进行拼接,并重写赋值var x = "Hello ";var y = "World!";var z = x + y;console.log(z);  // 输出:Hello World!x += y;console.log(x);  // 输出:Hello World!运行本项目html6.自增,自减运算符自增、自减运算符用来对变量的值进行自增(+1)、自减(-1)操作   代码示例: var x;x = 10;console.log(++x);  // 输出:11console.log(x);    // 输出:11x = 10;console.log(x++);  // 输出:10console.log(x);    // 输出:11x = 10;console.log(--x);  // 输出:9console.log(x);    // 输出:9x = 10;console.log(x--);  // 输出:10console.log(x);    // 输出:9 运行本项目html 7.比较运算符比较运算符会比较左右两侧的数据,最后返回一个布尔值(true或者false)    代码示例 var x = 25;var y = 35;var z = "25";console.log(x == z);  // 输出: trueconsole.log(x === z); // 输出: falseconsole.log(x != y);  // 输出: trueconsole.log(x !== z); // 输出: trueconsole.log(x < y);   // 输出: trueconsole.log(x > y);   // 输出: falseconsole.log(x <= y);  // 输出: trueconsole.log(x >= y);  // 输出: false 运行本项目html 8.逻辑运算符逻辑运算符通常来组合多个表达式,逻辑运算符的运算是一个布尔值(true/false)   代码示例: var year = 2021;// 闰年可以被 400 整除,也可以被 4 整除,但不能被 100 整除if((year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0))){    console.log(year + " 年是闰年。");} else{    console.log(year + " 年是平年。");}运行本项目 9.条件运算符JavaScript 还包含了基于某些条件对变量进行赋值的条件运算符 语法:       var result = (条件表达式)?结果1:结果2 当条件成立返回?后的内容,否则返回:后的内容 代码示例: var c = 1;var b = 2;var a = c > b ? c : b; // a = b;运行本项目10.控制语句选择结构1.单一选择结构(if) 2.二路选择结构(if/else) 3.多路选择结构(switch) 程序控制结构是循环结构1.由计数器控制的循环(for) 2.在循环的开头测试表达式(while) 3.在循环的末尾测试表达式(do/while) 4.break continue JavaScript中的选择结构和其他语言是相通的也是一样的. 如果你是第一次学习编程语言:学习推荐路径 函数1.定义函数函数定义的基本语法: function functionName([arguments]){ javascript statements; [return expression] } function: 表示函数定义的关键字; functionName:表示函数名; arguments:表示传递给函数的参数列表,各个参数之间用逗号隔开,可以为空; statements: 表示实现函数功能的函数体; return expression:表示函数将返回expression的值,同样是可选的的语句。 代码示例: sayHello---->函数名 name--->参数 alert("Hello " + name);--->函数体 return 0;---->返回值 function sayHello(name){    alert("Hello " + name);    return 0;}运行本项目html2.函数调用函数调用: 1.在<script>标签中直接调用 2.在其他函数中调用 3.通过组件 代码演示: <!DOCTYPE html><html><head><meta charset="utf-8"><title></title><script>function test(text){alert(text);}test("函数在<script>标签中调用");function a(){test("函数在其他函数中调用");}a();</script></head><body><input type="button" value="测试" onclick="test('组件调用函数')"/></body></html>运行本项目html 效果演示:   3.全局函数1.parseInt(arg) 把括号内的内容转换成整数之后的值。如果括号内是字符串, 则字符串开头的数字分被转换成整数,如果以字母开头,则返回 “NaN” 2.parseFloat(arg) 把括号内的字符串转换成浮点数之后的值,字符串开头的数字部分被转换成浮点数,如果以字母开头,则返回“NaN” 3.typeof (arg)返回arg值的数据类型 4.eval(arg) 可运算某个字符串 5.alert(String) 可以在浏览器中弹出一个提示框,里面的内容是String 6.console.log(String)在控制面板上打印String 事件JS 事件(event)是当用户与网页进行交互时发生的事情,例如单击某个链接或按钮,在文本框中输入文本、按下键盘上的某个按键、移动鼠标等等。当事件发生时,您可以使用 JavaScript 中的事件处理程序(也可称为事件监听器)来检测并执行某些特定的程序 常用事件: onclick()鼠标点击时 onblur()标签失去焦点 onfocus()标签获得焦点 onmouseover()鼠标被移到某标签之上 onmouseout鼠标从某标签移开 onload()是在网页加载完毕后触发相应的的事件处理程序 onchange()是指当前标签失去焦点并且标签的内容发生改变时触发事件处理程序   代码: <!DOCTYPE html><html><head><meta charset="utf-8"><title></title></head><body><div id="div" style=""><!-- 点击事件 -->onclick:<input type="button" value="onclick" onclick="test('pink')" /><br /><!-- onblur()标签失去焦点 -->onblur:<input type="text" onblur="test('red')" /><br /><!--  onfocus()标签获得焦点-->onfocus:<input type="text" onfocus="test('blue')"/><br /><!-- onmouseover()鼠标被移到某标签之上 -->onmouseover:<input type="text" onmouseover = "test('black')"/><br /><!--  onmouseout鼠标从某标签移开-->onmouseout:<input type="text" onmouseout = "test('aqua')"/><br /><!-- onchange()是指当前标签失去焦点并且标签的内容发生改变时触发事件处理程序 -->onchange:<input type="text" onchange = "test('yellow')"/></div> <script>var obj = document.getElementById("div");function test(color) {obj.style.backgroundColor = color;}</script></body></html>运行本项目html 效果显示:  内置对象1.String字符串属性 length 用法:返回该字符串的长度. 方法 charAt(n):返回该字符串位于第n位的单个字符.indexOf(char):返回指定char首次出现的位置.lastIndexOf(char) :跟 indexOf() 相似,不过是从后边开始找.substring(start,end) :返回原字符串的子字符串,该字符串是原字符串从start位 置到end位置的前一位置的一段.substr(start,length) :返回原字符串的子字符串,该字符串是原字符串从start位 置开始,长度为length的一段.split(分隔符字符) :返回一个数组,该数组是从字符串对象中分离开来的,决定了分离的地方,它本身不会包含在所返回的数组中。var string = new String("String字符串r2323");console.log(string.charAt(2));  // 打印结果是:econsole.log(string.indexOf('r'));//打印结果是:3console.log(string.lastIndexOf('r')); //打印结果是:9console.log(string.substring(2,5)); // 打印结果是:rinconsole.log(string.substr(2,3));//打印结果是:rinconsole.log(string.split('r'));//打印结果是:一个数组:'st' 'ing字符串' '2323'运行本项目html2.Array数组数组是值的有序集合,数组中的每个值称为一个元素,每个元素在数组中都有一个数字位置,称为索引,索引从 0 开始,依次递增。在 JavaScript 中,您可以使用 Array 对象定义数组,此外,Array 对象中还提供了各种有关数组的属性和方法。 创建 Array 对象的语法格式如下:  var = new Array(); 这样就定义了一个空数组。以后要添加数组元素,就用:           [下标] = 值; 如果想在定义数组的时候直接初始化数据,请用: var = new Array(, , ...); 还可以 var = [, , ...];     var a = new Array();    a[0] = 1;    a[1] = "aaa";    a[5] = true;        var b = new Array(1,2,3,4,5);        var a = [1,2,3,4,5];    a[5] = 3;运行本项目html属性 length :数组的长度,即数组里有多少个元素. 方法 join() :返回一个字符串,该字符串把数组中的各个元素串起来,用置于元素与元素之间。 reverse() 使数组中的元素顺序反过来。如果对数组[1, 2, 3]使用这个方法,它将使数组变成:[3, 2, 1]. sort() :使数组中的元素按照一定的顺序排列.如果不指定,则按字母顺序排列. 对数字排序需要调用排序函数。 function sortNumber(a,b){ return a - b; } 代码演示: var arr = [1, 11, 12, 55, 66, 8, 7, 9];console.log(arr.join()); //打印结果:1,5,8,10,2,6var str = "Hello World";var reversedStr = str.split("").reverse().join("");console.log(reversedStr); // Output: "dlroW olleH"console.log(arr.sort()); // Output: [1,11,12,55,66,7,8,9]  这里是根据字符计算的arr.sort(function(a, b) {return a - b;});console.log(arr); // Output: [1, 7, 8, 9, 11, 12, 55, 66]            //反序排列arr.sort(function(a, b) {return b - a;});console.log(arr); // Output: [66, 55, 12, 11, 9, 8, 7, 1]运行本项目html 3.Date获取日期 new Date() 返回当日的日期和时间 getFullYear() 返回四位数字年份 getDate() 返回一个月中的某一天 (1 ~ 31) getMonth() 返回月份 (0 ~ 11) getDay() 返回一周中的某一天 (0 ~ 6) getHours() 返回 Date 对象的小时 (0 ~ 23) getMinutes() 返回 Date 对象的分钟 (0 ~ 59) getSeconds() 返回 Date 对象的秒数 (0 ~ 59)) 代码演示:下代码是一个简单时间表的制作 <!DOCTYPE html><html><head>  <title></title>  <script>    function addTextToDiv() {      var myDiv = document.getElementById('myDiv');      var currentDate = new Date();      var year = currentDate.getFullYear();      var month = currentDate.getMonth() + 1;      var day = currentDate.getDate();      var hour = currentDate.getHours();      var minutes = currentDate.getMinutes();      var seconds = currentDate.getSeconds();      myDiv.innerHTML = year + "年" + month + "月" + day + "日 " + hour + ":" + minutes + ":" + seconds;    }setInterval("addTextToDiv()",1000);  </script></head><body onload="addTextToDiv()">  <div id="myDiv"></div></body></html>运行本项目html 4.Math Math 对象,提供对数据的数学计算。 属性 PI 返回π(3.1415926535...)。 方法 Math.abs(x) 绝对值计算; Math.pow(x,y) 数的幂; x的y次幂 Math.sqrt(x) 计算平方根; Math.ceil(x) 对一个数进行上舍入 Math.floor(x) 对一个数进行下舍入 Math.round(x)  把一个数四舍五入为最接近的整数 Math.random()  返回 0 ~ 1 之间的随机数 Math.max(x,y)  返回 x 和 y 中的最大值 Math.min(x,y)   返回 x 和 y 中的最小值 Html DOM● DOM是Document Object Model文档对象(网页中的标签)模型的缩写. ● 通过html dom,可用javaScript操作html文档的所有标签 1.查找元素如果我们想对标签进行操作,我们就得找到他们,那我们怎么找到他们呢? 有四种方法可以找到他们: 通过 id 找到 HTML 标签 document.getElementById(“id"); 通过标签名找到 HTML 标签 document.getElementsByTagName("p"); 通过类名找到 HTML 标签 document.getElementsByClassName("p"); 通过name找到 HTML 标签 document.getElementsByName(“name"); 2.改变HTMLHtml dom允许javaScript 改变html标签的内容.改变 HTML 标签的属性 document.getElementById(“username").value=“new value"; 代码演示: 演示代码就让通过js修改按钮的value值,让按钮的value值从测试变到按钮 <!DOCTYPE html><html><head><meta charset="utf-8"><title></title><script>function test(){document.getElementById("button").value = "按钮";}</script></head><body><input type="button" value="测试" id="button" onclick="test()"/></body></html>运行本项目html   代码演示:修改图片 document.getElementById("image").src=“new.jpg"; <!DOCTYPE html><html><head><meta charset="utf-8"><title></title><script>function test(){document.getElementById("img").src = "img/3.jpg";}</script></head><body><input type="button" value="测试" id="button" onclick="test()"/><img src="img/1.jpg" id="img"/></body></html>运行本项目html 效果演示:   修改 HTML 内容的最简单的方法时使用 innerHTML 属性 document.getElementById(“div”).innerHTML=new HTML 代码演示: <!DOCTYPE html><html><head><meta charset="utf-8"><title></title><script>function test(){document.getElementById("div").innerHTML = "加油胜利就在前方";}</script></head><body><input type="button" value="测试" id="button" onclick="test()"/><div style="font-size: 50px;" id="div">你好,登山者</div></body></html>运行本项目html 效果演示:   这里只是举例说明了这几个,我们还有更多的操作,自己动动手,去发现更多吧,加油登山者 3.改变 CSShtml dom允许 javaScript改变html标签的样式 这里演示上面的文案修改颜色 代码演示: <!DOCTYPE html><html><head><meta charset="utf-8"><title></title><script>function test(){document.getElementById("div").innerHTML = "加油胜利就在前方";} function color(){document.getElementById("div").style.color = "pink";}</script></head><body><input type="button" value="测试" id="button" onclick="test()"/><div style="font-size: 50px;" id="div">你好,登山者</div><input type="button" value="修改颜色" onclick="color()"/></body></html>运行本项目html 效果演示:   这里用一个实例:使一个div的背景颜色修改并且循环 效果:   代码: <!DOCTYPE html><html><head><meta charset="utf-8"><title></title><script>var color = ['red','green','blue'];var i = 0;function test(){var obj1 = document.getElementById("text");var obj2 = document.getElementById("button");obj1.style.backgroundColor = color[i];i++;if(i==color.length){i=0;}obj2.value = color[i];}</script></head><body><div style="width: 300px; height: 300px;" id="text"></div><input type="button" value="red" onclick="test()" id="button"/></body></html>运行本项目html  计时通过使用 JavaScript,我们有能力做到在一个设定的时间间隔之后来执 行代码,而不是在函数被调用后立即执行。我们称之为计时事件。 方法: setTimeout(“函数”,”时间”)未来的某时执行代码 <!DOCTYPE html><html><head>    <meta charset="utf-8">    <title>setTimeout 示例</title>    <script>        function showMessage() {            alert("这是一个 setTimeout 示例!");        }         // 调用 showMessage 函数,在 3 秒后显示消息        setTimeout("showMessage()", 3000);    </script></head><body>    <h1>setTimeout 示例</h1>    <p>页面加载后将在 3 秒后显示一条消息。</p></body></html>运行本项目html 效果: clearTimeout()取消setTimeout() 效果:   代码演示: <!DOCTYPE html><html><head>    <meta charset="utf-8">    <title>setTimeout 和 clearTimeout 示例</title>    <script>        var timeoutID;         function showMessage() {            alert("这是一个 setTimeout 示例!");        }         function setupTimeout() {            timeoutID = setTimeout(showMessage, 3000);        }         function cancelTimeout() {            clearTimeout(timeoutID);            alert("定时器已取消!");        }    </script></head><body>    <h1>setTimeout 和 clearTimeout 示例</h1>    <p>页面加载后将在 3 秒后显示一条消息。</p>    <button onclick="setupTimeout()">设置定时器</button>    <button onclick="cancelTimeout()">取消定时器</button></body></html>运行本项目html setInterval(“函数”,”时间”)每隔指定时间重复调用 clearInterval()取消setInterval() clearInterval()------是清除了一个计时器 效果: 代码:  <!DOCTYPE html><html><head><meta charset="utf-8"><title></title></head><body><input type="text" id="text"/><br /><br /><input type="button" value="开始" onclick="startTime()"/><input type="button" value="停止" onclick="stopTime()"/><input type="button" value="复位" onclick="fuwei()"/><script>var num = 0;var textobj = document.getElementById("text");textobj.value = 0;var index;function jishi(){num++;textobj.value = num;}function startTime(){index = setInterval("jishi()",1000);} function stopTime(){clearInterval(index);} function fuwei(){num = 0;textobj.value = 0;}</script></body></html>————————————————原文链接:https://blog.csdn.net/Dreamkidya/article/details/139717728
  • [技术干货] 一文读懂 Java 主流编译器:特性、场景与选择指南
    前言:如果你是 Java 开发者,或许曾有过这样的疑问:“为什么同样的代码,在不同环境下运行速度差异明显?”“明明用了最新的 Java 语法,换个工具却编译报错?” 这些问题的答案,往往藏在 “编译器” 这个关键环节里。作为连接 Java 源代码与可执行字节码的核心工具,编译器直接决定了代码的兼容性、运行效率和开发体验。今天,我们就来盘点 Java 生态中的主流编译器,帮你搞懂它们的特性、适用场景,从此选对工具少走弯路。目录一、Java 编译器的 “基石”:javac(Oracle JDK/OpenJDK 内置)核心特性:适用场景:小技巧:二、追求 “极致性能” 的编译器:GraalVM Native Image核心特性:适用场景:注意点:三、专注 “Android 开发” 的编译器:Jack & Jill(已淘汰)与 D8/R8D8 编译器:Android 的 “专属 javac”R8 工具:编译 + 混淆 “二合一”小提示:四、其他值得关注的 Java 编译器1. Eclipse ECJ(Eclipse Compiler for Java)2. AJC(AspectJ Compiler)五、如何选择适合自己的 Java 编译器?一、Java 编译器的 “基石”:javac(Oracle JDK/OpenJDK 内置)提到 Java 编译器,javac 绝对是绕不开的 “元老”。它是 Oracle JDK 和 OpenJDK 中默认集成的编译器,从 Java 诞生之初就伴随开发者,也是绝大多数 Java 项目的 “默认编译工具”—— 我们在命令行里敲下 javac HelloWorld.java 时,调用的就是它。核心特性:兼容性拉满:作为 Java 语言的 “官方标配”,javac 对 Java 语法标准的支持是 “标杆级” 的。从 Java 1.0 到最新的 Java 21,所有官方定义的语法特性(比如 Lambda 表达式、Record 类、虚拟线程),javac 都会第一时间稳定支持,几乎不会出现 “语法兼容问题”。轻量无依赖:不需要额外安装,只要装了 JDK/JRE,就能直接在命令行调用。无论是 Windows 的 cmd、Linux 的终端还是 macOS 的终端,输入命令就能编译代码,适合快速验证简单程序。稳定可靠:经过二十多年的迭代,javac 的稳定性已经过海量项目验证。企业级应用、开源框架(比如 Spring、MyBatis)在编译时,优先选择 javac,就是因为它 “不容易出幺蛾子”,能保证代码编译后的一致性。适用场景:绝大多数 Java 基础开发场景:比如学生作业、小型工具开发、企业后端接口开发;依赖官方语法特性的项目:比如使用 Java 21 虚拟线程、Java 17 Sealed 类的新项目;需要兼容多环境的项目:比如跨平台运行的桌面应用、分布式服务,javac 编译的字节码能在所有支持 JVM 的环境中运行。小技巧:如果想查看编译细节,可以加 -verbose 参数(比如 javac -verbose HelloWorld.java),能看到编译器加载类、生成字节码的全过程;如果想指定编译后的 Java 版本(比如用 JDK 17 编译出兼容 Java 8 的字节码),可以用 -source 和 -target 参数(javac -source 8 -target 8 HelloWorld.java)。二、追求 “极致性能” 的编译器:GraalVM Native Image如果你觉得 “Java 程序启动慢、内存占用高”,那一定要试试 GraalVM 的 Native Image—— 它不是传统意义上的 “字节码编译器”,而是能将 Java 代码直接编译成原生可执行文件(比如 Windows 的 .exe、Linux 的 ELF 文件),从根本上解决 Java 程序的 “启动痛点”。核心特性:启动速度极快:传统 Java 程序启动时,需要先启动 JVM,再加载类、初始化环境,而 Native Image 编译的原生程序,直接跳过 JVM 启动步骤,双击就能运行。比如一个简单的 Spring Boot 接口,用 javac 编译后启动要 3-5 秒,用 Native Image 编译后启动只要 0.1-0.3 秒。内存占用低:原生程序不需要 JVM 运行时环境,内存占用能减少 50% 以上。比如同样的 Java 工具,javac 编译后运行需要 200MB 内存,Native Image 版本可能只需要 80MB。跨平台但需 “针对性编译”:支持 Windows、Linux、macOS,但要注意 —— 在 Windows 上编译的原生程序,不能直接在 Linux 上运行,需要在对应平台上重新编译(这点和 C/C++ 类似)。适用场景:对启动速度敏感的场景:比如云原生应用(Docker 容器、K8s 服务)、命令行工具(CLI)、Serverless 函数(比如 AWS Lambda);资源受限的环境:比如嵌入式设备、边缘计算节点,原生程序的低内存占用更适合;追求 “Java 原生性能” 的项目:比如高性能网关、实时数据处理程序,原生程序能减少 JVM 垃圾回收(GC)的开销。注意点:Native Image 编译时会做 “静态分析”,如果代码里有反射、动态代理(比如 Spring 依赖注入),需要提前配置 “反射白名单”(通过 reflect-config.json),否则编译后的程序会报错;另外,它目前对部分 Java 特性支持有限(比如 JVM Attach API),使用前建议先查看官方兼容性文档。三、专注 “Android 开发” 的编译器:Jack & Jill(已淘汰)与 D8/R8如果你是 Android 开发者,对 “编译器” 的感知可能更强烈 ——Android 早期用的是 javac 编译 Java 代码,再用 dx 工具转换成 Dalvik 字节码,但随着 Android 生态的发展,谷歌推出了专门的编译器工具链,其中最核心的就是 D8 和 R8。先插一句:很多老开发者可能记得 “Jack & Jill” 编译器,它是谷歌早年试图替代 javac 的工具,但因为稳定性和兼容性问题,在 Android Studio 3.2 之后就被淘汰了,现在 Android 开发的主流是 D8(编译器)和 R8(混淆压缩工具)。D8 编译器:Android 的 “专属 javac”核心作用:将 Java 源代码(或 javac 编译的字节码)转换成 Android 虚拟机(ART)能识别的 DEX 格式文件(.dex)。优势:相比早期的 dx 工具,D8 编译速度更快,生成的 DEX 文件体积更小,而且对 Java 8+ 特性(比如 Lambda、Stream API)的支持更好 —— 不需要额外引入 retrolambda 这类兼容库,直接编译就能在低版本 Android 系统上运行。R8 工具:编译 + 混淆 “二合一”核心作用:在 D8 编译的基础上,增加了 “代码混淆”“无用代码删除”“资源压缩” 功能。比如项目中引用了庞大的第三方库,但只用到其中 20% 的代码,R8 会自动删除未使用的 80% 代码,让最终的 APK/APP Bundle 体积减少 30%-50%。适用场景:所有 Android 应用开发,尤其是需要上架应用商店的项目 —— 代码混淆能保护源码不被反编译,减少被破解的风险。小提示:在 Android Studio 中,D8 和 R8 是默认启用的(从 Android Gradle Plugin 3.0 开始),不需要手动配置;如果想关闭混淆(比如调试时),可以在 build.gradle 文件中设置 minifyEnabled false。四、其他值得关注的 Java 编译器除了上面三款主流工具,还有一些编译器在特定场景下很有用,适合有特殊需求的开发者:1. Eclipse ECJ(Eclipse Compiler for Java)它是 Eclipse IDE 内置的 Java 编译器,和 javac 相比,最大的优势是 “增量编译”—— 当你修改了项目中的一个文件,ECJ 只会重新编译这个文件以及依赖它的文件,而不是整个项目。对于大型 Java 项目(比如有上千个类的企业应用),ECJ 的增量编译能把编译时间从几分钟缩短到几秒,极大提升开发效率。现在很多 IDE(比如 IntelliJ IDEA、NetBeans)也支持配置 ECJ 作为编译器,如果你经常在 IDE 中频繁修改代码,试试 ECJ 会有惊喜。2. AJC(AspectJ Compiler)如果你用 AspectJ 做 “面向切面编程”(AOP),比如实现日志记录、事务管理、性能监控,就需要用到 AJC 编译器。它是 javac 的扩展,能在编译时将 AspectJ 语法(比如 @Aspect、@Before 注解)织入到 Java 代码中,生成支持 AOP 功能的字节码。AJC 支持两种编译方式:一种是直接编译 .java 和 .aj(AspectJ 源文件),另一种是对 javac 编译后的字节码做 “后织入”,灵活性很高,是 Java 企业级应用实现 AOP 的核心工具。五、如何选择适合自己的 Java 编译器?其实没有 “最好” 的编译器,只有 “最适合” 的 —— 记住这几个判断维度,就能快速做出选择:看开发场景:普通 Java 后端 / 桌面开发选 javac,Android 开发选 D8/R8,云原生 / 高性能场景选 GraalVM Native Image,Eclipse 开发选 ECJ,AOP 开发选 AJC;看核心需求:追求兼容性和稳定性选 javac,追求启动速度和低内存选 GraalVM,追求 Android 体积和混淆选 D8/R8,追求 IDE 增量编译选 ECJ;看项目依赖:如果项目用了 Spring Boot 3.x,优先试试 GraalVM(Spring 官方对 Native Image 有很好的支持);如果项目是 Android 应用,直接用 D8/R8 即可,不用纠结其他编译器。结语:ava 编译器的迭代,本质上是为了适配不同场景的需求 —— 从 javac 保障兼容性,到 GraalVM 突破性能瓶颈,再到 D8/R8 优化移动端体验,每一款编译器都在解决特定的问题。建议大家根据自己的项目需求多尝试,比如用 GraalVM 把自己写的小工具改成原生程序,感受一下 “秒启动” 的快乐;或者在 Android 项目中看看 R8 压缩后的体积变化,或许能发现新的优化思路。————————————————原文链接:https://blog.csdn.net/2503_91389547/article/details/150951709
  • [技术干货] 为什么 Java 不让 Lambda 和匿名内部类修改外部变量?final 与等效 final 的真正意义
    引言  在Java编程中,尤其是在使用匿名内部类时,许多开发者都会遇到这样一个限制:从匿名内部类中访问的外部变量必须声明为final或是"等效final"。这个看似简单的语法规则背后,其实蕴含着Java语言设计的深层考量。本文将深入探讨这一限制的原因、实现机制以及在实际开发中的应用。 一、什么是匿名内部类?  在深入讨论之前,我们先简单回顾一下匿名内部类的概念。匿名内部类是没有显式名称的内部类,通常用于创建只使用一次的类实例。button.addActionListener(new ActionListener() {    @Override    public void actionPerformed(ActionEvent e) {        System.out.println("Button clicked!");    }});AI运行代码java二、final限制的历史与现状1、Java 8之前的严格final要求在Java 8之前,语言规范强制要求:任何被匿名内部类访问的外部方法参数或局部变量都必须明确声明为final// Java 7及之前版本public void process(String message) {    final String finalMessage = message; // 必须声明为final        new Thread(new Runnable() {        @Override        public void run() {            System.out.println(finalMessage); // 访问外部变量        }    }).start();}AI运行代码java2、Java 8的等效final(effectively final)Java 8引入了一个重要改进:等效final的概念如果一个变量在初始化后没有被重新赋值,即使没有明确声明为final,编译器也会将其视为final,这就是"等效final"// Java 8及之后版本public void process(String message) {    // message是等效final的,因为它没有被重新赋值    new Thread(new Runnable() {        @Override        public void run() {            System.out.println(message); // 可以直接访问        }    }).start();        // 如果取消下面的注释,会导致编译错误    // message = "modified"; // 这会使message不再是等效final的}AI运行代码java三、为什么需要final或等效final限制?1、变量捕获与生命周期差异核心问题:方法参数和局部变量的生命周期与匿名内部类实例的生命周期不一致方法参数和局部变量存在于栈帧中,方法执行完毕后就会被销毁匿名内部类对象可能存在于堆中,生命周期可能远超方法执行时间解决方案:Java通过值捕获而不是引用捕获来解决这个生命周期不匹配问题public void example() {    int value = 10; // 局部变量        Runnable r = new Runnable() {        @Override        public void run() {            // 这里访问的是value的副本,不是原始变量(引用地址不一样)            System.out.println(value);        }    };        // value变量可能在此处已经销毁,但匿名内部类仍然存在    new Thread(r).start();}AI运行代码java2、数据一致性保证(不限制出现的问题)如果允许修改捕获的变量,会导致令人困惑的行为// 假设Java允许这样做(实际上不允许)public void problematicExample() {    int counter = 0;        Runnable r = new Runnable() {        @Override        public void run() {            // 如果允许访问非final变量,这里应该看到什么值?            System.out.println(counter);        }    };        counter = 5; // 修改原始变量(实际开发,如果这里修改变量就会导致匿名内部类访问外部类编译报错)    r.run(); // 输出应该是什么?0还是5?}// 通过final限制,Java确保了捕获的值在内部类中始终保持一致,避免了这种不确定性AI运行代码java而且匿名内部类可能在另一个线程中执行,而原始变量可能在原始线程中被修改。final限制避免了线程安全问题四、底层实现机制Java编译器通过以下方式实现这一特性:值拷贝:编译器将final变量的值拷贝到匿名内部类中合成字段:在匿名内部类中创建一个合成字段来存储捕获的值构造函数传递:通过构造函数将捕获的值传递给匿名内部类实例可以通过反编译匿名内部类来观察这一机制:// 源代码public class Outer {    public void method(int param) {        Runnable r = new Runnable() {            @Override            public void run() {                System.out.println(param);            }        };    }}AI运行代码java反编译后的内部类和内部类大致如下:(参数自动添加final,内部类通过构造方法引入变量)// 反编译的外部类 public class Outer {    public void method(final int var1) {        Runnable var10000 = new Runnable() {            public void run() {                System.out.println(var1);            }        };    }}// 反编译后的匿名内部类class Outer$1 implements Runnable {    Outer$1(Outer var1, int var2) {        this.this$0 = var1;        this.val$param = var2;    }    public void run() {        System.out.println(this.val$param);    }}AI运行代码java五、解决方案如果确实需要“共享可变状态”,可以使用一个单元素数组、或者一个Atomicxxx类(如 AtomicInteger)​,或者将变量封装到一个对象中final int[] holder = new int[]{42};Runnable r = () -> {    System.out.println(holder[0]); // 可以读取    holder[0] = 100; // 可以修改数组内容,但不修改数组引用本身};AI运行代码java注意:这里你修改的是数组的内容,而不是变量 holder的引用,所以不违反规则六、常见问题与误区1、为什么实例变量没有这个限制?实例变量存储在堆中,与对象生命周期一致,因此内部类可以通过持有外部类引用直接访问它们,不需要值拷贝public class Outer {    private int instanceVar = 10; // 实例变量        public void method() {        new Thread(new Runnable() {            @Override            public void run() {                instanceVar++; // 可以直接修改实例变量            }        }).start();    }}AI运行代码java2、等效final的实际含义等效final意味着变量虽然没有明确声明为final,但符合final的条件:只赋值一次且不再修改public void effectivelyFinalExample() {    int normalVar = 10; // 等效final    final int explicitFinal = 20; // 明确声明为final    // 两者都可以在匿名内部类中使用    Runnable r = () -> {        System.out.println(normalVar + explicitFinal);    };        // 如果这里修改变量,同样会编译报错    // normalVar = 5;}AI运行代码java————————————————原文链接:https://blog.csdn.net/qq_35512802/article/details/151362542
  • [技术干货] Java内功修炼——并发的四重境界:单例之固、生产消费之衡、定时之准、池化之效
    1.单例模式1.1 概述单例模式(Singleton Pattern):是一种常用的设计模式,主要用于确保一个类在整个应用程序中只有一个实例,并提供一个全局访问点 核心作用 1.控制资源访问:常用于管理共享资源,可以避免多线程竞争或重复创建资源导致的性能问题2.全局状态管理:当某些对象需要被多个模块或组件共享时,单例模式提供统一的访问入口3.保证数据一致性:避免多个实例导致的数据不一致问题常见实现方式 饿汉模式:在类加载时就创建实例,避免了线程安全问题,但可能会造成资源浪费,尤其是当实例初始化过程复杂或占用较多资源时懒汉模式:一种延迟初始化的单例实现方式。实例在第一次被使用时才创建,而非在类加载时就创建。这种方式可以节省资源,但需要考虑线程安全问题1.2 饿汉模式public class Singleton {//类加载时进行实例化    private static final Singleton hungry = new Singleton();    //全局唯一获取实例的接口    public static Singleton getInstance(){        return hungry;    }    //构造方法私有化    private Singleton(){}}AI运行代码java 特点: 类加载时进行实例化不存在运行时实例化过程,所以不存在线程安全问题缺点: 即使后面的场景中没有使用到该实例,也会将该实例创建出来,可能会造成不必要的资源浪费1.3 懒汉模式class Singleton{//volatile:禁止指令重排序    private static volatile Singleton lazy = null;    //创建锁对象    private static final Object object = new Object();    //全局唯一获取实例的接口    public static Singleton getInstance(){        //外层if判断:优化,提高性能        if (lazy == null) {            //避免多线程时实例化多个对象            synchronized (object) {                if (lazy == null) {                    lazy = new Singleton();                }            }        }        return lazy;    }    //构造方法私有化    private Singleton(){}}AI运行代码java 实现细节: 1.通过synchronized加锁解决线程安全问题2.外层if判断,减少锁的开销3.volatile防止指令重排序(避免半初始化对象)特点: 只有在真正需要时才创建实例,减少系统启动时的资源占用,资源利用率高缺点: 若未正确使用同步机制(如synchronized或volatile),可能导致多线程环境下实例被多次创建,线程安全实现复杂1.4 懒汉模式半初始化Java对象初始化流程 1.内存分配阶段:当使用new关键字创建对象时,JVM会在堆内存中为该对象分配空间2.对象初始化阶段:对象内存分配完成后,开始执行初始化3.引用赋值阶段:内存分配完成后,JVM将分配的内存地址赋值给引用变量Java对象初始化流程(指令重排序后) 1.内存分配阶段:当使用new关键字创建对象时,JVM会在堆内存中为该对象分配空间2.引用赋值阶段:内存分配完成后,JVM将分配的内存地址赋值给引用变量3.对象初始化阶段:对象内存分配完成后,开始执行初始化 1.5 懒汉/饿汉优缺点对比特性 懒汉模式 饿汉模式实例化时机 第一次使用时 类加载时资源消耗 节省资源 可能浪费资源线程安全 需要额外同步机制 天然线程安全实现复杂度 较复杂 简单适用场景 实例化开销大,延迟加载 实例化开销小,不需要延迟加载2.生产者/消费者模式2.1 概述生产者/消费者模式(Producer/consumer model):用于协调多个线程或进程之间的任务分配与数据处理。生产者负责生成数据或任务,消费者负责处理这些数据或任务,二者通过共享的缓冲区(队列)进行解耦,避免直接依赖 核心作用 1.解耦生产与消费逻辑:生产者仅负责生成数据并放入缓冲区,消费者仅从缓冲区获取数据并处理。两者无需直接交互,降低代码复杂度,提高模块化程度2.平衡处理速率差异:生产者与消费者通常以不同速度运行。缓冲区作为中间层,允许生产者持续写入数据,消费者按自身能力消费,避免互相阻塞3.削峰填谷:通过缓冲队列平滑流量波动,避免系统因瞬时高负载崩溃。当生产者突然产生大量请求时,缓冲区暂时存储这些请求,消费者按照自身处理能力逐步消费;当生产者速度降低时,缓冲区逐步释放积压的请求,保持消费者稳定工作2.2 实现阻塞队列class MyBlockingQueue{    private int head = 0;    private int tail = 0;    private int useSize = 0;    private final String[] array;     public MyBlockingQueue(int capacity){        array = new String[capacity];    }    //添加    public synchronized void put(String string) throws InterruptedException {        if (isFull()){            //队列满了,等待消费者消耗元素            this.wait();        }            array[tail] = string;            tail++;            tail = (tail + 1) % array.length;            useSize++;            this.notify();    }    //删除    public String take() throws InterruptedException {        String ret;        synchronized (this) {            if (useSize <= 0) {                //队列空了,等待生产者添加元素.                this.wait();            }            ret = array[head];            head++;            head = (head + 1) % array.length;            useSize--;            this.notify();        }        return ret;    }    //判断是否满了    public boolean isFull(){        return useSize >= array.length;    }}AI运行代码java 2.3 实现生产者/消费者模式public class Producer_Consumer_Blog {    public static void main(String[] args) {        MyBlockingQueue queue = new MyBlockingQueue(1000);        Thread thread1 = new Thread(()->{            int n = 1;            while (true){                try {                    queue.put(n + "");                    System.out.println("生产元素n = " + n);                    n++;                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }            }        });        Thread thread2 = new Thread(()->{            while (true){                try {                    System.out.println("消费元素n = " + queue.take());                    Thread.sleep(1000);                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }            }        });        thread1.start();        thread2.start();    }}AI运行代码java 3.定时器3.1 概述定时器(Timer):用于在特定时间间隔或指定时间点执行任务的编程模式,广泛应用于定时任务调度、延迟操作、周期性任务等场景。核心思想是将任务的执行逻辑与时间控制解耦,通过统一的定时器管理多个任务 核心作用/特点 1.管理异步任务调度:Timer允许你安排一个任务在未来的某个时间点执行,或者以固定的间隔重复执行2.后台执行:Timer可以使用一个后台线程来执行任务,这意味着调度和执行任务不会阻塞主线程(主线程结束后后台线程跟着结束)3.简单易用:Timer提供了一个相对简单的方式来处理定时任务,适合用于不需要复杂调度的场景标准库Timer构造方法 //1.默认构造方法//创建一个Timer对象,是一个后台线程,并使用线程的默认名字public Timer() {        this("Timer-" + serialNumber());}//2.指定线程名字的构造方法//创建一个Timer对象,是一个后台线程,并使用指定的线程名字public Timer(String name) {        thread.setName(name);        thread.start();}//3.指定是否为后台线程的构造方法//传入true,是后台线程;传入false,是前台线程public Timer(boolean isDaemon) {        this("Timer-" + serialNumber(), isDaemon);}//4.指定线程名字和是否为后台线程的构造方法public Timer(String name, boolean isDaemon) {        thread.setName(name);        thread.setDaemon(isDaemon);        thread.start();}AI运行代码java 标准库Timer的schedule方法 1.schedule(TimerTask task, Date time):安排任务在指定的时间执行一次public static void main(String[] args) {        Timer timer = new Timer();        TimerTask timerTask = new TimerTask() {            @Override            public void run() {                System.out.println("延迟三秒执行");            }        };        //使用Date对象来指定具体的执行时间        //new Date(System.currentTimeMillis()+1000表示当前时间等待1000ms        timer.schedule(timerTask,new Date(System.currentTimeMillis()+1000));}AI运行代码java 2.schedule(TimerTask task, Date firstTime, long period):安排任务在指定的时间首次执行,然后每隔一段时间重复执行public static void main(String[] args) {        Timer timer = new Timer();        TimerTask timerTask = new TimerTask() {            @Override            public void run() {                System.out.println("延迟三秒执行");            }        };        //当前时间等待1000ms后第一次执行任务        //此后每间隔1000ms就执行一次任务        timer.schedule(timerTask,new Date(System.currentTimeMillis()+1000),1000);}AI运行代码java 3.schedule(TimerTask task, long delay):安排任务在指定的延迟时间后执行一次(相对于当前时间)public static void main(String[] args) {        Timer timer = new Timer();        TimerTask timerTask = new TimerTask() {            @Override            public void run() {                System.out.println("延迟三秒执行");            }        };        //当前时间延迟3000ms后执行        timer.schedule(timerTask,3000);}AI运行代码java 4.schedule(TimerTask task, long delay, long period):安排任务在指定的延迟时间后首次执行,然后每隔一段时间重复执行public static void main(String[] args) {        Timer timer = new Timer();        TimerTask timerTask = new TimerTask() {            @Override            public void run() {                System.out.println("延迟三秒执行");            }        };        //当前时间延迟3000ms后执行        //此后每间隔3000ms就执行一次任务        timer.schedule(timerTask,3000,3000);}AI运行代码java 3.2模拟实现定时器class MyTask implements Comparable<MyTask>{    private final Runnable runnable;    private final long time;    public MyTask(Runnable runnable,long delay){        this.runnable = runnable;        this.time = System.currentTimeMillis() + delay;    }    public long getTime(){        return this.time;    }    public void run(){        runnable.run();    }    @Override    public int compareTo(MyTask o) {        return (int)(this.time - o.time);    }}class MyTime{    private final PriorityQueue<MyTask> queue = new PriorityQueue<>();    public void schedule(Runnable runnable,long delay){        synchronized (this) {            MyTask myTask = new MyTask(runnable, delay);            queue.offer(myTask);            this.notify();        }    }    public MyTime(){        Thread thread = new Thread(() -> {            while (true) {                try {                    synchronized (this) {                        while (queue.isEmpty()) {                            this.wait();                        }                        MyTask myTask = queue.peek();                        long curTime = System.currentTimeMillis();                        if (curTime >= myTask.getTime()) {                            myTask.run();                            queue.poll();                        } else {                            this.wait(myTask.getTime() - curTime);                        }                    }                }catch (InterruptedException e){                    throw new RuntimeException(e);                }            }        });        thread.setDaemon(true);        thread.start();    }}AI运行代码java 4.线程池4.1 概述线程池:线程池是一种管理和复用线程的编程模式。它预先创建一定数量的线程,在执行任务需要时,将任务分配给这些线程,从而提高运行效率 核心作用:优化多线程任务的执行效率与管理资源 特点 线程复用:当线程执行完一个任务时,不会立即销毁,而是等待下一个任务的到来(当然这种等待是有时间限制的),这样避免了频繁的创建和销毁线程动态调整:根据实际环境需要动态调整线程数量,以达到最佳性能任务队列:线程池会维护一个任务队列,用于存放待执行的任务,当线程空闲时,从队列中取出任务并执行标准库线程池构造方法 1.int corePoolSize:核心线程数2.int maximumPoolSize:最大线程数3.long keepAliveTime:非核心线程的空闲时的最大存活时间4.TimeUnit unit:时间单位5.BlockingQueue< Runnable > workQueue:任务队列6.ThreadFactory threadFactory:线程工厂,用于创建新线程的工厂7.RejectedExecutionHandler handler:拒绝策略4.3线程池的执行流程假设现在有一个线程池:核心线程数2,最大线程数4,等待队列2 任务数量<=2(A,B)时,由核心线程执行任务2<任务数量<=4(A,B,C,D)时,核心线程无法同时处理所有任务,未被执行的任务(C,D)将会进入等待队列中等待核心线程执行4<任务数量<=6(A,B,C,D,E,F),此时等待队列也满了,线程池就会就会开放非核心线程来执行任务,C和D任务继续在等待队列中等待,新添加的E和F任务由非核心线程来执行任务数量>6,核心线程,等待队列,非核心线程都被任务所占用,仍然无法满足需求,此时就会触发线程池的拒绝策略4.4 拒绝策略  1.AbortPolicy:直接抛异常2.CallerRunsPolicy:由提交该任务的线程来执行3.DiscardPolicy:丢弃新任务4.DiscardOldestPolicy:丢弃最老的任务4.5 模拟实现线程池public class MyThreadPoolExecutor {    private final int capacity = 1000;    //阻塞队列    private final MyBlockingQueue queue = new MyBlockingQueue(capacity);    private final List<Thread> list = new ArrayList<>();    //创建线程    public MyThreadPoolExecutor(int n){        for (int i = 0; i < n; i++) {            Thread thread = new Thread(()->{                while (true) {                    try {                        queue.take().run();                    } catch (InterruptedException e) {                        throw new RuntimeException(e);                    }                }            });            thread.start();            list.add(thread);        }    }    //添加任务    public void submit(Runnable runnable) throws InterruptedException {        queue.put(runnable);    }    public int getCapacity(){        return capacity;    }    //获取线程    public List<Thread> getList() {        return list;    }}AI运行代码java ————————————————原文链接:https://blog.csdn.net/2401_89167985/article/details/150641639
  • [技术干货] 新手向:C语言、Java、Python 的选择与未来指南
    语言即工具,选对方向比埋头苦学更重要你好,编程世界的新朋友!当你第一次踏入代码的宇宙,面对形形色色的编程语言,是否感到眼花缭乱?今天我们就来聊聊最主流的三种编程语言——C语言、Java 和 Python——它们各自是谁,适合做什么,以及未来十年谁能带你走得更远。一、编程世界的三把钥匙:角色定位如果把编程比作建造房屋,那么:C语言是钢筋骨架:诞生于1972年,它直接与计算机硬件“对话”,负责构建最基础的支撑结构。Java是精装套房:1995年问世,以“一次编写,到处运行”闻名,擅长打造稳定、可复用的功能模块。Python是智能管家:1991年出生却在近十年大放异彩,像一位高效助手,用最少的指令完成复杂任务13。二、核心差异对比:从底层到应用1. 语言类型与设计哲学C语言:属于面向过程的编译型语言。代码在执行前需全部翻译成机器指令,运行效率极高,但需要开发者手动管理内存(类似自己打扫房间)15。Java:面向对象的半编译语言。代码先转为字节码,再通过Java虚拟机(JVM)运行。牺牲少许效率换来跨平台能力——Windows、Linux、Mac 都能执行同一份代码39。Python:多范式的解释型语言。代码边翻译边执行,开发便捷但速度较慢。支持面向对象、函数式编程,语法如英语般直白78。翻译2. 语法与学习曲线# Python 打印10次"Hello" for i in range(10):     print("Hello") // Java 实现相同功能public class Main {    public static void main(String[] args) {        for(int i=0; i<10; i++){            System.out.println("Hello");        }    }} /* C语言版本 */#include <stdio.h>int main() {    for(int i=0; i<10; i++){        printf("Hello\n");    }    return 0;}运行本项目Python 接近自然语言,新手1天就能写出实用脚本5Java 需理解类、对象等概念,1-2个月可入门9C语言 需掌握指针、内存分配,门槛最高13. 性能特点语言    执行速度    内存管理    典型场景C语言    ⚡⚡⚡⚡⚡    手动管理    实时系统、高频交易Java    ⚡⚡⚡⚡    自动回收    企业后台服务Python    ⚡⚡    自动回收    数据分析、原型开发C语言直接操作硬件,速度可比Python快50倍以上;Java居中;Python虽慢但可通过C扩展提速210。4. 应用领域C语言:操作系统(Linux内核)、嵌入式设备(空调芯片)、游戏引擎(Unity底层)27Java:    - 安卓APP(微信、支付宝)    - 银行交易系统(高可靠性必须)    - 大型网站后端(淘宝、京东)28Python:    - 人工智能(ChatGPT的基石语言)    - 数据分析(处理百万行Excel只需几行代码)    - 自动化脚本(批量处理文件/网页)185. 生态系统支持Python:拥有28万个第三方库,如NumPy(科学计算)、TensorFlow(AI)2Java:Spring框架统治企业开发,Android SDK构建移动应用2C语言:标准库较小,但Linux/Windows API均以其为核心7三、未来十年:谁主沉浮?1. AI战场:Python 正面临 Java 的挑战Python目前占据90%的AI项目,但2025年可能成为转折点。Java凭借企业级性能正加速渗透:    - Spring AI项目获阿里等巨头支持    - 直接调用GPU提升计算效率(Project Babylon)    - 大厂倾向将AI集成到现有Java系统中46Python 仍靠易用性守住数据科学家阵地,但需解决性能瓶颈10。2. 新兴领域卡位战边缘计算(IoT设备):C语言因极致效率成为传感器、工控设备首选10云原生服务:Java和Go语言(非本文主角)主导容器化微服务8Web3与区块链:Java的强安全性被蚂蚁链等采用23. 就业市场真相Java:国内70%企业系统基于Java,岗位需求最稳定68Python:AI工程师平均薪资比Java高18%,但竞争加剧8C语言:嵌入式开发缺口大,入行门槛高但职业生涯长9四、给新手的终极建议学习路径规划:零基础入门:选 Python → 快速建立成就感,两周做出小工具求职导向:学 Java → 进入金融/电信等行业的核心系统硬件/高薪偏好:攻 C语言 → 深耕芯片、自动驾驶等高端领域关键决策原则:graph LRA[你的目标] --> B{选择语言}B -->|做AI/数据分析| C(Python)B -->|开发企业软件/安卓APP| D(Java)B -->|写操作系统/驱动/引擎| E(C语言)运行本项目专家提醒:2025年之后,掌握“双语言能力”更吃香:Python + C:用Python开发AI原型,C语言加速核心模块Java + Python:Java构建系统,Python集成智能组件五、技术架构深度拆解1. C语言:系统级开发的基石内存操作直接通过malloc()/free()管理内存,程序员可精确控制每一字节:int *arr = (int*)malloc(10 * sizeof(int)); // 申请40字节内存free(arr); // 必须手动释放,否则内存泄漏运行本项目指针的威力与风险指针直接访问物理地址,可实现高效数据传递:void swap(int *a, int *b) { // 通过指针交换变量    int temp = *a;    *a = *b;    *b = temp;}运行本项目典型事故:缓冲区溢出(如strcpy未检查长度导致系统崩溃)应用场景扩展领域    代表项目    关键技术点操作系统    Linux内核    进程调度、文件系统实现嵌入式系统    无人机飞控    实时响应(<1ms延迟)高频交易    证券交易所系统    微秒级订单处理图形渲染    OpenGL底层    GPU指令优化2. Java:企业级生态的王者JVM虚拟机机制Java源码 → 字节码 → JIT编译 → 机器码跨平台原理:同一份.class文件可在Windows/Linux/Mac的JVM上运行垃圾回收(GC)奥秘分代收集策略:graph LRA[新对象] --> B[年轻代-Eden区]B -->|Minor GC| C[Survivor区]C -->|年龄阈值| D[老年代]D -->|Full GC| E[回收]运行本项目调优关键:-Xmx设置堆大小,G1GC减少停顿时间企业级框架矩阵框架    作用    代表应用Spring Boot    快速构建微服务    阿里双11后台Hibernate    对象-数据库映射    银行客户管理系统Apache Kafka    高吞吐量消息队列    美团订单分发系统Netty    高性能网络通信    微信消息推送3. Python:科学计算的终极武器动态类型双刃剑graph TD  A[数据获取] --> B(Pandas处理)  B --> C{建模选择}  C --> D[机器学习-scikit-learn]  C --> E[深度学习-TensorFlow/PyTorch]  D --> F[模型部署-Flask]  E --> F  F --> G[Web服务]运行本项目六、行业应用全景图1. C语言:硬科技核心载体航天控制火星探测器着陆程序:实时计算轨道参数(C代码执行速度比Python快400倍)火箭燃料控制系统:直接操作传感器寄存器汽车电子特斯拉Autopilot底层:毫米波雷达信号处理发动机ECU(电子控制单元):微控制器(MCU)仅支持C工业自动化PLC编程:三菱FX系列用C编写逻辑控制数控机床:实时位置控制精度达0.001mm2. Java:商业系统支柱金融科技支付清算:Visa每秒处理6.5万笔交易(Java+Oracle)风控系统:实时反欺诈检测(Apache Flink流计算)电信领域5G核心网:爱立信Cloud RAN基于Java微服务计费系统:中国移动月账单生成(处理PB级数据)电子商务淘宝商品搜索:Elasticsearch集群(Java开发)京东库存管理:Spring Cloud微服务架构3. Python:数据智能引擎生物医药基因序列分析:Biopython处理FASTA文件药物分子模拟:RDKit库计算3D结构金融分析量化交易:pandas清洗行情数据,TA-Lib技术指标计算风险建模:Monte Carlo模拟预测股价波动AIGC革命Stable Diffusion:PyTorch实现文生图大模型训练:Hugging Face Transformers库七、性能优化实战对比1. 计算圆周率(1亿次迭代)// C语言版:0.8秒#include <stdio.h>int main() {    double pi = 0;    for (int k = 0; k < 100000000; k++) {        pi += (k % 2 ? -1.0 : 1.0) / (2*k + 1);    }    printf("%f", pi * 4);}运行本项目// Java版:1.2秒public class Pi {    public static void main(String[] args) {        double pi = 0;        for (int k = 0; k < 100000000; k++) {            pi += (k % 2 == 0 ? 1.0 : -1.0) / (2*k + 1);        }        System.out.println(pi * 4);    }}运行本项目# Python版:12.7秒 → 用Numpy优化后:1.5秒import numpy as npk = np.arange(100000000)pi = np.sum((-1)**k / (2*k + 1)) * 4print(pi)运行本项目2. 内存消耗对比(处理1GB数据)语言    峰值内存    关键影响因素C    1.1GB    手动分配精确控制Java    2.3GB    JVM堆内存开销Python    5.8GB    对象模型额外开销八、未来十年技术演进预测1. C语言:拥抱现代安全特性新标准演进:C23引入#elifdef简化宏,nullptr替代NULL安全强化:边界检查函数(如strcpy_s())静态分析工具(Clang Analyzer)2. Java:云原生时代进化GraalVM革命:将Java字节码直接编译为本地机器码(启动速度提升50倍)Project Loom:虚拟线程支持百万级并发(颠覆传统线程模型)3. Python:性能突围计划Pyston v3:JIT编译器使速度提升30%Mojo语言:兼容Python语法的超集,速度达C级别(专为AI设计)九、开发者能力矩阵建议能力维度    C语言工程师    Java架构师    Python数据科学家核心技能    指针/内存管理    Spring Cloud生态    Pandas/NumPy汇编接口调用    JVM调优    Scikit-Learn实时系统设计    分布式事务    TensorFlow辅助工具    GDB调试器    Arthas诊断工具    Jupyter NotebookValgrind内存检测    Prometheus监控    MLflow实验管理薪资范围    3-5年经验:30-50万    5-8年经验:50-80万    AI方向:60-100万+结语:三角平衡的编程生态C语言守护数字世界的物理边界——没有它,芯片无法启动,火箭不能升空Java构筑商业文明的数字基石——支撑全球70%的企业交易系统Python点燃智能时代的创新引擎——驱动90%的AI研究论文————————————————原文链接:https://blog.csdn.net/2302_77626561/article/details/151645868
  • [技术干货] 【专题汇总】 八月份技术干货汇总它虽迟但到,快进来瞧瞧吧!
    大家好,8月份的技术汇总贴它虽迟但到。本次带来了关于SpringBoot,C++,Java,python,GO语言等等多方面知识,量大管饱,希望可以帮助到大家。1、Java报错:org.springframework.beans.factory.BeanCreationException的五种解决方法【转载】cid:link_02、C#控制台程序同步调用WebApi实现方式【转载】cid:link_73、java -jar example.jar 产生的日志输出到指定文件的方法【转载】cid:link_84、 SpringBoot项目自定义静态资源映射规则的实现代码【转载】cid:link_15、 SpringBoot中9个内置过滤器用法的完整指南【转载】cid:link_26、C++中的list与forward_list介绍与使用【转载】cid:link_37、Java使用Redis实现消息订阅/发布的几种方式【转载】cid:link_98、Spring Boot中使用@Scheduled和Quartz实现定时任务的详细过程【转载】cid:link_109、 Python中的sort()和sorted()用法示例解析【转载】https://bbs.huaweicloud.com/forum/thread-02102190373187063025-1-1.html10、 python中update()函数的用法和一些例子【转载】cid:link_411、基于Python编写新手向的简易翻译工具 【转载】cid:link_1112、使用Python创建PowerPoint各种图表的详细教程【转载】cid:link_1213、python basicConfig()简介及用法举例【转载】cid:link_514、 Python 存根文件(.pyi)简介与实战案例及类型提示的高级指南【转载】cid:link_615、Go轻松构建WebSocket服务器的实现方案【转载】https://bbs.huaweicloud.com/forum/thread-0294190373041865026-1-1.html
  • [技术干货] Java JUC 详解:并发.util.concurrent 并发工具包指南
    JUC(Java Util Concurrent)即 Java 并发工具包,是java.util.concurrent包及其子包的简称,自 Java 5 引入,为并发编程提供了高效、安全、可靠的工具类,极大简化了多线程编程的复杂度。JUC 主要包含以下几类组件:线程池框架(Executor Framework)并发集合(Concurrent Collections)同步工具(Synchronizers)原子操作类(Atomic Classes)锁机制(Locks)并发工具类(如 CountDownLatch、CyclicBarrier 等)线程池框架线程池通过重用线程来减少线程创建和销毁的开销,提高系统性能。核心接口与类Executor:最基本的线程池接口,定义了执行任务的方法ExecutorService:扩展了 Executor,提供了更丰富的线程池操作ThreadPoolExecutor:线程池的核心实现类Executors:线程池的工具类,提供了常用线程池的创建方法线程池示例import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit; public class ThreadPoolExample {    public static void main(String[] args) {        // 创建固定大小的线程池        ExecutorService executor = Executors.newFixedThreadPool(3);                // 提交任务        for (int i = 0; i < 10; i++) {            final int taskId = i;            executor.submit(() -> {                try {                    System.out.println("任务 " + taskId + " 由线程 " +                                        Thread.currentThread().getName() + " 执行");                    TimeUnit.SECONDS.sleep(1);                } catch (InterruptedException e) {                    Thread.currentThread().interrupt();                }            });        }                // 关闭线程池        executor.shutdown();        try {            // 等待所有任务完成            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {                // 超时后强制关闭                executor.shutdownNow();            }        } catch (InterruptedException e) {            executor.shutdownNow();        }    }}AI生成项目ThreadPoolExecutor 核心参数手动创建线程池时,ThreadPoolExecutor的构造函数提供了最灵活的配置:public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue,                          ThreadFactory threadFactory,                          RejectedExecutionHandler handler)AI生成项目corePoolSize:核心线程数maximumPoolSize:最大线程数keepAliveTime:非核心线程的空闲超时时间workQueue:任务等待队列threadFactory:线程工厂handler:拒绝策略import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit; public class CustomThreadPool {    public static void main(String[] args) {        // 自定义线程池配置        ThreadPoolExecutor executor = new ThreadPoolExecutor(            2, // 核心线程数            5, // 最大线程数            30, // 空闲时间            TimeUnit.SECONDS,            new ArrayBlockingQueue<>(10), // 有界队列            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略        );                // 提交任务        for (int i = 0; i < 20; i++) {            final int taskId = i;            executor.execute(() -> {                try {                    System.out.println("任务 " + taskId + " 由线程 " +                                       Thread.currentThread().getName() + " 执行");                    TimeUnit.MILLISECONDS.sleep(500);                } catch (InterruptedException e) {                    Thread.currentThread().interrupt();                }            });        }                executor.shutdown();    }}AI生成项目并发集合JUC 提供了一系列线程安全的集合类,相比传统的同步集合,通常具有更好的性能。常用并发集合ConcurrentHashMap:线程安全的 HashMap 替代者CopyOnWriteArrayList:读多写少场景下的线程安全 ListCopyOnWriteArraySet:基于 CopyOnWriteArrayList 实现的 SetConcurrentLinkedQueue:高效的并发队列LinkedBlockingQueue:可阻塞的链表队列ArrayBlockingQueue:有界的数组队列PriorityBlockingQueue:支持优先级的阻塞队列ConcurrentHashMap 示例import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit; public class ConcurrentHashMapExample {    public static void main(String[] args) throws InterruptedException {        Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();        ExecutorService executor = Executors.newFixedThreadPool(4);                // 并发写入        for (int i = 0; i < 1000; i++) {            final int num = i;            executor.submit(() -> {                String key = "key" + (num % 10);                // 原子操作:计算并替换                concurrentMap.compute(key, (k, v) -> v == null ? 1 : v + 1);            });        }                executor.shutdown();        executor.awaitTermination(1, TimeUnit.MINUTES);                // 输出结果        concurrentMap.forEach((k, v) -> System.out.println(k + ": " + v));    }}AI生成项目同步工具类JUC 提供了多种同步工具,用于协调多个线程之间的协作。CountDownLatch允许一个或多个线程等待其他线程完成操作。import java.util.concurrent.CountDownLatch;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; public class CountDownLatchExample {    public static void main(String[] args) throws InterruptedException {        // 计数器为3        CountDownLatch latch = new CountDownLatch(3);        ExecutorService executor = Executors.newFixedThreadPool(3);                for (int i = 0; i < 3; i++) {            final int taskId = i;            executor.submit(() -> {                try {                    System.out.println("任务 " + taskId + " 开始执行");                    Thread.sleep(1000 + taskId * 500);                    System.out.println("任务 " + taskId + " 执行完成");                } catch (InterruptedException e) {                    Thread.currentThread().interrupt();                } finally {                    // 计数器减1                    latch.countDown();                }            });        }                System.out.println("等待所有任务完成...");        // 等待计数器变为0        latch.await();        System.out.println("所有任务已完成,继续执行主线程");                executor.shutdown();    }}AI生成项目CyclicBarrier让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障,所有被阻塞的线程才会继续执行。import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; public class CyclicBarrierExample {    public static void main(String[] args) {        // 3个线程到达屏障后,执行Runnable任务        CyclicBarrier barrier = new CyclicBarrier(3, () ->             System.out.println("所有线程已到达屏障,开始下一步操作"));                ExecutorService executor = Executors.newFixedThreadPool(3);                for (int i = 0; i < 3; i++) {            final int threadId = i;            executor.submit(() -> {                try {                    System.out.println("线程 " + threadId + " 正在执行任务");                    Thread.sleep(1000 + threadId * 500);                    System.out.println("线程 " + threadId + " 到达屏障");                    // 等待其他线程到达                    barrier.await();                    System.out.println("线程 " + threadId + " 继续执行");                } catch (InterruptedException | BrokenBarrierException e) {                    Thread.currentThread().interrupt();                }            });        }                executor.shutdown();    }}AI生成项目Semaphore信号量,用于控制同时访问特定资源的线程数量。import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit; public class SemaphoreExample {    public static void main(String[] args) {        // 允许3个线程同时访问        Semaphore semaphore = new Semaphore(3);        ExecutorService executor = Executors.newFixedThreadPool(5);                for (int i = 0; i < 10; i++) {            final int taskId = i;            executor.submit(() -> {                try {                    // 获取许可                    semaphore.acquire();                    System.out.println("任务 " + taskId + " 获得许可,开始执行");                    TimeUnit.SECONDS.sleep(2);                    System.out.println("任务 " + taskId + " 执行完成,释放许可");                } catch (InterruptedException e) {                    Thread.currentThread().interrupt();                } finally {                    // 释放许可                    semaphore.release();                }            });        }                executor.shutdown();    }}AI生成项目原子操作类JUC 提供了一系列原子操作类,用于在不使用锁的情况下实现线程安全的原子操作。主要原子类包括:基本类型:AtomicInteger、AtomicLong、AtomicBoolean数组类型:AtomicIntegerArray、AtomicLongArray等引用类型:AtomicReference、AtomicStampedReference等字段更新器:AtomicIntegerFieldUpdater等import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger; public class AtomicExample {    private static AtomicInteger counter = new AtomicInteger(0);        public static void main(String[] args) throws InterruptedException {        ExecutorService executor = Executors.newFixedThreadPool(10);                // 10个线程,每个线程自增1000次        for (int i = 0; i < 10; i++) {            executor.submit(() -> {                for (int j = 0; j < 1000; j++) {                    // 原子自增操作                    counter.incrementAndGet();                }            });        }                executor.shutdown();        executor.awaitTermination(1, TimeUnit.MINUTES);                // 结果应该是10000        System.out.println("最终计数: " + counter.get());    }}AI生成项目锁机制JUC 的java.util.concurrent.locks包提供了比synchronized更灵活的锁机制。Lock 接口Lock接口是所有锁的父接口,主要实现类有:ReentrantLock:可重入锁ReentrantReadWriteLock:可重入读写锁import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample {    private static int count = 0;    // 创建可重入锁    private static Lock lock = new ReentrantLock();        public static void main(String[] args) throws InterruptedException {        ExecutorService executor = Executors.newFixedThreadPool(5);                for (int i = 0; i < 1000; i++) {            executor.submit(() -> {                // 获取锁                lock.lock();                try {                    count++;                } finally {                    // 确保锁被释放                    lock.unlock();                }            });        }                executor.shutdown();        executor.awaitTermination(1, TimeUnit.MINUTES);                System.out.println("最终计数: " + count);    }}AI生成项目读写锁ReentrantReadWriteLock提供了读锁和写锁分离,适合读多写少的场景:import java.util.HashMap;import java.util.Map;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriteLockExample {    private Map<String, String> data = new HashMap<>();    private ReadWriteLock lock = new ReentrantReadWriteLock();        // 读操作使用读锁    public String get(String key) {        lock.readLock().lock();        try {            System.out.println("读取 key: " + key + ",线程: " + Thread.currentThread().getName());            return data.get(key);        } finally {            lock.readLock().unlock();        }    }        // 写操作使用写锁    public void put(String key, String value) {        lock.writeLock().lock();        try {            System.out.println("写入 key: " + key + ",线程: " + Thread.currentThread().getName());            data.put(key, value);        } finally {            lock.writeLock().unlock();        }    }        public static void main(String[] args) throws InterruptedException {        ReadWriteLockExample example = new ReadWriteLockExample();        ExecutorService executor = Executors.newFixedThreadPool(5);                // 添加写操作        executor.submit(() -> example.put("name", "Java"));                // 添加多个读操作        for (int i = 0; i < 4; i++) {            executor.submit(() -> {                for (int j = 0; j < 3; j++) {                    example.get("name");                    try {                        TimeUnit.MILLISECONDS.sleep(100);                    } catch (InterruptedException e) {                        Thread.currentThread().interrupt();                    }                }            });        }                executor.shutdown();        executor.awaitTermination(1, TimeUnit.MINUTES);    }}AI生成项目实战案例:生产者消费者模型使用 JUC 的阻塞队列实现经典的生产者消费者模型:import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit; public class ProducerConsumerExample {    // 容量为10的阻塞队列    private static BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);    private static final int MAX_ITEMS = 20;        // 生产者    static class Producer implements Runnable {        private int id;                public Producer(int id) {            this.id = id;        }                @Override        public void run() {            try {                for (int i = 0; i < MAX_ITEMS; i++) {                    int item = id * 100 + i;                    queue.put(item); // 放入队列,如果满了会阻塞                    System.out.println("生产者 " + id + " 生产了: " + item +                                        ",队列大小: " + queue.size());                    TimeUnit.MILLISECONDS.sleep(100); // 模拟生产耗时                }            } catch (InterruptedException e) {                Thread.currentThread().interrupt();            }        }    }        // 消费者    static class Consumer implements Runnable {        private int id;                public Consumer(int id) {            this.id = id;        }                @Override        public void run() {            try {                for (int i = 0; i < MAX_ITEMS; i++) {                    int item = queue.take(); // 从队列取,如果空了会阻塞                    System.out.println("消费者 " + id + " 消费了: " + item +                                        ",队列大小: " + queue.size());                    TimeUnit.MILLISECONDS.sleep(150); // 模拟消费耗时                }            } catch (InterruptedException e) {                Thread.currentThread().interrupt();            }        }    }        public static void main(String[] args) throws InterruptedException {        ExecutorService executor = Executors.newFixedThreadPool(4);                // 创建2个生产者        executor.submit(new Producer(1));        executor.submit(new Producer(2));                // 创建2个消费者        executor.submit(new Consumer(1));        executor.submit(new Consumer(2));                executor.shutdown();        executor.awaitTermination(1, TimeUnit.MINUTES);    }}AI生成项目总结JUC 为 Java 并发编程提供了强大的工具支持,大大简化了多线程程序的开发难度。掌握 JUC 的使用,能够帮助开发者编写高效、安全的并发程序,应对多线程环境下的各种挑战。在实际开发中,应根据具体场景选择合适的并发工具,同时注意线程安全和性能之间的平衡。通过不断实践和深入理解这些工具的原理,您将能够构建出更健壮、更高效的并发应用程序。————————————————原文链接:https://blog.csdn.net/m0_57836225/article/details/149613910
  • [技术干货] 新手向:Java方向讲解
    从诺基亚塞班到阿里双11,从安卓应用到华尔街交易,Java用一行System.out.println()征服了数字世界1998年,诺基亚在塞班系统上首次采用Java ME技术,让手机具备了运行应用程序的能力,开启了移动互联网的序幕。当时的Java开发者们可能不会想到,这个简单的System.out.println()打印语句,会成为改变世界的代码。2009年,阿里首次在双11购物节中使用Java构建的分布式系统,成功应对了每秒数万笔交易的挑战。在2019年双11期间,阿里云更是创下单日处理54.4万笔/秒的世界纪录,这背后是数百万行Java代码的完美配合。在移动端,Android系统基于Java语言构建的应用生态已经覆盖全球超过25亿台设备。从简单的计算器应用到复杂的3D游戏,Java的跨平台特性让同一个应用能在不同设备上稳定运行。在金融领域,华尔街90%以上的高频交易系统使用Java开发。高盛、摩根士丹利等投行依靠Java的稳定性和高性能特性,在纳秒级的时间窗口内完成数以亿计的交易。一个简单的System.out.println()调试语句,可能就关系着数百万美元的交易决策。一、设计哲学:一次编写,到处运行的虚拟王国核心三支柱:graph LR  A[Java语言] --> B[字节码]  B --> C[JVM虚拟机]  C --> D[操作系统] AI生成项目跨平台本质:字节码作为通用货币,JVM担任央行(Windows/Mac/Linux分别实现本地化)内存安全革命:自动垃圾回收(GC)终结手动内存管理时代对象王国宪法:万物皆对象(除基本类型)单继承多接口(规避C++菱形继承问题)强类型检查(编译期拦截90%类型错误)版本进化里程碑:版本    代号    革命性特性    商业影响JDK 1.2    Playground    集合框架/内部类    企业级开发奠基Java 5    Tiger    泛型/注解/枚举    企业注解驱动开发爆发Java 8    Spider    Lambda/Stream API    函数式编程普及Java 17    LTS    密封类/模式匹配    云原生时代标准基石二、JVM虚拟机:万亿级商业系统的动力引擎1. 字节码执行全流程public class Main {      public static void main(String[] args) {          int sum = 0;          for (int i = 1; i <= 100; i++) {              sum += i;          }      }  }  AI生成项目编译后字节码关键指令:0: iconst_0         // 压入常数0  1: istore_1         // 存储到变量1  2: iconst_1         // 压入1  3: istore_2         // 存储到循环变量i  4: iload_2          // 加载i  5: bipush 100       // 压入100  7: if_icmpgt 20     // 比较i>100则跳转  AI生成项目2. JIT即时编译黑科技分层编译策略:层级    编译方式    适用场景Level 0    解释执行    冷门代码Level 3    C1简单编译    短期存活方法Level 4    C2深度优化    热点方法(>万次)逃逸分析优化:// 未优化前:在堆分配100万对象  void process() {      for(int i=0; i<1_000_000; i++){          User user = new User(); // 对象分配      }  }  AI生成项目JIT优化后:拆解User字段为局部变量,彻底消除对象分配翻译3. GC垃圾回收王朝更迭收集器    工作方式    适用场景    暂停时间Serial GC    单线程复制    客户端小程序    数百msParallel GC    多线程标记整理    吞吐优先系统    几十msCMS    并发标记清除    响应敏感系统    10ms以下G1 GC    分区域并发收集    大内存应用    10ms级可控ZGC    染色指针+并发转移    10TB级内存    <1ms阿里双11实战配置:-XX:+UseG1GC -Xmx100g -XX:MaxGCPauseMillis=200  三、技术生态:四大疆域的统治版图1. 企业级开发王国(Java EE / Jakarta EE)Spring帝国架构:graph TD  A[Spring Boot] --> B[自动配置]  A --> C[嵌入式容器]  B --> D[Spring Data]  B --> E[Spring Security]  C --> F[Tomcat/Netty]  AI生成项目微服务黄金组合:注册中心:Nacos/Zookeeper服务调用:OpenFeign熔断降级:Sentinel配置中心:Apollo高并发架构案例(12306系统):@RestController  public class TicketController {      @Autowired      private RedisTemplate<String, Ticket> redisTemplate;       @GetMapping("/grab")      public String grabTicket(@RequestParam String trainId) {          // Redis分布式锁确保原子性          Boolean locked = redisTemplate.opsForValue()                  .setIfAbsent("lock_"+trainId, "1", 10, TimeUnit.SECONDS);          if(locked) {              Ticket ticket = redisTemplate.opsForList().rightPop(trainId);              if(ticket != null) return "抢票成功";          }          return "票已售罄";      }  }  AI生成项目2. 移动端王国(Android)Android架构演进:架构    代表技术    解决痛点MVC    Activity全能控制    逻辑视图耦合MVP    Presenter中介    单元测试困难MVVM    LiveData+DataBinding    数据驱动视图MVI    单向数据流    状态管理混乱Jetpack组件矩阵:graph LR  A[Lifecycle] --> B[ViewModel]  B --> C[LiveData]  C --> D[Room]  A --> E[WorkManager]  D --> F[Paging] AI生成项目3. 大数据王国Hadoop生态链:组件    Java类占比    核心功能HDFS    98%    分布式文件存储MapReduce    100%    批处理计算框架HBase    85%    列式数据库Spark    30%    内存计算(Scala主导)Flink流处理Java示例:DataStream<String> data = env.socketTextStream("localhost", 9999);  data.flatMap((String line, Collector<WordCount> out) -> {          for (String word : line.split(" ")) {              out.collect(new WordCount(word, 1));          }      })      .keyBy(WordCount::getWord)      .sum("count")      .print(); // 实时词频统计  AI生成项目4. 云原生新边疆Quarkus:云原生Java革命@Path("/hello")  public class GreetingResource {      @GET      @Produces(MediaType.TEXT_PLAIN)      public String hello() {          return "启动时间: " + (System.currentTimeMillis() - StartupTimer.start);      }  }  AI生成项目性能对比:指标    传统Tomcat    Quarkus启动时间    4.5秒    0.038秒内存占用    285MB    45MB请求延迟    15ms    3ms四、开发工具链:帝国工程师的武器库1. 构建工具进化史工具    配置文件    依赖管理机制    构建速度Ant    build.xml    手动下载jar    慢Maven    pom.xml    中央仓库自动解析    中等Gradle    build.gradle    增量编译+缓存    快(快30%)Gradle多模块配置:// settings.gradle  include 'user-service', 'order-service', 'gateway'   // build.gradle  subprojects {      apply plugin: 'java'      dependencies {          implementation 'org.springframework.boot:spring-boot-starter-web'      }  }  AI生成项目2. 诊断调优神器JFR飞行记录仪:java -XX:StartFlightRecording=duration=60s,filename=recording.jfr MyApp  Arthas在线诊断:watch com.example.service.UserService queryUser '{params, returnObj}' -x 3  GC日志分析:java -Xlog:gc*=debug:file=gc.log -jar app.jar  五、未来战场:危机与变革1. 云原生时代的挑战者语言    优势领域    Java应对策略Go    高并发微服务    Quarkus/GraalVMRust    系统编程    Panama FFI接口Kotlin    Android开发    Jetpack Compose整合2. 颠覆性技术突破GraalVM原生编译:native-image --no-fallback -jar myapp.jar  将Spring Boot应用转为独立可执行文件(启动<50ms)Loom虚拟线程:try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {      for(int i=0; i<10_000; i++) {          executor.submit(() -> {              Thread.sleep(Duration.ofSeconds(1));              return i;          }); // 万级并发无压力      }  }  AI生成项目Valhalla值类型:__value class Point {      int x;      int y;  } // 栈分配替代对象,性能提升5倍  AI生成项目六、开发者进阶路线图1. 职业赛道选择方向    技术栈    薪资范围(3-5年)企业级开发    Spring Cloud + Alibaba    30-50万Android开发    Jetpack Compose + KMM    25-40万大数据开发    Flink + Hadoop    35-60万云原生架构    Quarkus + Kubernetes    50-80万2. 知识体系图谱graph LR  A[Java基础] --> B[JVM原理]  A --> C[并发编程]  B --> D[性能调优]  C --> E[分布式系统]  D --> F[云原生架构]  E --> G[领域驱动设计]  AI生成项目结语:永不落幕的帝国当Oracle的律师团为版权奔走时,当Rustaceans高呼内存安全时,当Go开发者炫耀协程效率时——Java依然运行在:全球45亿台Android设备华尔街78% 的交易系统阿里云上百万台服务器Java的终极竞争力:用严谨的类型系统构建数字世界的秩序用虚拟机的智慧平衡效率与跨平台用二十年的生态沉淀驾驭技术变革浪潮正如James Gosling在Java诞生时的预言:“我们不是在创造语言,而是在构建数字文明的基石。” 从智能卡到航天器,从物联网到元宇宙,Java帝国仍在拓展它的疆域。————————————————原文链接:https://blog.csdn.net/2302_77626561/article/details/149091813
  • [技术干货] java常见基本算法
    一.查找算法(1)基本查找基本查找就是一个一个遍历数组,直到找到所需元素,效率最低(2)二分/折半查找前提条件:数组中的数据是有序的核心逻辑:每次去除一半的查找范围优点:效率比基本查找要高实现:1.max是数组最大索引2.min是数组最小索引3.mid是二者的整除所取的整数,拿它来跟目标值作比较4.如果mid索引的整数比目标值大,那么min=mid+15.如果比目标值小,那么max=mid-1如果目标值不在数组中,返回索引-1,并且这时候min会大于max(因为当要找的数据大于所有数据的时候,min最后会比max大;如果要找的数据小于所有数据,max最后比min小;如果在数据之中找不到数据,min最后也会比max大;但是一般要查找的的数据都在数组里面)public class BinarySearch {    public static void main(String[] args) {        int[] arr = {1, 22, 33, 44, 55, 6666, 77777, 88888, 99999, 100000};        System.out.println(binarySearch(arr, 22));    }     public static int binarySearch(int[] arr, int target) {        int min=0;        int max=arr.length-1;        while (true){            int mid=(min+max)/2;            if (min>max){                return -1;            }            //mid索引在目标左边            if (arr[mid]<target){                min=mid+1;            }            //mid索引在目标右边            if (arr[mid]>target){                max=mid-1;            }            //mid索引在目标上            if (arr[mid]==target){                return mid;            }        }    }}AI生成项目java运行(3)分块查找用于每个数据之间没有顺序,但是每个区域间有顺序的情况:例如有个数组:{1,3,2,4,5,66,88,77,999,100,101}(会发现每个数据之间确实是没有顺序可言,但是可以把这几个数据分区域,第一个区域的最大值为5,第二个区域最大值为88,第三个区域最大值为101,而且第二个区域的所有数据都大于第一个区域,第三个区域的数据也大于第二个区域的最大数值,那么就可以利用分块查找了。)1.既然要分区,那我们可以先创建一个区域类:Blockclass Block{int maxIndex;int minIndex;int maxNumber;还有构造方法和get,set方法等等}2.创建对象,把每个区域信息填进去,一般创建数据个数的平方根个对象3.创建存放Block对象的数组4.创建方法去把目标值的所在区域找到5.创建方法在区域中找目标值,并返回索引实例:public class BlockSearch {    public static void main(String[] args) {     int[] arr = {1, 22, 34, 33, 55, 66, 78, 68, 9999, 10000};     Block b1= new Block(3, 0, 34);     Block b2= new Block(7, 4, 78);     Block b3= new Block(9, 8, 10000);     Block[] blockArr = {b1, b2, b3};     int target = 33;     int index = finalSearch(blockArr, target, arr);     System.out.println("index = " + index);    }    //先找区域    public static int blockSearch(Block[] arr,int target){        for(int i=0;i<arr.length;i++){            if(arr[i].maxNumber>=target){                return i;            }        }        return -1;    }    //再找位置    public static int finalSearch(Block[] arr1,int target,int[] arr2){        int index = blockSearch(arr1,target);        if (index==-1){            return -1;        }         for(int i=arr1[index].minIndex;i<=arr1[index].maxIndex;i++){            if (arr2[i]==target){                return i;            }        }        return -1;    } }//Blockclass Block{    int maxIndex;    int minIndex;    int maxNumber;     public Block(int maxIndex, int minIndex, int maxNumber) {        this.maxIndex = maxIndex;        this.minIndex = minIndex;        this.maxNumber = maxNumber;    } }AI生成项目java运行结果: 二.排序算法(1)冒泡排序相邻的元素两两比较,先按照要求把最大或者最小的先放到右边:——外循环是看它要执行的次数,也就是数据数量-1次——内循环的-1是为了不让i+1(最后一个数据)超出索引——内循环的-i是为了效率,因为每次执行完,就可以减少最后一个数据,也就是减少一次public class BubbleSort {     public static void main(String[] args) {     int[] arr={2,5,8,9,1,5,6,4,7,8};     for (int i=0;i<arr.length-1;i++){         for (int j=0;j<arr.length-i-1;j++){             if(arr[j]>arr[j+1]){                 int temp=arr[j];                 arr[j]=arr[j+1];                 arr[j+1]=temp;             }         }     }        System.out.println(Arrays.toString(arr));    }}AI生成项目java运行结果:  (2)选择排序 拿第一个元素跟后面的元素比,符合要求的放到一个,继续循环,让第二个元素再依次跟后面比,让符合规则的放到第二个,然后一个一个把最大的挑出来(从大到小排序)。以此类推,跟冒泡不同的是,把确定的元素放到左边——i是用来做比较的下标——j是比i大1,然后逐渐递增的下标的元素下面是排序从大到小:public class SelectionSort {    public static void main(String[] args) {        int[] arr={2,5,8,9,1,5,6,4,7,8};        //i是要拿来做比较的元素下标        for (int i=0;i<arr.length-1;i++){            for(int j=i+1;j<arr.length;j++){                if(arr[j]>arr[i]){                        int temp=arr[i];                        arr[i]=arr[j];                        arr[j]=temp;                }            }        }        System.out.println(Arrays.toString(arr));}}AI生成项目java运行结果:  (3) 插入排序将0索引到n索引的数据看作是有序的,把索引为n+1一直到最后一个索引当作是无序的,遍历无序的数组,将遍历到的数据插入到有序序列中的合适位置,如遇到相同数据,排在后面,这个方法就是插入排序public class InsertSort {    public static void main(String[] args) {         //将下面的数组按照从大到小排列                int[] arr={5,8,3,2,17,19,15,16,90};                //找无序的开始索引                int startIndex=-1;                for(int i=0;i<arr.length;i++){                    if(arr[i+1]>arr[i]){                        startIndex=i+1;                        break;                    }                }                //从无序开始索引开始插入排序,反向遍历,一个一个对比插入                for(int i=startIndex;i<arr.length;i++){                    int j=i;                    while(j>0&&arr[j]>arr[j-1]){                        int temp=arr[j];                        arr[j]=arr[j-1];                        arr[j-1]=temp;                        j--;                    }                }                //看结果                System.out.println(Arrays.toString(arr));    }}AI生成项目java运行结果: 三.递归算法 递归指的是方法中调用方法自己本身的现象。递归的注意点:一定要有出口,否则会内存溢出。所谓出口就是调用到第几次就不再调用自己了。递归的作用:大事化小,小事化了书写递归的两个核心:1.出口2.找规则如何把大问题转换成规模较小的小问题3.再次调用方法的时候,要更靠近出口实例:求1——100的和public class Recursion {    public static void main(String[] args) {        System.out.println(getSum(100));    }    public static int getSum(int number){        if(number == 1){            return 1;        }        return number + getSum(number - 1);    }}AI生成项目java运行//其实就是把1+100转换成:100+1—99的和99+1—98的和98+1—97的和。。。2+1的和1就是出口————————————————原文链接:https://blog.csdn.net/2403_86850076/article/details/146273968
  • [技术干货] java对接第三方接口的三种实现方式详解
    Java 对接第三方接口的三种实现方式详解在 Java 开发中,调用第三方接口是常见的需求。无论是与外部系统进行数据交互,还是集成第三方服务(如支付、地图、短信等),都需要通过 HTTP 请求与目标接口进行通信。本文将详细介绍三种主流的实现方式:HttpURLConnection、Apache HttpClient 和 Spring RestTemplate,并提供代码示例和注意事项,帮助开发者选择最适合的方案。一、使用 HttpURLConnection(Java 原生方式)1.1 特点与适用场景HttpURLConnection 是 Java 自带的 HTTP 客户端工具,无需引入额外依赖,适合简单的 HTTP 请求场景。其优势在于轻量级和无需配置,但功能相对基础,复杂请求(如设置请求头、处理 JSON 数据)需要手动实现。1.2 示例代码以下是一个发送 POST 请求并处理响应的示例:import java.io.*;import java.net.HttpURLConnection;import java.net.URL;public class HttpURLConnectionExample {    public static void main(String[] args) {        try {            // 创建URL对象            URL url = new URL("https://api.example.com/data");            HttpURLConnection conn = (HttpURLConnection) url.openConnection();                        // 设置请求方法为POST            conn.setRequestMethod("POST");            conn.setRequestProperty("Content-Type", "application/json; utf-8");            conn.setRequestProperty("Accept", "application/json");            conn.setDoOutput(true); // 允许输出流(写入请求体)                        // 构建JSON请求体            String jsonInputString = "{\"name\": \"John\", \"age\": 30}";                        // 写入请求体            try (OutputStream os = conn.getOutputStream()) {                byte[] input = jsonInputString.getBytes("utf-8");                os.write(input, 0, input.length);            }                        // 读取响应            try (BufferedReader br = new BufferedReader(                     new InputStreamReader(conn.getInputStream(), "utf-8"))) {                StringBuilder response = new StringBuilder();                String responseLine;                while ((responseLine = br.readLine()) != null) {                    response.append(responseLine.trim());                }                System.out.println("响应结果: " + response.toString());            }        } catch (Exception e) {            e.printStackTrace();        }    }}AI生成项目java运行1.3 优点与缺点优点:无需依赖,简单易用,适合小型项目或快速原型开发。缺点:功能有限,手动处理请求头和响应较繁琐,不支持异步请求。1.4 注意事项需要手动管理连接超时和读取超时:conn.setConnectTimeout(5000);  // 连接超时时间conn.setReadTimeout(5000);     // 读取超时时间AI生成项目java运行对于需要认证的接口,需手动设置请求头:String auth = "Basic " + Base64.getEncoder().encodeToString("username:password".getBytes());conn.setRequestProperty("Authorization", auth);AI生成项目java运行二、使用 Apache HttpClient(功能强大,灵活可控)2.1 特点与适用场景Apache HttpClient 是一个功能强大的 HTTP 客户端库,支持更复杂的请求场景(如文件上传、身份验证、代理配置等)。它比 HttpURLConnection 更灵活,但需要引入额外依赖。2.2 示例代码以下是一个使用 Apache HttpClient 发送 GET 请求的示例:import org.apache.http.HttpEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;public class HttpClientExample {    public static void main(String[] args) {        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {            // 创建GET请求            HttpGet httpGet = new HttpGet("https://api.example.com/data");                        // 设置请求头            httpGet.setHeader("Content-Type", "application/json");            httpGet.setHeader("Accept", "application/json");                        // 执行请求            CloseableHttpResponse response = httpClient.execute(httpGet);                        // 获取响应实体            HttpEntity entity = response.getEntity();            if (entity != null) {                String result = EntityUtils.toString(entity);                System.out.println("响应结果: " + result);            }        } catch (Exception e) {            e.printStackTrace();        }    }}AI生成项目java运行2.3 优点与缺点优点:功能全面,支持高级特性(如连接池、重试机制、超时配置)。缺点:需要引入依赖(如 httpclient 和 httpcore),代码略显冗长。2.4 注意事项依赖配置(Maven):<dependency>    <groupId>org.apache.httpcomponents</groupId>    <artifactId>httpclient</artifactId>    <version>4.5.13</version></dependency>AI生成项目xml自定义配置(如超时时间):RequestConfig config = RequestConfig.custom()    .setConnectTimeout(5000)    .setSocketTimeout(5000)    .build();CloseableHttpClient httpClient = HttpClients.custom()    .setDefaultRequestConfig(config)    .build();AI生成项目java运行三、使用 Spring RestTemplate(Spring 生态首选)3.1 特点与适用场景RestTemplate 是 Spring 框架提供的 HTTP 客户端工具,专为 RESTful 接口设计。它简化了请求和响应的处理,支持自动序列化/反序列化(如 JSON、XML),是 Spring 项目中的首选方案。3.2 示例代码以下是一个使用 RestTemplate 发送 GET 和 POST 请求的示例:3.2.1 发送 GET 请求import org.springframework.web.client.RestTemplate;public class RestTemplateExample {    public static void main(String[] args) {        RestTemplate restTemplate = new RestTemplate();        String url = "https://api.example.com/data";                // 发送GET请求并获取响应        String response = restTemplate.getForObject(url, String.class);        System.out.println("GET响应结果: " + response);    }}AI生成项目java运行3.2.2 发送 POST 请求import org.springframework.http.HttpEntity;import org.springframework.http.HttpHeaders;import org.springframework.http.MediaType;import org.springframework.web.client.RestTemplate;public class RestTemplatePostExample {    public static void main(String[] args) {        RestTemplate restTemplate = new RestTemplate();        String url = "https://api.example.com/data";                // 构建请求头        HttpHeaders headers = new HttpHeaders();        headers.setContentType(MediaType.APPLICATION_JSON);                // 构建请求体        String requestBody = "{\"name\": \"John\", \"age\": 30}";        HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);                // 发送POST请求并获取响应        String response = restTemplate.postForObject(url, requestEntity, String.class);        System.out.println("POST响应结果: " + response);    }}AI生成项目java运行3.3 优点与缺点优点:与 Spring 框架无缝集成,支持自动序列化/反序列化,代码简洁。缺点:需要依赖 Spring 框架,不适合非 Spring 项目。3.4 注意事项异常处理:RestTemplate 抛出的 RestClientException 需要捕获处理:try {    String response = restTemplate.getForObject(url, String.class);} catch (RestClientException e) {    e.printStackTrace();}AI生成项目java运行配置超时时间:RestTemplate restTemplate = new RestTemplate();restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());((HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory())    .setConnectTimeout(5000);AI生成项目java运行四、三种方式的对比与选择建议实现方式    适用场景    优点    缺点HttpURLConnection    简单的小型项目或快速原型开发    无需依赖,轻量级    功能有限,手动处理较繁琐Apache HttpClient    复杂请求(如认证、文件上传等)    功能全面,灵活可控    需要引入依赖,代码冗长Spring RestTemplate    Spring 项目中的 RESTful 接口交互    与 Spring 无缝集成,代码简洁    依赖 Spring 框架,不适合非 Spring 项目选择建议简单需求:优先使用 HttpURLConnection。复杂需求:选择 Apache HttpClient,尤其是需要高级功能(如连接池、重试)时。Spring 项目:推荐使用 RestTemplate,充分利用 Spring 的生态优势。五、最佳实践统一封装工具类:无论选择哪种方式,建议将 HTTP 请求逻辑封装为工具类,便于复用和维护。异常处理:所有 HTTP 请求都应捕获异常(如网络超时、认证失败),并记录日志。性能优化:使用连接池(如 Apache HttpClient 的 PoolingHttpClientConnectionManager)。启用 GZIP 压缩(Accept-Encoding: gzip)。安全性:避免硬编码敏感信息(如 API Key),使用环境变量或配置中心。验证响应数据的合法性(如 JSON 格式校验)。六、总结Java 对接第三方接口的三种主流方式各有特点:HttpURLConnection 适合轻量级需求,但功能有限;Apache HttpClient 提供了更强大的功能,适合复杂场景;Spring RestTemplate 在 Spring 项目中表现优异,代码简洁高效。开发者可根据项目需求和框架选择合适的实现方式,并结合最佳实践优化性能和可维护性。随着 Spring 6 和 Java 11+ 的普及,未来可能会更多采用 HttpClient(Java 11 新特性)或 WebClient(Spring WebFlux),但目前这三种方法仍是主流选择。————————————————原文链接:https://blog.csdn.net/huayula/article/details/148243340
  • [技术干货] Java 排序
    排序稳定的排序:排序之前和排序之后它们俩的相对顺序没有发生变化内部排序:在内存上的排序外部排序:需要借助磁盘等外部空间进行的排序插入排序思想:假设这组数据的第一个元素是有序的,从第二个元素和前面的元素进行比较,找到合适的位置进行插入  // 插入排序    public static void insertSort(int[] array){        for(int i = 1;i < array.length;i++){            int j = i - 1;            int tmp = array[i];            for(;j >= 0;j--){                if(array[j] > tmp){                    array[j+1] = array[j];                }else{                    break;                }            }             array[j + 1] = tmp;        }    }AI生成项目java运行 分析时间复杂度:O(N^2)最坏情况:O(N^2)最好情况:有序的数组,O(N)数据越有序,排序越快适用于待排序数组基本上趋于有序了,时间复杂度趋于O(N)空间复杂度:O(1)稳定性:是一个稳定的排序例子:5 5 7稳定的排序可以改成不稳定的排序,但是不稳定的排序不能改成稳定的排序希尔排序对直接插入排序进行了优化,如果是 5 4 3 2 1 会退化为O(N^2)分组:分完组后,每组都采用直接插入排序中间隔一些元素进行分组的好处:比较大的元素都往后走了,比较小的元素都往前走了缩小增量到最后会把整体看成是一组,5 3 1 组,前面的5 3 都是预排序,真正的排序是最后的一组缩小增量的目的:为了让排序更接近O(N),为了让排序更快   // 希尔排序    public static void shellSort(int[] array){        int gap = array.length;        while(gap > 1){            gap /= 2;            shell(array,gap);        }    }     // 对每组进行插入排序    public static void shell(int[] array,int gap){        for(int i = gap;i < array.length;i++){            int j = i - gap;            int tmp = array[i];            for(;j >= 0;j -= gap){                if(array[j] > tmp){                    array[j+gap] = array[j];                }else{                    break;                }            }             array[j + gap] = tmp;        }    }AI生成项目java运行 分析时间复杂度:O(N^1.3 - N ^ 1.5),时间复杂度不好计算空间复杂度:O(1)稳定性:不稳定的排序检测排序速度: public static void testInsert(int[] array){        long startTime = System.currentTimeMillis();        int[] tmp = Arrays.copyOf(array,array.length);        Sort.insertSort(tmp);         long endTime = System.currentTimeMillis();        System.out.println(" " + (endTime - startTime));    }     public static void testShell(int[] array){        long startTime = System.currentTimeMillis();        int[] tmp = Arrays.copyOf(array,array.length);        Sort.shellSort(tmp);         long endTime = System.currentTimeMillis();        System.out.println(" " + (endTime - startTime));    }     // 逆序初始化    public static void initOrder(int[] array){        for(int i = 0;i < array.length;i++){            array[i] = array.length - i;        }    }    public static void main(String[] args) {        int[] arr = new int[10000];        initOrder(arr);        testInsert(arr);        testShell(arr);    }AI生成项目java运行 选择排序在当前i下标对应的值的后面,找到后面最小的值和i下标对应的值交换 // 交换    public static void swap(int i, int j, int[] array){        int tmp = array[i];        array[i] = array[j];        array[j] = tmp;    }     // 选择排序    public static void selectSort(int[] array){        // 在i下标的后面找到比i下标对应的值的最小值,然后交换        int n = array.length;        for(int i = 0;i < n;i++){            int minIndex = i;            for(int j = i + 1;j < n;j++){                if(array[j] < array[i]){                   if(array[j] < array[minIndex]){                       minIndex = j;                   }                }            }            swap(i,minIndex,array);        }    }AI生成项目java运行 双向选择排序时间复杂度还是O(N^2)左边找最大的,右边找最小的,和第一个数和最后一个数交换 存在缺陷,maxIndex下标可能是在0下标是最大的,0下标会和最小值小标的值交换,那么0下标就不是最大值下标,应该更新为maxIndex = minIndex  public static void selectSort2(int[] array){        // 在i下标的后面找到比i下标对应的值的最小值,然后交换        int left = 0;        int right = array.length - 1;        while(left < right){            int minIndex = left;            int maxIndex = left;            for(int i = left + 1;i <= right;i++){                if(array[i] < array[minIndex]){                    minIndex = i;                }                if(array[i] > array[maxIndex]){                    maxIndex = i;                }            }            swap(left,minIndex,array);            // 第一个数是最大的数,防止最小的下标和第一个数换了,最大值就在minIndex的位置了            if(maxIndex == left){                maxIndex = minIndex;            }            swap(right,maxIndex,array);            left++;            right--;        }    }AI生成项目java运行 分析时间复杂度:O(N^2)空间复杂度:O(1)稳定性:不稳定的排序堆排序// 堆排序    private static void shifDown(int[] array,int parent,int len){        int child = 2 * parent + 1;        while(child < len){            if(child + 1 < len && array[child] < array[child + 1]){                child++;            }             if(array[child] > array[parent]){                swap(child,parent,array);                parent = child;                child = 2 * parent + 1;            }else{                break;            }        }    }     private static void createHeap(int[] array){        // 建立大根堆        for(int parent = (array.length - 1 - 1) / 2;parent >= 0;parent--) {            shifDown(array, parent, array.length);        }    }     public static void heapSort(int[] array){        createHeap(array);        int end = array.length - 1;        while(end > 0){            swap(end,0,array);            shifDown(array,0,end);            end--;        }    }AI生成项目java运行 分析时间复杂度:O(N * logN)空间复杂度:O(1)稳定性:不稳定的排序冒泡排序// 冒泡排序    public static void bubbleSort(int[] array){        // i是趟数        for(int i = 0;i < array.length;i++){            // j是比较的大小的            boolean flag = true;            for(int j = 0;j < array.length - i - 1;j++){                if(array[j] > array[j + 1]){                    swap(j,j + 1,array);                    flag = false;                }            }            if(flag) {                break;            }        }    }AI生成项目java运行 分析时间复杂度:O(N ^ 2)空间复杂度:O(1)稳定性:稳定的排序快速排序霍尔法根据二叉树进行递归划分  // 快速排序    public static void quickSort(int[] array){        quick(array,0,array.length - 1);    }      private static void quick(int[] array,int start,int end){        if(start >= end){            return;        }         int prvot = partitionHoare(array,start,end);        quick(array,start,prvot - 1);        quick(array,prvot + 1,end);    }     private static int partitionHoare(int[] array,int left,int right){        // 基准元素        int tmp = array[left];        // 记录第一个基准下标        int i = left;        while(left < right) {            // 必须先找先右再左            // 找小            while (left < right && array[right] >= tmp) {                right--;            }            // 找大            while (left < right && array[left] <= tmp) {                left++;            }            swap(left, right, array);        }        swap(i,left,array);         return left;    }AI生成项目java运行 为什么有等于号?没有等于号,会死循环,比如两端都是6  为什么先从右边开始,不先从左边开始?先走左边的话,先遇到大的停下来,如果相遇的话,那么相遇位置的值就是大于基准元素的,这时候交换的话,6的左边有一个数比6大 分析时间复杂度:最好情况下:O(N * logN)每层都有N个节点,高度为logN,需要每个节点都遍历到,N * logN次遍历最坏情况下:O(N^2),有序/逆序,单分支的树空间复杂度:最好情况下:O(logN)最坏情况下:O(N),有序/逆序,单分支的树递归左边再递归右边,递归右边左边没有释放稳定性:不稳定的排序挖坑法找基准先走右边再走左边,以第一个元素为基准,并且拿出基准元素,基准元素的位置就是坑,如果右边找到比基准小的,把它放入坑中,左边找到比基准元素大的放到坑中,最后两者相遇,把基准元素放入其中// 挖坑法    private static int partitionHole(int[] array,int left,int right){        // 基准元素        int tmp = array[left];        while(left < right) {            // 必须先找先右再左            // 找小            while (left < right && array[right] >= tmp) {                right--;            }            array[left] = array[right];            // 找大            while (left < right && array[left] <= tmp) {                left++;            }            array[right] = array[left];        }        array[left] = tmp;         return left;    }AI生成项目java运行 前后指针法如果cur比基准元素小并且cur下标的值和prev下标的值不相等,  // 前后指针法    public static int partition(int[] array,int left,int right){        int prev = left;        int cur = left + 1;        while(cur <= right){            while(array[cur] < array[left] && array[++prev] != array[cur]){                swap(cur,prev,array);            }            cur++;        }        swap(prev,left,array);         return prev;    }AI生成项目java运行 题目优先试挖坑法,其次是Hoare,最后是前后指针法A   快排的优化均匀的分割数组让递归的次数变少三数取中法三数取中法:left,right,mid三个下标中的中间值和第一个数交换位置,然后右边找比基准元素小的值,左边找比基准元素大的值规定array[mid] <= array[left] <= array[right] // 三数取中法,求中位数的下标    private static int middleNum(int[] array,int left,int right){        int mid = (left + right) / 2;        if(array[left] < array[right]){            // 1. x < 3 < 9            // 2. 3 < 9 < x            // 3. 3 < x < 9            if(array[mid] < array[left]){                return left;            }            else if(array[mid] > array[right]){                return right;            }            else{                return mid;            }        }else{            // 9 > 3 == left > right            // 1. x > left > right            if(array[mid] > array[left]){                return left;            }else if(array[right] > array[mid]){                // 2. left > right > x                return right;            }else{                // 3. left > x > right                return mid;            }        }    }     private static void quick(int[] array,int start,int end){        if(start >= end){            return;        }         // 1 2 3 4 5 6 7        // 中间值的下标和第一个数交换,作为新的基准元素        int index = middleNum(array,start,end);        swap(index,start,array);        // 4 2 3 1 5 6 7        // 为了让左右分布更均匀,降低树的高度,减少递归的次数         int prvot = partition(array,start,end);        quick(array,start,prvot - 1);        quick(array,prvot + 1,end);    }AI生成项目java运行 只剩最后几层时,使用插入排序进行优化,降低递归次数,可以使用插入排序是因为前面递归成有序的序列了 public static void insertSort(int[] array,int left,int right){        for(int i = left + 1;i <= right;i++){            int j = i - 1;            int tmp = array[i];            for(;j >= left;j--){                if(array[j] > tmp){                    array[j+1] = array[j];                }else{                    break;                }            }             array[j + 1] = tmp;        }    }     private static void quick(int[] array,int start,int end){        if(start >= end){            return;        }         if(end - start + 1 <= 15){            // 减少递归的次数            // 因为最后几层节点数最多,递归次数也多            insertSort(array,start,end);            return;        }         // 1 2 3 4 5 6 7        // 中间值的下标和第一个数交换,作为新的基准元素        int index = middleNum(array,start,end);        swap(index,start,array);        // 4 2 3 1 5 6 7        // 为了让左右分布更均匀,降低树的高度,减少递归的次数         int prvot = partition(array,start,end);        quick(array,start,prvot - 1);        quick(array,prvot + 1,end);    }AI生成项目java运行 非递归实现快排找基准里面会进行交换元素,进行排序,外面就进行找到左右下标位置   // 非递归实现快速排序    public static void quickSortNor(int[] array){        int start = 0;        int end = array.length - 1;        Stack<Integer> stack = new Stack<>();         int pivot = partitionHoare(array,start,end);        if(pivot - 1 > start){            // 左边有两个元素            stack.push(start);            stack.push(pivot - 1);        }        else if(pivot + 1 < end){            // 右边至少有两个元素            stack.push(pivot + 1);            stack.push(end);        }        // 找基准里面会互换元素进行排序        while(!stack.empty()){            end = stack.pop();            start = stack.pop();             pivot = partitionHoare(array,start,end);            if(pivot - 1 > start){                // 左边有两个元素                stack.push(start);                stack.push(pivot - 1);            }            else if(pivot + 1 < end){                // 右边至少有两个元素                stack.push(pivot + 1);                stack.push(end);            }        }AI生成项目java运行 归并排序分解:根据mid进行分解 合并:第一个有序段和第二个有序段,比较大小放入一个新的数组中  // 归并排序    public static void mergeSort(int[] array){        merge(array,0,array.length - 1);    }     private static void merge(int[] array,int start,int end){        if(start >= end){            return;        }         int mid = (start + end) / 2;        // 分解        merge(array,start,mid);        merge(array,mid + 1,end);        // 合并        mergeHeB(array,start,mid,end);    }     private static void mergeHeB(int[] array, int left, int mid, int right) {        int s1 = left;        int e1 = mid;        int s2 = mid + 1;        int e2 = right;         // 新数组的下标        int k = 0;        int[] tmpArray = new int[right - left + 1];         while(s1 <= e1 && s2 <= e2){            if(array[s1] < array[s2]){                tmpArray[k++] = array[s1++];            }else{                tmpArray[k++] = array[s2++];            }        }         while(s1 <= e1){            tmpArray[k++] = array[s1++];        }         while(s2 <= e2){            tmpArray[k++] = array[s2++];        }         // left是因为有左边还有右边的数组        // tmpArray是临时数组,会销毁的,需要拷贝回原来的数组        for(int i = 0;i < tmpArray.length;i++){            array[i + left] = tmpArray[i];        }    }AI生成项目java运行 分析时间复杂度:O(N * logN)空间复杂度:O(N),因为每次合并数组的时候要开O(N)大小的额外空间稳定性:稳定的排序非递归实现归并排序先一个一个有序,两个两个有序,四个四个有序,八个八个有序…gap = 1left = imid = left + gap - 1right = mid + gapgap *= 2每组合并完,最终就有序了  // 非递归实现归并排序    public static void mergeSortNor(int[] array){        // gap表示每组的数据个数        int gap = 1;        while(gap < array.length){             for(int i = 0;i < array.length;i = i + 2 * gap){                int left = i;                 // mid和right可能会越界                // 比如只有5个元素                // 分解右边一半的时候                // l = i mid = 5 r = 7                int mid = left + gap - 1;                int right = mid + gap;                 if(mid >= array.length){                    mid = array.length - 1;                }                if(right >= array.length){                    right = array.length - 1;                }                  mergeHeB(array,left,mid,right);            }             gap *= 2;        }    }AI生成项目java运行 海量数据的排序使用归并排序进行外部排序,如果内存中不够空间的话  非比较的排序计数排序通过统计每次数字出现的次数,最后按照顺序从小到大输出这些数字适用的场景:指定范围内的数据  // 计数排序,指定范围内的数据进行排序// O(Max(N,范围))    public static void countSort(int[] array){        int minVal = array[0];        int maxVal = array[0];                // O(N)        for(int i = 0;i < array.length;i++){            if(minVal > array[i]){                minVal = array[i];            }            if(maxVal < array[i]){                maxVal = array[i];            }        }         int len = maxVal - minVal + 1;        int[] count = new int[len];        // O(N)        for(int i = 0;i < array.length;i++){            count[array[i] - minVal]++;        }                               // 写回array数组中        // O(范围)        // 因为count数组集中于一个数字,那么也是O(范围)        // 如果count数组不集中于一个数子,也是N倍的范围        // 也是O(范围)        int k = 0;        for(int i = 0;i < count.length;i++){            while(count[i] > 0){                array[k++] = i + minVal;                count[i]--;            }        }    }AI生成项目java运行 分析时间复杂度:O(Max(N,范围))空间复杂度:O(范围)稳定性:稳定的排序基数排序开10个大小的空间,分别依次比较个位大小,十位大小和百位大小,最终达到有序,每个空间都是一个队列  桶排序先把数字放入一个范围的区间内进行排序,排好序依次拿出来,最终就有序了———————————————— 原文链接:https://blog.csdn.net/2301_79722622/article/details/149584998
总条数:737 到第
上滑加载中