在java中,产生对象的过程是这样的:
(1)加载类,为在静态块和类变量分配一个唯一的内存,此时都是默认值(唯一是因为java的每一个类都对应唯一的一个class对象,类变量可以说是这个class对象的实例变量)
(2)对静态块赋初值
(3)当调用new的时候,首先就会为这个对象以及其所有的从父类继承的实例变量分配一块内存并赋值默认值,然后进行初始化:
在初始化这里,有一个初始化的顺序:定义实例变量时指定的初始值和非静态初始化块中对实例变量指定初始化是位于同一个level的,初始化顺序与他们在代码中的先后顺序一致,而构造器是最后才进行赋值的(实质上在java的实质运行中这三种初始化最后都会在构造器中进行,这也表明了构造方法只是起到了初始化数值的作用,当你一开始new的时候,已经分配了内存并且赋予了默认值)
当你真正创建的子类对象的时候,会按照这个顺序去调用初始化方法:
(1)祖父的非静态初始化块
(2)隐式或显示的调用祖父类的一个或多个构造器方法进行初始化
然后依次类推到父亲,儿子
最后才是创建出了子类对象出来,然而这个子类对象里面是只有子类对象一个的,并没有其他父类对象,所有this一直都是表示这个子类对象,
class Creature
{
{
System.out.println("Creature的非静态初始化块");
}
// 下面定义两个构造器
public Creature()
{
System.out.println("Creature无参数的构造器");
}
public Creature(String name)
{
// 使用this调用另一个重载的、无参数的构造器
this();
System.out.println("Creature带有nmae参数的构造器,name参数:"
+ name);
}
}
class Animal extends Creature
{
{
System.out.println("Animal的非静态初始化块");
}
public Animal(String name)
{
super(name);
System.out.println("Animal带一个参数的构造器,name参数:" + name);
}
public Animal(String name , int age)
{
// 使用this调用另一个重载的构造器
this(name);
System.out.println("Animal带2个参数的构造器,其age:" + age);
}
}
class Wolf extends Animal
{
{
System.out.println("Wolf的非静态初始化块");
}
public Wolf()
{
// 显式调用父类的带2个参数的构造器
super("灰太狼", 3);
System.out.println("Wolf无参数的构造器");
}
public Wolf(double weight)
{
// 使用this调用另一个重载的构造器
this();
System.out.println("Wolf的带weight参数的构造器,weight参数:"
+ weight);
}
}
public class InitTest
{
public static void main(String[] args)
{
new Wolf(5.6);
}
}
这里就是先进行creature的初始化,再进行animal的初始化,最后进行wolf的初始化(记住这里只有一个wolf对象)
(4)当你使用多态的时候,你用父类的引用变量去调用对象的实例变量的时候,是调用父类的实例变量,而调用方法的时候,是调用实质上的对象的方法:
这是因为在实质jvm中,方法是被直接放到子类中的,是可以覆盖的,而实例变量是分配了一块新的内存
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对象
Apple a = new Apple();
// 调用getSuper()方法获取Apple对象关联的super引用
Fruit f = a.getSuper();
// 判断a和f的关系
System.out.println("a和f所引用的对象是否相同:" + (a == f));
System.out.println("访问a所引用对象的color实例变量:" + a.color);
System.out.println("访问f所引用对象的color实例变量:" + f.color);
// 分别通过a、f两个变量来调用info方法
a.info();
f.info();
// 调用AccessSuperInfo来调用父类的info()方法
a.AccessSuperInfo();
}
}
(5)final修饰符
一.当你用final修饰符修饰一个变量的时候,这个变量一旦被赋值就不可以改变值,更加重要的是,当final变量的值时可以直接得到的时候,编译期间会被替换成宏变量,相当于就是将那个变量直接变成了那个对应值的常量(这里注意一下判断==是在编译期间判断的)
二.当你使用final方法的时候,final是指不能被继承,但是在这个前提上,子类必须要有权限访问到父类中的那个方法,否则子类中就可以重新定义那个方法,不过不是重写,而是完全的一个新的方法而已
三.最后一点是在内部类的局部变量必须要使用final使它的值不能被改变,否则的话当多个线程同时使用一个局部变量的时候,便会出现线程安全的问题