文章目录
复用有两种方式,组合和继承
1. 组合
在新类中创建现有类的对象。这种方式叫做 “组合”(Com-position),通过这种方式复用代码的功能,而非其形式
class WaterSource {
private String s;
WaterSource() {
System.out.println("WaterSource()");
s = "Constructed";
}
@Override public String toString() {
return s; }
}
public class SprinklerSystem {
private String valve1, valve2, valve3, valve4;
private WaterSource source = new WaterSource();
private int i;
private float f;
@Override public String toString() {
return
"valve1 = " + valve1 + " " +
"valve2 = " + valve2 + " " +
"valve3 = " + valve3 + " " +
"valve4 = " + valve4 + "\n" +
"i = " + i + " " + "f = " + f + " " +
"source = " + source; // [1]
}
public static void main(String[] args) {
SprinklerSystem sprinklers = new SprinklerSystem();
System.out.println(sprinklers);
}
}
/* Output:
WaterSource()
valve1 = null valve2 = null valve3 = null valve4 = null
i = 0 f = 0.0 source = Constructed
*/
2. 继承
继承是所有面向对象语言的一个组成部分。事实证明,在创建类时总是要继承,因
为除非显式地继承其他类,否则就隐式地继承 Java 的标准根类对象(Object)
继承的父类子类的构造顺序:
class Art {
Art() {
System.out.println("Art constructor");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing constructor");
}
}
public class Cartoon extends Drawing {
public Cartoon() {
//super(); 这句调用隐藏了
System.out.println("Cartoon constructor");
}
public static void main(String[] args) {
Cartoon x = new Cartoon();
}
}
/* Output:
Art constructor
Drawing constructor
Cartoon constructor
*/
继承时的各个类的构造顺序为:从最高层的父类依次往下进行构造
注意一个问题,上面的Drawing类的构造方法中其实隐式地调用了父类Carton地无参构造方法,假设给Craton类只定义一个有参构造方法,则编译器不会给Carton类提供一个默认的构造方法,此时必须在Drawing的构造方法中显示地调用Carton类的有参构造方法
class Carton{
public Carton(int i){
System.out.println("Carton 类的有参构造方法");
}
}
public class Drawing extends Carton {
public Drawing(){
super(1);//必须加上这一句 否则编译错误
System.out.println("Drawing 类的无参构造方法");
}
}
方法重写和变量覆盖:
class Carton{
String name="Carton";
public void print(){
System.out.println("Carton");
}
}
public class Drawing extends Carton {
String name="Drawing";
public void print(){
System.out.println("Drawing");
}
public void newMethod(){
System.out.println("newMethod");
}
public static void main(String[] args){
Carton c=new Drawing();
System.out.println(c.name);//Carton
c.print();//Drawing
// c.newMethod();//编译错误 父类中没有newMethod方法
}
}
当引用变量是父类类型,创建的对象的子类类型时,调用的变量时父类的,调用的重载的方法是子类的
3. final关键字
- final修饰基本类型时,必须在定义常量的时候进行赋值或代码块中或构造器中赋值,该基本类型的值后面不能被改变
final int a=1;//ok
final int a; a=1;//not ok
final int a;
{
a=1;//ok
}
- final修饰引用类型时,引用变量不能再指向其他的对象,但是对象里面的数据可以改变
final Apple apple=new Appale();
appple=new Apple();//not ok
apple.price=2.2;//ok
final修饰方法和类:
- fianl修饰的方法不能被子类重写覆盖
- 类中所有的 private 方法都隐式地指定为 final, private方法不能被子类访问,因此不能被重写,注意一点,如果父类的中的方法只是被private修饰,在子类中依然可以定义一个具有相同方法签名的方法,此时不是重写,而是在子类中创建了一个新的方法,与父类中的方法没有关系
- 一个类是 final (final 关键字在类定义之前),就意味着它不能被继承
4. 类初始化和加载
“类的代码在首次使用时加载 “。这通常是指创建类的第一个对象,或者是访问了类的 static 属性或方法。构造器也是一个 static 方法尽管它的 static 关键字是隐式的
class Insect {
private int i = 9;
protected int j;
Insect() {
System.out.println("i = " + i + ", j = " + j);
j = 39;
}
private static int x1 = printInit("static Insect.x1 initialized");
static int printInit(String s) {
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect {
private int k = printInit("Beetle.k initialized");
public Beetle() {
System.out.println("k = " + k);
System.out.println("j = " + j);
}
private static int x2 =printInit("static Beetle.x2 initialized");
public static void main(String[] args) {
System.out.println("Beetle constructor");
Beetle b = new Beetle();
}
}
/* Output:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9, j = 0
Beetle.k initialized
k = 47
j = 39
*/
- 加载Beetle类,发现Beetle类有一个基类,于是先加载基类,加载基类时不论是否创建了基类的对象
- 父类的静态初始化操作
- 子类的静态初始化操作
- 类加载完成,开始对象创建
- 先创建父类的对象: 1. 父类对象中的所有基本类型变量都被置为默认值,对象引用被设为 null 2.调用基类的构造器(基本类型和引用类型的显示赋值实际上会被提取到构造器方法中,代码块中的显示赋值语句也会被提取到构造器中)
- 创建子类的对象,过程和创建父类的对象相同
总结:
- 静态代码块比非静态代码块先执行
- 静态变量只会分配一次空间,静态代码块只会执行一次
- 代码块中的语句比构造器中的语句先执行(可以看出将代码块中的语句提到构造器中的前面部分)
- 变量值的变化: 分配空间和默认值->显示指定的值(如果定义时指定值和代码块中指定值同时存在,按定义的值和代码块的相对位置来判断,最后更新的值为最终的值)->构造器方法中指定的值->普通方法中指定的值