JAVA编程思想——读书笔记 类再生

类再生

  1. 组合(Composition):在新类创建现有类的的对象
  2. 继承(Inheritance):创建一个新类,将其做为现有类的一个“类型”。我可原样采用现有类的形式,并在其中加入代码,同时不会对现有类产生影响。

1. 组合的语法

对象句柄的初始化

确保所有字段在使用之前得到初始化

  1. 在对象定义时。意味着在调用构造器之前肯定能得到初始化。
  2. 在那个类的构造器中。
  3. 紧靠在要求实际使用的对象之前。这样可减少不必要的开销(如果对象并不需要创建的话)

示例:

package c6;
//:Bath.java
class Soap {
	private String s;
	Soap(){	//Constructed
		System.out.println("Soap()");
		s= new String("Constructed");
	}
	public String toString() { return s;}
}

public class Bath {
	private String
	//Initializing at point of definition(在定义变量(对象)的时候初始化)
	s1=new String("Happy"),
	s2="Happy",
	s3,s4;
	Soap castille;
	int i;
	float toy;
	public Bath() {
		System.out.println("Inside Bath()");
		//在构造方法内初始化
		s3=new String("JOY");
		i=47;
		toy=3.14f;
		castille=new Soap();
	}
	void print() {
		//Delayed initialization(对象使用之前初始化):
		if(s4==null)
			s4=new String("JOY");
		System.out.println("s1 = " + s1);
		System.out.println("s2 = " + s2);
		System.out.println("s3 = " + s3);
		System.out.println("s4 = " + s4);
		System.out.println("i = " + i);
		System.out.println("toy = " + toy);
		System.out.println("castille = " + castille);
	}

	public static void main(String[] args) {
		Bath b=new Bath();
		b.print();
	}

}

2. 继承的语法

“这个新类和那个旧类差不多” “子类是父类的一种类型”
class 子类 extends 父类 {...}
举个栗子:

//:Detergent.java
//Inheritance syntax & properties
class Cleanser{

	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);}
	public static void main(String[] args) {
		Cleanser x= new Cleanser();
		x.dilute(); x.apply(); x.scrub();
		x.print();
		
	}
}
public class Detergent extends Cleanser {
	//Change s method:
	@Override
	public void scrub() {  //重写scrub方法
		append(" Detergent.scrud()");
		super.scrub(); //Call base-class version(调用父类原本的scrub方法)
	}
	//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();
		x.print();
		System.out.println("Test base class:");
		Cleanser.main(args); //调用父类主方法
		
	}
}

3. 初始化父类

编译器强制我们在子类的构造器中首先设置对父类构造器的调用 具体如下:

默认构造器

父类会在子类调用它之前得到正确的初始化,即使子类没有构造器编译器也会其合成一个默认构造器

//:Cartoon.java
//Constructor calls during inheritance
class Art{
	Art(){
		System.out.println("Art constructor");
	}
}
class Drawing extends Art{
	Drawing(){
	    super();  //默认省略
		System.out.println("Drawing constructor");
	}
}
public class Cartoon extends Drawing {
	public Cartoon() {
		System.out.println("Cartoon constructor");
	}
	public static void main(String[] args) {
		Cartoon x=new Cartoon();
	}
}
输出:
Art constructor
Drawing constructor
Cartoon constructor

有参构造器

有参数的构造器,必须明确的编写对父类构造方法的调用代码,可用super关键字实现。

//:Chess.java
//Inheritance,constructors and argument

class Game{
	Game(int i){
		System.out.println("Game constructor"+i);
	}
}

class BoardGame extends Game {
	BoardGame(int i){
	super(i);	
	System.out.println("BoardGame constructor"+i);
}
}
public class Chess extends BoardGame {
	Chess(){
		super(2);
		System.out.println("Chess constructor");
	}
	public static void main(String[] args) {
		Chess x=new Chess();
	}

}

4.合成和继承的结合

编译器强制我对父类初始化,并要求在构造器的开头。但它并不会监控我们是否做了正确的初始化,所一必须特别留意。

//:PlaceSetting.java
//Combining composition & inheritance
class Plate {
	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");
	}
}

//A cultural way of doing something
class Custom{
	Custom(int i){
		System.out.println("Custom constructor");
	}
}
public class PlaceSetting extends Custom{
	Spoon sp;
	Fork frk;
	Knife kn;
	DinnerPlate pl;
	PlaceSetting(int i){
		super(i+1);
		sp = new Spoon(i+2);
		frk= new Fork(i+3);
		kn=new Knife(i+4);
		pl=new DinnerPlate(i+5);
		System.out.println("PlaceSetting constructor");
		}
	public static void main(String[] args) {
		PlaceSetting x=new PlaceSetting(9);
	}
}

输出:

Custom constructor
Utensil constructor
Spoon constructor
Utensil constructor
Fork constructor
Utensil constructor
Knife constructor
Plate constructor
DinnerPlate constructor
PlaceSetting constructor

5.选择组合还是继承

谨慎使用继承

  1. 只有继承在所有方法中最有效时,才能考虑使用它。
  2. 子类是否要向上转型回到父类,若必须向上转型就使用继承。
  3. 时常问自己我真的需要向上转型吗

“属于”关系永继承来表达 “包含”关系有组合来表达
例:

//:Car.java
//Composition with public objects

class Engine{
	public void start() {}
	public void rev() {}
	public void stop() {}
}

class Wheel{
	public void inflate(int psi) {}
}

class Window{
	public void rollup() {}
	public void rolldown() {}
}

class Door{
	public Window window=new Window();
	public void open() {}
	public void close() {}
}

public class Car {
	public Engine engine=new Engine();
	public Wheel[] wheel=new Wheel[4];
	public Door left=new Door(),
			rigth=new Door();
	Car(){
		for(int i=0;i<4;i++)
			wheel[i]=new Wheel();
	}
	public static void main(String[] args) {
		Car car=new Car();
		car.left.window.rolldown();
		car.wheel[0].inflate(72);
	}
	
}

6.向上转型

继承并没有给新类提供方法,而是对子类和父类之间关系的表达;“子类属于父类的一种类型”

  • 向上转型: 通过子类对象(小范围)实例化父类对象(大范围),属于自动转换。
  • 向下转型: 通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换

例:一个父类Instrument(乐器),其中有一个子类 Wind(管乐器),继承意味父类中的所有方法都可以在子类中使用,若Instrument类有个paly()方法,则子类Wind类中也会有这个方法,这样我们可以 肯定的认为wind对象也是Instrument的一种类型

//:Wind.java
//Inheritance & upcasting
import java.util.*;
class Instrument{
	public void paly() {}
	static void tune(Instrument i) {
		//.....
		i.paly();
	}
}
//Wind objects are instrument 
//because they have the same interface:
 class Wind extends Instrument {
	 public static void main(String[] args) {
		Wind flute=new Wind();  //flute:长笛
		//由于Wind类继承自Instrument类,所以Wind对象也是一个Instrument对象
		Instrument.tune(flute); //upcasting
	}
} ///:~

这里我们把一个Win句柄转换为Instrument的行为叫做:向上转型

7.什么是向上转型

graph BT
Wind子类-->Instrument父类

通常类继承图的画法是根部位于最顶端,在逐渐向下扩展。
由于是造型的方向是从子类到父类,箭头由下到上所以叫向上转型,即upcasting

8.final关键字

  1. 被final修饰的类不可以被继承
  2. 被final修饰的方法不可以被重写
  3. **被final修饰的变量不可变

由于应用的场景不一样,final关键的含义也会有一些差异

final修饰Data

final的3种初始化方式:
  1. 定义变量是直接初始
  2. 声明变量后在构造方法中初始化(在每个构造器中都要进行初始化)
  3. 声明变量后在构造代码块中初始化
常量:
  1. 编译期常量,它永远不会改变
  2. 在运行期初始化的一个值,我们不希望它发生变化
final修饰对象:
  1. final会把对象句柄变成“常量”,声明时必须将句柄初始化一个具体的对象
  2. 该句柄不能在指向另一个对象,而对象本身是可以修改的
/:FinalData.java
//The effect of final on fields
class Value{
	int i=1;
}
public class FinalData {
	//can be compile-time constants(可做为编译时的常量):
	final int i1=9;
	static final int I2=99;
	//Typical public constant:
	public static final int I3=39;
	//Cannot be compile-time constants:
	final int i4=(int)(Math.random()*20);
	static final int i5=(int)(Math.random()*20);
	
	Value v1=new Value();
	final Value v2=new Value();
	static final Value v3=new Value(); //
	
	//Arrays:
	final int[] a= {1,2,3,4,5,6};
	
	public void print(String id) {
		System.out.println(
				id + ": " + "i4 = " + i4 + 
				 ", i5 = " + i5);
	}
	public static void main(String[] args) {
		FinalData fd1 = new FinalData();
//!		fd1.i1++;  //Error:无法更改值
		fd1.v2.i++;
//!		fd1.v2=new Value();
		fd1.v1 = new Value(); // oK--not final
		for (int i = 0; i < fd1.a.length; i++)
			fd1.a[i]++; // Object isn't constant
		// ! fd1.v2 = new Value(); // Error: Can't
		// ! fd1.v3 = new Value(); // change handle
		// ! fd1.a = new int[3];
		
		fd1.print("fd1");
		System.out.println("Creating new FinalData");
		FinalData fd2=new FinalData();
		fd1.print("fd1");
		 fd2.print("fd2");
	}
}

空白final(未在声明时初始化)

尽管被声明成 final,但却未得到一个初始值。无论在哪种情况下,空白 final 都必须在实际使用前得到正确的初始化。

空白final具有很大的灵活性
比如:想要一个final变量对每一个对象都不同,并且依然持有保持“不变”的特性。

//:BlankFinal.java
//"Blank" final data members 

class Poppet{}

public class BlankFinal {
	final int i=0;	//Initialized final 
	final int j;	//Blank final 
	final Poppet p;	//Blank final handle
	//Blank finals MUST be initialized
	//in the constructor
	BlankFinal(){
		j=1; //initialized blank final
		p=new Poppet();	//initialized blank handle
		System.out.println(j);
	}
	BlankFinal(int x){
		j=x;	//initialized blank final
		p=new Poppet();	
		System.out.println(j);
	}
	public static void main(String args[]) {
		BlankFinal df=new BlankFinal();
	}
}
final修饰参数
  1. 这意味这在一个方法的内部,我们不能改变参数句柄所指向的东西;
  2. 只能读取final修饰的参数;
//: FinalArguments.java
// Using "final" with method arguments
class Gizmo{
	public void spin() {}
}
public class FinalArguments {
	void with(final Gizmo g) {
//!		g=new Gizmo();  //Illegal--g is final
		g.spin();
	}
	void wihtout(Gizmo g) {
		g=new Gizmo();	//OK--g not final 
		g.spin();
	}
	void f(final int i) {
//		i++;
	}
//	You can only read from a final primitive
	int g(final int i) {
		return i+1;
	}
	public static void main(String[] args) {
		FinalArguments bf=new FinalArguments();
		bf.wihtout(null);
		bf.with(null);
	}
}
final修饰方法
  1. final修饰的方法不能子类被改写或覆盖
  2. final修饰的方法可以提升性能。不过通常,只有在方法的代码量非常少,或者想明确禁止方法被覆盖的时候,才应考虑将一个方法设为final。
  3. private方法都默认是final,也可为private方法添加final,但不会有额外的效果
final修饰类

final类不允许被继承。
当我们不想类做出任何改变,或出于安全方面的理由,不希望类被继承时可用final关键字
注意:

  • final类下的所有方法的是final的,与单个方法声明为final是相同的。
  • 可以为final下的方法添加final关键字,但那样做是没意义的
//Jurssic.java
//Making an entire class final
class SmallBrain {}

final class Dinosaur{
	int i=7;
	int j=1;
	SmallBrain x =new SmallBrain();
	void f() {}
}
//!class Further extends Dinosaur{} //Error:Cannot extend final class 'Dinosaur'
public class Jurassic {
 public static void main(String[] args) {
	Dinosaur n=new Dinosaur();
	n.f();
	n.i++;
	n.j++;
 }
}

9.初始化和类载入

通常,我们可认为除非那个类的一个对象构造完毕,
否则代码不会真的载入。由于 static 方法存在一些细微的歧义,所以也能认为“类代码在首次使用的时候载入”。
首次使用的地方也是static初始化发生的地方。装载的时候,所有static 对象和 static 代码块都会按照本
来的顺序初始化(亦即它们在类定义代码里写入的顺序)。当然,static 数据只会初始化一次。

继承初始化

我们有必要对整个初始化过程有所认识,其中包括继承,对这个过程中发生的事情有一个整体性的概念。请
观察下述代码:

//Beetle.java
//The full process of initialization
class Insect {
    static int x1 = prt("Static Insect.x1 initialized");
    int i = 9;
    int j;

    Insect() {
        prt("i=" + i + ",j=" + j);
        j = 39;
    }

    static int prt(String a) {
        System.out.println(a);
        return 47;
    }
}

public class Beetle extends Insect {
    int k = prt("Beetle.k initialized");

    Beetle() {
        prt("k=" + k);
        prt("j=" + j);
    }
    static int x2=prt("Static Beetle.x2 initialized");

      static int prt(String a) {
          System.out.println(a);
          return 63;
     }

    public static void main(String[] args) {
        prt("Beetle constructor");
        Beetle beetle=new Beetle();

    }
}

out:
Static Insect.x1 initialized
Static Beetle.x2 initialized
Beetle constructor
i=9,j=0
Beetle.k initialized
k=63
j=39
  1. 首先装载外部类(父类) ps如果java程序需要时
  2. 初始化父类Static 再是子类 static 依次类推
  3. 此时必要的类已装载完成,所以可以创建对象
  4. 这个对象中的所有基本数据类型都会设为默认值,而将对象句柄设为null
  5. 随后会调用父类构造方法(一般是自动调用的,也可以用super来自行指定构造函数)
  6. 父类构造方法完成以后,实例变量会按原来的顺序得以初始化
  7. 最后执行构造方法剩余的主体部分
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值