继承
1、关键字:extends
2、格式
//定义父类的方式:(一个普通的类的定义)
public class 父类名{
//....
}
//定义子类的格式:
public class 子类名 extends 父类名{
//...
}
3、继承中变量的重名
在父子继承关系中,如果成员变量重名,则创建子类对象时,访问有两种方式:
1、直接通过子类对象访问:
(该对象被创建时) 等号左边是谁,就优先用谁,没有则向上找
2、间接通过成员方法访问成员变量
该方法属于谁,就优先用谁,没有则向上找
【例】:
public class Fu {
int numFu = 1;
int num = 3;
public int getNum(){
return num;
}
}
public class Zi extends Fu {
int numZi = 2;
int num = 4;
public int getNumZi(){
return num;
}
}
public class Text{
public static void main(String[] args) {
Fu fu = new Fu();//创建一个父类对象
//1、直接通过对象访问:
//等号左边是谁,就优先用谁,没有则向上找 (Fu fu = 等号左边为Fu类)
System.out.println("fu.num "+fu.num);
//2、间接通过成员方法访问成员变量
// 该方法属于谁,就优先用谁,没有则向上找 (getNum()方法属于父类)
System.out.println("fu.getNum() "+fu.getNum());
Zi zi = new Zi();
System.out.println("zi.numFu "+zi.numFu+" zi.numZi "+zi.numZi);
//1、直接通过对象访问:
//等号左边是谁,就优先用谁,没有则向上找 ( Zi zi = 等号左边为Zi类)
System.out.println("zi.num "+zi.num);
//2、间接通过成员方法访问成员变量
// 该方法属于谁,就优先用谁,没有则向上找 (getNumZi()方法属于子类)
System.out.println("zi.getNumZi() "+zi.getNumZi());
}
}
4、继承关系中,父类构造方法特点
以下为子类中的几种情况:
情况1,子类中无构造
如果子类中没写构造方法,则会自动赠送一个无参构造
(无参构造中又会隐式的调用父类中的无参构造,详情见【情况2】)
class Fu {
//无参构造
public Fu(){
System.out.println("父类无参构造执行!");
}
//带参构造
public Fu(int num){
System.out.println("父类有参构造执行 "+num);
}
}
class Zi extends Fu{
}
public class Main {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
运行结果:
父类无参构造执行!
可以理解为:子类的系统默认构造函数,默认调用了super();
所以,如果父类中有 无参构造,子类就不用写构造方法
情况2,子类中的构造隐式调用父类构造
如果子类的构造方法中没有出现自定义super语句(没有显式的调用父类构造),则编译器会“赠送”一个super() 作为子类构造方法的第一条语句,从而调用到父类的无参构造
(super只能有一个,还必须是第一句)
a).子类无参构造中的隐式调用
class Fu {
//无参构造
public Fu(){
System.out.println("父类无参构造执行!");
}
//带参构造
public Fu(int num){
System.out.println("父类有参构造执行 "+num);
}
}
class Zi extends Fu{
public Zi(){
//这里会有一个隐藏的super()
System.out.println("子类无参构造执行!");
}
}
public class Main {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
运行结果:
父类无参构造执行!
子类无参构造执行!
b).子类带参构造中的隐式调用
class Fu {
int nun;
public Fu(){
System.out.println("父类中的无参构造执行");
}
public Fu(int a,int b){
System.out.println("父类中的带参构造执行,参数:"+a+","+b);
}
}
class Zi extends Fu {
public Zi(int a,int b){
//这里也有个隐藏的编译器赠送的super()
/*
这里可以通过super(9,12)语句显式的调用父类中的带参构造
这时的输出结果就为:
父类中的带参构造执行,参数:9,12
子类的带参构造:参数:3,6
【注意】,如果这样使用后,编译器便不会赠送super()语句
*/
System.out.println("子类的带参构造:参数:"+a+","+b);
}
}
public class Main {
public static void main(String[] args) {
Zi zi =new Zi(3,6);
}
}
运行结果:
父类中的无参构造执行
子类的带参构造:参数:3,6
情况3,private修饰不可以被继承
如果父类的构造方法只有一个,且修饰符是private,则该方法不可以被继承。
【延申】:
防止类的扩展:使用final修饰符表明一个类值终极的,不可被继承的。
【注意】:
修饰符可以用在类和成员上,只有final还可以用在方法中的局部变量上(常量)。
小结:子类中如果没写构造方法,则编译器会自动赠送一个
如果子类中使用无参构造且其中没有出现自定义super语句,则编译器会“赠送”一个super(),(super只能有一个,还必须是第一句)
子类构造可以通过super关键字来调用父类带参构造
如果父类的构造函数只有一个,且修饰符是private,则不可以被继承。
5、覆盖重写(override)
在父子类的关系继承当中,创建子类对象,访问成员方法规则
创建的对象是谁 就优先用谁 如果没有则向上找(去父类中找)
【注意】 无论成员方法还是成员变量,如果没有都是向上找父类,绝不会向下找子类
方法的重写:在继承关系中,方法的名与参数列表都一样的
重写(override):方法名一样,参数列表【也一样】。也叫覆盖,覆写。
重载(overload):方法名一样,参数列表【不一样】。
重写特点:创建的是子类对象,则优先用子类方法
【方法覆盖的注意事项】
1、保证父子类间的方法名相同,参数列表也相同
@Override 写在方法前面,用来检测是不是有效的正确的覆盖重写
2、子类方法的返回值必须小于等于父类方法的返回值范围
java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类
3、子类方法的权限必须【大于等于】父类方法的权限修饰符
public > protected > (default) > private
(default)不是关键字default 是什么都不写,即:(这里的空气就是default)int a;
静态方法的继承
静态方法也能被继承,但是静态方法不能被覆盖。如果父类中的静态方法在子类中被重新定义,那么定义在父类中的的静态方法将会被被隐藏。
可以使用 父类名.静态方法名 调用被隐藏的静态方法。
class Fu{
public Fu(){
}
public static void sayHello(){
System.out.println("Hello");
}
}
class Zi extends Fu{
public static void sayHello(){
System.out.println("我不");
}
}
public class StaticMethodInExtends {
public static void main(String[] args) {
Zi.sayHello();
Fu.sayHello();
}
运行结果:
我不
Hello
6、例题
public class HelloB extends HelloA{
public HelloB(){//构造方法
System.out.println("\n子类中的构造方法");
}
{//构造代码块
System.out.println("\n子类中的构造代码块");
}
//静态代码块
static{
System.out.println("\n子类中的静态代码块");
}
public static void main(String[] args) {
new HelloB();
}
}
public class HelloA {
public HelloA(){
System.out.println("\n父类中的构造方法");
}
{
System.out.println("\n父类中的构造代码块");
}
static{
System.out.println("\n父类中的静态代码块");
}
}
运行结果是:
父类中的静态代码块
子类中的静态代码块
父类中的构造代码块
父类中的构造方法
子类中的构造代码块
子类中的构造方法
结论: 父类静态代码块内容先执行,后执行子类中的静态代码块,接着执行父类构造代码块和构造方法,然后执行子类构造代码块和构造方法。