第6-8章 访问权限控制、复用种和多态
第6-8章 访问权限控制、复用类和多态
访问权限控制
1、java类可以不显示编写"package xxx.xxx",这样的话该类存在于"(default package)"下。
2、一个java文件(编译单元)只能有一个public的类,该类名与文件名完全相同,文件内其他非public的类具有“包访问权限”(只能被同一包中的其他类所访问)。一个java文件可以没有public的类(这并不常见)。
3、编写java类时,package位于除了注释和空行之外的第一行。
4、类的访问控制修饰符只有如下几种:1、public 2、无 3、final 4、abstract。类修饰符不存在protected和private。
5、Cannot use super in a static context
在子类中的任何静态方法中无法使用this或者super关键字调用父类的的成员
-------------------------------
复用类
1、组合语法
package reusing;//: reusing/Bath.java
// Constructor initialization with composition.
import static net.mindview.util.Print.*;
class Soap {
private String s;
Soap() {
print("Soap()");
s = "Constructed";
}
public String toString() { return s; }
}
public class Bath {
private String // Initializing at point of definition:
s1 = "Happy",
s2 = "Happy",
s3, s4;
private Soap castille;
private int i;
private float toy;
public Bath() {
print("Inside Bath()");
s3 = "Joy";
toy = 3.14f;
castille = new Soap();
//Soap中的构造器在之后执行,如果Soap是Bath的父类,则会先行加载,而在此两者没有这种关系
}
// Instance initialization:在构造器之前执行
{ i = 47; System.out.println("Before Constructor");}
public String toString() {
if(s4 == null) // Delayed initialization:
s4 = "Joy";
return
"s1 = " + s1 + "\n" +
"s2 = " + s2 + "\n" +
"s3 = " + s3 + "\n" +
"s4 = " + s4 + "\n" +
"i = " + i + "\n" +
"toy = " + toy + "\n" +
"castille = " + castille;
}
public static void main(String[] args) {
Bath b = new Bath();
print(b);
}
} /* Output:
Inside Bath()
Soap()
s1 = Happy
s2 = Happy
s3 = Joy
s4 = Joy
i = 47
toy = 3.14
castille = Constructed
*///:~
2a、继承的概念和实质
当创建出一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你用基类直接创建出的对象是一样的,二者区别在于,后者来自于外部,而基类的子对象被包装在导出类对象内部
2b、如下代码分析
package reusing;//: reusing/Detergent.java
// Inheritance syntax & properties.
import static net.mindview.util.Print.*;
class Cleanser {
private String s = "Cleanser";
public void append(String a) { s += a; }
public void dilute() { append(" dilute()"); }
public void apply() { append(" apply()"); }
public void scrub() { append(" scrub()"); }
public String toString() { return s; }
public static void main(String[] args) {
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub();
print(x);
}
}
public class Detergent extends Cleanser {
// Change a method:
public void scrub() {
append(" Detergent.scrub()");
super.scrub(); // Call base-class version
}
// Add methods to the interface:
public void foam() { append(" foam()"); }
// Test the new class:
public static void main(String[] args) {
Detergent x = new Detergent();
x.dilute();
x.apply();
x.scrub();
x.foam();
print(x);
print("Testing base class:");
Cleanser.main(args);
}
} /* Output:
Cleanser dilute() apply() Detergent.scrub() scrub() foam()
Testing base class:
Cleanser dilute() apply() scrub()
*///:~
注意要点:
父类中的s是private的,子类并不会存在这个成员,所以子类中的dilute()、apply()等方法中操作的字符串为父类的成员域s
3、分析以下代码:
public class A {
public A() {System.out.println(" A Constructor ");}
public static void main(String[] args) {
new C();
}
}
class B {
public B() {
System.out.println(" B Constructor ");
}
}
class C extends A {
public B b = new B();
}
问题:先输出" A Constructor "还是" B Constructor "?答案:尽管与成员初始化优先于构造器,但在继承关系中,还是先加载父类。
4、如果基类没有默认的构造器并且存在带参数的构造器,那么在子类中的构造器中需要显式的调用super(参数),否则无法初始化基类,如代码所示:
package reusing;//: reusing/Chess.java
// Inheritance, constructors and arguments.
import static net.mindview.util.Print.*;
class Game {
Game(int i) {
print("Game constructor");
}
}
class BoardGame extends Game {
BoardGame(int i) {
super(i);
print("BoardGame constructor");
}
}
public class Chess extends BoardGame {
Chess() {
super(11);
print("Chess constructor");
}
public static void main(String[] args) {
Chess x = new Chess();
}
} /* Output:
Game constructor
BoardGame constructor
Chess constructor
*///:~
4、关于类的加载次序问题
一、一般情况下,类的成员对象new初始化优先于类的构造器;
二、A extends B,new A(),如果A中存在static C c = new C()的静态成员,则优先于B的构造器,落后于B的static区块;如果是普通成员C c = new C(),则父类B构造器优先;
三、A extends B,没有new A(),类加载到A.class,会执行B中static区块的内容,但不包括B的构造器;既然A中存在static C c = new C()的静态成员,没有new A(),C中的任何静态非静态区块和构造器都不会执行
5、@Override进行编译检查
6、组合和继承的选择:is-a的关系就用继承;has-a的关系就用组合;真的需要向上转型就使用继承,否则慎用。
-------------------------------------
多态
1、父类中的private方法与子类中的同名方法不具有继承关系,是2个独立的方法。
public class PrivateOverride {
private void f() { print("private f()"); }
public static void main(String[] args) {
PrivateOverride po = new Derived();
po.f();
Derived d = new Derived();
d.f();
}
}
class Derived extends PrivateOverride {
protected void f() { print("public f()"); }
} /* Output:
private f()
*///:~
2a、类中定义有OtherClass[] o = new OtherClass[5];执行到该类时不会加载OtherClass。
2、父类中的域和静态方法与子类中的同名域和方法不具有继承关系。
class Super {
public int field = 0;
public int getField() { return field; }
}
class Sub extends Super {
public int field = 1;
public int getField() { return field; }
public int getSuperField() { return super.field; }
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub(); // Upcast
System.out.println("sup.field = " + sup.field +
", sup.getField() = " + sup.getField());
Sub sub = new Sub();
System.out.println("sub.field = " +
sub.field + ", sub.getField() = " +
sub.getField() +
", sub.getSuperField() = " +
sub.getSuperField());
}
} /* Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*///:~
当Sub转型为Super时,任何域访问操作都是由编译器解析,所以不是多态的。Sub包含2个field,自己的和父类的。
访问权限控制
1、java类可以不显示编写"package xxx.xxx",这样的话该类存在于"(default package)"下。
2、一个java文件(编译单元)只能有一个public的类,该类名与文件名完全相同,文件内其他非public的类具有“包访问权限”(只能被同一包中的其他类所访问)。一个java文件可以没有public的类(这并不常见)。
3、编写java类时,package位于除了注释和空行之外的第一行。
4、类的访问控制修饰符只有如下几种:1、public 2、无 3、final 4、abstract。类修饰符不存在protected和private。
5、Cannot use super in a static context
在子类中的任何静态方法中无法使用this或者super关键字调用父类的的成员
-------------------------------
复用类
1、组合语法
package reusing;//: reusing/Bath.java
// Constructor initialization with composition.
import static net.mindview.util.Print.*;
class Soap {
private String s;
Soap() {
print("Soap()");
s = "Constructed";
}
public String toString() { return s; }
}
public class Bath {
private String // Initializing at point of definition:
s1 = "Happy",
s2 = "Happy",
s3, s4;
private Soap castille;
private int i;
private float toy;
public Bath() {
print("Inside Bath()");
s3 = "Joy";
toy = 3.14f;
castille = new Soap();
//Soap中的构造器在之后执行,如果Soap是Bath的父类,则会先行加载,而在此两者没有这种关系
}
// Instance initialization:在构造器之前执行
{ i = 47; System.out.println("Before Constructor");}
public String toString() {
if(s4 == null) // Delayed initialization:
s4 = "Joy";
return
"s1 = " + s1 + "\n" +
"s2 = " + s2 + "\n" +
"s3 = " + s3 + "\n" +
"s4 = " + s4 + "\n" +
"i = " + i + "\n" +
"toy = " + toy + "\n" +
"castille = " + castille;
}
public static void main(String[] args) {
Bath b = new Bath();
print(b);
}
} /* Output:
Inside Bath()
Soap()
s1 = Happy
s2 = Happy
s3 = Joy
s4 = Joy
i = 47
toy = 3.14
castille = Constructed
*///:~
2a、继承的概念和实质
当创建出一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你用基类直接创建出的对象是一样的,二者区别在于,后者来自于外部,而基类的子对象被包装在导出类对象内部
2b、如下代码分析
package reusing;//: reusing/Detergent.java
// Inheritance syntax & properties.
import static net.mindview.util.Print.*;
class Cleanser {
private String s = "Cleanser";
public void append(String a) { s += a; }
public void dilute() { append(" dilute()"); }
public void apply() { append(" apply()"); }
public void scrub() { append(" scrub()"); }
public String toString() { return s; }
public static void main(String[] args) {
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub();
print(x);
}
}
public class Detergent extends Cleanser {
// Change a method:
public void scrub() {
append(" Detergent.scrub()");
super.scrub(); // Call base-class version
}
// Add methods to the interface:
public void foam() { append(" foam()"); }
// Test the new class:
public static void main(String[] args) {
Detergent x = new Detergent();
x.dilute();
x.apply();
x.scrub();
x.foam();
print(x);
print("Testing base class:");
Cleanser.main(args);
}
} /* Output:
Cleanser dilute() apply() Detergent.scrub() scrub() foam()
Testing base class:
Cleanser dilute() apply() scrub()
*///:~
注意要点:
父类中的s是private的,子类并不会存在这个成员,所以子类中的dilute()、apply()等方法中操作的字符串为父类的成员域s
3、分析以下代码:
public class A {
public A() {System.out.println(" A Constructor ");}
public static void main(String[] args) {
new C();
}
}
class B {
public B() {
System.out.println(" B Constructor ");
}
}
class C extends A {
public B b = new B();
}
问题:先输出" A Constructor "还是" B Constructor "?答案:尽管与成员初始化优先于构造器,但在继承关系中,还是先加载父类。
4、如果基类没有默认的构造器并且存在带参数的构造器,那么在子类中的构造器中需要显式的调用super(参数),否则无法初始化基类,如代码所示:
package reusing;//: reusing/Chess.java
// Inheritance, constructors and arguments.
import static net.mindview.util.Print.*;
class Game {
Game(int i) {
print("Game constructor");
}
}
class BoardGame extends Game {
BoardGame(int i) {
super(i);
print("BoardGame constructor");
}
}
public class Chess extends BoardGame {
Chess() {
super(11);
print("Chess constructor");
}
public static void main(String[] args) {
Chess x = new Chess();
}
} /* Output:
Game constructor
BoardGame constructor
Chess constructor
*///:~
4、关于类的加载次序问题
一、一般情况下,类的成员对象new初始化优先于类的构造器;
二、A extends B,new A(),如果A中存在static C c = new C()的静态成员,则优先于B的构造器,落后于B的static区块;如果是普通成员C c = new C(),则父类B构造器优先;
三、A extends B,没有new A(),类加载到A.class,会执行B中static区块的内容,但不包括B的构造器;既然A中存在static C c = new C()的静态成员,没有new A(),C中的任何静态非静态区块和构造器都不会执行
5、@Override进行编译检查
6、组合和继承的选择:is-a的关系就用继承;has-a的关系就用组合;真的需要向上转型就使用继承,否则慎用。
-------------------------------------
多态
1、父类中的private方法与子类中的同名方法不具有继承关系,是2个独立的方法。
public class PrivateOverride {
private void f() { print("private f()"); }
public static void main(String[] args) {
PrivateOverride po = new Derived();
po.f();
Derived d = new Derived();
d.f();
}
}
class Derived extends PrivateOverride {
protected void f() { print("public f()"); }
} /* Output:
private f()
*///:~
2a、类中定义有OtherClass[] o = new OtherClass[5];执行到该类时不会加载OtherClass。
2、父类中的域和静态方法与子类中的同名域和方法不具有继承关系。
class Super {
public int field = 0;
public int getField() { return field; }
}
class Sub extends Super {
public int field = 1;
public int getField() { return field; }
public int getSuperField() { return super.field; }
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub(); // Upcast
System.out.println("sup.field = " + sup.field +
", sup.getField() = " + sup.getField());
Sub sub = new Sub();
System.out.println("sub.field = " +
sub.field + ", sub.getField() = " +
sub.getField() +
", sub.getSuperField() = " +
sub.getSuperField());
}
} /* Output:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*///:~
当Sub转型为Super时,任何域访问操作都是由编译器解析,所以不是多态的。Sub包含2个field,自己的和父类的。