Java:创建对象小记
Java:创建对象小记
对 Java 中的创建对象的内容,做一个微不足道的小小小小记
创建对象的方式概述
使用 new 关键字:
Person person = new Person();
反射创建:使用 Class 类的 newInstance 方法,该方法调用 无参的构造器 创建对象
// 使用 Class 类的 newInstance 方法
String str2 = (String) Class.forName("java.lang.String").newInstance();
String str3 = String.class.newInstance(); // 使用 Constructor 类的 newInstance 方法
Constructor<String> constructor = String.class.getConstructor();
String str4 = constructor.newInstance();
使用 clone() 方法:
// 要使用 clone 方法,我们需要先实现 Cloneable 接口并实现其定义的clone方法。
Person person1 = new Person("刘桂香");
Person person2 = (Person) person1.clone();
反序列化,比如调用 ObjectInputStream 类的 readObject() 方法。
// 将对象写入文件——序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("./data.obj"));
out.writeObject(person2);
out.close(); // 将对象读取出来——反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("./data.obj"));
Person person3 = (Person) in.readObject();
in.close();
System.out.println(person3);
构造函数
创建对象方式1:使用 new 关键字:
Person person = new Person();
构造函数特性:
- 名字与类名相同;
- 没有返回值,但不能用 void 声明构造函数;
- 生成类的对象时自动执行,无需调用。
默认构造方法:
Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法/无参构造”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来显示调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。
解决办法是:在父类里加上一个不做事且没有参数的构造方法,即默认构造方法。
对 super 的进一步展开:
访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。
访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。
this()
和super()
不能同时出现在一个构造函数里面,因为 this(...) 必然会调用其它的构造函数,其它的构造函数必然也会有 super(...) 语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。简而言之:
super()
和this()
同时出现的话,会出现初始化父类两次的不安全操作
克隆对象
创建对象方式3:使用 clone() 方法,来创建对象
实现对象的克隆
将A对象的值分别通过set方法加入B对象中;
Student stu1 = new Student();
stu1.setName("张三");
Student stu2 = new Student();
stu2.setName(stu1.getName());
实现 Cloneable 接口并重写 Object 类中的
clone()
方法;浅克隆:
- 需要复制的类实现 Cloneable 接口(该接口为标记接口,接口中无任何方法)
- 覆盖
clone()
方法,方法中调用super.clone()
方法得到需要的复制对象。
import java.util.Date; public class Student implements Cloneable {
private String name;
private Date birthday; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Date getBirthday() {
return birthday;
} public void setBirthday(Date birthday) {
this.birthday = birthday;
} @Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
} public static void main(String[] args) throws CloneNotSupportedException {
//测试
Student stu1 = new Student();
stu1.setName("张三");
stu1.setBirthday(new Date());
Student stu2 = (Student) stu1.clone();
System.out.println(stu2.getName());
System.out.println(stu2.getBirthday()); // 被拷贝对象中引用对象
System.out.println(stu1.getBirthday() == stu2.getBirthday()); // true
}
}
深克隆
- 通过覆盖 Object 类的
clone()
方法可以实现深克隆,将clone()
方法的修饰符修改为 public,总而言之:深克隆把要复制的对象所引用的对象都复制了一遍。
// 覆盖clone方法
@Override
public Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
// 拷贝对象和原始对象的引用类型引用不同对象
student.setBirthday(new Date());
return student;
} //测试
Student stu1 = new Student();
stu1.setName("张三");
stu1.setBirthday(new Date());
Student stu2 = (Student) stu1.clone();
System.out.println(stu2.getName());
System.out.println(stu2.getBirthday()); // 被拷贝对象中引用对象
System.out.println(stu1.getBirthday() == stu2.getBirthday()); // false
- 通过覆盖 Object 类的
实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆;
- 序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。
- 通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。
工具类 BeanUtils 和 PropertyUtils 进行对象复制
有待验证
深克隆和浅克隆的区别
浅克隆:拷贝对象和原始对象的引用类型引用同一个对象。浅克隆只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅克隆。
@Override
public Object clone() {
Student stu = null;
try{
// 浅复制
stu = (Student)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
深克隆:拷贝对象和原始对象的引用类型引用不同对象。深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝。
// 简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
@Override
public Object clone() {
Student stu = null;
try{
stu = (Student)super.clone(); // 浅克隆
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
stu.addr = (Address)addr.clone(); // 深克隆
return stu;
}
克隆的补充:
深克隆的实现就是在引用类型所在的类实现 Cloneable 接口,并使用 public 访问修饰符重写 clone 方法。
Java 中定义的 clone 没有深浅之分,都是统一的调用 Object 的 clone 方法。为什么会有深克隆的概念?是由于我们在实现的过程中刻意的嵌套了 clone 方法的调用。也就是说深克隆就是在需要克隆的对象类型的类中重新实现克隆方法 clone()。
序列化克隆对象
概念
概念:
序列化:把Java对象转换为字节序列的过程。
反序列化:把字节序列恢复为Java对象的过程。
解释:
对象序列化是一个用于将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序。从字节流创建对象的相反的过程称为反序列化。而创建的字节流是与平台无关的,在一个平台上序列化的对象可以在不同的平台上反序列化。序列化是为了解决在对象流进行读写操作时所引发的问题。
序列化的实现:将需要被序列化的类实现 Serializable 接口,该接口没有需要实现的方法,只是用于标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个 ObjectOutputStream 对象,接着使用 ObjectOutputStream 对象的 writeObject(Object obj)
方法可以将参数为 obj 的对象写出,要恢复的话则使用输入流。
操作
序列化/反序列化操作
public class Person implements Serializable{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 序列化/反序列化对象
public class SerializableTest {
public static void main(String[] args) throws Exception {
Person person = new Person("王麻子", 40);
// 序列化对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("./data.obj"));
out.writeObject("你好!"); //写入字面值常量
out.writeObject(new Date()); //写入匿名Date对象
out.writeObject(person); //写入person对象
out.close();
//反序列化对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("./data.obj"));
System.out.println("obj1:" + (String)in.readObject()); //读取字面值常量
System.out.println("obj2:" + (Date)in.readObject()); //读取匿名Date对象
System.out.println("obj3:" + (Person)in.readObject()); //读取person对象
in.close();
}
}
// 结果:
// obj1:你好!
// obj2:Thu Aug 20 22:02:51 CST 2020
// obj3:Person{name='王麻子', age=40}
使用场景
什么情况下需要序列化?
当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
当你想用套接字在网络上传送对象的时候;
- 当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个 Java 对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为 Java 对象。
- 只能将支持
java.io.Serializable
接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
当你想通过 RMI 传输对象的时候:
Remote Method Invocation,远程方法调用:https://www.cnblogs.com/grefr/p/5046313.html
transient
补充一个关键字:transient
对于不想进行序列化的变量,使用 transient 关键字修饰。
transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化。当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。
参考
https://www.cnblogs.com/wxd0108/p/5685817.html
https://blog.csdn.net/ztchun/article/details/79110096
https://www.cnblogs.com/shoshana-kong/p/10538661.html
https://www.cnblogs.com/wshuage/archive/2018/11/13/9955130.html
Java:创建对象小记的更多相关文章
- effective java读书小记(一)创建和销毁对象
序言 <effective java>可谓是java学习者心中的一本绝对不能不拜读的好书,她对于目标读者(有一点编程基础和开发经验)的人来说,由浅入深,言简意赅.每一章节都分为若干的条目, ...
- Java创建对象的几种方法
有时候,也可能碰到这样面试题,如: Java创建对象有哪几种方法? 除了new之外,java创建对象还有哪几种方式? 本文结合例子,给出几种Java创建对象的方法,Here we go~~~~ 使用n ...
- Java创建对象的4种方式?
[Java创建对象的4种方式?] 1)通过new语句实例化一个对象 2)通过反射机制创建对象 3)通过clone()方法创建一个对象 (复制) 4)通过反序列化方式创建对象
- Java创建对象的几种方式
解析:Java创建对象的几种方式(重要):(1) 用new语句创建对象,这是最常见的创建对象的方法.(2) 运用反射手段,调用java.lang.Class或者java.lang.reflect.Co ...
- JAVA 创建对象4种方法
java创建对象的几种方式 博客分类: java (1) 用new语句创建对象,这是最常见的创建对象的方法.(2) 运用反射手段,调用java.lang.Class或者java.lang.refl ...
- &和&&的共同点和区别、Java字符含义和Java创建对象的几种方式
一.&和&&的共同点和区别 1.&和&&的联系(共同点): &和&&都可以用作逻辑与运算符,但是要看使用时的具体条件来决定. 操 ...
- Java创建对象的4种方式
Java创建对象的方式共有四种: 使用new语句实例化一个对象: 通过反射机制创建对象: 通过clone()方法创建一个对象: 通过反序列化的方式创建对象. 一.使用new语句实例化一个对象 new语 ...
- java创建对象 的初始化顺序
java创建对象 的初始化顺序 1.初始化块 初始化块通常写在类的构造方法之前,由花括号括起来,通常包含对成员属性进行初始化的语句: 初始化块分为instance初始化块和static初始化块,初始化 ...
- Java创建对象的几种方式。
Java创建对象的几种方式(重要): (1) 用new语句创建对象,这是最常见的创建对象的方法. (2) 运用反射手段,调用java.lang.Class或者java.lang.reflect.Con ...
随机推荐
- Django——Auth模块(用户认证模块)
1.Auth模块简介 auth模块是对登录认证方法的一种封装,之前我们获取用户输入的用户名及密码后需要自己从user表里查询有没有用户名和密码符合的对象. 而有了auth模块之后就可以很轻松的去验证用 ...
- Linux下用Sed查找IP地址
ip addr|sed -n '9p'|egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'|sed -nr 's#^.*inet (.*) b ...
- IDEA SpotBugs代码安全审计插件
IDEA SpotBugs代码安全审计插件 在寻找idea代码审计插件的时候,发现Findbugs已经停止更新,无法在idea2020.01版本运行,由此找到SpotBugs SpotBugs介绍 S ...
- v-for列表渲染之数组变动检测
1.简单举一个v-for列表渲染例子 <template> <div> <ul> <li v-for="item in items"> ...
- Spring Boot 2.x 之 H2 数据库
1. Spring Boot下H2数据库的常用配置项 # 指定数据库的类型 spring.datasource.platform=h2 # 数据库连接地址(文件模式) ## AUTO_SERVER=T ...
- 第25篇-虚拟机对象操作指令之putstatic
之前已经介绍了getstatic与getfield指令的汇编代码执行逻辑,这一篇介绍putstatic指令的执行逻辑,putfield将不再介绍,大家可以自己去研究,相信大家有这个实力. putsta ...
- 博客主题——element v2
主题预览 主题下载 gshang.element-v2.rar
- xmind使用技巧
xmind看似每个人都会使用,但是掌握一些小技巧,能够有效提升工作效率. 多行复制粘贴 在xmind中选中多行,复制然后可以直接粘贴到excel.word当中. 在excel.word选中多行,复制然 ...
- 写SQL的套路
定义问题 转化问题 如要解决的问题是:查出每门课程成绩都大于80分学生的姓名,可以转化为:只要学生最小分数的课程大于80分,就是所有课程成绩都大于80分. 查询同名同姓学生名单并统计同名人数--> ...
- jquery中请求格式
$.ajax({ url:"/ceshi/", type:"get", cache:false, dataType:"json", data ...