Java对象创建方式
父类代码和子类代码如下:
//父类
public class SuperClass {
private String name;
private static int age;
{
name = "hh";
System.out.println("父类。。初始化实例块。。");
}
static {
age = 18;
System.out.println("父类。。初始化静态代码块。。");
}
public SuperClass(String name) {
System.out.println("父类。。有参构造函数。。");
this.name = name;
}
public SuperClass() {
System.out.println("父类。。无参构造函数。。");
}
@Override
public String toString() {
return "SuperClass{" +
"name='" + name + '\'' +
"age" + age + '\'' +
'}';
}
}
//子类
public class SubClass extends SuperClass {
private String company;
private static String phone;
{
company = "wangwang";
System.out.println("子类。。初始化实例块。。");
}
static {
phone = "1888888888";
System.out.println("子类。。初始化静态代码块。。");
}
public SubClass(String name, String company) {
super(name);
this.company = company;
System.out.println("子类。。有参构造函数1。。");
}
public SubClass(String company) {
this.company = company;
System.out.println("子类。。有参构造函数2。。");
}
public SubClass() {
System.out.println("子类。。无参构造函数。。");
}
@Override
public String toString() {
return super.toString() + "SubClass{" +
"company='" + company + '\'' +
"phone='" + phone + '\'' +
'}';
}
}
- 使用new关键字
SubClass subClass = new SubClass("Willow","niu");
- 使用Class类的newInstance方法(反射机制)
newInstance方法调用无参构造器创建对象
SubClass subClass = (SubClass) Class.forName("com.bean.SubClass").newInstance();
SubClass subClass = SubClass.class.newInstance();
- 使用Constructor类的newInstance方法(反射机制)
该方法和Class类中的newInstance方法很像,但Constructor类的newInstance可以调用有参数的和私有的构造函数
Constructor<SubClass> constructor = SubClass.class
.getConstructor(String.class);
SubClass subClass = constructor.newInstance("Willow");
- 使用Clone
调用clone()方法时:1.对象必须继承Cloneable 2、必须重载clone方法。
//继承+override clone(),其他同上
public class SubClass extends SuperClass implements Cloneable {
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//创建时调用
SubClass subClass = new SubClass("Willow","niu");
SubClass subClass2 = (SubClass) subClass.clone();
- 使用(反)序列化机制
当反序列化一个对象时,JVM会创建一个单独对象并且不会调用任何构造函数。为了反序列化一个对象,类必须实现Serializable接口。
父类实现Serializable接口
public class SuperClass implements Serializable
反序列化对象
public static void main(String[] args) throws Exception {
SubClass subClass = new SubClass("Willow","Willow's company");
ObjectOutputStream output = new ObjectOutputStream(
new FileOutputStream("SubClass.txt"));
output.writeObject(subClass);
output.close();
System.out.println(subClass);
System.out.println("***********************");
ObjectInputStream input = new ObjectInputStream(new FileInputStream(
"SubClass.txt"));
SubClass outSubClass = (SubClass) input.readObject();
System.out.println(outSubClass);
input.close();
}
输出结果
类的加载顺序
虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块叫“类加载器”
类加载器就是寻找类或接口字节码文件进行解析并构造JVM内部对象表示的组件,在java中类装载器把一个类装入JVM,经过以下步骤:
- 加载:查找和导入Class文件
- 链接:其中解析步骤是可以选择的 (a)检查:检查载入的class文件数据的正确性 (b)准备:给类的静态变量分配存储空间
(c)解析:将符号引用转成直接引用 - 初始化:对静态变量,静态代码块执行初始化工作
public class Demo1Application {
public static void main(String[] args) throws Exception {
SubClass subClass = new SubClass("Willow","niu");
System.out.println(subClass);
}
}
结果如下:
在类中,加载顺序为:
1.首先加载父类的静态字段或者静态语句块。
2.子类的静态字段或静态语句块。
3.父类普通变量以及语句块。
4.父类构造方法被加载。
5.子类变量或者语句块被加载。
6.子类构造方法被加载。
静态方法,调用的时候才会加载,不调用的时候不会加载。
静态语句块和静态变量被初始化的顺序与代码先后顺序有关。
JVM双亲委派模型
JVM双亲委派模型:如果一个类加载器收到类加载请求,它不会立刻加载这个类,而是将请求委派给父类加载器去加载该类,最终加载请求会打到引导类加载器,只有当父类加载器无法完成加载请求(也就是搜索范围内无该类),子加载器才会自己尝试去加载这个类。
为什么JVM的类加载要采用双亲委派模型加载类?
基于双亲委派模型设计,那么Java中基础的类,Object类似Object类重复多次的问题就不会存在了,因为经过层层传递,加载请求最终都会被Bootstrap ClassLoader所响应。加载的Object类也会只有一个,否则如果用户自己编写了一个java.lang.Object类,并把它放到了ClassPath中,会出现很多个Object类,这样Java类型体系中最最基础的行为都无法保证,应用程序也将一片混乱。
双亲委派模型的好处:
1.安全性,避免用户自己编写的类动态替换JAVA的一些核心类。
2.避免类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader加载就是不同的两个类。
在JVM中表示两个class对象是否为同一个类对象存在两个必要条件:
1.类的全限定名必须一致,包括包名。
2.加载这个类的ClassLoader(指ClassLoader实例对象)必须相同。