目录

基础概念

Java的特点/优势

Java的缺点

Java是跨平台的

JVM、JDK、JRE三者的关系

Java的解释和编译

编译型语言和解释型语言

值传递和引用传递

数据类型

八种基本的数据类型

类型互转的问题

解决浮点数运算的精度丢失问题

装箱和拆箱

自动装箱的弊端

Integer包装类以及它的作用

Integer与int的区别

保留int的原因

介绍一下Integer的静态缓存池


基础概念

Java的特点/优势

  1. 平台无关性:实现了“一次编写,随处运行”,编译器将源代码编译成字节码,字节码可以在所有安装了Java虚拟机的操作系统上运行。

  2. 面向对象:Java的一切几乎都是对象,这个特性使得代码容易维护和封装。

  3. 内存管理:Java拥有垃圾回收机制,自动回收不使用的对象,因此开发者不需要手动管理内存,减少了内存泄露的风险。

  4. 生态强大:社区多,企业使用Java也比较广泛,拥有Spring等框架。

Java的缺点

  1. 语言繁琐:模板代码比较多,完成同样功能往往需要比Python/Go语言写更多代码,不够简洁。

  2. 启动慢:JVM启动时需要加载类、初始化环境,启动时间甚至会超过程序本身的执行时间。

  3. 内存占用高:JVM的许多功能,如垃圾回收机制,都很占用内存空间。

Java是跨平台的

编译器将源代码编译成字节码文件,字节码文件可以在所有安装了Java虚拟机的操作系统上运行。

Java虚拟机是桥梁,是实现跨平台的关键。Java虚拟机负责将字节码文件解释或编译成不同操作系统下的机器码然后运行。

不同操作系统对应不同的Java虚拟机

JVM、JDK、JRE三者的关系

  • JVM是虚拟机。负责将字节码文件解释或编译成不同操作系统下的机器码然后运行。

  • JRE是Java程序运行所需的最小环境。它包含了JVM和一组Java类库(提供常用的功能和 API)。

  • JDK是开发工具包,开发Java程序所需的工具。它包含了JVM、一组Java类库、以及编译器(javac)、调试器(jdb)等开发工具

Java的解释和编译

  1. 启动时:解释器

      JVM 先加载字节码,直接用解释器逐行翻译字节码,不用等编译,所以程序能快速启动。

    1. 运行一段时间后: JIT 编译器。

        方法调用计数器(PC计数器),统计哪些代码被频繁调用(“热点代码”)。当某段代码被调用次数超过阈值,JIT 编译器会把这段字节码编译成机器码,存在 CodeCache 里。之后再执行这段代码时,直接用编译好的机器码(不用再解释),运行速度就变快了。

        左侧负责解释,右侧负责编译
        无论左右,CodeCache存放的都是机器码

        编译型语言和解释型语言

        • 编译型语言:整个源代码会被编译,生成可执行文件。直接运行编译后的代码,速度快,但跨平台性较差。

        • 解释型语言:逐行解释执行源代码,不生成独立的可执行文件。解释器边解释边执行,跨平台性好,但执行速度较慢。

        • 典型的编译型语言如C、C++,典型的解释型语言如Python、JavaScript。

        值传递和引用传递

        • 值传递:方法调用时,实参将自己的值复制一份传递给形参。形参和实参是两个独立的变量,在方法内部修改形参的值,不会影响外部实参的值。

                比如: int 型变量 num=10 传入方法,方法内把 num 改成 100,外部的 num 还是 10。

        • 引用传递:方法调用时,实参将自己的内存地址直接传递给形参,形参和实参共享同一个变量内存,使用的是同一个引用,对形参的任何修改,都是直接修改实参变量本身。但Java不支持这种传递方式。

                比如:形参指向了一个新的的对象,原有的实参也会改变指向。

        • Java中只有值传递,不存在引用传递。只有C、C++才有引用传递。

            基本类型 传递 “值的副本”,修改副本不影响原值。

            引用类型 传递 “引用的副本”,通过副本可修改对象内容,但无法改变原引用的指向

        public static void main(String[] args) {
            
            Person p = new Person("Alice");    Alice →  Bob  →  Charlie
            changeName(p);
            System.out.println(p.name); // 输出 "Bob"(对象内部被修改) Alice →  Bob
            
            changeReference(p);
            System.out.println(p.name); // 仍输出 "Bob"(原引用指向未变)
        }
        
        // 修改对象内部数据
        public static void changeName(Person obj) {
            obj.name = "Bob"; // 副本和原引用指向同一个对象
        }
        
        // 修改副本的指向(不影响原引用)
        public static void changeReference(Person obj) {
            obj = new Person("Charlie"); // 副本指向新对象,原引用仍指向旧对象
        }
        
        补充:这两种传递的场景都是函数调用。

        数据类型

        八种基本的数据类型

        两种数据类型: 基本数据类型和引用数据类型。

        八种基本数据类型

        • 数值型:整数类型(byte、short、int、long)和浮点类型(float、double)

        • 字符型:char

        • 布尔型:boolean

        三种引用数据类型

        • 接口

        • 数组

        补充:

        • Java八种基本数据类型的字节数:1字节(byte、boolean)、 2字节(short、char)、4字节(int、float)、8字节(long、double)

        • 浮点数的默认类型为double(如果需要声明一个常量为float型,则必须要在末尾加上f或F)

        • 整数的默认类型为int(声明Long型在末尾加上l或者L)

        • 八种基本数据类型的包装类:除了char的是Character、int类型的是Integer,其他都是首字母大写

        • char类型是无符号的,不能为负,所以是0开始的

        类型互转的问题

        • 基本数据类型转换的问题

        大范围数据类型赋值给小范围数据类型时,会发生数据数据溢出或者精度损失的问题

        • 对象引用转换的问题

        如果父类对象实际上并不是目标子类的实例,向下转型时就会抛出ClassCastException异常。

        Animal animal = new Animal();
        Dog dog = (Dog) animal; // 运行时抛出ClassCastException
        
          
        //解决方式是需要使用 instanceof 检查   
                                            用法:对象 instanceof 类
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal; // 只有确认animal是Dog的实例时才进行转型
        }

        解决浮点数运算的精度丢失问题

        double会出现精度丢失的问题,double执行的是二进制浮点运算,二进制有些情况下不能准确的表示一个小数,比如0.1不能够精确表示。

        BigDecimal 是精确计算 , 一般牵扯到金钱的计算。 包括add、subtract、multiply方法。

        import java.math.BigDecimal;
        
        public class BigDecimalExample {
            public static void main(String[] args) {
                BigDecimal num1 = new BigDecimal("0.1");  创建BigDecimal对象时,应该使用字符串作为参数,而不是直接使用浮点数值
                BigDecimal num2 = new BigDecimal("0.2");
        
                BigDecimal sum = num1.add(num2);
                BigDecimal product = num1.multiply(num2);
        
                System.out.println("Sum: " + sum);
                System.out.println("Product: " + product);
            }
        }

        装箱和拆箱

        基本数据类型 和对应的 包装类 之间进行转换的过程。

        装箱:基本数据类型→包装类。 编译器自动调用包装类的valueOf()方法。

        拆箱:包装类→基本数据类型。编译器自动调用包装类的xxxValue()方法(如 int 对应intValue())。

        Integer i = 10;  //装箱
        int n = i;   //拆箱
        
        
        // 1. 手动装箱(极少用,仅演示)
        Integer manualBox = Integer.valueOf(10); 
        // 2. 自动装箱(日常开发常用,编译器帮你调valueOf())
        Integer autoBox = 10; 
        
        // 1. 手动拆箱(极少用)
        int manualUnbox = autoBox.intValue();
        // 2. 自动拆箱(编译器帮你调intValue())
        int autoUnbox = autoBox;

        自动装箱的弊端

        • 空指针异常:拆箱时如果包装类对象为 null,会直接抛 NullPointerException。

        • 循环中反复装箱拆箱:如果用于累加的变量声明为包装类,会创建多余的对象,影响程序的性能。

        Integer sum = 0; 
        for (int i = 1000; i < 5000; i++) {   
            sum += i;   先拆箱 再装箱
        } 
        // + 这个操作符不适用于Integer对象,首先sum进行自动拆箱操作,进行数值相加操作,最后发生自动装箱操作转换成Integer对象。
        // sum += i  等价于  sum = Integer.valueOf(sum.intValue() + i)

        Integer包装类以及它的作用

        1. 类型转换:包装类同时包含数据以及处理数据的方法,可用于类型转换。

         1.字符串转成基本类型int
        String numStr = "123";
        int num = Integer.parseInt(numStr); 
        
         2.int/String转成Integer
        int num = 789;
        Integer numObj2 = Integer.valueOf(num);
        
         3.int转为String
        int i = 10;
        Integer integer = new Integer(i);
        String str = integer.toString();
        1. 集合类和泛型仅处理引用类型对象,无法操作基本类型,因此需使用对应的包装类,才能正常使用集合的各类方法

        Integer与int的区别

        1. 类型转换:包装类同时包含数据以及处理数据的方法,可用于类型转换。

        2. 集合类和泛型仅处理引用类型对象,无法操作基本类型,因此需使用对应的包装类,才能正常使用集合的各类方法,而int不行。

        3. 支持空值:int 是基本类型,默认值是 0,无法表示 “无数据”;Integer是对象,可通过null表示空值。

        4. 读写效率和存储效率

        保留int的原因

        • 读写效率:包装类是引用类型,对象的引用和对象本身是分开存储,读取更耗时,而基本类型数据可直接读取。

        • 存储效率:Integer对象占用16个字节的内存空间,而int类型数据只占用4字节的内存空间,更省内存。

        介绍一下Integer的静态缓存池

        • 存储Integer对象:存储特定范围内的整数值对应的Integer对象,范围-128至127。

        • 通过Integer.valueOf(int)方法创建一个在这个范围内的整数对象时,并不会每次都生成新的对象实例,而是复用缓存中的现有对象


        上述内容也同步在我的飞书,欢迎访问

        https://my.feishu.cn/wiki/QLauws6lWif1pnkhB8IcAvkhncc?from=from_copylink

        如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,你们的支持就是我坚持下去的动力!

        Logo

        汇聚全球AI编程工具,助力开发者即刻编程。

        更多推荐