java基础之拷贝、单例

一、拷贝

常见的拷贝有引用拷贝、浅拷贝、深拷贝

1.引用拷贝

就是我们常见的=赋值

实现方式 : Person p2=p1;

特点:两个对象名指向同一个内存空间,任何对象对其属性进行修改,都会直接影响到另一个变量,属于对同一个对象的操作,所以算不上真正意义上的赋值,如图:

代码例子:

java 复制代码
public class CopyTest {
    public static void main(String[] args) {
        Address addr = new Address("北京");
        User user1 = new User(20, addr);
        User user2 = user1; 

        // 修改user2,user1也会变
        user2.age = 30;
        user2.address.city = "上海";

        System.out.println(user1.age); // 30(被修改)
        System.out.println(user1.address.city); // 上海(被修改)
    }
}

2.浅拷贝

如果对象里面属性全是基本数据类型,那么浅拷贝实际上是创建了两个空间,一个对象对其属性进行修改,不会影响另一个对象,但是!如果对象属性有引用数据类型,那么两个对象的引用数据类型的变量就会指向同一个地址,没有实现完全独立

也就是说浅拷贝会直接赋值内部引用数据类型变量的引用地址,拷贝对象和原对象的属性共用一个对象(引用数据)

实现方法:类实现 Cloneable 接口;重写 clone() 调用 super.clone()

代码例子:

java 复制代码
public static void main(String[] args) throws CloneNotSupportedException {
    Address addr = new Address("北京");
    User user1 = new User(20, addr);

    // ========== 浅拷贝 ==========
    User user2 = (User) user1.clone();

    // 1. 修改基本类型:互不影响
    user2.age = 30;
    System.out.println(user1.age); // 20(独立)

    // 2. 修改引用类型:互相影响!
    user2.address.city = "上海";
    System.out.println(user1.address.city); // 上海(共享)
}

3.深拷贝

不管是引用数据类型还是基本数据类型,都会生成一份新的,对内部数据进行修改不会互相影响;

实现方法:重写 clone () + 递归拷贝引用对象;序列化 / 反序列化(最简单通用)

代实现方法一代码例子:

java 复制代码
//手动递归clone
@Override
protected Object clone() throws CloneNotSupportedException {
    // 1. 先浅拷贝当前对象
    User user = (User) super.clone();
    // 2. 手动拷贝引用类型成员 → 变成深拷贝
    user.address = (Address) this.address.clone();
    return user;
}

//测试
public static void main(String[] args) throws CloneNotSupportedException {
    Address addr = new Address("北京");
    User user1 = new User(20, addr);

    // ========== 深拷贝 ==========
    User user2 = (User) user1.clone();

    // 修改引用类型:互不影响!
    user2.address.city = "上海";
    System.out.println(user1.address.city); // 北京(完全独立)
}

二、单例

1.定义

是一种设计模式,该模式保证在整个应用中,一个类只能有唯一一个实例对象 ,全局共享这一个对象,私有化构造方法,禁止外部 new。这个类被称为单例类

2.实现思路

在一般类里面,如果想要限制只能创建一个对象,那么我们会想到用private来修饰对象,但是其他类想要调用的时候获取不到,那么我们使用get方法来获取,而get方法需要其他类来创建对象才能调用该类的方法,那么我们会想到使用static来修饰这个get方法,让它属于类,那么我就可以直接用类名来调用,与之对应的private修饰的对象也要变成static属于类;经过以上步骤就能粗略的实现只允许创建一个对象的功能

3.实现方法

(1)饿汉式

优点:实现简单,类加载时直接创建实例,多线程下安全

缺点:实例对象在类加载时就初始化,不管是否使用,都会创建,造成空间浪费。

(2)懒汉式-不安全版本

优点: 在饿汉式的基础上添加了if判断,当使用对象,对象没创建的时候才创建,避免了不用也创建的缺点

缺点:在多线程下,如果多个线程同时进入判断语句,可能会创建多个实例,违反了单例原则

(3)懒汉式-安全版本

在不安全的版本中添加一道锁:synchronized

优点:加锁保证安全

缺点:每次获取都加锁,并发性能差。

java 复制代码
public static synchronized Singleton getInstance(){
    if(instance == null){
        instance = new Singleton();
    }
    return instance;
}

(4)双重检查锁

优点:延迟加载 + 线程安全 + 效率高,必须加 volatile 禁止指令重排

(5)匿名内部类

优点:利用类加载机制保证线程安全,实现了懒加载、无锁、效率高。

java 复制代码
public class Singleton {
    private Singleton(){}
    // 静态内部类
    private static class Inner{
        private static final Singleton instance = new Singleton();
    }
    public static Singleton getInstance(){
        return Inner.instance;
    }
}

4.应用场景

资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。

控制资源的情况下,方便资源之间的互相通信。如线程池等。

相关推荐
2601_9621066433 分钟前
【Java Set综合实战】商品管理系统 + 图书收藏系统|Product、Book自定义类全套代码
java
RuoyiOffice1 小时前
SpringBoot+Vue3 凭证生命周期设计:审核、过账、冲销与反过账怎样保持账目一致
java·spring boot·事务·springboot3·财务系统·会计凭证·ruoyi office
2601_962106642 小时前
HashMap入门|工号-姓名键值存储、两种遍历方式、根据key/value查
java
杨凯凡2 小时前
Spring Framework 7 API 版本管理实战:请求头路由、兼容迁移与废弃策略
java·rest api·springframework·spring boot 4·api版本管理
T_Wizard3 小时前
同一个问题写了 4 遍:官方 SDK / HTTP / Spring AI / LangChain4j,到底该选哪个
java·ai编程
hai_android3 小时前
AudioTrack 深度解析:从原理到实战
android·java·kotlin
斯维赤3 小时前
限流、熔断、降级,你真的分得清吗?
java·后端·架构
aiks_yuan6 小时前
Java CPU 100% 怎么排查:JDK 11 起 jstack 自带线程 CPU 时间,不用再 top -H 换算十六进制(JDK 8、11、17 实测)
java
Flynn Liang7 小时前
AiServices 源码级拆解:一个接口如何变成 RAG Agent
java·langchain·源码分析·rag·langchain4j·aiservices
Flynn Liang7 小时前
LangChain4j 1.23 架构全景:Java 大模型应用的 6 层抽象
java·langchain·大模型·rag·ai agent·langchain4j