上篇博客《对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";
}
}