多态 向上转型 向下转型 抽象类 抽象方法 类与接口的关系 匿名内部类
身边很多同学都说多态这部分是学java过程中遇到的第一个难点,里面很多抽象的内容很难理解。感觉这块还是得多看概念,不是所有代码学习都能只用通过大量代码练习才能学会,有时概念上的理解还先于实操。至于类,接口这里,则相反。
多态
面向对象编程有三大特性:封装、继承、多态
多态表示事物的多种形态
对象的多态性: 同一个对象,可能在不同的环境下,有不同的名称,不同的执行结果
譬如 我在学JAVA,对于学校来说我是学生,但回到家后我是儿子,出门对于社会来说我是百姓
多态的实现
多态实现的前提:
-
要有父子类的继承关系
-
方法的重写
-
父类类型的引用指向子类对象
定义格式:
父类类型 变量名=new 子类类型();
多态访问成员方法的特点
编译看左,运行看右
编译的时候,要看=左边的引用类型中,是否定义了该方法,如果有,就编译通过,如果没有就编译失败
运行的时候,要看= 右边,对象所属的类中,如果对这个方法进行实现,最终运行的是子类中对这个方法的实现
package Test2;
public class Tt {
public static void main(String[] args) {
Fu f = new Zi();//编译看左,父类中没有定义该方法(show()方法父类中有)
f.show();//运行看右,看子类对该方法的实现("子类的show方法")
}
}
class Fu{
public void show() {
System.out.println("父类的show方法");
}
}
class Zi extends Fu{
public void show() {
System.out.println("子类的show方法");
}
}
这里编译看左,Fu f 父类中存在和子类Zi一样的show()方法,所以编译通过
运行看右,Zi 子类中有对show()方法的实现,所以输出的是子类的方法实现
多态的转型
多态的转型分为向上转型和向下转型两种
向上转型
创建多态关系 (多态本身就是向上转型的过程)
使用父类的引用指向了子类对象
定义格式:父类类型 变量名=new 子类类型();
向上转型的本质:缩小了对象本身的访问范围,减少了他的访问权限,只访问父类中的内容
向下转型
让一个指向子类对象的父类引用,恢复成子类的引用
变量的强制转换
定义格式:
子类类型 引用名称 = (子类类型)父类类型的引用
向下转型的本质:恢复子类对象的访问权限与范围
package Test2;
public class Tt {
public static void main(String[] args) {
Fu f = new Zi();//向上转型
f.show();//缩小访问范围只能访问父类的内容
f.fuwork();//父类中存在的方法子类若重写即可访问
Zi z = (Zi)f;//向下转型:恢复本身的访问范围
z.show();
z.ziwork();
}
}
class Fu{
public void show() {
System.out.println("父类的show方法");
}
public void fuwork() {
System.out.println("只有父类有的方法");
}
}
class Zi extends Fu{
public void show() {
System.out.println("子类的show方法");
}
public void ziwork() {
System.out.println("只有子类有的方法");
}
}
总结:父类类型的引用指向子类对象完成向上转型后,只能访问父类中的属性和方法,如果子类中没有这些方法和属性,是无法访问的,但子类要是重写了父类的某种方法,那调用时会实现子类的内容
多态的好处
-
提高了代码的拓展性
-
在方法中,如果形参是父类类型,调用这个方法时候,可以把这个父类类型的子类创建出来的对象,当做实参进行传递
-
如果一个方法的返回值是父类类型,那么就可以在方法内将父类的子类对象,当做方法返回值返回,调用者可使用父类类型的引用来接收,或向下转型来接受都可
package Test2; public class Tt { public static void main(String[] args) { Student stu = new Student(); stu.show(new Cat());实参 传递的是子类类型的对象 stu.show(new Dog()); } } class Student{ public void show(Pet p) {//形参 是父类类型的引用 p.work(); } } class Pet{ public void work() { System.out.println("工作"); } } class Cat extends Pet{ public void work() { System.out.println("抓老鼠"); } } class Dog extends Pet{ public void work() { System.out.println("看门"); } }
抽象类
抽象类
抽象:抽取像的,相同的或者相似的
可以定义抽象方法的类就是抽象类
1、抽象类的定义格式
abstract class 类名{
类体;
}
如果父类中的方法被继承过去的子类都要重写 那就这个方法就没有在父类实现的意义了,可以直接定义为抽象类
抽象方法
抽象方法:被abstract修饰方法,被修饰后这个方法不会有具体实现,只能在抽象了的子类中通过方法重写进行实现,只有方法的声明,没有方法的实现,这种方法叫做抽象方法
抽象方法定义的格式:
在方法的前面加上一个关键字:abstract
因为没有方法的实现,不需要写大括号,
例如: public abstract void show();
package Test2;
public class Tt {
public static void main(String[] args) {
Wor wor = new Wor();
wor.test1();
wor.test2();
}
}
abstract class Bos{//可以定义抽象方法的类,一定是抽象类
public abstract void test1();//定义抽象方法
public abstract void test2();
}
class Wor extends Bos{
@Override
public void test1() {//父类中的抽象方法,子类必须重写
System.out.println("重写父类test1");
}
@Override
public void test2() {
System.out.println("重写父类test2");
}
}
抽象类的特点
抽象类和抽象方法的关系
1、都要使用abstract关键字修饰
-
抽象类中可以有抽象方法,也可以没有抽象方法
-
抽象方法必须在抽象类中
-
抽象类不能实例化,但可以定义抽象类子类,由子类去创建对象从而调用方法
2、抽象类的子类的分类
-
如果子类实现了抽象类父类中的所有抽象方法,那这个类就是一个普通子类,就可以创建对象了
-
如果子类,没有完全实现抽象类父类中的抽象方法,那这个子类还是一个抽象类,无法实例化
3、抽象类中可以定义成员变量和定义常量,但因为抽象类不能实例化,所以不能被抽象类使用,可以通过子类继承,访问父类的构造方法,来完成对抽象父类中成员变量的初始化,也可以有成员方法,这里定义的方法内容子类一般不会去重写
package Test2;
public class Tt {
public static void main(String[] args) {
Wor wor = new Wor();
wor.test1();
}
}
abstract class Bos{//可以定义抽象方法的类,一定是抽象类
int age = 40 ;//定义成员变量
final String name = "老板";//定义常量
public abstract void test1();//定义抽象方法
public Bos() {
super();
}
public Bos(int age) {
super();
this.age = age;
}
public void eat() {//定义成员方法,一般是子类中不需要重写的内容
System.out.println("吃大餐");
}
}
class Wor extends Bos{
@Override
public void test1() {//父类中的抽象方法,子类必须重写
System.out.println("重写父类test1");
}
}
接口
是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口的好处:
-
可以扩展一个事物的功能
-
可以达到高内聚低耦合
-
方便后期维护
接口在程序中的好处:
- 规则的定义和规则的实现,分离
规则:对应的就是方法,都是抽象方法
实现者:对应的就是方法的具体实现,本质是一个对象
只需要说明应该有哪些方法,不需要管谁来实现了方法,实现了规则的定义和规则的实现分离
接口的格式
interface 接口名{
方法的声明;
}
实现接口的方式:
定义类来实现接口,这个类就叫接口实现类
class 类名 implements 接口名{
重写接口定义的所有抽象方法
}
package Test2;
public class Tt {
public static void main(String[] args) {
Person p = new Person();
p.test();
p.test1();
}
}
interface Study {//定义接口
public abstract void test();//定义规则,由子类去实现
void test1();
}
class Person implements Study {//接口实现类,实现接口中的方法
@Override
public void test() {
System.out.println("Person中重写的test方法");
}
@Override
public void test1() {
System.out.println("Person中重写的test1方法");
}
}
接口的特点
- 一个类如果实现了接口中的所有抽象方法,那么就是一个普通类,如果没有完全实现,就是一个抽象类
- 接口中方法的声明,都是抽象方法,是一个抽象类型,不能实例化,只能定义实现类,实现接口,通过实现类创建对象,再由对象调用方法
- 接口中定义的属性都默认前面 public static final,都是静态常量属性,所以就也没成员变量
- 接口中没有成员变量,所以就不能定义构造方法
- 接口中不能定义成员方法,只能定义抽象方法
package Test2;
public class Tt {
public static void main(String[] args) {
Student s = new Student();
s.test();
s.study();
}
}
interface Book {//定义接口
public abstract void test();//定义规则,由子类去实现
void study();//这其实也是个抽象方法,默认public static final修饰
//无成员变量,都是是静态常量属性,所以不能有构造方法
}
class Student implements Book {//接口实现类,实现接口中的方法
@Override
public void test() {
System.out.println("Student中重写的test方法");
}
//如果一个接口中的方法,没有全部在子类中被重写,那么这个类就是一个抽象类
//所以都必须重写
@Override
public void study() {
System.out.println("Student中重写的study方法");
}
}
类与接口的关系
类与类
使用extend继承,可以单继承,不可以多继承,可以多层继承
类与接口
类与接口使用implements实现,可以单实现,也可以多实现,不能多层实现
多实现的格式1:
class 类名 implements 接口1,接口2,接口3…{
重写所有接口中的抽象方法
}
多实现没有安全隐患的,因为即使两个接口中有一样的抽象方法,但是类中,只需要提供一个实现就够了
多实现的格式2:
class 类名 extends 父类名 implements 接口1,接口2,接口3{
重写所有的抽象方法
}
package Test2;
public class Tt {
public static void main(String[] args) {
}
}
interface Book {
public abstract void study1();
}
interface Journal {
public abstract void study2();
}
interface Newspaper {
public abstract void study3();
}
class Teacher{
public void teach() {
System.out.println("老师教书");
}
}
class Student implements Book,Journal,Newspaper{//可以让一个实现类实现多个接口
@Override
public void study3() {
System.out.println("Student中重写Book的方法");
}
@Override
public void study2() {
System.out.println("Student中重写Journal的方法");
}
@Override
public void study1() {
System.out.println("Student中重写Newspaper的方法");
}
}
class Person2 extends Teacher implements Book,Journal{//可以继承一个类之后再实现多个接口
@Override
public void study2() {
// TODO Auto-generated method stub
}
@Override
public void study1() {
// TODO Auto-generated method stub
}
}
interface Book2 extends Book,Newspaper,Journal{
//接口可以多继承
}
interface Book3 extends Book2{
//接口可以多继承
}
匿名内部类
没有名字的内部类
适合创建那种只需要使用一次的类
使用前提:
-
继承一个类
-
实现一个接口
定义格式:
new 父类类名或者接口名(){
父类方法的重写或者是接口内容的实现
}
本质:创建了一个子类对象、接口实现类对象
package Test2;
public class Tt {
public static void main(String[] args) {
new rest(){//接口实现类,可以理解为一个子类
@Override
public void eat() {
System.out.println("吃零食");
}
@Override
public void play() {
System.out.println("打游戏");
}
}.play();
//父类类型的引用指向子类对象
//利用多态 把实现类对象赋值给父接口
rest r = new rest() {
//这里 rest 是父类类型 new rest(){}是子类对象
@Override
public void play() {
System.out.println("吃零食~~~~~");
}
@Override
public void eat() {
System.out.println("打游戏~~~~~");
}
};
r.eat();
r.play();
}
}
interface rest{
public abstract void eat();
public abstract void play();
}