一、继承深入理解
1.1 继承中变量的访问
①如果子类中没有与父类相同的属性,那么在使用时使用父类的属性
public class FatherTest{
public static void main(String[] args) {
Son s=new Son();
s.name="使用父类name属性进行数据存储";
System.out.println(s.name);
}
}
class Father {
public String name;
}
class Son extends Father{
}
②如果子类中有与父类相同的属性(在继承中一般不这样书写)
public class FatherTest{
public static void main(String[] args) {
Son s=new Son();
s.name="使用自己的属性进行存储";
}
}
class Father {
public String name;
}
class Son extends Father{
public String name;
}
在子类继承父类后,如果子类拥有相同的属性那么在使用时,默认使用子类的属性,方法也是一样
1.2 方法的重写
在继承过程中,子类继承父类方法,父类方法不满足子类需求,子类重写父类方法方法体,实现方法的重载
方法重载要求:修饰符访问范围大于或等于父类 返回值不能修改 方法名不能修改 参数列表不能修改 方法体根据需求修改
public class FatherTest{
public static void main(String[] args) {
Son s=new Son();
s.eat();
}
}
class Father {
public String name;
public void eat(){
System.out.println("爸爸会吃饭");
}
}
class Son extends Father{
public String name;
//子类重写父类方法 修改方法体 执行默认执行子类方法
@Override
public void eat(){
System.out.println("儿子会吃饭");
}
}
@Override
重写注解,无特殊功能,用于标识重写的方法,被该注解标识的方法必须满足方法重写的语法规则
面试题
override与overload的区别
overload:方法重载
在一个类,方法名相同参数列表不同称发生了方法的重载,或多个方法互为重载
参数列表不同:个数不同、类型不同、顺序不同
override:方法重写
发生继承过程中,子类重写继承父类方法,称之为重写
发生位置不同:重载一般发生在同一类中,重写发生在继承中
书写语法不同:重载只需要方法名与参数列表就可以发生,重写返回值、方法名、参数列表必须与父类相同
1.3 继承数据存储的原理
子类在创建时,只会根据当前子类拥有的属性开辟内存空间存储对应数据,在程序执行中在创建子类时首先都会创建一个与之唯一对应的父类对象(子类构造方法第一行调用父类构造方法),在进行存储时,如果存储子类没有的属性,实际上是存储到了与之相对应的父类对象中
实际存储如下
public class FatherTest{
public static void main(String[] args) {
Son s=new Son();
s.name="儿子";
s.eat();
}
}
class Father {
public String name;
public Father(){
}
public void eat(){
System.out.println("爸爸会吃饭");
}
}
class Son extends Father{
//子类重写父类方法 修改方法体 执行默认执行子类方法
@Override
public void eat() {
super.eat();
System.out.println(super.name+"会吃饭"+this.name);
}
}
二、super关键字
所有的类都有父类,如果书写了关键字继承父类 那么指定的父类就是其父类,如果一个类没有书写父类那么默认继承Object(基类 超类)
super代表父类
在进行变量与方法执行时,如果子类拥有与父类相同的变量与方法,那么默认会调用子类的,如果想获取执行父类的变量与方法需要使用super关键字进行标识
this.成员变量 ‐‐ 本类的
super.成员变量 ‐‐ 父类的
this.成员方法名() ‐‐ 本类的
super.成员方法名() ‐‐ 父类的
在子类中,子类构造方法会默认调用父类无参构造方法,创建父类对象,并且子类构造方法调用父类构造方法的语句必须书写在子类构造方法的第一行
面试题
this与super关键字的区别
this:当前类中方法声明的形参与成员变量相同时,使用this关键字标识成员变量
super:在继承过程中子类属性方法与父类属性方法相同时,使用super关键字标识父类属性与方法
this与super都可以调用构造方法
this(...) ‐‐ 本类的构造方法
super(...) ‐‐ 父类的构造方法
三、抽象类
本质还是一个类,在类的书写过程中如果存在由于类的定义无法实现的方法时,将方法定义为抽象方法,同时将类定义为抽象类。
3.1 概念
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。
3.2 书写
使用abstract关键字对类与方法进行修饰,被其修饰的类称之为抽象类,方法称之为抽象方法
一个类如果有抽象方法,那么这个类一定是抽象类,如果一个类是抽象类不一定拥有抽象方法
//图形类
public abstract class Figure {
//计算周长
public abstract void zc();
//计算面积
public abstract void mj();
}
可以理解为,当书写的类过于抽象,并且其中的方法方法体无法实现时,使用抽象类
3.3 抽象的使用
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父类所有的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
-
代码演示
public abstract class Animal { public abstract void run(); } public class Cat extends Animal { public void run (){ System.out.println("小猫在墙头走~~~"); } } public class CatTest { public static void main(String[] args) { // 创建子类对象 Cat c = new Cat(); // 调用run方法 c.run(); } }
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法。
3.4 抽象的注意事项
-
抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
-
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
-
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设
计。
-
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有
意义。
练习
1.使用方法重写完成如下练习
书写员工类Staff,拥有属性生日月份 工资 ,拥有方法获取当月工资,根据传入的月份计算工资(在生日当月工资额外100),书写FiedStaff固定工资员工类,继承员工类,书写hourStaff小时工类 拥有属性 工作小时数以及 每小时工资数 继承员工类
乞丐类 无底薪 计算工资方法为0~10000随机
销售人员,无底薪 工资为销售额*提成
有底薪的销售人员 固定底薪+销售额*提成
所有员工类重写计算工资方法,实现调用对应的计算工资方法 返回对应员工工资
public class StaffTest {
public static void main(String[] args) {
FiedStaff fs = new FiedStaff(5, 4000);
HourStaff hs = new HourStaff(500, 100, 9);
//乞丐类 无底薪 计算工资方法为0~10000随机
Beggar b = new Beggar();
b.getMoney();
//销售人员,无底薪 工资为销售额1000*提成 5
SalesMan s1 = new SalesMan(1000, 5);
s1.getSalary();
//有底薪的销售人员 固定底薪2300+销售额1000*提成5
SalesOtherMan s2 = new SalesOtherMan(1000, 5);
s2.getSalary();
}
}
// 书写员工类Staff,
// 拥有属性生日月份 工资 ,
// 拥有方法获取当月工资,
// 根据传入的月份计算工资(在生日当月工资额外100),
// 书写FiedStaff固定工资员工类,继承员工类,
// 书写hourStaff小时工类 拥有属性 工作小时数以及 每小时工资数 继承员工类
// 所有员工类重写计算工资方法,实现调用对应的计算工资方法 返回对应员工工资
// 员工类
class Staff {
public int birthdayMonth;// 生日月份
public double money;// 工资
public Staff(int birthdayMonth) {
this.birthdayMonth = birthdayMonth;
}
public Staff(int birthdayMonth, double money) {
this.birthdayMonth = birthdayMonth;
this.money = money;
}
public double getMoney(int month) {
if (birthdayMonth == month) {
money += 100;
}
return money;
}
}
// 固定工资员工类
class FiedStaff extends Staff {
// 书写构造方法
public FiedStaff(int birthdayMonth, double money) {
// super();
// super.money=money;
super(birthdayMonth, money);
}
@Override
public double getMoney(int month) {
super.getMoney(month);
return money;
}
}
// 小時工
class HourStaff extends Staff {
private int hour;// 工作小时数
private double hourMoney;// 每小时工资
public HourStaff(int hour, double hourMoney, int birthdayMonth) {
super(birthdayMonth);
this.hour = hour;
this.hourMoney = hourMoney;
}
@Override
public double getMoney(int month) {
super.getMoney(month);
money += hour * hourMoney;
return money;
}
}
// 乞丐类 无底薪 计算工资方法为0~10000随机
class Beggar {
public int money;
public void getMoney() {
money = (int) (Math.random() * 10001);
System.out.println("乞丐的每月能获得的钱是:" + money);
}
}
// 销售人员,无底薪 工资为销售额*提成
class SalesMan {
public int salary;//工资
public int sales;// 销售额
public int commission;// 提成
public SalesMan(int sales, int commission) {
this.sales = sales;
this.commission = commission;
}
public void getSalary() {
salary = sales * commission;
System.out.println("无底薪的销售人员的每月能获得的钱是:" + salary);
}
}
// 有底薪的销售人员 固定底薪+销售额*提成
class SalesOtherMan extends SalesMan {
public int basicsalary = 2300;
public SalesOtherMan(int sales, int commission) {
super(sales, commission);
}
public void getSalary() {
salary = basicsalary + sales * commission;
System.out.println("有底薪的销售人员的每月能获得的钱是:" + salary);
}
}
2.书写图形类,拥有计算周长与面积的方法,书写矩形类继承图形类重写对应方法,书写圆形类继承图形类重写对应方法,书写正方形类继承矩形类实现方法
//书写图形类,拥有计算周长与面积的方法,书写矩形类继承图形类重写对应方法,书写圆形类继承图形类重写对应方法,书写正方形类继承矩形类实现方法
public class FigureTest {
public static void main(String[] args){
rectangle r = new rectangle(1,7);
System.out.println("矩形的面积为:"+r.getArea());
System.out.println("矩形的周长为:"+r.getPerimeter());
round y = new round(1);
System.out.println("圆的面积为:"+y.getArea());
System.out.println("圆的周长为:"+y.getPerimeter());
square s = new square(2);
System.out.println("正方形的面积为:"+s.getArea());
System.out.println("正方形的周长为:"+s.getPerimeter());
}
}
abstract class Figure{
public double area;//面积
public double perimeter;//周长
public abstract double getArea();
public abstract double getPerimeter();
}
//矩形
class rectangle extends Figure{
int a;
int b;
public rectangle(int a) {
this.a = a;
}
public rectangle(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public double getArea() {
return a*b;
}
@Override
public double getPerimeter() {
return (a+b)*2;
}
}
//圆形
class round extends Figure{
public double r;//长
public double PI = 3.14;//宽
public round(){
}
public round(double r){
this.r = r;
}
public double getArea(){
return PI*r*r;
}
public double getPerimeter(){
return PI*4*r;
}
}
//正方形
class square extends rectangle{
public square(int a){
super(a);
}
public double getArea(){
return a*a;
}
public double getPerimeter(){
return 4*a;
}
}