Java復制一個對象并且不想復制其中的空值屬性問題
Java復制一個對象并且不想復制其中的空值屬性
Java復制一個對象并且不想復制其中的空值屬性,你可以通過幾種方式來實現(xiàn)。
Java類庫
1.使用Java中的BeanUtils類。
這個類提供了一個copyProperties()方法,可以用來復制一個對象的屬性到另一個對象。
默認情況下,copyProperties()方法會復制所有屬性,包括空值屬性。
但是,你可以使用BeanUtils中的BeanUtilsBean類來設置nullsAreIgnored屬性為true,這樣就可以忽略掉源對象中的空值屬性。
示例代碼如下:
BeanUtilsBean beanUtilsBean = BeanUtilsBean.getInstance(); beanUtilsBean.getConvertUtils().register(false, true, 0); beanUtilsBean.copyProperties(destObject, srcObject);
第三方庫
1.Apache Commons Lang庫中的ObjectUtils類。
這個類提供了一些有用的方法來操作對象,包括復制對象并忽略空值屬性的方法。
示例代碼如下:
ObjectUtils.clone(srcObject, new CloneNullsStrategy());
其中,CloneNullsStrategy類是一個實現(xiàn)了org.apache.commons.lang3.ObjectUtils.Null的自定義策略,它用于在復制對象時忽略空值屬性。
2.hutool的BeanUtil類。
示例代碼如下:
//null,表示無限制,true表示若父類中屬性值為空則忽略,不傳給子類 private final CopyOptions copyOption = CopyOptions.create(null, true); BeanUtil.copyProperties(srcObject, destObject, copyOption);
Java對象復制的三種方式
在實際編程過程中,我們常常要遇到這種情況:有一個對象A,在某一時刻A中已經(jīng)包含了一些有效值,此時可能 會需要一個和A完全相同新對象B,并且此后對B任何改動都不會影響到A中的值,也就是說,A與B是兩個獨立的對象,但B的初始值是由A對象確定的。
例如下面程序展示的情況:
class Student { private int number; public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } } public class Test { public static void main(String args[]) { Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = stu1; stu1.setNumber(54321); System.out.println("學生1:" + stu1.getNumber()); System.out.println("學生2:" + stu2.getNumber()); } }
結果:
學生1:54321
學生2:54321
為什么改變學生2的學號,學生1的學號也發(fā)生了變化呢?
原因出在(stu2 = stu1) 這一句。該語句的作用是將stu1的引用賦值給stu2,這樣,stu1和stu2指向內存堆中同一個對象。
那么,怎么能干干凈凈清清楚楚地復制一個對象呢。在 Java語言中,用簡單的賦值語句是不能滿足這種需求的。
要滿足這種需求有很多途徑:
(1)將A對象的值分別通過set方法加入B對象中;
(2)通過重寫java.lang.Object類中的方法clone();
(3)通過org.apache.commons中的工具類BeanUtils和PropertyUtils進行對象復制;
(4)通過序列化實現(xiàn)對象的復制。
將 A 對象的值分別通過 set 方法加入 B 對象中
對屬性逐個賦值,本實例為了演示簡單就設置了一個屬性:
Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = new Student(); stu2.setNumber(stu1.getNumber());
我們發(fā)現(xiàn),屬性少對屬性逐個賦值還挺方便,但是屬性多時,就需要一直get、set了,非常麻煩。
重寫 java.lang.Object 類中的方法 clone()
先介紹一下兩種不同的克隆方法,淺克隆(ShallowClone)和深克隆(DeepClone)。
在Java語言中,數(shù)據(jù)類型分為值類型(基本數(shù)據(jù)類型)和引用類型,值類型包括int、double、byte、boolean、char等簡單數(shù)據(jù)類型,引用類型包括類、接口、數(shù)組等復雜類型。淺克隆和深克隆的主要區(qū)別在于是否支持引用類型的成員變量的復制,下面將對兩者進行詳細介紹。
淺克隆
一般步驟:
1.被復制的類需要實現(xiàn)Clonenable接口(不實現(xiàn)的話在調用clone方法會拋出CloneNotSupportedException異常), 該接口為標記接口(不含任何方法)。
2.覆蓋clone()方法,訪問修飾符設為public。方法中調用super.clone()方法得到需要的復制對象(native為本地方法)。
class Student implements Cloneable{ private int number; public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public Object clone() { Student stu = null; try{ stu = (Student)super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return stu; } } public class Test { public static void main(String args[]) { Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = (Student)stu1.clone(); System.out.println("學生1:" + stu1.getNumber()); System.out.println("學生2:" + stu2.getNumber()); stu2.setNumber(54321); System.out.println("學生1:" + stu1.getNumber()); System.out.println("學生2:" + stu2.getNumber()); } }
結果:
學生1:12345
學生2:12345
學生1:12345
學生2:54321
在淺克隆中,如果原型對象的成員變量是值類型,將復制一份給克隆對象;如果原型對象的成員變量是引用類型,則將引用對象的地址復制一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的內存地址。
簡單來說,在淺克隆中,當對象被復制時只復制它本身和其中包含的值類型的成員變量,而引用類型的成員對象并沒有復制。
在Java語言中,通過覆蓋Object類的clone()方法可以實現(xiàn)淺克隆。
深克隆
package abc; class Address { private String add; public String getAdd() { return add; } public void setAdd(String add) { this.add = add; } } class Student implements Cloneable{ private int number; private Address addr; public Address getAddr() { return addr; } public void setAddr(Address addr) { this.addr = addr; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public Object clone() { Student stu = null; try{ stu = (Student)super.clone(); //淺復制 }catch(CloneNotSupportedException e) { e.printStackTrace(); } return stu; } } public class Test { public static void main(String args[]) { Address addr = new Address(); addr.setAdd("杭州市"); Student stu1 = new Student(); stu1.setNumber(123); stu1.setAddr(addr); Student stu2 = (Student)stu1.clone(); System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd()); System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd()); addr.setAdd("西湖區(qū)"); System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd()); System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd()); } }
結果:
學生1:123,地址:杭州市
學生2:123,地址:杭州市
學生1:123,地址:西湖區(qū)
學生2:123,地址:西湖區(qū)
怎么兩個學生的地址都改變了?
原因是淺復制只是復制了addr變量的引用,并沒有真正的開辟另一塊空間,將值復制后再將引用返回給新對象。
為了達到真正的復制對象,而不是純粹引用復制。我們需要將Address類可復制化,并且修改clone方法,完整代碼如下:
package abc; class Address implements Cloneable { private String add; public String getAdd() { return add; } public void setAdd(String add) { this.add = add; } @Override public Object clone() { Address addr = null; try{ addr = (Address)super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return addr; } } class Student implements Cloneable{ private int number; private Address addr; public Address getAddr() { return addr; } public void setAddr(Address addr) { this.addr = addr; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public Object clone() { Student stu = null; try{ stu = (Student)super.clone(); //淺復制 }catch(CloneNotSupportedException e) { e.printStackTrace(); } stu.addr = (Address)addr.clone(); //深度復制 return stu; } } public class Test { public static void main(String args[]) { Address addr = new Address(); addr.setAdd("杭州市"); Student stu1 = new Student(); stu1.setNumber(123); stu1.setAddr(addr); Student stu2 = (Student)stu1.clone(); System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd()); System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd()); addr.setAdd("西湖區(qū)"); System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd()); System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd()); } }
結果:
學生1:123,地址:杭州市
學生2:123,地址:杭州市
學生1:123,地址:西湖區(qū)
學生2:123,地址:杭州市
在深克隆中,無論原型對象的成員變量是值類型還是引用類型,都將復制一份給克隆對象,深克隆將原型對象的所有引用對象也復制一份給克隆對象。
簡單來說,在深克隆中,除了對象本身被復制外,對象所包含的所有成員變量也將復制。
在Java語言中,如果需要實現(xiàn)深克隆,可以通過覆蓋Object類的clone()方法實現(xiàn),也可以通過序列化(Serialization)等方式來實現(xiàn)。(如果引用類型里面還包含很多引用類型,或者內層引用類型的類里面又包含引用類型,使用clone方法就會很麻煩。這時我們可以用序列化的方式來實現(xiàn)對象的深克隆。
工具類BeanUtils和PropertyUtils進行對象復制
Student stu1 = new Student(); stu1.setNumber(12345); Student stu2 = new Student(); BeanUtils.copyProperties(stu2,stu1);
這種寫法無論多少種屬性都只需要一行代碼搞定,很方便吧!除BeanUtils外還有一個名為PropertyUtils的工具類,它也提供copyProperties()方法,作用與BeanUtils的同名方法十分相似,主要的區(qū)別在于BeanUtils提供類型轉換功能,即發(fā)現(xiàn)兩個JavaBean的同名屬性為不同類型時,在支持的數(shù)據(jù)類型范圍內進行轉換,而PropertyUtils不支持這個功能,但是速度會更快一些。
在實際開發(fā)中,BeanUtils使用更普遍一點,犯錯的風險更低一點。
通過序列化實現(xiàn)對象的復制
序列化就是將對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在于內存中。
通過序列化實現(xiàn)的拷貝不僅可以復制對象本身,而且可以復制其引用的成員對象,因此通過序列化將對象寫到一個流中,再從流里將其讀出來,可以實現(xiàn)深克隆。
需要注意的是能夠實現(xiàn)序列化的對象其類必須實現(xiàn)Serializable接口,否則無法實現(xiàn)序列化操作。
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
idea perttier的使用和縮進改為4不成功問題及解決
這篇文章主要介紹了idea perttier的使用和縮進改為4不成功問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05Java?AQS?原理與?ReentrantLock?實現(xiàn)方法
AQS 的作用是解決同步器的實現(xiàn)問題,它將復雜的同步器實現(xiàn)分解為簡單的框架方法,開發(fā)者只需要實現(xiàn)少量特定的方法就能快速構建出可靠的同步器,這篇文章主要介紹Java AQS原理與ReentrantLock實現(xiàn),需要的朋友可以參考下2025-03-03mybatis TypeHandler注入spring的依賴方式
這篇文章主要介紹了mybatis TypeHandler注入spring的依賴方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01