继承
1、成员的继承
/* 成员的继承 子类继承了父类,子类就会继承父类的成员。 1当子类与父类在同一个包中时,子类继承父类 public,protected与默认访问全限定的成员。 2当子类与父类不在同一个包中时,子类继承父类 public,protected访问权限的成员。 换一种说法: 子类继承父类public,protected访问权限的成员。不能 继承父类private访问权限的成员。如果子类与父类在同一 个包中,则继承父类默认访问权限的成员。 */ package in; public class InheritMember { public static void main(String[] args) { } } class Super { public int pubX; protected int proX; int defX; private int priX; public void pubM() { } protected void proM() { } void defM() { } private void priM() { } } class Sub extends Super { public void f() { pubX = 1; proX = 1; defX = 1; //priX = 1; pubM(); proM(); defM(); //priM(); } }
2、继承的关键字
/* 继承 继承使用extends关键字。 如果A extends B,则A继承B,A就是B的子类,B 就是A的父类。 子类继承了父类,子类便是一种特殊的父类,子类 就会具有父类的能力。 子类会继承父类的成员,就好像是子类中自己声明的一样。 继承的优势: 1 子类继承了父类,子类便具有父类的功能(父类的功能无需 在子类中重复实现)。这就能够编码代码的重复,实现代码的 重用。 2 使用继承有利于程序的维护。以后程序更新时,只需要更新 父类即可。 如果没有显式使用extends继承一个类,则该类继承Object类。 Object是Java中类的祖先。所有类都是Object的直接或间接 子类。 类的单继承性 在Java中,类只支持单重继承。即一个子类只能继承一个 父类。 */ public class Inherit { //public class Inheirt extends Object { public static void main(String[] args) { Animal a = new Animal(); a.introduce(); Tiger t = new Tiger(); t.introduce(); Lion l = new Lion(); l.introduce(); } } class Animal { public void introduce() { System.out.println("动物的自我介绍"); System.out.println("突然改变了实现"); } } class Tiger extends Animal { /*public void introduce() { System.out.println("动物的自我介绍"); }*/ } class Lion extends Animal { /*public void introduce() { System.out.println("动物的自我介绍"); }*/ }
3、构造器的继承
/* 构造器的“继承” 构造器不是方法,也不是类的成员。构造器 不能被子类所继承。(public的构造器也不能)。 虽然子类不会继承父类的构造器,但是在子类的构造器 中,会首先调用父类的构造器。这是一个递归的过程, 一直调用到Object类的构造器为止。 如果子类构造器中没有显式的调用父类的构造器,则会调用 父类无参的构造器。(编译器会自动生成一条语句:super()) 此时,如果父类没有无参的构造器,将会 产生编译错误。 子类中通过super调用父类的构造器。 使用super调用父类构造器的规则: 1 需要使用super调用父类构造器,而不能使用父类构造器 的名字。 2 只能在子类的构造器中调用父类的构造器,而不能在 子类构造器之外调用父类的构造器。 3 使用super调用父类构造器,必须是子类构造器中的第一条 语句。 编译器自动补充super调用父类构造器的情况: 子类构造器中没有显式的调用父类构造器,又没有使用this 调用子类其他构造器时,编译器才会补充super()语句,调用 父类无参的构造器。如果子类构造器中使用this调用了其他 构造器,则编译器不会补充super()语句,以免进行重复的 初始化。 */ public class ConInherit { public static void main(String[] args) { Sub2 s = new Sub2(); } } class Super2 { public Super2(int x) { } /*public Super2() { }*/ } class Sub2 extends Super2 { /* Sub2() { super(); } */ public Sub2() { //super(5); this(5); } public Sub2(int x) { //super(5); } }
4、super关键字的使用
/* super关键字的使用: 1 通过super可以调用父类的构造器 2 通过super可以访问父类的成员。 */ public class SuperUse { public static void main(String[] args) { } } class Super3 { public void f() { } } class Sub3 extends Super3 { public Sub3() { //调用父类无参的构造器 super(); } public void f() { //通过super访问父类的成员。 super.f(); } }
5、
public class Teacher { private String name; private String group; public Teacher(String name, String group) { this.name = name; this.group = group; } public void introduce() { System.out.println("姓名:" + name + ",所在小组:" + group); } public void teach() { System.out.println("知识点讲解"); System.out.println("总结提问"); } } -------------------------------------- public class JavaTeacher2 extends Teacher { public JavaTeacher2(String name, String group) { super(name, group); } public void teach() { System.out.println("打开记事本"); System.out.println("输入代码"); super.teach(); } } ------------------------------------------------- public class JapanTeacher2 extends Teacher { public JapanTeacher2(String name, String group) { super(name, group); } public void teach() { System.out.println("打开日语书"); System.out.println("读单词"); super.teach(); } }
6、方法的重写
/* 方法重写 父类中的方法,在子类中重新又声明一次,就是方法重写。 重写的规则: 1 子类方法与父类方法具有相同的名字。 2 子类方法参数列表需要与父类相同或者与父类擦除后相同。 3 子类方法的返回类型是父类方法返回类型的可替换类型。 可替换类型: 如果是基本数据类型(包含void),则要求子类方法的返回类型 与父类方法的返回类型一致。 如果是引用类型,则要求子类方法的返回类型与父类方法一致, 或者是父类方法返回类型的子类型。 4 子类方法的访问权限不能低于父类方法的 访问权限(可以相同)。 5 子类方法不能比父类方法抛出更多的受检异常。 我们可以使用@Override注解给编译器提供重写的信息。 如果@Override标记的方法没有重写父类的方法,将会 产生编译错误。 */ public class OverrideTest { public static void main(String[] args) { } } class Super4 { public void f1() { } public void f2() { } /*public int f3() { }*/ public Object f3() { return new Object(); } /*public void f4() { }*/ public void f4() { } } class Sub4 extends Super4 { public void g() { } //不是重写,而是重载。 //@Override public void f2(int x) { } /*public byte f3() { } public Object f3() { }*/ public String f3() { return ""; } /*protected void f4() { }*/ }
7、成员变量的隐藏
/* 成员变量的隐藏 在子类中声明了与父类同名的变量,则称 子类隐藏了父类同名的成员变量。 */ public class Hidden { public static void main(String[] args) { } } class Super5 { int a; } class Sub5 extends Super5 { //变量的隐藏。 int a; }
8、父类引用与子类引用(对象)
/* 父类引用与子类引用(对象) 父类的引用既可以指向父类的对象,也可以指向子类 的对象(因为子类是一种特殊的父类,所有的子类都 是父类),子类的引用可以指向子类的对象,但是不能 指向父类的对象。 子类可以赋值给父类,但父类不能赋值给子类。 引用类型之间的转换: 1 自动转换 子类型向父类型的转换(向上转型) 2 强制转换 父类型向子类型的转换(向下转型) 当进行强制转换时,父类引用指向必须是子类对象时, 才能够真正的转换成子类的类型,否则,编译时没有 问题,但在运行时会产生ClassCastException异常。 引用类型转换只能发生在具有父子关系的两个类型 之间,而不能发生在无父子关系的类型之间(编译 错误)。 通过父类的引用不能访问子类新增的成员。即使 父类的引用指向的确实是一个子类的对象也不行。 */ public class Reference { public static void main(String[] args) { Super6 sup = new Super6(); Sub6 sub = new Sub6(); //自动转换 //sup = sub; //sup = new Sub6(); //错误 //sub = sup; //sub = new Super6(); sup = new Sub6(); //错误,不能访问子类新增的成员。 //sup.newM(); sub = (Sub6)sup; sub.newM(); //错误,没有父子关系。 //String s = (String) sup; } } class Super6 { } class Sub6 extends Super6 { public void newM() { System.out.println("子类新增加的成员"); } }
9、
/* instanceof 判断左侧操作数是否为右侧操作数的类型(或者右侧操作数的 子类型),如果是,返回true,否则返回false。 instanceof 要求两侧操作数的类型具有父子关系,否则 会产生编译错误。 instanceof的应用 如果instanceof返回true,则我们就可以放心的将左侧 操作数转换成右侧操作数的类型,而不会在运行时产生 ClassCastException异常。 */ public class InstanceOf { public static void main(String[] args) { String s = "abc"; System.out.println(s instanceof String); System.out.println(s instanceof Object); //System.out.println(s instanceof InstanceOf); InstanceOf i = new InstanceOf(); System.out.println(i instanceof InstanceOf); Object o = new Object(); System.out.println(o instanceof String); i.dodoSomething(new Sub6()); i.dodoSomething(new Super6()); } public void doSomething(Super6 sup) { //Sub6 sub = (Sub6) sup; //sub.newM(); //进行判断,然后在转换,这样就可以避免 //在运行时产生ClassCastException异常。 if (sup instanceof Sub6) { Sub6 sub = (Sub6) sup; sub.newM(); } } }
10、多态
/* 多态 多种形态,运行时会根据对象的真正类型, 来决定调用哪一个类型的方法。 */ public class Py { public static void main(String[] args) { Py p = new Py(); p.doSomething(new Super7()); p.doSomething(new Sub71()); p.doSomething(new Sub72()); p.doSomething(new Sub73()); } public void doSomething(Super7 sup) { sup.f(); } } class Super7 { public void f() { System.out.println("Super7"); } } class Sub71 extends Super7 { @Override public void f() { System.out.println("Sub71"); } } class Sub72 extends Super7 { @Override public void f() { System.out.println("Sub72"); } } class Sub73 extends Super7 { @Override public void f() { System.out.println("Sub73"); } }
11、重写与隐藏
/* 重写与隐藏 实例方法可以被子类重写,静态方法,静态成员变量 与实例成员变量只能被子类隐藏,不能被子类重写。 重写可以实现多态,是根据运行时引用指向的对象真正 类型,来决定调用哪一个类的成员(实例方法)。 隐藏不能实现多态,是根据编译时引用的类型来决定 访问哪一个成员。 能否实现多态,是重写与隐藏的本质区别。 */ public class OverrideAndHidden { public static void main(String[] args) { Sup8 sup = new Sub8(); sup.m(); sup.staM(); System.out.println(sup.x); System.out.println(sup.staX); } } class Sup8 { int x = 1; static int staX = 2; public void m() { System.out.println("sup m"); } public static void staM() { System.out.println("sup staM"); } } class Sub8 extends Sup8 { int x = 111; static int staX = 222; public void m() { System.out.println("sub m"); } public static void staM() { System.out.println("sub staM"); } }
12、
public class Evaluate { public static void main(String[] args) { Evaluate e = new Evaluate(); e.evl(new JavaTeacher2("t1", "g1")); e.evl(new JapanTeacher2("t2", "g2")); //e.evl(new EnglishTeacher2("t3", "g3")); } /* public void evl(JavaTeacher2 t) { t.introduce(); t.teach(); } public void evl(JapanTeacher2 t) { t.introduce(); t.teach(); } */ /* 程序耦合性太高,明天如果增加新的老师, 则该程序还需要进行修改。 public void evl(EnglishTeacher t) { t.introduce(); t.teach(); } */ /* 程序修改: 使用一个父类来表示。 参数使用父类的类型,这样只需要一个方法。 父类类型既可以接受父类的对象,也可以接受 所有子类的对象。 */ public void evl(Teacher t) { t.introduce(); t.teach(); } }