类中静态代码块、构造块、构造方法的执行顺序
静态代码优先于非静态的代码,是因为被static修饰的成员都是类成员,会随着JVM加载类的时候加载而执行,而没有被static修饰的成员也被称为实例成员,需要创建对象才会随之加载到堆内存。所以:静态块>非静态构造块>构造函数。
下面来讨论当构造函数中包含(this(),super())时,这些方法的调用顺序:
执行构造器(构造方法)的时候,在执行方法体之前存在隐式三步:
1,super语句,可能出现以下三种情况:
1)构造方法体的第一行是this语句,则不会执行隐式三步,
2)构造方法体的第一行是super语句,则调用相应的父类的构造方法,
3)构造方法体的第一行既不是this语句也不是super语句,则隐式调用super(),即其父类的默认构造方法,这也是为什么一个父类通常要提供默认构造方法的原因;
2,初始化非静态变量;
3,构造代码块。
由此可知,构造代码块优先于构造方法的方法体,但是this关键字跟super关键字不能同时出现,而且只能在代码的第一行。如果出现了this关键字,隐式三步就不会执行。
public class Demo {
public static int a = 0;
static {
a = 10;
System.out.println("静态代码块在执行a=" + a);
}
{
a = 8;
System.out.println("非静态代码块在执行a=" + a);
}
public Demo() {
this("带参构造方法在执行a="+a); // 调用另外一个构造方法
System.out.println("无参构造方法开始执行:");
System.out.println("无参构造方法在执行a=" + a);
System.out.println("无参构造方法执行完毕!");
}
public Demo(String n) {
System.out.println("有参构造方法开始执行:");
System.out.println(n);
System.out.println("有参构造方法执行完毕!");
}
public static void main(String[] args) {
Demo t = null;
System.out.println("!!!!!!!!!!!!!!!!!!!!!");
t = new Demo();
}
}
/**
静态代码块在执行a=10
!!!!!!!!!!!!!!!!!!!!!
非静态代码块在执行a=8
有参构造方法开始执行:
带参构造方法在执行a=10
有参构造方法执行完毕!
无参构造方法开始执行:
无参构造方法在执行a=8
无参构造方法执行完毕!
**/
- 构造代码块优先于构造方法的方法体。但此处的无参构造函数首行的 this() 语句会先于构造块执行。当通过this调用有参构造时,参数已经传入有参构造函数 public Demo(String n) ,即此时 a==10;
- 然后执行构造代码块(在调用了有参构造会依然遵循:构造代码块优先于构造方法的方法体),修改 a=8 并输出。
参考
简单java类的一对多映射关系
这里使用公司部门和雇员之间的一对多的关系映射,主要记录代码实现细节以及注意事项。
class Dept {
private String name ;
private int num ;
private String loc ;
private Emp emps[] ;
public Dept(String name,int num,String loc){
this.name = name ;
this.num = num ;
this.loc = loc ;
}
/***********************************/
public void setEmps(Emp [] emps){ // 此处设置数组时,不学先行实例化数组,同时给出数组长度
this.emps = new Emp [emps.length] ;
for(int i = 0; i<emps.length; i++){
this.emps[i] = emps[i] ;
}
}
/**************************************/
public Emp[] getEmps(){
return this.emps ;
}
public String getInfo(){
return "部门编号:" + this.num + ", 部门名称:" + this.name + ", 部门地址:" + this.loc ;
}
}
class Emp{
private String name ;
private String job ;
private int num ;
private double comm ;
private double sal ;
private Dept dept ;
private Emp mgr ;
public Emp(int num,String name,String job,double comm,double sal){
this.name = name ;
this.num = num ;
this.job = job ;
this.sal = sal ;
this.comm = comm ;
}
public void setMgr(Emp mgr){
this.mgr = mgr ;
}
public void setDept(Dept dept){
this.dept = dept ;
}
public Emp getMgr(){
return this.mgr ;
}
public Dept getDept(){
return this.dept ;
}
public String getInfo(){
return "雇员编号:" + this.num + ", 雇员姓名:" + this.name + ", 雇员工作:" + this.job + ", 雇员工资:" + this.sal + ", 雇员奖金:" + this.comm;
}
}
public class Demo {
public static void main(String args[]) {
// 第一步:根据数据表结构设置数据
// 产生各自独立对象
Dept Dept_Account = new Dept("ACCOUNTING",10,"ShangHai") ;
Emp ea = new Emp(7369,"Smith","CLERK",800.00,0.00) ;
Emp eb = new Emp(7902,"Ford","MANAGER",2450.00,0.00) ;
Emp ec = new Emp(7839,"King","PRESIDENT",8000.00,0.00) ;
// 设置雇员和领导关系
ea.setMgr(eb) ;
eb.setMgr(ec) ;
// 设置关于和部门关系
ea.setDept(Dept_Account) ;
eb.setDept(Dept_Account) ;
ec.setDept(Dept_Account) ;
Dept_Account.setEmps(new Emp[]{ea,eb,ec}) ;
// 第二步:根据数据表描述取得设置的数据
System.out.println(ea.getInfo()) ;
System.out.println("\t-|" + ea.getMgr().getInfo()) ;
System.out.println("\t-|" + ea.getDept().getInfo()) ;
System.out.println(Dept_Account.getInfo()) ;
/**************************************/
for(int i = 0 ; i < Dept_Account.getEmps().length ; i ++){ // 循环查找,代码链实现
System.out.println("\t-|" + Dept_Account.getEmps()[i].getInfo()) ;
if( Dept_Account.getEmps()[i].getMgr() != null ){
System.out.println("\t\tMGR: -|" + Dept_Account.getEmps()[i].getMgr().getInfo()) ;
}
}
/**************************************/
}
}
运行结果:
Java为什么不支持泛型数组
在编写Java列表(泛型类型)的时候,链表转数组功能出现以下错误:
Note: LinkDemo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
然后使用 javac -Xlint LinkDemo.java
进行编译,出现如下错误:
LinkDemo.java:77: warning: [unchecked] unchecked cast
this.retArray = (T[])new Object[this.count] ;
^
required: T[]
found: Object[]
where T is a type-variable:
T extends Object declared in class Link
LinkDemo.java:163: warning: [overrides] Class Student overrides equals, but neither it nor any superclass overrides hashCode method
class Student {
^
2 warnings
发现是java不支持泛型数组所致。
原因,转自知乎
根本的原因是:数组在创建的时候必须知道内部元素的类型,而且一直都会记得这个类型信息,每次往数组里添加元素,都会做类型检查。
但因为Java泛型是用擦除(Erasure)实现的,运行时类型参数会被擦掉。比如:
List<String> l = new ArrayList<String>();
l.add("hello");
String str=l.get(0);
运行时,类型参数<String>
都被擦掉,只有在最后读取内部元素的时候,才插入一个类型转换。看起来就像下面这样,
List l = new ArrayList<String>();
List.add("hello");
String str=(String)List.get(0);
所以,如果像下面这样初始化泛型数组的话,
List<String>[] l = new ArrayList<String>[10]; //Error
运行时编译器只能看到ArrayList,而看不到泛型的String类型参数。数组由于无法确定所持有元素的类型,所以不允许初始化。
Java Language Specification明确规定:数组内的元素必须是“物化”的。
It is a compile-time error if the component type of the array being initialized is not reifiable.
对“物化”的第一条定义就是不能是泛型:
A type is reifiable if and only if one of the following holds:
It refers to a non-generic class or interface type declaration.
... ...
因为Array的具体实现是在虚拟机层面,嵌地非常深,也查不到源码。只好用javap反编译看看具体初始化数组的字节码。我们反编译下面一段代码:初始化一个String数组和一个int数组。
String[] s=new String[]{“hello”};
int[] i=new int[]{1,2,3};
反编译的片段如下:
Code:
0: iconst_1
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String hello
8: aastore
9: astore_1
10: iconst_3
11: newarray int
13: dup
14: iconst_0
15: iconst_1
... ...
其中:
"1: anewarray #2":创建String数组
"11: newarray int":创建int数组
anewarray和newarray都是虚拟机内部用来创建数组的命令。最多只能有2的8次方256个操作码,光创建数组就占了不止一个,可见数组的地位有多特殊。
其中newarray用atype来标记数组类型。anewarray用index来标记。从描述里可以看到,数组除了元素类型,还有一个必须确定的是长度,因为数组是一段连续内存。
查一下 Java Virtual Machine 对anewarray命令的描述,
anewarray <type>
<type> indicates what types of object references are to be stored in the array. It is either the name of a class or interface, e.g. java/lang/String, or, to create the first dimension of a multidimensional array, <type> can be an array type descriptor, e.g.[Ljava/lang/String;
比如anewarray字节码命令的格式就是anewarray后面跟一个具体的元素类型。所以不能确定的确切类型,就无法创建数组。
最初在还没有泛型的时候,数组就一直是以“The Special One”的形象出现。那个时候所有容器都还只是持有Object。在其他容器都不太关心类型安全的年代,数组就特立独行地坚持类型检查,它的使命就是提供一个“类型安全”和效率更高的容器。所以类型检查和长度限制都被写到了字节码的规范里。至于到了支持泛型的年代,泛型“泛化”的本质就和数组“精确高效”的主旨根本上是相违背的。而且要改的话就要在字节码里动刀了。还是历史包袱的问题。
参考
相关学习网站
博客:
Luc–博客园–Google