java基础-继承
女神镇楼
1、类的继承
继承是面向对象中的一个非常重要的概念,是的整个程序具有一定的弹性,在程序中,复用一些已经定义的完善的类,不仅可以减少软件的开发周期,还可以提高软件的可维护性和可拓展性。
继承的基本思想是基于某个父类的拓展,制造出一个新的子类,子类可继承父类的属性和方法,也可以增加原父类不具备的属性和方法,或者重写父类中的某个方法。比如平行四边形继承了四边形类,这个时候,平行四边形类将所有四边形类中的属性和方法保留下来,并拓展了新的属于自己的属性和方法。
代码示例---父类:
public class Test {
public Test(){ //构造方法
//SomeSentence
}
protected void doSomething(){
// 成员方法
}
protected Test dolt(){
// 方法返回值类型为Test类型
return new Test();
}
}
代码示例---子类:
public class Test2 extends Test {
public Test2(){
//构造方法
super(); // 调用父类的构造方法
super.doSomething(); //调用父类的成员方法
}
public void doSomethingNeW(){
//新增方法
}
public void doSomeThing(){
//重写父类方法
}
protected Test2 dolt(){
//重写父类方法,返回值类型为Test2类型
return new Test2();
}
}
实例说明:
实例中,Test类是Test2类的父类,Test2类是Test类的子类,在子类中连同初始化父类的构造方法来完成子类的初始化操作,即可以在子类的构造方法中使用super()语句调用父类的构造方法,也可以在子类中通过super关键字调用父类的成员方法。但是在子类中没有权限调用父类中使用private修饰的方法,只可以调用父类中修饰为public或者protected的成员方法。
继承不止可以拓展父类的功能,还可以对父类的方法进行重写,重写也可以成为覆盖,就是在子类中,将父类的成员方法的名称保留,重写成员方法实现的内容,可更改成员方法的储存权限,或者修改成员方法的返回值类型,还可以将权限修饰符修改为public。
继承中还有一种特殊的重写方式,子类与父类的成员方法的返回值、方法名称、参数类型及个数完全相同,唯一不同的是方法实现内容,这种特殊的重写方式称为重构。
当重写父类方法时,修改方法的修饰权限只能从小的范围到大的范围改变,例如,父类中的
doSomething0方法的修饰权限为protected, 继承后子类中的方法doSomething0的修饰权限只能修改
为public,不能修改为private。
在java中,一切都是一对象的形式进行处理,在继承机制中,创建一个子类对象,将包含一个父类子对象,这个对象与父类创建的对象是一样的,两者的区别就在于,后者来自于外部,而前者来自子类对象内部。当实例化子类对象时,父类对象也相应被实例化,也就是说,在实例化子类对象时,java编译器会在子类的构造方法中自动调用父类的无参构造方法。
代码示例:
public class Parent {
Parent(){
System.out.println("调用父类的Parent()方法");
}
}
public class Subparent extends Parent { //继承Parent
Subparent(){
System.out.println("调用子类Subparent的构造方法");
}
}
public class Subbroutine extends Subparent { // 继承 Subparent
Subbroutine(){
System.out.println("调用子类Subbroutine的方法。");
}
public static void main(String[] args) {
Subbroutine s = new Subbroutine();
}
}
运行结果:
运行结果说明:
子类Subroutine的主方法中只调用子类的构造方法实例化子类对象,并且在子类的构造方法中没有调用父类构造方法的任何语句,但是在实例化子类对象时,他会相应的调用父类的构造方法,调用构造方法的顺序是:顶级父类==上一级父类==子类,也就是实例化子类对象时首先实例化父类对象,再实例化子类对象。在子类的构造方法访问父类的构造方法之前,已经完成了父类的实例化操作。
说明:
在实例化子类对象时,父类无参构造方法将会被自动调用,但是有参构造方法不能被自动调用,只能借用super关键字显式的调用父类方法。
如果使用finalize()方法对对象进行清理,需要确保子类的finalize()方法最后一个动作是调用父类的finalize()方法,用来保证垃圾回收对象所占用的内存时,对象所有部分都能被正常终止。
极客时间视频课总结:
1、继承的语法就是在类名后面使用extends 加 要继承的类名
被继承的类叫做父类(Parent Class),比如本例中的MerchandiseV2。
继承者叫做子类(Sub Class),比如本例中的PhoneExtendsMerchandise。
Java中只允许一个类有一个直接的父类(Parent Class),即所谓的单继承
没错,别的类也可以继承子类,比如可以有一个HuaweiPhone继承PhoneExtendsMerchandise
这时候,HuaweiPhone就是PhoneExtendsMerchandise的子类了。
子类继承了父类什么呢?所有的属性和方法。
但是子类并不能访问父类的private的成员(包括方法和属性)。
public class PhoneExtendsMerchandise extends MerchandiseV2 {
...
}
public class HuaweiPhone extends PhoneExtendsMerchandise {
public HuaweiPhone(String name, String id, int count, double soldPrice, double purchasePrice, double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os) {
super(name, id, count, soldPrice, purchasePrice, screenSize, cpuHZ, memoryG, storageG, brand, os);
}
}
2、super与父类沟通的桥梁
public double buy(int count) {
if (count > MAX_BUY_ONE_ORDER) {
System.out.println("购买失败,手机一次最多只能买" + MAX_BUY_ONE_ORDER + "个");
return -2;
}
return super.buy(count);
}
public void useSuper() {
super.describe();
super.buy(66);
System.out.println("父类里的count属性:" + super.count);
}
public Phone(
String name, String id, int count, double soldPrice, double purchasePrice,
double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os
) {
// >> TODO 可以认为,创建子类对象的时候,也就同时创建了一个隐藏的父类对象
this.screenSize = screenSize;
this.cpuHZ = cpuHZ;
this.memoryG = memoryG;
this.storageG = storageG;
this.brand = brand;
this.os = os;
// >> TODO 所以,才能够setName,对name属性进行操作。
this.setName(name);
this.setId(id);
this.setCount(count);
this.setSoldPrice(soldPrice);
this.setPurchasePrice(purchasePrice);
}
public Phone(
String name, String id, int count, double soldPrice, double purchasePrice,
double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os
) {
super(name, id, count, soldPrice * 1.2, purchasePrice);
init(screenSize, cpuHZ, memoryG, storageG, brand, os);
}
MerchandiseV2 m = ph;
MerchandiseV2 m2 = new Phone(
"手机002", "Phone002", 100, 1999, 999,
4.5, 3.5, 4, 128, "索尼", "安卓"
);
Phone notDoable = new MerchandiseV2();
因为子类继承了父类的方法和属性,所以父类的对象能做到的,子类的对象肯定能做到
换句话说,我们可以在子类的对象上,执行父类的方法
当父类的引用指向子类的实例(或者父类的实例),只能通过父类的引用,像父类一样操作子类的对象
也就是说"名"的类型,决定了能执行哪些操作
ph和m都指向同一个对象,通过ph可以调用getBrand方法
Phone ph = new Phone(
"手机001", "Phone001", 100, 1999, 999,
4.5, 3.5, 4, 128, "索尼", "安卓"
);
MerchandiseV2 m = ph;
因为ph的类型是Phone,Phone里定义了getBrand方法
ph.getBrand();
ph和m都指向同一个对象,但是通过m就不可以调用getBrand方法
因为m的类型是MerchandiseV2,MerchandiseV2里没有你定义getBrand方法
m.getBrand();
如果确定一个父类的引用指向的对象,实际上就是一个子类的对象(或者子类的子类的对象),可以强制类型转换
Phone aPhone = (Phone) m2;
完整代码(父类):
package com.geekbang.supermarket;
public class MerchandiseV2 {
public String name;
public String id;
public int count;
public double soldPrice;
public double purchasePrice;
public MerchandiseV2(String name, String id, int count, double soldPrice, double purchasePrice) {
this.name = name;
this.id = id;
this.count = count;
this.soldPrice = soldPrice;
this.purchasePrice = purchasePrice;
}
public MerchandiseV2(String name, String id, int count, double soldPrice) {
// double purPrice = soldPrice * 0.8;
// this(name, id, count, soldPrice, purchasePrice);
this(name, id, count, soldPrice, soldPrice * 0.8);
// double purPrice = soldPrice * 0.8;
}
public MerchandiseV2() {
this("无名", "000", 0, 1, 1.1);
}
public void describe() {
System.out.println("商品名字叫做" + name + ",id是" + id + "。 商品售价是" + soldPrice
+ "。商品进价是" + purchasePrice + "。商品库存量是" + count +
"。销售一个的毛利润是" + calculateProfit());
}
public double calculateProfit() {
double profit = soldPrice - purchasePrice;
// if(profit <= 0){
// return 0;
// }
return profit;
}
public double buy(int count) {
if (this.count < count) {
System.out.println("购买失败,库存不够");
return -1;
}
this.count -= count;
double cost = count * soldPrice;
System.out.println("购买成功,花费为" + cost);
return cost;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public double getSoldPrice() {
return soldPrice;
}
public void setSoldPrice(double soldPrice) {
this.soldPrice = soldPrice;
}
public double getPurchasePrice() {
return purchasePrice;
}
public void setPurchasePrice(double purchasePrice) {
this.purchasePrice = purchasePrice;
}
}
完整代码(phone子类):
package com.geekbang.supermarket;
public class Phone extends MerchandiseV2 {
// 给Phone增加新的属性和方法
private double screenSize;
private double cpuHZ;
private int memoryG;
private int storageG;
private String brand;
private String os;
private static int MAX_BUY_ONE_ORDER = 5;
public Phone(
String name, String id, int count, double soldPrice, double purchasePrice,
double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os
) {
super(name, id, count, soldPrice * 1.2, purchasePrice);
this.screenSize = screenSize;
this.cpuHZ = cpuHZ;
this.memoryG = memoryG;
this.storageG = storageG;
this.brand = brand;
this.os = os;
}
public double buy(int count) {
if (count > MAX_BUY_ONE_ORDER) {
System.out.println("购买失败,手机一次最多只能买" + MAX_BUY_ONE_ORDER + "个");
return -2;
}
return super.buy(count);
}
public String getName() {
return this.brand + ":" + this.os + ":" + super.getName();
}
public void describe() {
System.out.println("此手机商品属性如下");
super.describe();
System.out.println("手机厂商为" + brand + ";系统为" + os + ";硬件配置如下:\n" +
"屏幕:" + screenSize + "寸\n" +
"cpu主频" + cpuHZ + " GHz\n" +
"内存" + memoryG + "Gb\n" +
"存储空间" + storageG + "Gb");
}
public boolean meetCondition() {
return true;
}
public double getScreenSize() {
return screenSize;
}
public void setScreenSize(double screenSize) {
this.screenSize = screenSize;
}
public double getCpuHZ() {
return cpuHZ;
}
public void setCpuHZ(double cpuHZ) {
this.cpuHZ = cpuHZ;
}
public int getMemoryG() {
return memoryG;
}
public void setMemoryG(int memoryG) {
this.memoryG = memoryG;
}
public int getStorageG() {
return storageG;
}
public void setStorageG(int storageG) {
this.storageG = storageG;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
}
完整代码(ShellColorChangePhone孙子类):
package com.geekbang.supermarket;
public class ShellColorChangePhone extends Phone {
private boolean enableShellColorChange;
public ShellColorChangePhone(String name, String id, int count, double soldPrice, double purchasePrice,
double screenSize, double cpuHZ, int memoryG, int storageG, String brand, String os) {
super(name, id, count, soldPrice, purchasePrice, screenSize, cpuHZ, memoryG, storageG, brand, os);
enableShellColorChange = false;
}
public boolean isEnableShellColorChange() {
return enableShellColorChange;
}
public void setEnableShellColorChange(boolean enableShellColorChange) {
this.enableShellColorChange = enableShellColorChange;
}
@Override
public void describe() {
super.describe();
System.out.println("壳色随着屏幕色变的功能开启状态:" + enableShellColorChange);
}
@Override
public double calculateProfit() {
// TODO 厂家提供10个点的返点
return super.calculateProfit() + super.getPurchasePrice() * 0.1;
}
}
完整代码(使用类):
package com.geekbang;
import com.geekbang.supermarket.MerchandiseV2;
import com.geekbang.supermarket.Phone;
import com.geekbang.supermarket.ShellColorChangePhone;
public class ReferenceAssign {
public static void main(String[] args) {
Phone ph = new Phone(
"手机001", "Phone001", 100, 1999, 999,
4.5, 3.5, 4, 128, "索尼", "安卓"
);
// >> TODO 可以用子类的引用给父类的引用赋值,也就是说,父类的引用可以指向子类的对象
MerchandiseV2 m = ph;
MerchandiseV2 m2 = new Phone(
"手机002", "Phone002", 100, 1999, 999,
4.5, 3.5, 4, 128, "索尼", "安卓"
);
// >> TODO 但是反之则不行,不能让子类的引用指向父类的对象。因为父类并没有子类的属性和方法呀
// Phone notDoable = new MerchandiseV2();
// >> TODO 重点
// >> TODO 因为子类继承了父类的方法和属性,所以父类的对象能做到的,子类的对象肯定能做到
// TODO 换句话说,我们可以在子类的对象上,执行父类的方法
// >> TODO 当父类的引用指向子类的实例(或者父类的实例),只能通过父类的引用,像父类一样操作子类的对象
// TODO 也就是说"名"的类型,决定了能执行哪些操作
// >> TODO ph和m都指向同一个对象,通过ph可以调用getBrand方法
// TODO 因为ph的类型是Phone,Phone里定义了getBrand方法
ph.getBrand();
// >> TODO ph和m都指向同一个对象,但是通过m就不可以调用getBrand方法
// TODO 因为m的类型是MerchandiseV2,MerchandiseV2里没有你定义getBrand方法
// m.getBrand();
// TODO 如果确定一个父类的引用指向的对象,实际上就是一个子类的对象(或者子类的子类的对象),可以强制类型转换
Phone aPhone = (Phone) m2;
// MerchandiseV2是Phone的父类,Phone是shellColorChangePhone的父类
ShellColorChangePhone shellColorChangePhone = new ShellColorChangePhone(
"手机002", "Phone002", 100, 1999, 999,
4.5, 3.5, 4, 128, "索尼", "安卓"
);
// TODO 父类的引用,可以指向子类的对象,即可以用子类(以及子类的子类)的引用给父类的引用赋值
MerchandiseV2 ccm = shellColorChangePhone;
// TODO 父类的引用,可以指向子类的对象。
// TODO 确定MerchandiseV2的引用ccm是指向的是Phone或者Phone的子类对象,那么可以强制类型转换
Phone ccp = (Phone) ccm;
// TODO 确定MerchandiseV2的引用ccm是指向的是ShellColorChangePhone或者ShellColorChangePhone的子类对象
// TODO 那么可以强制类型转换
ShellColorChangePhone scp = (ShellColorChangePhone) ccm;
// TODO 会出错,因为m2指向的是一个Phone类型的对象,不是ShellColorChangePhone的对象
ShellColorChangePhone notCCP = (ShellColorChangePhone) m2;
}
}