尚硅谷Java面试题第四季-Java基本功

1.服务可用性多少个9是什么意思?


2.Arrays.asList()把数组转换成集合大坑

代码:

java 复制代码
public class Arrays_BugDemo
{
    public static void main(String[] args)
    {
        List<Integer> list = Arrays.asList(1,2);
        list.add(3);

        list.forEach(System.out::println);
    }
}

阿里手册

阿里巴巴Java开发规范,使用工具类 Arrays.asList() 方法把数组转换成集合时,不能使用其修改集合相关的方法。

因为它的 add/remove/clear 方法会抛出 UnsupportedOperationException(),我们来看一下为什么会出现这种情况?

源码分析

Arrays.ArrayList类继承自AbstractList,实现了List接口。它重写了add()、remove()等修改List结构的方法,并将它们直接抛出UnsupportedOperationException异常,从而禁止了对List结构的修改 。具体来说,Arrays.asList()方法返回的是Arrays类中的一个私有静态内部类ArrayList,它继承自AbstractList类,实现了List接口

最佳实践

如果使用了 Arrays.asList0()的话,最好不要使用其集合的操作方法

如果非要使用:
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
可以在外面这样包一层真正的 ArrayList

java 复制代码
public class Arrays_BugDemo
{
    public static void main(String[] args)
    {
        //List<Integer> list = Arrays.asList(1,2);
        List<Integer> list = new ArrayList<>(Arrays.asList(1,2));
        list.add(3);

        list.forEach(System.out::println);
    }
}

3.遍历集合时remove或add操作注意事项

代码

java 复制代码
package com.atguigu.interview2.javase;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * @auther zzyy
 * @create 2024-05-09 10:18
 */
public class IteratorRemoveDemo
{
    public static void main(String[] args)
    {
        List<Integer> list = new ArrayList<>();
        list.add(11);
        list.add(12);
        list.add(13);
        list.add(14);
        list.add(15);

        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext())
        {
            Integer value = iterator.next();
            if(value == 12)
            {
                list.remove(value);
            }
        }

        list.forEach(v -> System.out.println(v));
    }
}

阿里手册

最佳实践

java 复制代码
package com.atguigu.interview2.javase;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * @auther zzyy
 * @create 2024-05-09 10:18
 */
public class IteratorRemoveDemo
{
    public static void main(String[] args)
    {
        List<Integer> list = new ArrayList<>();
        list.add(11);
        list.add(12);
        list.add(13);
        list.add(14);
        list.add(15);

        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext())
        {
            Integer value = iterator.next();
            if(value == 12)
            {
                //list.remove(value); //Exception in thread "main" java.util.ConcurrentModificationException
                iterator.remove();
            }
        }
        list.forEach(v -> System.out.println(v));
    }
}

4.hashcode冲突案例

java 复制代码
package com.atguigu.interview2.javase;

import java.util.HashSet;

/**
 * @auther zzyy
 * @create 2024-05-01 15:49
 */
public class HashConflictDemo
{
    static class Book
    {
        int id;
    }

    public static void main(String[] args)
    {
        //请你写一个hashcode冲突的案例V2
        // 当我们正常的new对象的时候,new多少次对象,会有可能出现hash冲突???
		// 10w次左右
        HashSet<Integer> hashSet = new HashSet<>();

        for (int i = 1; i <= 15 * 10000 ; i++)
        {
            int bookHashCode = new Object().hashCode();
            if(!hashSet.contains(bookHashCode))
            {
                hashSet.add(bookHashCode);
            }else{
               System.out.println("发生了hash冲突,在第:"+i+"次,值是:"+bookHashCode);
            }
        }
        System.out.println(hashSet.size());
    }

    private static void m1()
    {
        //请你写一个hashcode冲突的案例
        System.out.println("AA".hashCode());
        System.out.println("BB".hashCode());
        System.out.println();

        System.out.println("Aa".hashCode());
        System.out.println("BB".hashCode());
        System.out.println();

        System.out.println("柳柴".hashCode());
        System.out.println("柴柕".hashCode());
    }
}

5.整型包装类对象Integer

Integer的构造方法从Java8以后有变动?



Integer在使用时数值如何比较?

java 复制代码
package com.atguigu.interview2.javase;

/**
 * @auther zzyy
 * @create 2024-05-02 17:15
 *
 * 阿里手册
 *
 * 【强制】所有整型包装类对象之间值的比较,全部使用equals方法比较。
 * 说明:对于Integer var = ? 在 -128至127之间的赋值,Integer 对象是在IntegerCache.cache 产生,会复用已有对
 * 象,这个区间内的Integer 值可以直接使用 == 进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复
 * 用已有对象,这是一个大坑,推荐使用equals方法进行判断。
 */
public class Integer_BugDemo
{
    public static void main(String[] args)
    {
        
        //构造方法可行?
        //Integer i = new Integer(1200);
        //Integer i2 = Integer.valueOf(1200);
        
        Integer a = Integer.valueOf(600);
        Integer b = Integer.valueOf(600);
        int c = 600;
        System.out.println(a == b);
        System.out.println(a.equals(b));
        System.out.println(a == c);

        System.out.println("===================");

        Integer x = Integer.valueOf(99);
        Integer y = Integer.valueOf(99);
        System.out.println(x == y);
        System.out.println(x.equals(y));

		输出:
		false
		true
		true
		===================
		true
		true
    }
}

将基本类型int赋值给包装类型Integer时,自动装箱,调用的是 Integer.valueOf方法 ,查看源码可知(如下代码),在-127与128之间的基本类型做自动装箱操作时,会返回一个IntegerCache的缓存池内容,所以比较地址是相同的

6.BigDecimal的大坑

BigDecimal概述

​Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。

**​BigDecimal所创建的是对象故我们不能使用传统的+,-,×÷等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。**方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

Entity VS SQL


故障代码演示

java 复制代码
package com.atguigu.interview2.javase;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * @auther zzyy
 * @create 2024-04-30 19:01
 */
public class BigDecimal_BugDemo
{
    public static void main(String[] args)
    {
        //double类型精度丢失
        //doubleDemo();

        //===================BigDecimal===================
        //m1();
        //m2();
        //m3();
        m4();
    }

    /**
     * 科学计数法相关
     *
     */
    private static void m4()
    {
        BigDecimal amount1 = BigDecimal.valueOf(1234567890123456789.3141592631415926);
        System.out.println(amount1);                    //科学计数法:    1.23456789012345677E+18
        System.out.println(amount1.toString());         //科学计数法:    1.23456789012345677E+18
        System.out.println(amount1.toPlainString());    //不想使用科学计数法

        System.out.println();
        BigDecimal amount2 = new BigDecimal("1234567890123456789.3141592631415926");
        System.out.println(amount2);
        System.out.println(amount2.toString());
        System.out.println(amount2.toPlainString());

    }

    /**除法商的结果,需要指定精度
     * BigDecimal 的 8 种 RoundingMode(舍入模式)
     * https://my.oschina.net/u/3644969/blog/4927776
     *
     * ROUND_HALF_UP  也就是我们常说的我们的 "四舍五入"。
     * 向 "最接近" 的数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。
     * 如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。
     * 这种模式也就是我们常说的我们的 "四舍五入"。
     *
     * ROUND_HALF_DOWN
     *
     * 向 "最接近" 的数字舍入,如果与两个相邻数字的距离相等,则为向下舍入的舍入模式。
     * 如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。
     * 这种模式也就是我们常说的我们的 "五舍六入"。
     */
    private static void m3()
    {
        BigDecimal amount1 = new BigDecimal("2.0");
        BigDecimal amount2 = new BigDecimal("3.0");
        //System.out.println(amount1.divide(amount2)); //Non-terminating decimal expansion; no exact representable decimal result.
        //我们该如何处理?
        System.out.println(amount1.divide(amount2, 2,RoundingMode.HALF_UP)); //这种模式也就是我们常说的我们的 "四舍五入"。
    }

    /**参考阿里手册
     * 10.【强制】BigDecimal的等值比较应使用compareTo() 方法,而不是equals() 方法。
     */
    private static void m2()
    {
        BigDecimal amount1 = new BigDecimal("0.9");
        BigDecimal amount2 = new BigDecimal("0.90");
        //给同学们看一下equals源码,它有个精度范围比较导致出错
        System.out.println("equals比较结果:"+amount1.equals(amount2));
        System.out.println("compareTo比较结构:"+amount1.compareTo(amount2));
    }

    /**
     * 参考阿里手册
     * 12.【强制】禁止使用构造方法BigDecimal(double) 的方式把double值转化为BigDecimal对象
     * 优先推荐入参为String的构造方法,或使用BigDecimal的valueOf方法,此方法内部其实执行了Double的
     * toString,而Double的toString按double的实际能表达的精度对尾数进行了截断。
     */
    private static void m1()
    {
        //看看构造方法源码第934行
        BigDecimal amount1 = new BigDecimal(0.03);
        BigDecimal amount2 = new BigDecimal(0.02);
        //12.【强制】禁止使用构造方法BigDecimal(double) 的方式把double值转化为BigDecimal对象,非要转那?
        System.out.println("amount1: "+amount1);
        System.out.println("amount2: "+amount2);
        //应该等于0.01
        System.out.println("amount1 - amount2等于: "+amount1.subtract(amount2));
        System.out.println();
        //优先推荐入参为String的构造方法
        BigDecimal amount3 = new BigDecimal("0.03");
        BigDecimal amount4 = new BigDecimal("0.02");
        //应该等于0.01
        System.out.println(amount3.subtract(amount4));
        System.out.println();

        //使用BigDecimal的valueOf方法,给同学们看一下valueOf源码
        BigDecimal amount5 = BigDecimal.valueOf(0.03);
        BigDecimal amount6 = BigDecimal.valueOf(0.02);
        //应该等于0.01
        System.out.println(amount5.subtract(amount6));
    }


    /**
     * why
     * double类型的两个参数相减会转换成二进制,
     * 因为double有效位数为16位这就会出现存储小数位数不够的情况,这种情况下就会出现误差
     */
    private static void doubleDemo()
    {
        double amount1 = 0.03;
        double amount2 = 0.02;
        //执行结果会是多少?
        System.out.println(amount1 - amount2);

    }
}

小总结

  1. 当使用float/double这些浮点数据时,会丢失精度String/int则不会,BigDecimal(double) 存在精度损失风险。
  2. 等值比较应使用compareTo() 方法。equals() 方法会比较值和精度(1.0与 1.00返回结果为 false),而compareTo() 则会忽略精度。
  3. 除法商的结果需要指定精度
  4. 科学计数法的使用问题

编码最佳实践

java 复制代码
package com.atguigu.interview2.utils;

import java.math.BigDecimal;

/**用于高精确处理常用的数学运算
 * @auther zzyy
 * @create 2024-05-02 17:21
 */
public class ArithmeticUtils
{
    //默认除法运算精度
    private static final int DEF_DIV_SCALE = 10;

    /**
     * 提供精确的加法运算
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */

    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    /**
     * 提供精确的加法运算
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2);
    }

    /**
     * 提供精确的加法运算
     *
     * @param v1    被加数
     * @param v2    加数
     * @param scale 保留scale 位小数
     * @return 两个参数的和
     */
    public static String add(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2);
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @return 两个参数的差
     */
    public static String sub(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2);
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2, int scale) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return round(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     */
    public static String mul(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
     * 小数点以后10位,以后的数字四舍五入
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */

    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
}

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
     * 定精度,以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
     * 定精度,以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示需要精确到小数点以后几位
     * @return 两个参数的商
     */
    public static String div(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v1);
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static String round(String v, int scale) {
        if (scale < 0)
        {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(v);
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 取余数
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static String remainder(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 取余数  BigDecimal
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0)
        {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 比较大小
     *
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于v2 则 返回true 否则false
     */
    public static boolean compare(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        int bj = b1.compareTo(b2);
        boolean res;
        if (bj > 0)
            res = true;
        else
            res = false;
        return res;
    }
}

7.如何去除list中的重复元素?

java 复制代码
package com.atguigu.interview2.javase;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @auther zzyy
 * @create 2024-05-01 14:59
 *
 */
public class ListRemoveDuplicates
{
    /**
     * list去除重复元素你能想到几个方法,校招题
     * 有些面试官不让你用API,要求纯手写
     *
     * 如果你能找到更多更好方法,请给阳哥邮件:  zzyybs@126.com
     *
     * @param args
     */
    public static void main(String[] args)
    {
        //m1();
        //m2();
        //m3();
        //m4();
        //m5();
    }


    /**
     * 双for循环对比
     */
    private static void m5()
    {
        List<Integer> initList = Arrays.asList(70,70,-1,5,3,3,4,4,4,4,99);
        List<Integer> srcList = new ArrayList<>(initList);
        List<Integer> newList = new ArrayList<>(initList);

        for (int i = 0; i < newList.size()-1; i++) {
            System.out.println("外层循环第:"+i+" "+newList);
            for (int j = newList.size()-1; j > i ; j--) {
                if(newList.get(j).equals(newList.get(i))){
                    newList.remove(j);
                }
                System.out.println("   内存循环第:"+j+" "+newList);
            }
            System.out.println();
            System.out.println();
        }
        newList.forEach((s) -> System.out.print(s+" "));
        System.out.println();
        System.out.println();
    }

    /**
     * 循环坐标去除重复
     */
    private static void m4()
    {
        List<Integer> initList = Arrays.asList(70,70,-1,5,3,3,4,4,4,4,99);
        List<Integer> srcList = new ArrayList<>(initList);
        List<Integer> newList = new ArrayList<>(initList);

        System.out.println(srcList.indexOf(70));
        System.out.println(srcList.lastIndexOf(70));


        for (Integer element : srcList) {
            if(newList.indexOf(element) != newList.lastIndexOf(element)) {
                newList.remove(newList.lastIndexOf(element));
            }
        }
        newList.forEach((s) -> System.out.print(s+", "));
        System.out.println();
        System.out.println();
    }

    /**
     * Stream流式计算
     */
    private static void m3()
    {
        List<Integer> initList = Arrays.asList(70,70,-1,5,3,3,4,4,4,4,99);
        List<Integer> srcList = new ArrayList<>(initList);
        List<Integer> newList = null;

        newList = srcList.stream().distinct().collect(Collectors.toList());

        newList.forEach((s) -> System.out.print(s+", "));
    }

    /**
     * HashSet或者LinkedHashSet去重复
     */
    private static void m2()
    {
        List<Integer> srcList = Arrays.asList(70,70,-1,5,3,3,4,4,4,4,99);
        List<Integer> newList = new ArrayList<>(new HashSet<>(srcList));
        newList.forEach((s) -> System.out.print(s+" "));
        System.out.println();
        System.out.println();

        newList = new ArrayList<>(new LinkedHashSet<>(srcList));
        newList.forEach((s) -> System.out.print(s+" "));
        System.out.println();
        System.out.println();
    }

    /**
     * for循环遍历判断是否含有,没有就新增到newList里
     */
    private static void m1()
    {
        List<Integer> initList = Arrays.asList(70,70,-1,5,3,3,4,4,4,4,99);
        List<Integer> srcList = new ArrayList<>(initList);
        List<Integer> newList = new ArrayList<>();

        for (int i = 0; i < srcList.size(); i++)
        {
            if(!newList.contains(srcList.get(i)))
            {
                newList.add(srcList.get(i));
            }
        }
        System.out.println(newList);
        System.out.println();
    }

}

==和equals对比

java 复制代码
package com.atguigu.interview2.javase;

import java.util.HashSet;
import java.util.Set;
import com.atguigu.interview2.entities.Person;

/**
 * @auther zzyy
 * @create 2024-05-01 16:11
 *   == 和 equals
 *  1 比较范围
 *    1.1 == 既可以比较基本类型也可以比较引用类型,

 *
 *    1.2 equals
 *    只能比较引用类型,equals(Object obj)
 *
 *  2 比较规则
 *     ==对于基本类型值是否相等,对于引用类型内存地址是否相等
 *    equals比较规则,看是否被重写过。
 *    没有被重写,出厂默认就是==
 *    如果被重写,具体看实现方法
 */
public class TestEquals
{
    public static void main(String[] args)
    {

        String s1 = new String("abc");
        String s2 = new String("abc");
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        Set<String> set01 = new HashSet<>();
        set01.add(s1);
        set01.add(s2);
        System.out.println(set01.size());
        System.out.println(s1.hashCode()+"\t"+s2.hashCode());
        System.out.println("================================");


        Person p1 = new Person("abc");
        Person p2 = new Person("abc");
        System.out.println(p1 == p2);
        System.out.println(p1.equals(p2));
        Set<Person> set02 = new HashSet<>();
        set02.add(p1);
        set02.add(p2);
        System.out.println(set02.size());
        System.out.println(p1.hashCode()+"\t"+p2.hashCode());
        System.out.println("================================");
        System.out.println();
    }
}

9.传值还是传引用

java 复制代码
package com.atguigu.interview2.javase;

import com.atguigu.interview2.entities.Person;

/**
 * @auther zzyy
 * @create 2024-05-01 21:11
 */
public class TransmitValueOrRef {
    public void changeValue1(int age) {
        age = 30;
    }

    public void changeValue2(Person person) {
        person.setPersonName("xxx");
    }

    public void changeValue3(String str) {
        //字符串常量池,添加 "xxx",str指向"xxx"
        str = "xxx";
    }

    public static void main(String[] args) {
        TransmitValueOrRef test = new TransmitValueOrRef();
        //基本类型
        int age = 20;
        test.changeValue1(age);
        System.out.println("age----" + age);//要求你打印出来的age到底是那个方法的变量?main方法中的

        //Person(类模板,在方法区), person(引用,在栈里面),new Person("abc")(new的在堆里);
        Person person = new Person("abc");
        test.changeValue2(person);
        System.out.println("personName-----" + person.getPersonName());

        //字符串常量池,添加 "abc"
        String str = "abc";
        test.changeValue3(str);
        System.out.println("String-----" + str);
    }
}

10.深拷贝+浅拷贝

对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部数据。

浅拷贝(Shallow Copy)

  • 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,类似一个分支。
  • 拷贝基本类型:拷贝的就是基本类型的值。
  • 拷贝引用类型:拷贝的就是内存地址,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
  • 多个引用指向同一个对象,如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝(Deep Copy)

  • 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象,分灶吃饭截然不同。深拷贝则是拷贝了源对象的所有值,所以即使源对象的值发生变化时,拷贝对象的值也不会改变。
  • 新有一个对象出现,两个不同对象

为什么要使用深拷贝?

避免共享引用

当你复制一个对象时,如果不使用深拷贝,那么复制的实际上是对原对象的引用,而不是真正的副本。这意味着对副本的任何修改都会影响到原对象。深拷贝则能确保复制的是对象的真正副本,与原对象没有引用关系。
线程安全

在多线程环境中,如果多个线程同时访问和修改同一个对象,可能会导致数据不一致和竞态条件。通过深拷贝创建对象的副本,每个线程都可以在自己的副本上进行操作,从而避免了线程安全问题。

代码验证

Cloneable接口

知识点

如果某个类没有实现Cloneable接口直接使用clone()方法,抛出异常。

先实现cloneab1e接口后调用clone方法才OK

浅拷贝-代码实践

代码证明Object类里面的clone方法是浅拷贝

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
class Boss implements Cloneable
{
    private String bossName;
    private String title;

    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}
java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
class Emp implements Cloneable
{
    private String empName;
    private Integer age;

    private Boss boss;

    public Emp(String empName, Integer age, String bossName,String title)
    {
        this.empName = empName;
        this.age = age;
        this.boss = new Boss(bossName,title);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}

测试:

java 复制代码
package com.atguigu.interview2.javase;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @auther zzyy
 * @create 2024-05-29 17:29
 */
public class ShallowDeepCopyDemo
{
    public static void main(String[] args) throws CloneNotSupportedException
    {
        m1();
    }

    private static void m1() throws CloneNotSupportedException
    {
        Emp emp = new Emp("z3",15,"雷军","CEO");
        System.out.println("原始对象:"+emp.getBoss().getTitle());

        Emp emp2 = (Emp)emp.clone();
        System.out.println("拷贝对象:"+emp2.getBoss().getTitle());

        System.out.println();
        emp2.getBoss().setTitle("CTO");
        System.out.println("------emp2拷贝对象修改Title=CTO,是否会影响原始对象");


        System.out.println("原始对象:"+emp.getBoss().getTitle());
        System.out.println("拷贝对象:"+emp2.getBoss().getTitle());
    }
}


@Data
@AllArgsConstructor
@NoArgsConstructor
class Boss implements Cloneable
{
    private String bossName;
    private String title;

    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Emp implements Cloneable
{
    private String empName;
    private Integer age;

    private Boss boss;

    public Emp(String empName, Integer age, String bossName,String title)
    {
        this.empName = empName;
        this.age = age;
        this.boss = new Boss(bossName,title);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}

深拷贝-代码实践

修改Emp类

java 复制代码
package com.atguigu.interview2.javase;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @auther zzyy
 * @create 2024-05-29 17:29
 */
public class ShallowDeepCopyDemo
{
    public static void main(String[] args) throws CloneNotSupportedException
    {
        m1();
    }

    private static void m1() throws CloneNotSupportedException
    {
        Emp emp = new Emp("z3",15,"雷军","CEO");
        System.out.println("原始对象:"+emp.getBoss().getTitle());

        Emp emp2 = (Emp)emp.clone();
        System.out.println("拷贝对象:"+emp2.getBoss().getTitle());

        System.out.println();
        emp2.getBoss().setTitle("CTO");
        System.out.println("------emp2拷贝对象修改Title=CTO,是否会影响原始对象");


        System.out.println("原始对象:"+emp.getBoss().getTitle());
        System.out.println("拷贝对象:"+emp2.getBoss().getTitle());
    }
}


@Data
@AllArgsConstructor
@NoArgsConstructor
class Boss implements Cloneable
{
    private String bossName;
    private String title;

    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Emp implements Cloneable
{
    private String empName;
    private Integer age;

    private Boss boss;

    public Emp(String empName, Integer age, String bossName,String title)
    {
        this.empName = empName;
        this.age = age;
        this.boss = new Boss(bossName,title);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException
    {
        return new Emp(empName,age,boss.getBossName(),boss.getTitle());
    }
}
相关推荐
小道仙9718 小时前
Dubbo如何使用Nacos做注册中心的
java·nacos·dubbo·服务注册
q***985218 小时前
【保姆级教程】apache-tomcat的安装配置教程
java·tomcat·apache
不说别的就是很菜18 小时前
【前端面试】Vue篇
前端·vue.js·面试
CodeAmaz18 小时前
统一发包管理(Maven 仓库)详细步骤
java·maven·运维开发·个人开发
在等晚安么18 小时前
力扣面试150题打卡
算法·leetcode·面试
没有bug.的程序员18 小时前
Spring Cloud Bus 事件广播机制
java·开发语言·spring boot·hystrix·feign·springcloudbus·事件广播机制
找不到、了19 小时前
Java系统设计知识整理《1》
java·开发语言
程序猿七度19 小时前
【Excel导入】读取WPS格式嵌入单元格内的图片
java·开发语言·wps
用户2986985301419 小时前
Java: 为PDF批量添加图片水印实用指南
java·后端·api
月弦笙音19 小时前
【vue3】这些不常用的API,却很实用
前端·vue.js·面试