组合语法
在新类中产生现有类的对象
继承语法
按照现有类的类型来创建新类
继承会自动得到基类中所有域和方法
在继承的过程中,并不一定非得使用父类的方法,也可以在子类中添加新方法
对于子类对象来说,除了调用子类的可用方法外,还可以调用父类中所有可用方法
class B extends A {}
重写的方法中调用对应父类中的方法使用super
当创建了一个导出类的对象时,该对象包含了一个基类的子对象
该子对象与用基类直接创建的对象是一样的
在导出类构造器中调用基类构造器来进行初始化
JAVA会自动在导出类的构造器中插入对基类构造器的调用
1,总是被调用
2,在导出类构造器之前被调用
没有默认的基类构造器,或想调用带参数的基类构造器,就必须用关键字super显式的 编写调用基类构造器的语句,并配以适当的参数列表
调用基类构造器必须是在导出类构造器中要做的第一件事
class Base {
public Base(int num) {}
}
class Child extends Base {
public Child() {
super(1);
}
}
代理
我们使用代理时,可以拥有更多的控制力,因为我们可以选择只提供在成员对象中的方法的某个子集
// PackageManager 就是 PackageManagerService的代理
// 该例子使用了组合和继承
class PackageManagerService {
public void installPackage() {/* ... */}
public void deletePackage() {/* ... */}
protected void getPackageInfo() {/* ... */};
}
abstract class PackageManager {
public abstract void installPackage();
public abstract void deletePackage();
}
class ApplicationPackageManager extends PackageManager {
PackageManagerService mPm;
public ApplicationPackageManager() {
mPm = new PackageManagerService();
}
@Override
public void installPackage() {
mPm.installPackage();
}
@Override
public void deletePackage() {
mPm.deletePackage();
}
}
结合使用组合和继承
扩展类可以使用与基类完全相同的特征签名及返回类型来覆盖具有相同名称的方法
@Override可以防止不想重载时意外进行了重载
class Base {
void print(int i) {}
void print(String s) {}
}
class Child extends Base {
void print(Double d) {}
@Override
void print(int i) {/* .. */ }
}
在组合和继承之间选择
组合和继承都允许在新的类中放置子对象,组合是显式地这么做,继承是隐式地做
组合:通常用于想在新类中使用现有类的功能而非它的接口,即在新类中嵌入某个对象,让其实现所需的功能。(has-a/有一个)的关系
继承:使用某个现有类,并开发一个它的特殊版本。(is-a/是一个)的关系。如果需要向上转型,那么继承是必须的
protected关键字
就类用户而言,这是private的,但对于任何继承于此类的导出类或其他任何位于同一包内的类来说,它却是可访问的
protected也提供了包访问权限
尽管可以创建protected域,但是最好的方法还是将域保持为private,然后通过protected方法来控制类的继承者的访问权限
向上转型
能够向基类发送的所有信息同样也可以向导出类发送
向上转型是从一个较专用类型向较通用类型转换,所以总是很安全的
class Instrument {
protected void play() {
System.out.print("Instrument play");
}
public static void tune(Instrument instrument) {
instrument.play();
}
}
public class Test extends Instrument {
@Override
protected void play() {
System.out.print("Test play");
}
public static void main(String[] args) {
Instrument.tune(new Test());
}
}
//输出Test play
final关键字
1,一个永不改变的编译时常量
2,一个在运行时被初始化的值,而不希望它被改变
一个既是static又是final的域只占据一段不能改变的存储空间
对于基本类型,final使数值恒定不便
对于对象引用,final使引用恒定不便,然而对象其自身却是可以被修改的
被声明为final但又未给定初值的域
class Value {
int v;
public Value(int i) {
v = i;
}
}
public class Test {
public static final int NUM = new Random().nextInt(10); // 编译时常量
final int num = new Random().nextInt(10); // 运行时常量,编译时并不知道值
static final Value mValue = new Value(1); // 对象引用不能改变,但对象自身可改变
final int t; // 空白final
public Test() {
t = 1;
}
Test(int i) {
t = i;
}
public static void main(String[] args) {
System.out.println(NUM + " " + new Test().num + " " + mValue.v + " " + new Test(1).t);
mValue.v = 2;
System.out.println(NUM + " " + new Test().num + " " + mValue.v + " " + new Test(2).t);
// NUM始终不变
}
}
将参数声明为final,意味着无法在方法中更改参数引用所指向的对象
主要用来向匿名内部类传递数据
class Value {
int v = 10;
}
public class Test {
void print(final int i) {
//i++; 不可修改,只能使用
System.out.println(i + 1);
}
void print(final Value value) {
//value = new Value(); 不可修改,只能使用
System.out.println(value.v);
}
public static void main(String[] args) {
new Test().print(1);
new Test().print(new Value());
}
}
final方法不能被重写,用于方法锁定,以防止任何继承类修改它的含义
作用:确保在继承中使方法行为保持不变,并且不被覆盖
类中所有的private方法都隐式地指定为final的
由于无法取用private方法,所有也就无法覆盖它
"覆盖"只有在某方法是基类的接口的一部分时才会出现,如果方法为private,它就不是接口的一部分
final类不能被继承
final类中所有的方法都隐式指定为final的
初始化及类的加载
类的代码在初始使用时才加载(创建类的第一个对象或访问static域或static方法)
static的东西只会被初始化一次
1,加载基类->加载导出类
2,基类static初始化,static域->static代码块
3,导出类static初始化,static域->static代码块
4,设置所有基本类型为默认值,对象引用被设置为null
5,基类构造器被调用,显式赋值->构造代码块->构造函数
6,导出类构造器被调用,显式赋值->构造代码块->构造函数
class Base {
int i1 = getI1();
static int i2 = getI2();
int i3;
static int i4;
int i5;
int getI1() {
System.out.print("i1 ");
return 1;
}
static int getI2() {
System.out.print("i2 ");
return 2;
}
static {
i4 = 4;
System.out.print("i4 ");
}
{
i3 = 3;
System.out.print("i3 ");
}
public Base() {
i5 = 5;
System.out.print("i5 ");
}
}
public class Test extends Base {
int j1 = getJ1();
static int j2 = getJ2();
int j3;
static int j4;
int j5;
int getJ1() {
System.out.print("j1 ");
return 1;
}
static int getJ2() {
System.out.print("j2 ");
return 2;
}
static {
j4 = 4;
System.out.print("j4 ");
}
{
j3 = 3;
System.out.print("j3 ");
}
public Test() {
j5 = 5;
System.out.print("j5 ");
}
public static void main(String[] args) {
System.out.print("main ");
new Test();
}
}
//输出为:i2 i4 j2 j4 main i1 i3 i5 j1 j3 j5