对Java继承的思考-属性

上篇博客《对Java继承的思考-构造方法》已经介绍了父类与子类构造函数之间的关系。今天我们来分析一下属性之间的关系。

一、属性与作用域

我们知道,Java的类属性都有其作用域(也就是变量的作用域),属性的作用域决定了其是否能在子类中使用,一共有四种作用域:

1.public

这应该是最常见的作用域了,它表示属性是公开的,它既能够在子类中使用,也能够被父类和子类实例直接调用:

// Animal类,在任意包中
public class Animal {
  //  public变量
  public String name;
}
// Dog类,在任意包中
public class Dog extends Animal {
  public void printName() {
    // 直接读取父类变量
    System.out.println(name)
  }
  public void setName(String name) {
    // 可直接通过this修改父类name属性的值
    this.name = name;
  }
}
Animal animal = new Animal();
// 父类实例修改内部属性
animal.dog = "大红";
Dog dog = new Dog();
// 实例调用父类name属性,并修改name的值
dog.name = "大黄";
System.out.println(name);
// 子类内部修改父类属性name
dog.setName("大黑")
System.out.println(name);

运行结果如下:

大黄
大黑

明显使用public修饰类属性是不安全的。属性被暴露出来,使其很容易就被修改,一般不推荐类属性使用public修饰符。

2.default

在没有显式添加作用域修饰符的情况下,属性的作用域为default。default修饰的属性只能在包内可见:

public class Animal {
  // Java的默认作用域修饰符为default
  (default) String name;
}

子类内部能否直接调用父类中使用default修饰的属性,取决于父类和子类是否在同一个包内:

// 父类Animal类
// 在com.axen.animal包下
package com.axen.animal;
public class Animal {
  String name;
}

// 与父类在同一个包下
package com.axen.animal;
public class Dog extends Animal {
  public void setName(String name) {
   // 子类内部直接修改父类属性name
    this.name = name;
  }
}

// 与父类不在同一个包下
package com.axen.cat;
public class Cat extends Animal {
  public void setName(String name) {
    // 错误,无法直接调用name属性
    this.name = name;
  }
}

父类实例是否能直接调用父类中使用default修饰的属性,取决于父类实例所在的类是否与父类在同一个包内:

// Test类
// 与Animal类在同一包下
package com.axen.animal;
public class Test {
  public static void main(String args[]) {
    Animal animal = new Animal();
    // 父类实例直接修改属性name
    animal.name = "二黑";
  }
}

// 与Animal不在同一个包下
package com.axen.test;
public class Test1 {
  public static void main(String args[]) {
    Animal animal = new Animal();
    //错误,无法直接调用name属性
    animal.name = "二黑";
  }
}

子类实例能否直接调用父类中使用default修饰的属性,取决于子类实例所在的类是否与父类在同一个包内;子类实例是否能够直接调用其自身内部使用default修饰的属性,取决于子类实例所在的类是否与子类在同一个包内:

// 父类Animal类
package com.axen.animal;
public class Animal {
  String name;
}

// 子类Dog类,与父类在同一个包下
package com.axen.animal
public class Dog extends Animal {
  String name1;
}

// 子类Cat类,与父类不在同一个包下
package com.axen.cat
public class Cat extends Animal {
  String name2;
}

// Test类
// 与Animal类在同一包下
package com.axen.animal;
public class Test {
  public static void main(String args[]) {
    Animal animal = new Animal();
    // 能直接调用name属性
    animal.name = "123";
  }
}

// Test1类
package com.axen.animal;
public class Test1 {
  public static void main(String args[]) {
    Dog dog = new Dog();
    // Test1类与Dog类及Animal类在同一个包下,
    // 因此能直接调用name和name1属性
    dog.name = "123";
    dog.name1 = "1234";

  }
 }

// Test2类
// 与Animal类在同一包下
package com.axen.animal;
public class Test2 {
  public static void main(String args[]) {
    Cat cat = new Cat();
    // 能直接调用name属性
    cat.name = "123";
    // 不能直接调用name2属性,
    // Cat类与Test2类不在同一包下
    dog.name2 = "1234";
  }
 }

// Test3类
// 与Cat类在同一个包下
package com.axen.cat;
public class Test3 {
  public static void main(String args[]) {
    Cat cat = new Cat();
    // 不能直接调用name属性,
    // Animal类与Test3类不在同一个包内
    cat.name = "123";
    // 能直接调用name2属性,
    dog.name2 = "1234";
  }
 }

3.protected

protected修饰符所代表的意思是仅能在父类或者子类内部调用,父类和子类的实例都无法直接调用protected修饰的属性。如下例子:

// Animal类,可以在任意包内
public class Animal {
  protected String name;
}

// Dog类,可以在任意包内
public class Dog extends Animal {
   public void setName(String name) {
    // 子类内部调用protected修饰的父类属性name
     this.name = name; 
   }
}

// Test类
public class Test {
  public static void main(String args[]) {
    Animal animal = new Animal();
    // 错误,无法直接调用属性name
    animal.name = "123";
    Dog dog = new Dog();
    // 错误,无法直接调用属性name
    dog.name = "1234";
  }
}

4.private

private修饰符修饰的属性是私有的,只能在父类内部使用,子类内部、父类实例和子类实例都无法直接调用私有属性。

// Animal类,可以在任意包内
public class Animal {
  private String name;
}

// Dog类,可以在任意包内
public class Dog extends Animal {
   public void setName(String name) {
    // 错误,子类内部无法调用private修饰的父类属性name
     this.name = name; 
   }
}

// Test类
public class Test {
  public static void main(String args[]) {
    Animal animal = new Animal();
    // 错误,无法直接调用属性name
    animal.name = "123";
    Dog dog = new Dog();
    // 错误,无法直接调用属性name
    dog.name = "1234";
  }
}

你可能感兴趣的:(对Java继承的思考-属性)