关于Java继承,咱不得不说的4个要点

目录

1.继承必须使用extends关键字

2.父类中非私有的属性和方法可以被子类继承

3.构造方法不能被继承,只能通过super关键字去调用

4.调用构造方法并不会创建对象,但是会初始化数据

创建对象的方式1.new关键字 + 构造方法

创建对象的方式2.classloader

创建对象的方式3.反射


那么大家晚上好,我是今天晚上的主讲老师,我是兔哥。
Java的三大特性,继承、封装和多态。
今天,咱就来聊聊继承。虽然在Java内卷的今天,继承不一定会在面试中问到,但是,这个知识点是我们平时开发代码的重中之重。

因此,掌握Java继承非常有必要。

1.继承必须使用extends关键字

extends关键字代表我们可以继承一个父类,也叫超类。
比如,我有一个A类。

public class A {
    private String name = "A";
}

在新建一个B类,去继承A类

public class B extends A{
}

那么我们就说B类是A类的子类。Java不允许多继承,只允许单继承,但是可以多层继承。

什么是多层继承,就是我在新建一个C类,继承B类

public class C extends B{
}

2.父类中非私有的属性和方法可以被子类继承

我们一般会使用到的访问权限,就说public,protected和private三种。

A类有一个私有的name,子类无法访问,只能自己访问。

验证

如果我们换成public

public class A {
    public String name = "A";
}

 都没有报错 , 验证成功!

不过,对于我们希望被子类继承的部分,可以用protected,意思是受保护的,允许传承给子类。

如果我们给name设置成protected,再看B和C类 

一样不会报错。 

但是,protected的属性,在其他包不能访问。(报错了)

Demo.java

public class Demo {
    public static void main(String[] args) {
        String name = new A().name;
        System.out.println(name);
    }
}

 上面的代码是会报错的,同时,它也不能在A类的同一个包的子包中被访问到。

 

只能在同一个包中,或者自己的子类中被访问。

如果Demo也是A的子类

public class Demo extends A{

    public void show(){
        System.out.println(super.name);
    }
}

这是允许的,不会报错(哪怕不在一个包中),我们在B类和C类也能访问到name,但是需要用到super关键字。

让我们绕回来,总结下就是父类中非私有的属性和方法可以被子类继承!

如果不设置成public,会造成很多麻烦,其他包调用不到了!所以,父类一般就是给我们继承用的,而不是给你直接在其他地方调用的。

3.构造方法不能被继承,只能通过super关键字去调用

父类的构造方法是不能被继承的,但我们可以通过super关键字去调用。

当我们new一个B类对象,A类的构造器会优先调用。

当我们new一个C类对象,A类和B类的构造器会先后调用。

验证:

public class A {
    protected String name = "A";

    public A(){
        System.out.println("A");
    }
}
public class B extends A{

    public B(){
        System.out.println("B");
    }

}
public class C extends B{
    public C(){
        System.out.println("C");
    }
}

测试

public class Demo {
    public static void main(String[] args) {
       new C();
    }
}

结果

同样的,父类构造器必须是public或protected,如果用private,子类也无法使用

C类虽然编译不报错,但是运行一定报错。

父类构造器如果没有声明缺省的,却声明了有参构造器,则子类必须用super关键字调用父类的有参构造。

public class A {
    protected String name = "A";

    protected A(String name){
        System.out.println("A");
        this.name = name;
    }
}

B类报错

必须手动用super关键字调用一下父类的有参构造才行,哪怕你只是做做样子

public class B extends A{

    public B(){
        super(null);
        System.out.println("B");
    }

}

如果A类有多个构造器,但偏偏就是没有显式声明缺省构造

public class A {
    protected String name = "A";
    protected int price = 100;

    protected A(String name){
        System.out.println("A");
        this.name = name;
    }

    protected A(String name,int price){
        System.out.println("A");
        this.name = name;
        this.price = price;
    }
}

子类随便super一个父类的构造就行,但一定要有。

public class B extends A{

    public B(){
        super(null);
        System.out.println("B");
    }

}

这种也行

public class B extends A{

    public B(){
        super(null,1);
        System.out.println("B");
    }

}

看你具体的情况。

4.调用构造方法并不会创建对象,但是会初始化数据

我们举了这么多例子,不难发现,构造方法的作用只是初始化数据而已。

而我们创建对象是用new关键字配合构造方法的形式,所以我们可能会误认为调用构造方法就会创建对象了。

创建对象的方法,大体有三种:

创建对象的方式1.new关键字 + 构造方法

A a = new A("A",100);

创建对象的方式2.classloader

DiskClassLoader dcl = new DiskClassLoader("D:\\idea-workspace\\j2se\\out\\production\\j2se\\com\\javaxbfs\\bean");
/** 找到对应的class * */
Class<?> aClass = dcl.findClass("com.javaxbfs.bean.A");
/** 获取构造函数 * */
Constructor<?> constructor = aClass.getConstructor(String.class);
/** 用构造函数创建对象 * */
Object o = constructor.newInstance("A");
Method show = aClass.getMethod("show");
show.invoke(o);

DiskClassLoader是我们的自定义类加载器,用来加载磁盘上另外的class。DiskClassLoader的代码附在最后。

创建对象的方式3.反射

其实上面的例子已经用到了反射。

可见,构造方法只是用来初始化数据的,并不会新建一个对象。

而且,构造方法在当前类中,可以在其他构造方法中通过this调用,在子类构造方法中,可以通过super关键字调用(也只能出现在子类的构造方法中)。并且,必须是在方法的第一行

最后,说说继承的好处,最大的好处,自然是代码的复用,缺点则是增加了耦合

附上DiskClassLoader的代码:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;


public class DiskClassLoader extends ClassLoader {

    private String mLibPath;

    public DiskClassLoader(String path) {
        // TODO Auto-generated constructor stub
        mLibPath = path;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // TODO Auto-generated method stub

        String fileName = getFileName(name);

        File file = new File(mLibPath,fileName);

        try {
            FileInputStream is = new FileInputStream(file);

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len = 0;
            try {
                while ((len = is.read()) != -1) {
                    bos.write(len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            byte[] data = bos.toByteArray();
            is.close();
            bos.close();

            return defineClass(name,data,0,data.length);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return super.findClass(name);
    }

    //获取要加载 的class文件名
    private String getFileName(String name) {
        // TODO Auto-generated method stub
        int index = name.lastIndexOf('.');
        if(index == -1){
            return name+".class";
        }else{
            return name.substring(index+1)+".class";
        }
    }

}

猜你喜欢

转载自blog.csdn.net/weixin_39570751/article/details/123602133