Java重要知识点

news2025/2/27 20:48:10

Java

JVM

参考:https://www.kuangstudy.com/bbs/1557549426359590914

  1. 请你谈谈你对jvm的理解?
  2. Java8虚拟机和之前的变化更新?
  3. 什么是OOM?什么是栈溢出StackOverFlowError?怎么分析?
  4. jvm的常见调优参数有哪些?
  5. 内存快照如何抓取?怎么分析Dump文件?
  6. 谈谈jvm中,类加载器你的认识?
  • JVM的位置

在这里插入图片描述

  • JVM的体系结构

    • jvm结构图,从.java文件 -> .class文件 -> classloader -> 分配空间

      在这里插入图片描述

    • jvm垃圾回收,在jvm中,线程私有的区域不会存在垃圾回收,因为都是栈和程序计数器,如果存在垃圾,那么程序就会死掉,无法正常运行。

    在这里插入图片描述

    • jvm调优,调优肯定是垃圾回收的优化,那么就是线程共享的区域:堆、方法区

      在这里插入图片描述

类加载器

  • 类加载的过程:加载、初始化、实例化,这里的Car Class可以看作是一个模板

在这里插入图片描述

  • 哪些类加载器:

    • 引导类加载器(BootstrapClassloader):用C++编写,是JVM自带的类加载器;负责加载Java的核心类库。(该加载器无法直接获取)
    • 扩展类加载器(ExtClassloader):负责加载/jre/lib/ext目录下的jar包。
    • 应用程序类加载器(AppClassloader):负责加载java -classpath或-D java.class.path所指的目录下的类与jar包。(最常用的加载器)
  • 双亲委派机制:(检查顺序从下至上,加载顺序从上至下,也就是从app->ext->boot检查,从boot->ext->app加载)

    • 类加载器接收到一个加载请求时,他会委派给他的父加载器,实际上是去他父加载器的缓存中去查找是否有该类,如果有就加载返回,如果没有则继续委派给父类加载,直到顶层类加载器。
    • 如果顶层类加载器也没有加载该类,则会依次向下查找子加载器的加载路径,如果有就加载返回,如果都没有,则会抛出异常。

双亲委派机制的类加载流程:

1、类加载器收到类加载请求

2、将这个请求向上委托给父类加载器,一直向上委托,直到启动类加载器

3、启动类加载器检查是否能执行该类,能加载就结束,使用当前加载器,不能加载就抛出异常通知子加载器进行加载。

如果类加载器返回null,那么java中找不到该类,可能在C、C++中,注意:java中调用系统本地的方法使用native字段,比如线程的线程的启动方法,java处理不了,就只能去用接口调用本地方法。

  • 沙箱安全机制,了解

Native、方法区

  • Native:

    • 凡是使用了native关键字的,说明Java的作用范围已经达不到了,它会去调用底层的C语言的库。
      • 进入本地方法栈。
      • 调用本地方法接口。JNI(Java Native Interface)的作用:扩展Java的使用,融合不同的语言为Java所用。(最初是为了融合C、C++语言)
      • 所以Java在JVM内存区域专门开辟了一块标记区域Native Method Area Stack,用来登记native方法。
        在最终执行(执行引擎执行)的时候,通过JNI来加载本地方法库中的方法。
  • 方法区:Method Area方法区(此区域属于共享区间,所有定义的方法的信息都保存在该区域)
    方法区是被所有线程共享,所有字段、方法字节码、以及一些特殊方法(如构造函数,接口代码)也在此定义。

静态变量(static)、常量(final)、类信息(构造方法、接口定义)(class模板)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关。

  • 程序计数器:Program Counter Register
    每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

  • 栈的作用:栈内存,主管程序的运行,生命周期和线程同步;线程结束,栈内存也就释放了,对于栈来说,不存在垃圾回收问题

  • 栈存储的内容:8大基本类型、对象引用,实例的方法。

  • 栈的运行原理:本地变量->局部变量表,注意父帧和子帧

img
  • 栈+堆+方法区之间的交互关系:

在这里插入图片描述

  • 概念:Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。

  • 类加载器读取了类文件后,一般会把什么东西放到堆中?
    类、方法、常量、变量、保存我们所有引用类型的真实对象。

  • 堆内存中细分为三个区域:

    • 新生代内存(Young Generation)(Eden)
    • 老生代(Old Generation)
    • 永久代(Permanent Generation) -> 元空间,可以理解为方法区的具体实现,所以常量池,静态变量,Class放在元空间中
  • 堆内存结构

在这里插入图片描述

  • 永久代和元空间:方法区是一种规范,不同的虚拟机厂商可以基于规范做出不同的实现,永久代和元空间就是出于不同jdk版本的实现。
    方法区就像是一个接口,永久代与元空间分别是两个不同的实现类。
    只不过永久代是这个接口最初的实现类,后来这个接口一直进行变更,直到最后彻底废弃这个实现类,由新实现类—元空间进行替代。

    • jdk1.8之前:

      在这里插入图片描述

    • jdk1.8:在堆内存中,逻辑上存在,物理上不存在(元空间使用的是本地内存)

      在这里插入图片描述

  • 常量池:

    • 在jdk1.7之前,运行时常量池+字符串常量池是存放在方法区中,HotSpot VM对方法区的实现称为永久代。

      在这里插入图片描述

    • 在jdk1.7中,字符串常量池从方法区移到堆中,运行时常量池保留在方法区中。

      在这里插入图片描述

    • jdk1.8之后,HotSpot移除永久代,使用元空间代替;此时字符串常量池保留在堆中,运行时常量池保留在方法区中,只是实现不一样了,JVM内存变成了直接内存。

    在这里插入图片描述

GC垃圾回收

  • 垃圾回收的区域

在这里插入图片描述

  • 如何判断一个常量是废弃常量:假如在字符串常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,“abc” 就会被系统清理出常量池了。

  • 如何判断一个类是无用的类:无用的类需要同时满足下面3个条件

    • Java堆中不存在该类的任何实例
    • 加载该类的ClassLoader已被收回
    • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
  • GC之引用计数法:给对象中添加一个引用计数器:

    • 每当有一个地方引用它,计数器就加 1;
    • 当引用失效,计数器就减 1;
    • 任何时候计数器为 0 的对象就是不可能再被使用的。

    这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间循环引用的问题。

在这里插入图片描述

所谓对象之间的相互引用问题,如下面代码所示:除了对象 objAobjB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。

  • GC之标记清楚法:标记-清除(Mark-and-Sweep)算法分为“标记(Mark)”和“清除(Sweep)”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它作为最基础的收集算法,后续的算法都是对其不足进行改进,这个算法会带来两个明显的问题:

    • 效率问题:标记和清除两个过程效率不高

    • 空间问题:标记清楚后会产生大量不连续的空间碎片

      在这里插入图片描述

  • GC之复制算法(主要使用在新生代):为了解决标记-清除算法的效率和内存碎片问题,复制(Copying)收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。虽然改变了空间和效率的问题,但又产生了新问题:

    • 因为将内存分成两半,那么可用内存就变小了
    • 不适合老年代,如果存货对象数量比较大,复制性能就会变差

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在的商业虚拟机都采用这种收集算法来回收新生代,但是并不是将新生代划分为大小相等的两块,而是分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 空间和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象一次性复制到另一块 Survivor 空间上,最后清理 Eden 和使用过的那一块 Survivor。

HotSpot 虚拟机的 Eden 和 Survivor 的大小比例默认为 8:1,保证了内存的利用率达到 90%。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 空间就不够用了,此时需要依赖于老年代进行分配担保,也就是借用老年代的空间存储放不下的对象。

  • GC之标记整理算法:根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。由于多了整理这一步,因此效率也不高,适合老年代这种垃圾回收频率不是很高的场景。

在这里插入图片描述

  • GC之分代回收算法:现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。

    一般将堆分为新生代和老年代。

    • 新生代使用: 复制算法
    • 老年代使用: 标记 - 清除 或者 标记 - 整理 算法

垃圾收集器

前面的收集算法是内存回收的方法论,而垃圾收集器就是这些方法的具体实现,我们能需要根据具体应用场景选择适合自己的垃圾收集器

  • Serial收集器:串行
  • ParNew收集器:Serial+并行

了解

为什么需要对垃圾回收进行分区?

这其实是和对象的存活概率挂钩的,存活时间长的,放在老年代,减少GC(垃圾回收)扫描的概率,新创建出来的对象实例,一般存放在伊甸园,而且不同的区域采用的垃圾回收的算法也是不一样的。

Young GC的触发机制

在新生代的Eden区域满了之后就会触发,采用复制算法来 回收新生代的垃圾

Old GC和Full GC的触发时机

1、发生Young GC之前进行检查,如果“老年代可用的连续内存空间” < “新生代历次Young GC后升入老年代的对象 总和的平均大小”,说明本次Young GC后可能升入老年代的对象大小,可能超过了老年代当前可用内存空间,此时必须先触发一次Old GC给老年代腾出更多的空间,然后再执行Young GC

这也就是 ygc之前,触发old gc

2、执行Young GC之后有一批对象需要放入老年代,此时老年代就是没有足够的内存空间存放这些对象了,此时必 须立即触发一次Old GC

这也就是ygc之后,触发old gc

3、老年代内存使用率超过了92%,也要直接触发Old GC,当然这个比例是可以通过参数调整的。

其实说白了,上述三个条件你概括成一句话,就是老年代空间也不够了,没法放入更多对象了,这个时候务必执行Old GC对老年代进行垃圾回收。

参考:https://zhuanlan.zhihu.com/p/420273533

代理

  • 代理模式:**使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。**比如:日志记录、参数校验、事务管理、权限校验
  • 静态代理:在程序运行之前就给目标类编写了其代理类的方法,并对其编译,程序运行的时候能直接读取

具体代码可以看javaGuide:https://javaguide.cn/java/basis/proxy.html#_2-%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86

  • 动态代理不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( JDK、CGLIB 动态代理机制)。**从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。**说到动态代理,Spring AOP、RPC 框架应该是两个不得不提的,它们的实现都依赖了动态代理。
  • JDK动态代理的实现流程:JDK动态代理只能代理实现了接口的类
    • 定义一个接口及其实现类;
    • 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
    • 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象。

具体代码可以看javaGuide:https://javaguide.cn/java/basis/proxy.html#_2-%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86

  • 静态代理、动态代理最大的区别在于动态代理不需要为每个类创建一个代理,使用统一的代理并通过代理工厂的方式实例化代理类,从而实现代理的动态化。

  • CGLIB动态代理机制:CGLIB是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知名的开源框架都使用到了CGLIB, 例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

    • 在使用过程中需要自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法。
    public interface MethodInterceptor
    extends Callback{
        // 拦截被代理类中的方法
        //obj : 被代理的对象(需要增强的对象)
        //method : 被拦截的方法(需要增强的方法)
        //args : 方法入参
        //proxy : 用于调用原始方法
        public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
    }
    
    • 通过 Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法
  • JDK 动态代理和 CGLIB 动态代理对比

    • JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
    • 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

    基础:

  • 多态:可以看成一个向上转形,Animal dog = new Dog(),将dog转形为Animal,不能调用只有Dog类中存在但是Animal类中不存在的方法;如果子类重写了父类中的方法,那么就执行子类的方法。

  • ==和equals():

    • 类没有重写 equals()方法:通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Objectequals()方法。

    • 类重写了 equals()方法:一般我们都重写 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等).

  • 异常捕获过程中:无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

  • 静态变量可以被类的所有实例共享。无论一个类创建了多少个对象,它们都共享同一份静态变量。

  • 重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理

  • 方法的重写要遵循“两同两小一大”

    • “两同”即方法名相同、形参列表相同;
    • “两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
    • “一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。

在这里插入图片描述

  • Java中的引用类型就是除了基本数据类型之外的所有类型(如class类型)

  • 基本类型和包装类型的区别?Java 中有 8 种基本数据类型,分别为:byteshortintlongfloatdoublechar、boolean这八种基本类型都有对应的包装类分别为:ByteShortIntegerLongFloatDoubleCharacterBoolean

    • 成员变量包装类型不赋值就是 null ,而基本类型有默认值且不是 null

    • 包装类型可用于泛型,而基本类型不可以。

    • 基本数据类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 static 修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。

    • 相比于对象类型, 基本数据类型占用的空间非常小

所有整型包装类对象之间值的比较,全部使用 equals 方法比较

在这里插入图片描述

  • 如何解决浮点数运算的精度丢失问题?BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。

  • **为什么重写 equals() 时必须重写 hashCode() 方法?**因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。

    • equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。

    • 两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞)。

Java引用类型:

  • 强引用: 最普通的引用 Object o = new Object(),如果没有被引用那么GC就会被回收。
  • 软引用: 垃圾回收器, 内存不够的时候回收 (缓存)。
  • 弱引用: 垃圾回收器看见就会回收 (防止内存泄漏),ThreadLocal中key是弱引用, 其目的就是讲ThreadLocal对象的生命周期和和线程的生命周期解绑. 减少内存使用
  • 虚引用: 垃圾回收器看见二话不说就回收,跟没有一样 (管理堆外内存) DirectByteBuffer -> 应用到NIO Netty

ThreadLocal

  • 提供线程内的局部变星,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度.

  • 特点:

特点内容
线程并发在多线程并发场景下
传递数据我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量
(保存每个线程的数据,在需要的地方可以直接获取, 避免参数直接传递带来的代码耦合问题)
线程隔离每个线程的变量都是独立的, 不会互相影响.(核心)
(各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失)
  • ThreadLocal 和Synchronized的区别
synchronizedsynchronizedThreadLocal
原理同步机制采用以时间换空间的方式,只提供了一份变量, 让不同的线程排队访问ThreadLocal采用以空间换时间的方式, 为每一个线程都提供了一份变量的副本, 从而实现同访问而相不干扰
侧重点多个线程之间访问资源的同步多线程中让每个线程之间的数据相互隔离
  • 实现流程

    1. 每个THreadLocal线程内部都有一个Map(ThreadLocalMap)

    2. Map里面存储的ThreadLocal对象(key)和线程变量副本(Value)也就是存储的值

    3. Thread内部的Map是由ThreadLocal维护的, 有THreadLocal负责向map获取和设置线程变量值

    4. 对于不同的线程, 每次获取value(也就是副本值),别的线程并不能获取当前线程的副本值, 形成了副本的隔离,互不干扰.

  • 强弱引用和内存泄漏

    • 如果key是强引用:在这里插入图片描述

    • 如果key是弱引用:在这里插入图片描述

    • 内存泄漏的真实原因:在这里插入图片描述

    • 那么为什么 key 要用弱引用呢:事实上,在 ThreadLocalMap 中的set/getEntry 方法中,会对key为null(也即是ThreadLocal为null)进行判断,如果为null的话,那么将value置为null的.这就意味着使用完 ThreadLocal , CurrentThread 依然运行的前提下.就算忘记调用 remove 方法,弱引用比强引用可以多一层保障:弱引用的 ThreadLocal 会被回收.对应value在下一次 ThreadLocaIMap 调用 set/get/remove 中的任一方法的时候会被清除,从而避免内存泄漏.

此处参考:https://blog.csdn.net/bbscz007/article/details/105686382

常见面试题:

底层原理
内存泄漏的场景
为什么虚引用了还要remove
父线程的ThreadLocal子线程可以用吗
IheritableThreadLocal原理
线程池里的线程能用IheritableThreadLocal吗
为什么ThreadLocalMap中的key要用弱引用,Java有哪些引用
使用弱引用的话,如果key被垃圾回收了,value怎么办
那你如果调用get()方法之后,key被回收了,value会不会被回收(猜的)
threadlocal,定义在哪里的?threadlocal存在哪里?threadlocalMap放在哪里,key和value指向的是啥

ArrayList扩容机制

1.ArrayList的扩容是懒惰的,在没有添加元素之前,即使指定了容量,也不会真正创建数组;

2.使用add方法时:当存入数据时进行扩容,第一次扩容到长度为10的数组,替换掉长度为0的数组;第二次扩容为上一次的1.5倍(实际上是[>>>1]),也就意味着第二次扩容大小为15,同时新数组依旧替换掉旧数组。

3使用addAll()方法时:

3.1当ArrayList中为空时,添加的元素的数量小于等于10时,第一次扩容为10,当添加的元素数量大于10时,扩容大小为元素数量。

例如:添加5个元素,第一次扩容为10;

添加12个元素,第一次扩容为12。

3.2当ArrayList中不为空时,规则类似于上一条,添加的元素数量与原本的元素数量如果大于当前容量且小于下次扩容的大小,则为1.5倍数组大小扩容,如果两者之和大于下次扩容大小,则扩容大小为两者元素和大小;

例如:当前ArrayList中已经有10个元素,再增加3个元素,则一共13个元素,小于扩容大小15,下一次扩容大小为15;

当前ArrayList中已经有10个元素,再增加6个元素,则一共16个元素,大于扩容大小15,下一次扩容大小为16。

CyclicBarrier 和 CountDownLatch 的区别

1、CountDownLatch 简单的说就是一个线程等待,直到他所等待的其他线程都执

行完成并且调用 countDown()方法发出通知后,当前线程才可以继续执行。

2、cyclicBarrier 是所有线程都进行等待,直到所有线程都准备好进入 await()方

法之后,所有线程同时开始执行!

3、CountDownLatch 的计数器只能使用一次。而 CyclicBarrier 的计数器可以使

用 reset() 方法重置。所以 CyclicBarrier 能处理更为复杂的业务场景,比如如果

计算发生错误,可以重置计数器,并让线程们重新执行一次。

4、CyclicBarrier 还提供其他有用的方法,比如 getNumberWaiting 方法可以获

得 CyclicBarrier 阻塞的线程数量。isBroken 方法用来知道阻塞的线程是否被中断。

如果被中断返回 true,否则返回 false。

说一下Select、Poll、Epoll的区别

https://zhuanlan.zhihu.com/p/367591714

在这里插入图片描述

OOM如何分析

https://blog.csdn.net/CSDN_WYL2016/article/details/107749678

算法

sort函数

sort()方法会接受一个比较函数compare(a, b),该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。最后,永远要记住一句话,凡是返回1或者大于0的正数的时候就要交换位置。

(1)Arrays.sort:针对数组
递增排序:Arrays.sort(arr);
递减排序:Arrays.sort(arr, (a, b) -> b - a);
或使用:Arrays.sort(scores,Collections.reverseOrder()); 需要注意的是 arr不能使用基本类型(int,double, char),如果是int型需要改成Integer,float要改成Float。这里也要注意object[] arr是需要自己new每个对象的,因为object的默认值是null;而int[] arr等基本类型的默认值不是null,所以可以直接使用。

按[0]维升序排,如果[0]相等,则按[1]维升序排:

int[][] array = {{5, 7}, {5, 2}, {2, 5}, {5, 8}, {7, 1}, {4, 3}, {2, 3}};
Arrays.sort(array, (e1, e2) -> e1[0] == e2[0] ? e1[1] - e2[1] : e1[0] - e2[0]);

(2)Collections.sort:针对集合类(容器)
递增排序:Collections.sort(list);
按getName()结果递增排序:Collections.sort(list, (e1, e2) -> e1.getName()-e2.getName());
按x升序排,如果x相等,则按y升序排:

Collections.sort(list,(e1,e2)->e1.x==e2.x ? e1.y-e2.y: e1.x-e2.x);

(3)类实现Comparable接口,重写compareTo() 比直接在sort()里面用lamda函数时间复杂度低

class Node implements Comparable{
    int x;
    int y;
    public Node(int x,int y) {
        this.x = x;
        this.y = y;
    }
    @Override
    public int compareTo(Object o) {
        Node node = (Node) o;
        //按x升序排,如果x相等,则按y升序排
        return this.x==node.x?this.y-node.y:this.x-node.x;
    }
}

String#equals() 和 Object#equals() 有何区别?

String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Objectequals 方法是比较的对象的内存地址。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/990330.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据如何应用于业务和决策?_光点科技

大数据已经成为当今商业和决策制定中的一个关键因素。随着互联网的普及和技术的不断进步&#xff0c;我们生产的数据量呈指数级增长。这些数据不仅包括来自社交媒体、传感器、移动设备等各种来源的信息&#xff0c;还包括过去难以存储和分析的结构化和非结构化数据。如何利用这…

spring boot 使用AOP+自定义注解+反射实现操作日志记录修改前数据和修改后对比数据,并保存至日志表

一、添加aop starter依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>二&#xff1a;自定义字段翻译注解。&#xff08;修改功能时&#xff0c;需要显示如…

高压放大器在制备功能材料中的应用

高压放大器在制备功能材料中具有广泛的应用。功能材料是一类具有特殊性能和功能的材料&#xff0c;可以在各个领域中发挥重要作用&#xff0c;如能源存储、电子、光电、催化等。高压放大器在制备功能材料的研究和应用中起到关键的作用&#xff0c;本文将介绍高压放大器在制备功…

快速查询快递单号物流,跟踪物流详情

如果你是一位经常网购的人&#xff0c;那么你一定对快递单号物流查询的过程不陌生。每次收到快递&#xff0c;我们都会在快递官网或者第三方平台上输入快递单号进行查询&#xff0c;整个过程比较繁琐。但是&#xff0c;现在有一款名为“固乔快递查询助手”的软件&#xff0c;可…

WRFDA资料同化实践技术应用

数值预报已经成为提升预报质量的重要手段&#xff0c;而模式初值质量是决定数值预报质量的重要环节。资料同化作为提高模式初值质量的有效方法&#xff0c;成为当前气象、海洋和大气环境和水文等诸多领域科研、业务预报中的关键科学方法。资料同化新方法的快速发展&#xff0c;…

向量数据库Milvus Cloud 核心组件Knowhere升级,支持 GPU 索引和 Cosine 相似性类型

熟悉我们的朋友都知道,在 Milvus Cloud和 Zilliz Cloud 中,有一个至关重要的组件——Knowhere。 Knowhere 是什么?如果把向量数据库整体看作漫威银河护卫队宇宙,那么 Knowhere 就是名副其实的总部,它的主要功能是对向量精确搜索其最近邻或通过构建索引进行低延迟、近似…

解决Microsoft Edge无法正常运行的有效方案分享!

Microsoft Edge打不开或不能加载网页是用户在Windows 10、Android、Mac和iOS设备上的网络浏览器上遇到的许多错误之一。其他Microsoft Edge问题可能包括浏览器窗口和选项卡冻结、网站崩溃、互联网连接错误消息以及丢失Microsoft Edge书签、收藏夹、密码和收藏。 Microsoft Edg…

树的基本概念和存储结构

一、树的基本概念 1、树的定义 树是n&#xff08;n>0&#xff09;个结点的有限集。当n 0时&#xff0c;称为空树。在任意一棵非空树中应满足&#xff1a; 1、有且仅有一个特定的称为根的结点。 2、当n>1时&#xff0c;其余节点可分为m&#xff08;m>0&#xff09…

AFL模糊测试

一、AFL简介 AFL&#xff08;American Fuzzy Lop&#xff09;号称是当前最高级的Fuzzing 测试工具之一 &#xff0c;是由安全研究员Michał Zalewski&#xff08;lcamtuf&#xff09;开发的一款 基于覆盖引导&#xff08;Coverage-guided&#xff09;的 模糊测试工具&#xff0…

CentOS系统环境搭建(十九)——CentOS7安装chat GPT

centos系统环境搭建专栏&#x1f517;点击跳转 CentOS7安装chat GPT Welcome to the AI era! 基于上一篇文章CentOS系统环境搭建&#xff08;十八&#xff09;——CentOS7安装Docker20.10.12和docker compose v2&#xff0c;你已经安装了docker20以上的版本。那么&#xff0…

代码随想录第30天 | ● 860.柠檬水找零 ● 406.根据身高重建队列 ● 452. 用最少数量的箭引爆气球

860.柠檬水找零 /*** param {number[]} bills* return {boolean}*/ var lemonadeChange function(bills) {let d50let d100let d200for(let i0;i<bills.length;i){if(bills[i]5){d51}else if(bills[i]10){if(d5>1){d5--d10}elsereturn false}else{if(d5>1&&…

圣杯交易系统选股公式,ADX指标和EMA均线配合使用

圣杯交易系统是由琳达布拉德福德拉希克(Linda Bradford Raschke)开发的&#xff0c;她是杰克施瓦格《新金融怪杰》书中的访谈对象。圣杯策略的目标是发现强劲趋势确立后的首次回撤&#xff0c;依据平均趋向指标ADX和指数移动平均线EMA的协同作用来确定趋势强度和合适的交易区域…

Vue中extend基本用法

1.Vue.extend(options) 参数: {Object} options用法&#xff1a; 使用基础Vue构造器&#xff0c;创建一个"子类"。参数是一个包含组件选项的对象。 data选项是特例&#xff0c;需要注意&#xff0c;在Vue.extend()中它必须是函数。 <html><head><tit…

武汉凯迪正大—直读激光盐密灰密测试仪

一、凯迪正大—绝缘子灰密盐密测试仪产品概述 凯迪正大绝缘子灰密盐密测试仪采用的检测技术将灰密测试与盐密测试合二为一&#xff0c;可同时检测出被测绝缘子的灰密度和盐密度&#xff0c;简化了绝缘子污秽检测的流程&#xff0c;适合在巡检现场和实验室使用。 二、凯迪正大…

JDK17:未来已来,你准备好了吗?

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

一元多项式的乘法与加法运算

设计函数分别求两个一元多项式的乘积与和。 输入格式: 输入分2行&#xff0c;每行分别先给出多项式非零项的个数&#xff0c;再以指数递降方式输入一个多项式非零项系数和指数&#xff08;绝对值均为不超过1000的整数&#xff09;。数字间以空格分隔。 输出格式: 输出分2行…

正中优配:散户怎么实现T+0?散户在股市上怎么变相T+0?

T0是指当天买入的标的物&#xff0c;在当天就能卖出的买卖方式&#xff0c;其中&#xff0c;在a股市场上&#xff0c;散户能够通过一些办法直接地完成T0买卖方式&#xff0c;接下来&#xff0c;正中优配为大家预备了相关内容&#xff0c;以供参阅。 散户在股票市场上&#xff0…

Linear Decryption: Rate-1 FHE TLP

参考文献&#xff1a; [ILL89] Russell Impagliazzo, Leonid A. Levin, and Michael Luby. Pseudo-random generation from oneway functions (extended abstracts). In 21st Annual ACM Symposium on Theory of Computing, pages 12–24, Seattle, WA, USA, May 15–17, 1989.…

苹果macOS13Ventura更新体验:新功能带来的全新体验

macOS 13 Ventura 是一款功能强大、界面美观的操作系统。它为用户提供了更好的使用体验&#xff0c;加强了与其他设备的互联互通&#xff0c;提高了隐私和安全性。无论是日常办公还是娱乐&#xff0c;macOS 13 Ventura 都能满足用户的需求&#xff0c;并带来更多的便利和乐趣。…

Linux之history、tab、alias、命令执行顺序、管道符以及exit

目录 Linux之history、tab、alias、命令执行顺序、管道符以及exit history历史命令 格式 参数 修改默认记录历史命令条数 案例 案例1 --- 显示history历史记录中出现次数最高的top10 案例2 --- 增加history显示的时间信息 命令与文件名补全 --- tab 命令别名 格式 案…