java与js对象的创建与使用
对象
就是人们要研究的任何事物,不仅能表示具体事物,还能表示抽象的规则,计划或事件。
属性的无序集合,每个属性可以存一个值(原始值,对象,函数)
对象的特性:封装,尽可能的隐藏对象的部分细节,使其受到保护。只保留有限的接口和外部发生联系。
js创建对象
js 中{},[] 来定义数组和对象
1.{ } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数。
2.[ ]中括号,表示一个数组,也可以理解为一个数组对象。
3.{ } 和[ ] 一起使用,我们前面说到,{ } 是一个对象,[ ] 是一个数组,我们可以组成一个对象数组
调用起来对象的属性用.(点)叠加,数组用 [下标] 来访问。
Js自定义对象的6种方式
一、通过”字面量“方式创建。
方法:将成员信息写到{}中,并赋值给一个变量,此时这个变量就是一个对象。
如果{}中为空,则将创建一个空对象:
var person = (name:'dongjc', work:function() {console.log('write coding')});
var person = {} //创建空对象
<script type="text/javascript">
var person = {
name: "dongjc",
age: 32,
Introduce: function () { alert("My name is " + this.name + ".I'm " + this.age); }
};
person.Introduce();
</script>
<script type="text/javascript">
var person = {
name: "dongjc",
age: 32,
Introduce: function () { alert("My name is " + this.name + ".I'm " + this.age); }
};
person.worker = 'coding'; //丰富成员信息
</script>
对象初始化器构造对象
var marry={
name:"marry",
age:2,
shout:function(){
alert("我是:"+this.name+",今年:"+this.age);
},
action:function(){
alert("会吃");
}
};
alert(marry.name);
alert(marry.age);
marry.shout();
marry.action();
我们还可以给对象丰富成员信息。
对象.成员名称 = 值;
对象[成员名称] = 值;
也可以获取对象的成员信息。
对象.成员名称;
对象[成员名称];
var ren ={};
ren.name="张三";
ren.sex="男";
ren.eat=function () {
alert("吃饭");
}
alert(ren.name);
alert(ren["name"]);
当我们创建一个对象 ren,会在栈内存中保存一个地址,栈为长度不可变的地址。
而栈中的地址就对应堆中的存储地址。堆中的存储地址,只要实例化会在堆中开辟一块空间,地址就是栈的地址,内容就是实例化对象里面的内容,如name,sex,eat。可以通过地址引用,访问里面的属性和方法。
当我们再实例化一个对象,又会保存另一个地址及开辟一块空间。
代码段,共同的属性或方法放在代码段中,不在堆中。只执行一次,节省内存空间。代码段会一直存在内存的空间中,知道浏览器关闭。使用prototype方法创建
二、通过object方式创建。
方法:先通过object构造器new一个对象,再往里丰富成员信息。
var obj = new Object();
<script type="text/javascript">
var person = new Object();
person.name = "dongjc";
person.age = 32;
person.Introduce = function () {
alert("My name is " + this.name + ".I'm " + this.age);
};
person.Introduce();
</script>
<body>
<script type="text/javascript">
/* function speak(something){
alert(something);
} */
/* var p=new Object();
p.name="Jack"; // 动态的添加属性
p.func=speak; // 动态的添加方法
alert(p.name);
p.func("Hello,Hello,大家好!"); */
/* delete p.name;
alert(p.name);
delete p.func;
p.func("Hello,Hello,大家好!"); */
/* p.name=undefined;
p.func=undefined;
alert(p.name);
p.func("Hello,Hello,大家好!"); */
function person(name,age){
this.name2=name;
this.age2=age;
function speak(something){
alert(something);
}
this.func=speak;
}
var p1=new person("Jack",12);
alert(p1.name2);
p1.func("Hello,EveryOne!");
</script>
</body>
工厂模式
工厂模式虽然解决多次创建相似对象的重复性问题,但是并没有解决对象识别问题,也就是typeof之后他都显示object,具体的对象是什么并没有显示。
function createPerson(name,age,job)
{
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(this.name);//this指的是o
}
return o;
}
var person1=createPerson("Tom",23,"厨师");
person1.sayName();
三、通过”构造函数“方式创建。
方法:
var obj = new 函数名();
js的方法是一个对象
<script type="text/javascript">
/* function say(name,age){
alert(name+"今年"+age+"岁了");
}
say("张三",3); */
var sayFunc=new Function("name","age","alert(name+'今年'+age+'岁了')");
sayFunc("李四",4);
alert(sayFunc.toString());
alert(sayFunc.valueOf());
</script>
定义对象模拟数组,arguments为js内置的对象。
function myArray () {
var lengs= arguments.length;
for (var i=0; i<lengs; i++) {
this[i]=arguments[i];
}
}
var arr=new myArray(1,2,3);
alert(arr[0]);
构造函数模式和工厂模式的区别
1.没有显式的创建对象。
2.将属性和方法赋给了this对象。
3.没有return语句。
4.函数名第一个字母大写。
构造函数模式优于工厂模式的原因就是,构造函数模式中的对象实例(person1)通过constructor属性或instanceof操作符可以验证person1既是Object的实例,也是Person的实例,同时也证明所有对象均来自于Object。
function Person(name,age,job)
{
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);//this是Person
}
}
var person1=new Person("Tom",23,"厨师");
person1.sayName();
<script type="text/javascript">
var marry={
name:"marry",
age:2,
shout:function(){
alert("我是:"+this.name+",今年:"+this.age);
},
action:function(){
alert("会吃");
}
};
alert(marry.name);
alert(marry.age);
marry.shout();
marry.action();
function Dog(name,age){
this.name=name;
this.age=age;
this.shout=function(){
alert("我是:"+this.name+",今年:"+this.age);
};
this.action=function(){
alert("会吃");
};
}
var jack=new Dog("jack",1);
alert(jack.name);
alert(jack.age);
jack.shout();
jack.action();
</script>
这与通过类创建对象有本质的区别。通过该方法创建对象时,会自动执行该函数。创建对像时,会自动调用构造函数,因此该方法称为通过"构造函数“方式创建对象。
<script type="text/javascript">
function Person() {
this.name = "dongjc"; //通过this关键字设置默认成员
var worker = 'coding'; //没有this关键字,对象创建后,该变量为非成员
this.age = 32;
this.Introduce = function () {
alert("My name is " + this.name + ".I'm " + this.age);
};
alert("My name is " + this.name + ".I'm " + this.age);
};
var person = new Person();
person.Introduce();
</script>
此代码一共会两次跳出对话框,原因在于创建对象是自动执行了该函数。
注意:this关键字的使用。这里的this与php中话法意思类似,指调用该函数的对象,这里指的是person。
当有过多的引用的时候,尽量保证每次的引用都是一个新的对象
var points = [];
var data = retData.data;
if(!$.isEmptyArray(data)){
rush.removePoint();
for(var i=0;i<data.length;i++){
if($.isEmptyStr(data[i].lng) || $.isEmptyStr(data[i].lat)){
continue;
}
var point = {
"lng":data[i].lng,
"lat":data[i].lat,
};
points.push(point)
var drawPoint = {
"lng":data[i].lng,
"lat":data[i].lat,
"type":pointType[1]
}
rush.drawPoint(drawPoint);
var content= [];
content.push("<div class='input-card content-window-card'>");
content.push("<div style=\"padding:7px 0px 0px 0px;\">");
content.push("<p class='input-item'>地址:"+ data[i].address +"</p>");
content.push("<p class='input-item'>编号 :"+data[i].deviceCode +"</p></div></div>");
drawPoint.type["infowindow"]={
content:content.join(""),
xoffset: 0,
yoffset:-31,
width:360,
height:100
};
drawPoint.type["onClick"]=function(point,e){
rush.showInfoWindow(point);
}
}
var pointType = [
{
"imgUrl":CONTEXT_PATH+"/view/cusviews/images/normal_station.png",
"height":34,
"width":30,
"xoffset": -10,
"yoffset": -34,
'onClick' : function(point, e){
console.log('坐标:LAT:' + point.lat + ';LNG:' + point.lng);
},
},
{
"imgUrl":CONTEXT_PATH+"/view/cusviews/images/red_point.png",
"height":34,
"width":30,
"xoffset": -10,
"yoffset": -34,
'onClick' : function(point, e){
console.log('坐标:LAT:' + point.lat + ';LNG:' + point.lng);
},
},
{
"imgUrl":CONTEXT_PATH+"/view/cusviews/images/green_point.png",
"height":34,
"width":30,
"xoffset": -10,
"yoffset": -34,
'onClick' : function(point, e){
console.log('坐标:LAT:' + point.lat + ';LNG:' + point.lng);
},
},
{
"imgUrl":CONTEXT_PATH+"/view/cusviews/images/start_point.png",
"height":34,
"width":30,
"xoffset": -10,
"yoffset": -34,
'onClick' : function(point, e){
console.log('坐标:LAT:' + point.lat + ';LNG:' + point.lng);
},
},
{
"imgUrl":CONTEXT_PATH+"/view/cusviews/images/end_point.png",
"height":34,
"width":30,
"xoffset": -10,
"yoffset": -34,
'onClick' : function(point, e){
console.log('坐标:LAT:' + point.lat + ';LNG:' + point.lng);
},
}
]
$.each(data[i],function(n,m){
debugger;
this;
var pointColor = {};
pointColor= {
"imgUrl":CONTEXT_PATH+"/view/cusviews/images/red_point.png",
"height":34,
"width":30,
"xoffset": -10,
"yoffset": -34,
'onClick' : function(point, e){
console.log('坐标:LAT:' + point.lat + ';LNG:' + point.lng);
},
}
var drawPoint = {}; //创建一个新的对象,有过多的引用,js有时会默认引用的是同一个对象
drawPoint = {
"lng":data[i].lng,
"lat":data[i].lat,
"type":pointType[1]
}
if(0 == i){
drawPoint.type = pointType[3]
}
else if((data.length - 1) == i){
drawPoint.type = pointType[4]
}else{
drawPoint.type = pointColor
}
var info = [];
info.push("<div class='input-card content-window-card'>");
info.push("<div style=\"padding:7px 0px 0px 0px;\">");
info.push("<p class='input-item'>开始时间:"+ data[i].startTime +"</p>");
info.push("<p class='input-item'>检测人 :"+data[i].checkUser +"</p></div></div>");
var myInfo = {newInfo:{
content: info.join(""),
xoffset: 0,
yoffset:-31,
width:360,
height:100
}}
for(var j in myInfo){
var mythisInfo = {};
mythisInfo = myInfo[j];
drawPoint.type["infowindow"] =mythisInfo;
window.drawPoints.push(drawPoint);
}
Leak.drawNewPoint(drawPoint);
drawPoint.type["onClick"] = function(point, e){
Leak.showInfoWindow(point)
}
})
全局作用域
但是构造函数也有缺点,对象是引用类型,对象实例化不是指针的改变,而是简单的复制,复制对象的方法和属性,假设一个对象有上千个实例,它就会复制上千个功能相同的方法,这显然是不可取的。
我们也可以把sayName()函数的定义战役到构造函数的外部,这样我们就将sayName属性设置成等于全局的sayName函数,这样实例化对象就共享全局作用域中的同一个sayName(),解决了构造函数对象方法的多次创建问题。但是全局作用域定义的sayName()函数只能被某个对象调用谈什么全局作用域,而且如果构造函数对象的方法有很多,就需要定义很多全局函数,封装性又从何谈起,于是原型模式应运而生。
function Person(name,age,job)
{
this.name=name;
this.age=age;
this.job=job;
this.sayName=sayName;
}
function sayName(){
alert(this.name)
}
var person1=new Person("Tom",23,"厨师");
person1.sayName();
四、原型模式创建对象
我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象中包含着所有对象实例的属性和方法,这个对象就是原型对象。通俗的说,原型对象中的方法和属性可以被所有对象的实例所共享,对象实例就不用多次创建相同的方法和属性。
function Person(){
};
Person.prototype={
name:"Tom",
age:23,
job:"web前端工程师",
sayName:function(){
alert(this.name);
}
}
var person1=new Person();
person1.sayName();
prototype方法创建对象的比较
1.工厂函数封装,通过一个函数封装起来
function dianshi (color,size,brand) {
var Tv={};
Tv.color=color;
Tv.size=size;
Tv.brand=brand;
Tv.look=function () {
alert("看电视");
}
Tv.play=function () {
alert("玩游戏");
}
Tv.dvd=function () {
alert("DVD");
}
return Tv;
}
var ds=dianshi("red","30inch","sony");
//alert(typeof ds)//返回object
alert(ds.color)
var ds1=dianshi("blue","40inch","changh");
alert(ds1["size"])//传递参数
2.构造函数
function Tv(color,size,brand) {
this.color=color;//那个对象引用this就代表那个对象
this.size=size;
this.brand=brand;
this.play=function () {
alert("玩游戏");
}
this.look=function () {
alert("看电视");
}
}
var sony=new Tv("red","20 inch","sony");
alert(sony.color)
3.prototype方法
共同的属性与方法放在代码段,节省内存空间
function Tv(color,size,brand) {
this.color=color;
this.size=size;
this.brand=brand;
this.play=function () {
alert("玩游戏");
}
}
Tv.prototype.look=function () {
alert("看电视");
}
Tv.prototype.dvd=function () {
alert("DVD");
}
Tv.prototype.aaa={name:"张三"};//只能共享属性或函数,不能共享对象
var sony=new Tv("red","20 inch","sony");
var changhong =new Tv("red","20 inch","CH");
delete sony.color
delete sony.play//undefine
delete sony.look//能访问到
sony.look();
changhong.look();
sony.aaa.name="李四"//李四
changhong.aaa.name//李四
全局对象Array扩展增加removeByValue方法
Array.prototype.removeByValue = function(val) {
for(var i=0; i<this.length; i++) {
if(this[i] == val) {
this.splice(i, 1);
break;
}
}
}
虽然可以通过对象实例访问保存在原型对象中的值,但却不能通过对象实例重写原型的值。其实对象实例获取某一属性的值是从本身开始寻找,然后是原型对象,最后是构造函数对象,所以重写对象实例的属性值(这个值可以通过delete操作符删除)仅仅是阻断了获取原型属性值的途径,但是没有改变其中的值。
function Person(){
};
Person.prototype.name="Tom";
Person.prototype.age=23;
Person.prototype.job="厨师";
Person.prototype.sayName=function(){
alert(this.name);
}
var person1=new Person();
var person2=new Person();
person1.name="Mike";
alert(person1.name);
alert(person2.name);
alert(person1.name);
alert(person2.name);
4.面向对象创建对象
用面向对象语法表示的时候,原型对象的constructor属性不在指向Person,因为每创建一个函数,同时会创建它的prototype对象,用面向对象语法本质上相当于重写了prototype对象,constructor属性也会变成新对象的constructor属性(这里指向Object)
function Person(){
};
Person.prototype={
constructor:Person,
name:"Tom",
age:23,
job:"厨师",
sayName:function(){
alert(this.name);
}
}
var person1=new Person();
var person2=new Person();
person1.name="Mike";
alert(person1.name);
alert(person2.name);
原型模式的缺点:因为所以对象实例共享原型对象的方法和属性,但是往往实例都有他自己私有的属性,这时候原型模式就不适用了,所以我们可以混合使用构造函数模式和原型模式。
五.混合方法创建对象
组合使用构造函数模式和原型模式结合了构造函数和原型模式的优点,构造函数定义实例的私有属性,原型模式定义共享属性和方法。
function Tv(color,size,brand) {
this.color=color;
this.size=size;
this.brand=brand;
this.play=function () {
alert("玩游戏");
}
Tv.prototype.aaa={name:"张三"};
}
Tv.prototype.look=function () {
alert("看电视");
}
Tv.prototype.dvd=function () {
alert("DVD");
}
}
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
};
Person.prototype={
constructor:Person,
sayName:function(){
alert(this.name);
}
}
var person1=new Person("Tom");
var person2=new Person("Mike");
alert(person1.name);
alert(person2.name);
六、$.extend创建对象
jQuery.extend(object);为扩展jQuery类本身.为类添加新的方法。
jQuery.fn.extend(object);给jQuery对象添加方法。
创建对象
var option = $.extend({elem: "#" + _self.container,
url : _self.url,
cols: _self.title,
method: RequestMethod.METHOD_POST,
id : _self.tableId,
even: true,
page: true, //是否显示分页
pageNum: 1,
limit: _self.pageSize, //每页默认显示的数量
limits:[5,10,15,20,30],
done:function(res, curr, count){
debugger;
if(_self.afterDone && $.isFunction(_self.afterDone)){
_self.afterDone(res, curr, count);
}
}}, _self.layOption);
//展示已知数据
layui.table.render(option);
在处理构造函数的时候,可以通过let 绑定来共享一个或者多个私有成员,而不使用闭包:
1 var Thing;
2 {
3 let privateScope = new WeakMap();
4 let counter = 0;
5
6 Thing = function(){
7 this.someProperty = 'foo';
8 privateScope.set(this, {
9 hidden: ++counter,
10 });
11 };
12
13 Thing.prototype.showPublic = function(){
14 return this.someProperty;
15 };
16
17 Thing.prototype.showPrivate = function(){
18 return privateScope.get(this).hidden;
19 };
20 }
21
22 console.log(typeof privateScope); //undefined
23
24 var thing = new Thing();
25 console.log(thing); //Thing { someProperty: 'foo' }
26 console.log(thing.showPublic()); //foo
27 console.log(thing.showPrivate()); //1
你可以拓展一个对象到jQuery的 prototype里去,这样的话就是插件机制了。
(function($) {
$.fn.tooltip = function(options) {};
//等价于 var
tooltip = {
function(options) {}
};
$.fn.extend(tooltip) = $.prototype.extend(tooltip) = $.fn.tooltip
})(jQuery);
Query.fn.extend()的调用把方法扩展到了对象的prototype上,所以实例化一个jQuery对象的时候,它就具有了这些方法,在jQuery.JS中到处体现这一点
Objectj Query.extend( target, object1, [objectN])
用一个或多个其他对象来扩展一个对象,返回被扩展的对象
var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
jQuery.extend(settings, options);
结果:settings == { validate: true, limit: 5, name: "bar" }
js对象属性
如果属性的值是函数,我们叫做他是对象的方法,否则叫做是属性。
1.私有属性,对象属性,类属性
<script type="text/javascript">
function C(){
this.objPro="对象属性";
C.prototype.objPro2="对象属性2";//原型
var privatePro="私有属性";//只能在方法内部使用
}
C.classPro="类属性";
alert(C.classPro);
var c=new C();
alert(c.objPro);
alert(c.objPro2);
</script>
2.私有方法,对象方法,类方法
<script type="text/javascript">
function C(){
var privateFunc=function(){
alert("私有方法");
};
privateFunc();
this.objFunc=function(){
alert("对象方法");
};
C.prototype.objFunc2=function(){
alert("对象方法2");
};
}
C.classFunc=function(){
alert("类方法");
};
C.classFunc();
var c=new C();
c.objFunc();
c.objFunc2();
</script>
js函数
1. 函数的定义:
a.采用关键字function来定义
function fun(){
alert("大家好") ;
}
// fun() ;
b.采用匿名函数的方式(采用function作为名字)‘’
var a = function(){
alert("我是匿名函数") ;
}
// a() ;
c.采用new Function()的方式
小括号中最后一个参数是函数体,之前所有的参数都是形参.
var b = new Function("x","y","z","alert(x+y+z)") ;
// b(3,4,5) ;
前面是参数,最后的是方法体
var sayFunc=new Function("name","age","alert(name+'今年'+age+'岁了')");
// sayFunc("李四",4);
alert("sayFunc方法对象的方法参数个数:"+sayFunc.length);
alert(sayFunc.toString());//获取源码
alert(sayFunc.valueOf());//获取源码
2.调用函数:
调用函数的时候是用函数名来寻找的。
定义函数的时候千万不要重名.
(function () {alert("函数调用");})();
3.函数劫持
函数劫持:改变javascript的预定义的函数预定义好的功能
window.alert = function(x){
document.write(x) ;
}
alert("abc") ;
a.如果两个函数的命名相同,后面的将会覆盖前面的函数。
b.以基本语法声明的函数,会在代码运行的时候,提前加载到内存当中,以供以后使用,
但是匿名函数形式命名的函数,会在执行到的时候,才进行赋值
c.在不同的<script></script>块中的函数,使用和调用的时候,应该先定义,后执行。
4.js函数内置的参数Arguments对象
每创建一个函数,该函数就会隐式创建一个arguments对象,他包含有实际传入参数的信息。
1.length 检测实际传入参数的个数
2.callee 对本身的调用
访问传入参数的具体的值。([下标])
function fun (a,b) {
for (var i=0; i<arguments.length; i++) {
alert(arguments[i])
}
}
java创建对象
我们知道类构造器是创建Java对象的途径之一,通过new 关键字调用构造器完成对象的实例化,还能通过构造器对对象进行相应的初始化。一个类必须要有一个构造器的存在,如果没有显示声明,那么系统会默认创造一个无参构造器,在JDK的Object类源码中,是看不到构造器的,系统会自动添加一个无参构造器。我们可以通过:
Object obj = new Object();构造一个Object类的对象。
java创建对象的五种方式
①、通过 new 关键字
这是最常用的一种方式,通过 new 关键字调用类的有参或无参构造方法来创建对象。比如 Object obj = new Object();
②、通过 Class 类的 newInstance() 方法
这种默认是调用类的无参构造方法创建对象。比如 Person p2 = (Person) Class.forName("com.test.Person").newInstance();
③、通过 Constructor 类的 newInstance 方法
这和第二种方法类时,都是通过反射来实现。通过 java.lang.relect.Constructor 类的 newInstance() 方法指定某个构造器来创建对象。
Person p3 = (Person) Person.class.getConstructors()[0].newInstance();
实际上第二种方法利用 Class 的 newInstance() 方法创建对象,其内部调用还是 Constructor 的 newInstance() 方法。
④、利用 Clone 方法
Clone 是 Object 类中的一个方法,通过 对象A.clone() 方法会创建一个内容和对象 A 一模一样的对象 B,clone 克隆,顾名思义就是创建一个一模一样的对象出来。
Person p4 = (Person) p3.clone();
Object 类的 clone() 方法在 Object.class 类中,源码为:
protected native Object clone() throws CloneNotSupportedException;
native 修饰的方法就是告诉操作系统,这个方法我不实现了,让操作系统去实现。具体怎么实现我们不需要了解,只需要知道 clone方法的作用就是复制对象,产生一个新的对象。
⑤、反序列化
序列化是把堆内存中的 Java 对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点(在网络上传输)。而反序列化则是把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。
基本类型和引用类型的区别。
一般我们知道Java分为两种数据类型,一种是基本数据类型,有八个分别是 byte short int long float double char boolean,一种是引用类型,包括类,接口,数组等等。而Java中还有一种特殊的 null 类型,该类型没有名字,所以不可能声明为 null 类型的变量或者转换为 null 类型,null 引用是 null 类型表达式唯一可能的值,null 引用也可以转换为任意引用类型。我们不需要对 null 类型有多深刻的了解,我们只需要知道 null 是可以成为任意引用类型的特殊符号。
在 Java 中数据类型可以分为两大类:基本类型和引用类型。
基本类型也称为值类型,分别是字符类型 char,布尔类型 boolean以及数值类型 byte、short、int、long、float、double。
引用类型则包括类、接口、数组、枚举等。
Java 将内存空间分为堆和栈。基本类型直接在栈中存储数值,而引用类型是将引用放在栈中,实际存储的值是放在堆中,通过栈中的引用指向堆中存放的数据
上图定义的 a 和 b 都是基本类型,其值是直接存放在栈中的;而 c 和 d 是 String 声明的,这是一个引用类型,引用地址是存放在 栈中,然后指向堆的内存空间。
下面 d = c;这条语句表示将 c 的引用赋值给 d,那么 c 和 d 将指向同一块堆内存空间。
②、利用序列化
序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。
注意每个需要序列化的类都要实现 Serializable 接口,如果有某个属性不需要序列化,可以将其声明为 transient,即将其排除在克隆属性之外。