继承
(is-a):子类扩展基类,并获取基类中的全部成员变量与方法(非private),因此在定义子类时只需指出与基类的不同之处即可。换句话说,通用的方法放在基类,特殊方法放在子类。
Java在类中只支持单继承关系
public class Fruit {
public double weight;
public void info() {
System.out.println("ClassFriut " + weight + " g");
}
}
------------------------------------------------------------------
public class Apple extends Fruit {
public static void main(String[] args) {
Apple a = new Apple();
a.weight = 100;
a.info();
}
方法重写(覆盖)
基本要求是子类与基类方法严格一致。另外,要求子类方法的返回值类型<=基类方法的返回值类型;子类中的异常类<=基类声明抛出的异常类;子类方法的访问权限>=父类的访问权限(基类的私有方法和静态方法以及final方法只能被重载不能被重写)
方法重载 | 方法重写 |
---|---|
方法名相同 | 方法名相同 |
与修饰符无关 | 修饰符>=基类 |
返回值无关 | 返回值<=基类 |
参数列表不同(类型,个数,顺序[针对两个不同类型参数]) | 参数列表相同 |
public class Bird {
int a = 5;
static int b = 16;
public void fly() {
System.out.println("Bird flying...");
}
}
--------------------------------------------------------
public class OtherBird extends Bird {
int a = 10;
static int b = 15;
public void fly() {
System.out.println("I can't fly");
System.out.println(a);
System.out.println(b);
}
public void getSuperMethod() {
// 访问基类方法
super.fly();
// 访问基类同名变量
System.out.println(super.a);
// 访问基类同名静态变量
System.out.println(Bird.b);
}
public static void main(String[] args) {
OtherBird ob = new OtherBird();
ob.fly();// 子类重写(覆盖)基类方法
ob.getSuperMethod();
}
}
super关键字
super关键字用于调用基类的实例变量和方法。
情况一:子类与基类成员变量同名是,则基类的成员变量会被隐藏。此时需用super进行访问
情况二:子类对基类方法进行重写,需访问基类方法时使用super
情况三:在子类构造器中,显式调用基类构造器进行成员初始化,此时super语句要放子类构造器第一行
注:在访问某个成员变量x,但没有显式指定调用者的查找顺序是:
该方法中是否有该成员变量x--->当前类中是否有成员变量x--->父类中是否有成员变量x....--->直到Object类--->编译出错
public class HideTest {
public static void main(String[] args) {
Son s = new Son();
// System.out.println(s.s);
//子类只是隐藏基类的同名变量,不会完全覆盖
//s.tag对象中,既有子类的值,也有基类的值
System.out.println(((Parent) s).s);
}
}
class Parent {
public String s = "super";
}
class Son extends Parent {
private String s = "son";
}
public class ConstructorTest extends B {
public ConstructorTest() {
super("cons", 2);
System.out.println("C子类构造器");
}
public static void main(String[] args) {
new ConstructorTest();
//从最顶层开始执行
}
}
class A {
public A() {
System.out.println("无参构造器");
}
}
class B extends A {
public B(String s) {
System.out.println("B的构造器有参数" + s);
}
public B(String s, int i) {
this(s);
System.out.println("B的重载构造器" + i);
}
}
子类在调用基类构造器时有以下几种情况:
- 若没有super进行显式调用,子类会默认调用基类无参构造器一次
- 若子类构造器第一行super显示调用了基类构造器,系统会根据参数列表自动选择构造器
- 若子类第一行使用this调用另一个构造器,系统会在另一个构造器中调用基类构造器
this | super |
---|---|
引用隐式参数 | 调用基类的方法或者成员变量 |
调用本类中其他构造器 | 调用基类的构造器 |
this是一个引用对象 | super指示调用基类的关键字 |
多态
表面上看基类除了可以引用自己本身的对象,还可引用任何子类的对象。
从机制上看,在编译时类型由声明的变量决定,运行时由实际赋值给该变量的类型决定,即为多态
package inheritance;
public class SubClass extends BaseClass {
public String book = "why";
public void test() {
System.out.println("重写方法");
}
public void sub() {
System.out.println("subMethod");
}
public static void main(String[] args) {
BaseClass bc = new BaseClass();
System.out.println(bc.book);
bc.base();
bc.test();
SubClass sc = new SubClass();
System.out.println(sc.book);
sc.base();
sc.test();
// 多态,向上转型,子类直接赋值给基类无须类型转换
BaseClass bc2 = new SubClass();
// 访问基类成员变量
System.out.println(bc2.book);
// 继承于基类
bc2.base();
// 访问当前类的test()方法
bc2.test();
// bc2.sub();
// 原因是bc2编译时类型为BaseClass类型,基类中没有此方法
}
}
class BaseClass {
public int book = 6;
public void base() {
System.out.println("superMethod_1");
}
public void test() {
System.out.println("superMethod_2");
}
}
利用组合代替继承
package inheritance;
public class CompsiteTest {
public static void main(String[] args) {
//显式创建被组合对象
Animal a1 = new Animal();
Birddd b = new Birddd(a1);
b.breath();
b.fly();
Animal a2 = new Animal();
Wolf w = new Wolf(a2);
w.breath();
w.run();
}
}
class Animal {
private void beat() {
System.out.println("herat beating...");
}
public void breath() {
beat();
System.out.println("breathing..");
}
}
class Birddd {
private Animal a;
public Birddd(Animal a) {
this.a = a;
}
public void breath() {
a.breath();
}
public void fly() {
System.out.println("flying...");
}
}
class Wolf {
private Animal a;
public Wolf(Animal a) {
this.a = a;
}
public void breath() {
a.breath();
}
public void run() {
System.out.println("running...");
}
}
初始化块与静态初始化块
初始化块会先于构造器执行。
初始化块和对象声明的执行顺序是按代码顺序执行
静态初始化块与类相关,只在类加载时初始化一次。其他特点与初始化块一致
public class InitBlock {
/*
* { a = 6; } int a = 9;
*/
// 静态初始化块(类初始化块)
static int a = 10;
static {
a = 5;
}
// 初始化块,按照代码排列顺序执行
// 初始化块可看作是对构造器的补充,初始化块总是在构造器之前执行
public static void main(String[] args) {
System.out.println(InitBlock.a);
}
}
public class TextBlock {
public static void main(String[] args) {
new Leaf();
new Leaf();
// 静态初始化块,随类的产生只初始化一次
}
}
class Root {
static {
System.out.println("root静态初始化块");
}
{
System.out.println("root初始化块");
}
public Root() {
System.out.println("root无参构造器");
}
}
class Mid extends Root {
static {
System.out.println("mid静态初始化块");
}
{
System.out.println("mid初始化块");
}
public Mid() {
System.out.println("mid无参构造器");
}
public Mid(String s) {
this();
System.out.println("mid有参构造器" + s);
}
}
class Leaf extends Mid {
static {
System.out.println("leaf静态初始化块");
}
{
System.out.println("leaf初始化块");
}
public Leaf() {
super("参数mid");
System.out.println("leaf构造器");
}
}