面向对象进阶
多态
同类型对象,表现出的不同形态
表现形式
父类类型 对象名称 = 子类对象;
前提
*有继承/实现关系
*有父类引用指向子类对象
*有方法重写
好处
使用父类作为参数,可以接受所有子类对象
多态调用成员的特点:
public class test {
public static void main(String[ ] args) {
Animal a = new Dog();
//调用成员变量:编译看左边,运行也看左边
//编译看左边:javac编译的时候,会看左边的父类中有没有这个变量,如果有编译成功,如果没有,编译失败
//运行也看左边:运行时,实际获取的是左边父类中的成员变量
System.out.println(a.name);//动物
//调用成员方法
//编译看左边:同上
//运行看右边:运行的子类的方法
a.show();//执行子类的方法“D 方法”
a.eat();//会报错
}
}
class Animal{
String name = "动物";
public void show(){
System.out.println("A 方法");
}
}
class Dog extends Animal{
String name ="小狗";
@Override
public void show() {
System.out.println("D 方法");
public void eat(){
System.out.println("狗在吃骨头");
}
}
}
多态的弊端:
不能调用子类的特有方法
如:上面代码中调用a.eat则会报错
解决方法:变回子类类型
if(a instanceof Dog){
Dog d =(Dog)a;
}else {
System.out.println("没有这个类型");
}
此时可以调用
注:不能转化为其他类型,否则会报错
最好使用instanceof关键字进行判断
包:
作用:就是文件夹,来管理各种不同功能的java类
全类名:包名+类名
何时导包:
*使用同一个包中的类时,不需要导包
*使用java.lang包在的类时,不需要
*其他情况下都需要
*如果同时使用两个包中的同类名,需要用全类名
final
修饰方法: 表示该方法是最终方法,不能被重写
修饰类: 表明该类是最终类,不能被继承
修饰变量: 叫做常量,只能被赋值一次
常量的命名规范
单个单词:全部大写
多个单词:全部大写,单词之间用下划线隔开
注:如果修饰的变量是引用类型,地址值不变,对象内部可以改变
代码块
局部代码块:提前结束变量的声明周期
构造代码块:抽取构造方法中的重复代码
静态代码块:
格式:staticc{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
使用场景:在类加载的时候,做一些数据初始化的时候使用
抽象类
作用:抽取共性时,无法确定方法体,就把方法定义为抽象的
强制让子类按照某种格式重写(必须按照父类格式重写)
格式:
抽象方法 public abstract 返回值类型 方法名(参数列表);
抽象类 public abstract class 类名{}
抽象类和抽象方法的注意事项
●抽象类不能实例化
●抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
●可以有构造方法
继承抽象类时:
*要么重写抽象类中的所有抽象方法
*要么是抽象类
接口
创建
public interface 接口名{}
*接口和类之间是实现关系,通过implements关键字表示
可以单实现,也可以多实现
public class 类名 implements 接口名1,接口名2{}
也可以在继承一个类的同时实现多个接口
public class 类名 implements 接口名1,接口名2{}
*接口的子类(实现类)
要么重写抽象类中的所有抽象方法
要么是抽象类
注:如果多实现时有重名的方法,重写时相当于将两个方法都重写
接口中成员的特点
*成员变量
只能是常量
默认修饰符:public static final
*构造方法 没有
*成员方法
只能是抽象方法
修饰默认符:public abstract
接口和类的关系
*类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承
*类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
*接口和接口的关系
继承关系,可以单继承和多继承(多继承时如果继承最子接口 需要全部重写方法)
JDK8以后接口中新增的方法
默认方法
*允许接口中定义默认方法,用关键词default修饰
作用:解决接口升级的问题
默认方法的定义格式:public default 返回值类型 方法名 (参数列表){}
package practice;
public interface Inter {
public default void show(){
System.out.println("默认方法");
}
}
//默认方法必须写方法体
注意事项
*默认方法不是抽象方法,不强制重写 如果重写要去掉default关键字
*public可以省略,default不可以省略
*如果实现了多个接口,其中有相同名字的默认方法,子类就必须重写
静态方法
格式:public static 返回值类型 方法名 (参数列表){}
public static void show (){
System.out.println("静态方法");
}
注意事项:
只能通过接口名调用,不能类名和对象名调用
public可以省略,static不可以省略
JDK9新增的方法
public static void show1 (){
System.out.println("show1方法");
show4();
}
public static void show2(){
System.out.println("show2方法");
show4();
}
//普通的私有方法,给默认方法服务
static void show3(){
System.out.println("记录程序运行的一些细节");
}
//静态的私有方法,给静态方法服务的
private static void show4(){
System.out.println("记录程序运行的一些细节");
}
}
适配器
当一个接口中抽象方法过多,但是我只要使用其中一部分的时候(类似于一个中间变量 temp)
书写步骤:
编写中间类XXXAdapet 实现对应的接口
对接口中的抽象方法进行空实现(方法体空着 不写)
让真正的实现类继承中间类,并重写需要的方法
为了避免其他类创建适配类的对象,中间的适配类要用abstarct进行修饰
如果实现类有其他父类,可以让适配类去继承
内部类
写在一个类里面的类就叫内部类
用处:
B类表示的事物是A类的一部分,且B单独存在没有意义
如:汽车的发动机,ArrayList的迭代器,人的心脏
访问特点:
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须先创建对象
成员内部类
写在成员位置的,属于外部类的成员
获取方法:
当成员内部类被private修饰时
在外部编写方法,对外提供内部对象
非私有修饰时,直接创建对象
当重名时
public class test1 {
public static void main(String[] args) {
//创建内部类的对象,并调用show方法
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
class Outer{
private int a = 10;
class Inner{
private int a =20;
public void show(){
int a =30;
//outer.this 获取了外部类对象的地址值
System.out.println(Outer.this.a);//10
System.out.println(this.a);//20
System.out.println(a);//30
}
}
}
静态内部类
创建静态内部类的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
调用静态方法的格式:
外部类名.内部类名.方法名();
只要是静态的东西,都可以用类名点直接获取
public class test1 {
public static void main(String[] args) {
//创建静态内部类的对象
Outer.Inner oi = new Outer.Inner();
//调用非静态,先创建对象
oi.show1();
//直接用类名调用静态
Outer.Inner.show2();
}
}
class Outer{
static class Inner{
=
public void show1(){
System.out.println("非静态方法");
}
public static void show2(){
System.out.println("静态方法");
}
}
}
匿名内部类
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式:
new 类/接口(){
重写的方法
};
包含继承或实现,方法重写,创建对象
整体就是一个类的子类对象或者接口的实现类对象
使用场景:
当方法的参数时接口或者类时,可以传递这个类
如果实现类只使用一次,就可以用匿名内部类简化代码