组合和继承
组合:在新类中产生现有类对象;
继承:按照现有类的类型来创建新类。
使用组合和继承的实例:
class Plate{
public Plate(int i) {
System.out.println("Plate constructor");
}
}
class DinnerPlate extends Plate{
DinnerPlate(int i){
super(i);
System.out.println("DinnerPlate constructor");
}
}
class Utensil{
Utensil(int i){
System.out.println("Utensil constructor");
}
}
class Spoon extends Utensil{
Spoon(int i ) {
super(i);
System.out.println("Spoon constructor");
}
}
class Fork extends Utensil{
Fork(int i){
super(i);
System.out.println("Fork constructor");
}
}
class Knife extends Utensil{
Knife(int i){
super(i);
System.out.println("Knife constructor");
}
}
class Custom{
Custom(int i){
System.out.println("Custom constructor");
}
}
public class PlaceSetting extends Custom{
private Spoon spoon;
private Fork fork;
private Knife knife;
private DinnerPlate dinnerPlate;
public PlaceSetting(int i) {
super(i+1);
spoon = new Spoon(i+2);
fork = new Fork(i+3);
knife = new Knife(i+4);
dinnerPlate = new DinnerPlate(i+5);
System.out.println("PlaceSetting constructor");
}
public static void main(String[] args) {
PlaceSetting ps = new PlaceSetting(4);
}
}
Custom constructor
Utensil constructor
Spoon constructor
Utensil constructor
Fork constructor
Utensil constructor
Knife constructor
Plate constructor
DinnerPlate constructor
PlaceSetting constructor
组合和继承都允许在新的类中放置子对象,组合是显示地做,而继承是隐式地做。组合技术通常用于想在新类中使用现有类的功能而并非它的接口。
代理
将一个成员对象置于所要构造的类中(就像组合),但与此同时在新类中暴露了该成员对象的所有方法(就像继承)。例如,太空船需要一个控制模块:
class SpaceControls{
void up(int velocity){}
void down(int velocity){}
void left(int velocity){}
void right(int velocity){}
void forward(int velocity){}
void back(int velocity){}
void turnBoost(){}
}
构造太空船的一种方式是使用继承:
class SpaceShip extends SpaceControls{
private String name;
public SpaceShip(String name) {
this.name = name;
}
public String tosString() {
return name;
}
public static void main(String[] args) {
SpaceShip ship = new SpaceShip("NESA Protextor!");
}
}
但是,SpaceShip并非真正的SpaceShipControls类型,使用代理的方式实现:
public class SpacshipDelegation {
private String name;
private SpaceShipControls controls = new SpaceShipControls();
public SpacshipDelegation(String name){
this.name = name;
}
public void down(int velocity) {
controls.down(velocity);
}
public void back(int velocity) {
controls.back(velocity);
}
public void left(int velocity) {
controls.left(velocity);
}
public void right(int velocity) {
controls.right(velocity);
}
public void up(int velocity) {
controls.up(velocity);
}
public void forward(int velocity) {
controls.forward(velocity);
}
public static void main(String[] args) {
SpacshipDelegation delegation = new SpacshipDelegation("NESA Protector");
delegation.forward(100);
}
}
fianl关键字
三种情况:数据、方法和类
final数据
1 、一个永不改变的编译是变量。
2、一个在运行时被初始化的值,该值不希望被改变。
在java中,这类变量必须是基本数据类型,并且是以关键字final表示,定义的时候必须对其进行赋值。
一个既是static又是final的域只很真哪句一段不能改变的存储空间。
对于基本类型,final使数值恒定不变;而对于对象引用,final使易用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象,然而对象自身却是可以修改的。
示例:
package com.wkx.seven.finall;
import java.io.File;
import java.util.Random;
public class FinalData {
private static Random random = new Random(47);
private String id;
public FinalData(String id) { this.id = id ; }
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
public static final int VALUE_THREE = 39;
private final int i4 = random.nextInt(20);
static final int INT_5 = random.nextInt(20);
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33);
private final int[] a = {1,2,3,4,5,6};
@Override
public String toString() {
return id + ": " + "i4 = " + i4 + ",INT_5 = " + INT_5;
}
public static void main(String[] args) {
FinalData fd1 = new FinalData("fd1");
// fd1.valueOne ++; //valueOne为final类型的,值不可改变
fd1.v2.i++; //v2引用不可变,但指向的对象可以改变
fd1.v1 = new Value(9);
for (int i = 0; i < fd1.a.length; i++) {
fd1.a[i]++; //a的指向不可改变,但数据内容可以改变
}
// fd1.v2 = new Value(10); //v2对象的引用不可以改变
// fd1.VAL_3 = new Value(10); //对象的引用不可以改变
// fd1.a = new int[3]; //引用为final类型的,不可改变
System.out.println(fd1);
System.out.println("creating new finaldata");
FinalData fd2 = new FinalData("fd2");
System.out.println(fd1);
System.out.println(fd2);
}
}
class Value {
int i;
public Value(int i ){this.i = i ; }
}
执行结果:
fd1: i4 = 15,INT_5 = 18
creating new finaldata
fd1: i4 = 15,INT_5 = 18
fd2: i4 = 13,INT_5 = 18
不能因为某数据是final的就认为在编译时可以知道它的值,在运行时使用随机生成的数值来初始化i4和INT_5可以说明这一点,在fd1和fd2中,INT_5的值是不可以通过创建第二个对象加以改变的,是因为它是static的,在装载时已被初始化,而不是每次创建新对象时都初始化。
另外值得注意的是,不能因为v2是final的,就认为无法改变它的值,由于它是一个引用,final意味着无法将v2再次指向另一个新的对象,对数组具有相同的意义,数组只不过是另一种引用。
1、空白final:被声明为final但又未给定初值的域,无论什么情况,要确保空白final在使用前必须初始化,这样空白final可以做到根据对象而有所不同,我们只需要在不同的构造器中对空白final进行不同的赋值即可。
2、final参数:java允许在参数列表中以声明的方式将参数指明为final,意味着无法在方法中更改参数引用所指向的对象:
package com.wkx.seven.finall;
public class FinalArgements {
void with (final Gizmo g){
// g= new Gizmo(); 错误,无法改变final引用的指向
}
void without(Gizmo g){
g= new Gizmo();
g.spin();
}
void f(final int i){
// i++; 错误,无法改变final的值
}
int g(final int i){
return i+1;
}
public static void main(String[] args) {
FinalArgements faArgements = new FinalArgements();
faArgements.with(null);
faArgements.without(null);
}
}
class Gizmo{
public void spin() {
}
}
final方法
为什么使用final方法?
1、将方法锁定,以防任何继承类修改它的含义,确保在继承中使方法行为保持不变,并不会被覆盖;
2、效率。
类中所有的private方法都隐式地指定为final的,但值得注意的是在基类和继承类中可以存在名称相同的方法,但也仅是名称相同而已,并不是类继承中所提到的“覆盖”。
final类
定义为final的类,是不允许被其他类继承的。
public class Jurassic {
public static void main(String[] args) {
}
}
class SmallBrain{}
final class Dinosar{
int i = 7;
int j = 9;
SmallBrain xBrain = new SmallBrain();
void f() {}
}
//Dinosar被定义为final类型的,无法被继承
// class Futher extends Dinosar {}
注意:final类的域可以根据个人的意愿选择是或者不是final,由于final禁止继承,所以final类中所有的方法都隐式为final的。
类的初始化与加载
类的代码在初次使用时才加载,在访问static域和方法时,也会发生加载。另外类的构建器也是static方法,尽管static关键字并没有显式的写出来。
public class Beetle extends Insect{
private int k = printInit("Beetle.k init!");
public Beetle(){
System.out.println("k = " + k);
System.out.println("j = " + j);
}
private static int x2 = printInit("static Beetle.x2 init!");
public static void main(String[] args) {
System.out.println("Beetle constructor!");
Beetle beetle = new Beetle();
}
}
class Insect{
private int i = 9;
protected int j;
public Insect() {
System.out.println("i = " + i +",j= " + j);
j = 39;
}
private static int x1 = printInit("static Insect.x1 init!");
static int printInit(String s){
System.out.println(s);
return 47;
}
}
执行结果:
static Insect.x1 init!
static Beetle.x2 init!
Beetle constructor!
i = 9,j= 0
Beetle.k init!
k = 47
j = 39
说明:在Beetle上运行java时,第一件事访问Beetle.main()方法,于是加载器开始启动并找出Beetle类的编译代码,(在名为Beetle.class文件中)。在加载过程中,发现它有一个基类,于是继续进行基类加载,此时不管是否产生一个该基类的对象,都要进行基类加载。
如果该基类还有基类,那么第二个基类也会被加载,如此类推。接下来,基类中的static初始化会被执行,然后是下一个继承类。这种方法是因为导出类的static初始化可能会依赖基类成员是否被正确初始化。
当必要类加载完毕,对象就可以被创建了。首先对象中的所有基本类型会被设置为默认值,对象引用被设置为null,然后基类构造器被调用,自动调用或使用super来指定。基类构造器先执行,导出类构造器后执行。
顺序:基类static变量->导出类static变量->基类构造器->导出类构造器