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();
}
}