复用类
1,组合
2,继承
组合:
//★ 组合的概念: 类的成员是对象引用
class WaterSource // 水源
{
private String s;
WaterSource() {
s = "松山湖";
}
public String toString() {
return s;
}
}
class Bottledwater // 桶装水
{
private String value = new String("大岭山");
private WaterSource source = new WaterSource();
public String toString() {
return "value=" + value + " source=" + source;
}
public static void main(String[] args) {
Bottledwater sprinkler = new Bottledwater();
System.out.println(sprinkler);
}
}
public class 复用类 {
public static void main(String[] args) {
System.out.println("Hello world!");
Bottledwater fun = new Bottledwater();
fun.main(null);
}
}
★ 解释:toString()方法
**◆每一个对象都有一个toString( )**方法
◆调用时机:当编译器需要一个String**(字符串)**
而你却只有一个对象时,该对象中的toString方法将被调用。
继承:
1**、** 继承的概念和语法
★子类名后面,使用extends****关键字,跟随基类的名字。自动获得基类的所有数据成员以及方法。
(包括main函数)
java
class CarmeloAnthony {
private String s = "CarmeloAnthony";
public void append(String a) {
s += a;
}
public void scrub() {
append(" score()");
}
public String toString() {
return s;
}
public static void main(String[] args) {
CarmeloAnthony x = new CarmeloAnthony();
x.scrub();
System.out.println(x);
}
}
class Detergent extends CarmeloAnthony { // 继承CarmeloAnthony类
public void scrub() // 改写scrub函数
{
append(" He has scored 28289 during NBA");
super.scrub(); //调用了父类的 scrub() 方法
}
public void birth() {
append(" 1984)");
}
public static void main(String[] args) {
Detergent x = new Detergent();
x.scrub();
x.birth();
System.out.println("--------------");
System.out.println(x);
CarmeloAnthony.main(args);
}
}
public class 继承 {
public static void main(String[] args) {
CarmeloAnthony fun = new CarmeloAnthony();
Detergent func = new Detergent();
fun.main(null);
func.main(null);
}
}
初始化基类:
**⑴当创建了一个导出类的对象时,该对象包含了一个(由super指向的)**基类的子对象。
⑵ 无参构造器情形
★Java会自动在子类的构造器中插入基类无参构造器的调用。
◆ 先调用基类构造器,再调用导出类的构造器。基类子对象会在子类对象之前初始化
java
// 类函数认祖归宗哈哈哈哈哈哈哈哈哈!
class Art {
Art() {
System.out.println("Art constructor");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing constructor");
}
}
class Cartoon extends Drawing {
public Cartoon() {
System.out.println("Cartoon constructor");
}
public static void main(String[] args) {
Cartoon x = new Cartoon();
}
}
public class exp1 {
public static void main(String[] args) {
Cartoon x = new Cartoon( );
}
}
子类构造函数中**,如果第一行没有super和this语句,编译器会自动插入super ()。**
⑶****有参构造器情形★必须用关键字super显示地调用基类的****构造器
java
class Game { // P130
Game(int i) { System.out.println("Game constructor"); }
}
class BoardGame extends Game {
BoardGame (int i)
{ super(i); System.out.println("BoardGame constructor"); }
}
class Chess extends BoardGame {
Chess()
{ super(11); System.out.println("Chess constructor"); }
public static void main(String[] args)
{
Chess x = new Chess();
}
}
public class exp2 {
public static void main(String[] args){
Chess fun = new Chess();
fun.main(null);
}
}
子类构造函数第一条语句可以是调用基类构造函数super语句,但并不是先执行子类构造函数体,后执行基类构造函数体。而是先执行基类构造函数体,在执行基类构造函数时使用的是子类构造函数中第一条调用基类构造函数语句中给出的参数!
同时使用组合和继承 :
java
class Plate {
Plate (int i) { System.out.println("Plate constructor"); }
}
class DinnerPlate extends Plate {
DinnerPlate (int i) { super(i); System.out.println("DinnerPlateconstructor");}
}
class Custom {
Custom(int i) { System.out.println("Custom constructor"); }
}
class PlaceSetting extends Custom {
DinnerPlate pl;
PlaceSetting(int i) { super(i + 1); pl = new DinnerPlate(i ); }
public static void main(String[] args) {
PlaceSetting x = new PlaceSetting(9);
}
}
public class exp3 {
public static void main(String[] args){
PlaceSetting fun = new PlaceSetting(10);
}
}
重载:
⑴ 重载的定义
**★**类对自身已有的同名方法的重新定义
⑵ 重载的本质
★ 利用不同的参数列表,包括形式参数的
个数、类型、顺序的不同来区分重载
覆盖:
⑴ 覆盖的定义
**★**子类对继承自父类方法的重新定义
⑵ 覆盖的本质
★ 在子类中定义了具有与父类完全相同的返回值类型、方法名和参数列表(但重新编写了方法体)的方法。
(3)如果在子类中需要调用父类的被覆盖的方法,使用super****关键字来实现
重载与覆盖的区别:
**★**关键:发生的地点不同
**◆**重载发生在同一个类中
**◆**覆盖发生在基类与派生类中
名称屏蔽:
如果Java的基类拥有某个已被多次重载的方法,那么在导出类中重新重载该方法并不会覆盖在基类的任何版本
java
class Homer
{
char doh(
char c) {
System.out.println("doh(char)");
return 'd';
}
float doh(
float c) // 重载函数
{
System.out.println("doh(float)");
return 1.0f;
}
}
class Milhouse {
}
class Bart extends Homer {
void doh(
Milhouse m) // 重载而非覆盖
{
System.out.println("doh(Milhouse)");
}
}
public class exp4 {
public static void main(String[] args){
Bart fun = new Bart();
Milhouse func = new Milhouse();
fun.doh(func);
}
}
**★**分析:
◆从前面的分析可以看出:重载是水平的(发生在类的内部),覆盖是垂直的(发生在基类与****派生类之间)
@override
◆当你试图覆盖该方法但却不小心重载时,编译器会产生一条错误信息@override void doh(Milhouse m)
向上转型:
**◆****概念是:**在使用基类对象的地方,可以使用子类对象来代替
java
class Instrument // 乐器
{
public void play() // 演奏
{
System.out.println("playing....!");
}
static void tune(Instrument i) // 调音
{
i.play();
}
}
class Wind extends Instrument // 长笛
{ // 音乐分为管乐、弦乐、鼓乐等等
public static void main(String[] args) {
Wind flute = new Wind();
Instrument.tune(flute);
}
}
public class exp5 {
public static void main(String[] args){
}
}
★ 分析执行过程
◆对象flute首先向上转型为Instrument**,然后再调用tune****方法**
**◆**称为向上转型的原因
★向上转型:是从一个较专用类型向较通用类型转换,所以总是很安全
final****关键字:
⑴final的基本概念
**★**最终的,无法改变
⑵****常量可以进一步细分为:
★ 编译期常量
**◆**编译期间就能确定它的值
★ 非编译期常量
**◆**直到运行期间才能确定它的值
(a)****、编译期常量
◆ privatefinal int valueOne = 9;
(b)****、非编译期常量
◆ Random rand = new Random**();**
privatefinal int i4 = rand.nextInt(20)****;
2**、final数据**
**★**对于基本类型,final使数值恒定不变
对于对象引用,final使引用恒定不变
final关键字可以与static****关键字一起使用:
**★**按照惯例:
一个既是static又是final的域,将采用****大写字母表示,并且单词之间用下划线分隔;
**★**典例:
◆ public static final intVALUE_THREE = 39;
private final int valueOne = 9**;****//**编译期常量
private static final int VALUE_TWO = 99**;**
// final与static final****的区别在于: 尽管都属于常量,
**//**但后者是属于类这个层次的,将在类被装载的时候
**//**被初始化,而不是每次创建对象时都初始化
public static final int VALUE_THREE = 39**;**
**//**典型的公共常量,可以被包以外访问
final****方法 :
⑴使用final****方法的原因之一
**★**将方法锁定,以防止任何继承类修改它的
含义(防止覆盖)
⑵ 使用final方法的原因之二
★ 效率:编译器将方法的所有调用转换为
内嵌调用
◆ 副作用:使代码膨胀(最新版本的****Java
已不再需要final来进行优化了)
初始化及类的加载 :
每个类的编译代码都存在于它自己独立的文件中,该文件只有在需要使用程序代码时才会被加载
java
class Insect {
private int i = 9;
protected int j;
Insect() {
prt("i = " + i + ", j = " + j);
j = 39;
}
static int x1 = prt("static Insect.x1 initialized");
static int prt(String s) {
System.out.println(s);
return 47;
}
}
class Beetle extends Insect {
private int k = prt("Beetle.k initialized");
Beetle() {
prt("k = " + k);
prt("j = " + j);
}
static int x2 = prt("static Beetle.x2 initialized");
public static void main(String[] args) {
prt("Beetle constructor");
Beetle b = new Beetle();
}
}
public class exp7 {
public static void main(String[] args) {
Beetle func = new Beetle();
func.main(null);
}
}
★ **初始化的整个过程:**两上两下
1**、向上加载到根基类**
◆ 加载器开始启动并找出Beetle****类的编译代码
**◆加载过程中,编译器注意到它有一个基类,于是继续加载基类(如果基类上还有其自身的基类,**继续加载,以此类推)
2**、向下的静态初始化**
◆首先执行根基类中的static初始化,然后是它的****导出类,以此类推
⑴ 因为做的原因在于:导出类的static初始化可能****会依赖于基类成员能否被正确初始化
⑵ 这也再次验证了****初始化的第二基本原则
初始化的顺序是先静态成员,而后是非静态成员**.**
静态成员只在类被载入内存时,初始化一次
3**、再次上溯到根基类**
◆由于子类构造器没有能力对来自基类的数据成员进行初始化,于是只好****上溯到基类
◆如果基类还有更高一层的基类,也将面临同样的问题,于是再次上溯
**◆**这个过程将不断重复,一直上溯到根基类
4**、向下创建对象**
◆ 首先创建基类对象,然后再创建子类对象,如此不断重复,一直下溯到最底层的子类
◆在对象的创建和初始化过程中,仍然按照初始化****的第一基本原则进行
先自动初始化,再指定初始化,最后调用构造****器进行初始化