站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题Java编程思想(2nd)学习笔记(6)

Java编程思想(2nd)学习笔记(6)

摘要: Java编程思想(2nd)学习笔记(6)

第6章 重复运用classes
一.继承(inheritance)
1. 在derived class中overriding某个函数时,只能覆写base class中的接口,
即base class中的public或protected或friendly函数。如果试图overriding一个
private函数,虽然编译通过,但实际上你只是在derived class中添加了一个函数。如
class Cleanser{
private void prt(){//(b)
System.out.println("Cleanser.prt()"); } }
public class ExplicitStatic extends Cleanser{
public void prt(){
System.out.println("ExplicitStatic.prt()"); }
public static void main(String[] args){
Cleanser x = new ExplicitStatic();
x.prt();//(a) } }
因为Cleanser中的prt()是private,所以不能在其derived class中被覆写。
ExplicitStatic中的prt()只是ExplicitStatic中的一个函数,所以当试图在(a)
处通过多态来调用prt()时,会发生错误。如果把(b)处的private去掉,则结果为
ExplicitStatic.prt()
2. Super的使用
1)通过关键字super可以调用当前class的superclass(父类)。
例6.1.1.1
class Base{
Base(){System.out.println("Base()");}
public void scrub() { System.out.println(" Base.scrub()"); } }
class Cleanser extends Base{
private String s = new String("Cleanser");
public void append(String a) { s+=a; }
public void dilute() { append(" dilute()"); }
public void apply() { append(" apply()"); }
public void scrub() { append(" scrub()"); }
public void print() { System.out.println(s); }
Cleanser(){
System.out.println("Cleanser(): " + s); }
public static void testStatic(){
System.out.println("testStatic()"); }
public static void main(String[] args){
Cleanser x = new Cleanser();
x.dilute(); x.apply(); x.scrub(); x.print(); } }
public class ExplicitStatic extends Cleanser{
ExplicitStatic(){
System.out.println("ExplicitStatic()"); }
public void scrub(){
append(" Detergen.scrub()");
super.testStatic();
super.scrub();//调用的是Cleanser.scrub() }
public void foam() { append(" foam()"); }
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();
x.dilute(); x.apply(); x.scrub(); x.foam();
x.print(); System.out.println("Test base class:");
Cleanser.main(args);
testStatic(); } }
运行结果:
Base()
Cleanser(): Cleanser
ExplicitStatic()
testStatic()
Cleanser dilute() apply() Detergen.scrub() scrub() foam()
Test base class:
Base()
Cleanser(): Cleanser
Cleanser dilute() apply() scrub()
testStatic()
2)通过super来调用superclass中的成员时,调用的是最近成员。
例6.1.1.2
class Base{
protected String baseS = "Base";//(a)
//private String baseS = "Base";
Base(){System.out.println("Base()");} }
class Cleanser extends Base{
protected String baseS = "Cleanser";//(b)
public String s = new String("Cleanser");
Cleanser(){
System.out.println("Cleanser(): " + s); }
Cleanser(String a){
System.out.println("Cleanser(" + a + "): s = " + s ); } }
public class ExplicitStatic extends Cleanser{
String s2 = s;
String baseS = super.baseS; //(c)
ExplicitStatic(){
super("ExplicitStatic");
System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = " +
baseS + "super.baseS = " + super.baseS);
baseS = "ExplicitStatic";
System.out.println("baseS = " + baseS + " , super.baseS = " +
super.baseS); }
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic(); } }
结果1:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Cleanser,super.baseS = Cleanser
baseS = ExplicitStatic , super.baseS = Cleanser
在上面例子中,在三个class中都存在String bases实例。在ExplicitStatic中如果
直接调用baseS,则实际调用的是当前类ExplicitStatic中的baseS(即(c)处的成
员);如果通过super.bases来调用baseS,则调用的是离当前类ExplicitStatic最
近的baseS成员,即Cleanser class中的baseS实例(即(b)处),产生的结果如结
果1所示。如果把(b)处语句注释掉,则将调用Base class中的baseS,结果如结果2所示。
结果2:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Base,super.baseS = Base
baseS = ExplicitStatic , super.baseS = Base
3. Base class的初始化
2.1 当你产生derived class对象时,其中会包含base class子对象
(subobject)。这个子对象就和你另外产生的base class对象一模一样。
2.2 通过super()可调用base class的构造函数,但必须放在构造函数的第一
行,并且只能在构造函数中运用。
2.3 初始化顺序为:
1) 加载代码(.class文件)
2) 初始化class的静态成员,初始化顺序了“从里到外”,即从base
 class开始。
3) 在derived class的构造函数中调用base class的构造函数。
如果在derived class的构造函数中没有通过super()显式调用调用base class的构
造函数,编译器会调用bass class的default构造函数并自动生成相应的调用语句,
从而产生一个base class实例。如果在derived class的构造函数中通过super()显
示调用了父类的构造函数,则调用所指定的构造函数。调用构造函数的调用顺序是“从里到外”。
4) 调用derived class的构造函数。
**:当base class没有default构造函数时,必须在derived class的构造函数中
通过super显示调用base class的构造函数。
例:下面代码的初始化过程为:
1) 装载ExplicitStatic的代码(装载ExplicitStatic.class文件)。
2) 发现ExplicitStatic有关键字extends,装载ExplicitStatic的base class的
代码(装载Cleanser.class文件)。
3) 发现Cleanser有关键字extends,装载Cleanser的base class的代码(装载
Base.class文件)。
4) 初始化Base class中的静态成员。
5) 初始化Cleanser class中的静态成员。
6) 初始化ExplicitStatic class中的静态成员。
如果把(c)处的代码注释掉,那么初始化工作到此就结束了。
7) 为ExplicitStatic对象分配存储空间,并把存储空间初始化为0。
8) 在ExplicitStatic class的构造中调用super("ExplicitStatic")(在
ExplicitStatic class的构造函数中显式调用父类的构造函数),试图产生一个
Cleanser class实例。
9) 为Cleanser对象分配存储空间,并把存储空间初始化为0。
10) 由于Cleanser class又是继承自Base class,会在Cleanser
class的构造函数中通过super()(由于没有显式调用父类的构造函数,所以自动调
用父类的default构造函数)调用父类的构造函数,试图产生一个Cleanser class实例。
11) 产生一个Base class实例。先初始化成员变量,再调用构造函数。
12) 回到Cleanser class,产生一个实例。首先初始化Cleanser
class中的成员数据,再执行构造函数Cleanser(String a)中的其余部分。
13) 回到ExplicitStatic class,产生一个实例。首先初始化
ExplicitStatic class中的成员数据,再执行构造函数ExplicitStatic ()中的其余
部分(System.out.println(“ExplicitStatic()”))。
class Base{
static int s1 = prt("s1 initialized.", 11);
int i1 = prt("i1 initialized.", 12);
Base(){
System.out.println("Base()");
System.out.println("s1 = " + s1 + " ,i1 = " + i1);
draw();//(d) }
void draw(){
System.out.println("base.draw:s1 = " + s1 + " ,i1 = " + i1); }
static int prt(String s, int num) {
System.out.println(s);
return num; } }
class Cleanser extends Base{
static int s2 = prt("s2 initialized.", 21);
int i2 = prt("i2 initialized.", 22);
Cleanser(){
System.out.println("Cleanser()");
System.out.println("s2 = " + s2 + " ,i2 = " + i2); }
Cleanser(String a){
//super();(b)
System.out.println("Cleanser(" + a + ")");
System.out.println("s2 = " + s2 + " ,i2 = " + i2); }
void draw(){
System.out.println("Cleanser.draw:s2 = " + s2 + " ,i2 = " + i2); } }
public class ExplicitStatic extends Cleanser{
static int s3 = prt("s3 initialized.", 31);
int i3 = prt("i3 initialized", 31);
ExplicitStatic(){
super("ExplicitStatic");//(a)
System.out.println("ExplicitStatic()");
System.out.println("s3 = " + s3 + " ,i3 = " + i3); }
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic();//(c) } }
结果:
s1 initialized.
s2 initialized.
s3 initialized.
//如果把(c)处的代码注释掉,输出结果到此为止,不会输出下面结果
i1 initialized.
Base()
s1 = 11 ,i1 = 12
Cleanser.draw:s2 = 21 ,i2 = 0//(d)处结果
i2 initialized.
Cleanser(ExplicitStatic)//(a)处结果
s2 = 21 ,i2 = 22
i3 initialized
ExplicitStatic()
s3 = 31 ,i3 = 31
由于在Base()中调用draw()时,Cleanser中的i2还未进行初始化,而在为Cleanser
对象分配存储空间时,把存储空间初始化为0,所以此时i2为0。
2.4 代码及结果中的(a)说明了是先产生当前class的base class的实例,
否则在derived class中无法调用base class的成员。在调用Cleanser class的构造
函数Cleanser(String a)时,在Cleanser(String a)中没有用super显式调用Base
class的构造函数,所以系统会自动生成调用Base class的default构造函数的语句,如(b)。
4. 组合与继承之间的快择
. 1)继承表示的是一种“is-a(是一个)”的关系,如货车是汽车中
的一种;组合表示的是一种“has-a(有一个)”的关系,如汽车有四个轮子。
2)是否需要将新的class向上转型为base class。
5. 在继承中的访问权限
protect变量能被子孙类所调用。如Base class中的baseS能被Cleanser class和
ExplicitStatic class调用。
class Base{
protected String baseS = "Base";
//private String baseS = "Base";
Base(){System.out.println("Base()");} }
class Cleanser extends Base{
protected String baseS = "Cleanser";
public String s = new String("Cleanser");
Cleanser(){
System.out.println("Cleanser(): " + s); }
Cleanser(String a){
System.out.println("Cleanser(" + a + "): s = " + s ); } }
public class ExplicitStatic extends Cleanser{
String s2 = s;
String baseS = super.baseS;
ExplicitStatic(){
super("ExplicitStatic");
System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = " +
baseS + "super.baseS = " + super.baseS);
baseS = "ExplicitStatic";
System.out.println("baseS = " + baseS + " , super.baseS = " +
super.baseS); }
public static void main(String[] args){
ExplicitStatic x = new ExplicitStatic(); } }
结果:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Cleanser, super.baseS = Cleanser
baseS = ExplicitStatic , super.baseS = Cleanser
二.关键字final
1.Final data
1.1 final data
1)当基本型别被定义为final,表示它的数据值不能被改变。如
final int i = 9;
i++;//编译错误,不能改变I的值
2) 当object reference被定义为final时,不能改变的只是reference而不是对象本身。如
class Value{
int i = 1; }
public class ExplicitStatic extends Cleanser{
public static void main(String[] args){
final Value v = new Value();//v.i = 1
v.i++;//v.i = 2
//v = new Value(); } }
由于v为final,所以不能通过new Value()使v重新指向一个对象;但是v所指向的对
象的值是可以改变的(v.i++)。
1.2 blank finals
我们可以将数据成员声明为final但不给予初值,这就是blank finals。但blank 
finals必须且只能在构造函数中进行初始化。
public class ExplicitStatic {
final int ib;
final int i = 1;
ExplicitStatic() {
ib = 2;//(a)
//i = 3; (b)
System.out.println("i = " + i + ", ib = " + ib); }
public static void main(String[] args){
ExplicitStatic ex = new ExplicitStatic(); } }
ib为blank finals,所以可以在构造函数中进行初始化。如果把(a)处的代码注
释掉,则ib没有初值,编译出错。而i在定义处已进行了初始化,则不能改变i的值
,(b)处的代码编译错误。
**:非blank finals成员即使在构造函数中也不能更改其值
2.Final methods
1)被声明为final的函数不能被覆写
2)class中所有private函数自然而然会是final。
1. Final classes
1)当一个class被声明为final时,表示它不能被继承,但class的数据成员不是
final,可以被改变。如
class SmallBrain{}
final class Dinosaur{
int i = 7;
int j = i;
SmallBrain x = new SmallBrain();
void f(){}; }
//不能继承final函数
//class Further extends Dinosaur{}
public class ExplicitStatic{
public static void main(String[] args){
Dinosaur n = new Dinosaur();
n.f();
n.i = 40;//final class中的non-final数据成员可以被改变
n.j++; } }
↑返回目录
前一篇: Java编程思想(2nd)学习笔记(7)
后一篇: Java比.NET更好的101条理由