`
supportopensource
  • 浏览: 515047 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

两个程序的说明

阅读更多
1、程序1的结果是:
class A{
	static{
		System.out.println("load a");
	}
	A(){
		System.out.println("create a");
	}
}
class B extends A{
	static{
		System.out.println("load b");
	}
	B(){
		System.out.println("create b");
	}
}
public class TestABDemo{
	public static void main(String args[]){
		new B();
	}
}

程序运行的结果入下:
load a
load b
create a
create b


结果说明:
该程序主要涉及到静态初始化块和构造器的执行顺序。

(1)构造器的执行顺序:创建任何Java对象总是从该类所在继承树的最顶层类的构造器开始执行,然后依次向下执行,最后才执行本类的构造器。也就是说,创建任何Java对象,最先执行的总是java.lang.Object类的构造器。

(2)初始化块:是Java类里可出现的第四种成员(其他三种为属性、方法和构造器),一个类里可以有多个初始化块,相同类型的初始化块之间有顺序:前面定义的初始化块先执行,后面定义的初始化块后执行。

初始化块的修饰符只能是static,使用static修饰的初始化块称为静态初始化块。初始化块里的代码可以包含任何可执行语句,包括定义局部变量、调用其他对象的方法、使用分支、循环语句等。

初始化块虽然也是Java类的一种成员,但它没有名字,也就没有标识,因此无法通过类、对象来调用初始化块。初始化块只在创建Java对象时隐式执行,而且在执行构造器之前执行。

注意:当Java创建一个对象时,系统先为该对象的所有实例属性分配内存(前提是该类已经被加载过了),接着程序开始对这些实例属性执行初始化,其初始化顺序是:先执行初始化块或声明属性时指定的初始值,再执行构造器里指定的初始值。

当创建一个Java对象时,不仅会执行该类的普通初始化块和构造器,系统会一直上溯到java.lang.Object类,先执行java.lang.Object类的初始化块,开始执行java.lang.Object类的构造器,依次向下执行其父类的初始化块,开始执行其父类的构造器……最后才执行该类的初始化块和构造器,返回该类的对象。

静态初始化块是类相关的,系统将在类初始化阶段执行静态初始化块,而不是在创建对象时才执行。因此静态初始化块总是比普通初始化块先执行

静态初始化块是类相关的,用于对整个类进行初始化处理,通常用于对类属性执行初始化处理。静态初始化块不能对实例属性进行初始化处理。

与普通初始化块类似的是,系统在类初始化阶段执行静态初始化块时,不仅会执行本类的静态初始化块,还会一直上溯到java.lang.Object类(如果它包含静态初始化块),先执行java.lang.Object类的静态初始化块,然后执行其父类的静态初始化块……最后才执行该类的静态初始化块,经过这个过程,才完成了该类的初始化过程。只有当类初始化完成后,才可以在系统中使用这个类,包括访问这个类的类方法、类属性,或者用这个类来创建实例。

例程1:
class Root{
	static{
		System.out.println("Root的静态初始化块");
	}
	{
		System.out.println("Root的普通初始化块");
	}
	public Root(){
		System.out.println("Root的无参构造器");
	}
}
class Mid extends Root{
	static{
		System.out.println("Mid的静态初始化块");
	}
	{
		System.out.println("Mid的普通初始化块");
	}
	public Mid(){
		System.out.println("Mid的无参构造器");
	}
	public Mid(String msg){
		//通过this调用同一个类中重载的构造器
		this();
		System.out.println("Mid的带参构造器,其参数值是:"+msg);
	}
}
class Leaf extends Mid{
	static{
		System.out.println("Leaf的静态初始化块");
	}
	{
		System.out.println("Leaf的普通初始化块");
	}
	public Leaf(){
		//通过super调用父类中有一个字符串参数的构造器
		super("Think in Java");
		System.out.println("执行Leaf的构造器");
	}
}
public class Test{
	public static void main(String args[]){
		new Leaf();
		//new Leaf();
	}
}


程序的运行结果1(注释掉后面一个new Leaf();):
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参构造器
Mid的普通初始化块
Mid的无参构造器
Mid的带参构造器,其参数值是:Think in Java
Leaf的普通初始化块
执行Leaf的构造器


程序的运行结果2(去掉后面一个new Leaf();的注释):
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参构造器
Mid的普通初始化块
Mid的无参构造器
Mid的带参构造器,其参数值是:Think in Java
Leaf的普通初始化块
执行Leaf的构造器
Root的普通初始化块
Root的无参构造器
Mid的普通初始化块
Mid的无参构造器
Mid的带参构造器,其参数值是:Think in Java
Leaf的普通初始化块
执行Leaf的构造器


从结果可以看出:类的初始化阶段,先执行最顶层父类的静态初始化块,依次向下,最后执行当前类的静态初始化块;接着,对象的初始化阶段,先执行最顶层父类的初始化块、构造器,依次向下,最后执行当前类的初始化块、构造器。

注意:静态初始化块只执行一次

2、程序2的结果是:
import java.util.*;
public class TestSet{
	public static void main(String args[]){
		Set<String> books = new HashSet<String>();
		books.add(new String("Think in Java"));
		boolean result = books.add(new String("Think in Java"));
		System.out.println(result);
		System.out.println(books);
		System.out.println(books.size());
	}
}

程序的运行结果如下:
false
[Think in Java]
1

结果说明:

Set集合不允许包含相同的元素,如果试图把两个相同的元素加入到同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。

Set集合判断两个对象是否相同,不是使用==运算符,而是使用equals()方法。也就是说,如果只要两个对象用equals()方法比较返回true,Set就不会接受这两个对象;反之,只要两个对象用equals()方法比较返回false,Set就会接受这两个对象(甚至着两个对象是同一个对象,Set也可以把它们当成两个对象处理)。

例程2:
源代码:Person.java
public class Person{
	private String number;
	public Person(String n){
		this.number = n;
	}
	public String getNumber(){
		return number;
	}
	public void setNumber(String n){
		this.number = n;
	}
	//public int hashCode(){//重新实现hashCode()方法
	//	return number.hashCode();
	//}
	public boolean equals(Object obj){//重新实现equals()方法
		Person p = (Person)obj;
		return number.equals(p.number);
	}
}

源代码:TestSet.java
import java.util.*;
public class TestSet{
	public static void main(String args[]){

		Person p1= new Person("张三");
		Person p2= new Person("张三");
		System.out.println("p1 == p2的值为:"+(p1 == p2));
		System.out.println("p1.equals(p2)的值为:"+(p1.equals(p2)));

		System.out.println("----------");

		HashSet<Person> set = new HashSet<Person>();
		set.add(new Person("张三"));
		boolean result = set.add(new Person("张三"));
		System.out.println("又添加一个张三是否成功:"+result);
		set.add(new Person("李四"));
		System.out.println("Set集合中的元素如下:");
		Iterator<Person> it = set.iterator();
		while(it.hasNext()){
			Person p = it.next();
			System.out.println(p.getNumber());
		}
		System.out.println("个数为:"+set.size());
	}
}


程序的运行结果1(注释掉hashCode()方法):
p1 == p2的值为:false
p1.equals(p2)的值为:true
----------
又添加一个张三是否成功:true
Set集合中的元素如下:
张三
张三
李四
个数为:3


程序的运行结果2(去掉hashCode()方法的注释):
p1 == p2的值为:false
p1.equals(p2)的值为:true
----------
又添加一个张三是否成功:false
Set集合中的元素如下:
张三
李四
个数为:2


结果说明:

public int hashCode()

返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
hashCode 的常规协定是:
在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)

public boolean equals(Object obj)

指示其他某个对象是否与此对象“相等”。
equals 方法在非空对象引用上实现相等关系:
自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
对于任何非空引用值 x,x.equals(null) 都应返回 false。
Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。
注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics