Java-继承与多态

  • 静态代码块优先于主方法执行的,且只在类加载时执行一次。构造代码块优先于构造方法块执行。
  • 高内聚低耦合:内聚就是自己独立完成某件事情的能力,耦合就是类与类之间的关系。
 
jvm先加载Fu.class,再加载Zi.class,然后读取Zi z = new Zi();
先输出父类的静态代码块,再输出子类的静态代码块,
然后,先输出父类的构造代码块、父类的构造方法,再输出子类的构造方法块、子类的构造方法。
 
 
 
继承的特点
  1. 只支持单继承,不支持多继承(一个儿子只能有一个爹)
  2. Java支持多层继承(继承体系):如果想用这个体系的所有功能,用最底层的类创建对象;如果想看这个体系的共性功能,看最顶层的类。
 
继承的注意事项
  1. 子类只能继承父类所有非私有的成员(成员方法和成员变量)。
  2. 子类不能继承父类的构造方法,但是可以通过super关键字去访问父类的构造方法。
  3. 不要为了部分功能去继承。
  4. is a :判断谁是谁的一种,就可以考虑用继承。
  5. (子父类出现同名的变量)就近原则:子类中有的变量就不用父类的了。(一般不会出现同名变量,因为子类可以继承父类的变量,不需要自己再申明一个相同的,就算父类这个变量是私有的,也可以通过设置 setXxx() 和 getXxx() 方法去调用)。
 
 
this 与 super的区别
  • 调用成员变量:this代表当前对象的引用,谁来调用我,我就代表谁,super代表当前对象父类的引用。
  • 调用构造方法:this(.....)调用本类的构造方法,super(.....)调用父类的构造方法。
  • 调用成员方法:this可以调用本类的成员方法,也可以调用父类的成员方法。super只能调用父类的构造方法。
 
子类中所有的构造方法默认都会访问父类中空参数的构造方法,因为子类会继承父类中的数据,可能还会使用父类中的的数据,所以,子类初始化前,一定要先完成父类数据的初始化。
其实,每一个构造方法的第一句默认都是super() Object类是最顶层的类。
 
 
方法重写
如果父类和子类出现相同的方法名,调用该方法名会输出子类的方法,也就是方法的重写。
当子类需要父类的功能,而功能主体子类需要自己特有的内容时,可以重写父类的方法,这样就沿袭了父类的功能。
 
重写注意事项
  1. 父类中的私有方法不能被重写;
  2. 子类重写父类方法时,访问权限不能更低,最好就一致;
  3. 父类是静态方法,子类也必须通过静态方法进行重写。(这种不算重写,非静态才算重写,静态只能覆盖静态);
  4. 子类重写父类方法时,声明最好一模一样。
 
方法的重载
概念:方法名相同,参数列表不同,与返回值类型无关
分类:
  1. 参数个数不同
  2. 参数类型不同(包括顺序不同)
 
 
 
final修饰的类、方法以及变量的特点
  1. 修饰类:类不能被继承。
  2. 修饰方法:方法不能被重写。
  3. 修饰成员变量:变量就变成了常量,只能被赋值一次。 //常量命名规范:如果是一个单词,每次字母都大写,如果是多个单词,每个字母都大写且中间用下划线隔开。
 
final修饰局部变量:
基本类型:值不能被改变;
引用类型:地址值不能被改变,对象中的属性可以改变
final修饰变量:
final int num; //默认初始化值是无效的,必须给num赋值,只要在对象构造完毕前完成赋值即可。
 
 
 
多态的前提
  1. 要有继承关系;
  2. 要有方法重写;
  3. 要有父类引用指向子类对象:Father f=new Son();
Father f=new Son();
成员变量:编译看左边(父类),运行也看左边(父类)
成员方法:编译看左边(父类),运行看右边(子类)动态绑定
静态方法:编译看左边(父类),运行看左边(父类)
静态和类相关,算不上重写,所以访问的还是左边的
只有非静态的成员方法,编译看左边,运行看右边
 
 
Person p = new SuperMan (); //父类引用指向子类对象就是向上转型
SuperMan sm = (SuperMan)p; //向下转型
 
关键字instanceof判断前边的引用是否是后边的数据类型
开发的是很少在创建对象的时候用父类引用指向子类对象,直接创建子类对象更方便,可以使用子类中的特有属性和行为;
当作参数的时候用多态最好,因为扩展性强.
 
 
抽象类(abstract)
抽象类的特点:
  1. 抽象类和抽象方法必须用abstract关键字修饰
1> abstract class 类名{ }
2> public abstract void eat();
  1. 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类或者是接口
  2. 抽象类不能实例化,那么该如何实例化?
按照多态的方式,由具体的子类实例化,其实这也是多态的一种,抽象类多态。
  1. 抽象类的子类
1> 要么是抽象类。
2> 要么重写抽象类中的所有抽象方法。
问:
  1. 一个抽象类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
可以,目的是不让其他类创建本类对象,交给子类完成。
  1. abstract不能和哪些关键字共存?
abstract和static:被abstract修饰的方法没有方法体,被static修饰的可以用类名. 调用,但是类名. 调用抽象方法是没有意义的。
abstract和final:被abstract修饰的方法强制子类重写,被final修饰的方法不让子类重写。
abstract和private:被abstract修饰的方法就是为了让子类看到并强制子类重写,被private修饰不让子类访问。
 
接口(interface)
接口的特点:
  1. 接口用关键字interface表示:interface 接口名 { }。
  2. 类实现接口用implements表示:class 类名 implements 接口名 { }。
  3. 接口不能实例化,那么该如何实例化?
按照多态的方式来实例化。父类引用指向子类对象:Inter i = new Demo();
  1. 接口的子类:
    1. 可以是抽象类,但是意义不大。
    2. 可以是具体类,要重写接口中的所有抽象方法(推荐用这种)。
  2. 接口中的方法都是抽象的。
 
接口的成员特点:
成员变量:只能是常量,并且是静态的公共的。默认修饰符:public static final,三个可以互相交换位置。
构造方法:接口中没有构造方法
成员方法:只能是抽象方法,即不能有方法体。默认修饰符:public abstract,建议自己手动给出。
注意:实现接口的方法必须是public,因为接口中的抽象方法是public,如果在接口中自己不写,系统也会默认加上public。
 
类与类、类与接口、接口与接口的关系
  • 类与类:继承,只能单继承,可以多层继承
  • 类与接口:实现关系,可以是单实现,也可以是多实现:calss Demo inplements InterA, InterB{ }。并且可以继承一个类的同时同时实现多个接口。
  • 接口与接口:继承关系,可以单继承,也可以多继承。
 
抽象类与接口的区别
  1. 成员区别
  • 抽象类
成员变量:变量,常量
构造方法:有
成员方法:抽象,非抽象
  • 接口
成员变量:常量
成员方法:抽象
  1. 关系区别
  • 类与类:单继承
  • 类与接口:单实现,多实现
  • 接口与接口:单继承,多继承
  1. 设计理念区别
  • 抽象类:被继承体现的是 is a 的关系,抽象类中定义的是该继承体系的共性功能。
  • 接口:被实现体现的是 like a 的关系,接口中定义的是该继承体系的扩展功能。
 
有包的情况下运行:
java -d . Demo1.java
Java com.zhangshan.Demo1java
 
 
四种权限修饰符
修饰符 本类 同一个包下(子类和无关类) 不同包下(子类) 不同包下(无关类)
private Y
默认 Y Y
protected Y Y Y
public Y Y Y Y
 
 
类及其组成所使用的常见修饰符
* A:修饰符:
* 权限修饰符:private,默认的,protected,public
* 状态修饰符:static,final
* 抽象修饰符:abstract
* B:类:
* 权限修饰符:默认修饰符,public
* 状态修饰符:final
* 抽象修饰符:abstract
 
* 用的最多的就是:public
 
* C:成员变量:
* 权限修饰符:private,默认的,protected,public
* 状态修饰符:static,final
 
* 用的最多的就是:private
 
* D:构造方法:
* 权限修饰符:private,默认的,protected,public
 
* 用的最多的就是:public
 
* E:成员方法:
* 权限修饰符:private,默认的,protected,public
* 状态修饰符:static,final
* 抽象修饰符:abstract
 
* 用的最多的就是:public
 
* F:除此以外的组合规则:
* 成员变量:public static final 接口
* 成员方法:
* public static
* public abstract
* public final
 
内部类
内部类的访问特点
  1. 内部类可以直接访问外部类的成员,包括私有。
  2. 外部类要访问内部类的成员,必须创建对象。
  3. 外部类名. 内部类名 对象名 = 外部类对象. 内部类对象;
 
成员内部类私有使用:
class OIclass{ public static void main(String args[]){ /* Outer.Inner oi = new Outer().new Inner(); oi.method(); */ Outer o = new Outer(); o.print(); } } class Outer{ private int num = 100; private class Inner{ public void method(){ System.out.println(num); } } public void print(){ Inner i = new Inner(); i.method(); } }
 
访问静态内部类:
class Demo2_InnerClass{ public static void main(String agrs[]){ //外部类名.内部类名 对象名 = 外部类名.内部类对象 //通常把new写在前面 Outer.Inner oi = new Outer.Inner(); oi.method(); Outer.Inner2.print(); } } class Outer{ static class Inner{ //静态内部类里有个非静态方法 public void method(){ System.out.println("method"); } } static class Inner2{ public static void print(){ System.out.println("print"); } } }
 
内部类之所以能获取外部类成员,是因为它能获取到外部类的引用,外部类. this。
 
局部内部类是在方法中定义的类:
局部内部类访问局部变量必须用final修饰,若没有final,它的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失,那么如果局部内部类还没有马上消失想用这个变量,就没有了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,也可以继续使用。
但是,JDK1.8取消了这个事情,所以我认为这是一个bug。
class Demo3_InnerClass{ public static void main(String args[]){ Outer o = new Outer(); o.method(); } } class Outer{ public void method(){ final int num=10; class Inner{ public void print(){ System.out.println(num); } } Inner i = new Inner(); i.print(); } //局部内部类只能在其所在的方法中访问 }
 
匿名内部类
  • 匿名内部类:就是内部类的简化写法。
  • 前提:存在一个类或者接口(这个类可以是具体类,也可以是接口)。
  • 格式:
new 类名或者接名(){
重写方法;
}. xxx();
  • 本质:是一个继承了该类或者实现了该接口的子类匿名对象。
class Demo4{ public static void main(String args[]){ Outer o = new Outer(); o.method(); } } interface Inter{ public void print(); } class Outer{ //有名字的内部类 /* class Inner implements Inter{ public void print(){ System.out.println("内部类"); } } public void method(){ Inner i = new Inner(); i.print(); } */ //匿名内部类 public void method(){ new Inter(){ //实现Inter接口 public void print(){ //重写抽象方法 System.out.print("匿名内部类"); } }.print(); } }
 
匿名内部类重写多个方法调用:
匿名内部类只针对重写一个方法时使用
匿名内部类是不能向下转型的,因为没有子类类名。
class Demo4_NoNameInnerClass{ public static void main(String args[]){ Outer o = new Outer(); o.method(); } } interface Inner{ public void show1(); public void show2(); } class Outer{ public void method(){ Inner i = new Inner(){ public void show1(){ System.out.println("show1"); } public void show2(){ System.out.println("show2"); } /* public void show3(){ //匿名内部类是不能向下转型的,因为没有子类类名。 System.out.println("show3"); } */ }; i.show1(); i.show2(); // i.show3(); } }
 
匿名内部类当作参数传递,本质是把匿名内部类看作一个对象
class Demo4Class{ public static void main(String args[]){ //如何调用PersonDemo中method方法? PersonDemo pd = new PersonDemo(); pd.method(new Person(){ //匿名内部类当作参数传递(本质把匿名内部类看作一个对象) public void show(){ System.out.println("show"); } }); } } abstract class Person{ public abstract void show(); } class PersonDemo{ public void method(Person p){ p.show(); } }
不用匿名内部类,可以创建一个student继承Person,重写show方法,在主方法调用method时传入new Student()。
 
匿名类和内部类中的中的this :
有时候,我们会用到一些内部类和匿名类。当在匿名类中用this时,这个this则指的是匿名类或内部类本身。 这时如果我们要使用外部类的方法和变量的话,则应该加上外部类的类名
 
 

posted @ 2021-02-19 17:53  鲸落98  阅读(149)  评论(0)    收藏  举报