Java初始化顺序
实例变量的实例化
三个地方:
- 定义实例变量指定初始值
- 非静态初始化块中对实例变量指定初始值
- 构造器中对实例变量指定初始值
其中,1、2中方式比第三种方式更早执行,1、2种执行顺序与它们在源程序中的排列顺序相同。
类变量的初始化
两个地方:
- 定义是指定
- 静态初始化块中指定
这两种方式的执行顺序与它们在源程序中的排列顺序相同
public class StaticInitTest { static int count = 2; static { System.out.println("StaticInitTest静态初始化块"); name = "Java编程"; } static String name = "疯狂java讲义"; public static void main(String[] args) { System.out.println("count类变量的值:" + StaticInitTest.count); System.out.println("name类变量的值:" + StaticInitTest.name); } }
输出:
StaticInitTest静态初始化块
count类变量的值:2
name类变量的值:疯狂java讲义
解析:name刚开始赋值为Java编程
从内存看Java实例化
class Price { final static Price INSTANCE = new Price(2.8); static double initPrice = 20; double currentPrice; public Price(double discount){ currentPrice = initPrice - discount; } } public class PriceTest{ public static void main(String[] args) { System.out.println(Price.INSTANCE.currentPrice); Price p = new Price(2.8); System.out.println(p.currentPrice); } }
输出:-2.8
17.2
解析:
初始化分为以下两个阶段:
1. 系统为Price的两个类变量分配内存空间
2. 按初始化代码的排列顺序执行初始化
Java初始化的内存分配
(注意java在继承变量和方法的时候是有区别的)
当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定。但通过该变量调用它引用的对象的实例方法时,该方法的行为将由它实际所引用的对象来决定。
原因:Java编译器会将父类的方法加到子类中去,而父类中的实例变量则不会。
结论:当一个程序创建一个子类对象时,系统不仅会为该类中定义的实例变量分配内存,也会为其父类中定义的所有实例变量分配内存。
public class Test { public static void main(String[] args) { new Derived(); } } class Base{ private int i = 2; public Base(){ //要弄清楚这里的this指向的是子类 //可以通过this.getClass开看 //所以this.display()调用的是子类的,输出的i当然就是子类的i了 this.display(); } public void display(){ System.out.println(i); } } class Derived extends Base{ private int i = 22; public Derived(){ i = 222; } public void display(){ System.out.println(i); } } 输出:0
例子2: class Animal{ private String desc; public Animal(){ this.desc = getDesc(); } public String getDesc(){ System.out.println("Animal"); return "Animal"; } } public class Wolf extends Animal{ private String name; private double weight; public Wolf(String name, double weight){ this.name = name; this.weight = weight; } @Override public String getDesc(){ System.out.println("Wolf[name=" + name + " ,weight=" + weight +"]"); return "Wolf[name=" + name + " ,weight=" + weight +"]"; } public static void main(String[] args) { System.out.println(new Wolf("灰太狼", 32.3)); } }
输出:Wolf[name=null ,weight=0.0]
解析:因为子类重写了父类的方法,所以实例化子类实例时,不会把父类的那个方法复制过来,所以getDesc()调用的是子类的重写方法
例子3:
class Fruit{ String color = "未确定颜色"; public Fruit getThis(){ return this; } public void info(){ System.out.println("Fruit 方法"); } } public class Apple extends Fruit{ @Override public void info(){ System.out.println("Apple 方法"); } //通过super调用父类的info()方法 public void AccessSuperInfo(){ super.info(); } //尝试返回super关键子代表的内容 public Fruit getSuper(){ return super.getThis(); } String color = "红色 "; public static void main(String[] args) { Apple a = new Apple(); Fruit f = a.getSuper(); System.out.println("a和f所引用的对象是否相同:" + (a == f)); System.out.println("访问a所引用对象的color实例变量: " + a.color); System.out.println("访问f所引用对象的color实例变量:" + f.color); a.info(); f.info(); //调用AccessSuperInfo来调用父类的info()方法 a.AccessSuperInfo(); } }