lengjingbuliaoyidian
文章8
标签6
分类5
注解和反射

注解和反射

注解的含义:

  • Annotation的作用:不是程序本身,可以对程序做出解释;可以被其他程序(比如:编译器等)读取,注解可以用来修饰类、方法、变量、参数等,可以用来检查代码质量、约束开发者的编程风格、提高代码的可读性和可维护性(检查、约束)。
  • 格式:以“@注释名”在代码中存在,还可以在后面添加一些参数值
  • 使用范围:可以附加在package、class、method、filed等上面,相当于给他们添加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问

内置注解:

  • @Override: 定义在java.lang.Override中,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的另外一个方法声明。

  • @Deprecated:定义在Java.lang.Deprecated中,此注解可以用于修饰方法,属性,类,表示不鼓励程序员使用这样的元素(通常事因为它很危险或者存在更好的选择),但是可以此方法可以运行。

  • @SuppressWarnings:定义在Java.lang.SuppressWarnings中,用来抑制编译时的警告信息(后面要有参数才能正确使用),例如:

      @SuppressWarnings("all");
      @SuppressWarnings("unchecked");
      @SuppressWarnings("unchecked","deprecation");
    

元注解:

  • 作用:负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明。

  • 类型:@Target ,@Retention, @Documented ,@Inherited (这些类型和他们所支持的类在Java。狼。annotation包中可以找到。)

          @Target:用于描述注解的使用范围(即被描述的代码可以用在那些地方)
    
          @Retention:表示需要在说明级别保存该注解信息,用于描述注解的生命周期(Runtime>class>source)
          @Document:说明该注解将被包含在Javadoc 中
          @Inherited:说明子类可以继承父类中的该注解
    
  • 格式:
    @Target(value ={ElementType.METHOD,ElementType.TYPE})
    @Retention(value=RetentionPolicy.RUNTIME)

自定义注解:

  • 用法:使用@interface自定义注解时,自动继承java.lang.annotation.Annotation接口
  • 归纳:
  1. @interface用来声明一个注解,格式:public @interface 注解名{定义内容}
  2. 其中的每一个方法实际上是声明一个配置参数
  3. 方法的名称就是参数的名称
  4. 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
  5. 可以通过default来声明参数的默认值
  6. 如果只有一个参数成员,一般参数名为value
  7. 注解元素必须要由值,定义注解元素时,经常使用空字符串0作为默认值
  • 代码如下:
    package com.st.test;

    //测试自定义注解

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    public class Test03 {
    //注解可以显示赋值,如果没有默认值(default 数值),我们就必须给注解赋值
    @MyAnnotation2(name=”李明” ,schools ={“湖南大学”,”清华大学”} )
    public void test(){}

    //如果自定义的注解只有一个值的时候可以使用value表示,在方法注解表示的时候可以直接省略value
    @MyAnnotation3(“李明”)
    public void test2(){}
    }

    @Target(value={ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation2{
    //注解的参数:参数类型+参数名();
    String name() ; //赋值为李明
    int age() default 0;
    int id() default -1; //如果默认值为-1,则代表不存在

    String[] schools();
    }
    @Target(value={ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation3{
    String value();
    }

java反射

  • 特点:Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得此类似动态语言的特性。Java的动态性让在编程的时候更加灵活!

  • 反射:被视为是动态语言的关键,反射机制允许程序在执行期间借助于’Reflection API’获取任何类的内部信息,并直接操作任意对象的内部属性及方法

           Class c=Class.forName("java.lang.String")
    

    加载完类之后,在堆内存的方法中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,通过镜子看到类的结构,所以形象的称为:反射

  • 对比:

  1. 正常的方式:先引用需要的“包类”名称——> 通过new实例化——> 取得实例化对象
  2. 反射方式: 实例化对象——> getClass()方法——> 得到完整的“包类”名称。
  • 反射机制在运行时提供的功能:
  1. 判断任意一个对象所属的类
  2. 可构造任意一个类的对象
  3. 可判断任意一个类所具有的成员变量和方法
  4. 可获取泛型信息
  5. 可调用任意一个对象的成员变量和方法
  6. 可处理注解
  7. 生成动态代理(AOP)
  • 创建对象的五种方式: new、newInstance、clone、反序列化、反射。

  • 代码:

        package com.st.reflection;
        public class Test002 {
        public static void main(String[] args) throws ClassNotFoundException {
      //通过反射获取类的Class对象
     Class c1=Class.forName("com.st.reflection.User");
      System.out.println(c1);
    
     //查看他们是不是属于同一个类,就可以查看他们的哈希一不一样
      Class c2=Class.forName("com.st.reflection.User");
      Class c3=Class.forName("com.st.reflection.User");
      Class c4=Class.forName("com.st.reflection.User");
     //结果是一样的
      //一个类在内存中只有一个Class对象,一个类被加载之后,类的整个结构都会被封装在Class对象中
      System.out.println(c2.hashCode());
      System.out.println(c3.hashCode());
      System.out.println(c4.hashCode());
       }
    }
      
      //实体类:pojo entity
      class User{
       private String name;
       private int id;
       private  int age;
    
      public User(){
    
       }
      public User(String name, int id, int age) {
      this.name = name;
      this.id = id;
      this.age = age;
      }
    
      public String getName() {
          return name;
      }
    
      public int getId() {
          return id;
      }
    
      public int getAge() {
          return age;
      }
    
      public void setName(String name) {
          this.name = name;
      }
    
      public void setId(int id) {
          this.id = id;
      }
    
      public void setAge(int age) {
          this.age = age;
      }
    
      @Override
      public String toString() {
          return "User{" +
                  "name='" + name + '\'' +
                  ", id=" + id +
                  ", age=" + age +
                  '}';
      }
    }
    

获取Class类的实例

  • 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高

              Class clazz =Person.class;
    
  • 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

              Class clazz=Class.forName("demo01.Student");
    
  • 已知某个类的实例,调用实例的getClass()方法获取Class对象

              Classclazz=person.getClass();
    
  • 内置基本数据类型可以用类名.TYPE

  • 还可以利用ClassLoader

  • 综合示例:

              package com.st.reflection;
              //测试Class 类的创建方式有哪些
              public class Test003 {
                  public static void main(String[] args) throws ClassNotFoundException {
                      Person person=new Student();
                      System.out.println("这个人是:"+person.name);
    
                      //方式一:通过对象获得
                      Class c1=person.getClass();
                      System.out.println(c1.hashCode());
    
                      //方式二:通过forname获得
                      Class c2 = Class.forName("com.st.reflection.Student");
                      System.out.println(c2.hashCode());
    
                      //方式三:通过类名。class 获得
                      Class<Student> c3 = Student.class;
                      System.out.println(c3.hashCode());
    
                      //方式四:基本内置类型的包装类都有一个Type属性
                      Class c4=Integer.TYPE;
                      System.out.println(c4.hashCode());
    
                      //获取父类类型
                      Class c5=c1.getSuperclass();
                      System.out.println(c5);
    
    
                  }
              }
    
              class Person{
                  public String name;
                  public Person(){
    
                  }
    
                  public Person(String name) {
                      this.name = name;
                  }
    
                  @Override
                  public String toString() {
                      return "Person{" +
                              "name='" + name + '\'' +
                              '}';
                  }
              }
    
              class Student extends Person{
                  public Student(){
                      this.name="学生";
                  }
              }
              class Teacher extends Person{
                  public Teacher() {
                      this.name="老师";
                  }
              }
    

哪些类型可以有Class对象

  1. class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
  2. interface:接口
  3. []:数组(多维)
  4. enum:枚举
  5. annotation:注解@interface
  6. primitive type:基本数据类型
  7. void
Linux.md

Linux.md

前提

  • 下好虚拟机后,记得每一次做任务前最好做好快照(血泪的教训)
  • 掌握主机名/IP地址等网络信息配置的方法(一定要保证号网络可用,不然后面吃大亏,下点软件都用不来哦。)

设置IP地址:

  1. 直接在终端运行nmtui命令配置网络(图形界面自己会了)。修改后使用systemctl restart NetworkManager 命令重启,使IP更改信息生效。
  2. nmcli命令配置网络,Ubuntu 通过NetworkManager.service(简称NM)进行网络配置,包括动态和静态IP,因此Ubuntu系统必循开启NM,否则无法使用网络,网络配置存储在/etc/NetworkManager/system-connections/目录中。
  • 用法1:查看IP
    nmcli
  • 用法2:查看connection列表
    nmcli connection show
  • 用法3:修改ens33网卡,配置方式为manual手动,设置静态IP地址,网卡,DNS信息。
  • 用法4;立即生效connection
    nmcli connection up ens33
    nmcli device reapply ens33
    nmcli device connect ens33

快捷键:

  1. Ctrl+Alt+T 组合键打开命令行终端
  2. Ctrl+l 清屏
  3. 打开控制面板的网络适配\连接: Win+R 输入ncpa.cpl
  4. 重启vm虚拟机:reboot

实施SSH远程连接:

  1. 前提Linux中有OpenSSH服务(没有自己下)
  2. 在进行远程登陆前,通过systemctl stop ufwsystemctl disable ufw `关闭防火墙功能。

文件与目录管理:

  • Linux 常用目录功能:
  1. ls(英文全拼:list files): 列出目录及文件名
  2. cd(英文全拼:change directory):切换目录
  3. pwd(英文全拼:print work directory):显示目前的目录
  4. mkdir(英文全拼:make directory):创建一个新的目录
  5. rmdir(英文全拼:remove directory):删除一个空的目录
  6. cp(英文全拼:copy file): 复制文件或目录
  7. rm(英文全拼:remove): 删除文件或目录
  8. mv(英文全拼:move file): 移动文件与目录,或修改文件与目录的名称

系统目录结构:

  1. /bin:
    bin 是 Binaries (二进制文件) 的缩写, 这个目录存放着最经常使用的命令。

  2. /boot:
    这里存放的是启动 Linux 时使用的一些核心文件,包括一些连接文件以及镜像文件。

  3. /dev :
    dev 是 Device(设备) 的缩写, 该目录下存放的是 Linux 的外部设备,在 Linux 中访问设备的方式和访问文件的方式是相同的。

  4. /etc:
    etc 是 Etcetera(等等) 的缩写,这个目录用来存放所有的系统管理所需要的配置文件和子目录。

  5. /home:
    用户的主目录,在 Linux 中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的,如上图中的 alice、bob 和 eve。

  6. /lib:
    lib 是 Library(库) 的缩写这个目录里存放着系统最基本的动态连接共享库,其作用类似于 Windows 里的 DLL 文件。几乎所有的应用程序都需要用到这些共享库。

  7. /media:
    linux 系统会自动识别一些设备,例如U盘、光驱等等,当识别后,Linux 会把识别的设备挂载到这个目录下。

  8. /lost+found:
    这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件。

  9. /mnt:
    系统提供该目录是为了让用户临时挂载别的文件系统的,我们可以将光驱挂载在 /mnt/ 上,然后进入该目录就可以查看光驱里的内容了。

  10. /opt:
    opt 是 optional(可选) 的缩写,这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。

  11. /proc:
    proc 是 Processes(进程) 的缩写,/proc 是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息。
    这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件,比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器:

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
12. /root:
该目录为系统管理员,也称作超级权限者的用户主目录。

  1. /sbin:
    s 就是 Super User 的意思,是 Superuser Binaries (超级用户的二进制文件) 的缩写,这里存放的是系统管理员使用的系统管理程序。

  2. /selinux:
    这个目录是 Redhat/CentOS 所特有的目录,Selinux 是一个安全机制,类似于 windows 的防火墙,但是这套机制比较复杂,这个目录就是存放selinux相关的文件的。

  3. /srv:
    该目录存放一些服务启动之后需要提取的数据。

16./sys:
这是 Linux2.6 内核的一个很大的变化。该目录下安装了 2.6 内核中新出现的一个文件系统 sysfs 。
sysfs 文件系统集成了下面3种文件系统的信息:针对进程信息的 proc 文件系统、针对设备的 devfs 文件系统以及针对伪终端的 devpts 文件系统。
该文件系统是内核设备树的一个直观反映。
当一个内核对象被创建的时候,对应的文件和目录也在内核对象子系统中被创建。

  1. /tmp:
    tmp 是 temporary(临时) 的缩写这个目录是用来存放一些临时文件的。

  2. /usr:
    usr 是 unix system resources(unix 系统资源) 的缩写,这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于 windows 下的 program files 目录。

  3. /usr/bin:
    系统用户使用的应用程序。

  4. /usr/sbin:
    超级用户使用的比较高级的管理程序和系统守护程序。

  5. /usr/src:
    内核源代码默认的放置目录。

  6. /var:
    var 是 variable(变量) 的缩写,这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。

  7. /run:
    是一个临时文件系统,存储系统启动以来的信息。当系统重启时,这个目录下的文件应该被删掉或清除。如果你的系统上有 /var/run 目录,应该让它指向 run。

  • 总结:

在 Linux 系统中,有几个目录是比较重要的,平时需要注意不要误删除或者随意更改内部文件。

/etc: 上边也提到了,这个是系统中的配置文件,如果你更改了该目录下的某个文件可能会导致系统不能启动。

/bin, /sbin, /usr/bin, /usr/sbin: 这是系统预设的执行文件的放置目录,比如 ls 就是在 /bin/ls 目录下的。

值得提出的是 /bin、/usr/bin 是给系统用户使用的指令(除 root 外的通用用户),而/sbin, /usr/sbin 则是给 root 使用的指令。

/var: 这是一个非常重要的目录,系统上跑了很多程序,那么每个程序都会有相应的日志产生,而这些日志就被记录到这个目录下,具体在 /var/log 目录下,另外 mail 的预设放置也是在这里。

安装软件的步骤:

  • 以安装gcc为例:
  1. 检查是否安装了gcc
    rpm -qa|grep gcc

  2. 若是没有显示有安装包,则可以用dnf命令安装所需要的软件包,步骤如下:
    mount /dev/cdrom /media

完全背包问题.md

完全背包问题.md

题目:

  • 有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。第 i 种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式:第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式:
输出一个整数,表示最大价值。

数据范围:
0<N,V≤1000
0<vi,wi≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10

-题解:

1.二维数组:

import java.util.Scanner;
public class Main{
public static void main(String[] args){
    Scanner sc=new Scanner(System.in);
    //读取物品得数量和背包得体积
    int N=sc.nextInt();
    int V=sc.nextInt();
    
    // 分别用于存储物品的体积和价值,数组下标从1开始使用,方便对应物品编号
    int[] v=new int[N+1];
    int[] w=new int[N+1];
    for(int i=1;i<=N;i++){
        v[i]=sc.nextInt();
        w[i]=sc.nextInt();
    }
    //定义一个二维dp数组,通过两层循环来填充dp数组,dp[i][j]表示在前i个物品中,背包容量为j时能获得的最大价值
    int[][] dp = new int[N + 1][V + 1];

    // 初始化边界情况,当没有物品可选(i = 0)时,无论背包容量是多少,最大价值都为0
    for (int j = 0; j <= V; j++) {
        dp[0][j] = 0;
    }
    //核心代码:
    for(int i=1;i<=N;i++){
        for(int j=0;j<=V;j++){
            //如果装得下
            if(j>=v[i]){
                dp[i][j]=Math.max(dp[i][j-v[i]]+w[i],dp[i-1][j]);
            }else{
                dp[i][j]=dp[i-1][j];
            }
        }
    }
    System.out.println(dp[N][V]);
}
}

总结:

01背包问题

01背包问题

题目:

  • 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤1000

0<vi,wi≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8

  • 看会了,就来试一试吧:经典的01背包问题
  • 哈哈哈,祝你的代码状态为:Accepted
  • 要是还不会就看看这几个大佬的视频讲解吧:黑马程序员(多看几遍,多动动手)、代码随想录中的算法公开课
  • 文字版的讲解:代码随想录
  • 题解:创建了二维数组 dp,其中 dp[i][j] 表示在前 i 个物品中,当背包容量为 j 时所能获得的最大价值,通过这样使用二维数组的动态规划方法,全面地考虑了放入和不放入每个物品的各种情况,最终准确地求出满足条件的背包能够装入物品的最大总价值。
  • 代码:

二维数组dp数组

import java.util.Scanner;
public class Main {
public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    // 读取物品数量N和背包容积V
    int N = scanner.nextInt();
    int V = scanner.nextInt();

    // 分别用于存储物品的体积和价值,数组下标从1开始使用,方便对应物品编号
    int[] v = new int[N + 1];
    int[] w = new int[N + 1];
    for (int i = 1; i <= N; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
    }

    // dp[i][j]表示在前i个物品中,背包容量为j时能获得的最大价值
    int[][] dp = new int[N + 1][V + 1];

    // 初始化边界情况,当没有物品可选(i = 0)时,无论背包容量是多少,最大价值都为0
    for (int j = 0; j <= V; j++) {
        dp[0][j] = 0;
    }

    // 动态规划核心逻辑,通过两层循环来填充dp数组
    for (int i = 1; i <= N; i++) {
        for (int j = 0; j <= V; j++) {
            if (j >= v[i]) {
                // 如果背包容量j大于等于第i个物品的体积v[i],可以选择放或不放该物品
                // 取放与不放该物品能得到的最大价值
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);
            } else {
                // 如果背包容量j小于第i个物品的体积v[i],无法放入该物品,价值与前i - 1个物品时相同
                dp[i][j] = dp[i - 1][j];
            }
        }
    }

    // 输出在前N个物品中,背包容量为V时能获得的最大价值
    System.out.println(dp[N][V]);

}
}

一维数组:

import java.util.Scanner;
public class KnapsackProblemOneDArray {
public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    // 读取物品数量N和背包容积V
    int N = scanner.nextInt();
    int V = scanner.nextInt();

    // 分别用于存储物品的体积和价值
    int[] v = new int[N + 1];
    int[] w = new int[N + 1];
    for (int i = 1; i <= N; i++) {
        v[i] = scanner.nextInt();
        w[i] = scanner.nextInt();
    }

    // dp[j]表示背包容量为j时能获得的最大价值(使用一维数组进行优化)
    int[] dp = new int[V + 1];

    // 动态规划核心逻辑,通过两层嵌套循环来填充dp数组
    for (int i = 1; i <= N; i++) {
        // 注意这里要从V开始逆序遍历到v[i],避免重复使用更新后的数据
        for (int j = V; j >= v[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
        }
    }

    // 输出背包容量为V时能获得的最大价值
    System.out.println(dp[V]);
 }
}
不同路径I、II-动态规划

不同路径I、II-动态规划

动态规划的理论基础

  • 总结:动态五部曲
    1.确定dp数组(dp table)以及下标的含义
    2.确定递推公式
    3.dp数组如何初始化
    4.确定遍历顺序
    5.举例推导dp数组
    详细来源:代码随想录

不同路径I

  1. 题目:
  • 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish” )。问总共有多少条不同的路径?
  1. 公开课:不同路径I
  2. 思路:

-

代码如下:

class Solution {
public int uniquePaths(int m, int n) {
    //定义一个dp的二维数组
    int[][] dp=new int[m][n];
    //初始化dp[i][0]和dp[0][i]
    for(int i=0;i<m;i++){
        dp[i][0]=1;
    }
    for(int i=0;i<n;i++){
        dp[0][i]=1;
    }
    //深推递推公式
   
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            dp[i][j] = dp[i-1][j]+dp[i][j-1];
        }
    }
     return dp[m-1][n-1];
    
}
}
最大子序和-贪心算法

最大子序和-贪心算法

贪心算法的理论基础

最大子序和

  1. 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
  • 例如:对于数组 [ -2, 1, -3, 4, -1, 2, 1, -5, 4 ]
  • 它能够找出和最大的连续子数组(子数组是数组中的一个连续部分。在这里是 [ 4, -1, 2, 1 ],其和为 6)
  • 并返回这个最大和的值。
  • Leetcode题目链接

思路

  1. 说实话一开始看到这个题目,脑子的反应就是想用暴力解法,结果一试一试,还真行。哈哈。
  • 思路:暴力解法的思路,第一层 for 就是设置起始位置,第二层 for 循环遍历数组寻找最大值。

代码如下:

class Solution {
public int maxSubArray(int[] nums) {
    int result =Integer.MIN_VALUE;
    int count = 0;
    for (int i = 0; i < nums.length; i++) { // 设置起始位置
        count = 0;
        for (int j = i; j < nums.length; j++) { // 每次从起始位置i开始遍历寻找最大值
            count += nums[j];
            result = count > result ? count : result;
        }
    }
    return result;


}
}
  • 但是时间复杂度:O(n^2)
  • 空间复杂度:O(1),不推荐,还有更好的算法
  1. 那么就是贪心算法(其实也还有其他的解法,例如:dp动态规划)

代码:

class Solution {
public int maxSubArray(int[] nums) {
    if (nums.length == 1){
        return nums[0];
    }
    int sum = Integer.MIN_VALUE; //sum:用于记录在遍历数组过程中找到的最大子数组的和,
    //初始化为 Integer.MIN_VALUE(Java 中 int 类型能表示的最小值),这样可以确保后续在比较和更新最大和时,任何有效的子数组和都能够比初始值大,从而可以正确地更新 sum 的值。
    int count = 0; //count:用于累计当前正在考虑的连续子数组的元素和,初始化为 0,随着遍历数组不断累加元素的值来更新这个累计和
    for (int i = 0; i < nums.length; i++){
        count += nums[i];
        sum = Math.max(sum, count); // 取区间累计的最大值(相当于不断确定最大子序终止位置)
        if (count <= 0){
            count = 0; // 相当于重置最大子序起始位置,因为遇到负数一定是拉低总和
        }
    }
   return sum;
}
}
我的第一篇博客

我的第一篇博客