2019-01-19~20
主要学习了继承、super关键字、访问权限修饰符、方法重写、final关键字、多态、抽象类、接口
继承
为什么需要继承?
例如:小学生、中学生、大学生,他们都有姓名、年龄、学号的属性,交学费的行为
把他们相同的属性和行为抽取出来放到单独的一个学 d生类中,小学生、中学生、大学生只需要继承就好了,大大的减少了代码
总结:类似对对象的抽取,继承是对某一批类的抽象
继承的理解
- 继承是对一批类的抽象
- 继承本质就是向上抽取代码,也就是说如果多个类出现重复代码,我们可以定义一个类来存储这些重复代码,并且给这个类取一个名字就好了
继承的格式:
通过extends关键字就可以实现类与类的继承
class 子类名 extends 父类名 { }
被继承的这个类叫父类,基类或者超类
继承的这个类称为子类或者派生类
继承的好处:
3. 简化了代码
4. 提高了代码的维护性和可扩展性
5. 让类与类之间产生关系,这是多态的必要条件
继承的缺点:
开发设计的原则:高内聚,低耦合
类与类之间的关系不要太高,尽量保证类与类之间的独立性
1.造成耦合性过高
2.牵一发动全身 A B C D extends X(go())
继承要慎用
继承的特点
- 单一继承性(一个儿子只有一个爸爸)
- java支持多层继承,不支持多继承
class Son extends Father{}
class Father extends GrandFather{} - 如果一个父类的成员变量使用private关键字修饰,那么对子类成员不可见
一般父类成员使用protected和public修饰 - 如果一个子类不仅可以继承父类的所有公共方法和属性,还可以有自己的方法
- 子类不能继承父类的构造方法
什么时候用继承?
- 存在公共代码
- 满足A is a B
如何继承?
- 先写子类
- 观察多个子类之间是否存在公共代码
- 存在就将公共代码抽取出来,放置在父类中
- 将子类继承父类
- 将子类的公共代码删除
super关键字
this和super用法类似,this代表本类引用,super代码父类引用
this和super的对比使用
访问成员变量
this.成员变量 super.成员变量
访问构造方法
this():访问本类无参构造 this(参数列表) 访问本类带参构造
super():访问本类无参构造 super(参数列表) 访问本类带参构造
访问成员方法
this.成员方法():访问本类成员方法
super.成员方法():访问父类成员方法
this和super的区别
this表示访问当前对象,super表示访问父类对象
既然super理解为一个父类对象,那么super应该可以用父类的成员
this主要作用是用来帮我们初始化本类成员(当外界传入的变量名和本类变量名相同的时候)
任何一个本类成员默认都隐含一个this
任何一个本类成员同时也可以使用super访问父类成员
super特点
- 任何一个构造方法都默认会在构造方法的第一句写上super(),访问父类的无参构造方法 Constructor call must be the first statement in a constructor
- super和this访问构造方法必须出现在构造方法的第一句
this和super在访问构造方法的时候,二者不能共享 - static静态上下文中不能出现this和super
- super的主要作用是帮助我们初始化父类继承过来的成员
访问权限修饰符
为什么需要访问权限修饰符
控制成员(成员变量 成员方法 构造方法)的访问权限
访问权限修饰符的总结
- 访问权限修饰符有四个
- 访问权限修饰符可以修饰成员变量、成员方法、还可以修饰类(只有public可以)
在这里需要注意,其实protected/private可以修饰类,但必须是内部类
四个修饰符特点 - public修饰的成员对一切类可见
- protected修饰的成员对子类可见
- default修饰的成员仅 对同包下可见
- private修饰的成员仅对本类可见
访问权限修饰符的宽严关系
public > protected > default > private
Java当中封装就是通过访问权限修饰符来实现的
封装: 就是屏蔽内部的成员,而屏蔽内部的成员是通过访问权限修饰符来控制的
方法重写
为什么需要方法重写?
父类的功能不能满足子类的需求,需要方法重写
方法重写的概念:覆盖父类的方法,实现自己所特有的功能
方法重写的特点:
- 发生在具有子父关系 的两个类中
- 方法名相同
- 参数列表完全相同(参数的个数,顺序,类型都一样)
- 返回值类型可以相同或者是其子类
- 访问权限修饰符不能严于子类
- 子类声明的异常不能够被扩大
访问权限修饰符的宽严关系
public > protected > default > private
方法重写的注意事项
- 私有修饰的方法不能够被重写【因为私有的修饰方法不能被继承】
- 构造方法不能够被重写
- 子类重写父类方法时,访问权限修饰符不能够严于父类
- 子类声明的异常不能够被扩大
- 重载和重写的区别–>
重载的特点:发生在一个类中,方法名相同,参数列表不同
重写的特点:如上所示 - 重写是多态的必要条件
final关键字
为什么需要final关键字
final关键字最终的意思
final关键字的特点:
- final修饰的类不能够被子类继承
- final修饰的方法不能够被子类重写
- final修饰变量:不能够被二次赋值,final修饰的变量直接升级为常量
成员变量:可以声明之后初始化
局部变量:一定在声明的同时初始化
常量:
字面值常量 20 20.5 “ABC”
自定义常量 public static final int NUM = 10;
多态
为什么需要多态
一个动作在不同环境下变现出来的不同状态
eg: cut
理发师:剪头发
医生:动刀子
导演;暂停
多态的概述:
生活中: 同一个动作在不同的环境下体现出来的不同的状态
代码中: 同一个方法在不同的类下体现出来的不同的实现
内存中: 父类引用指向子类对象
多态在代码中如何来实现的?
- 必须要存在继承关系
- 必须存在方法重写
- 父类引用指向子类对象
多态访问成员的特点:
成员变量:编译时看左边类型,运行时看左边类型
成员方法:编译时看左边类型,运行时看右边类型
构造方法:帮助子类初始化父类继承过来的成员
静态方法:编译时看左边类型,运行时看左边类型
多态的优点:
- 简化了代码
- 提高了维护性和可扩展性
eg:饲养员给不同的动物喂不同的食物
public class DuoTaiDemo03 {
public static void main(String[] args) {
Feeder f = new Feeder();
// f.feedMeatToTiger(new Tiger(), new Meat());
//
// f.feedBambooToPanda(new Panda(), new Bamboo());
Animal an = new Tiger();
Food food = new Meat();
f.feed(an, food);
f.feed(new Monkey(), new Banana());
}
}
//开闭原则: 对扩展开放,对修改关闭
class Feeder{
// 给老虎喂肉
/*
* 返回值类型: void
* 参数列表: Tiger t, Meat m
* 方法名: feedMeatToTiger
*/
/*public void feedMeatToTiger(Tiger t, Meat m) {
t.show();
m.show();
}
public void feedBambooToPanda(Panda p, Bamboo b) {
p.show();
b.show();
}*/
// 利用多态
public void feed(Animal an, Food f) {
// Animal an = new Tiger();
// Food f = new Meat();
an.show();
f.show();
}
}
class Animal{
public void show() {
System.out.println("我是动物");
}
}
class Bear extends Animal{
@Override
public void show() {
System.out.println("我是狗熊");
}
}
class Tiger extends Animal{
@Override
public void show() {
System.out.println("我是老虎");
}
}
class Panda extends Animal{
@Override
public void show() {
System.out.println("我是熊猫");
}
}
class Monkey extends Animal {
@Override
public void show() {
System.out.println("我是猴子");
}
}
class Food{
public void show() {
System.out.println("我是食物");
}
}
class Meat extends Food{
public void show() {
System.out.println("我是肉");
}
}
class Bee extends Food{
@Override
public void show() {
System.out.println("我是蜂蜜");
}
}
class Bamboo extends Food{
public void show() {
System.out.println("我是竹子");
}
}
class Banana extends Food {
@Override
public void show() {
System.out.println("我是香蕉");
}
}
多态的缺点:使用多态不能够访问子类特有的方法
解决办法:向下转型
向上转型(自动转换)
格式:<父类型> <引用变量名> = new <子类型>();
特点:
子类转为父类 父类的引用指向子类对象。自动进行类型转换
此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法
此时通过父类引用变量无法调用子类特有的属性和方法
向下转型(强制类型转换)
格式:<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
特点:
父类转为子类,父类引用转为子类对象。强制类型转换
在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常
java.lang.ClassCastException: com.sxt.duotaidemo.Benz cannot be cast to com.sxt.duotaidemo.BMW
ClassCastException: 类型转换异常
产生原因: 在向下转型的过程当中,没有转换成真实的类型
解决办法: 在向下转型的过程当中,对父类类型的所有子类做逐一判断
instanceof关键字
左边对象 instanceof 右边类 这是一个表达式,结果boolean类型
判断左边对象是否属于右边类的实例
抽象类
eg:一个形状类 有三角形 圆形 矩形三个子类
public class AbstractDemo01 {
public static void main(String[] args) {
Shape s = new Triangle(2,4);
System.out.println(s.getArea());
s = new Circle(2);
System.out.println(s.getArea());
}
}
abstract class Shape {
public abstract double getArea();
}
class Triangle extends Shape {
private double d;
private double h;
public Triangle(double d, double h) {
super();
this.d = d;
this.h = h;
}
public Triangle() {
super();
}
@Override
public double getArea() {
return d * h / 2;
}
}
class Circle extends Shape {
private double r;
public Circle(double r) {
super();
this.r = r;
}
public Circle() {
super();
}
@Override
public double getArea() {
return 3.14 * Math.pow(r, 2);
}
}
通过观察发现,父类的getArea方法如果删除,那么多态编译报错,所以父类方法存在有意义
但是父类方法getArea的实现没有意义,像这种用来给子类重写,但是本身有没有任何实现的方法可以定义为抽象方法
格式:访问权限修饰符 abstract 返回值类型 方法名();
Abstract methods do not specify a body
The abstract method getArea in type Shape can only be defined by an abstract class
只要有一个抽象方法的类就必须定义为抽象类
抽象类特点:
- 抽象类和抽象方法使用abstract修饰
- 有一个抽象方法必须声明抽象类
- 抽象类中可以没有抽象方法
- 抽象方法的抽象类有意义吗?有意义,不能够实例化,防止外界创建对象
- 如果我们想要“实例化”–利用多态
- 抽象类的子类特点:
a.如果子类想要继承抽象类,那么就必须实现抽象类中的抽象方法
b.如果子类不想要实现抽象类中的抽象方法,可以,子类也升级为抽象类 - 抽象类普通类的区别:
抽象类中 可以有成员变量吗,可以有成员方法吗,静态方法,构造方法吗? 如果有的话有意义吗?
成员变量 --> 可以给子类使用
成员方法 --> 可以给子类使用
构造方法 --> 帮助子类初始化父类继承过来的成员
抽象方法 --> 可以给子类重写
燃烧自己,照亮孩子
==所以抽象本质就是一个服务类,服务于所有的子类 == - private final static 可以用来修饰抽象方法吗
private修饰的方法不能够被子类继承,abstract修饰的方法希望被子类重写,二者冲突
final修饰的方法不能够被子类重写,abstract修饰的方法希望被子类重写,二者冲突
static修饰方法用来方便调用,某个方法的调用肯定是需要功能的实现,而abstract修饰的方法没有方法体,调用就毫无意义,二者没有意义 - 即热抽象类是一个服务类,服务于子类所以抽象类一般都是用public protected
接口
生活中的接口是指某些企业和组织等制定的
一种约定或标准(USB)
USB风扇按照电脑产商的USB接口设计生产
就可以使得电脑具备风扇散热的功能。
USB鼠标按照电脑产商的USB接口设计生产
就可以使得电脑具备鼠标移动的功能。
U盘按照电脑产商的USB接口设计生产
就可以使得电脑具备U盘存储的功能。
生活中的接口的理解
1.接口是一种规范,是一种标准
2.接口还能够扩展功能
3.接口是即插即拔,是灵活的
接口概念:接口就是抽象方法和常量的集合,接口是比抽象类更抽象的类
接口的特点:
- 接口使用interface修饰
- 接口就是抽象方法和常量的集合,接口是比抽象类更抽象的类,在JDK8以后,接口可以有静态方法和默认方法
- 接口成员变量默认都是常量默认会省略public static final,接口中的成员都是抽象方法,默认使用 public abstract修饰
- 接口不能实例化,如果需要实例化,可以利用多态
- 接口子类的特点:
a.如果一个实现某接口,那么就必须实现接口里面的抽象方法
b.如果子类不想要实现接口,那么子类也可以升级为接口 - 接口是一种规范
- 接口是灵活的
- 接口是用来扩展功能的,还是继承者本身的实现
- 接口和类的关系
类和类 单继承 不可以实现
类和接口 多实现 不可以继承
接口和接口 多继承 不可以实现 - 接口和继承的区别
什么时候使用接口,什么时候使用继承?
大部分情况下都是使用接口,因为接口的灵活,降低了耦合性。而且接口是一种规范
继承主要体现在代码的抽取,如果出现了重复代码就使用继承
或者需要使用父类的某个功能 - 面向接口的编程
多态的应用
作为形参
基本类型
引用类型
普通类
当一个形参希望我们传入一个普通类时,我们实际上传入的是该类的对象/匿名对象
抽象类
当一个形参希望我们传入一个抽象类时,我们实际上传入的是该类的子类对象/子类匿名对象
接口
当一个形参希望我们传入一个接口时,我们实际上传入的是该类的实现类对象/实现类匿名对象
作为返回值
基本类型
引用类型
普通类
当一个方法的返回值是一个普通类时,实际上返回的是该类的对象,我们可以使用该类的对象接收
抽象类
当一个方法的返回值是一个抽象类时,实际上返回的是该抽象类的子类对象,我们可以使用该抽象类接收
接口
当一个方法的返回值是一个接口时,实际上返回的是该接口的实现类对象,我们可以使用接口接收
当返回值类型是引用类型时,可以使用链式调用
内部类
为什么需要内部类?
- 提高类独栋安全性
- 防止外界创建对象
什么情况下可以不让外界创建对象
- 抽象类
- 接口
- 内部类
- 构造方法私有化
内部类的概念:在类中定义一个类叫内部类
内部类的分类
成员内部类
局部内部类
静态内部类
匿名内部类
内部类的特点:
- 外部类可以直接访问内部类成员,包括私有
- 内部类通过编译器生成字节码文件的格式:外部类$内部类
- 外界需要访问内部类可以通外部创建内部类对象
- 外界也可以通过如下格式创建内部类对象
外部类.内部类 对象名=new 外部类.new内部类;