最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JAVA 對象創(chuàng)建與對象克隆

 更新時間:2022年02月17日 16:27:40   作者:BOKERR  
這篇文章主要介紹了JAVA 對象創(chuàng)建與對象克隆,new 創(chuàng)建、反射、克隆、反序列化,克隆它分為深拷貝和淺拷貝,通過調用對象的 clone方法,進行對象的克隆,下面來看看文章的詳細內容吧

一、對象的4種創(chuàng)建方式

  • new 創(chuàng)建
  • 反射
  • 克隆
  • 反序列化

二、通過new創(chuàng)建對象

一般情況下,對象通過new 關鍵字創(chuàng)建,首先會在堆上給對象分配空間,然后執(zhí)行構造函數(shù)進行一系列的初始化,在分配的內存空間上為一眾屬性賦值;完成初始化后再將堆區(qū)對象的引用傳遞給棧區(qū),最終參與程序的運行。

三、反射

調用Java.lang.Class或者java.lang.reflect.Constructor類的newInstance()實例方法。

四、克隆對象

它分為深拷貝和淺拷貝,通過調用對象的 clone方法,進行對象的克隆(拷貝)

我們可以看到 clone 方法的定義:

  • 首先它是一個 native 方法
  • 它被 protected 修飾,那么,它的訪問權限就只有它的子類或者[同包:java.lang.*];
  • 雖然說類都繼承自Object,當對象A引用對象B,A沒法調用:B.clone() { B -> Object.clone() } 的,因為,Object上的clone()方法被protected修飾,
  • 若A一定要訪問B的clone()方法:B重寫clone()為public修飾、或者通過public方法將 clone() 訪問權限傳遞出去。
  • 此外我們還需要實現(xiàn):Cloneable接口,我們可以看它的定義,實際上空無一物,可以把Cloneable視為一個標記,若不實現(xiàn)它,當我們調用重寫的clone方法進行克隆時會拋出異常:[java.lang.CloneNotSupportedException]

  • clone不會調用類的構造方法,它只是復制堆區(qū)上的對象信息。

為了測試 clone 我定義了兩個類:

用戶信息:UserBean

package com.bokerr.canaltask.po;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

@Data
@AllArgsConstructor
public class UserBean implements Cloneable, Serializable {

? ? private static final long serialVersionUID = 2022010901L;

? ? /** 基本類型、不可變類型 */
? ? private int age;
? ? private int sex;
? ? private String name;
? ? private String home;
? ? /** 引用類型 */
? ? private SubInfo subInfo;

? ? public UserBean(){}

? ? /***
? ? ?* 重寫 clone 為 public 讓任意對象都有 clone的訪問權限
? ? ?* */
? ? @Override
? ? public UserBean clone(){
? ? ? ? UserBean clone = null;
? ? ? ? try{
? ? ? ? ? ? clone = (UserBean) super.clone();
? ? ? ? } catch (CloneNotSupportedException e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return clone;
? ? }
}

附加信息類:SubInfo

package com.bokerr.canaltask.po;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

@Data
@AllArgsConstructor
public class SubInfo implements Cloneable, Serializable {
? ? private static final long serialVersionUID = 2022010902L;
? ? /**
? ? ?* SubInfo 類的屬性都是基本類型、不可變對象(String)
? ? ?* */
? ? private String work;
? ? private Integer salary;
? ? private int idNum;

? ? public SubInfo(){}
? ??
? ? /**
? ? ?* 此處通過public 方法對外提供對象clone方法訪問權限
? ? ?* */
? ? public SubInfo selfClone(){
? ? ? ? try{
? ? ? ? ? ? return (SubInfo) clone();
? ? ? ? } catch (CloneNotSupportedException e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return null;
? ? }
? ? /*@Override
? ? public SubInfo clone(){
? ? ? ? try{
? ? ? ? ? ? return (SubInfo) super.clone();
? ? ? ? } catch (CloneNotSupportedException e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return null;
? ? }*/
}

淺拷貝

我們需要知道,單純調用一個對象的clone方法,它進行的是:"淺表復制",當對象的屬性都是基本類型或者不可變(final)類型時是沒有問題的;但是存在可變對象引用時,對它的拷貝并不是一個深層的拷貝,它只拷貝該對象的引用,這樣就會造成原對象和克隆對象的修改,都會反映到該引用對象上。

關于淺拷貝看如下測試代碼:

package com.bokerr.canaltask.workerrun;

import com.bokerr.canaltask.po.SubInfo;
import com.bokerr.canaltask.po.UserBean;

public class ExecuteTest {

? ? public static void main(String[] args){
? ? ? ? UserBean userBean1 = new UserBean();
? ? ? ? userBean1.setAge(25);
? ? ? ? userBean1.setSex(1);
? ? ? ? userBean1.setName("bokerr");
? ? ? ? userBean1.setHome("貴州銅仁");

? ? ? ? SubInfo subInfo1 = new SubInfo();
? ? ? ? subInfo1.setIdNum(3423);
? ? ? ? subInfo1.setSalary(Integer.valueOf(15000));
? ? ? ? subInfo1.setWork("coder");
? ? ? ? userBean1.setSubInfo(subInfo1);

? ? ? ? System.out.println("userBean-orign:" + userBean1);
? ? ? ? UserBean userBean2 = userBean1.clone();
? ? ? ? userBean2.setHome("貴州貴陽");
? ? ? ? userBean2.setAge(26);
? ? ? ? SubInfo subInfo2 = userBean2.getSubInfo();
? ? ? ? subInfo2.setSalary(Integer.valueOf(25000));
? ? ? ? subInfo2.setWork("manager");
? ? ? ? subInfo2.setIdNum(100002);
? ? ? ? System.out.println("######################");
? ? ? ? System.out.println("userBean-orign:" + userBean1);
? ? ? ? System.out.println("userBean-clone:" + userBean2);
? ? }
}

UserBeanclone 方法定義如下,我們可以看見它只調用了super.clone 而對 super.clone 的返回值沒做任何修改:

@Override
? ? public UserBean clone(){
? ? ? ? UserBean clone = null;
? ? ? ? try{
? ? ? ? ? ? clone = (UserBean) super.clone();
? ? ? ? } catch (CloneNotSupportedException e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return clone;
? ? }

輸出如下,結合測試code,我們可以發(fā)現(xiàn),克隆得到的對象對 SubInfo 的修改同樣體現(xiàn)到了原對象引用的 SubInfo 對象上,因為調用 super.clone 只是一個 "淺表復制"

userBean-orign:UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=coder, salary=15000, idNum=3423))
######################
userBean-orign:UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=manager, salary=25000, idNum=100002))
userBean-clone:UserBean(age=26, sex=1, name=bokerr, home=貴州貴陽, subInfo=SubInfo(work=manager, salary=25000, idNum=100002))

深拷貝

深拷貝生成的對象,必須擁有完全獨立的對象內存空間,拷貝對象和原對象上的修改,都不會影響對方;

前邊提到通過super.clone 調用 Object上的clone方法實際上進行的只是一個淺拷貝

為了實現(xiàn)深拷貝我們則必須修改 UserBean 的clone 方法:

/***
? ? ?* 重寫 clone 為 public 讓任意對象都有 clone的訪問權限
? ? ?* */
? ? @Override
? ? public UserBean clone(){
? ? ? ? UserBean clone = null;
? ? ? ? try{
? ? ? ? ? ? clone = (UserBean) super.clone();
? ? ? ? ? ? /** SubInfo.selfClone() 提供SubInfo對象clone()方法權限 */
? ? ? ? ? ? /** 克隆可變引用對象 SubInfo,并賦值給 super.clone() 返回的:UserBean 完成深拷貝 */
? ? ? ? ? ? SubInfo cloneSub = this.subInfo.selfClone();
? ? ? ? ? ? clone.setSubInfo(cloneSub);
? ? ? ? } catch (CloneNotSupportedException e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return clone;
? ? }

實際上除此之外,測試代碼一成不變,然后我們來看現(xiàn)在的輸出,可以發(fā)現(xiàn)對克隆對象的引用對象:SubInfo 的修改,并未使原對象的SubInfo變化

userBean-orign:UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=coder, salary=15000, idNum=3423))
######################
userBean-orign:UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=coder, salary=15000, idNum=3423))
userBean-clone:UserBean(age=26, sex=1, name=bokerr, home=貴州貴陽, subInfo=SubInfo(work=manager, salary=25000, idNum=100002))

此時問題來了:你可能會說假如我的對象進行了多層引用呢,且引用了多個對象該怎么辦呢?那我只能一個一個去重寫 clone 方法么?

是的你如果使用 clone 方法可能,你確實需要那樣去處理。

假如,存在如下以對象A為根節(jié)點的引用關系:

A ?-> ?B
? ? ? ?C -> E -> F
? ? ? ? ? ? G -> G
? ? ? ?D -> H?
? ? ? ? ? ? I -> J -> K

我相信處理深拷貝的人會瘋掉的。。。。

那么有更省事的方法么? 當然有,那就是下一節(jié)提到的反序列化。

五、反序列化

  • java中的序列化是將對象轉化成一個二進制字節(jié)序列,它可以持久化到磁盤文件,也可通過網絡進行傳輸;
  • 而反序列化是指將該二進制字節(jié)序列,重新還原成一個對象并加載到內存中的過程。
  • 對象的反序列化過程中,沒有調用任何構造函數(shù),整個對象都是通過將,文件流中取得的數(shù)據(jù)恢復,從而得來。
  • 序列化只保存對象的屬性狀態(tài),而不會保存對象的方法。
  • 只有實現(xiàn)了Serializable接口的類才能被序列化,官方建議自定義一個SerialversionUID,若用戶沒有自定義SerialversionUID那么會生成默認值;序列化和反序列化就是通過對比其SerialversionUID來進行的,一旦SerialversionUID不匹配,反序列化就無法成功
  • 如果一個類的屬性包含對象引用,那么被引用的對象也將被序列化,[被引用的對象也必須實現(xiàn)Serializable接口,否則會拋出異常:java.io.NotSerializableException]
  • statictransient修飾的變量不會被序列化,可以理解為:static是類屬性存在于方法區(qū)而不在堆區(qū);transient常用于修飾涉及安全的信息,它只能和Serializable接口一起使用,比如:用戶密碼,不應該讓它序列化之后在網絡上傳輸。

參考代碼:

package com.bokerr.canaltask.workerrun;

import com.bokerr.canaltask.po.NoCloneInfo;
import com.bokerr.canaltask.po.SubInfo;
import com.bokerr.canaltask.po.UserBean;
import org.apache.commons.lang3.SerializationUtils;

import java.util.Arrays;

public class ExecuteTest {

? ? public static void main(String[] args){
? ? ? ? UserBean userBean1 = new UserBean();
? ? ? ? userBean1.setAge(25);
? ? ? ? userBean1.setSex(1);
? ? ? ? userBean1.setName("bokerr");
? ? ? ? userBean1.setHome("貴州銅仁");
? ? ? ? SubInfo subInfo1 = new SubInfo();
? ? ? ? subInfo1.setIdNum(3423);
? ? ? ? subInfo1.setSalary(Integer.valueOf(15000));
? ? ? ? subInfo1.setWork("coder");
? ? ? ? userBean1.setSubInfo(subInfo1);

? ? ? ? System.out.println("序列化前" + userBean1);
? ? ? ? /** 對象序列化為二進制字節(jié)序列 */
? ? ? ? byte[] serializeBytes = SerializationUtils.serialize(userBean1);
? ? ? ? /** 反序列化還原為Java對象 */
? ? ? ? UserBean userBeanSer = SerializationUtils.deserialize(serializeBytes);
? ? ? ? userBeanSer.getSubInfo().setSalary(800000);
? ? ? ? userBeanSer.getSubInfo().setWork("CTO");
? ? ? ? System.out.println("反序列化" + userBeanSer);
? ? ? ? System.out.println(userBean1 == userBeanSer);
? ? }
}

輸出:

序列化前UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=coder, salary=15000, idNum=3423))
反序列化UserBean(age=25, sex=1, name=bokerr, home=貴州銅仁, subInfo=SubInfo(work=CTO, salary=800000, idNum=3423))
false

我們可以發(fā)現(xiàn)最終輸出了:subInfo.work=CTO subInfo.salary=800000,對反序列化得到的對象引用的SubInfo對象的修改,并未影響到原對象,所以可以通過反序列化進行對象的深拷貝。

六、補充

PS*有一說一:lombok 是真的好用,雖然 set、get 方法可以自動生成,但是用 lombok后明顯代碼更簡潔了 ;

commons-lang3 它是apache的一個工具包,我用的序列化工具來自它。

可能有小伙伴不了解,我還是貼一下Maven依賴吧,雖然我知道大家都人均大佬了。

<dependency>
? ? ? ? ? ? <groupId>org.projectlombok</groupId>
? ? ? ? ? ? <artifactId>lombok</artifactId>
? ? ? ? ? ? <optional>true</optional>
? ? ? ? ? ? <scope>compile</scope>
? ? ? ? </dependency>


? ? ? ? <dependency>
? ? ? ? ? ? <groupId>org.apache.commons</groupId>
? ? ? ? ? ? <artifactId>commons-lang3</artifactId>
? ? ? ? ? ? <version>3.12.0</version>
? ? ? ? </dependency>

到此這篇關于JAVA 對象創(chuàng)建與對象克隆的文章就介紹到這了,更多相關JAVA 對象的創(chuàng)建與克隆內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • SpringSecurity安全框架的使用

    SpringSecurity安全框架的使用

    SpringSecurity是一個用于企業(yè)應用系統(tǒng)的安全框架,可以控制用戶登錄權限,實現(xiàn)不同權限用戶訪問不同內容,文章介紹了SpringSecurity的簡單配置和使用,包括環(huán)境搭建、測試用例和自定義登錄頁面的配置
    2025-02-02
  • Java基本類型與byte數(shù)組之間相互轉換方法

    Java基本類型與byte數(shù)組之間相互轉換方法

    下面小編就為大家?guī)硪黄狫ava基本類型與byte數(shù)組之間相互轉換方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08
  • SpringBoot使用hutool-captcha實現(xiàn)驗證碼生成與驗證

    SpringBoot使用hutool-captcha實現(xiàn)驗證碼生成與驗證

    在springboot的登陸頁面中為了防止機器大規(guī)模注冊,機器暴力破解數(shù)據(jù)密碼等危害,需要驗證隨機生成的驗證碼,本文主要介紹了SpringBoot使用hutool-captcha實現(xiàn)驗證碼生成與驗證,感興趣的可以了解一下
    2023-12-12
  • java基礎實現(xiàn)猜數(shù)字小游戲

    java基礎實現(xiàn)猜數(shù)字小游戲

    這篇文章主要為大家詳細介紹了java基礎實現(xiàn)猜數(shù)字小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • java簡單手寫版本實現(xiàn)時間輪算法

    java簡單手寫版本實現(xiàn)時間輪算法

    這篇文章主要為大家詳細介紹了java簡單手寫版本實現(xiàn)時間輪算法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • java 頁面url傳值中文亂碼的解決方法

    java 頁面url傳值中文亂碼的解決方法

    本節(jié)講的是ajax 的URL參數(shù)中有中文值,傳到服務端,在用request.getParameter()方法,得到的常常會是亂碼。
    2013-03-03
  • Java使用異或運算實現(xiàn)簡單的加密解密算法實例代碼

    Java使用異或運算實現(xiàn)簡單的加密解密算法實例代碼

    這篇文章主要介紹了Java使用異或運算實現(xiàn)簡單的加密解密算法實例代碼,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • 詳解Spring Cloud Consul 實現(xiàn)服務注冊和發(fā)現(xiàn)

    詳解Spring Cloud Consul 實現(xiàn)服務注冊和發(fā)現(xiàn)

    這篇文章主要介紹了Spring Cloud Consul 實現(xiàn)服務注冊和發(fā)現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • Java鍵盤錄入Scanner類的使用方法詳析

    Java鍵盤錄入Scanner類的使用方法詳析

    在Java編程中,引用數(shù)據(jù)類型是用來存儲對象的引用(地址),而Scanner類是引用數(shù)據(jù)類型的一種,用于讀取輸入數(shù)據(jù),文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-09-09
  • Spring Boot如何動態(tài)創(chuàng)建Bean示例代碼

    Spring Boot如何動態(tài)創(chuàng)建Bean示例代碼

    這篇文章主要給大家介紹了關于Spring Boot如何動態(tài)創(chuàng)建Bean的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2017-09-09

最新評論

阳信县| 罗田县| 白城市| 龙泉市| 贞丰县| 正定县| 永州市| 田阳县| 广宗县| 云林县| 体育| 开平市| 通城县| 郸城县| 长寿区| 绍兴县| 来安县| 沾化县| 龙岩市| 抚顺县| 乌兰县| 西充县| 安陆市| 四平市| 阜平县| 泾川县| 自贡市| 威远县| 赤城县| 阿克陶县| 宁南县| 于都县| 收藏| 湛江市| 枞阳县| 拉孜县| 白城市| 平南县| 大埔区| 太康县| 区。|