有4种显式地创建对象的方式:
1.用new语句创建对象,这是最常用的创建对象的方式。
2.运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。
3.调用对象的clone()方法。
4.运用反序列化手段,调用java.io.ObjectInputStream对象的readObject()方法.
下面演示了用前面3种方式创建对象的过程。
public class Customer implements Cloneable{
private String name;
private int age;
public Customer(){
this("unknown",0);
System.out.println("call default constructor");
}
public Customer(String name,int age){
this.name=name;
this.age=age;
System.out.println("call second constructor");
}
public Object clone()throws CloneNotSupportedException{
return super.clone();
}
public boolean equals(Object o){
if(this==o)return true;
if(! (o instanceof Customer)) return false;
final Customer other=(Customer)o;
if(this.name.equals(other.name) && this.age==other.age)
return true;
else
return false;
}
public String toString(){
return "name="+name+",age="+age;
}
public static void main(String args[])throws Exception{
//运用反射手段创建Customer对象
Class objClass=Class.forName("Customer");
Customer c1=(Customer)objClass.newInstance(); //会调用Customer类的默认构造方法
System.out.println("c1: "+c1); //打印name=unknown,age=0
//用new语句创建Customer对象
Customer c2=new Customer("Tom",20);
System.out.println("c2: "+c2); //打印name=tom,age=20
//运用克隆手段创建Customer对象
Customer c3=(Customer)c2.clone(); //不会调用Customer类的构造方法
System.out.println("c2==c3 : "+(c2==c3)); //打印false
System.out.println("c2.equals(c3) : "+c2.equals(c3)); //打印true
System.out.println("c3: "+c3); //打印name=tom,age=20
}
}
以上程序的打印结果如下:
call second constructor
call default constructor
c1: name=unknown,age=0
call second constructor
c2: name=Tom,age=20
c2==c3 : false
c2.equals(c3) : true
c3: name=Tom,age=20
从以上打印结果看出,用new语句或Class对象的newInstance()方法创建Customer对象时,都会执行Customer类的构造方法,而用对象的clone()方法创建Customer对象时,不会执行Customer类的构造方法。(区别)
除了以上4种显式地创建对象的方式以外,在程序中还可以隐含地创建对象,包括以下几种情况:
1.对于java命令中的每个命令行参数,Java虚拟机都会创建相应的String对象,并把它们组织到一个String数组中,再把该数组作为参数传给程序入口main(String args[])方法。
2.程序代码中的String类型的直接数对应一个String对象,例如:
String s1="Hello";
String s2="Hello"; //s2和s1引用同一个String对象
String s3=new String("Hello");
System.out.println(s1==s2); //打印true
System.out.println(s1==s3); //打印false
执行完以上程序,内存中实际上只有两个String对象,一个是直接数,由Java虚拟机隐含地创建,还有一个通过new语句显式地创建。
3.字符串操作符“+”的运算结果为一个新的String对象。例如:
String s1="H";
String s2=" ello";
String s3=s1+s2; //s3引用一个新的String对象
System.out.println(s3=="Hello"); //打印false
System.out.println(s3.equals("Hello")); //打印true
4.当Java虚拟机加载一个类时,会隐含地创建描述这个类的Class实例
Class类的forName方法public static Class<?> forName(String className)
throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:
Class.forName(className, true, currentLoader)
其中 currentLoader 表示此类的定义类加载器。
例如,以下代码片段返回 java.lang.Thread 类的运行时 Class 描述符。
Class t = Class.forName("java.lang.Thread")
调用 forName("X") 将导致名为 X 的类被初始化。
参数:
className - 所需类的完全限定名。
返回:
具有指定名的类的 Class 对象。
抛出:
LinkageError - 如果链接失败
ExceptionInInitializerError - 如果此方法所激发的初始化失败
ClassNotFoundException - 如果找不到该类
Object类的clone()方法
protected Object clone()
throws CloneNotSupportedException
创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。一般来说,对于任何对象 x,如果表达式:
x.clone() != x
是正确的,则表达式:
x.clone().getClass() == x.getClass()
将为 true,但这些不是绝对条件。一般情况下是:
x.clone().equals(x)
将为 true,但这不是绝对条件。
按照惯例,返回的对象应该通过调用 super.clone 获得。如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()。
按照惯例,此方法返回的对象应该独立于该对象(正被克隆的对象)。要获得此独立性,在 super.clone 返回对象之前,有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被克隆对象的内部“深层结构”的所有可变对象,并使用对副本的引用替换对这些对象的引用。如果一个类只包含基本字段或对不变对象的引用,那么通常不需要修改 super.clone 返回的对象中的字段。
Object 类的 clone 方法执行特定的克隆操作。首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。注意:所有的数组都被视为实现接口 Cloneable。否则,此方法会创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被自我克隆。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。
Object 类本身不实现接口 Cloneable,所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常。
返回:
此实例的一个克隆。
抛出:
CloneNotSupportedException - 如果对象的类不支持 Cloneable 接口,则重写 clone 方法的子类也会抛出此异常,以指示无法克隆某个实例。
[转载]http://blog.youkuaiyun.com/yakihappy/archive/2009/03/11/3979348.aspx
例子2:
package org.whb.test.demo724;
import java.io.*;
import java.util.Date;
/**
* 对象的序列化和反序列化测试类.
*1、序列化是干什么的?
简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自 己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。
*2、什么情况下需要序列化
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
*
*3、相关注意事项
a)序列化时,只对对象的状态进行保存,而不管对象的方法;
b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
d)并非所有的对象都可以序列化,,至于为什么不可以,有很多原因了,比如:
1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输 等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。
2. 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分 配,而且,也是没有必要这样实现。
*/
public class ObjectSaver {
public static void main(String[] args) throws Exception {
ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream("D:/objectFile.swf"));
//序列化对象
Customer customer = new Customer("haobo", 24);
out.writeObject("你好!");
out.writeObject(new Date());
out.writeObject(customer);
out.writeInt(123); //写入基本类型数据
out.close();
//反序列化对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/objectFile.swf"));
System.out.println("obj1=" + (String) in.readObject());
System.out.println("obj2=" + (Date) in.readObject());
Customer obj3 = (Customer) in.readObject();
System.out.println("obj3=" + obj3);
int obj4 = in.readInt();
System.out.println("obj4=" + obj4);
in.close();
}
}
class Customer implements Serializable {
/**
*
*/
private static final long serialVersionUID = -88175599799432325L;
private String name;
private int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "name=" + name + ", age=" + age;
}
}
/*
* obj1=你好!
obj2=Sat Jul 24 21:18:19 CST 2010
obj3=name=haobo, age=24
obj4=123
*/