继承(inheritance)

子类(subclass, child class, or extended class)
父类(superclass, parent class, or base class) ——>自己编写的类/Java类库中的类

Java不支持多重继承,即子类只能有一个父类。

1
2
3
4
class 子类名 extends 父类名  
{
...
}

继承性

同一包(package)中,
子类自然地继承了父类中不是private的成员变量和方法
(即:friendly, protected, public)

不在同一包(package)中,
子类只能继承父类的protected, public成员变量和方法

一个类A中的protected成员变量和方法可以被它的直接子类和间接子类继承,
比如B是A的子类,C是B的子类 ,D又是C的子类,那么B、C和D类都继承了A的protected成员变量和方法。
but
在E类中,用D类创建了一个对象d,d通过“.”运算符访问protected成员变量和protected方法,则E类和D类需在同一包中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 子类中声明定义的方法不可以操作未继承的成员变量,但从父类继承的方法可以操作没有继承的变量。
class A
{
private int x=10;
protected int y=20;
void f()
{
y=y+x;
System.out.println(y);
}
}
class B extends A
{
void g()
{
y=y+1;
System.out.println(y);
}
}
class Example5_2
{
public static void main(String args[])
{
B b=new B();
b.g();
b.f();
b.g();
}
}

成员变量隐藏和方法重写

当在子类中定义和父类中同名的成员变量时,子类就隐藏了继承的成员变量
子类通过方法重写(overriding)来隐藏继承的方法。
方法重写:子类中定义一个方法,并且这个方法的名字、返回类型、参数个数和类型与从父类继承的方法完全相同。
使用关键字super子类才能使用被隐藏的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class A 
{
protected double x=8.0,y=0.888888;
public void speak()
{
System.out.println("I love NBA");
}
public void cry()
{
y=x+y;
System.out.printf("y=%f\n",y);
}
}
class B extends A
{
int y=100,z; //子类隐藏了继承的成员变量y
public void speak() //方法重写
{
z=2*y;
System.out.println("I love This Game");
System.out.printf("y=%d,z=%d",y,z);
}
}
public class Example5_4
{
public static void main(String args[])
{
B b=new B();
b.cry();
b.speak();
}
}

父类的某个方法被其子类重写(overriding)时,可以产生自己的功能行为 ——> 多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Animal 
{
void cry(){}
}
class Dog extends Animal
{
void cry()
{
System.out.println("Wang!...");
}
}
class Cat extends Animal
{
void cry()
{
System.out.println("miao~~");
}
}
public class Example5_9
{
public static void main(String args[])
{
Animal animal;
animal=new Dog();
animal.cry(); //Wang!...
animal=new Cat();
animal.cry(); //miao~~
}
}

重载

如果子类在准备隐藏继承的方法时,
参数个数或参数类型与父类的方法不尽相同,
那实际上也没有隐藏继承的方法,这时子类就出现两个方法具有相同的名字,
重载(overloading)

super

  • 在子类中使用super调用父类的构造方法
  • 在子类中使用super调用被子类隐藏的成员变量和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class A 
{
int x;
A()
{
x=100;
}
A(int x)
{
this.x=x;
}
}
class B extends A
{
int z;
B(int x)
{
super(x);
z=30;
}
B()
{
super();
z=300;
}
public void f()
{
System.out.printf("x=%d,z=%d\n",x,z);
}
}
public class Example5_6
{
public static void main(String args[])
{
B b1=new B(10);
b1.f(); //x=10,z=30
B b2=new B();
b2.f(); //x=100,z=300
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class A 
{
int m=0;
long f()
{
return m;
}
}
class B extends A
{
int m=1;
long f()
{
super.m=10;
return super.f()+m;
}
long g()
{
return super.f()/2;
}
}
public class Example5_7
{
public static void main(String args[])
{
B b=new B(); //b.m=1
b.m=3; //b.m=3
System.out.println(b.g()); //A.f(),A.m=0 //0
System.out.println(b.f()); //A.m=10,b.m=3 //13
System.out.println(b.g()); //A.m=10 //5
}
}

final

final类不能被继承,即不能有子类,不能被重写

对象的上转型对象/下转型对象

1
2
3
4
5
6
A a; 
B b; //class B extends A
b = new B();
a = b; //implicit casting,又称upcasting
//与之对应的是explicit casting(又称downcasting).
//变量a的declared type是class A; 变量a的actual type是class B.
  • 上转型对象不能操作子类新增的成员变量和方法
  • 上转型对象可以访问被子类继承或隐藏的成员变量(即父类中的变量),也可以调用被子类继承的方法(即父类中的方法)或重写的方法(即被子类重写的方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class A 
{
double n;
int m;
void f()
{
System.out.printf("f(): n=%f,m=%d\n",n,m);
}
void g()
{
System.out.printf("n=%f,m=%d\n",n,m);
}
}
class B extends A
{
int n=12;
void g() //重写
{
System.out.printf("g(): n=%d,m=%d\n",n,m);
}
void h()
{
System.out.printf("h(): n=%d,m=%d\n",n,m);
}
}
public class Example5_8
{
public static void main(String args[])
{
A a;
a=new B(); //upcasting
a.f(); //f(): n=1.000000,m=2
a.n=0.618;
a.m=200;
a.f(); //f():n=0.618000,m=200

a.g(); //g():n=12,m=200

B b=(B)a; //downcasting
b.n=555;
b.h(); //h():n=555,m=200
}
}

abstract类(抽象类)

  • abstract类中如果自己提供constructor(构造方法),则用protected修饰,因为是给子类用的(抽象类需要继承)。
  • abstract类不能用new运算符创建对象,必须产生其子类,由子类创建对象。
  • abstract类的类体中有abstract方法,只允许声明,而不允许实现;
    而该类的非abstract子类必须实现abstract方法,即重写(override)父类中的abstract方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public abstract class Geometry 
{
public abstract double getArea();
}
public class Circle extends Geometry
{
double r;
Circle(double r)
{
this.r = r;
}
public double getArea()
{
return(3.14*r*r);
}
}
public class Ladder extends Geometry
{
double a,b,h;
Ladder(double a,double b,double h)
{
this.a = a;
this.b = b;
this.h = h;
}
public double getArea()
{
return((1/2.0)*(a+b)*h);
}
}
public class Pillar
{
Geometry bottom;
double height;
Pillar(Geometry bottom, double height)
{
this.bottom = bottom;
this.height = height;
}
void changeBottom(Geometry bottom)
{
this.bottom = bottom;
}
public double getVolume()
{
return bottom.getArea()*height;
}
}
public class Example5_10
{
public static void main(String args[])
{
Pillar pillar;
Geometry geometry;
geometry = new Ladder(1,1,1); //
System.out.println("Ladder area: " + geometry.getArea()); //Ladder area: 1.0
pillar = new Pillar(geometry,1);
System.out.println("Pillar volume: " + pillar.getVolume()); //Pillar volume: 1.0
geometry = new Circle(1);
System.out.println("Circle area: " + geometry.getArea()); //Circle area: 3.14
pillar.changeBottom(geometry);
System.out.println("Pillar volume: " + pillar.getVolume()); //Pillar volume: 3.14
}
}

interface接口

  • 一个类可以实现多个接口。
  • 接口体中只进行方法的声明,不许提供方法的实现
  • 一个类通过使用关键字implements声明实现一个或多个接口。如果实现多个接口,用逗号隔开接口名。如: class A implements Printable, Addable
  • 在实现接口中的方法时,一定要用public来修饰,不可以省略
  • 接口可被继承,即可以通过关键字extends声明一个接口是另一个接口的子接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
interface Computable 
{
final int MAX=100; // public static final
int f(int x); // public abstract
public abstract int g(int x,int y);
}
class A implements Computable
{
public int f(int x)
{
return x*x;
}
public int g(int x,int y)
{
return x+y;
}
}
class B implements Computable
{
public int f(int x)
{
return x*x*x;
}
public int g(int x,int y)
{
return x*y;
}
}
public class Example5_11
{
public static void main(String args[])
{
A a=new A();
B b=new B();
System.out.println(a.MAX); //100
System.out.println(a.f(5)+" "+a.g(1,2)); //25 3
System.out.println(b.MAX); //100
System.out.println(b.f(5)+" "+b.g(1,2)); //125 2
}
}

接口回调

接口回调

实现某一接口的类创建的对象的引用赋给该接口声明的接口变量,
那么该接口变量就可以调用被类实现的接口中的方法,
当接口变量调用被类实现的接口中的方法时,
就是通知相应的对象调用接口的方法,这一过程称作对象的接口回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface ElectricalAppliances 
{
void showTradeMark();
}
class PC implements ElectricalAppliances
{
public void showTradeMark()
{
System.out.println("PC");
}
}
class TV implements ElectricalAppliances
{
public void showTradeMark()
{
System.out.println("TV");
}
}
public class Example5_12
{
public static void main(String args[])
{
ElectricalAppliances ea;
ea=new TV(); //
ea.showTradeMark(); //TV
ea=new PC(); //
ea.showTradeMark(); //PC
}
}

接口作参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
interface Show 
{
void show();
}
class A implements Show
{
public void show()
{
System.out.println("I love This Game");
}
}
class B implements Show
{
public void show()
{
System.out.println("I love NBA");
}
}
class C
{
public void f(Show s) //
{
s.show();
}
}
public class Example5_13
{
public static void main(String args[])
{
C c = new C();
c.f(new A()); //I love This Game
c.f(new B()); //I love NBA
}
}

抽象类与接口的比较

  • 抽象类中可以有abstract方法、非abstract方法;接口中只可以有abstract方法
    【注:Java 8之后可以有默认方法(用default修饰)和静态方法(用static修饰)】
  • 抽象类中可以有常量、变量; 接口中只可以有常量
  • 如果某个问题需要使用继承才能更好的解决,如子类除了需要实现父类的抽象方法,还需要从父类继承一些变量或继承一些重要的非抽象方法,可以考虑用抽象类
  • 如果某个问题不需要继承,只是需要给出某些重要的抽象方法的实现细节,就可以考虑使用接口

内部类

在一个类中声明另一个类,这样的类称作内部类,而包含内部类的类称为内部类的外嵌类

  • 外嵌类把内部类看作是自己的成员
  • 外嵌类的成员变量在内部类中仍然有效,内部类中的方法可以调用外嵌类中的方法
  • 内部类的类体中不可以声明静态变量(类变量)和静态方法(类方法)
    如果这个内部类被声明为static,则可以有静态变量和静态方法
  • 外嵌类可以用内部类声明对象,作为外嵌类的成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class A 
{
int x=10,y=10;
A2 a2; // 外嵌类用内部类声明对象,作为外嵌类的成员
A()
{
a2 = new A2();
}
void f()
{
System.out.println("A");
a2.speak();
}

// inner class
class A2
{
int z;
void speak()
{
System.out.println("A2");
}
void g()
{
z=x+y; // 外嵌类的成员变量在内部类中仍然有效
System.out.println(z);
f(); // 内部类中的方法可以调用外嵌类中的方法
}
}
}
public class Example5_15
{
public static void main(String args[])
{
A a = new A();
a.f(); //A A2
a.a2.g(); //20 A A2
}
}

匿名类

和类有关的匿名类

匿名类就是一个子类,由于无名可用,所以不可能用匿名类声明对象,
但却可以直接用匿名类创建一个对象。

  • 匿名类可以继承类的方法也可以重写类的方法。
  • 我们使用匿名类时,必然是在某个类中直接用匿名类创建对象,因此匿名类一定是内部类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
abstract class Student 
{
abstract void speak();
}
class Teacher
{
void look(Student stu)
{
stu.speak();
}
}
public class Example5_16
{
public static void main(String args[])
{
Teacher zhang = new Teacher();
zhang.look(
new Student()
{
void speak()
{
System.out.println("这是匿名类中的方法");
}
}
);
}
}

和接口有关的匿名类

如果某个方法的参数是接口类型,那么我们可以使用接口名和类体组合创建一个匿名对象传递给方法的参数,类体必须要实现接口中的全部方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface Show 
{
public void show();
}
class A
{
void f(Show s)
{
s.show();
}
}
public class Example5_17
{
public static void main(String args[])
{
A a=new A();
a.f(
new Show()
{
public void show()
{
System.out.println("这是实现了接口的匿名类");
}
});
}
}

异常类

当程序运行出现异常时,Java运行环境就用异常类Exception的相应子类创建一个异常对象,并等待处理。
try-catch
将可能出现的异常操作放在try-catch语句的try部分,当try部分中的某个语句发生异常后,try部分将立刻结束执行,而转向执行相应的catch部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Example5_18 
{
public static void main(String args[])
{
int n=0,m=0,t=0;
try{
t=3;
m=Integer.parseInt("2");
n=Integer.parseInt("1s"); // Exception
System.out.println("我没有机会输出");
}
//各个catch参数中的异常类都是Exception的某个子类,表明try部分可能发生的异常
catch(Exception e)
{
System.out.println("Exception");
n=1;
}
System.out.println("n=" + n + ",m=" + m + ",t=" + t);
}
}

自定义异常类

继承Exception类,定义自己的异常类,然后规定哪些方法产生这样的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyException extends Exception 
{
String message;
MyException(int n)
{
message = n + ": not a positive number";
}
public String getMessage()
{
return message;
}
}
class A
{
//使用throws关键字声明抛出所要产生的若干个异常
public void f(int n) throws MyException
{
if(n<0)
{
MyException ex = new MyException(n);
throw(ex); // 抛出异常,结束方法f的执行
}
double number = Math.sqrt(n);
System.out.println("square root of "+ n + ": " + number);
}
}
public class Example5_19
{
public static void main(String args[])
{
A a=new A();
try{
a.f(28); //square root of 28: 5.291502622129181
a.f(-8); //
}
catch(MyException e)
{
System.out.println(e.getMessage()); //-8: not a positive number
}
}
}

泛型类

generics

class A<E>;
A是泛型类的名称,E是其中的泛型,
也就是说我们并没有指定E是何种类型的数据,它可以是任何对象或接口,但不能是基本类型数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Chorus<E,F>     //泛型类声明 
{
void makeChorus(E person, F instrument)
{
person.toString();
instrument.toString();
}
}
class Singer
{
public String toString()
{
System.out.println("好一朵美丽的茉莉花");
return "";
}
}
class MusicalInstrument
{
public String toString()
{
System.out.println("|3 35 6116|5 56 5-|");
return "";
}
}
public class Example5_20
{
public static void main(String args[])
{
Chorus<Singer, MusicalInstrument> model = new Chorus<Singer, MusicalInstrument>(); //使用泛型类声明对象
Singer singer = new Singer();
MusicalInstrument piano = new MusicalInstrument();
model.makeChorus(singer, piano);
//好一朵美丽的茉莉花
//|3 35 6116|5 56 5-|
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Cone<E> 
{
double height;
E bottom;
public Cone(E b)
{
bottom = b;
}
public void computeVolume()
{
String s = bottom.toString();
double area = Double.parseDouble(s);
System.out.println("Volume:" + 1.0/3.0*area*height);
}
}
class Circle
{
double area,radius;
Circle(double r)
{
radius = r;
}
public String toString()
{
area = radius*radius*Math.PI;
return "" + area;
}
}
class Rectangle
{
double sideA,sideB,area;
Rectangle(double a,double b)
{
sideA=a;
sideB=b;
}
public String toString()
{
area = sideA*sideB;
return ""+area;
}
}
public class Example5_21
{
public static void main(String args[])
{
Circle circle = new Circle(1);
Cone<Circle> coneCircle = new Cone<Circle>(circle);
coneCircle.height=1;
coneCircle.computeVolume(); //1.047197551196

Rectangle rect = new Rectangle(1,1);
Cone<Rectangle> coneRectangle = new Cone<Rectangle>(rect);
coneRectangle.height = 1;
coneRectangle.computeVolume(); //0.3333333333
}
}

泛型接口

interface Computer<E>;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
interface Computer<E,F>         //
{
void makeChorus(E x, F y);
}
class Chorus<E,F> implements Computer<E,F>
{
public void makeChorus(E x, F y)
{
x.toString();
y.toString();
}
}
class MusicalInstrument
{
public String toString()
{
System.out.println("|5 6 3-|5 17 56|");
return "";
}
}
class Singer
{
public String toString()
{
System.out.println("美丽的草原,我可爱的家乡");
return "";
}
}
public class Example5_22
{
public static void main(String args[ ])
{
Chorus<Singer, MusicalInstrument> model = new Chorus<Singer, MusicalInstrument>();
Singer singer = new Singer();
MusicalInstrument piano = new MusicalInstrument();
model.makeChorus(singer, piano);
}
}